층화 및 시계열 교차 검증
학습자는 클래스 비율을 유지하기 위해 StratifiedKFold를 적용하고, 시간 순서가 있는 데이터셋에서 미래 데이터가 과거 폴드로 누출되지 않도록 TimeSeriesSplit을 사용합니다.
층화 및 시계열 교차 검증은(는) CoddyKit의 무료 Machine Learning Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Machine Learning Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Machine Learning Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
표준 K-Fold가 실패하는 경우
표준 KFold는 클래스 분포나 시간 순서를 고려하지 않고 데이터를 무작위로 나눕니다. 이로 인해 두 가지 심각한 문제가 발생합니다. (1) 불균형 데이터셋에서는 일부 폴드에 소수 클래스의 사례가 거의 없거나 전혀 없을 수 있어 폴드 간 변동이 매우 커집니다. (2) 시간 순서가 있는 데이터에서는 과거를 예측하기 위해 미래의 사례로 학습하게 되어 데이터 유출이 발생하고, CV 점수가 실제 환경의 성능보다 훨씬 높아집니다. 특화된 CV 전략을 사용하면 정직한 평가를 유지하면서 이러한 문제를 해결할 수 있습니다.
클래스 비율을 보존하는 Stratified K-Fold
StratifiedKFold는 각 폴드가 전체 데이터셋과 거의 동일한 클래스별 비율을 포함하도록 보장합니다. 예를 들어 데이터의 10%가 사기 사례라면 각 폴드에도 사기 사례가 약 10%씩 포함됩니다. 이렇게 하면 한 폴드에 사기 사례가 전혀 없어 해당 라운드에서 분류기가 사기를 탐지하는 방법을 학습할 수 없는 상황을 방지할 수 있습니다. 클래스가 2개보다 많거나 클래스 불균형이 큰 경우에는 언제나 StratifiedKFold를 사용하십시오.
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
import numpy as np
X, y = load_breast_cancer(return_X_y=True)
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(RandomForestClassifier(random_state=42), X, y, cv=skf)
print('Stratified 5-fold:', np.round(scores, 4))
print('Mean:', round(scores.mean(), 4), 'Std:', round(scores.std(), 4))층화가 제대로 적용되었는지 확인하기
각 폴드의 타깃 분포를 확인하면 StratifiedKFold가 클래스 비율을 유지하는지 검증할 수 있습니다. 각 테스트 폴드에서 양성 사례의 비율은 전체 양성 비율과 일치해야 합니다. 불균형 데이터셋에 표준 KFold와 StratifiedKFold를 적용한 뒤 클래스 분포를 비교하면 층화가 중요한 이유를 분명히 알 수 있습니다. 층화를 적용하지 않으면 폴드별 클래스 비율이 20% 이상 차이 날 수 있습니다.
from sklearn.model_selection import StratifiedKFold, KFold
import numpy as np
# Imbalanced data: 90% negative, 10% positive
np.random.seed(42)
y = np.array([0]*90 + [1]*10)
X = np.random.randn(100, 5)
for name, cv in [('KFold', KFold(n_splits=5, shuffle=True, random_state=42)),
('StratifiedKFold', StratifiedKFold(n_splits=5, shuffle=True, random_state=42))]:
ratios = [y[test].mean() for _, test in cv.split(X, y)]
print(f'{name} positive rates per fold:', np.round(ratios, 2))cross_val_score는 자동으로 층화를 사용합니다
분류 작업에서 cv=5(정수)를 cross_val_score에 전달하면 scikit-learn은 자동으로 StratifiedKFold를 사용합니다. 회귀에서는 일반 KFold를 사용합니다. 따라서 대부분의 사용자는 StratifiedKFold 객체를 직접 만들지 않아도 층화의 이점을 얻을 수 있습니다. 사용자 지정 분할 방식이 필요할 때(예: 그룹 분할, 시계열 분할 또는 셔플을 적용한 다른 폴드 수)에만 이 기본 설정을 재정의하십시오.
시계열 데이터: 고유한 문제
금융, 센서, 날씨 및 그 밖의 많은 실제 데이터셋은 시간 순서에 따라 정렬되어 있습니다. 시계열 모델의 핵심 특성은 과거 데이터만 사용하여 예측해야 한다는 점입니다. 4월 데이터로 모델을 학습하고 1월의 값을 예측한다면 미래 데이터로 학습하는 셈이며, 이를 시간적 데이터 유출이라고 합니다. 표준 K-Fold CV도 이를 위반합니다. 3번 폴드가 테스트 세트이고 4번 폴드가 학습 세트라면 미래 데이터로 과거를 예측하게 됩니다. TimeSeriesSplit은 이를 방지합니다.
TimeSeriesSplit: 항상 과거 데이터로 학습하기
TimeSeriesSplit은 학습 데이터가 항상 시간순으로 테스트 데이터보다 앞서도록 폴드를 만듭니다. 각 분할에서 학습 세트는 이전 테스트 폴드를 포함하면서 확장됩니다. 예를 들어 5개의 분할을 사용하면 분할 1에서는 처음 20%로 학습하고 다음 20%로 테스트하며, 분할 2에서는 처음 40%로 학습하고 다음 20%로 테스트하는 식으로 진행됩니다. 이를 통해 미래 정보가 학습 세트에 들어가지 않으며, CV 점수가 실제 시간 순서에서의 일반화 성능을 반영하게 됩니다.
from sklearn.model_selection import TimeSeriesSplit
import numpy as np
n = 100
X = np.random.randn(n, 5)
y = np.random.randn(n)
tscv = TimeSeriesSplit(n_splits=5)
for fold, (train_idx, test_idx) in enumerate(tscv.split(X, y), 1):
print(f'Fold {fold}: train [{train_idx[0]}-{train_idx[-1]}], '
f'test [{test_idx[0]}-{test_idx[-1]}]')cross_val_score와 함께 TimeSeriesSplit 사용하기
TimeSeriesSplit 객체를 cross_val_score의 cv 매개변수에 직접 전달하십시오. 그 결과로 얻는 점수는 이전 데이터로 학습하고 이후 데이터로 평가할 때 모델이 얼마나 잘 일반화되는지를 측정하며, 이는 실제 환경의 사용 사례와 정확히 일치합니다. 폴드가 진행될수록 학습 세트의 크기가 커지므로 초기 폴드에는 학습 사례가 더 적고 점수가 낮게 나타날 수 있습니다. 이는 오류가 아니라 예상되는 결과입니다.
from sklearn.model_selection import TimeSeriesSplit, cross_val_score
from sklearn.ensemble import GradientBoostingRegressor
import numpy as np
# Simulated time-series: price = trend + noise
np.random.seed(42)
n = 200
X = np.arange(n).reshape(-1, 1) + np.random.randn(n, 1) * 5
y = 0.5 * np.arange(n) + np.random.randn(n) * 10
tscv = TimeSeriesSplit(n_splits=5)
scores = cross_val_score(GradientBoostingRegressor(n_estimators=50, random_state=42),
X, y, cv=tscv, scoring='r2')
print('TimeSeriesSplit R² scores:', np.round(scores, 4))
print('Mean R²:', round(scores.mean(), 4))샘플 유출을 방지하는 GroupKFold
일부 데이터셋에는 하나의 개체에 해당하는 샘플이 여러 개 있습니다. 예를 들어 환자별 ECG 여러 개, 사용자별 거래 여러 개, 장면별 이미지 여러 개가 있을 수 있습니다. 동일한 환자가 학습 폴드와 테스트 폴드에 모두 포함되면 모델은 일반적인 패턴이 아니라 환자별 특성을 학습하게 됩니다. GroupKFold는 동일한 그룹(환자, 사용자, 장면)에 속한 모든 샘플이 같은 폴드에 들어가도록 하여 그룹 내부의 이러한 유출을 방지합니다. 그룹 식별자를 cross_val_score의 groups 매개변수에 전달하십시오.
from sklearn.model_selection import GroupKFold, cross_val_score
from sklearn.linear_model import LogisticRegression
import numpy as np
np.random.seed(42)
n = 100
X = np.random.randn(n, 5)
y = np.random.randint(0, 2, n)
groups = np.repeat(np.arange(10), 10) # 10 patients, 10 samples each
gkf = GroupKFold(n_splits=5)
scores = cross_val_score(LogisticRegression(max_iter=1000), X, y, cv=gkf, groups=groups)
print('GroupKFold scores:', np.round(scores, 4))
print('Mean:', round(scores.mean(), 4))시계열의 확장 윈도와 슬라이딩 윈도 비교
TimeSeriesSplit은 확장 윈도를 사용하므로 분할할 때마다 학습 세트가 커집니다. 대안으로 슬라이딩 윈도를 사용할 수 있습니다. 이 방식에서는 가장 최근 N일 또는 N개월만 학습에 사용하고 오래된 데이터는 제외합니다. 특성과 타깃 사이의 관계가 시간에 따라 변하고(개념 변화), 오래된 데이터가 더 이상 대표성을 갖지 못하는 경우에는 슬라이딩 윈도가 더 적합합니다. scikit-learn은 기본적으로 슬라이딩 윈도 CV를 제공하지 않지만, 사용자 지정 CV 생성기를 사용하거나 max_train_size와 함께 TimeSeriesSplit을 사용하여 구현할 수 있습니다.
from sklearn.model_selection import TimeSeriesSplit
import numpy as np
# Sliding window: fix the training size
tscv = TimeSeriesSplit(n_splits=4, max_train_size=50) # max 50 training samples
X = np.random.randn(200, 3)
for fold, (train_idx, test_idx) in enumerate(tscv.split(X), 1):
print(f'Fold {fold}: train size={len(train_idx)}, '
f'test [{test_idx[0]}-{test_idx[-1]}]')적절한 CV 전략 선택하기
간단한 선택 기준은 다음과 같습니다. (1) 클래스가 균형 잡힌 분류 → 표준 KFold 또는 cross_val_score 정수(자동 층화), (2) 불균형 분류 → 명시적으로 StratifiedKFold 사용, (3) 시간 순서가 있는 데이터 → TimeSeriesSplit, (4) 개체당 여러 샘플 → GroupKFold, (5) 불균형 + 그룹화 → StratifiedGroupKFold(scikit-learn 0.24 이상). CV 전략을 잘못 선택하면 일반화가 잘 되지 않는 모델이 훌륭해 보이거나 좋은 모델이 평범해 보일 수 있습니다. 항상 실제 배포 시나리오에 맞는 CV 방법을 선택하십시오.
프로덕션을 위한 워크 포워드 검증
실제 프로덕션 시계열 시스템에서는 모델을 주기적으로 다시 학습하고 이후 기간의 데이터로 평가합니다. 워크 포워드 검증은 이를 모방합니다. 즉, 1~6개월로 학습하고 7개월에 평가한 다음, 1~7개월로 학습하고 8개월에 평가하는 식입니다. 이는 gap 매개변수를 사용하는 TimeSeriesSplit과 동일하며, 학습 종료 시점과 테스트 시작 시점 사이에 완충 구간을 추가하여 서로 매우 가까운 시간 지점에서 발생하는 유출을 방지합니다. 결과로 얻은 점수의 흐름을 통해 모델 성능이 시간에 따라 안정적인지 또는 저하되는지도 확인할 수 있습니다.
from sklearn.model_selection import TimeSeriesSplit
import numpy as np
# Simulate 24 months of data
n_months = 24
np.random.seed(42)
X = np.random.randn(n_months, 5)
y = np.random.randn(n_months)
# Walk-forward: 12 train periods, 1 test each
tscv = TimeSeriesSplit(n_splits=12, test_size=1)
for fold, (tr, te) in enumerate(tscv.split(X), 1):
if fold <= 3 or fold == 12:
print(f'Fold {fold:2d}: train months 1-{len(tr)}, test month {te[0]+1}')빠른 확인
이 레슨에서 배운 Stratified 및 시계열 교차 검증에 대한 이해도를 확인해 보십시오.
레슨 요약
이 레슨에서 배운 내용은 다음과 같습니다. StratifiedKFold는 불균형 분류에서 폴드 간 클래스 비율을 보존합니다. TimeSeriesSplit은 항상 과거 데이터로 학습하여 시간적 유출을 방지합니다. 또한 GroupKFold는 여러 샘플이 동일한 개체에 속할 때 그룹 내부의 유출을 방지합니다. 다음으로 하이퍼파라미터 최적화를 위한 Grid Search와 Random Search를 비교하겠습니다.
AI 튜터와 함께 Python을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 30
- 레슨
- 120
자주 묻는 질문
“층화 및 시계열 교차 검증” 강의는 무료인가요?
네 — “층화 및 시계열 교차 검증” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Machine Learning Academy 강의 전체를 잠금 해제할 수 있습니다. Machine Learning Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“층화 및 시계열 교차 검증”에서 뭘 배우나요?
학습자는 클래스 비율을 유지하기 위해 StratifiedKFold를 적용하고, 시간 순서가 있는 데이터셋에서 미래 데이터가 과거 폴드로 누출되지 않도록 TimeSeriesSplit을 사용합니다. 브라우저에서 직접 실행하는 실습 코드로 Machine Learning Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Machine Learning Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Machine Learning Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.
“층화 및 시계열 교차 검증” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Machine Learning Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Machine Learning Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.