측정 데이터에 가장 잘 맞는 직선 구하기
이 자료에서 $[\boldsymbol A]$, $[\boldsymbol x]$는 행렬·벡터의 성분 배열을 뜻하며, 배열의 행렬곱은 $[\boldsymbol A][\boldsymbol x]$처럼 붙여 쓴다. 물리적 벡터·텐서의 단일수축은 $\cdot$로 표시한다.
이번 강의가 끝나면 다음을 할 수 있어야 한다.
np.polyfit()으로 직선을 적합하고 그래프로 확인할 수 있다.두 점이 주어지면 두 점을 정확히 지나는 직선을 하나 구할 수 있다. 그러나 실제 실험에는 측정오차, 시편 편차와 주변 환경의 영향이 포함된다. 따라서 여러 측정점이 하나의 직선 위에 정확히 놓이는 경우는 드물다.
예를 들어 탄성구간의 응력과 변형률은 이상적으로
\[\sigma=E\varepsilon\]의 선형관계를 따른다. 실제 측정값은 이 직선 주변에 흩어질 수 있다. 이때 일부 점만 골라 직선을 정하기보다 모든 측정점을 함께 고려하여 데이터를 가장 잘 대표하는 직선을 구하는 것이 합리적이다.
직선 모델을 다음과 같이 쓰자.
\[\widehat y=ax+b\]기호 $\widehat y$는 측정값 $y$와 구분하기 위해 사용한다.
$i$번째 데이터 $(x_i,y_i)$에서 모델의 예측값은
\[\widehat y_i=ax_i+b\]이다. 측정값과 예측값의 차이를 잔차(residual)라 한다.
\[\boxed{e_i=y_i-\widehat y_i}\]잔차를 단순히 더하면 양수와 음수가 서로 상쇄될 수 있다. 이를 피하기 위해 잔차를 제곱하여 더한다.
\[\boxed{ S(a,b)=\sum_{i=1}^{N}e_i^2 =\sum_{i=1}^{N}\left[y_i-(ax_i+b)\right]^2 }\]$S$를 잔차제곱합(sum of squared errors, SSE)이라고 한다. 최소제곱법(least squares method)은 $S$가 가장 작아지는 $a$와 $b$를 찾는 방법이다.
잔차를 제곱하면 다음 특징이 있다.
직선보다 단순한 수평선 $\widehat y=b$로 세 측정값 $2$, $4$, $6$을 대표한다고 생각하자. 잔차제곱합은
\[S(b)=(2-b)^2+(4-b)^2+(6-b)^2\]이다. $S$를 최소로 만드는 값은 데이터의 평균인
\[b=\frac{2+4+6}{3}=4\]이다. 즉, 평균도 모든 값과의 차이 제곱합을 가장 작게 만드는 최소제곱 결과로 이해할 수 있다.
import numpy as np
measurements = np.array([2.0, 4.0, 6.0])
best_constant = measurements.mean()
residuals = measurements - best_constant
sse = np.sum(residuals**2)
print(f"Best constant: {best_constant:.1f}")
print(f"Residuals: {residuals}")
print(f"SSE: {sse:.1f}")
직선 $\widehat y=ax+b$의 잔차제곱합은
\[S(a,b)=\sum_{i=1}^{N}\left[y_i-(ax_i+b)\right]^2\]이다. $S$가 최소인 곳에서는 $a$와 $b$ 방향의 기울기가 모두 0이다.
\[\frac{\partial S}{\partial a}=0, \qquad \frac{\partial S}{\partial b}=0\]이를 정리하면 다음 두 연립방정식을 얻는다.
\[a\sum_i x_i^2+b\sum_i x_i=\sum_i x_iy_i\] \[a\sum_i x_i+bN=\sum_i y_i\]이를 정규방정식(normal equations)이라 한다. 연립방정식을 풀면 기울기와 절편을 다음과 같이 나타낼 수 있다.
\[\boxed{ a= \frac{N\sum_i x_iy_i-(\sum_i x_i)(\sum_i y_i)} {N\sum_i x_i^2-(\sum_i x_i)^2} }\] \[\boxed{ b=\frac{\sum_i y_i-a\sum_i x_i}{N} =\bar y-a\bar x }\]수식을 외우는 것보다 “모든 점의 잔차제곱합이 가장 작은 기울기와 절편을 구한다”는 원리를 이해하는 것이 중요하다. 실제 계산에서는 검증된 NumPy 함수를 사용한다.
다음 세 점에 가장 잘 맞는 직선을 구해 보자.
\[(0,1),\quad(1,3),\quad(2,4)\]필요한 합을 표로 정리한다.
| $i$ | $x_i$ | $y_i$ | $x_i^2$ | $x_iy_i$ |
|---|---|---|---|---|
| 1 | 0 | 1 | 0 | 0 |
| 2 | 1 | 3 | 1 | 3 |
| 3 | 2 | 4 | 4 | 8 |
| 합 | 3 | 8 | 5 | 11 |
$N=3$이므로 기울기는
\[a=\frac{3(11)-(3)(8)}{3(5)-3^2} =\frac{9}{6}=1.5\]이고 절편은
\[b=\frac{8-1.5(3)}{3} =\frac{7}{6}\approx1.167\]이다. 따라서 최소제곱 직선은
\[\boxed{\widehat y=1.5x+1.167}\]이다. 세 점을 모두 정확히 지나지는 않지만 세 점 전체의 잔차제곱합을 가장 작게 만든다.
np.polyfit(x, y, 1)은 $x$와 $y$ 데이터에 1차 다항식, 즉 직선을 적합한다. 반환값은 기울기와 절편 순서다.
import matplotlib.pyplot as plt
x = np.array([0.0, 1.0, 2.0])
y = np.array([1.0, 3.0, 4.0])
slope, intercept = np.polyfit(x, y, 1)
predicted_y = slope * x + intercept
print(f"Slope: {slope:.3f}")
print(f"Intercept: {intercept:.3f}")
print(f"Predicted values: {predicted_y}")
결과는 손 계산에서 얻은 $a=1.5$, $b\approx1.167$과 같다.
측정점과 적합 직선을 함께 그려 보자.
x_line = np.linspace(x.min(), x.max(), 100)
y_line = slope * x_line + intercept
fig, ax = plt.subplots(figsize=(6, 4))
ax.scatter(x, y, color="black", label="Data")
ax.plot(x_line, y_line, color="tab:red", label="Least-squares line")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.grid(True, alpha=0.3)
ax.legend()
fig.tight_layout()
plt.show()
직선을 구한 다음에는 잔차를 확인해야 한다.
residuals = y - predicted_y
sse = np.sum(residuals**2)
rmse = np.sqrt(np.mean(residuals**2))
print(f"Residuals: {residuals}")
print(f"SSE: {sse:.4f}")
print(f"RMSE: {rmse:.4f}")
평균제곱근오차(root mean squared error, RMSE)는
\[\mathrm{RMSE} =\sqrt{\frac{1}{N}\sum_{i=1}^{N}e_i^2}\]로 정의한다. RMSE는 $y$와 같은 단위를 가지므로 오차의 대표적인 크기를 해석하기 쉽다.
잔차를 $x$에 대해 그리면 직선 모델이 놓친 경향이 있는지 확인할 수 있다.
fig, ax = plt.subplots(figsize=(6, 3))
ax.axhline(0.0, color="black", linewidth=1)
ax.scatter(x, residuals, color="tab:blue")
ax.set_xlabel("x")
ax.set_ylabel("Residual")
ax.set_title("Residual Plot")
ax.grid(True, alpha=0.3)
fig.tight_layout()
plt.show()
좋은 직선 모델에서는 잔차가 0 주변에 특별한 모양 없이 흩어지는 것이 바람직하다. 잔차가 곡선 모양을 보이면 데이터의 관계가 직선이 아닐 가능성이 있다.
일축인장의 작은 탄성변형 구간에서는 Hooke 법칙이 성립한다.
\[\sigma=E\varepsilon\]응력–변형률 그래프의 기울기 $E$가 영률이다. 다음은 알루미늄의 탄성구간에서 얻었다고 가정한 측정 데이터다.
strain = np.array([0.0000, 0.0005, 0.0010, 0.0015, 0.0020])
stress = np.array([2.0, 34.0, 71.0, 103.0, 141.0]) # MPa
youngs_modulus, stress_offset = np.polyfit(strain, stress, 1)
print(f"Young's modulus: {youngs_modulus / 1000:.1f} GPa")
print(f"Stress-axis intercept: {stress_offset:.2f} MPa")
적합 직선과 측정값을 비교하자.
strain_line = np.linspace(0.0, 0.0020, 100)
stress_line = youngs_modulus * strain_line + stress_offset
fig, ax = plt.subplots(figsize=(6, 4))
ax.scatter(strain, stress, color="black", label="Measured data")
ax.plot(strain_line, stress_line, color="tab:red", label="Linear fit")
ax.set_xlabel("Strain")
ax.set_ylabel("Stress (MPa)")
ax.set_title("Elastic Modulus from Tensile Data")
ax.grid(True, alpha=0.3)
ax.legend()
fig.tight_layout()
plt.show()
이 예제의 영률은 약 69 GPa이다. 절편이 정확히 0이 아닌 것은 하중 영점, 변형률 영점 또는 측정오차의 영향을 나타낼 수 있다.
이론적으로 원점을 지나야 한다는 이유로 무조건 절편을 0으로 고정하기 전에 측정 장비의 영점과 데이터 전처리가 올바른지 확인해야 한다. 또한 소성변형이 시작된 데이터까지 포함하면 탄성 기울기를 정확히 구할 수 없으므로 적합 구간 선택이 중요하다.
앞선 행렬 연산 강의와 연결하면 직선 모델을 다음과 같이 쓸 수 있다.
\[\begin{bmatrix} y_1\\y_2\\\vdots\\y_N \end{bmatrix} \approx \begin{bmatrix} x_1&1\\x_2&1\\\vdots&\vdots\\x_N&1 \end{bmatrix} \begin{bmatrix}a\\b\end{bmatrix}\]간단히 쓰면
\[[\boldsymbol y]\approx[\boldsymbol A][\boldsymbol c]\]이다. 여기서 $\boldsymbol c=[a,b]^T$다. 데이터가 직선 위에 정확히 놓이지 않으면 모든 식을 동시에 정확히 만족하는 해가 없을 수 있다. np.linalg.lstsq()는 $|[\boldsymbol y]-[\boldsymbol A][\boldsymbol c]|_2^2$를 가장 작게 만드는 해를 구한다.
design_matrix = np.column_stack((x, np.ones(x.size)))
coefficients, _, _, _ = np.linalg.lstsq(
design_matrix,
y,
rcond=None,
)
print(f"Slope: {coefficients[0]:.3f}")
print(f"Intercept: {coefficients[1]:.3f}")
이 결과는 np.polyfit() 결과와 같다. 직접 역행렬을 계산하기보다 np.linalg.lstsq()처럼 최소제곱 문제를 풀도록 만들어진 함수를 사용하는 것이 수치적으로 더 적절하다.
np.polyfit(x, y, 1)로 직선을 간단히 적합할 수 있다.np.linalg.lstsq()로 풀 수 있다.측정값이 $y_i=5$이고 모델의 예측값이 $\widehat y_i=4$일 때 잔차를 구하시오.
잔차를 단순히 더하지 않고 제곱하여 더하는 이유를 하나 설명하시오.
잔차가 $[-1,2,-1]$일 때 잔차제곱합을 구하시오.
np.polyfit(x, y, 1)의 마지막 숫자 1은 무엇을 의미하는가?
직선 $\widehat y=3x+2$에서 기울기와 절편을 각각 쓰고, $x=4$일 때 예측값을 구하시오.
탄성구간의 응력–변형률 직선 기울기가 200000 MPa이다. 이를 GPa로 나타내시오.
잔차 그래프가 뚜렷한 곡선 모양을 보인다면 직선 모델에 관해 무엇을 의심할 수 있는가?
영률을 구할 때 소성변형 구간까지 포함하면 안 되는 이유를 설명하시오.