Search

Model Selection 모듈 소개

학습 데이터와 테스트 데이터 세트를 분리하거나 교차 검증 분할 및 평가, 그리고 estimator의 하이퍼 파라미터를 튜닝하기 위한 다양한 함수와 클래스를 제공한다.

학습/테스트 데이터 세트 분리 - train_ test_split()

먼저 sklearn.model_selection 모듈에서 train_test_split() 을 로드한다.
train_test_split(피처 데이터 세트, 레이블 데이터 세트, test_size, shuffle=True, random_state)
이때, test_size의 default값은 0.25 다. shuffle=True 가 default인데, 데이터를 분리하기 전에 데이터를 미리 섞을지 결정하는 인자다.
붓꽃 데이터에 대해서 테스트 데이터 세트를 전체의 30%로 분리하자.
from sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeClassifier from sklearn.metrics import accuracy_score df_clf = DecisionTreeClassifier X_train, X_test, y_train, y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.3, random_state=121) # 의사결정나무를 학습하고 예측 정확도까지 측정해보자. df_clf.fit(X_train,y_train) pred = df_clf.predict(X_test) print('예측 정확도: {0.4f}'.format(accuracy_score(y_test,pred)))
Python
복사

교차 검증

교차 검증은 데이터 편중을 막기 위해서 별도의 여러 세트로 구성된 학습 데이터 세트와 검증 데이터 세트에서 학습과 평가를 수행하는 것이다. 각 세트에서 수행한 평가 결과에 따라 하이퍼 파라미터 튜닝 등의 모델 최적화를 더욱 쉽게 할 수 있다.
대부분의 머신러닝 모델의 성능 평가는 교차 검증 기반으로 1차 평가를 한 뒤에 최종적으로 테스트 데이터 세트에 적용해 평가하는 프로세스다.
k 폴드 교차 검증
가장 보편적으로 사용되는 교차 검증 기법. k개의 데이터 폴드 세트를 만들어서 k번만큼 각 폴드 세트에 학습과 검증 평가를 반복적으로 수행하는 방법이다.
사이킷런에서는 KFold 와 StratifiedKFold 클래스를 제공한다.
먼저, KFold 클래스를 이용해 교차 검증하고 예측 정확도를 측정해보자.
from sklearn.model_selection import KFold import numpy as np iris = load_iris() features = iris.data label = iris.target dt_clf = DecisionTreeClassifier(random_state=156) # 5개의 폴드 세트로 분리하는 KFold 객체와 폴드 세트별 정확도를 담을 리스트 객체 생성 kfold = KFold(n_splits=5) cv_accuracy=[] print('붓꽃 데이터 세트 크기:', features.shape[0])
Python
복사
n_iter=0 #kfold 객체의 split()을 호출하면 폴드 별 학습용, 검증용 테스트의 로우 인덱스를 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,y_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))
Python
복사
Stratified K 폴드
불균형한 분포도를 가진 레이블 데이터 집합을 위한 K폴드 방식이다. 즉, 특정 레이블 값이 특이하게 많거나 매우 적어서 분포가 한 쪽으로 치우치는 경우를 말한다.
먼저, K폴드가 어떤 문제를 가지고 있는지 확인해본 뒤 StratifiedKFold 클래스를 이용해 개선해보자.
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 Name: label, dtype: int64 '''
Python
복사
문제가 생기는 상황을 만들기 위해서 K=3 인 폴드 세트를 만들고 교차 검증 시마다 생성되는 학습/검증 레이블 데이터 값의 분포도를 확인해보자.
kfold = KFold(n_splits=3) n_iter=0 for train_index, test_index in kfold.split(iris_df): n_iter += 1 label_train = iris_df['label'].iloc[train_idex] label_test = iris_df['label'].iloc[test_index] print('## 교차 검증: {0}'.format(n_iter)) ''' ## 교차 검증: 1 학습 레이블 데이터 분포: 1 50 2 50 Name: label, dtype: int64 검증 레이블 데이터 분포: 0 50 Name: label, dtype: int64 ## 교차 검증: 2 학습 레이블 데이터 분포: 0 50 2 50 Name: label, dtype: int64 검증 레이블 데이터 분포: 1 50 Name: label, dtype: int64 ## 교차 검증: 3 학습 레이블 데이터 분포: 0 50 1 50 Name: label, dtype: int64 검증 레이블 데이터 분포: 2 50 Name: label, dtype: int64 ''' # 위의 결과처럼 학습 레이블과 검증 레이블이 완전히 다른 값으로 추출된 것을 확인할 수 있다. # StratifiedKFold 는 이런 실제 레이블 값의 분포도를 반영하지 못하는 kfold 문제를 해결해준다.
Python
복사
StratifiedKFold 는 split()의 인자로 데이터 세트뿐만 아니라 레이블 데이터 세트도 넣어줘야 한다.
from sklearn.model_selection import StratifiedKFold 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()) ''' ## 교차 검증: 1 학습 레이블 데이터 분포: 2 34 0 33 1 33 Name: label, dtype: int64 검증 레이블 데이터 분포: 0 17 1 17 2 16 Name: label, dtype: int64 ## 교차 검증: 2 학습 레이블 데이터 분포: 1 34 0 33 2 33 Name: label, dtype: int64 검증 레이블 데이터 분포: 0 17 2 17 1 16 Name: label, dtype: int64 ## 교차 검증: 3 학습 레이블 데이터 분포: 0 34 1 33 2 33 Name: label, dtype: int64 검증 레이블 데이터 분포: 1 17 2 17 0 16 Name: label, dtype: int64 '''
Python
복사
이제, 붓꽃 데이터를 교차 검증해보자.
dt_clf = DecisionTreeClassifier(random_state=156) 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)) ''' #1 교차 검증 정확도:0.98, 학습 데이터 크기: 100, 검증 데이터 크기: 50 #1 검증 세트 인덱스:[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115] ## 교차 검증별 정확도: [0.98] ## 평균 검증 정확도: 0.98 #2 교차 검증 정확도:0.94, 학습 데이터 크기: 100, 검증 데이터 크기: 50 #2 검증 세트 인덱스:[ 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132] ## 교차 검증별 정확도: [0.98 0.94] ## 평균 검증 정확도: 0.96 #3 교차 검증 정확도:0.98, 학습 데이터 크기: 100, 검증 데이터 크기: 50 #3 검증 세트 인덱스:[ 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149] ## 교차 검증별 정확도: [0.98 0.94 0.98] ## 평균 검증 정확도: 0.9666666666666667 '''
Python
복사
일반적으로 분류에서의 교차 검증은 k폴드가 아니라 stratified k 폴드로 분할이 되어야 한다. 회귀에서는 stratified k 폴드가 지원되지 않는다.
교차 검증을 보다 간편하게 제공해주는 사이킷런의 API를 살펴보자.
교차 검증을 보다 간편하게 - cross_val_score()
cross_val_score(estimator, X, y=None, scoring=None, cv=None, n_jobs=1, verbose=0, fit_params=None, pre_dispatch=’2*n_jobs’)
estimator, X, y, scoring, cv 가 주요 파라미터다. (X는 피처 데이터 세트, y는 레이블 데이터 세트, scoring은 예측 성능 평가 지표, cv는 교차 검증 폴드 수)
반환값은 scoring 파라미터로 지정된 성능 지표 측정값을 배열 형태
estimator가 분류일 경우에는 stratified kfold 방식으로, 회귀일 경우에는 k fold 방식으로 분할한다.
이제 활용해본다. 교차 검증 폴드 수는 3, 성능 평가 지표는 accuracy로 하겠다.
from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import cross_val_score, cross_validate from sklearn.datasets import load_iris iris_data = load_iris() dt_clf = DecisionTreeClassifier(random_state=156) data = iris_data.data label = iris_data.target # 성능 지표는 정확도, 교차 검증 세트는 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)) ''' 교차 검증별 정확도: [0.98 0.94 0.98] 평균 검증 정확도: 0.9667 '''
Python
복사
비슷한 API로 cross_validate() 가 있다. 여러 개의 평가 지표를 반환할 수 있다. 학습 데이터에 대한 성능 평가 지표와 수행 시간을 함께 제공한다. 하지만 cross_val_score()을 일반적으로 많이 사용한다.

