모델을 만들고 나서 테스트 데이터로 성능을 평가했는데, 그 결과가 얼마나 신뢰할 수 있는지 의문이 생길 때가 있습니다. 운 좋게 쉬운 테스트 세트를 받은 건지, 아니면 진짜로 모델이 잘 만들어진 건지 한 번의 평가로는 확신하기 어렵습니다. 교차 검증은 데이터를 여러 방식으로 나눠 반복 평가해서 더 신뢰할 수 있는 성능 추정을 만드는 방법입니다.
오늘은 교차 검증의 작동 방식과 언제 어떤 방법을 쓰는지 정리해보겠습니다. 📊
1. 단순 분할의 문제
데이터를 훈련/테스트로 한 번만 나누면 어떤 데이터가 테스트 세트에 들어가느냐에 따라 성능이 크게 달라질 수 있습니다. 특히 데이터가 적을 때 이 문제가 두드러집니다. 운 좋게 쉬운 샘플만 테스트 세트에 들어가면 성능이 과대평가되고, 어려운 샘플이 몰리면 과소평가됩니다.
교차 검증은 이 문제를 해결합니다. 데이터를 여러 폴드(Fold)로 나눠서, 각 폴드를 번갈아 테스트 세트로 쓰고 나머지로 훈련합니다. 모든 데이터가 한 번씩 테스트 세트로 쓰이기 때문에 평가 결과가 특정 분할에 의존하지 않습니다.
단순 분할: 한 번 나눠 한 번 평가 → 분할 방식에 의존 교차 검증: 여러 번 나눠 여러 번 평가 → 평균 성능으로 신뢰도 높임
2. K-Fold 교차 검증

가장 기본적인 교차 검증 방법입니다. 데이터를 K개의 폴드로 균등하게 나누고, K번 반복해서 각 폴드를 한 번씩 테스트 세트로 씁니다. K번의 평가 결과를 평균 내면 최종 성능 추정치가 됩니다.
K값은 보통 5나 10을 씁니다. K가 클수록 각 훈련 세트가 전체 데이터에 가까워져 편향이 줄지만, 반복 횟수가 늘어 계산 비용이 증가합니다. K가 너무 작으면 각 훈련 세트가 작아져 모델이 충분히 학습하지 못합니다.
from sklearn.model_selection import KFold, cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
import numpy as np
data = load_breast_cancer()
X, y = data.data, data.target
model = RandomForestClassifier(n_estimators=100, random_state=42)
# 5-Fold 교차 검증
kf = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=kf, scoring='accuracy')
print(f"각 폴드 정확도: {scores.round(4)}")
print(f"평균: {scores.mean():.4f}")
print(f"표준편차: {scores.std():.4f}")
표준편차가 크면 모델 성능이 데이터 분할에 민감하다는 신호입니다. 평균만큼이나 표준편차도 같이 봐야 합니다.
3. Stratified K-Fold
K-Fold의 단점 중 하나는 각 폴드에 클래스 비율이 보장되지 않는다는 점입니다. 불균형 데이터에서 특정 폴드에 소수 클래스가 거의 없으면 편향된 평가가 나옵니다. Stratified K-Fold는 각 폴드에 원본 데이터의 클래스 비율을 유지합니다. 분류 문제에서는 일반 K-Fold보다 Stratified K-Fold를 쓰는 게 기본입니다.
from sklearn.model_selection import StratifiedKFold, cross_val_score
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores_stratified = cross_val_score(
model, X, y, cv=skf, scoring='f1'
)
print(f"Stratified 각 폴드 F1: {scores_stratified.round(4)}")
print(f"평균 F1: {scores_stratified.mean():.4f}")
4. Leave-One-Out CV
데이터가 N개면 N번 반복해서, 매번 딱 1개만 테스트 세트로 씁니다. K-Fold에서 K=N인 극단적인 경우입니다. 모든 데이터 포인트가 한 번씩 테스트에 사용되기 때문에 가장 편향이 적은 평가를 줍니다. 단, N번 반복해야 하므로 데이터가 많으면 계산 비용이 너무 높아집니다. 데이터가 매우 적을 때(수십~수백 건) 적합합니다.
5. 시계열 데이터의 교차 검증

시계열 데이터에는 일반 K-Fold를 쓰면 안 됩니다. 미래 데이터가 과거 훈련에 섞이는 데이터 누수(Data Leakage)가 발생하기 때문입니다. TimeSeriesSplit은 항상 훈련 세트가 테스트 세트보다 시간적으로 앞에 오도록 분할합니다.
from sklearn.model_selection import TimeSeriesSplit
import numpy as np
tscv = TimeSeriesSplit(n_splits=5)
# 시계열 분할 구조 확인
X_dummy = np.arange(100)
for fold, (train_idx, test_idx) in enumerate(tscv.split(X_dummy)):
print(f"Fold {fold+1}: "
f"훈련 {train_idx[0]}~{train_idx[-1]} | "
f"테스트 {test_idx[0]}~{test_idx[-1]}")
6. 교차 검증 방법 비교
| 방법 | 적합한 상황 | 장점 | 단점 |
|---|---|---|---|
| K-Fold | 일반 회귀 문제 | 구현 단순, 안정적 | 클래스 비율 미보장 |
| Stratified K-Fold | 분류 문제 (불균형 포함) | 클래스 비율 유지 | 회귀에는 적용 불가 |
| Leave-One-Out | 소규모 데이터 | 편향 최소화 | 계산 비용 높음 |
| TimeSeriesSplit | 시계열 데이터 | 데이터 누수 방지 | 초반 폴드 훈련 데이터 적음 |
정리
처음엔 train_test_split 한 번으로 성능을 평가하다가, 같은 모델인데 분할할 때마다 결과가 다르게 나오는 경험을 하고 나서 교차 검증을 쓰기 시작했습니다. 특히 데이터가 적은 프로젝트에서 교차 검증 없이 단순 분할로 평가하면 결과를 신뢰하기 어렵습니다. 표준편차까지 같이 보는 습관이 생기면 모델 성능을 보는 시각이 달라집니다. 😊
'AI > ML' 카테고리의 다른 글
| [머신러닝] L1과 L2 정규화 (2) | 2026.06.05 |
|---|---|
| [머신러닝] 과적합, 모델이 외울 때 생기는 일 (0) | 2026.05.31 |
| [머신러닝] 회귀 모델 오차 지표 비교 (0) | 2026.05.29 |
| [머신러닝] 분류 모델 평가 완전 정리 (0) | 2026.05.28 |
| [머신러닝] SVM, 마진을 설계하는 모델 (0) | 2026.05.28 |
HELLO WORLD
안녕하세요. 데이터로 말하는 분석가 모모입니다.
데이터를 구조화하고 분석하는 과정과 실무에 활용되는 도구 중심의 내용을 기록합니다.