[파이썬 머신러닝 완벽 가이드] 교차검증
교차검증 (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)))
댓글남기기