본문 바로가기
두두 IT/파이썬

[머신러닝 3-2] 데이터셋 분할 & 교차 검증

by DoDo's 2026. 5. 12.
반응형

📊 [머신러닝 필수] 내 모델의 진짜 실력을 평가하라! 데이터셋 분할 & 교차 검증 완벽 가이드

머신러닝 모델을 만들 때 가장 뼈아픈 실수가 무엇일까요? 바로 자기가 공부한 문제만 잘 맞히고, 새로운 데이터(실전)에서는 바보가 되어버리는 '과적합(Overfitting)' 현상입니다. 이를 막기 위해 데이터 과학자들은 데이터를 아주 전략적으로 쪼개서 사용합니다.


1. 데이터의 3대 왕국: Train / Validation / Test

우리가 가진 데이터를 무작정 학습에 다 써버리면 안 됩니다. 모델을 제대로 평가하려면 데이터를 3가지 용도로 나누어야 합니다.

  1. Train 데이터셋 (학습용): 모델이 규칙을 찾고 공부하는 '교과서'입니다.
  2. Validation 데이터셋 (검증용): 모델의 설정(하이퍼파라미터)을 바꿔가며 성능을 끌어올릴 때 보는 '모의고사'입니다.
  3. Test 데이터셋 (평가용): 모델 튜닝이 완전히 끝난 후, 마지막에 딱 한 번만 실력을 측정하는 '수능 시험지'입니다.

💡 왜 Validation 세트가 따로 필요할까요?

만약 Test 세트로 성능을 확인하고 계속 설정을 바꾼다면, 결국 우리 모델은 Test 세트에만 억지로 끼워 맞춰진(과적합된) 엉터리 모델이 되어버립니다. 따라서 학습 중에는 Validation 세트로만 평가하고, Test 세트는 절대 건드리지 않는 것이 핵심입니다!

💻 코드 실습: 3개의 데이터셋으로 쪼개기

scikit-learn의 train_test_split()을 두 번 연속 사용해서 데이터를 나눕니다.

Python
 
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

X, y = load_iris(return_X_y=True)

# 1. 먼저 Train과 Test를 8:2로 분리합니다.
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=0
)

# 2. 남은 Train 데이터를 다시 Train과 Validation으로 8:2로 쪼갭니다.
X_train, X_val, y_train, y_val = train_test_split(
    X_train, y_train, test_size=0.2, stratify=y_train, random_state=0
)

print(X_train.shape, X_val.shape, X_test.shape)

▶️ 실행 결과:

((96, 4), (24, 4), (30, 4)) (전체 150개의 데이터가 96개, 24개, 30개로 나뉘었습니다)


2. 🚨 홀드아웃(Hold-out) 방식의 단점과 제너레이터(Generator)

방금 위에서 한 것처럼 데이터를 단순히 뚝 떼어 나누는 방식을 홀드아웃(Hold-out)이라고 합니다. 데이터 양이 아주 많을 때는 괜찮지만, 데이터가 적을 때는 "어떻게 나뉘느냐"에 따라 운이 크게 작용하여 성능 평가가 들쭉날쭉해지는 단점이 있습니다.

이 단점을 극복하기 위해 '교차 검증'을 배우기 전, 파이썬의 제너레이터(Generator) 개념을 잠깐 짚고 가야 합니다.

💡 제너레이터(yield)란?

return과 달리, 함수를 끝내지 않고 요청(next())이 올 때마다 순차적으로 값을 하나씩 뱉어주는 아주 똑똑한 파이썬 객체입니다.

Python
 
def test_generator(start):
    yield start + 10  # 첫 번째 호출 시 반환
    yield start + 20  # 두 번째 호출 시 반환
    yield start + 30  # 세 번째 호출 시 반환

gen = test_generator(5)
print(next(gen))

▶️ 실행 결과:

15


3. 🔄 공평한 모의고사: K-겹 교차 검증 (K-Fold CV)

홀드아웃의 억울함을 달래기 위해, 데이터를 K개로 쪼갠 뒤 돌아가면서 한 번씩 Validation(모의고사) 역할을 수행하게 만드는 것이 K-Fold 교차 검증입니다.

  • 일반 KFold: 순서대로 데이터를 K등분 합니다. (주로 회귀(Regression) 문제에 사용)
  • StratifiedKFold (층화 K-Fold): 원본 데이터의 정답(Class) 비율을 똑같이 유지하면서 나눕니다. (주로 분류(Classification) 문제에 필수 사용)

