교차검증 (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를 사용할 때는 각 폴드마다 별도의 모델을 학습시킨다.
일반적인 과정
- 데이터를 K개 폴드로 분할
- 각 폴드마다:
- 해당 폴드를 검증 데이터로 사용
- 나머지 K-1개 폴드를 훈련 데이터로 사용
- 모델 학습 (예: XGBoost, LightGBM)
- 검증 데이터로 성능 평가
- 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일까?
시계열 데이터는 시간 순서가 중요하다. 미래 데이터로 과거를 예측하는 일이 없도록 순서를 유지해야 한다.
자세한 내용은 시계열 데이터 처리를 참고하자.