사이킷런 - GridSearchCV

IngCoding·2022년 6월 11일
1

머신러닝

목록 보기
1/34

이 글은 파이썬 머신러닝 완벽 가이드 책 내용을 기반으로 정리했습니다.
내용출처 : 파이썬 머신러닝 완벽가이드

사이킷런으로 붓꽃 품종 예측

  • 사이킷런은 파이썬 머신러닝 라이브러리 중 가장 많이 사용되는 라이브러리
  • 최근엔 텐서플로, 케라스 등 딥러닝 라이브러리가 강세가 되고 있으나 여전히 많은 데이터 분석가가 의존하는 대표적인 파이썬 ML 라이브러리

사이킷런 라이브러리 활용

  1. 학습/테스트 데이터 분리 모델을 활용하면 과적합 문제 발생
    (데이터가 학습데이터에 지나치게 최적화돼서 다른 데이터는 제대로 예측하지 못하게 됨)

  2. 교차검증을 통해 과적합 문제를 해결할 수 있음

  3. 또한 하이퍼 파라미터 튜닝을 통해 모델의 성능을 높일 수 있음

GridSearchCV : 교차 검증 + 하이퍼 파라미터 튜닝

  • 머신러닝 모델은 수 없이 많은 함수가 정의되어 있는데, 함수 안에는 많은 파라미터(매개변수)가 존재한다.
# 매개변수는 함수의 내부에서 해당 함수로 전달된 값을 가리키기 위한 변수를 의미
# 예를 들어 아래와 같이 GridSearchCV 함수를 호출하면 () 안에 수 많은 매개변수들이 있다. 
# 매개변수로 특정 데이터는 물론, 다른 함수를 설정할수도 있어 수없이 많은 매개변수가 연결되기도 한다.  
grid_dtree = GridSearchCV(dtree, param_grid=parameters, cv=3, refit=True, return_train_score=True)
  • 매개변수(파라미터) 중 사용자가 직접 세팅할 수 있는 파라미터를 하이퍼 파라미터라고 한다.
  • 하이퍼 파라미터 튜닝을 통해 학습 조건을 잘 설정해야 최대의 성능을 내는 머신러닝 모델을 얻을 수 있다.
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

# iris 데이터를 로드
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()

### hyper-parameter 들을 딕셔너리 형태로 설정
parameters = {'max_depth':[1, 2, 3], 'min_samples_split':[2,3]}
import pandas as pd

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

# 붓꽃 Train 데이터로 param_grid의 하이퍼 파라미터들을 순차적으로 학습/평가 .
grid_dtree.fit(X_train, y_train)
grid_dtree.cv_results_
{'mean_fit_time': array([0.00056871, 0.00049432, 0.00065072, 0.00065748, 0.00079942,
        0.00065295]),
 'std_fit_time': array([1.26325154e-04, 4.01947295e-06, 1.44944553e-04, 4.08382253e-05,
        1.58665848e-04, 9.04048405e-05]),
 'mean_score_time': array([0.00030931, 0.00029031, 0.00042295, 0.00033903, 0.00038147,
        0.00041676]),
 'std_score_time': array([5.49178737e-05, 1.54025701e-05, 1.09174017e-04, 2.73638340e-05,
        8.19441107e-05, 9.32557035e-05]),
 'param_max_depth': masked_array(data=[1, 1, 2, 2, 3, 3],
              mask=[False, False, False, False, False, False],
        fill_value='?',
             dtype=object),
 'param_min_samples_split': masked_array(data=[2, 3, 2, 3, 2, 3],
              mask=[False, False, False, False, False, False],
        fill_value='?',
             dtype=object),
 'params': [{'max_depth': 1, 'min_samples_split': 2},
  {'max_depth': 1, 'min_samples_split': 3},
  {'max_depth': 2, 'min_samples_split': 2},
  {'max_depth': 2, 'min_samples_split': 3},
  {'max_depth': 3, 'min_samples_split': 2},
  {'max_depth': 3, 'min_samples_split': 3}],
 'split0_test_score': array([0.7  , 0.7  , 0.925, 0.925, 0.975, 0.975]),
 'split1_test_score': array([0.7, 0.7, 1. , 1. , 1. , 1. ]),
 'split2_test_score': array([0.7 , 0.7 , 0.95, 0.95, 0.95, 0.95]),
 'mean_test_score': array([0.7       , 0.7       , 0.95833333, 0.95833333, 0.975     ,
        0.975     ]),
 'std_test_score': array([1.11022302e-16, 1.11022302e-16, 3.11804782e-02, 3.11804782e-02,
        2.04124145e-02, 2.04124145e-02]),
 'rank_test_score': array([5, 5, 3, 3, 1, 1], dtype=int32),
 'split0_train_score': array([0.7   , 0.7   , 0.975 , 0.975 , 0.9875, 0.9875]),
 'split1_train_score': array([0.7   , 0.7   , 0.9375, 0.9375, 0.9625, 0.9625]),
 'split2_train_score': array([0.7   , 0.7   , 0.9625, 0.9625, 0.9875, 0.9875]),
 'mean_train_score': array([0.7       , 0.7       , 0.95833333, 0.95833333, 0.97916667,
        0.97916667]),
 'std_train_score': array([1.11022302e-16, 1.11022302e-16, 1.55902391e-02, 1.55902391e-02,
        1.17851130e-02, 1.17851130e-02])}
