10. Imbalanced Data (불균형 데이터) 완전정복
SMOTE, Class Weight, 평가 지표 선택
학습 목표
이 튜토리얼을 완료하면 다음을 할 수 있습니다:
- 불균형 데이터의 정의와 실제 문제 사례 이해
- 오버샘플링 기법 (SMOTE, ADASYN) 구현 및 원리 이해
- 언더샘플링 기법 (Random, Tomek Links) 구현
- Class Weight 조정을 통한 비용 민감 학습
- 적절한 평가 지표 (F1, ROC-AUC, PR-AUC) 선택 및 해석
- Threshold 조정을 통한 성능 최적화
핵심 개념
1. 불균형 데이터란?
클래스 간 샘플 수 차이가 크게 나는 데이터입니다.
| 실제 사례 | 다수 클래스 | 소수 클래스 비율 |
|---|---|---|
| 사기 탐지 | 정상 거래 | 사기 0.1% |
| 의료 진단 | 정상 환자 | 희귀 질환 1% |
| 제조 불량 | 정상 제품 | 불량품 2% |
| 고객 이탈 | 유지 고객 | 이탈 고객 5% |
왜 문제인가?
예: 1000개 데이터 (정상 990개, 사기 10개)
모델: "모든 거래가 정상" 예측
→ Accuracy: 99% (높아 보이지만...)
→ 사기 탐지율: 0% (완전히 무용지물!)Accuracy가 높아도 소수 클래스를 전혀 탐지하지 못할 수 있습니다. 불균형 데이터에서는 Accuracy를 신뢰하면 안 됩니다.
2. 해결 방법 분류
| 카테고리 | 방법 | 설명 |
|---|---|---|
| 데이터 레벨 | Over/Under Sampling | 샘플 수 조정 |
| 알고리즘 레벨 | Class Weight, Cost-Sensitive | 손실 함수 조정 |
| 평가 레벨 | 적절한 메트릭 선택 | F1, AUC 등 |
| 임계값 레벨 | Threshold 조정 | 확률 기준값 변경 |
3. 오버샘플링 (Oversampling)
소수 클래스의 샘플 수를 늘려 균형을 맞춥니다.
Random Oversampling
소수 클래스 샘플을 무작위로 복제합니다.
from imblearn.over_sampling import RandomOverSampler
ros = RandomOverSampler(random_state=42)
X_resampled, y_resampled = ros.fit_resample(X, y)SMOTE (Synthetic Minority Over-sampling Technique)
소수 클래스의 합성 샘플을 생성합니다.
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X, y)SMOTE 원리:
- 소수 클래스 샘플 선택
- k-최근접 이웃 중 하나 선택
- 두 샘플 사이의 선형 보간으로 새 샘플 생성
SMOTE는 단순 복제가 아닌 새로운 합성 샘플을 생성하므로, 다양성을 증가시켜 과적합 위험을 줄입니다.
ADASYN (Adaptive Synthetic Sampling)
SMOTE의 변형으로, 학습하기 어려운 샘플에 더 집중합니다.
from imblearn.over_sampling import ADASYN
adasyn = ADASYN(random_state=42)
X_resampled, y_resampled = adasyn.fit_resample(X, y)4. 언더샘플링 (Undersampling)
다수 클래스의 샘플 수를 줄여 균형을 맞춥니다.
Random Undersampling
다수 클래스에서 무작위로 샘플을 제거합니다.
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(random_state=42)
X_resampled, y_resampled = rus.fit_resample(X, y)Random Undersampling은 정보 손실이 크게 발생할 수 있습니다. 예: 8000개 → 800개 (90% 손실)
Tomek Links
클래스 경계의 모호한 샘플을 제거하여 경계를 명확히 합니다.
from imblearn.under_sampling import TomekLinks
tomek = TomekLinks()
X_resampled, y_resampled = tomek.fit_resample(X, y)Tomek Link: 서로 다른 클래스의 가장 가까운 이웃 쌍
- 다수 클래스의 Tomek Link 샘플 제거 → 경계 명확화
5. 복합 기법 (Over + Under)
오버샘플링과 언더샘플링을 결합하여 장점을 극대화합니다.
from imblearn.combine import SMOTETomek
smote_tomek = SMOTETomek(random_state=42)
X_resampled, y_resampled = smote_tomek.fit_resample(X, y)SMOTETomek 원리:
- SMOTE로 소수 클래스 합성
- Tomek Links로 노이즈 제거
6. Class Weight
손실 함수에서 소수 클래스에 더 큰 가중치를 부여합니다.
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
# 자동 계산 (불균형 비율의 역수)
model = LogisticRegression(class_weight='balanced')
# 직접 지정
model = RandomForestClassifier(class_weight={0: 1, 1: 10})balanced 공식: weight = n_samples / (n_classes * n_samples_class)
가중치 계산 예시
from sklearn.utils.class_weight import compute_class_weight
# 예: 7600개 vs 400개 (19:1 비율)
weights = compute_class_weight('balanced', classes=[0, 1], y=y_train)
# Class 0: 0.5263
# Class 1: 10.0000 (19배 높은 가중치)Class Weight는 데이터 자체를 변경하지 않으므로, 원본 데이터 분포를 유지하면서 불균형을 보정할 수 있습니다.
Random Forest의 balanced_subsample
rf_balanced = RandomForestClassifier(
n_estimators=100,
class_weight='balanced_subsample', # 각 트리 부트스트랩 샘플에 적용
random_state=42
)7. 평가 지표 선택
Accuracy를 쓰면 안 되는 이유
99% 정상, 1% 사기 데이터에서:
→ 모두 정상 예측해도 Accuracy = 99%
→ 하지만 사기 탐지 완전 실패!권장 지표
| 지표 | 공식 | 의미 | 사용 시점 |
|---|---|---|---|
| Precision | TP/(TP+FP) | 양성 예측 중 실제 양성 비율 | FP 비용 클 때 |
| Recall | TP/(TP+FN) | 실제 양성 중 탐지 비율 | FN 비용 클 때 |
| F1-Score | 2×P×R/(P+R) | Precision과 Recall 조화평균 | 균형 필요 |
| PR-AUC | PR 곡선 면적 | 불균형에 민감한 종합 평가 | 불균형 심할 때 |
| ROC-AUC | ROC 곡선 면적 | 분류 능력 종합 평가 | 일반적 |
ROC-AUC vs PR-AUC: 불균형 데이터에서는 PR-AUC가 더 민감합니다. ROC-AUC는 TN이 많을 때 높게 나오는 경향이 있어, 극심한 불균형에서는 PR-AUC를 권장합니다.
8. Threshold 조정
기본 threshold (0.5) 대신 비즈니스 요구사항에 맞게 조정합니다.
from sklearn.metrics import precision_recall_curve
# Threshold 별 Precision/Recall 계산
precision, recall, thresholds = precision_recall_curve(y_test, y_proba)
# 특정 Recall 보장 threshold 찾기
target_recall = 0.9
idx = np.argmax(recall >= target_recall)
optimal_threshold = thresholds[idx]
# 최적 threshold 적용
y_pred_optimal = (y_proba >= optimal_threshold).astype(int)Threshold를 낮추면 Recall 증가, Precision 감소, 높이면 그 반대입니다. 비즈니스 비용을 고려하여 선택하세요.
코드 요약
SMOTE + Model Pipeline
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline as ImbPipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, roc_auc_score
# SMOTE + Model Pipeline
pipeline = ImbPipeline([
('smote', SMOTE(random_state=42)),
('classifier', RandomForestClassifier(random_state=42))
])
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
# 평가
print(classification_report(y_test, y_pred))Class Weight 방식
model_weighted = RandomForestClassifier(
class_weight='balanced',
random_state=42
)
model_weighted.fit(X_train, y_train)올바른 Cross-Validation with SMOTE
from imblearn.pipeline import Pipeline as ImbPipeline
from sklearn.model_selection import cross_val_score, StratifiedKFold
# imblearn Pipeline (SMOTE + 모델)
pipeline = ImbPipeline([
('smote', SMOTE(random_state=42)),
('classifier', LogisticRegression(max_iter=1000, random_state=42))
])
# Stratified K-Fold (각 fold 내에서 SMOTE 적용)
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(pipeline, X_train, y_train, cv=skf, scoring='f1')
print(f'Mean F1: {scores.mean():.3f} (±{scores.std():.3f})')중요: 리샘플링은 Train 데이터에만 적용해야 합니다. Test 데이터는 원본을 유지하세요. CV에서는 imblearn의 Pipeline을 사용하면 각 fold 내에서만 SMOTE가 적용됩니다.
방법 비교
| 방법 | 장점 | 단점 |
|---|---|---|
| Random Undersampling | 빠름, 학습 시간 단축 | 정보 손실 큼 |
| Random Oversampling | 정보 보존 | 과적합 위험 (단순 복제) |
| SMOTE | 다양성 증가, 새로운 샘플 생성 | 노이즈 생성 가능 |
| ADASYN | 어려운 샘플에 집중 | SMOTE보다 더 많은 노이즈 가능 |
| Tomek Links | 경계 명확화 | 소량만 제거됨 |
| SMOTETomek | 장점 결합 | 계산 비용 증가 |
| Class Weight | 데이터 변경 없음 | 모델 의존적 |
실전 가이드
| 상황 | 권장 방법 |
|---|---|
| 데이터 충분 | Undersampling 또는 Class Weight |
| 데이터 부족 | SMOTE 또는 ADASYN |
| 트리 기반 모델 | Class Weight (balanced/balanced_subsample) |
| 노이즈가 많음 | SMOTETomek (노이즈 제거) |
| 심각한 불균형 (100:1 이상) | SMOTE + Class Weight 결합 |
| 실시간 예측 필요 | Class Weight (학습 시간 단축) |
Best Practices
- 항상 층화 샘플링 사용:
stratify=y로 Train/Test 분할 - 리샘플링은 Train에만: Test 데이터는 원본 유지
- Cross-Validation 주의: StratifiedKFold 사용, 각 fold 내에서 리샘플링
- 평가 지표 선택: Accuracy 대신 F1, ROC-AUC, PR-AUC 사용
- Threshold 조정 고려: 비즈니스 요구사항에 맞게 최적화
면접 질문 맛보기
- 불균형 데이터에서 Accuracy가 왜 부적절한가요?
- SMOTE의 원리와 한계는 무엇인가요?
- Precision과 Recall 중 무엇을 중시해야 하나요?
- ROC-AUC와 PR-AUC의 차이는?
- Class Weight는 어떻게 계산되나요?
더 많은 면접 질문은 Premium Interviews (opens in a new tab)에서 확인하세요.
실습 노트북
불균형 데이터 처리의 모든 기법을 직접 실습해보세요:
노트북에서는 추가로 다음 내용을 다룹니다:
- 불균형 데이터 생성 및 시각화 (PCA 2D 투영)
- 모든 방법의 성능 비교 실험 (6가지 방법)
- ROC Curve vs PR Curve 비교 분석
- Threshold 변화에 따른 성능 변화 시각화
- Random Forest의 balanced_subsample 활용
- 연습문제 (극단적 불균형, 비용 민감 학습, 다중 클래스)
이전: 09. Anomaly Detection | 다음: 11. Time Series