1 문제 제기
예측 모델의 성능을 평가할 때 피어슨 상관계수(\(r\))만 사용하는 것은 통계적으로 불충분한 분석이다. 피어슨 상관계수는 두 변수 사이의 ’상관성(Correlation)’을 측정할 뿐, 실제 값과 예측 값 사이의 ’일치성(Agreement)’을 보장하지 않기 때문이다.
이 주장은 종종 “상관계수가 높으니 예측이 정확하다”는 형태로 나타난다. 이는 ’필요조건’을 ’충분조건’으로 오인한 통계적 오류이다. 정확한 예측이라면 상관계수가 높아야 하는 것은 맞지만, 상관계수가 높다고 해서 반드시 정확한 예측인 것은 아니다.
2 수학적 반례: 상관계수의 맹점
2.1 상수 편향 (Constant Bias)
실제값이 \(y = [1, 2, 3]\)일 때 두 가지 예측 모델을 비교하자:
- 모델 A (완벽한 예측): \(\hat{y} = [1, 2, 3]\)
- 모델 B (상수 편향): \(\hat{y} = [11, 12, 13]\)
| 지표 | 모델 A | 모델 B |
|---|---|---|
| 피어슨 \(r\) | 1.0 | 1.0 |
| \(MSE\) | 0 | 100 |
| \(MAE\) | 0 | 10 |
모델 B는 실제값보다 항상 10만큼 크게 예측하고 있음에도 불구하고, 실제값과 변화의 방향 및 비율이 완벽하게 직선 관계를 이루기 때문에 \(r = 1.0\)이 나온다.
2.2 스케일 편향 (Scale Bias)
- 모델 C (2배 과대 예측): \(\hat{y} = [2, 4, 6]\)
| 지표 | 모델 A | 모델 C |
|---|---|---|
| 피어슨 \(r\) | 1.0 | 1.0 |
| \(MSE\) | 0 | 4.67 |
모델 C도 \(r = 1.0\)이다. 모든 값을 2배로 과대 예측하지만 피어슨 상관계수는 이를 감지하지 못한다.
3 수학적 근거: 왜 감지하지 못하는가
3.1 선형 변환의 불변성 (Invariance under Linear Transformation)
피어슨 상관계수의 정의상, 예측값에 임의의 양수 상수 \(a(>0)\)를 곱하거나 \(b\)를 더해도 상관계수는 변하지 않는다:
\[Cor(y, \hat{y}) = Cor(y, a\hat{y} + b), \quad a > 0\]
이는 피어슨 상관계수가 평균(\(\bar{y}\))으로부터의 편차를 표준편차(\(s\))로 나누어 정규화하기 때문이다. 정규화 과정에서 값의 절대적 크기 차이(Scale)와 평행 이동(Shift) 정보가 모두 소실된다.
만약 실제 매출이 100억인데 모델이 항상 200억으로 예측(\(a=2\))하거나, 항상 110억으로 예측(\(b=10\))하더라도 상관계수는 \(1.0\)이 나온다. 이를 “정확도가 높다”고 표현하는 것은 비즈니스 의사결정에서 치명적인 오판이다.
3.2 MSE의 분해식
\(MSE\)와 상관계수의 관계를 수학적으로 분해하면 다음과 같다:
\[MSE = E[(y - \hat{y})^2] = (\mu_y - \mu_{\hat{y}})^2 + \sigma_y^2 + \sigma_{\hat{y}}^2 - 2\sigma_y \sigma_{\hat{y}} \rho_{y\hat{y}}\]
이 식에서:
- \((\mu_y - \mu_{\hat{y}})^2\): 평균의 차이 (상수 편향)
- \(\sigma_y^2 + \sigma_{\hat{y}}^2 - 2\sigma_y \sigma_{\hat{y}} \rho\): 분산 불일치와 상관 항
상관계수(\(\rho\))가 1이라 하더라도 앞의 항들(평균의 차이, 분산의 차이)이 존재하면 \(MSE\)는 무한히 커질 수 있다. 이것이 상관계수만으로 정확도를 판단할 수 없는 수학적 이유이다.
4 올바른 예측 평가 지표
4.1 1. 오차 기반 지표 (절대적 정확도)
| 지표 | 수식 | 특징 |
|---|---|---|
| MSE | \(\frac{1}{n}\sum(\hat{y}_i - y_i)^2\) | 큰 오차에 높은 패널티, 스케일 의존적 |
| RMSE | \(\sqrt{MSE}\) | MSE의 제곱근, 원래 단위로 해석 가능 |
| MAE | \(\frac{1}{n}\sum|\hat{y}_i - y_i|\) | 이상치에 강건, 중앙값 최적해 |
| MAPE | \(\frac{100}{n}\sum\left|\frac{y_i - \hat{y}_i}{y_i}\right|\) | 스케일 독립적, \(y_i = 0\) 근처에서 불안정 |
\(MSE\) vs \(MAE\) 선택 기준:
- 큰 오차가 치명적인 도메인(금융, 의료): \(MSE\) (큰 오차에 제곱 패널티)
- 이상치가 많은 데이터: \(MAE\) (이상치에 강건)
- 비즈니스 보고용: \(MAPE\) (비율로 직관적 해석 가능)
4.2 2. 결정계수 (\(R^2\), 설명력)
\[R^2 = 1 - \frac{\sum(y_i - \hat{y}_i)^2}{\sum(y_i - \bar{y})^2} = 1 - \frac{SS_{res}}{SS_{tot}}\]
- \(R^2 = 1\): 완벽한 예측
- \(R^2 = 0\): 단순 평균 예측과 동일한 수준
- \(R^2 < 0\): 평균 예측보다 못함
단순 선형 회귀에서는 \(R^2 = r^2\)이 성립하지만, 다중 회귀나 비선형 모델에서는 \(R^2 \neq r^2\)이다. 또한 \(R^2\)는 데이터의 분산을 얼마나 설명하는지를 나타낼 뿐, 실제 오차의 크기를 직접적으로 나타내지는 않는다.
4.3 3. 일치상관계수 (CCC, Concordance Correlation Coefficient)
상관계수에 ‘정확도(Accuracy)’ 항을 추가하여, 데이터가 \(y = \hat{y}\) 선에서 벗어날수록 수치를 페널티로 깎는 지표이다.
\[CCC = \frac{2\rho\sigma_y\sigma_{\hat{y}}}{\sigma_y^2 + \sigma_{\hat{y}}^2 + (\mu_y - \mu_{\hat{y}})^2}\]
이를 분해하면:
\[CCC = \underbrace{\rho}_{\text{정밀도 (Precision)}} \times \underbrace{C_b}_{\text{정확도 (Accuracy)}}\]
여기서 \(C_b = \frac{2\sigma_y\sigma_{\hat{y}}}{\sigma_y^2 + \sigma_{\hat{y}}^2 + (\mu_y - \mu_{\hat{y}})^2}\)이다.
| 시나리오 | \(r\) | \(C_b\) | \(CCC\) |
|---|---|---|---|
| 완벽한 예측 | 1.0 | 1.0 | 1.0 |
| 상수 편향 (+10) | 1.0 | 0.02 | 0.02 |
| 스케일 편향 (x2) | 1.0 | 0.80 | 0.80 |
| 무작위 예측 | 0.0 | - | 0.0 |
CCC는 상관계수가 높더라도 편향이 있으면 수치를 깎기 때문에, 진정한 의미의 일치성을 측정한다.
import numpy as np
def concordance_correlation_coefficient(y_true, y_pred):
"""일치상관계수(CCC) 계산"""
mean_true = np.mean(y_true)
mean_pred = np.mean(y_pred)
var_true = np.var(y_true)
var_pred = np.var(y_pred)
covariance = np.mean((y_true - mean_true) * (y_pred - mean_pred))
numerator = 2 * covariance
denominator = var_true + var_pred + (mean_true - mean_pred) ** 2
return numerator / denominator
# 반례 시연
y_true = np.array([1, 2, 3, 4, 5])
y_perfect = np.array([1, 2, 3, 4, 5]) # 완벽한 예측
y_shifted = np.array([11, 12, 13, 14, 15]) # 상수 편향
y_scaled = np.array([2, 4, 6, 8, 10]) # 스케일 편향
print(f"완벽 예측 - r: {np.corrcoef(y_true, y_perfect)[0,1]:.3f}, "
f"CCC: {concordance_correlation_coefficient(y_true, y_perfect):.3f}")
print(f"상수 편향 - r: {np.corrcoef(y_true, y_shifted)[0,1]:.3f}, "
f"CCC: {concordance_correlation_coefficient(y_true, y_shifted):.3f}")
print(f"스케일 편향 - r: {np.corrcoef(y_true, y_scaled)[0,1]:.3f}, "
f"CCC: {concordance_correlation_coefficient(y_true, y_scaled):.3f}")4.4 4. 잔차 분석 (Residual Analysis)
수치 지표만으로는 오차의 패턴을 파악할 수 없다. 잔차(\(e_i = y_i - \hat{y}_i\))를 시각화하여 다음을 점검해야 한다:
- 잔차 vs 예측값 산점도: 잔차가 무작위로 분포하는지 확인. 패턴이 보이면 모델에 시스템적 편향이 있음
- 잔차의 정규성: Q-Q plot으로 잔차가 정규분포를 따르는지 확인
- 등분산성: 예측값이 커질수록 잔차가 커지는 이분산(Heteroscedasticity) 여부 확인
5 지표의 올바른 조합
예측 모델의 성능을 종합적으로 평가하려면 여러 지표를 역할별로 분리하여 사용해야 한다:
| 역할 | 추천 지표 | 확인 사항 |
|---|---|---|
| 절대적 오차 | RMSE, MAE | 예측값이 실제값과 얼마나 가까운가 |
| 설명력 | \(R^2\) | 모델이 변동성의 몇 %를 설명하는가 |
| 일치성 | CCC | 예측값이 실제값과 체계적으로 일치하는가 |
| 방향성 | 피어슨 \(r\) (보조) | 모델이 경향성이라도 파악하고 있는가 |
| 오차 패턴 | 잔차 분석 | 시스템적 편향이 있는가 |
데이터 과학자는 \(L_2\) Loss(\(MSE\))를 최소화하는 방향으로 모델을 튜닝하지, 상관계수를 높이는 방향으로 튜닝하지 않는다. 상관계수는 모델이 데이터의 “순서(Ranking)”나 “흐름”을 파악하고 있는지 확인하는 보조 지표일 뿐이다.
6 실무에서의 논증 전략
“상관계수가 높으니 정확도가 높다”는 주장을 반박해야 하는 상황에서, 다음과 같은 논증이 효과적이다:
- 반례 제시: 상수 편향 모델(\(\hat{y} = y + 100\))은 \(r = 1.0\)이지만 \(RMSE = 100\)이다. “상관계수는 1인데 오차가 수조 원이 넘는 모델을 신뢰할 수 있는가?”
- 시각화: 실제값 vs 예측값 산점도에 \(y = \hat{y}\) 대각선을 그리고, 데이터가 대각선에서 벗어나 있음을 보여준다.
- CCC 활용: 동일한 데이터에 대해 \(r\)과 \(CCC\)를 함께 계산하여, \(r\)은 높지만 \(CCC\)는 낮은 경우를 실증한다.
import matplotlib.pyplot as plt
import numpy as np
y_true = np.array([10, 20, 30, 40, 50])
y_biased = y_true + 15 # 상수 편향
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 산점도
axes[0].scatter(y_true, y_biased, s=100)
axes[0].plot([0, 60], [0, 60], 'r--', label='y = ŷ (완벽한 예측)')
axes[0].set_xlabel('실제값')
axes[0].set_ylabel('예측값')
axes[0].set_title(f'r = {np.corrcoef(y_true, y_biased)[0,1]:.2f}')
axes[0].legend()
# 잔차
residuals = y_biased - y_true
axes[1].bar(range(len(residuals)), residuals)
axes[1].axhline(y=0, color='r', linestyle='--')
axes[1].set_xlabel('관측치')
axes[1].set_ylabel('잔차 (예측 - 실제)')
axes[1].set_title(f'RMSE = {np.sqrt(np.mean(residuals**2)):.1f}')
plt.tight_layout()
plt.show()