GridSearchCV - 교차 검증과 최적 하이퍼 파라미터 튜닝을 한번에

어떤 방식으로 파라미터에 대한 튜닝을 진행하는지 미리 알아보겠다. 이 튜닝을 통해서 예측 성능을 개선할 수 있다. 즉 파라미터의 집합을 만들고 순차적으로 적용하면서 최적화를 수행한다.
grid_parameters = {'max_depth':[1,2,3], 'min_samples_split':[2,3]} # 데이터 세트를 교차 검증을 위한 학습/테스트 세트로 자동 분할한다. # 최적의 파라미터를 찾는다.단, 수행시간이 상대적으로 오래 걸린다. # 6개의 파라미터 조합은 CV= 3 X 6 개의 파라미터 조합 = 18 번의 학습/평가가 이뤄진다.
Python
복사
GirdSearchCV의 주요 파라미터는
estimator : classifier, regressor, pipeline
param_grid : key + 리스트 값을 가지는 딕셔너리.
scoring : 예측 성능을 측정할 평가 방법 ( 문자열로 지정하거나 함수로 지정 )
cv : 교차 검증을 위한 학습/테스트 세트 개수
refit : default=True —> 가장 최적의 하이퍼 파라미터를 찾고 입력된 estimator 객체를 해당 하이퍼 파라미터로 재학습.
결정트리분류 모형의 중요 파라미터는 max_depth, min_samples_split 다.
from sklearn.datasets import load_iris from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import GridSearchCV,train_test_split # 데이터 로딩 및 분리 iris_data = 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() ### 파라미터를 딕셔너리 형태로 설정 parameters = {'max_depth':[1,2,3], 'min_samples_split':[2,3]}
Python
복사
fit 한 결과는 cv_results_ 속성에 기록한다. 딕셔너리 형태로 key값과 리스트 형태의 value값을 가진다.
import pandas as pd grid_tree = GridSearchCV(dtree, param_grid=parameters, cv=3) grid_tree.fit(X_train,y_train) # GridSearchCV 결과를 데이터프레임으로 변환 scores_df = pd.DataFrame(grid_tree.cv_results_) scores_df[['params','mean_test_score','rank_test_score','split0_test_score','split1_test_score', 'split2_test_score']] # 결과를 확인해보면 max_depth = 3, min_samples_split = 2 인 경우가 예측성능 1위로 나온다.
Python
복사
cv_results_의 결과에 대해서 보자면
params는 적용된 하이퍼 파라미터 조합을 보여준다.
rank_test_score는 하이퍼 파라미터 별 성능이 좋은 순위를 나타낸다.
mean_test_score는 하이퍼 파라미터 조합별 cv의 폴딩 테스트 세트에 대해 수행한 평가 평균값을 나타낸다.
fit()을 수행하면 최적의 하이퍼 파라미터 값과 그때의 평가 결과값이 best_params_ 와 best_score_ 속성에 기록된다.
# 최적의 하이퍼 파라미터 값과 그 정확도 print('GridSearchCV 최적 파라미터:', grid_tree.best_params_) print('GridSearchCV 최고 정확도:{0:.4f}'.format(grid_tree.best_score_)) ''' GridSearchCV 최적 파라미터: {'max_depth': 3, 'min_samples_split': 2} GridSearchCV 최고 정확도:0.9750 '''
Python
복사
이제, 테스트 데이터 세트에 대해 예측하고 성능을 평가해보자.
#GridSearchCV 의 refit로 이미 학습된 estimator 반환 estimator = grid_tree.best_estimator_ # GridSearchCV의 best_estimator_ 는 이미 학습이 완료 from sklearn.metrics import accuracy_score pred = estimator.predict(X_test) print('테스트 데이터 세트 정확도:{0:.4f}'.format(accuracy_score(y_test,pred))) ''' 테스트 데이터 세트 정확도:0.9667 '''
Python
복사