Skip to content

Sklearn train_test_split: Python에서 머신러닝 데이터 분할하기

Updated on

평가에 사용하는 것과 동일한 데이터로 머신러닝 모델을 훈련하면 오해의 소지가 있는 높은 정확도가 나옵니다. 모델이 일반화 가능한 패턴을 학습하는 대신 훈련 데이터를 암기합니다 -- 이것을 과적합이라고 합니다. 정직한 성능 지표를 얻으려면 모델이 훈련 중에 한 번도 보지 못한 별도의 테스트 세트가 필요합니다.

Scikit-learn의 train_test_split()은 데이터셋을 훈련과 테스트 부분으로 나누는 표준 방법입니다. 배열, DataFrame, 희소 행렬을 처리하며, 층화 추출, 재현성, 사용자 정의 분할 비율 옵션을 제공합니다.

📚

기본 사용법

from sklearn.model_selection import train_test_split
import numpy as np
 
# 샘플 데이터: 100개 샘플, 5개 특성
X = np.random.randn(100, 5)
y = np.random.randint(0, 2, 100)  # 이진 레이블
 
# 분할: 80% 훈련, 20% 테스트
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
 
print(f"훈련 세트: {X_train.shape[0]} 샘플")
print(f"테스트 세트: {X_test.shape[0]} 샘플")
# 훈련 세트: 80 샘플
# 테스트 세트: 20 샘플

주요 매개변수

매개변수기본값설명
test_size0.25테스트 샘플의 비율(0.0-1.0) 또는 절대 수
train_sizeNone훈련 샘플의 비율 또는 수 (test_size의 보수)
random_stateNone재현 가능한 분할을 위한 시드
shuffleTrue분할 전 데이터 셔플 여부
stratifyNone층화 분할에 사용할 배열

Pandas DataFrame과 함께 사용

from sklearn.model_selection import train_test_split
import pandas as pd
 
df = pd.DataFrame({
    'age': [25, 30, 35, 40, 45, 50, 55, 60, 28, 33],
    'income': [40, 50, 60, 70, 80, 90, 100, 110, 45, 55],
    'purchased': [0, 0, 1, 1, 1, 1, 1, 1, 0, 0]
})
 
X = df[['age', 'income']]
y = df['purchased']
 
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)
 
print(f"훈련 형태: {X_train.shape}")  # (7, 2)
print(f"테스트 형태: {X_test.shape}")   # (3, 2)
print(type(X_train))  # <class 'pandas.core.frame.DataFrame'>

분할 후에도 DataFrame은 DataFrame으로 유지됩니다 -- 열 이름과 인덱스가 보존됩니다.

random_state: 재현 가능한 분할

random_state 없이는 매번 다른 분할이 됩니다:

from sklearn.model_selection import train_test_split
import numpy as np
 
X = np.arange(10).reshape(5, 2)
y = np.array([0, 0, 1, 1, 1])
 
# random_state 없이: 매번 다른 분할
_, X_test1, _, _ = train_test_split(X, y, test_size=0.4)
_, X_test2, _, _ = train_test_split(X, y, test_size=0.4)
print(np.array_equal(X_test1, X_test2))  # 아마 False
 
# random_state 있으면: 매번 같은 분할
_, X_test3, _, _ = train_test_split(X, y, test_size=0.4, random_state=42)
_, X_test4, _, _ = train_test_split(X, y, test_size=0.4, random_state=42)
print(np.array_equal(X_test3, X_test4))  # True

재현성을 위해 항상 random_state를 설정하세요. 아무 정수나 사용하세요 -- 42가 관례이지만 구체적인 숫자는 중요하지 않습니다.

층화 분할

불균형 데이터셋에서는 랜덤 분할이 대부분의 소수 클래스 샘플을 한 세트에 넣을 수 있습니다. 층화는 두 세트가 같은 클래스 비율을 갖도록 보장합니다:

from sklearn.model_selection import train_test_split
import numpy as np
from collections import Counter
 
# 불균형 데이터셋: 90% 클래스 0, 10% 클래스 1
np.random.seed(42)
X = np.random.randn(200, 4)
y = np.array([0] * 180 + [1] * 20)
 
# 층화 없이
_, _, y_train_bad, y_test_bad = train_test_split(
    X, y, test_size=0.2, random_state=42
)
print("층화 없이:")
print(f"  훈련: {Counter(y_train_bad)}")
print(f"  테스트: {Counter(y_test_bad)}")
 
# 층화 있으면
_, _, y_train_good, y_test_good = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)
print("\nstratify=y 사용:")
print(f"  훈련: {Counter(y_train_good)}")
print(f"  테스트: {Counter(y_test_good)}")
# 훈련: Counter({0: 144, 1: 16})  -- 10% 클래스 1
# 테스트: Counter({0: 36, 1: 4})    -- 10% 클래스 1

언제 층화를 사용해야 하나

