데이터 분할
hold-out
stratify 옵션은 데이터의 분포를 강제로 맞추는 도구이므로, 그 메커니즘이 오히려 독이 되거나 물리적으로 불가능한 상황에서는 꺼야 한다.
- 분류 문제: 웬만하면 켜라. 특히 불균형 데이터라면 필수다.
- 회귀 문제: 무조건 꺼라.
- 에러 발생 시: 클래스별 샘플 수가 부족한지 확인하고 꺼라.
-
회귀(Regression) 문제일 때:
- stratify는 타깃값의 클래스(범주) 비율을 계산하여 나눈다. 하지만 회귀 모델의 타깃값은 연속적인 수치(float)다.
- 모든 수치가 고유값에 가깝기 때문에 비율을 계산할 클래스가 존재하지 않는다.
- 회귀 데이터에 stratify=y를 넣으면 에러가 발생하거나 의미 없는 연산이 된다.
-
타깃값이 고유값(Unique)이거나 샘플이 극도로 적을 때
- 특정 클래스의 샘플 수가 1개뿐인 경우다.
- stratify를 적용하려면 최소한 각 클래스가 Train과 Test에 나눠 들어갈 만큼의 샘플이 있어야 한다.
- 클래스당 샘플이 하나인 데이터에서 이 옵션을 켜면 ValueError가 발생한다.
-
시계열(Time-Series) 데이터일 때
- 시계열 데이터는 ‘과거의 데이터로 미래를 예측’하는 것이 핵심이다.
- stratify를 켜면 데이터를 무작위로 섞으면서(Shuffle) 클래스 비율을 맞춘다. 이 과정에서 시간의 순서가 완전히 파괴된다.
- 물론 시계열에서는 train_test_split 자체보다 TimeSeriesSplit을 쓰는 것이 정석이지만, 단순 분할을 할 때도 시계열 성격이 있다면 섞지 않는 것이 원칙이다.
-
데이터가 충분히 크고 이미 잘 섞여 있을 때
- 데이터셋이 수백만 건 이상이고, 수집 단계에서 이미 편향 없이 무작위로 수집되었다면 굳이 stratify를 켤 필요가 없다.
- 대수의 법칙에 의해 단순 무작위 추출만으로도 모집단의 분포를 거의 완벽하게 따르기 때문이다.
- 이 경우 계산 비용을 아주 미세하게나마 줄이기 위해 끄기도 한다.
-
의도적인 분포 불일치 테스트 (Out-of-Distribution)
- 학습 환경과 실제 서비스 환경의 데이터 분포가 다를 것을 가정하고 모델의 강건성(Robustness)을 테스트하고 싶을 때다.
- 실제 세상은 항상 학습 데이터와 동일한 클래스 비율을 유지하지 않는다.
- 특정 클래스가 극단적으로 적게 들어왔을 때 모델이 어떻게 반응하는지 보기 위해 전략적으로 무작위 분할을 선택할 수 있다.
import numpy as np; import pandas as pd
from sklearn.datasets import load_iris
iris = load_iris(as_frame=True).frame; df = iris
y=df['target']; X=df.drop(['target'], axis=1, errors=False)
# ==============================================================
#
# ==============================================================
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = (
train_test_split(X, y, test_size=0.33, random_state=42, stratify=False) # 층화추출 옵션 stratify =y
)
k-fold
데이터셋을 K개의 동일한 크기로 나누는 방법.
- 각 폴드를 한번씩 테스트 데이터로 사용하고 나머지를 훈련 데이터로 사용하여 모델의 성능을 평가
- 일반적으로 K는 5 또는 10으로 설정
k-fold 는 모든 데이터가 정확히 한 번씩 검증에 사용됨.
- train_test_split 반복 (Shuffle-Split) 은, 어떤 데이터는 여러 번 사용되고, 어떤 데이터는 한 번도 안 쓰일 수 있음.
KFold는 X만 인자로 받아도 작동하지만, StratifiedKFold는 클래스 비율을 알아야 하므로 반드시 split(X, y)와 같이 타깃값(y)을 함께 전달해야 한다.
분류 모델을 만들고 있다면 고민하지 말고 StratifiedKFold를 사용해라. 모델의 성능 평가가 훨씬 안정적이고 신뢰도가 높다.
import numpy as np; import pandas as pd
from sklearn.datasets import load_iris
iris = load_iris(as_frame=True).frame; df = iris
y=df['target']; X=df.drop(['target'], axis=1, errors=False)
# ==============================================================
#
# ==============================================================
# 방법 1
from sklearn.model_selection import KFold, StratifiedKFold
accuracies = []
# kf = KFold(n_splits=5, shuffle=True, random_state=42)
# for train_index, test_index in kf.split(X):
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
for train_index, test_index in skf.split(X, y):
X_train, X_test = X.iloc[train_index], X.iloc[test_index]
y_train, y_test = y.iloc[train_index], y.iloc[test_index]
cross_val
- 간결함: fit, predict, score 과정을 알아서 수행하고 각 폴드(Fold)의 점수들을 리스트로 반환한다.
- 자동 설정: 분류 모델이 입력되면 내부적으로 StratifiedKFold를, 회귀 모델이 입력되면 KFold를 기본값으로 사용한다.
- 병렬 처리: n_jobs=-1 설정을 통해 CPU 코어를 모두 사용하여 빠르게 계산할 수 있다.
실제 모델을 평가할 때는 단순히 평균(mean)만 보지 말고 표준 편차(std) 를 반드시 확인하라.
평균 점수가 높더라도 폴드별 점수 편차가 크다면, 그 모델은 데이터의 상태에 따라 성능이 널뛰는 불안정한 모델이라는 뜻이다.
| 분류 | cross_val_score | cross_validate |
|---|---|---|
| 자료형 | 점수 리스트 | 딕셔너리(Dict) |
| 용도 | 결과만 빠르게 확인하고 싶을 때 사용. | 학습 시간, 검증 시간, 여러 개의 평가지표를 동시에 보고 싶을 때 사용. |
from sklearn.model_selection import cross_val_score
scores = cross_val_score(model, X, y, cv=5)
mean_accuracy = scores.mean()