01. ML Pipeline 완전정복
Train/Val/Test Split, Cross-Validation, Data Leakage 방지
학습 목표
이 튜토리얼을 완료하면 다음을 할 수 있습니다:
- Train/Validation/Test Split의 원리와 올바른 분할 방법 이해
- Cross-Validation (K-Fold, Stratified K-Fold) 구현 및 활용
- Data Leakage의 위험성과 이를 방지하는 Pipeline 구축
- Feature Scaling 방법들 (Standard, MinMax, Robust) 비교 분석
sklearn.pipeline.Pipeline과ColumnTransformer를 활용한 재현 가능한 ML 워크플로우 구축
핵심 개념
1. Machine Learning Pipeline이란?
ML Pipeline은 데이터 전처리부터 모델 학습까지의 모든 단계를 순차적으로 연결한 워크플로우입니다.
Raw Data → 전처리 → Feature Engineering → 모델 학습 → 예측Pipeline이 없으면:
- 전처리 단계를 수동으로 관리해야 함
- Train/Test 데이터에 서로 다른 변환이 적용될 위험
- Data Leakage 발생 가능성 증가
2. Train/Validation/Test Split
왜 3-way Split이 필요한가?
| 세트 | 용도 | 비율 |
|---|---|---|
| Train | 모델 학습 | 60-70% |
| Validation | 하이퍼파라미터 튜닝 | 15-20% |
| Test | 최종 성능 평가 (한 번만!) | 15-20% |
⚠️
Test Set은 최종 평가에만 한 번 사용해야 합니다. 여러 번 사용하면 Test Set에 오버피팅됩니다.
잘못된 방법: 단순 Random Split
# stratify 없이 분할 → 클래스 비율 불균형 발생 가능
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)올바른 방법: Stratified Split
from sklearn.model_selection import train_test_split
# 타겟 분포 유지하며 분할
X_temp, X_test, y_temp, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
X_train, X_val, y_train, y_val = train_test_split(
X_temp, y_temp, test_size=0.25, random_state=42, stratify=y_temp
)
# 결과: Train 60%, Val 20%, Test 20% (모두 같은 클래스 비율)3. Data Leakage (데이터 누수)
Test 데이터의 정보가 학습 과정에 유출되는 현상
주요 원인
- 전처리 누수: 전체 데이터로 평균/표준편차 계산 후 스케일링
- Target Leakage: 타겟에서 파생된 피처 사용
- 시간 누수: 미래 데이터로 과거 예측 (시계열)
잘못된 예시
# ❌ 전체 데이터로 스케일링 → 분할 (Leakage!)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X) # 전체 데이터의 mean/std 사용
X_train, X_test = train_test_split(X_scaled, ...)
# 문제: Test 데이터의 정보가 Train에 유출됨올바른 예시
# ✅ 분할 → Train만 fit → 각각 transform
X_train, X_test = train_test_split(X, ...)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) # Train만!
X_test_scaled = scaler.transform(X_test) # transform만 (fit 없음)🚫
Leakage가 있어도 테스트 성능이 좋게 나올 수 있습니다. 하지만 실제 운영 환경에서 성능이 크게 저하됩니다!
4. Cross-Validation
단일 Split의 한계를 극복하기 위해 데이터를 여러 번 분할하여 평가
K-Fold CV
데이터를 K개로 나누고, 각 fold를 한 번씩 validation으로 사용:
from sklearn.model_selection import cross_val_score, StratifiedKFold
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=cv, scoring='accuracy')
print(f"평균: {scores.mean():.4f} (±{scores.std():.4f})")K-Fold vs Stratified K-Fold
| 방법 | 특징 | 사용 시점 |
|---|---|---|
| K-Fold | 단순히 K등분 | 회귀 문제 |
| Stratified K-Fold | 클래스 비율 유지하며 K등분 | 분류 문제 (특히 불균형 데이터) |
여러 메트릭으로 CV
from sklearn.model_selection import cross_validate
scoring = {
'accuracy': 'accuracy',
'precision': 'precision',
'recall': 'recall',
'f1': 'f1',
'roc_auc': 'roc_auc'
}
results = cross_validate(pipeline, X, y, cv=cv, scoring=scoring)5. Feature Scaling 비교
| Scaler | 공식 | 특징 | 사용 시점 |
|---|---|---|---|
| StandardScaler | (x - μ) / σ | 평균 0, 분산 1 | 일반적인 경우 |
| MinMaxScaler | (x - min) / (max - min) | [0, 1] 범위 | 신경망, 이미지 |
| RobustScaler | (x - Q2) / (Q3 - Q1) | 중앙값, IQR 사용 | 이상치가 많을 때 |
Scaling이 필요한 모델 vs 불필요한 모델
| Scaling 필수 | Scaling 불필요 |
|---|---|
| Logistic Regression | Decision Tree |
| SVM | Random Forest |
| KNN | XGBoost, LightGBM |
| Neural Network |
Pipeline 구축
기본 Pipeline
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
pipeline = Pipeline([
('scaler', StandardScaler()),
('classifier', RandomForestClassifier())
])
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)ColumnTransformer로 수치형/범주형 분리 처리
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
# 수치형 전처리
numeric_transformer = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
# 범주형 전처리
categorical_transformer = Pipeline([
('imputer', SimpleImputer(strategy='most_frequent')),
('encoder', OneHotEncoder(handle_unknown='ignore'))
])
# 통합
preprocessor = ColumnTransformer([
('num', numeric_transformer, ['age', 'fare', 'sibsp', 'parch']),
('cat', categorical_transformer, ['pclass', 'sex', 'embarked'])
])
# 전체 파이프라인 (전처리 + 모델)
pipeline = Pipeline([
('preprocessor', preprocessor),
('classifier', RandomForestClassifier(n_estimators=100))
])
# 학습 & 예측
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)Learning Curve 분석
Train/Validation 성능의 변화를 통해 Overfitting/Underfitting 진단:
from sklearn.model_selection import learning_curve
train_sizes, train_scores, val_scores = learning_curve(
pipeline, X, y,
train_sizes=np.linspace(0.1, 1.0, 10),
cv=5, scoring='accuracy'
)
# 시각화
plt.plot(train_sizes, train_scores.mean(axis=1), label='Train')
plt.plot(train_sizes, val_scores.mean(axis=1), label='Validation')
plt.xlabel('Training Set Size')
plt.ylabel('Accuracy')
plt.legend()해석:
- 두 곡선이 수렴 → 좋은 신호
- Gap이 크면 → Overfitting (정규화 또는 단순한 모델 필요)
- 두 곡선 모두 낮으면 → Underfitting (더 복잡한 모델 필요)
체크리스트
| 단계 | 체크 항목 |
|---|---|
| 데이터 분할 | ☐ Stratified Split 사용 |
| ☐ Train/Val/Test 3-way Split | |
| ☐ Test Set은 최종 평가에만 사용 | |
| 전처리 | ☐ Split 후에 전처리 수행 |
| ☐ Pipeline 사용하여 Leakage 방지 | |
| ☐ fit_transform은 Train에만 | |
| 검증 | ☐ Cross-Validation 사용 |
| ☐ 여러 메트릭 사용 | |
| ☐ Learning Curve로 과적합 체크 |
면접 질문 맛보기
- Data Leakage란 무엇이고, 어떻게 방지하나요?
- K-Fold CV와 Hold-out의 차이점은?
- Stratified Split은 언제 사용하나요?
- StandardScaler와 RobustScaler의 차이는?
- Pipeline을 사용하는 이유는?
더 많은 면접 질문은 Premium Interviews (opens in a new tab)에서 확인하세요.
실습 노트북
Titanic 데이터셋으로 위 개념들을 직접 실습해보세요:
노트북에서는 추가로 다음 내용을 다룹니다:
- EDA (탐색적 데이터 분석) 및 시각화
- Leakage 유무에 따른 성능 비교 실험
- 여러 Scaler별 모델 성능 비교
- Feature Importance 분석
- 연습문제