교차 검증
교차 검증 (Cross Validation, CV)
- 목적: 모형의 일반화 성능 (새로운 데이터 예측 성능) 평가 방법.
- 원리: 데이터를 훈련(train)셋과 테스트(test)셋으로 분할. 훈련셋으로 모형 학습 후, 별개 테스트셋으로 예측 성능 검증.
- 장점: 수정R²/AIC/BIC 등 이론적 보정보다 과적합 더 현실적으로 반영 가능. 이론적 가정 불필요. 데이터 충분 시 권장.
- 단점: 데이터 분할 필요. 계산 비용 증가.
교차 검증 종류
- 데이터 분할/활용 방식 따라 여러 종류 존재.
- LpO CV (Leave-p-out): p개 제외한 데이터로 학습, p개로 테스트. 모든 조합 시도 (현실적으로 어려움).
- LOOCV (Leave-one-out): 1개 제외한 데이터로 학습, 1개로 테스트. N번 반복 (N=데이터 개수).
- k-fold CV: 데이터 k개 묶음(fold) 분할. k-1개로 학습, 1개로 테스트. k번 반복하며 테스트셋 변경. 가장 널리 사용.
- Holdout: 데이터 한 번만 훈련/테스트셋 분할하여 1회 검증. 가장 간단.
교차 검증 결과 해석
훈련 오차 | 테스트 오차 | 상태 | 조치 |
---|---|---|---|
높음 | 높음 | 과소적합 (Underfitting) | 모형 복잡하게 수정 |
낮음 | 낮음 | 적절 | 바람직 |
낮음 | 높음 | 과적합 (Overfitting) | 모형 단순하게 수정 |
Python 교차 검증 코드 (Holdout 예시)
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
# 1. 데이터 분할 (80% 훈련, 20% 테스트)
train_df, test_df = train_test_split(
df, # 원본 데이터
test_size=0.2, # 테스트 데이터 비율
random_state=42 # 난수 시드 고정 (재현성 위함)
)
# 2. 훈련 데이터로 모형 학습
m = ols('price ~ year', data=train_df).fit()
# 3. 테스트 데이터로 예측
y_pred = m.predict(test_df)
# 4. 테스트 데이터 예측 오차(MSE) 계산
mean_squared_error(test_df.price, y_pred)
전진 선택
depvar = 'price'
features = ['year', 'mileage', 'model', 'my_car_damage', 'other_car_damage']
included = []
threshold = 0.01
changed = True
best_formula = depvar + ' ~ 1' # 절편만 있는 모형
best_model = ols(best_formula, data=train_df).fit()
best_mse = mean_squared_error(test_df[depvar], best_model.predict(test_df))
print(f'{best_formula} (MSE: {best_mse})') # 절편만 있는 모형 출력
while changed:
excluded = list(set(features) - set(included))
best_feature = None
for new_column in excluded:
formula = depvar + ' ~ ' + ' + '.join(included + [new_column])
model = ols(formula, data=train_df).fit()
mse = mean_squared_error(test_df[depvar], model.predict(test_df))
if mse < best_mse - threshold:
best_feature = new_column
best_mse = mse
best_model = model
best_formula = formula
changed = best_feature is not None
if changed:
included.append(best_feature)
print(f'{best_formula} (MSE: {best_mse})')
best_model.summary() # 최종 모형 요약 출력
후진 선택
depvar = 'price'
features = ['year', 'mileage', 'model', 'my_car_damage', 'other_car_damage']
included = features.copy()
threshold = 0.01
changed = True
best_formula = depvar + ' ~ ' + ' + '.join(included) # 초기 모형
best_model = ols(best_formula, data=train_df).fit()
best_mse = mean_squared_error(test_df[depvar], best_model.predict(test_df))
print(f'{best_formula} (MSE: {best_mse})') # 초기 모형 출력
while changed:
excluded = list(set(features) - set(included))
best_feature = None
for remove_feature in included:
formula = depvar + ' ~ ' + ' + '.join(list(set(included) - set([remove_feature])))
model = ols(formula, data=train_df).fit()
mse = mean_squared_error(test_df[depvar], model.predict(test_df))
if mse < best_mse - threshold:
best_feature = remove_feature
best_model = model
best_mse = mse
best_formula = formula
changed = best_feature is not None
if changed:
included.remove(best_feature)
print(f'{best_formula} (MSE: {best_mse})')
best_model.summary() # 최종 모형 요약 출력