💻 코드 실습: KFold 제너레이터 사용하기

앞서 배운 제너레이터 원리에 의해, kfold.split(X)는 한 번에 데이터를 주지 않고 K번 순회하며 (학습용 인덱스, 검증용 인덱스)를 뱉어줍니다.

Python
 
from sklearn.model_selection import KFold
import numpy as np

# 데이터를 5등분으로 나눕니다.
kfold = KFold(n_splits=5)
gen = kfold.split(X) # 제너레이터 생성

# 첫 번째 순서의 인덱스 뽑아보기
train_idx, test_idx = next(gen)
print("학습용 개수:", len(train_idx), "/ 검증용 개수:", len(test_idx))

▶️ 실행 결과:

학습용 개수: 120 / 검증용 개수: 30

 


4. 🪄 마법의 한 줄 코드: cross_val_score() & cross_validate()

위에서 본 복잡한 for문과 제너레이터를 한 방에 해결해 주는 사이킷런의 효자 함수들입니다. 알아서 K번 돌아가며 모델을 평가해 줍니다.

💡 실무 디테일: 왜 하필 neg_를 붙일까요?

회귀 문제에서 오차(Error)는 낮을수록 좋은 것입니다. 하지만 사이킷런의 시스템은 무조건 "숫자가 클수록 성능이 좋다"고 판단하도록 설계되어 있습니다. 그래서 평균 제곱 오차(MSE) 지표를 쓸 때 앞에 마이너스(neg_)를 붙여 음수로 만들어 버립니다. (예: 오차가 10, 5 일 때 마이너스를 붙이면 -10보다 -5가 더 큰 숫자가 되므로 정상 작동함)

💻 코드 실습: cross_validate()로 여러 지표 한 번에 보기

cross_val_score()는 지표를 1개만 볼 수 있지만, cross_validate()를 쓰면 리스트([])로 묶어서 여러 지표를 한 번에 검증할 수 있습니다.

Python
 
from sklearn.model_selection import cross_validate
from sklearn.tree import DecisionTreeRegressor

# 보스턴 집값 데이터라 가정하고 회귀 모델을 만듭니다.
model = DecisionTreeRegressor(max_depth=5, random_state=0)

# K-Fold를 4번(cv=4) 돌리면서 3개의 평가지표를 한 번에 계산!
result_dict = cross_validate(
    estimator=model, 
    X=X_train, y=y_train, 
    scoring=["neg_mean_squared_error", "r2", "neg_mean_absolute_error"], 
    cv=4
)

# 반환값은 딕셔너리입니다. 그중 MSE 결과만 뽑아서 확인!
print(result_dict['test_neg_mean_squared_error'])

▶️ 실행 결과:

[-19.36290091 -25.777347 -58.6167812 -33.3289009 ] (결과값에 음수가 붙어 나옵니다)


📝 [실습 TODO] 배운 내용 점검하기!

아래 미션 코드를 빈 셀에 직접 타이핑하며 실력을 다져보세요!

  • TODO 1 (데이터 나누기): train_test_split을 이용해 변수 X, y를 test_size=0.3 (7:3 비율)으로 나누고, random_state=42로 고정하세요.
  • TODO 2 (단일 교차검증): 붓꽃(Iris)과 같은 '분류' 데이터라면 cross_val_score() 함수의 scoring 파라미터에 어떤 평가지표 문자열을 넣어야 할까요? (힌트: 정답을 맞춘 비율인 'accuracy'를 사용합니다)
  • TODO 3 (다중 교차검증): cross_validate() 함수를 사용할 때 평가지표를 여러 개 넣으려면 파이썬의 어떤 자료형을 사용해서 묶어줘야 하는지 블로그 코드를 보고 복습해 보세요! (힌트: 괄호 모양 []을 생각해보세요)

제공해주신 데이터 과학 교재 내용을 완벽하게 마스터하셨습니다! 추가로 더 궁금하신 점이 있다면 편하게 말씀해 주세요. 😊

반응형