1. Scikit-learn의 Model Selection
- 개념
- Scikit-learn의 model_selection 모듈은 학습/테스트 데이터 분할, 교차 검증, 하이퍼파라미터 튜닝 등을 위한 다양한 함수와 클래스를 제공함.
- 대표적으로 train_test_split, KFold, StratifiedKFold, GridSearchCV, cross_val_score 등이 있음.
1-1. 학습/테스트 데이터 셋 분리 - train_test_split
- 중요성
- 학습 데이터만으로 모델을 학습하고 동일 데이터로 성능을 측정하면, 과적합이 발생할 수 있음.
- 학습 데이터와 테스트 데이터를 분리하여 일반화 성능을 평가하는 것이 필수적임.
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
# 데이터 로드
iris_data = load_iris()
X = iris_data.data
y = iris_data.target
# train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.3,
random_state=121)
# 모델 학습 & 예측
dt_clf = DecisionTreeClassifier()
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)
# 정확도 확인
print('예측 정확도: {:.4f}'.format(accuracy_score(y_test, pred)))
- 주요 파라미터
- test_size: 테스트 데이터셋 비율/크기 (예: 0.3 → 30%를 테스트로 사용).
- shuffle: 분할 전 데이터를 섞을지 여부(기본값=True).
- random_state: 재현성 유지 위해 난수 시드를 고정.
1-2. 교차 검증(Cross Validation)
- 왜 필요한가?
- 단순히 한 번의 학습/테스트 분할만으로는 데이터 편중이나 과적합 여부를 완전히 확인하기 어려움.
- 교차 검증은 데이터를 여러 번 나누어 학습/검증을 반복함으로써 모델의 안정적 성능을 평가하고, 하이퍼파라미터 튜닝에 도움을 줌.
- 프로세스
- 데이터를 여러 세트(폴드)로 나눈 뒤, 각각을 번갈아 가며 검증 세트로 사용.
- 각 폴드에서의 평가 점수를 평균 내어 최종 모델 성능으로 삼음.
- 일반적으로 데이터셋을 학습용, 검증용, 테스트용으로 세분화하는 방식도 존재.
1-2 (1). K-폴드 교차 검증
- 기본 아이디어
- 데이터를 K등분(K개의 폴드) 후, K번의 학습과 검증을 진행.
- 매번 1개 폴드를 검증용, 나머지(K-1) 폴드를 학습용으로 사용.
- 평가 점수 = (각 폴드 검증 점수)들의 평균.
from sklearn.model_selection import KFold
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
import numpy as np
iris = load_iris()
features = iris.data
label = iris.target
dt_clf = DecisionTreeClassifier(random_state=156)
kfold = KFold(n_splits=5) # 5 폴드
cv_accuracy = []
n_iter = 0
for train_index, test_index in kfold.split(features):
# 훈련/검증 데이터 분할
X_train, X_test = features[train_index], features[test_index]
y_train, y_test = label[train_index], label[test_index]
# 모델 학습 & 예측
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)
# 정확도 산출
accuracy = np.round(accuracy_score(y_test, pred), 4)
cv_accuracy.append(accuracy)
n_iter += 1
print(f"\n#{n_iter} 교차 검증 정확도 : {accuracy}, "
f"학습 데이터 크기: {X_train.shape[0]}, 검증 데이터 크기: {X_test.shape[0]}")
print(f"#{n_iter} 검증 세트 인덱스 : {test_index}")
print("\n## 평균 검증 정확도:", np.mean(cv_accuracy))
더보기
#1 교차 검증 정확도 :1.0, 학습 데이터 크기: 120, 검증 데이터 크기: 30
#1 검증 세트 인덱스:[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
24 25 26 27 28 29]
#2 교차 검증 정확도 :0.9667, 학습 데이터 크기: 120, 검증 데이터 크기: 30
#2 검증 세트 인덱스:[30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
54 55 56 57 58 59]
#3 교차 검증 정확도 :0.8667, 학습 데이터 크기: 120, 검증 데이터 크기: 30
#3 검증 세트 인덱스:[60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
84 85 86 87 88 89]
#4 교차 검증 정확도 :0.9333, 학습 데이터 크기: 120, 검증 데이터 크기: 30
#4 검증 세트 인덱스:[ 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
108 109 110 111 112 113 114 115 116 117 118 119]
#5 교차 검증 정확도 :0.7333, 학습 데이터 크기: 120, 검증 데이터 크기: 30
#5 검증 세트 인덱스:[120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
138 139 140 141 142 143 144 145 146 147 148 149]
- 주의사항
- KFold는 단순하게 폴드를 나누므로, 레이블 분포가 불균형한 분류 문제에선 사용 시 문제가 생길 수 있음(편향된 분배 가능).
1-2 (2). Stratified K 폴드
- Stratified KFold
- 불균형 클래스 분포를 다룰 때, 각 폴드가 전체 데이터의 클래스 분포를 최대한 동일하게 유지하도록 분할.
- 분류(Classification) 문제에서 더 권장되는 방식.
from sklearn.model_selection import StratifiedKFold
import pandas as pd
iris = load_iris()
iris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
iris_df['label'] = iris.target
skf = StratifiedKFold(n_splits=3)
n_iter = 0
for train_index, test_index in skf.split(iris_df, iris_df['label']):
n_iter += 1
label_train = iris_df['label'].iloc[train_index]
label_test = iris_df['label'].iloc[test_index]
print('## 교차 검증:', n_iter)
print('학습 레이블 분포:\n', label_train.value_counts())
print('검증 레이블 분포:\n', label_test.value_counts())
더보기
## 교차 검증: 1
학습 레이블 데이터 분포:
label
2 34
0 33
1 33
Name: count, dtype: int64
검증 레이블 데이터 분포 :
label
0 17
1 17
2 16
Name: count, dtype: int64
## 교차 검증: 2
학습 레이블 데이터 분포:
label
1 34
0 33
2 33
Name: count, dtype: int64
검증 레이블 데이터 분포 :
label
0 17
2 17
1 16
Name: count, dtype: int64
## 교차 검증: 3
학습 레이블 데이터 분포:
label
0 34
1 33
2 33
Name: count, dtype: int64
검증 레이블 데이터 분포 :
label
1 17
2 17
0 16
Name: count, dtype: int64
1-3. cross_val_score()
- 개념
- 교차 검증을 좀 더 간단히 수행할 수 있도록 Scikit-learn에서 제공하는 함수.
- 내부적으로 (KFold or StratifiedKFold) + (반복 학습 & 예측)를 자동으로 실행하고, 평가 스코어 리스트를 반환해 줌.
- 함수 시그니처
cross_val_score(estimator, X, y=None, scoring=None, cv=None, ...)
- estimator: 분류/회귀 모델 객체 (ex. DecisionTreeClassifier, LinearRegression 등)
- X, y: 피처, 타깃 데이터
- scoring: 예측 성능 평가 지표 (ex. 'accuracy', 'f1', 'roc_auc' 등)
- cv: 폴드 수 혹은 폴드 객체 (ex. 5 or StratifiedKFold(...))
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_iris
import numpy as np
iris_data = load_iris()
dt_clf = DecisionTreeClassifier(random_state=156)
data = iris_data.data
label = iris_data.target
scores = cross_val_score(dt_clf, data, label, scoring='accuracy', cv=3)
print('교차 검증별 정확도:', np.round(scores, 4))
print('평균 검증 정확도:', np.round(np.mean(scores), 4))
교차 검증별 정확도: [0.98 0.94 0.98]
평균 검증 정확도: 0.9667
1-4. GridSearchCV
- 1-4. GridSearchCV
- 교차 검증(Cross Validation)과 최적 하이퍼파라미터 튜닝을 동시에 수행하는 클래스.
- 사용자 정의 하이퍼파라미터 조합을 순차적으로 테스트하여, 가장 높은 성능을 내는 파라미터를 찾음.
- 파라미터 후보가 많아질수록, 교차 검증과 조합 테스트로 인해 연산 비용이 크게 증가할 수 있음.
- 최적 파라미터(best_params_)와 교차 검증 시 최고 점수(best_score_), 그리고 재학습된 최적 모델(best_estimator_)를 얻을 수 있음.
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.metrics import accuracy_score
import pandas as pd
# 1) 데이터 로드 & 분할
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
iris.data,
iris.target,
test_size=0.2,
random_state=121
)
# 2) 모델 & 파라미터 설정
dtree = DecisionTreeClassifier()
parameters = {'max_depth': [1, 2, 3],
'min_samples_split': [2, 3]}
# 3) GridSearchCV 생성
grid_dtree = GridSearchCV(
estimator=dtree,
param_grid=parameters,
cv=3, # 3-Fold 교차검증
refit=True # 최적 파라미터로 재학습
)
# 4) 모델 학습(교차 검증+파라미터 탐색 동시에 진행)
grid_dtree.fit(X_train, y_train)
# 5) 결과 분석
scores_df = pd.DataFrame(grid_dtree.cv_results_)
print(scores_df[['params', 'mean_test_score',
'rank_test_score',
'split0_test_score',
'split1_test_score',
'split2_test_score']])
print('GridSearchCV 최적 파라미터:', grid_dtree.best_params_)
print('GridSearchCV 최고 정확도: {:.4f}'.format(grid_dtree.best_score_))
# 6) 최적 모델로 테스트 세트 예측
estimator = grid_dtree.best_estimator_
pred = estimator.predict(X_test)
print('테스트 세트 정확도: {:.4f}'.format(accuracy_score(y_test, pred)))
GridSearchCV 최적 파라미터 : {'max_depth': 3, 'min_samples_split': 2}
GridSearchCV 최고 정확도 : 0.9750
테스트 데이터 세트 정확도 : 0.9667
코드 리뷰 & 요약
- train_test_split
- 학습/테스트 데이터를 무작위로 분리.
- test_size로 비율 설정, random_state로 재현성 확보.
- 분류 문제에서 클래스 불균형이 있다면, stratify=y 인자 사용 고려.
- KFold & StratifiedKFold
- KFold: 데이터 세트를 K개 폴드로 나눈 뒤 교차 검증 수행.
- StratifiedKFold: 분류 문제에서 계층화(stratified) 방식으로 폴드 분할 → 불균형 클래스에 적합.
- 교차 검증을 통해 데이터 편중을 줄이고, 모델 일반화 성능을 객관적으로 평가 가능.
- cross_val_score
- 내부적으로 **(KFold/StratifiedKFold + 반복 학습/평가)**를 자동화하고, 각 폴드별 점수를 리스트로 반환.
- 사용이 간단해 빠르게 교차 검증 결과 확인 가능.
- GridSearchCV
- 교차 검증과 하이퍼파라미터 탐색을 동시에 진행.
- **param_grid**에 정의된 파라미터 조합을 모두 시도 → 최적 조합(best_params_) 확인.
- best_estimator_로 최종 테스트 세트 예측 가능 → 편리하고 일관된 워크플로우.
- 단점: 조합이 많을 경우 연산량 급증 → 시간/자원 고려 필요.
결론
- Scikit-learn의 model_selection 모듈은 데이터 분할, 교차 검증, 하이퍼파라미터 튜닝 등을 체계적이고 간편하게 수행할 수 있도록 도와줌.
- 교차 검증 결과를 기반으로 최적의 모델(파라미터 포함)을 찾은 뒤, 테스트 세트에서 최종 성능을 평가하는 프로세스가 권장됨.
- 특히 GridSearchCV(또는 RandomizedSearchCV 등)를 활용하면, 모델 성능 개선과 최적화를 손쉽게 달성할 수 있음.