4 분 소요


사이킷런으로 시작하는 머신러닝

교차 검증

학습/테스트 데이터 분리만으로는 과적합(Overfitting)에 취약하다.
과소적합: 학습을 덜함(데이터가 충분하지 않아서)
과대적합: 너무 세밀하게 학습하여 조건이 엄격해져 실제데이터와 차이가 남

이를 피하기 위해 학습 데이터를 다시 분할하여 학습 데이터와 학습된 모델의 성능을 일차 평가하는 검증 데이터로 나눈다.

K 폴드

K개의 데이터 폴드 세트를 만들어 K번만큼 각 폴드 세트에 학습과 검증 평가를 반복적으로 수행하는 방법이다.

In [1]:
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold
import numpy as np
from sklearn.model_selection import StratifiedKFold
In [2]:
iris = load_iris()
dt_clf = DecisionTreeClassifier(random_state=156)
kfold = KFold(n_splits=5, shuffle=True) # n_splits=5(defalut) 몇개의 폴드로 나눌 것인지 # shuffle 섞어서 뽑을 것인지
In [3]:
cv_accuracy = []
n_iter = 0 # 반복횟수 카운트

for train_index, test_index in kfold.split(iris.data): # 150개를 n_splits=5로 나눠 train(120)과 test(30)로 반환
    # print(train_index)
    # print(test_index)
    # kfold.split()으로 반환된 인덱스를 이용해 학습용, 검증용 데이터 추출
    X_train, X_test = iris.data[train_index], iris.data[test_index]
    y_train, y_test = iris.target[train_index], iris.target[test_index]
    # 학습, 예측
    dt_clf.fit(X_train, y_train)
    pred = dt_clf.predict(X_test)
    n_iter += 1
    # 정확도 측정
    accuracy = accuracy_score(y_test, pred)
    cv_accuracy.append(accuracy)
    print(f"{n_iter}회차, 정확도: {accuracy}")
    print("테스트용 데이터 idx:", test_index)
Out [3]:
1회차, 정확도: 0.9333333333333333
테스트용 데이터 idx: [  6  17  20  24  27  54  63  67  68  85  89  90  94  96 109 112 117 119
 124 125 130 131 132 133 136 138 139 144 145 149]
2회차, 정확도: 0.9666666666666667
테스트용 데이터 idx: [  0  10  13  14  18  21  22  29  35  37  38  39  47  48  55  57  58  60
  76  88  98 103 108 111 114 121 126 127 134 147]
3회차, 정확도: 0.9
테스트용 데이터 idx: [  8  12  19  23  31  36  41  46  50  51  56  61  69  70  72  73  74  77
  78  79  84  92  93  97 104 106 113 115 123 142]
4회차, 정확도: 0.9666666666666667
테스트용 데이터 idx: [  4   5   7  26  28  32  33  42  44  45  52  59  62  66  71  80  86  87
 101 102 105 107 118 120 128 129 140 143 146 148]
5회차, 정확도: 0.9666666666666667
테스트용 데이터 idx: [  1   2   3   9  11  15  16  25  30  34  40  43  49  53  64  65  75  81
  82  83  91  95  99 100 110 116 122 135 137 141]

In [4]:
np.round(np.mean(cv_accuracy), 4)
Out [4]:
0.9467

학습에 사용하는 데이터, 평가에 사용하는 데이터 둘다 섞여야 함!

Stratified K 폴드

불균형한(imbalanced) 분포도를 가진 label 데이터 집합(정답 데이터)을 위한 K 폴드 방식이다.
각 교차 검증 시도마다 label 비율을 동일하게 맞춤(label의 비율을 따짐)

In [5]:
import pandas as pd
data = load_iris(as_frame=True)
# label 값의 분포 확인
data.target.value_counts()
# pd.Series(iris.target).value_counts()
Out [5]:
0    50
1    50
2    50
Name: target, dtype: int64
In [6]:
kfold = KFold(n_splits=3, shuffle=False) # 이상현상의 확인을 위해 n_splits=3 으로 설정
cv_accuracy = []
n_iter = 0

for train_index, test_index in kfold.split(iris.data):
    X_train, X_test = iris.data[train_index], iris.data[test_index]
    y_train, y_test = iris.target[train_index], iris.target[test_index]
    dt_clf.fit(X_train, y_train)
    pred = dt_clf.predict(X_test)
    n_iter += 1
    accuracy = accuracy_score(y_test, pred)
    cv_accuracy.append(accuracy)
    print(f"{n_iter}회차, 정확도: {accuracy}")
    print("테스트용 데이터:", test_index)
Out [6]:
1회차, 정확도: 0.0
테스트용 데이터: [ 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
 48 49]
2회차, 정확도: 0.0
테스트용 데이터: [50 51 52 53 54 55 56 57 58 59 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 90 91 92 93 94 95 96 97
 98 99]
3회차, 정확도: 0.0
테스트용 데이터: [100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
 118 119 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]

