흔한 실수 (Common Mistakes in ML)
난이도: 초급~중급
선수 지식: 교차 검증, 데이터 누수
관련 문서: 데이터 누수 | ML 파이프라인 설계 | 통계적 검정
핵심 요약: ML 프로젝트에서 가장 흔한 8가지 실수. 대부분 데이터 처리 순서에서 발생한다 — 전처리를 먼저 하고 분할하면 누수, 정확도만 보면 불균형 함정, 베이스라인 없이 시작하면 방향 상실.
초보자를 위한 핵심 용어
섹션 제목: “초보자를 위한 핵심 용어”- Data Snooping: 테스트 데이터를 보고 모델을 수정하는 실수. 시험 답안지를 미리 보고 공부 방향을 정하는 것과 같다.
- 검증 세트 과적합(Validation Overfitting): 검증 세트 결과를 보고 반복 튜닝하여, 검증 성능만 좋고 실전에서 떨어지는 현상.
- Training-Serving Skew: 학습 환경(Python)과 배포 환경(Java)의 전처리 차이로 성능이 급락하는 문제.
- Baseline: 비교 기준이 되는 가장 단순한 모델. 이것 없이 시작하면 “개선했는지” 자체를 판단할 수 없다.
ML 프로젝트에서 반복적으로 발생하는 실수들이 있다. 이 실수들은 대부분 성능의 과대 추정, 잘못된 결론, 또는 프로덕션에서의 실패로 이어진다. 경험이 풍부한 실무자도 실수할 수 있으며, 이를 미리 인지하고 체계적으로 방지하는 것이 중요하다.
비유: ML의 흔한 실수들은 운전 초보가 겪는 사고 유형과 닮았다. 대부분 기술이 부족해서가 아니라 확인을 건너뛰어서 일어난다. 사이드미러를 안 봤고(검증셋 확인), 후진 전에 뒤를 안 살폈고(데이터 누수), 계기판 경고등을 무시했다(성능 저하 모니터링). 그래서 이 장의 내용은 새로운 기술이 아니라 체크리스트에 가깝다.
탄생 배경
섹션 제목: “탄생 배경”ML의 흔한 실수들이 체계적으로 정리된 것은, Kaggle 대회와 산업 ML 프로젝트의 실패에서 반복적으로 발견된 패턴들이 축적된 결과이다.
Kaggle 초기 대회에서의 교훈 (2010~2015):
- Kaggle 대회 초기에는 참가자들이 테스트 세트에 대해 반복적으로 제출하면서 리더보드에 과적합(Overfitting to Leaderboard)하는 현상이 빈번했다. Public Leaderboard에서 1등이었던 팀이 Private Leaderboard에서 수백 위로 추락하는 사례가 반복되면서, “검증 세트 과적합”이라는 개념이 널리 알려졌다.
- 일부 대회에서는 의도치 않은 데이터 누수(Data Leakage)가 발견되어 대회가 무효화되기도 했다. 예를 들어, 파일명에 라벨 정보가 포함되어 있거나, 시간 순서가 무시된 분할이 사용된 경우가 있었다.
산업 ML 실패 사례의 축적 (2015~현재):
- 2015년 이후 기업들의 ML 도입이 본격화되면서, 학술 환경에서는 문제가 되지 않았던 실수들이 프로덕션에서 대규모 사고로 이어졌다. Training-Serving Skew로 인한 서비스 장애, 불균형 데이터에서의 잘못된 지표 선택으로 인한 비즈니스 손실, 교차 검증 전 전처리로 인한 성능 과대 추정 등이 반복적으로 보고되었다.
- 이러한 실패 사례들이 블로그, 컨퍼런스 발표, 논문 등을 통해 공유되면서, ML 실무자들 사이에서 “흔한 실수 체크리스트”가 자연스럽게 형성되었다. 본 문서는 그 축적된 교훈을 체계적으로 정리한 것이다.
핵심 개념
섹션 제목: “핵심 개념”1. 데이터 몰래 보기 (Data Snooping)
섹션 제목: “1. 데이터 몰래 보기 (Data Snooping)”테스트 데이터로 모델을 선택하거나 튜닝하여, 성능이 과도하게 낙관적으로 추정되는 문제이다.
| 잘못된 방법 | 올바른 방법 |
|---|---|
| 테스트 세트 결과를 보고 모델 수정 | Hold-out 테스트 세트는 최종 1회만 사용 |
| 여러 모델을 테스트 세트로 비교 | 검증 세트로 비교, 테스트 세트는 최종 모델에만 |
2. 부적절한 검증 (Improper Validation)
섹션 제목: “2. 부적절한 검증 (Improper Validation)”| 실수 | 올바른 방법 |
|---|---|
| 시계열에 random split | Time-based split |
| 그룹 데이터에서 그룹을 분할하지 않음 | Group K-Fold |
| 교차 검증 전에 전처리 | 각 fold 내부에서 전처리 |
| Stratification 없이 불균형 데이터 분할 | Stratified K-Fold |
잘못된 교차 검증 — 전체 데이터에서 전처리 후 교차 검증을 수행하면 검증 오차가 과소 추정된다:
올바른 교차 검증 — 각 fold 내부에서 전처리를 수행해야 한다:
여기서 전처리의 fit은 (학습 fold)에서만 수행하고, (검증 fold)에는 transform만 적용한다.
3. 클래스 불균형 무시
섹션 제목: “3. 클래스 불균형 무시”정확도(accuracy)만 보고 “모델이 잘 작동한다”고 판단하는 실수.
Before: “Accuracy 98%로 우수한 모델!”
After: “소수 클래스 Recall 0%… 모델이 다수 클래스만 예측”
→ 불균형 데이터에서는 F1, AUROC, AUPRC를 사용해야 한다.
4. 잘못된 지표 선택
섹션 제목: “4. 잘못된 지표 선택”비즈니스 목표와 최적화 지표의 불일치.
| 상황 | 잘못된 지표 | 적절한 지표 |
|---|---|---|
| 스팸 필터 | Recall (정상 메일 차단 위험) | Precision 우선 |
| 질병 선별 검사 | Precision (환자 누락 위험) | Recall 우선 |
| 추천 시스템 | Accuracy | nDCG, MAP |
| 회귀 (이상치 있음) | MSE | MAE 또는 Huber |
5. 검증 세트 과적합 (Overfitting to Validation Set)
섹션 제목: “5. 검증 세트 과적합 (Overfitting to Validation Set)”반복적인 하이퍼파라미터 튜닝으로 validation set에 과적합되는 문제. 번의 튜닝 시도를 하면, 검증 세트에서의 최적 성능은 실제보다 낙관적으로 추정된다:
- : 하이퍼파라미터 조합의 시도 횟수
- : 검증 세트 크기
- : 검증 세트에서 가장 좋았던 성능
이 부등식(union bound + Hoeffding)은 시도 횟수 이 증가할수록 과적합 위험이 으로 커짐을 보여준다.
해결:
- Nested cross-validation
- 별도의 최종 test set 유지
- 튜닝 횟수를 제한하고 기록
6. Baseline 미설정
섹션 제목: “6. Baseline 미설정”복잡한 모델부터 시작하면 개선 여부를 판단할 수 없다.
규칙: 항상 가장 간단한 모델로 baseline을 설정한 후, 복잡성을 늘려라.
7. 분할 후 특성 공학
섹션 제목: “7. 분할 후 특성 공학”| 실수 | 결과 |
|---|---|
| 전체 데이터에서 target encoding | 데이터 누수 |
| 전체 데이터에서 feature selection | 테스트 정보 유입 |
| 전체 데이터에서 스케일링 fit | 미미하지만 원칙적 누수 |
올바른 순서: 분할 → 학습 데이터로만 전처리 fit → 테스트에 transform
8. Training-Serving Skew
섹션 제목: “8. Training-Serving Skew”학습 시와 배포 시의 데이터 전처리 불일치. 누수 탐지를 위해 학습 세트와 테스트 세트의 특성 분포 차이를 정량적으로 측정할 수 있다. PSI (Population Stability Index)는 두 분포 간의 안정성을 측정한다:
- : 구간(bin)의 수
- : 실제(서빙) 데이터에서 번째 구간의 비율
- : 학습 데이터에서 번째 구간의 비율
- : 안정, : 주의, : 분포 변화 심각
원인: 학습은 Python/pandas, 서빙은 Java/C++ 등 다른 환경에서 전처리
해결: 전처리 파이프라인을 모델과 함께 직렬화 (sklearn Pipeline, ONNX 등)
숫자로 이해하기
섹션 제목: “숫자로 이해하기”여러 모델을 비교하다 보면 우연히 좋아 보이는 것이 나온다. 그 확률을 계산해 보자.
유의수준 로 독립적인 검정을 번 하면, 최소 한 번은 위양성이 나올 확률은:
검정 횟수 계산 위양성 확률 1 5.0% 10 40.1% 20 64.2% 100 99.4% 모델 20개를 비교하면 셋 중 두 번꼴로 “유의하게 더 좋은” 모델이 우연히 나온다. 실제로는 아무 차이가 없는데도 말이다.
Bonferroni 보정은 기준선을 나눠 이를 막는다:
실무적 함의: 검증셋을 보며 하이퍼파라미터를 100번 만지는 것도 사실상 100번의 검정이다. 그래서 테스트셋은 마지막에 딱 한 번만 열어야 한다.
상세 내용
섹션 제목: “상세 내용”실수 방지 체크리스트
섹션 제목: “실수 방지 체크리스트”Before/After 비교
섹션 제목: “Before/After 비교”| 실수 | Before (잘못된 방법) | After (올바른 방법) |
|---|---|---|
| 전처리 순서 | scale(전체) → split → train | split → scale.fit(train) → transform |
| 시계열 분할 | random_split(data) | data[:cutoff], data[cutoff:] |
| SMOTE 시점 | SMOTE(전체) → cross_val | for fold: SMOTE(train_fold) |
| 성능 평가 | accuracy = 99% → 배포 | AUPRC, F1 확인 → 오류 분석 → 배포 |
| Baseline | 바로 딥러닝 시작 | 로지스틱 회귀 → XGBoost → 딥러닝 |
언제 사용하는가
섹션 제목: “언제 사용하는가”이 체크리스트는 모든 ML 프로젝트에서 참고해야 한다. 특히:
- 새로운 프로젝트를 시작할 때
- 모델 성능이 비현실적으로 높을 때
- 프로덕션 배포 전 최종 검증 시
- 코드 리뷰 시
- 팀에 새로운 멤버가 합류할 때
실전 사례
섹션 제목: “실전 사례””8가지 실수를 모두 한 스타트업”
섹션 제목: “”8가지 실수를 모두 한 스타트업””한 헬스케어 AI 스타트업이 환자 재입원 예측(Patient Readmission Prediction) 모델을 개발하면서, 이 문서에서 다루는 거의 모든 실수를 경험한 사례이다.
실수 타임라인:
| 순서 | 실수 | 구체적 상황 | 결과 |
|---|---|---|---|
| 1 | Baseline 미설정 | ”딥러닝으로 시작해야 투자자에게 보여줄 수 있다”며 바로 LSTM 모델 구축 | 개선 기준 없이 3개월 소비 |
| 2 | 데이터 몰래 보기 | 테스트 세트 결과를 보고 모델 구조를 반복 수정 | 테스트 정확도 96% 달성 (과대 추정) |
| 3 | 부적절한 검증 | 시계열 특성의 환자 데이터에 Random Split 사용 | 미래 데이터가 학습에 포함 |
| 4 | 분할 후 특성 공학 | 전체 데이터에서 Target Encoding 수행 | 심각한 데이터 누수 |
| 5 | 클래스 불균형 무시 | 재입원 비율 8%인 데이터에서 Accuracy 92%에 만족 | 실제로는 “재입원 없음”만 예측 |
| 6 | 잘못된 지표 선택 | Accuracy만 보고하며 “우수한 모델” 주장 | 재입원 환자 Recall 0% |
| 7 | 검증 세트 과적합 | 200번 이상의 하이퍼파라미터 튜닝 | 검증 성능과 실제 성능 간 큰 괴리 |
| 8 | Training-Serving Skew | Python 전처리를 Java로 재구현하면서 날짜 파싱 차이 | 프로덕션 성능 급락 |
최종 결과:
- 테스트 세트 성능: 정확도 96%, F1 0.89 (실험실 환경)
- 프로덕션 성능: 정확도 71%, F1 0.23 (실제 환경)
- 투자된 비용: $1.2M (12개월)
- 실제 가치: 배포 후 2주 만에 서비스 중단
수정 후: 외부 컨설턴트의 감사를 거쳐, 올바른 Time-based Split + Stratified K-Fold + Pipeline 기반 전처리 + 로지스틱 회귀 베이스라인부터 다시 시작했다. 2개월 만에 프로덕션에서 안정적으로 동작하는 모델(F1 0.67)을 배포했으며, 이 모델이 처음의 “96% 정확도” 모델보다 실제 환경에서 압도적으로 유용했다.
직접 해보기
섹션 제목: “직접 해보기”import numpy as npfrom sklearn.datasets import make_classificationfrom sklearn.preprocessing import StandardScalerfrom sklearn.linear_model import LogisticRegressionfrom sklearn.pipeline import make_pipelinefrom sklearn.model_selection import cross_val_score, train_test_splitfrom sklearn.feature_selection import SelectKBest, f_classif
X, y = make_classification(n_samples=500, n_features=2000, n_informative=5, random_state=0)Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=.3, random_state=0)clf = lambda: make_pipeline(StandardScaler(), LogisticRegression(max_iter=2000))
# ❌ 누수: 전체 데이터(정답 포함)로 특성을 먼저 고른 뒤 교차검증sel = SelectKBest(f_classif, k=20).fit(X, y)leaked = cross_val_score(clf(), sel.transform(X), y, cv=5).mean()
# ✅ 올바름: 특성 선택도 파이프라인 안에 넣어 폴드마다 다시 수행pipe = make_pipeline(SelectKBest(f_classif, k=20), StandardScaler(), LogisticRegression(max_iter=2000))correct = cross_val_score(pipe, X, y, cv=5).mean()honest = pipe.fit(Xtr, ytr).score(Xte, yte)
print(f"❌ 전체로 특성 선택 후 CV : {leaked:.3f}")print(f"✅ 파이프라인 안에서 CV : {correct:.3f}")print(f"🎯 완전히 분리된 테스트셋 : {honest:.3f}")print(f"\n누수로 부풀려진 정도 = {leaked - correct:+.3f}")
# 실행 결과:# ❌ 전체로 특성 선택 후 CV : 0.862# ✅ 파이프라인 안에서 CV : 0.824# 🎯 완전히 분리된 테스트셋 : 0.780## 누수로 부풀려진 정도 = +0.038특성 선택을 교차 검증 밖에서 한 번 하는 것만으로 성능이 0.038 부풀려진다. 2,000개 특성 중 정답과 상관 높은 20개를 전체 데이터를 보고 골랐으니, 검증 폴드의 정답이 이미 특성 선택 과정에 새어 들어간 것이다. 전처리와 특성 선택은 반드시 파이프라인 안에 넣어야 한다.
흔한 오해와 함정
섹션 제목: “흔한 오해와 함정”-
“나는 경험이 많으니 이런 실수를 하지 않는다”: 압박과 시간 제약 하에서 누구나 실수할 수 있다. 체크리스트와 자동화된 파이프라인으로 방지하라.
-
“한 번 확인했으니 됐다”: 코드 변경, 데이터 업데이트 시 새로운 누수가 발생할 수 있다. CI/CD에 검증을 통합하라.
-
“Kaggle 대회와 실무는 같다”: 대회에서는 누수 특성도 활용하지만, 실무에서는 배포 시점의 특성 사용 가능성을 반드시 확인해야 한다.
-
“검증 성능이 좋으니 배포해도 된다”: 프로덕션에서는 데이터 drift, 지연시간, 공정성 등 검증 성능 이외의 요소도 중요하다.
스스로 점검하기
섹션 제목: “스스로 점검하기”1. 테스트셋을 여러 번 들여다보면 무엇이 문제인가?
정답 보기
볼 때마다 사실상 검정을 한 번씩 하는 셈이라, 테스트셋에 간접적으로 과적합된다. 결국 그 점수도 낙관적으로 편향된다. 테스트셋은 마지막에 한 번만 연다.
2. 검증 성능이 지나치게 좋을 때 가장 먼저 의심할 것은?
정답 보기
데이터 누수다. 전처리를 분할 전에 했는지, 정답에서 파생된 특성이 섞였는지, 시간 순서가 지켜졌는지, 중복 샘플이 훈련·검증에 나눠 들어갔는지 확인한다.
3. 성능 개선이 유의미한지 어떻게 판단하는가?
정답 보기
단일 숫자 비교가 아니라 교차 검증의 폴드 간 분산과 비교하고, 대응 표본 검정으로 확인한다. 여러 모델을 비교했다면 다중 비교 보정도 고려한다.
다른 주제와의 연결
섹션 제목: “다른 주제와의 연결”- 데이터 누수: 가장 흔한 실수의 상세 설명
- 불균형 데이터: 지표 선택의 중요성
- ML 파이프라인 설계: 올바른 파이프라인으로 실수 방지
- 실험 추적: 재현 가능한 실험
- 모델 배포: Training-Serving Skew
- 윤리적 AI: 공정성 관련 실수
- ML 시스템 설계 패턴: 프로덕션 운영의 실수