모델 평가 지표

topics 100-데이터분석 & AI 101 머신러닝
types 이론 학습 레퍼런스
tags

모델 평가 지표

왜 필요한가

모델을 학습시켰다고 끝이 아니다. 그 모델이 실제로 얼마나 잘 동작하는지 객관적으로 평가해야 한다.

평가 지표가 없다면?

  • 모델이 좋은지 나쁜지 판단할 수 없다
  • 여러 모델 중 어떤 게 나은지 비교할 수 없다
  • 어떤 부분을 개선해야 할지 알 수 없다

핵심: 문제 유형(분류/회귀)과 비즈니스 목표에 맞는 평가 지표를 선택해야 한다.


분류 평가 지표

Precision-Recall Curve

**Precision(정밀도)**과 **Recall(재현율)**의 트레이드오프를 시각화한 그래프다.

언제 사용할까?

  • 클래스 불균형이 심한 경우 (예: 사기 거래 탐지, 희귀 질병 진단)
  • Positive 클래스가 중요한 경우

그래프 해석

Precision
    ^
    |     ____
    |   /     \___
    |  /          \____
    | /                \___
    |/                     \___
    +-------------------------> Recall
  • 오른쪽 위에 가까울수록 좋은 모델
  • **곡선 아래 면적(AUC-PR)**이 클수록 좋음

왜 Accuracy가 아니라 PR Curve를 쓸까?

클래스 불균형이 심하면 Accuracy는 의미가 없다.

예시: 사기 거래 탐지
- 정상: 9,900건 (99%)
- 사기: 100건 (1%)

모든 거래를 "정상"으로 예측하면?
→ Accuracy = 99% (좋아 보임)
→ 하지만 사기를 하나도 못 잡음!

이럴 때 Precision과 Recall이 중요하다.

ROC Curve

ROC (Receiver Operating Characteristic) 곡선은 **TPR(True Positive Rate)**과 **FPR(False Positive Rate)**의 관계를 나타낸다.

정의

  • TPR (True Positive Rate) = Recall = TP / (TP + FN)
  • FPR (False Positive Rate) = FP / (FP + TN)

언제 사용할까?

  • 클래스가 비교적 균형잡힌 경우
  • Positive와 Negative 둘 다 중요한 경우

그래프 해석

TPR
 ^
 |       /----
 |     /
 |   /
 | /
 |/___________> FPR
  • 왼쪽 위 모서리에 가까울수록 좋은 모델
  • **대각선(y=x)**은 랜덤 추측 수준
  • AUC-ROC (곡선 아래 면적): 0.5~1.0 사이 값, 1.0에 가까울수록 좋음

PR Curve vs ROC Curve

구분 Precision-Recall Curve ROC Curve
적합한 상황 클래스 불균형이 심한 경우 클래스가 균형잡힌 경우
중점 Positive 클래스 성능 전체 클래스 균형
민감도 Positive 클래스 변화에 민감 전체적인 성능 파악

회귀 평가 지표

Pearson Correlation (pearsonr)

피어슨 상관계수는 두 변수 간의 선형 관계를 측정한다.

정의

$r = \frac{\sum(x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum(x_i - \bar{x})^2 \sum(y_i - \bar{y})^2}}$

값의 범위: -1 ~ 1

  • r = 1: 완벽한 양의 상관관계
  • r = 0: 상관관계 없음
  • r = -1: 완벽한 음의 상관관계

언제 사용할까?

패턴이 얼마나 유사한가를 측정할 때 사용한다.

예측값의 절대 크기는 틀려도, **경향성(트렌드)**이 맞으면 높은 점수를 받는다.

예시

from scipy.stats import pearsonr

# <span id="실제값"></span>실제값
y_true = [1, 2, 3, 4, 5]

# <span id="예측값-1-크기도-맞고-패턴도-맞음"></span>예측값 1: 크기도 맞고 패턴도 맞음
y_pred1 = [1.1, 2.0, 3.1, 3.9, 5.0]
r1, _ = pearsonr(y_true, y_pred1)
# <span id="r1-0999-거의-완벽"></span>r1 ≈ 0.999 (거의 완벽)

# <span id="예측값-2-크기는-틀렸지만-패턴은-맞음"></span>예측값 2: 크기는 틀렸지만 패턴은 맞음
y_pred2 = [10, 20, 30, 40, 50]
r2, _ = pearsonr(y_true, y_pred2)
# <span id="r2-10-완벽한-상관관계"></span>r2 = 1.0 (완벽한 상관관계)

# <span id="예측값-3-패턴이-반대"></span>예측값 3: 패턴이 반대
y_pred3 = [5, 4, 3, 2, 1]
r3, _ = pearsonr(y_true, y_pred3)
# <span id="r3--10-완벽한-음의-상관관계"></span>r3 = -1.0 (완벽한 음의 상관관계)

RMSE vs Pearson

지표 무엇을 측정하나 언제 사용하나
RMSE 예측값과 실제값의 절대적인 차이 정확한 값이 중요할 때 (예: 가격 예측)
Pearson 예측값과 실제값의 경향성/패턴 유사도 트렌드가 중요할 때 (예: 주식 방향 예측)

앙상블 평가

앙상블이란?

여러 모델의 예측을 결합하여 최종 예측을 만드는 기법이다.

왜 앙상블을 쓸까?

  • 단일 모델보다 안정적이다
  • 과적합을 줄인다
  • 일반적으로 성능이 향상된다

