
머신러닝 실전 가이드: 학습 곡선, 오차 분석, 다음 스텝 결정법
- 26회귀 모델 평가 지표: MSE, MAE, R², MAPE 언제 어떤 걸 쓸까
- 27교차 검증(Cross-Validation): K-Fold, Stratified, Time Series Split
- 28하이퍼파라미터 튜닝: Grid Search, Random Search, Bayesian Optimization
- 29머신러닝 실전 가이드: 학습 곡선, 오차 분석, 다음 스텝 결정법읽는 중
- 30범주형 데이터 인코딩: One-Hot, Label, Ordinal Encoding 총정리
지금까지 모델 선택, 평가 지표(분류 메트릭, 회귀 메트릭), 교차 검증, 하이퍼파라미터 튜닝까지 배웠다. 이론적으로 모델을 만들고 평가하는 파이프라인은 완성된 셈이다. 그런데 실전에서는 이것만으로 부족하다.
모델 정확도가 75%다. 다음에 뭘 해야 하는가?
- 데이터를 더 모은다?
- 더 복잡한 모델로 바꾼다?
- 피처를 추가한다?
- 정규화를 강화한다?
- 학습률을 바꾼다?
감으로 선택하면 시간을 낭비한다. 데이터가 부족한 게 아닌데 데이터를 더 모으고, 모델이 이미 과적합인데 더 복잡한 모델을 시도하고, 피처가 노이즈인데 피처를 추가한다. 이번 글에서 다루는 건 진단(Diagnosis) 이다. 학습 곡선과 오차 분석으로 문제를 정확히 짚고, 다음 스텝을 체계적으로 결정하는 방법을 정리한다.
1. 학습 곡선 (Learning Curves)
학습 곡선은 훈련 데이터의 양에 따라 훈련 에러와 검증 에러가 어떻게 변하는지를 시각화한 그래프다. 편향-분산 트레이드오프에서 배운 개념을 실전에서 진단하는 도구가 바로 이것이다.
학습 곡선을 그리는 방법
from sklearn.model_selection import learning_curve
import numpy as np
train_sizes, train_scores, val_scores = learning_curve(
model, X, y,
train_sizes=np.linspace(0.1, 1.0, 10),
cv=5,
scoring='accuracy'
)
train_mean = train_scores.mean(axis=1)
val_mean = val_scores.mean(axis=1)x축은 훈련 데이터 수, y축은 성능(또는 에러). 훈련 데이터를 10%, 20%, … 100%로 늘려가면서 모델을 학습하고, 각 단계에서 훈련 점수와 검증 점수를 기록한다.
패턴 1: 높은 편향 (과소적합)
에러
│
│ ──────────────── 훈련 에러 (높음)
│ ──────────────── 검증 에러 (높음, 비슷)
│
└──────────────────── 데이터 수두 곡선이 모두 높은 에러에서 수렴하고, 서로 가까이 붙어 있다. 데이터를 아무리 늘려도 두 에러가 함께 높은 상태로 머문다.
진단: 모델이 데이터의 패턴을 충분히 표현하지 못한다. 편향-분산 관점에서 편향이 높다. 이건 모델의 용량(capacity) 문제다.
패턴 2: 높은 분산 (과적합)
에러
│
│ ──────────────── 검증 에러 (높음)
│
│ (큰 갭)
│
│ ──────────────── 훈련 에러 (낮음)
│
└──────────────────── 데이터 수훈련 에러는 낮은데 검증 에러가 높다. 두 곡선 사이에 큰 갭이 있다. 데이터를 더 넣으면 갭이 서서히 줄어드는 경향을 보인다.
진단: 모델이 훈련 데이터를 외우고 있다. 분산이 높다. 데이터를 더 모으면 도움이 될 수 있다.
패턴 3: 이상적인 학습 곡선
훈련 에러와 검증 에러가 낮은 수준에서 수렴하고, 갭이 작다. 이 상태면 모델이 적절히 학습된 것이다.
2. 과소적합일 때: 어떻게 편향을 줄이는가
학습 곡선에서 높은 편향(과소적합) 패턴이 나왔다면, 다음 전략들을 시도한다.
더 복잡한 모델 사용
선형 회귀로 비선형 데이터를 피팅하고 있다면, 당연히 한계가 있다. 결정 트리, 랜덤 포레스트, XGBoost 같은 비선형 모델로 전환한다.
# 선형 모델이 과소적합이면
from sklearn.ensemble import GradientBoostingClassifier
# 비선형 모델로 교체
model = GradientBoostingClassifier(n_estimators=100)피처 추가 / 다항 피처
모델이 단순해서가 아니라 피처가 부족해서 패턴을 못 잡는 경우도 있다. 기존 피처의 다항 조합을 추가하거나, 도메인 지식으로 새 피처를 만든다.
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, interaction_only=True)
X_poly = poly.fit_transform(X)정규화 강도 줄이기
정규화를 너무 강하게 걸면 모델이 과도하게 제약받아 과소적합이 된다. alpha(또는 C의 역수)를 줄여본다.
# 정규화가 너무 강하면 과소적합
LogisticRegression(C=0.001) # C가 작으면 정규화가 강함
# 정규화를 줄여서 모델에 자유도 부여
LogisticRegression(C=10)신경망이라면: 더 깊거나, 더 오래
신경망에서 과소적합은 보통 두 가지 원인이다.
- 네트워크가 작다: 층이나 뉴런 수를 늘린다.
- 학습이 부족하다: 에포크를 늘리거나, 학습률을 조정한다. 옵티마이저 선택도 영향을 미친다.
과소적합 해결 체크리스트:
[1] 더 복잡한 모델 (트리 → 앙상블, 선형 → 비선형)
[2] 피처 추가 (다항식, 도메인 피처)
[3] 정규화 줄이기 (alpha↓, C↑)
[4] 학습 시간 늘리기 (에포크↑, 신경망)
[5] 데이터 더 모으기? → 보통 도움 안 됨 ✗3. 과적합일 때: 어떻게 분산을 줄이는가
학습 곡선에서 훈련-검증 갭이 크다면, 과적합이다. 이건 편향-분산에서 배운 분산이 높은 상태다.
데이터를 더 모은다
과적합의 가장 직접적인 해결책이다. 학습 곡선에서 데이터가 늘수록 검증 에러가 내려가는 추세라면, 더 모을 가치가 있다.
정규화 강화
- L1/L2 정규화: 정규화 글에서 배운 Ridge, Lasso. 가중치의 크기를 제한한다.
- Dropout: 신경망 팁에서 배운 기법. 학습 중 무작위로 뉴런을 끈다.
- Early Stopping: 검증 에러가 올라가기 시작하면 학습을 멈춘다.
더 단순한 모델
결정 트리의 max_depth를 줄이거나, 신경망의 층수를 줄이거나, 앙상블 대신 단일 모델을 쓴다.
피처 선택
불필요한 피처가 노이즈를 추가한다. 피처 중요도를 보고 기여도가 낮은 피처를 제거한다.
과적합 해결 체크리스트:
[1] 데이터 더 모으기 → 가장 효과적 ✓
[2] 정규화 강화 (L1/L2, Dropout, Early Stopping)
[3] 모델 단순화 (max_depth↓, 층수↓)
[4] 불필요한 피처 제거
[5] 데이터 증강 (augmentation)
[6] 더 복잡한 모델? → 도움 안 됨 ✗과소적합 해결책(모델 복잡도↑, 정규화↓)과 과적합 해결책(모델 복잡도↓, 정규화↑)은 정확히 반대다. 그래서 진단이 먼저다. 학습 곡선 없이 감으로 튜닝하면, 과소적합에 정규화를 강화하거나 과적합에 피처를 추가하는 삽질을 한다.
4. 오차 분석 (Error Analysis)
학습 곡선이 “전체적인 방향”을 알려준다면, 오차 분석은 구체적으로 어디서 틀리는지를 파고든다. Andrew Ng이 강조하는 데이터 중심 AI(Data-Centric AI)의 핵심 도구이기도 하다.
오차 분석이란
모델이 틀린 예시들을 직접 눈으로 살펴보고, 패턴을 찾는 것이다. 자동화된 메트릭으로는 보이지 않는 문제가 여기서 드러난다.
# 분류 모델의 오분류 사례 추출
from sklearn.metrics import confusion_matrix
y_pred = model.predict(X_val)
errors = X_val[y_pred != y_val]
# 어떤 클래스를 어떤 클래스로 잘못 예측하는가?
cm = confusion_matrix(y_val, y_pred)오차 분석의 구체적 절차
1단계: 오분류 사례 수집 — 검증 세트에서 모델이 틀린 사례를 모두 모은다.
2단계: 카테고리 분류 — 틀린 사례들을 유형별로 분류한다. 예를 들어 이메일 스팸 분류기라면:
오분류 사례 100개 분석:
| 유형 | 개수 | 비율 |
|-----------------|------|------|
| 약물 광고 스팸 | 35 | 35% |
| 피싱 이메일 | 25 | 25% |
| 프로모션 이메일 | 20 | 20% |
| 비영어 스팸 | 15 | 15% |
| 기타 | 5 | 5% |3단계: 우선순위 결정 — 가장 큰 비율을 차지하는 유형부터 해결한다. 위 예시에서 “약물 광고 스팸”이 35%니까, 약물 관련 키워드 피처를 추가하면 전체 에러의 35%를 잠재적으로 줄일 수 있다.
4단계: 개선 후 재평가 — 개선을 적용하고, 다시 오차 분석을 돌린다. 반복이다.
오차 분석에서 찾아야 할 것들
| 질문 | 발견 시 행동 |
|---|---|
| 특정 클래스에서 집중적으로 틀리는가? | 해당 클래스의 데이터를 보강 |
| 특정 피처 값 범위에서 에러가 몰리는가? | 해당 범위의 피처 엔지니어링 |
| 레이블이 잘못된 사례가 있는가? | 데이터 정제 (label cleaning) |
| 모델이 아예 본 적 없는 패턴인가? | 새 피처 추가 또는 데이터 수집 |
| 유사한 입력인데 레이블이 다른 사례? | 피처가 부족해서 구별 불가 → 피처 추가 |
전통적인 ML 워크플로우는 데이터를 고정하고 모델을 개선한다 (모델 중심). Andrew Ng이 제안한 데이터 중심 접근은 반대다 — 모델(또는 코드)을 고정하고 데이터의 품질을 체계적으로 개선한다. 오차 분석은 이 철학의 핵심 도구다. 실무에서 성능 향상의 가장 큰 레버는 대부분 모델이 아니라 데이터에 있다.
5. 베이스라인 설정: 시작점을 정하라
성능이 “좋다”와 “나쁘다”를 판단하려면 기준점이 필요하다. 정확도 75%가 좋은 건지 나쁜 건지는 문제에 따라 다르다.
베이스라인의 종류
1. 인간 수준 성능 (Human-Level Performance)
사람이 이 작업을 하면 몇 %나 맞추는가? 의료 이미지 판독이라면 전문의의 정확도가 기준이 된다. 모델이 인간 수준에 한참 못 미치면 개선 여지가 크다. 인간 수준에 근접하면 남은 에러는 대부분 irreducible noise다.
2. 단순 모델 베이스라인
# 분류: 가장 빈번한 클래스를 항상 예측
from sklearn.dummy import DummyClassifier
baseline = DummyClassifier(strategy='most_frequent')
# 회귀: 평균값을 항상 예측
from sklearn.dummy import DummyRegressor
baseline = DummyRegressor(strategy='mean')불균형 데이터에서 정확도 95%라고 좋아했는데, DummyClassifier도 95%면 모델이 아무것도 배우지 않은 것이다. 분류 메트릭에서 배운 교훈과 정확히 연결된다.
3. 기존 시스템/논문의 성능
이미 배포된 모델이 있다면 그것이 베이스라인이다. 논문의 벤치마크 결과도 참고점이 된다.
에러 갭 분석
인간 수준 에러: 1%
훈련 에러: 5%
검증 에러: 10%
→ 회피 가능한 편향 (Avoidable Bias) = 5% - 1% = 4%
→ 분산 (Variance) = 10% - 5% = 5%
분산이 약간 더 크므로 과적합 해결에 집중해야 한다.인간 수준 에러: 1%
훈련 에러: 8%
검증 에러: 9%
→ 회피 가능한 편향 = 8% - 1% = 7%
→ 분산 = 9% - 8% = 1%
편향이 압도적으로 크므로 모델 복잡도를 높여야 한다.이 프레임워크를 쓰면 “다음에 뭘 해야 하는가?”에 대한 답이 명확해진다.
6. 반복적 개발 프로세스
실전에서 가장 중요한 원칙: 처음부터 복잡한 모델을 쓰지 마라.
ML 프로젝트의 올바른 순서
[1단계] 빠르게 시작
└→ 단순한 모델 (로지스틱 회귀, 결정 트리)
└→ 기본 피처만 사용
└→ 10분 안에 첫 결과를 얻는다
[2단계] 평가 파이프라인 구축
└→ 교차 검증 설정 (/ml/cross-validation/)
└→ 적절한 메트릭 선택 (/ml/classification-metrics/)
└→ 베이스라인 성능 기록
[3단계] 진단
└→ 학습 곡선으로 과소적합/과적합 판단
└→ 오차 분석으로 구체적 실패 패턴 파악
[4단계] 개선
└→ 진단 결과에 따라 하나씩 변경
└→ 변경마다 성능 기록
└→ 동시에 여러 개 바꾸지 않는다
[5단계] 반복
└→ 3~4단계를 성능이 수렴할 때까지 반복이 과정에서 흔히 보는 실수가 “1단계를 건너뛰고 바로 딥러닝부터 쓰는 것”이다. 정형 데이터에서 XGBoost가 딥러닝보다 좋은 경우가 많은데, 처음부터 신경망을 쓰면 디버깅에 시간을 쏟느라 정작 데이터 문제를 놓친다.
7. 데이터 증강과 수집 전략
학습 곡선이 “데이터가 더 필요하다”고 말할 때, 두 가지 선택지가 있다.
데이터 수집
가장 직접적이지만 가장 비용이 크다. 오차 분석과 결합하면 효율적이다 — 전체 데이터를 무작위로 모으는 대신, 모델이 가장 많이 틀리는 유형의 데이터를 집중 수집한다.
오차 분석 결과: "비영어 스팸"에서 15% 에러
→ 비영어 스팸 이메일 데이터를 집중 수집
→ 전체 데이터를 10% 늘리는 것보다 효과적데이터 증강 (Data Augmentation)
기존 데이터를 변형해서 새 데이터를 만든다.
이미지: 회전, 반전, 크롭, 색상 변환, 노이즈 추가
from torchvision import transforms
augment = transforms.Compose([
transforms.RandomHorizontalFlip(),
transforms.RandomRotation(15),
transforms.ColorJitter(brightness=0.2, contrast=0.2),
])텍스트: 동의어 대체, 역번역(한→영→한), 랜덤 삭제
정형 데이터: SMOTE(오버샘플링), 노이즈 주입. 단, 정형 데이터에서 증강은 이미지/텍스트만큼 효과적이지 않은 경우가 많다.
8. 모델 선택 의사결정 트리
“어떤 모델을 써야 하는가?”에 대한 실용적 가이드.
데이터 유형은?
│
├── 정형 데이터 (테이블)
│ ├── 데이터 < 1000건 → 로지스틱 회귀, SVM, KNN
│ ├── 데이터 1000~10만건 → XGBoost / LightGBM (1순위)
│ └── 데이터 > 10만건 → LightGBM (속도), 딥러닝 (시도해볼 가치)
│
├── 이미지 → CNN (딥러닝 필수)
│ └── 데이터 적으면 → 사전학습 모델 + Fine-tuning
│
├── 텍스트 → Transformer 기반 모델
│ └── 데이터 적으면 → 사전학습 LLM + Fine-tuning
│
└── 시계열 → ARIMA, Prophet, 또는 LSTM/Transformer정형 데이터에서 XGBoost/LightGBM이 왜 1순위인지는 XGBoost vs LightGBM 글에서 자세히 다뤘다. 핵심은 피처 엔지니어링을 덜 해도 되고, 결측값 처리가 내장되어 있고, 학습이 빠르다는 점이다.
정형 데이터 대회에서 상위권 솔루션의 압도적 다수가 트리 기반 앙상블을 사용한다. 딥러닝이 우위를 보이는 건 이미지, 텍스트, 음성 같은 비정형 데이터다. "딥러닝 = 항상 최고"가 아니다. 문제에 맞는 도구를 고르는 게 실력이다.
9. 흔한 실수 체크리스트
실전에서 모델 성능이 안 나오는 원인이 모델이 아니라 파이프라인 버그인 경우가 놀라울 정도로 많다.
데이터 누수 (Data Leakage)
문제: 모델이 학습 시점에 “미래 정보”를 보는 것.
# 잘못된 예: 전체 데이터로 스케일링 후 분할
scaler.fit(X) # 테스트 데이터 정보가 스케일링에 포함됨
X_scaled = scaler.transform(X)
X_train, X_test = train_test_split(X_scaled)
# 올바른 예: 훈련 데이터만으로 스케일링
X_train, X_test = train_test_split(X)
scaler.fit(X_train) # 훈련 데이터로만 fit
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)교차 검증에서 배운 것처럼, 전처리도 반드시 훈련 데이터 기준으로 해야 한다. Pipeline을 쓰면 이 실수를 자동으로 방지할 수 있다.
타겟 누수 (Target Leakage)
피처 중에 타겟 변수로부터 파생된 정보가 포함된 경우다. 예를 들어 “환자가 퇴원했는가?”를 예측하는데 피처에 “퇴원일”이 포함되어 있으면, 모델은 100%에 가까운 정확도를 보이지만 실전에서는 쓸모가 없다.
시계열 데이터에서 랜덤 분할
시계열 데이터는 시간 순서를 유지해야 한다. 랜덤으로 섞으면 미래 데이터가 훈련에 포함된다.
# 잘못: 시계열인데 랜덤 분할
train_test_split(X, y, random_state=42)
# 올바른: 시간 기준 분할
X_train = X[X['date'] < '2024-01-01']
X_test = X[X['date'] >= '2024-01-01']잘못된 평가 지표
불균형 데이터에서 accuracy를 쓰거나, 비즈니스 목표와 맞지 않는 메트릭을 최적화하는 실수. 분류 메트릭에서 배운 것처럼, 문제에 맞는 메트릭을 선택하는 것이 첫 번째다.
전체 체크리스트
실전 디버깅 체크리스트:
[ ] 데이터 누수 확인 (전처리가 테스트 데이터를 오염시키지 않는가?)
[ ] 타겟 누수 확인 (피처에 타겟 파생 정보가 있지 않은가?)
[ ] 분할 방식 확인 (시계열이면 시간 기준 분할인가?)
[ ] 평가 지표 확인 (문제에 적합한 메트릭인가?)
[ ] 데이터 셔플링 (훈련 데이터가 한 클래스에 몰려 있지 않은가?)
[ ] 재현성 확인 (random_state를 고정했는가?)
[ ] 피처 스케일링 (거리 기반 모델에 스케일링을 했는가?)
[ ] 결측값 처리 (NaN이 모델에 영향을 주고 있지 않은가?)10. 피처 엔지니어링 맛보기: Phase 7 예고
지금까지의 개선 전략에서 반복적으로 등장한 키워드가 있다: 피처. 과소적합일 때 “피처를 추가하라”, 오차 분석에서 “이 패턴을 잡을 피처가 없다”, 데이터 중심 접근에서 “데이터 품질을 개선하라” — 모두 결국 입력 데이터를 어떻게 가공하느냐의 문제다.
피처 엔지니어링은 모델에 “무엇을 볼지”를 알려주는 작업이다. 같은 모델이라도 피처에 따라 성능이 극적으로 달라진다. 특히 정형 데이터에서는 피처 엔지니어링이 모델 선택보다 더 큰 영향을 미치는 경우가 많다.
다음 글인 Phase 7에서는 이 피처 엔지니어링을 본격적으로 다룬다. 범주형 변수를 숫자로 변환하는 카테고리컬 인코딩부터 시작해서, 수치형 피처 변환, 피처 선택, 피처 생성까지 체계적으로 정리할 예정이다.
Phase 6 마무리: 평가와 진단의 전체 그림
Phase 6 (Applied ML)에서 배운 것들을 정리하자.
| 주제 | 핵심 질문 | 답을 주는 도구 |
|---|---|---|
| 분류 메트릭 | 모델이 얼마나 잘 분류하는가? | Precision, Recall, F1, AUC |
| 회귀 메트릭 | 모델이 얼마나 잘 예측하는가? | MSE, MAE, R² |
| 교차 검증 | 성능 추정이 신뢰할 만한가? | K-Fold, Stratified K-Fold |
| 하이퍼파라미터 튜닝 | 최적의 설정을 어떻게 찾는가? | Grid Search, Random Search |
| 실전 진단 (이 글) | 성능이 안 나올 때 다음에 뭘 하는가? | 학습 곡선, 오차 분석 |
이 다섯 가지가 합쳐지면 ML 모델을 만들고, 평가하고, 개선하는 완전한 사이클이 된다. 하지만 여기서 빠진 퍼즐 한 조각이 있다 — 모델에 넣을 데이터를 어떻게 준비하는가. Phase 7에서 피처 엔지니어링을 시작한다. 첫 글은 범주형 변수 인코딩이다.