시나리오층화 사용?
균형 잡힌 클래스 (50/50)선택 사항
불균형 클래스 (90/10)
다중 클래스 분류
회귀 (연속 타겟)아니오 (지원 안 함)
작은 데이터셋 (< 100 샘플)예 (빈 클래스 방지)

훈련/검증/테스트 분할

하이퍼파라미터 튜닝에는 훈련, 검증, 테스트의 세 세트가 필요합니다. train_test_split을 두 번 호출합니다:

from sklearn.model_selection import train_test_split
import numpy as np
 
X = np.random.randn(1000, 10)
y = np.random.randint(0, 3, 1000)
 
# 첫 번째 분할: 80% 훈련+검증, 20% 테스트
X_temp, X_test, y_temp, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)
 
# 두 번째 분할: 75% 훈련, 25% 검증 (80%의 25% = 전체 60/20/20)
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
)
 
print(f"훈련:      {X_train.shape[0]} 샘플 (60%)")
print(f"검증:      {X_val.shape[0]} 샘플 (20%)")
print(f"테스트:    {X_test.shape[0]} 샘플 (20%)")

완전한 ML 파이프라인 예제

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
import numpy as np
 
# 샘플 데이터 생성
np.random.seed(42)
X = np.random.randn(500, 8)
y = (X[:, 0] + X[:, 1] > 0).astype(int)
 
# 데이터 분할
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)
 
# 특성 스케일링 (훈련 세트에서만 fit!)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)  # 훈련 통계량 사용
 
# 모델 훈련
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train_scaled, y_train)
 
# 평가
y_pred = model.predict(X_test_scaled)
print(f"정확도: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred))

중요한 규칙: 스케일러(및 모든 전처리)는 훈련 세트에서만 fit하세요. 동일한 변환을 테스트 세트에 적용합니다. 전체 데이터셋에서 fit하면 데이터 누수가 발생합니다.

일반적인 분할 비율

분할훈련테스트언제 사용
80/2080%20%기본 선택, 대부분의 데이터셋
70/3070%30%작은 데이터셋, 더 큰 테스트 세트 필요
90/1090%10%큰 데이터셋 (10k+ 샘플)
60/20/2060%20% 검증 + 20% 테스트하이퍼파라미터 튜닝 시

모델 결과 탐색

분할하고 모델을 훈련한 후, PyGWalker (opens in a new tab)를 사용하면 Jupyter에서 예측 vs 실제 값, 훈련/테스트 세트 간의 특성 분포, 오류 패턴을 인터랙티브하게 탐색할 수 있습니다:

import pandas as pd
import pygwalker as pyg
 
results = pd.DataFrame({
    'actual': y_test,
    'predicted': y_pred,
    'correct': y_test == y_pred
})
walker = pyg.walk(results)

자주 묻는 질문

sklearn의 train_test_split은 무엇을 하나요?

train_test_split()은 배열이나 DataFrame을 무작위로 훈련용과 테스트용 두 하위 집합으로 나눕니다. 모델 평가가 훈련 중 보지 못한 데이터를 사용하도록 보장하여 정직한 성능 추정치를 제공합니다.

최적의 훈련/테스트 분할 비율은?

80/20이 표준 기본값입니다. 신뢰할 수 있는 테스트 세트가 필요한 작은 데이터셋에는 70/30을, 큰 데이터셋(10k+ 샘플)에는 90/10을 사용합니다. 하이퍼파라미터 튜닝에는 60/20/20(훈련/검증/테스트)을 사용합니다.

train_test_split에서 random_state는 무엇을 하나요?

random_state는 분할 전 셔플링의 랜덤 시드를 설정합니다. 같은 random_state 값을 사용하면 매번 같은 분할이 생성되어 결과를 재현할 수 있습니다. 아무 정수나 사용할 수 있습니다.

train_test_split에서 stratify는 언제 사용해야 하나요?

타겟 변수가 불균형하거나(예: 95% 음성, 5% 양성) 데이터셋이 작을 때 stratify=y를 사용합니다. 층화는 훈련 세트와 테스트 세트 모두 각 클래스의 같은 비율을 갖도록 보장합니다.

데이터를 훈련, 검증, 테스트 세트로 나누려면?

train_test_split을 두 번 호출합니다. 먼저 훈련+검증과 테스트로 분할(예: 80/20). 그다음 훈련+검증을 훈련과 검증으로 분할(예: 80%의 75/25로 전체 60/20/20). 또는 교차 검증을 위해 sklearn.model_selection.KFold를 사용합니다.

결론

train_test_split()은 Python에서 모든 머신러닝 워크플로의 기반입니다. 항상 훈련 전에 사용하세요 -- 절대 훈련 데이터에서 평가하지 마세요. 재현성을 위해 random_state를 설정하고, 불균형 클래스에는 stratify=y를 사용하며, 전처리 단계는 훈련 세트에서만 fit해야 한다는 것을 기억하세요. 모델 선택을 위해서는 3방향 분할(훈련/검증/테스트)하거나 더 강건한 추정을 위해 교차 검증을 사용하세요.

📚