Data Analysis & ML/Machine Learning

[Machine Learning][머신러닝] 과적합과 해결방법(그리드서치/파이프라인)

YSY^ 2020. 8. 31. 14:56

이번 포스팅에서는 머신러닝 모델링할때 주의해야할 과적합이 무엇인지, 그리고 과적합을 해결하는 방법에 대해 알아보겠습니다.

Overfitting (과대적합)

  • 일반화 (Generalization)
    • 모델이 새로운 데이터셋(테스트 데이터)에 대하여 정확히 예측하면 이것을 (훈련데이터에서 테스트데이터로) 일반화 되었다고 말한다.
    • 모델이 훈련 데이터로 평가한 결과와 테스트 데이터로 평가한 결과의 차이가 거의 없고 좋은 평가지표를 보여준다.
  • 과대적합 (Overfitting)
    • 모델이 훈련 데이터에 대한 예측성능은 너무 좋지만 일반성이 떨어져 새로운 데이터(테스트 데이터)에 대해선 성능이 좋지 않은 것을 Overfitting이라고 한다.
    • 이는 모델이 훈련 데이터 세트의 특징을 너무 맞춰서 학습 되었기 때문에 일반화 되지 않아 새로운 데이터셋(테스트세트)에 대한 예측 성능이 떨져 발생한다.
  • 과소적합 (Underfitting)
    • 모델이 훈련 데이터과 테스트 데이터셋 모두에서 성능이 안좋은 것을 말한다.
    • 모델이 너무 간단하여 훈련 데이터에 대해 충분히 학습하지 못해 데이터셋의 패턴들을 다 찾아내지 못해서 발생한다.

Overfitting(과대적합)의 원인

  • 모델이 너무 복잡한 경우
    • Overfitting을 줄이기 위한 규제 하이퍼파라미터 설정
    • Feature 개수 줄이기
  • 데이터의 문제
    • 데이터 전처리를 통해 질 좋은 데이터를 만든다.
    • 데이터를 더 수집한다.
      • 현실적으로 어려운 경우가 많다.(비용과 시간의 문제)

Overfitting 확인

  • 위스콘시 암환자 데이터 사용
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

cancer = load_breast_cancer()
X, y = cancer.data, cancer.target

X_train, X_test, y_train, y_test =\
    train_test_split(X, y, stratify=y, random_state=1)


from sklearn.tree import DecisionTreeClassifier
#모델 생성
dt = DecisionTreeClassifier()
#학습
dt.fit(X_train, y_train)

# ==> DecisionTreeClassifier(ccp_alpha=0.0, class_weight=None, criterion='gini',
#                       max_depth=None, max_features=None, max_leaf_nodes=None,
#                       min_impurity_decrease=0.0, min_impurity_split=None,
#                       min_samples_leaf=1, min_samples_split=2,
#                       min_weight_fraction_leaf=0.0, presort='deprecated',
#                       random_state=None, splitter='best')

from sklearn.metrics import accuracy_score
pred_train = dt.predict(X_train) #학습데이터로 분류 예측
pred_test = dt.predict(X_test) #테스트데이토 분류 예측

print('Train 정확도:', accuracy_score(y_train, pred_train))
print('Test 정확도:', accuracy_score(y_test, pred_test))


Train 정확도: 1.0
Test 정확도: 0.9370629370629371
# 결정트리의 트리구조 시각화 - graphviz 이용
from sklearn.tree import export_graphviz
from graphviz import Source

g = export_graphviz(dt,  #학습이 끝난 결정트리객체
                    out_file=None,
                    feature_names = cancer.feature_names,
                    class_names = cancer.target_names,
                    rounded=True,
                    filled=True)
graph = Source(g)
graph

수많은 가지가 형성된다.

 

Overfitting (과대적합) 해결 방법

1. Decision Tree 복잡도 제어(규제)

  • Decision Tree 모델을 복잡하게 하는 것은 노드가 너무 많이 만들어 지는 것이다.
    • 노드가 많이 만들어 질수록 훈련데이터셋에 과대적합된다.
  • 적절한 시점에 트리 생성을 중단해야 한다.
  • 규제 관련 하이퍼파라미터
    • max_depth: 트리의 최대 깊이(질문수)
    • max_leaf_nodes: Leaf node 의 최대 개수를 제한
    • min_samples_leaf: leaf 노드가 되기 위한 sample 수 제한
    • min_samples_split: 나누는 최소 샘플 수