# 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

-> 가장 좋은 hyper-parameter는 {'max_depth': 3, 'min_samples_split': 3}

# (참고) GridSearchCV 결과 전체 확인
grid_dtree.cv_results_
{'mean_fit_time': array([0.00062299, 0.00051562, 0.00049472, 0.00049384, 0.00055027,
        0.00056203]),
 'std_fit_time': array([1.91088984e-04, 3.85178642e-05, 4.78423975e-06, 5.67327127e-06,
        5.41397446e-05, 4.54078739e-05]),
 'mean_score_time': array([0.00034698, 0.00027935, 0.00027466, 0.00027641, 0.00029429,
        0.00029667]),
 'std_score_time': array([1.06681118e-04, 5.47267447e-06, 1.01152436e-06, 7.37000982e-07,
        2.53145697e-05, 2.36794450e-05]),
 'param_max_depth': masked_array(data=[1, 1, 2, 2, 3, 3],
              mask=[False, False, False, False, False, False],
        fill_value='?',
             dtype=object),
 'param_min_samples_split': masked_array(data=[2, 3, 2, 3, 2, 3],
              mask=[False, False, False, False, False, False],
        fill_value='?',
             dtype=object),
 'params': [{'max_depth': 1, 'min_samples_split': 2},
  {'max_depth': 1, 'min_samples_split': 3},
  {'max_depth': 2, 'min_samples_split': 2},
  {'max_depth': 2, 'min_samples_split': 3},
  {'max_depth': 3, 'min_samples_split': 2},
  {'max_depth': 3, 'min_samples_split': 3}],
 'split0_test_score': array([0.7  , 0.7  , 0.925, 0.925, 0.975, 0.975]),
 'split1_test_score': array([0.7, 0.7, 1. , 1. , 1. , 1. ]),
 'split2_test_score': array([0.7 , 0.7 , 0.95, 0.95, 0.95, 0.95]),
 'mean_test_score': array([0.7       , 0.7       , 0.95833333, 0.95833333, 0.975     ,
        0.975     ]),
 'std_test_score': array([1.11022302e-16, 1.11022302e-16, 3.11804782e-02, 3.11804782e-02,
        2.04124145e-02, 2.04124145e-02]),
 'rank_test_score': array([5, 5, 3, 3, 1, 1], dtype=int32),
 'split0_train_score': array([0.7   , 0.7   , 0.975 , 0.975 , 0.9875, 0.9875]),
 'split1_train_score': array([0.7   , 0.7   , 0.9375, 0.9375, 0.9625, 0.9625]),
 'split2_train_score': array([0.7   , 0.7   , 0.9625, 0.9625, 0.9875, 0.9875]),
 'mean_train_score': array([0.7       , 0.7       , 0.95833333, 0.95833333, 0.97916667,
        0.97916667]),
 'std_train_score': array([1.11022302e-16, 1.11022302e-16, 1.55902391e-02, 1.55902391e-02,
        1.17851130e-02, 1.17851130e-02])}
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)))
GridSearchCV 최적 파라미터: {'max_depth': 3, 'min_samples_split': 2}
GridSearchCV 최고 정확도: 0.9750
테스트 데이터 세트 정확도: 0.9667
# 테스트 데이터 예측 정확도 확인
accuracy_score(y_test, pred)
0.9666666666666667

estimator 종류

  1. 분류 : DecisionTreeClassifier, RandomForestClassifier, ...
  2. 회귀 : LinearRegression, ...
# GridSearchCV의 refit으로 이미 학습이 된 estimator 반환
# 위에서 dtree = DecisionTreeClassifier() 로 estimator를 선언했고, 이를 GridSearchCV에 넣었으므로,
estimator = grid_dtree.best_estimator_
estimator
DecisionTreeClassifier(max_depth=3)
# GridSearchCV의 best_estimator_는 이미 최적 하이퍼 파라미터로 학습이 됨
pred = estimator.predict(X_test)
print('테스트 데이터 세트 정확도: {0:.4f}'.format(accuracy_score(y_test, pred)))
테스트 데이터 세트 정확도: 0.9667

결론:
GridSearchCV를 사용했더니 교차검증을 통해 최적의 모델 성능을 내는 하이퍼 파라미터 튜닝을 했다.
즉, 정확도가 높은 모델을 얻었다!

profile
Data & PM

0개의 댓글