교차검증 (K-Fold, OOF)

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

교차검증 (K-Fold, OOF)

왜 필요한가

모델을 학습시킬 때 훈련 데이터로만 평가하면 과적합 여부를 알 수 없다. 그렇다고 테스트 데이터로 평가하면 테스트 데이터에 과적합될 위험이 있다.

교차검증이 없다면?

  • 데이터 분할이 운에 따라 달라진다
  • 한 번의 평가로는 모델 성능을 신뢰하기 어렵다
  • 과적합 여부를 제대로 판단할 수 없다

핵심: K-Fold는 모든 데이터를 훈련과 검증에 골고루 사용하여 신뢰할 수 있는 성능 평가를 가능하게 한다.


K-Fold Cross Validation이란

데이터를 K개의 폴드(fold)로 나누어, 각 폴드를 한 번씩 검증 데이터로 사용하고 나머지를 훈련 데이터로 사용하는 방법이다.

기본 개념

예시: K = 3인 경우

데이터: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Fold 1: 훈련[0,1,2,3,4,5,6] 검증[7,8,9]
Fold 2: 훈련[0,1,2,7,8,9] 검증[3,4,5,6]
Fold 3: 훈련[3,4,5,6,7,8,9] 검증[0,1,2]

각 폴드마다 모델을 학습하고 검증하여, 총 K개의 성능 점수를 얻는다. 이를 평균 내어 최종 성능으로 사용한다.


OOF (Out-Of-Fold)

OOF는 K-Fold의 다른 이름이다. K-Fold 방식 중 하나라고 보면 된다.

"Out-Of-Fold"의 의미

  • 각 폴드에서 학습에 사용되지 않은 데이터(검증 데이터)를 "fold 밖의 데이터"라고 부른다
  • 모든 데이터가 한 번씩 검증 데이터로 사용되므로, 전체 데이터에 대한 예측값을 얻을 수 있다

각 Fold별로 모델 학습

K-Fold를 사용할 때는 각 폴드마다 별도의 모델을 학습시킨다.

일반적인 과정

  1. 데이터를 K개 폴드로 분할
  2. 각 폴드마다:
    • 해당 폴드를 검증 데이터로 사용
    • 나머지 K-1개 폴드를 훈련 데이터로 사용
    • 모델 학습 (예: XGBoost, LightGBM)
    • 검증 데이터로 성능 평가
  3. K개의 성능 점수를 평균 내어 최종 평가

K-Fold의 장점

1. 성능 평가

실제 테스트 데이터 없이도 모델 성능을 정확히 측정할 수 있다.

모든 데이터가 검증에 사용되므로, 한 번의 train/test split보다 훨씬 신뢰할 수 있는 평가가 가능하다.

2. 앙상블

여러 모델의 예측을 조합할 때 사용한다.

각 폴드에서 학습한 K개의 모델을 모두 사용하여 예측하고, 이를 평균 내거나 투표하여 최종 예측을 만든다.

3. 과적합 방지

훈련 데이터로 직접 평가하지 않는다.

각 데이터는 해당 데이터가 훈련에 사용되지 않은 모델로만 평가되므로, 과적합 여부를 정확히 판단할 수 있다.


평가 지표

RMSE (Root Mean Squared Error)

K-Fold에서 주로 사용하는 평가 지표다.

의미: 예측값과 실제값의 오차

$\text{RMSE} = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2}$

  • $y_i$: 실제값
  • $\hat{y}_i$: 예측값
  • 값이 작을수록 좋다

코드 예시

from sklearn.model_selection import KFold
from sklearn.metrics import mean_squared_error
import numpy as np

# <span id="k-fold-설정"></span>K-Fold 설정
kf = KFold(n_splits=3, shuffle=True, random_state=42)

scores = []

# <span id="각-폴드마다-학습-및-평가"></span>각 폴드마다 학습 및 평가
for fold, (train_idx, val_idx) in enumerate(kf.split(X)):
    print(f"Fold {fold + 1}")

    # 데이터 분할
    X_train, X_val = X[train_idx], X[val_idx]
    y_train, y_val = y[train_idx], y[val_idx]

    # 모델 학습
    model = XGBRegressor()  # 또는 LightGBM 등
    model.fit(X_train, y_train)

    # 예측 및 평가
    y_pred = model.predict(X_val)
    rmse = np.sqrt(mean_squared_error(y_val, y_pred))
    scores.append(rmse)

    print(f"RMSE: {rmse:.4f}")

# <span id="최종-평가"></span>최종 평가
print(f"\n평균 RMSE: {np.mean(scores):.4f}")
print(f"표준편차: {np.std(scores):.4f}")

주의사항

Shuffle 설정

  • 일반적인 경우: shuffle=True (데이터를 섞어서 분할)
  • 시계열 데이터: shuffle=False (시간 순서를 유지)

왜 시계열은 shuffle=False일까?
시계열 데이터는 시간 순서가 중요하다. 미래 데이터로 과거를 예측하는 일이 없도록 순서를 유지해야 한다.

자세한 내용은 시계열 데이터 처리를 참고하자.


관련 문서