# max_depth: 작은 값일 수록 단순한 모델이 된다.
dt2 = DecisionTreeClassifier(max_depth=3)

dt2.fit(X_train, y_train)
pred_train2 = dt2.predict(X_train)
pred_test2 = dt2.predict(X_test)


print('Train:', accuracy_score(y_train, pred_train2))
print('Test:', accuracy_score(y_test, pred_test2))

Train: 0.971830985915493
Test: 0.9440559440559441
from sklearn.tree import export_graphviz
from graphviz import Source

g = export_graphviz(dt2,  #학습이 끝난 결정트리객체
                    out_file=None,
                    feature_names = cancer.feature_names,
                    class_names = cancer.target_names,
                    rounded=True,
                    filled=True)
graph = Source(g)
graph

깊이 수를 제한하니 가지수가 많이 줄어들었다.

 

2. GridSearch (격자탐색)

  • GridSearch란 하이퍼파리미터에 넣을 수 있는 값을 순차적으로 입력한 다음에 가장 높은 성능을 보이는 하이퍼파라미터를 찾는 방법
  • 적당한 max depth를 찾는것이 핵심이다.
max_depth_list = range(1,20)
# 학습/테스트 데이터셋의 정확도들 저장할 리스트
train_acc_list = []
test_acc_list = []
for max_depth in max_depth_list:
    tree = DecisionTreeClassifier(max_depth=max_depth)
    tree.fit(X_train, y_train)

    pred_train = tree.predict(X_train)
    pred_test = tree.predict(X_test)

    train_acc_list.append(accuracy_score(y_train, pred_train))
    test_acc_list.append(accuracy_score(y_test, pred_test))

import pandas as pd
d = {
    "max_depth":max_depth_list,
    "Train정확도":train_acc_list,
    "Test정확도":test_acc_list
}
acc_df = pd.DataFrame(d)
acc_df.head()

import matplotlib.pyplot as plt
acc_df.set_index('max_depth').plot(figsize=(10,7))
plt.xticks(max_depth_list)
plt.ylabel('정확도')
plt.show()

Grid Search 를 이용한 하이퍼파라미터 튜닝

  • 하이퍼 파라미터 (Hyper Parameter)
    • 머신러닝 모델을 생성할 때 사용자가 직접 설정하는 값
    • 머신러닝 모델에 따라 다르기는 하지만 많은 하이퍼파라미터들을 변경할 수 있다.
  • 하이퍼 파라미터 튜닝
    • 하이퍼 파라미터의 설정에 따라 모델의 성능이 달라진다.

최적의 하이퍼파라미터 찾기

  1. 만족할 만한 하이퍼파라미터들의 값의 조합을 찾을 때 까지 일일이 수동으로 조정
  2. GridSearch 사용
    • GridSearchCV()
      • 시도해볼 하이퍼파라미터들을 지정하면 모든 조합에 대해 교차검증 후 제일 좋은 성능을 내는 하이퍼파라미터 조합을 찾아준다.
      • 적은 수의 조합의 경우는 괜찮지만 시도할 하이퍼파라미터와 값들이 많아지면 너무 많은 시간이 걸린다.
  3. Random Search 사용
    • RandomizedSearchCV()
      • GridSeach와 동일한 방식으로 사용한다.
      • 모든 조합을 다 시도하지 않고 각 반복마다 임의의 값만 대입해 지정한 횟수만큼만 평가한다.

GridSearchCV 매개변수및 결과조회

  • 주요 매개변수
    • estimator: 모델객체 지정
    • params : 하이퍼파라미터 목록을 dictionary로 전달 '파라미터명':[파라미터값 list] 형식
    • scoring: 평가 지표
    • cv : 교차검증시 fold 개수.
    • n_jobs : 사용할 CPU 코어 개수 (None:1(기본값), -1: 모든 코어 다 사용)
  • 메소드
    • fit(X, y) : 학습
    • predict(X): 제일 좋은 성능을 낸 모델로 predict()
    • predict_proba(X): 제일 좋은 성능을 낸 모델로 predict_proba() 호출
  • 결과 조회 변수
    • cv_results_ : 파라미터 조합별 결과 조회
    • best_params_ : 가장 좋은 성능을 낸 parameter 조합 조회
    • best_estimator_ : 가장 좋은 성능을 낸 모델 반환
from sklearn.model_selection import GridSearchCV
from sklearn.tree import DecisionTreeClassifier

