데이터 분석/머신러닝

[ML] 4. 교차 검증 (Cross Validation)

eunnys 2023. 11. 20. 18:57
# 패키지 로딩
import numpy as np
import pandas as pd

from sklearn.datasets import load_iris
from sklearn.model_selection import KFold, StratifiedKFold, train_test_split, cross_val_score, cross_validate
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

 
 
None Cross Validation
 

iris = load_iris()

# data는 독립변수, target은 종속변수
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3, random_state=10)

model = DecisionTreeClassifier(random_state=1) # 임의로 하나의 데이터를 뽑아올 때 시드값 고정

model.fit(x_train, y_train)

y_hat = model.predict(x_test)

# 검증의 정확도 (정답, 예측값)
print(f'정확도: {accuracy_score(y_test, y_hat):.2f}') 
# 오분류율이 2%밖에 안됨
정확도: 0.98

 
 
K-Fold Cross Validation
 

print(iris.data.shape) # (150, 4)
print(iris.target)     # 종속변수 확인 (1:1:1 비율)
(150, 4)
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]

 

# 교차검증을 위해 데이터를 분할 : n_splits 분할 개수 (전체 데이터 150개를 5등분 = 30개씩), shuffle=True 임의로 섞음
k_fold = KFold(n_splits=5, shuffle=True)

x = iris.data
y = iris.target

# 교차 검증의 횟수를 저장할 변수
k = 0 
# 각 교차검증마다 측정한 정확도를 리스트로 저장
cv_acc = []
# 학습할 모형 객체
model = DecisionTreeClassifier(random_state=1)

# k=5, train_idx=k-1(4등분), test_idx=k(1등분)
for train_idx, test_idx in k_fold.split(x):
    k += 1
    print(f'{k}번째 검증 데이터 셋')
    print('Train:', train_idx) # 학습 데이터의 인덱스를 가져옴
    print('Test:', test_idx)   # 평가 데이터의 인덱스를 가져옴
    x_train, x_test = x[train_idx], x[test_idx]
    y_train, y_test = y[train_idx], y[test_idx]

    model.fit(x_train, y_train)
    y_hat = model.predict(x_test)
    acc = accuracy_score(y_test, y_hat)
    print(f'{k}번째 교차검증의 정확도: {acc:.2f}')
    cv_acc.append(acc)

print()
print(f'평균 정확도: {np.mean(cv_acc):.2f}')
# 편향된 데이터로 검증하여 과대적합 문제 발생
# shuffle을 사용하면 순차적으로 데이터를 사용하는 것이 아닌 임의로 섞은 데이터로 검증
1번째 검증 데이터 셋
Train: [  0   1   3   4   5   6   7   8   9  10  11  12  13  15  16  17  18  19
  20  22  23  24  25  26  27  28  29  30  31  32  33  35  36  37  38  39
  41  42  43  44  45  47  49  50  51  52  53  54  57  58  59  60  62  63
  67  68  69  70  72  74  76  77  79  80  81  82  83  85  86  87  88  90
  92  93  94  95  97  98  99 100 101 102 103 104 105 106 107 108 109 111
 112 113 114 115 116 117 118 121 123 124 125 126 127 128 129 130 131 132
 133 134 136 137 138 139 141 142 143 145 147 149]
Test: [  2  14  21  34  40  46  48  55  56  61  64  65  66  71  73  75  78  84
  89  91  96 110 119 120 122 135 140 144 146 148]
1번째 교차검증의 정확도: 0.97
2번째 검증 데이터 셋
Train: [  1   2   3   4   5   6   7   8  10  11  12  13  14  16  18  19  20  21
  22  24  25  27  28  29  30  31  32  34  36  39  40  41  42  44  45  46
  47  48  50  55  56  58  59  61  62  64  65  66  67  69  71  72  73  74
  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90  91  92
  93  94  96  97  98  99 100 101 102 103 105 106 107 108 110 111 112 114
 115 116 117 118 119 120 122 124 125 126 127 128 129 130 131 132 133 134
 135 136 138 139 140 141 142 144 145 146 147 148]
Test: [  0   9  15  17  23  26  33  35  37  38  43  49  51  52  53  54  57  60
  63  68  70  95 104 109 113 121 123 137 143 149]
