이번 포스팅에서는 머신러닝 모델의 성능을 중간에 평가하는 즉, 해당 모델을 Test 데이터로 검증하기 전에 모델의 성능을 중간점검 하는 하나의 객관적인 방법으로서 교차 검증(Cross validation)을 Scikit-learn 라이브러리를 이용해서 어떻게 해보는지에 대해 다루려고 한다. 그리고 모델링의 가장 최적의 하이퍼파라미터 값을 찾기 위해 일일이 모두 다 해보는 방법인 GridSearch를 코드상에서 어떻게 수행할 수 있는지 소개한다.
여기서 소개할 교차 검증방법으로는 일반적으로 사용되는 K-Fold 방법과 Stratified K-Fold를 소개한다. 그리고 앞의 교차 검증방법을 하나의 함수로 단 한번에 해결할 수 있는 Scikit-learn 라이브러리의 cross_val_score함수에 대해 알아본다. 목차는 다음과 같다.
1. 일반적인 K-fold
2. Stratified K-fold
3. 단 한번에, cross_val_score !
4. GridSearchCV
1. 일반적인 K-fold
일반적인 K-fold 교차 검증 방법은 아래의 그림과 같다.
위 그림 처럼 Fold는 파란색 네모를 의미한다. 즉 만약 Fold 개수(K값)를 5로 설정한다면 위 그림 처럼 5번의 모델링 검증을 시행할 것이다. 하지만 1번 검증을 시행할 때마다 검증용 데이터가 각각 파란색으로 매번 달라진다. 그리고 모든 교차검증이 종료되고 즉, 5번의 검증이 종료된 후 각 검증마다 도출된 정확도들의 평균값으로 최종적인 정확도를 계산하게 된다.
위의 방법을 Scikit-learn 라이브러리를 이용하는 실습은 아래 코드와 같다. 데이터는 sklearn에 내장되어 있는 Iris 데이터로 진행하였다.
# cross validation
# 일반적인 K-Fold cv
import pandas as pd
import numpy as np
from sklearn.model_selection import KFold
kf = KFold(n_splits=5)
all_acc = []
fold_idx = 0
features = iris.data
labels = iris.target
for train_idx, test_idx in kf.split(features):
train_x, train_y = features[train_idx], labels[train_idx]
test_x, test_y = features[test_idx], labels[test_idx]
model = DecisionTreeClassifier()
model.fit(train_x, train_y)
pred_y = model.predict(test_x)
acc = accuracy_score(test_y, pred_y)
fold_idx += 1
all_acc.append(acc)
print(f"KFold 모두 수행 후 평균 예측도 : {np.mean(all_acc)}")
출력 결과물은 다음과 같다.
2. Stratified K-fold
다음은 Stratified K-fold 방법이다. 'Stratified'의 사전적 정의는 '층화된' 이라는 의미이다. 사전적인 의미만 봐서는 피부에 와닿게 이해가 되지 않는다. Stratified K-fold 방법은 Train 데이터에서의 label(정답) 분포가 Test 데이터에서의 label(정답) 분포와 유사하도록 맞춰주는 교차검증 방법이다.
단적인 예로 원본 데이터의 label의 종류에 ['개', '고양이', '원숭이'] 가 있다고 하자. 모델이 학습하는 Train 데이터에서의 label 종류가 ['개', '고양이'] 밖에 없는 상태로 학습이 진행되었다. 그리고 이제 Test 데이터로 검증을 실시해야 한다. 그런데 Test 데이터로 검증할 때 갑자기 ['원숭이'] 라는 label이 등장한다. 모델은 학습할 때 분명히 '개' 또는 '고양이' 밖에 보지 못했는데 '원숭이'가 등장했을 때 몹시 당황할 것이고 아마 정답을 맞추지 못할 것이다. 매우 극단적인 예시를 들었지만 앞서 설명했던 상황을 방지하기 위해서 Stratified K-fold 방법은 Train, Test 데이터 각각에 ['개', '고양이', '원숭이'] label들을 유사한 비율로 분포하도록 Train, Test 데이터를 나누고 모델 검증을하게 된다.
Stratified K-fold는 친절하게도 Scikit-learn에서 제공하고 있으며 코드상에서 구현하는 방법에 대해 알아보자. 데이터는 일반적인 K-fold 때와 마찬가지로 iris 데이터를 사용하였다.
# Stratified Cross validation
from sklearn.model_selection import StratifiedKFold
skf = StratifiedKFold(n_splits=5)
all_acc = []
fold_idx = 0
features = iris.data
labels = iris.target
for train_idx, test_idx in skf.split(features, labels):
train_x, train_y = features[train_idx], labels[train_idx]
test_x, test_y = features[test_idx], labels[test_idx]
model = DecisionTreeClassifier()
model.fit(train_x, train_y)
pred_y = model.predict(test_x)
acc = accuracy_score(test_y, pred_y)
fold_idx += 1
all_acc.append(acc)
print(f"KFold 모두 수행 후 평균 예측도 : {np.mean(all_acc)}")
결과물은 다음과 같다. 일반적인 K-fold 때와 비교해보면 예측도가 더 올라갔음을 알 수가 있다.
3. 단 한번에, cross_val_score !
지금까지 살펴보았던 일반적인 K-fold와 Stratified K-fold를 구현하기 위해서 어느정도 코드를 입력해야 하는 수고(?)가 든다. 하지만 이를 단 하나의 함수로 해결해줄 수 있는 함수가 있다. 바로 cross_val_score 함수이다. 이를 구현하기 위한 코드는 다음과 같다.
# cross_val_score로 집약해서 교차검증
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score, cross_validate
import numpy as np
iris_data = load_iris()
features = iris_data.data
labels = iris.target
model = DecisionTreeClassifier()
scores = cross_val_score(model, features, labels,
scoring='accuracy', cv=5)
print(f"한 번씩 검증 때마다 accuracy : {scores}")
print()
print(f"5번 모두 검증한 accuracy 총 평균 : {np.mean(scores)}")
딱 봐도 코드상에서 몇 줄 줄어든 느낌이 느껴진다. 그렇다. cross_val_score 함수를 사용하게 된다면 이전처럼 for 문을 사용하지않은 것이 확 눈에 띈다. 즉 cross_val_score은 인자에 설정한 모델, feature, label을 설정해주고 또 어떤 metric을 사용할지(위 예시에서는 분류 metric인 accruacy를 사용했다.), 교차검증을 몇 번 수행할지까지 설정해준다. 참고로 cross_val_score에서 수행해주는 Cross validation방법은 일반적인 K-fold가 아닌 Stratified K-fold 검증방법을 사용한다는 것을 알아두자.
4. GridSearchCV(GridSerach + Cross Validation)
GridSearch란, 모델링시 필요한 하이퍼파라미터를 설정할 때 가장 최적의 파리미터값을 찾아주는 방법 중 하나이다. 그 중에서도 GridSearch방법은 이른바 '무식하게 하나 하나 다 해보기' 이다. 즉, 파라미터 후보값들을 사전에 정의해주고 이를 일일이 for 문 처럼 돌려서 적용해보는 것을 대신해주는 방법이다. 그리고 추가적으로 교차검증까지 수행해준다. 이것 또한 scikit-learn에서 제공해주면 코드로 구현하면 다음과 같다.
# 최고의 Hyperparameter 찾기 위한 GridsearchCV 사용
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_data = load_iris()
train_x, test_x, train_y, test_y = train_test_split(iris_data.data,
iris_data.target,
test_size=0.2,
random_state=45)
model = DecisionTreeClassifier()
# parameter 넣어줄 값들 dict 형태로 정의해주기
h_para = {'max_depth':[1,2,3], 'min_samples_split':[2,3]}
grid_dtree = GridSearchCV(model, param_grid=h_para,
cv=5, refit=True, return_train_score=True)
# GridSearchCV 인자설명
# cv = 하나의 파라미터 쌍으로 모델링할 때 train, test 교차검증을 3번실시하겠다는 뜻
# refit=True : GridSearch한 후 가장 최고로 좋은 파라미터로 학습시켜 놓겠다.
# ㄴ> 이것 때문에 애초에 GridSearchCV 적용한 객체만으로 최적의 파라미터 적용된 모델로드 가능
# GridSearch 하면서 모든 파라미터값들에 대해 학습 수행
grid_dtree.fit(train_x, train_y)
# 각 파라미터값들에 대한 모델 결과값들이 cv_results_ 객체에 할당됨
scores_df = pd.DataFrame(grid_dtree.cv_results_)
# score 결과값(ndarray형태로 할당됨) 중 특정 칼럼들만 가져오기
scores_df[['params', 'mean_test_score', 'rank_test_score',
'split0_test_score', 'split1_test_score', 'split2_test_score']]
마지막 결과값인 DataFrame을 출력하면 아래의 그림과 같다. 아래의 DataFrame은 각각의 하이퍼파라미터 조합을 일일이 적용해서 도출된 각 모델링들의 성능을 결과표로 정리한 것이다.(원래 반환해주는 결과값이 n차원의 array 형태라서 직접 pandas를 이용해 DataFrame을 만들어준 것이다.(참고로 파라미터값의 종류 즉, max_depth, min_samples_split인 이유는 위에서 사용하는 모델이 Decision Tree이기 때문이다.)
이제 위에서 GridSearch를 수행한 여러개의 모델 중 가장 성능이 좋은 모델을 출력하기 위해 다음과 같은 코드를 실행시킨다.
# 최적의 파라미터는 best_params_에 할당되어있음
print(f"최적의 파라미터 : {grid_dtree.best_params_}")
print(f"최적의 파라미터로 모델의 정확도 : {grid_dtree.best_score_}")
print()
print()
# 최적의 파라미터로 학습되어 있는 모델링 할당
estimator = grid_dtree.best_estimator_
# 최적의 모델로 예측해보고 실제값이랑 정확도 비교
pred_y = estimator.predict(test_x)
print(f"실제값과 예측값 정확도 : {accuracy_score(test_y, pred_y)}")
결과화면은 다음과 같다.
'Data Science > Machine Learning 구현' 카테고리의 다른 글
[ML] Regression metric과 Polynominal Regression 구현하기 (2) | 2020.08.05 |
---|---|
[ML] Scikit-learn을 이용한 Feature engineering 구현하기 (0) | 2020.08.03 |
[ML] Scikit-learn을 이용한 Stacking 구현하기 (4) | 2020.08.03 |
[ML] Scikit-learn을 이용해 Ensemble 모델들 구현하기 (0) | 2020.07.30 |
[ML] Precision 과 Recall의 Trade-off, 그리고 ROC Curve (0) | 2020.07.27 |