슬라이스별 앙상블

데이터를 슬라이스(구간)별로 나누어 앙상블하는 방법이다.

예시: Cutoff별 앙상블

# <span id="full-데이터로-학습한-모델"></span>FULL 데이터로 학습한 모델
model_full = train_model(X_full, y_full)
pred_full = model_full.predict(X_test)

# <span id="최근-50개-데이터로-학습한-모델"></span>최근 50개 데이터로 학습한 모델
model_50 = train_model(X[-50:], y[-50:])
pred_50 = model_50.predict(X_test)

# <span id="앙상블"></span>앙상블
pred_ensemble = (pred_full + pred_50) / 2

앙상블 방법 비교

1. 단순 평균 (Simple Average)

모든 모델의 예측을 동일한 가중치로 평균 낸다.

# <span id="3개-모델의-예측"></span>3개 모델의 예측
pred1 = model1.predict(X_test)
pred2 = model2.predict(X_test)
pred3 = model3.predict(X_test)

# <span id="단순-평균"></span>단순 평균
pred_avg = (pred1 + pred2 + pred3) / 3

장점

  • 간단하고 직관적
  • 빠르게 구현 가능
  • 과적합 위험 적음

단점

  • 성능 차이를 고려하지 않음
  • 나쁜 모델도 같은 비중

2. 가중 평균 (Weighted Average)

각 모델의 성능에 따라 다른 가중치를 부여한다.

# <span id="각-모델의-검증-점수"></span>각 모델의 검증 점수
score1 = 0.85
score2 = 0.90
score3 = 0.88

# <span id="점수에-비례한-가중치"></span>점수에 비례한 가중치
total = score1 + score2 + score3
w1 = score1 / total
w2 = score2 / total
w3 = score3 / total

# <span id="가중-평균"></span>가중 평균
pred_weighted = w1 * pred1 + w2 * pred2 + w3 * pred3

장점

  • 좋은 모델에 더 많은 가중치
  • 일반적으로 단순 평균보다 성능 좋음

단점

  • 가중치 최적화가 필요
  • 검증 데이터에 과적합 위험

앙상블 평가 예시

pearsonr로 앙상블 비교

from scipy.stats import pearsonr

# <span id="각-방법별-예측"></span>각 방법별 예측
pred_simple = (pred1 + pred2 + pred3) / 3
pred_weighted = 0.3 * pred1 + 0.5 * pred2 + 0.2 * pred3

# <span id="pearson-상관계수로-평가"></span>pearson 상관계수로 평가
r_simple, _ = pearsonr(y_test, pred_simple)
r_weighted, _ = pearsonr(y_test, pred_weighted)

print(f"단순 평균 pearson: {r_simple:.4f}")
print(f"가중 평균 pearson: {r_weighted:.4f}")

# <span id="더-높은-상관계수를-가진-방법-선택"></span>더 높은 상관계수를 가진 방법 선택
if r_weighted > r_simple:
    final_pred = pred_weighted
else:
    final_pred = pred_simple

평가 지표 선택 가이드

분류 문제

상황 추천 지표
클래스 균형 Accuracy, ROC-AUC
클래스 불균형 Precision-Recall, F1-Score
FP 비용이 높음 Precision 중시
FN 비용이 높음 Recall 중시

회귀 문제

상황 추천 지표
절대값 정확도 중요 RMSE, MAE
트렌드/패턴 중요 Pearson, Spearman
이상치에 민감 RMSE
이상치에 둔감 MAE

코드 예시

분류 평가

from sklearn.metrics import (
    precision_recall_curve,
    roc_curve,
    auc,
    classification_report
)
import matplotlib.pyplot as plt

# <span id="precision-recall-curve"></span>Precision-Recall Curve
precision, recall, _ = precision_recall_curve(y_test, y_pred_proba)
pr_auc = auc(recall, precision)

plt.plot(recall, precision, label=f'PR AUC = {pr_auc:.3f}')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.title('Precision-Recall Curve')
plt.legend()
plt.show()

# <span id="roc-curve"></span>ROC Curve
fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
roc_auc = auc(fpr, tpr)

plt.plot(fpr, tpr, label=f'ROC AUC = {roc_auc:.3f}')
plt.plot([0, 1], [0, 1], 'k--', label='Random')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.legend()
plt.show()

회귀 평가 + 앙상블

from scipy.stats import pearsonr
from sklearn.metrics import mean_squared_error
import numpy as np

# <span id="여러-모델-예측"></span>여러 모델 예측
predictions = [
    model1.predict(X_test),
    model2.predict(X_test),
    model3.predict(X_test)
]

# <span id="단순-평균"></span>단순 평균
pred_simple = np.mean(predictions, axis=0)

# <span id="가중-평균-검증-점수-기반"></span>가중 평균 (검증 점수 기반)
weights = [0.3, 0.5, 0.2]
pred_weighted = np.average(predictions, axis=0, weights=weights)

# <span id="평가"></span>평가
for name, pred in [('Simple', pred_simple), ('Weighted', pred_weighted)]:
    rmse = np.sqrt(mean_squared_error(y_test, pred))
    r, _ = pearsonr(y_test, pred)

    print(f"\n{name} 앙상블:")
    print(f"  RMSE: {rmse:.4f}")
    print(f"  Pearson: {r:.4f}")

관련 문서