[파이썬 머신러닝 완벽 가이드] 교차검증

4 분 소요

교차검증 (Cross Validation)

학습 데이터를 다시 분할하여 학습 데이터와 학습된 모델의 성능을 평가하는 검증 데이터로 나눠서 검증을 반복하는 방법. 고정된 학습 데이터와 고정된 테스트 데이터만 가지고 학습 및 평가를 하다보면 편향된 모델을 만들게되어 과적합이 발생할 수 있기 때문에 이를 막기 위해서 교차검증을 사용한다.

K-Fold 교차검증

— K개의 폴드 세트를 만들어서 K번의 학습과 검증 평가를 반복 수행

  • 폴드세트 : 데이터셋을 K개의 세트로 나눈 것.
  • 매 학습마다 이미 나눠진 폴드세트 중 하나의 세트를 검증 세트로 정하고 나머지를 학습폴드세트로 하여 학습과 검증평가 진행
  • 검증 평가 값들의 평균을 통해 교차 검증 최종 평가
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold
import numpy as np

# 붓꽃 데이터셋 호출
iris = load_iris()
features = iris.data
label = iris.target

# 의사결정트리 모델 객체 생성
dt_clf = DecisionTreeClassifier()

# 5개의 폴드 세트로 분리하는 KFold 객체 생성
kfold = KFold(n_splits=5)
# 폴드 세트별 정확도를 담을 리스트 객체 생성
cv_accuracy = []

# 반복횟수 확인용
n_iter = 0

# KFold 객체의 split() 호출하면 폴드 별 학습용, 검증용 테스트의 row 인덱스를 array로 반환  
for train_index, test_index  in kfold.split(features):
    # kfold.split()으로 반환된 인덱스를 이용하여 학습용, 검증용 테스트 데이터 추출
    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)
    n_iter += 1
    
    # 반복 시 마다 정확도 측정
    accuracy = np.round(accuracy_score(y_test,pred), 4)
    train_size = X_train.shape[0]
    test_size = X_test.shape[0]
    print('\\n#{0} 교차 검증 정확도 :{1}, 학습 데이터 크기: {2}, 검증 데이터 크기: {3}'
          .format(n_iter, accuracy, train_size, test_size))
    print('#{0} 검증 세트 인덱스:{1}'.format(n_iter,test_index))
    
    cv_accuracy.append(accuracy)
    
# 개별 iteration별 정확도를 합하여 평균 정확도 계산 
print('\\n## 평균 검증 정확도:', np.mean(cv_accuracy))

Stratified K-Fold

— 불균형한 레이블 분포도를 가진 데이터셋을 위한 K폴드 방식

  • 학습 데이터와 검증 데이터 세트가 가지는 레이블 분포도가 유사하도록 폴드세트를 구성.
  • 예를 들어 신용카드 사기를 잡아내야 하는데 그 비율이 100건 중 10건이라면, 어떤 데이터셋에는 사기인 경우가 하나도 없고 어떤 데이터셋에는 사기인 레이블이 몰려있지 않도록 폴드세트의 레이블 구성 비율을 동일하게 나눔.
  • 대부분 분류 모델의 경우 stratified k-fold를 사용하는 것이 좋다
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import StratifiedKFold
import numpy as np
import pandas as pd

# 붓꽃 데이터셋 호출
iris = load_iris()
# 레이블 분포 확인
iris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
iris_df['label']=iris.target
iris_df['label'].value_counts()  # 0:50, 1:50, 2:50

# 3개의 폴드세트로 나누는 Stratified K-Fold 객체 생성
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('## 교차 검증: {0}'.format(n_iter))
    print('학습 레이블 데이터 분포:\\n', label_train.value_counts())
    print('검증 레이블 데이터 분포:\\n', label_test.value_counts())

# 의사결정트리 모델 객체 생성
dt_clf = DecisionTreeClassifier()

skfold = StratifiedKFold(n_splits=3)
n_iter=0
cv_accuracy=[]

# StratifiedKFold의 split() 호출시 반드시 레이블 데이터 셋도 추가 입력 필요  
for train_index, test_index  in skfold.split(features, label):
    # split()으로 반환된 인덱스를 이용하여 학습용, 검증용 테스트 데이터 추출
    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)

    # 반복 시 마다 정확도 측정 
    n_iter += 1
    accuracy = np.round(accuracy_score(y_test,pred), 4)
    train_size = X_train.shape[0]
    test_size = X_test.shape[0]
    
    print('\\n#{0} 교차 검증 정확도 :{1}, 학습 데이터 크기: {2}, 검증 데이터 크기: {3}'
          .format(n_iter, accuracy, train_size, test_size))
    print('#{0} 검증 세트 인덱스:{1}'.format(n_iter,test_index))
    cv_accuracy.append(accuracy)
    