2번째 교차검증의 정확도: 0.93
3번째 검증 데이터 셋
Train: [  0   1   2   3   4   5   7   8   9  11  13  14  15  16  17  18  19  20
  21  23  26  27  29  30  31  32  33  34  35  36  37  38  39  40  42  43
  44  45  46  47  48  49  51  52  53  54  55  56  57  59  60  61  63  64
  65  66  67  68  69  70  71  73  74  75  76  77  78  79  80  81  83  84
  85  86  87  88  89  90  91  94  95  96  98  99 101 102 104 105 106 107
 108 109 110 111 113 116 118 119 120 121 122 123 125 127 129 131 133 135
 136 137 138 139 140 142 143 144 145 146 148 149]
Test: [  6  10  12  22  24  25  28  41  50  58  62  72  82  92  93  97 100 103
 112 114 115 117 124 126 128 130 132 134 141 147]
3번째 교차검증의 정확도: 1.00
4번째 검증 데이터 셋
Train: [  0   2   3   6   7   8   9  10  12  13  14  15  17  20  21  22  23  24
  25  26  27  28  30  31  32  33  34  35  37  38  39  40  41  43  46  48
  49  50  51  52  53  54  55  56  57  58  60  61  62  63  64  65  66  68
  69  70  71  72  73  74  75  76  78  82  84  85  86  87  89  90  91  92
  93  94  95  96  97  98  99 100 101 102 103 104 105 107 109 110 112 113
 114 115 116 117 119 120 121 122 123 124 126 128 129 130 131 132 133 134
 135 137 139 140 141 143 144 145 146 147 148 149]
Test: [  1   4   5  11  16  18  19  29  36  42  44  45  47  59  67  77  79  80
  81  83  88 106 108 111 118 125 127 136 138 142]
4번째 교차검증의 정확도: 0.93
5번째 검증 데이터 셋
Train: [  0   1   2   4   5   6   9  10  11  12  14  15  16  17  18  19  21  22
  23  24  25  26  28  29  33  34  35  36  37  38  40  41  42  43  44  45
  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63
  64  65  66  67  68  70  71  72  73  75  77  78  79  80  81  82  83  84
  88  89  91  92  93  95  96  97 100 103 104 106 108 109 110 111 112 113
 114 115 117 118 119 120 121 122 123 124 125 126 127 128 130 132 134 135
 136 137 138 140 141 142 143 144 146 147 148 149]
Test: [  3   7   8  13  20  27  30  31  32  39  69  74  76  85  86  87  90  94
  98  99 101 102 105 107 116 129 131 133 139 145]
5번째 교차검증의 정확도: 0.97

평균 정확도: 0.96

 
 
Stratified K-Fold Validation
 

# 교차검증을 위해 데이터를 분할 : n_splits 분할 개수 (전체 데이터 150개를 5등분 = 30개씩)
stf_kfold = StratifiedKFold(n_splits=5)

x = iris.data
y = iris.target

# 교차 검증의 횟수를 저장할 변수
k = 0 
# 각 교차검증마다 측정한 정확도를 리스트로 저장
cv_acc = []
# 학습할 모형 객체
model = DecisionTreeClassifier(random_state=1)

# k=5, train_idx=k-1(4등분), test_idx=k(1등분)
# split함수에 종속변수 y가 들어가야 함
for train_idx, test_idx in stf_kfold.split(x, y):
    k += 1
    print(f'{k}번째 검증 데이터 셋')
    print('Train:', train_idx) # 학습 데이터의 인덱스를 가져옴
    print('Test:', test_idx)   # 평가 데이터의 인덱스를 가져옴
    x_train, x_test = x[train_idx], x[test_idx]
    y_train, y_test = y[train_idx], y[test_idx]

    model.fit(x_train, y_train)
    y_hat = model.predict(x_test)
    acc = accuracy_score(y_test, y_hat)
    print(f'{k}번째 교차검증의 정확도: {acc:.2f}')
    cv_acc.append(acc)

print()
print(f'평균 정확도: {np.mean(cv_acc):.2f}')
1번째 검증 데이터 셋
Train: [ 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  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 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]
Test: [  0   1   2   3   4   5   6   7   8   9  50  51  52  53  54  55  56  57
  58  59 100 101 102 103 104 105 106 107 108 109]
1번째 교차검증의 정확도: 0.97
2번째 검증 데이터 셋
Train: [  0   1   2   3   4   5   6   7   8   9  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  50  51  52  53  54  55  56  57  58  59  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 100 101 102 103 104 105 106 107 108 109
 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]