In [7]:
kfold = StratifiedKFold(n_splits=3, shuffle=False)
cv_accuracy = []
n_iter = 0
data = load_iris(as_frame=True)

for train_index, test_index in kfold.split(data.data, data.target): # label의 비율을 동일하게 해야하므로 split에 label 데이터 세트를 넣어줌
    X_train, X_test = data.data.iloc[train_index], data.data.iloc[test_index]
    y_train, y_test = data.target.iloc[train_index], data.target.iloc[test_index]
    n_iter += 1
    print(f"### {n_iter}회차")
    print('학습데이터 분포\n', y_train.value_counts())
    print('검증데이터 분포\n', y_test.value_counts())
    dt_clf.fit(X_train, y_train)
    pred = dt_clf.predict(X_test)
    accuracy = accuracy_score(y_test, pred)
    cv_accuracy.append(accuracy)
    print(f"정확도: {accuracy}")
    # print("테스트용 데이터:", y_test)
Out [7]:
### 1회차
학습데이터 분포
 2    34
0    33
1    33
Name: target, dtype: int64
검증데이터 분포
 0    17
1    17
2    16
Name: target, dtype: int64
정확도: 0.98
### 2회차
학습데이터 분포
 1    34
0    33
2    33
Name: target, dtype: int64
검증데이터 분포
 0    17
2    17
1    16
Name: target, dtype: int64
정확도: 0.94
### 3회차
학습데이터 분포
 0    34
1    33
2    33
Name: target, dtype: int64
검증데이터 분포
 1    17
2    17
0    16
Name: target, dtype: int64
정확도: 0.98

cross_val_score() : 교차검증을 수치값으로 확인

교차검증 일련의 과정을 한꺼번에 수행해주는 API

In [8]:
from sklearn.model_selection import cross_val_score
In [9]:
dt_clf = DecisionTreeClassifier(random_state=156)
iris = load_iris()
cross_val_score(dt_clf, iris.data, iris.target, cv=3) # cv:교차검증 나누는 수(StratifiedKFold 방식)
Out [9]:
array([0.98, 0.94, 0.98])

GridSearchCV

교차 검증과 최적 하이퍼 파라미터 튜닝을 한번에
(하이퍼 파라미터: n_splits과 같이 사람이 직접 지정해 줘야 하는 파라미터)

데이터 상황에 따라 최적 파라미터가 달라진다!

In [10]:
grid_param = {
    'max_depth':[1, 2, 3],
    'min_samples_split':[2, 3]
}
In [11]:
from sklearn.model_selection import GridSearchCV, train_test_split
import pandas as pd
In [12]:
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)
dtree = DecisionTreeClassifier()
grid_dtree = GridSearchCV(dtree, grid_param, cv=3, refit=True)
# scoring: 정확도 체크, refit: 제일 좋았던 경우의 수로 한번더 학습(default=True)
# 파라미터 경우의 수 * cv(교차검증 나누는 수) = (3*2)*3 = 18번 돌림
In [13]:
grid_dtree.fit(X_train, y_train)
Out [13]:
GridSearchCV(cv=3, estimator=DecisionTreeClassifier(),
             param_grid={'max_depth': [1, 2, 3], 'min_samples_split': [2, 3]})
In [14]:
grid_dtree.predict(X_test)
Out [14]:
array([1, 2, 1, 0, 0, 1, 1, 1, 1, 2, 2, 1, 1, 0, 0, 2, 1, 0, 2, 0, 2, 2,
       1, 1, 1, 1, 0, 0, 2, 2])
In [15]:
# 결과 확인
df = pd.DataFrame(grid_dtree.cv_results_)
In [16]:
df.columns
Out [16]:
Index(['mean_fit_time', 'std_fit_time', 'mean_score_time', 'std_score_time',
       'param_max_depth', 'param_min_samples_split', 'params',
       'split0_test_score', 'split1_test_score', 'split2_test_score',
       'mean_test_score', 'std_test_score', 'rank_test_score'],
      dtype='object')
In [17]:
df[['params', 'mean_test_score', 'rank_test_score', 'split0_test_score', 'split1_test_score', 'split2_test_score']]
Out [17]:
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

max_depth : 의사 결정 트리의 깊이
min_samples_split : 가지를 나눌 수 있는 최소 샘플 수

In [18]:
grid_dtree.best_params_ # 보통은 앞의 것 선택
Out [18]:
{'max_depth': 3, 'min_samples_split': 2}
In [19]:
grid_dtree.best_score_
Out [19]:
0.975
In [20]:
pred = grid_dtree.best_estimator_.predict(X_test)
In [21]:
accuracy_score(y_test, pred)
Out [21]:
0.9666666666666667

Reference

  • 이 포스트는 SeSAC 인공지능 자연어처리, 컴퓨터비전 기술을 활용한 응용 SW 개발자 양성 과정 - 심선조 강사님의 강의를 정리한 내용입니다.

댓글남기기