노트와 노트

과적합(Overfitting)과 교차 검증(Cross Validation): K-fold, Stratified K-fold, GridSearchCV 본문

Machine Learning & DeepLearning

과적합(Overfitting)과 교차 검증(Cross Validation): K-fold, Stratified K-fold, GridSearchCV

gellygelly 2022. 3. 25. 22:03

※ 이 글은 <파이썬 머신러닝 완벽 가이드(위키북스)> 책 공부한 내용&개인적으로 공부한 내용을 정리한 글입니다!

 

 

오버피팅(Overfitting)과 언더피팅(Underfitting)

  • 오버피팅: 모델이 학습 데이터셋에 대해 과하게 학습된 상태. 노이즈(noise, 잡음)를 지나치게 반영하여 학습 데이터 외의 다른 데이터에 대해선 예측 성능이 과도하게 떨어짐. 오버피팅이 발생했을 때 일반적인 해결책은 아래와 같다.

1. 모델의 복잡도 낮추기: 모델의 layer 개수를 낮춰 학습 데이터에 비해 과하게 학습되지 않도록 조절

2. Dropout: 학습 시 일부 뉴런의 연결을 끊기

3. L1/L2 정규화

  • L1 정규화 - 예측 영향력이 작은 피처를 0으로 만들어 예측 시 해당 피처가 선택되지 않도록 하는 것
  • L2 정규화 - 상대적으로 큰 가중치의 값을 작게 만들어 특정 가중치가 과하게 커지는 것을 방지함

4. 학습 데이터 보강

  • 언더피팅: 모델이 학습 데이터셋의 특징(패턴)을 충분히 학습하지 못한 상태. 아래와 같은 경우에 발생한다.

1. 학습 반복 횟수가 너무 적어 충분히 학습을 진행하지 못 함

2. 데이터의 특성에 비해 모델이 너무 간단함

3. 데이터 양이 너무 적음

 

 

교차 검증(Cross Validation)

 

교차 검증이란, 데이터 편중을 막기 위해서 별도의 여러 세트로 구성된 학습 데이터 세트와 검증 데이터 세트에서 학습과 평가를 수행하는 것.

 

 

고정된 학습 데이터와 테스트 데이터로 평가를 하다보면 테스트 데이터에만 최적의 성능을 발휘하도록 모델을 유도하게 되는데, 이러한 경우를 방지하기 위해 사용함. 

 

① K 폴드 교차 검증(K-Fold Cross Validation)

 

K개의 데이터 폴드 세트를 만들어서 K번만큼 각 폴드 세트에 학습과 검증을 반복하는 방법. 

k-fold CV / 출처:&nbsp;https://wooono.tistory.com/105

 

② Stratified K 폴드 교차 검증(Stratified K-Fold Cross Validation)

불균형한 분포도를 가진 레이블(결정 클래스) 데이터 집합을 위한 K-폴드 방식. '불균형한 분포도를 가진 레이블(클래스)'란, 특정 레이블 값이 지나치게 많거나 너무 적은 상태를 의미함. 

 

예) 개와 고양이 판단 모델에서 고양이 데이터는 3000개, 개 데이터는 150개인 경우

 

Stratified K-fold는 원본 데이터의 레이블 분포(비율)를 반영하여 이 분포와 동일하게 학습과 검증 데이터를 분배한다. 

 

Stratified k-fold / 출처: dataaspirant.com

 

편리한 교차 검증 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: 사이킷런의 분류 알고리즘 클래스인 Classifier 또는 회귀 알고리즘 클래스인 Regressor를 의미
  • X: 피처 데이터 셋
  • y: 레이블 데이터 셋
  • scoring: 예측 성능 평가 지표
  • cv: 교차 검증 폴드 수
  • return : cv로 지정된 횟수만큼 scoring 파라미터로 지정된 평가 지표로 결과값을 배열로 반환
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score, cross_validate
from sklearn.datasets import load_iris

iris = load_iris()
dt_clf = DecisionTreeClassifier(random_state=156)

data = iris.data
label = iris.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))

 

cross_validate()

 

cross_val_score()는 하나의 성능 평가 지표만 반환 가능하나, cross_validate()는 여러 개의 성능 평가 지표도 반환 가능하며 학습 데이터에 대한 성능 평가 지표와 수행 시간도 같이 제공한다. 

 

cross_validate()의 파라미터 및 예제는 아래 skearn 사이트에서 확인할 수 있다. 

https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.cross_validate.html

 

sklearn.model_selection.cross_validate

Examples using sklearn.model_selection.cross_validate: Categorical Feature Support in Gradient Boosting Categorical Feature Support in Gradient Boosting, Combine predictors using stacking Combine p...

scikit-learn.org

 

③ GridSearchCV 

 

GridSearchCV는 사용자가 튜닝하고자 하는 여러 종류의 하이퍼 파라미터를 순차적으로 테스트하면서 최적의 파라미터를 찾게 해준다. 

 

GridSearchCV의 파라미터인 refit 을 True로 설정 시, 최적의 하이퍼 파라미터를  찾은 뒤 입력된 estimator(classifier, regressor) 객체를 해당 하이퍼 파라미터로 재학습 시킬 수 있어 편리하다.

 

 다만, 순차적으로 파라미터를 테스트하므로 수행 시간이 상대적으로 오래 걸린다는 단점이 있다. 

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV, train_test_split
import pandas as pd

# iris data로 GridSearchCV 실습!

iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2)
dtree = DecisionTreeClassifier()

## 테스트할 파라미터를 딕셔너리 형태로 설정
parameters = {'max_depth':[1,2,3], 'min_samples_split':[2,3]}

# param_grid의 하이퍼 파라미터를 3개의 train, test set fold로 나누어 테스트 수행 설정
### refit = True가 default, True이면 가장 좋은 파라미터 설정으로 재학습시킴
grid_dtree = GridSearchCV(dtree, param_grid =parameters, cv=3, refit=True)

grid_dtree.fit(X_train, y_train)

# GridSearchCV 결과를 추출해 DataFrame으로 변환
scores_df = pd.DataFrame(grid_dtree.cv_results_)
print(scores_df[['params', 'mean_test_score', 'rank_test_score', 'split0_test_score', 'split1_test_score', 'split2_test_score']])

 

## 출력 ##
                                     params  ...  split2_test_score
0  {'max_depth': 1, 'min_samples_split': 2}  ...               0.65
1  {'max_depth': 1, 'min_samples_split': 3}  ...               0.65
2  {'max_depth': 2, 'min_samples_split': 2}  ...               0.95
3  {'max_depth': 2, 'min_samples_split': 3}  ...               0.95
4  {'max_depth': 3, 'min_samples_split': 2}  ...               0.95
5  {'max_depth': 3, 'min_samples_split': 3}  ...               0.95

[6 rows x 6 columns]