Test: [ 10  11  12  13  14  15  16  17  18  19  60  61  62  63  64  65  66  67
  68  69 110 111 112 113 114 115 116 117 118 119]
2번째 교차검증의 정확도: 0.97
3번째 검증 데이터 셋
Train: [  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17
  18  19  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  60  61  62  63
  64  65  66  67  68  69  80  81  82  83  84  85  86  87  88  89  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 130 131 132 133 134 135 136 137
 138 139 140 141 142 143 144 145 146 147 148 149]
Test: [ 20  21  22  23  24  25  26  27  28  29  70  71  72  73  74  75  76  77
  78  79 120 121 122 123 124 125 126 127 128 129]
3번째 교차검증의 정확도: 0.90
4번째 검증 데이터 셋
Train: [  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  40  41  42  43  44  45
  46  47  48  49  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  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 120 121 122 123 124 125 126 127
 128 129 140 141 142 143 144 145 146 147 148 149]
Test: [ 30  31  32  33  34  35  36  37  38  39  80  81  82  83  84  85  86  87
  88  89 130 131 132 133 134 135 136 137 138 139]
4번째 교차검증의 정확도: 1.00
5번째 검증 데이터 셋
Train: [  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  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 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]
Test: [ 40  41  42  43  44  45  46  47  48  49  90  91  92  93  94  95  96  97
  98  99 140 141 142 143 144 145 146 147 148 149]
5번째 교차검증의 정확도: 1.00

평균 정확도: 0.97

 
 
cross_val_score()
- estimator : 학습할 모델 객체
- x : 독립변수 데이터 셋
- y : 종속변수 데이터 셋
- cv : 교차검증 K값 (default: 5), 또는 교차검증 객체
 

score = cross_val_score(model, x, y)
print('score:', score)
print(f'평균 정확도: {np.mean(score):.2f}')
score: [0.96666667 0.96666667 0.9        1.         1.        ]
평균 정확도: 0.97

 
 
cross_validate()
- 매 검증마다 소요된 학습 시간과 검증 시간을 딕셔너리로 반환
- 여러가지 평가지표를 리스트 형태로 전달 가능
 

# 평가지표 종류 확인
import sklearn
sklearn.metrics.SCORERS.keys()
dict_keys(['explained_variance', 'r2', 'max_error', 'matthews_corrcoef', 'neg_median_absolute_error', 'neg_mean_absolute_error', 'neg_mean_absolute_percentage_error', 'neg_mean_squared_error', 'neg_mean_squared_log_error', 'neg_root_mean_squared_error', 'neg_mean_poisson_deviance', 'neg_mean_gamma_deviance', 'accuracy', 'top_k_accuracy', 'roc_auc', 'roc_auc_ovr', 'roc_auc_ovo', 'roc_auc_ovr_weighted', 'roc_auc_ovo_weighted', 'balanced_accuracy', 'average_precision', 'neg_log_loss', 'neg_brier_score', 'positive_likelihood_ratio', 'neg_negative_likelihood_ratio', 'adjusted_rand_score', 'rand_score', 'homogeneity_score', 'completeness_score', 'v_measure_score', 'mutual_info_score', 'adjusted_mutual_info_score', 'normalized_mutual_info_score', 'fowlkes_mallows_score', 'precision', 'precision_macro', 'precision_micro', 'precision_samples', 'precision_weighted', 'recall', 'recall_macro', 'recall_micro', 'recall_samples', 'recall_weighted', 'f1', 'f1_macro', 'f1_micro', 'f1_samples', 'f1_weighted', 'jaccard', 'jaccard_macro', 'jaccard_micro', 'jaccard_samples', 'jaccard_weighted'])

 

# 3가지 평가지표를 사용하여 교차검증
result = cross_validate(model, x, y, cv=5, scoring=['accuracy', 'precision_macro', 'roc_auc_ovr'])
for key, val in result.items():
    print('평가지표:', key)
    print(f'평균값: {np.mean(val):.2f}')
    print('-'*30)
평가지표: fit_time
평균값: 0.00
------------------------------
평가지표: score_time
평균값: 0.00
------------------------------
평가지표: test_accuracy
평균값: 0.97
------------------------------
평가지표: test_precision_macro
평균값: 0.97
------------------------------
평가지표: test_roc_auc_ovr
평균값: 0.97
------------------------------