# 교차 검증별 정확도 및 평균 정확도 계산 
print('\\n## 교차 검증별 정확도:', np.round(cv_accuracy, 4))
print('## 평균 검증 정확도:', np.mean(cv_accuracy))

cross_val_score()

— 교차검증을 보다 간편하게 해주는 API. 위에서 했던 for문을 통한 반복을 생략시켜준다.

주요 파라미터

  • estimator : 분류/회귀 모델 객체
  • X : 피처 데이터셋
  • y : 레이블 데이터셋
  • scoring : 평가기준 (평균, 정확도 등)
  • cv : 교차검증 횟수
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score , cross_validate
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

# 성능 지표는 정확도(accuracy) , 교차 검증 세트는 3개 
scores = cross_val_score(dt_clf , data , label , scoring='accuracy',cv=3)
print('교차 검증별 정확도:',np.round(scores, 4))
print('평균 검증 정확도:', np.round(np.mean(scores), 4))

GridSearchCV

— 교차검증과 하이퍼 파라미터 튜닝을 한 번에 해주는 모듈

  • Grid, 격자처럼 모든 경우의 수를 촘촘하게 확인
  • 예를들어 grid_parameters = {‘max_depth’:[1,2,3], ‘min_samples_split’:[2,3]} 이라면 파라미터 별 경우의 수가 3 x 2 =6, 여기에 교차검증을 3회 한다고 하면 6 x 3, 총 18회의 학습/검증을 수행하게 된다.
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

# 데이터를 로딩하고 학습데이터와 테스트 데이터 분리
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris_data.data, iris_data.target, 
                                                    test_size=0.2, random_state=121)
dtree = DecisionTreeClassifier()

# 적용시켜 볼 parameter들을 dictionary 형태로 설정
parameters = {'max_depth':[1, 2, 3], 'min_samples_split':[2,3]}

# param_grid의 하이퍼 파라미터들을 3개의 train, test fold 로 나누어서 테스트 수행 설정  
# refit: True면 학습/검증이 모두 끝난 후 가장 좋은 파라미터 설정으로 재학습 시킴 (디폴트 True)  
grid_dtree = GridSearchCV(dtree, param_grid=parameters, cv=3, refit=True, return_train_score=True)

# 붓꽃 Train 데이터로 param_grid의 하이퍼 파라미터들을 순차적으로 학습/평가 .
grid_dtree.fit(X_train, y_train)

# GridSearchCV 결과는 cv_results_ 라는 딕셔너리로 저장됨
# 이를 DataFrame으로 변환
scores_df = pd.DataFrame(grid_dtree.cv_results_)
scores_df[['params', 'mean_test_score', 'rank_test_score', 
           'split0_test_score', 'split1_test_score', 'split2_test_score']]
params mean_test_score rank_test_score split0_test_score split1_test_score split2_test_score
0 {'max_depth': 1, 'min_samples_split': 2} 0.700000 5 0.700 0.7 0.70
1 {'max_depth': 1, 'min_samples_split': 3} 0.700000 5 0.700 0.7 0.70
2 {'max_depth': 2, 'min_samples_split': 2} 0.958333 3 0.925 1.0 0.95
3 {'max_depth': 2, 'min_samples_split': 3} 0.958333 3 0.925 1.0 0.95
4 {'max_depth': 3, 'min_samples_split': 2} 0.975000 1 0.975 1.0 0.95
5 {'max_depth': 3, 'min_samples_split': 3} 0.975000 1 0.975 1.0 0.95
print('GridSearchCV 최적 파라미터:', grid_dtree.best_params_)
print('GridSearchCV 최고 정확도: {0:.4f}'.format(grid_dtree.best_score_))

# refit=True로 설정된 GridSearchCV 객체는 fit()으로 학습이 완료된 Estimator를 포함하고 있음
# 즉, predict()를 통해 최적의 하이퍼 파라미터 설정으로 예측도 가능. 
pred = grid_dtree.predict(X_test)
print('테스트 데이터 세트 정확도: {0:.4f}'.format(accuracy_score(y_test,pred)))

# 만약 refit=False 였다면 다음과 같이하여 같은 결과를 볼 수 있다
# GridSearchCV의 refit으로 이미 학습이 된 estimator 반환
estimator = grid_dtree.best_estimator_

# GridSearchCV의 best_estimator_는 이미 최적 하이퍼 파라미터로 학습이 됨
pred = estimator.predict(X_test)
print('테스트 데이터 세트 정확도: {0:.4f}'.format(accuracy_score(y_test,pred)))

댓글남기기