dt = DecisionTreeClassifier()
param_grid = {
    'max_depth':[4,5,6,7],
    'max_leaf_nodes':[3,5,7,9,10],
    'criterion':['entropy'] #기본값 말고 다른값을 사용할 경우.
}

gs = GridSearchCV(dt, 
                  param_grid=param_grid,
                  scoring='accuracy',
                  cv = 3,
                  n_jobs=-1)
gs.fit(X_train, y_train)

RandomizedSearchCV

  • 주요 매개변수
    • estimator: 모델객체 지정
    • param_distributions : 하이퍼파라미터 목록을 dictionary로 전달 '파라미터명':[파라미터값 list] 형식
    • n_iter : 파라미터 검색 횟수
    • scoring: 평가 지표
    • cv : 교차검증시 fold 개수.
    • n_jobs : 사용할 CPU 코어 개수 (None:1(기본값), -1: 모든 코어 다 사용)
  • 메소드
    • fit(X, y) : 학습
    • predict(X): 제일 좋은 성능을 낸 모델로 predict()
    • predict_proba(X): 제일 좋은 성능을 낸 모델로 predict_proba() 호출
  • 결과 조회 변수
    • cv_results_ : 파라미터 조합별 결과 조회
    • best_params_ : 가장 좋은 성능을 낸 parameter 조합 조회
    • best_estimator_ : 가장 좋은 성능을 낸 모델 반환
from sklearn.model_selection import RandomizedSearchCV

dt = DecisionTreeClassifier()
param = {
    'max_depth':range(1, 21),
    'max_leaf_nodes':range(5, 101, 5),
    'criterion':['entropy','gini']
}
# 800
n_iter = 80 #기본 :10

rs = RandomizedSearchCV(dt,
                        param_distributions=param,
                        n_iter=n_iter, 
                        cv=5, 
                        n_jobs=-1)

rs.fit(X_train, y_train)

 

3. Pipeline (파이프라인)

  • 여러 단계의 머신러닝 프로세스 (전처리의 각 단계, 모델생성, 학습) 처리 과정을 설정하여 한번에 처리되도록 한다.
  • 파이프라인은 여러개의 변환기와 마지막에 변환기 또는 추정기를 넣을 수 있다. (추정기-Estimator는 마지막에 만 올 수 있다.)
  • 전처리 작업 파이프라인 : 변환기들로만 구성
  • 전체 프로세스 파이프 라인 : 마지막에 추정기를 넣는다

Pipeline 생성 및 학습

  • (이름, 변환기) 를 리스트로 묶어서 전달한다.
  • 마지막에는 추정기가 올 수있다.
  • pipeline.fit()
    • 각 순서대로 각 변환기의 fit_transform()이 실행되고 결과가 다음 단계로 전달된다. 마지막 단계에서는 fit()만 호출한다.
    • 보통 마지막이 추정기일때 사용
  • pipeline.fit_transform()
    • fit()과 동일하나 마지막 단계에서도 fit_transform()이 실행된다.
    • 보통 전처리 작업 파이프라인(모든 단계가 변환기)일 때 사용
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline

# 정규화 -> SVM 학습

pipe = [
    ('scaler', StandardScaler()),
    ('svm', SVC())
]
pipeline = Pipeline(pipe, verbose=True)
print(pipeline.steps)

pred_train = pipeline.predict(X_train)
pred_test = pipeline.predict(X_test)

accuracy_score(y_train, pred_train)
0.9882629107981221

accuracy_score(y_test, pred_test)
0.9790209790209791

GridSearch에서 Pipeline 사용

  • 하이퍼파라미터 지정시 파이프라인 프로세스이름__하이퍼파라미터 형식으로 지정한다.
pipe = [
    ('scaler', StandardScaler()),
    ('svm', SVC())
]
pipeline = Pipeline(pipe, verbose=True)

param_grid = {
    'svm__C':[0.01, 0.1,1,10,100],
    'svm__gamma':[0.01, 0.1,1,10,100]
}
gs = GridSearchCV(pipeline, param_grid=param_grid, cv=5)
gs.fit(X_train, y_train)
gs.best_params_ # {'svm__C': 10, 'svm__gamma': 0.01}

make_pipeline() 함수를 이용한 파이프라인 생성을 편리하게 하기

from sklearn.pipeline import make\_pipeline

pipeline2 = make\_pipeline(StandardScaler(),  
SVC())  
print(pipeline2.steps)  

728x90
반응형