Programming/AI & ML

[OUTTA Alpha팀 Medical AI& 3D Vision 스터디] 기초 및 심화 머신러닝 5(Scikit-learn의 Model Selection)

YeonJuJeon 2025. 1. 11. 14:59

1. Scikit-learn의 Model Selection

  • 개념
    • Scikit-learn의 model_selection 모듈은 학습/테스트 데이터 분할, 교차 검증, 하이퍼파라미터 튜닝 등을 위한 다양한 함수와 클래스를 제공함.
    • 대표적으로 train_test_split, KFold, StratifiedKFold, GridSearchCV, cross_val_score 등이 있음.

1-1. 학습/테스트 데이터 셋 분리 - train_test_split

  1. 중요성
    • 학습 데이터만으로 모델을 학습하고 동일 데이터로 성능을 측정하면, 과적합이 발생할 수 있음.
    • 학습 데이터테스트 데이터를 분리하여 일반화 성능을 평가하는 것이 필수적임.
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-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()

  1. 개념
    • 교차 검증을 좀 더 간단히 수행할 수 있도록 Scikit-learn에서 제공하는 함수.
    • 내부적으로 (KFold or StratifiedKFold) + (반복 학습 & 예측)를 자동으로 실행하고, 평가 스코어 리스트를 반환해 줌.
  2. 함수 시그니처
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

GridSearchCV 최적 파라미터 :  {'max_depth': 3, 'min_samples_split': 2}
GridSearchCV 최고 정확도 : 0.9750
테스트 데이터 세트 정확도 : 0.9667

코드 리뷰 & 요약

  1. train_test_split
    • 학습/테스트 데이터를 무작위로 분리.
    • test_size로 비율 설정, random_state로 재현성 확보.
    • 분류 문제에서 클래스 불균형이 있다면, stratify=y 인자 사용 고려.
  2. KFold & StratifiedKFold
    • KFold: 데이터 세트를 K개 폴드로 나눈 뒤 교차 검증 수행.
    • StratifiedKFold: 분류 문제에서 계층화(stratified) 방식으로 폴드 분할 → 불균형 클래스에 적합.
    • 교차 검증을 통해 데이터 편중을 줄이고, 모델 일반화 성능을 객관적으로 평가 가능.
  3. cross_val_score
    • 내부적으로 **(KFold/StratifiedKFold + 반복 학습/평가)**를 자동화하고, 각 폴드별 점수를 리스트로 반환.
    • 사용이 간단해 빠르게 교차 검증 결과 확인 가능.
  4. GridSearchCV
    • 교차 검증하이퍼파라미터 탐색동시에 진행.
    • **param_grid**에 정의된 파라미터 조합을 모두 시도 → 최적 조합(best_params_) 확인.
    • best_estimator_로 최종 테스트 세트 예측 가능 → 편리하고 일관된 워크플로우.
    • 단점: 조합이 많을 경우 연산량 급증 → 시간/자원 고려 필요.

결론

  • Scikit-learn의 model_selection 모듈데이터 분할, 교차 검증, 하이퍼파라미터 튜닝 등을 체계적이고 간편하게 수행할 수 있도록 도와줌.
  • 교차 검증 결과를 기반으로 최적의 모델(파라미터 포함)을 찾은 뒤, 테스트 세트에서 최종 성능을 평가하는 프로세스권장됨.
  • 특히 GridSearchCV(또는 RandomizedSearchCV 등)를 활용하면, 모델 성능 개선과 최적화를 손쉽게 달성할 수 있음.