본문 바로가기
Machine Learning

[혼자 공부하는 머신러닝+딥러닝] 교차 검증과 그리드 서치

by 가론노미 2023. 5. 9.
[혼자 공부하는 머신러닝+딥러닝] 책의 내용을 정리한 글입니다.

개념

검증 세트

: 하이퍼파라미터 튜닝을 위해 모델을 평가할 때, 테스트 세트를 사용하지 않기 위해 훈련 세트에서 다시 떼어 낸 데이터 세트

  • 훈련 세트와 테스트 세트만 사용해 테스트를 반복하며 파라미터 튜닝을 진행할 경우, 결국은 테스트 세트에 잘 맞는 모델이 만들어질 수 있음
  • 훈련 세트에서 모델을 훈련하고 검증 세트로 모델을 평가하며 가장 적합한 파라미터를 찾은 후 테스트 세트로 최정 점수를 평가

교차 검증

: 훈련 세트를 여러 폴드로 나눈 다음 한 폴드가 검증 세트의 역할을 하고 나머지 폴드에서는 모델을 훈련하는 방식으로 모든 폴드에 대해 검증 점수를 얻어 평균하는 방법

  • 훈련에 더 많은 데이터를 사용하고 안정적인 검증 점수를 얻기 위해 사용

그리드 서치

: 하이퍼파라미터 탐색을 자동화해 주는 도구

  • 탐색할 매개변수를 나열하면 교차 검증을 수행하여 가장 좋은 검증 점수의 매개변수 조합을 선택
  • 마지막으로 최적의 매개변수 조합에서 교차 검증에 사용한 훈련 세트가 아닌 전체 훈련 세트를 사용해 최종 모델을 훈련

랜덤 서치

: 그리드서치와 다르게 탐색할 값을 직접 나열하는 것이 아닌 탐색 값을 샘플링할 수 있는 확률 분포 객체를 전달하여 하이퍼파라미터 탐색

  • 연속된 매개변수 값을 탐색할 때 유용
  • 지정된 횟수만큼 샘플링하여 교차 검증을 수행하기 때문에 시스템 자원이 허락하는 만큼 탐색량을 조절할 수 있음

→ 매개변수 값의 범위나 간격을 미리 정하기 어려운 경우, 너무 많은 매개변수 조건이 있어 그리드 서치 수행 시간이 오래 걸리는 경우 사용

 

코드 작성

최적의 모델을 위한 하이퍼파라미터를 탐색하는 방법들을 알아보자

와인 데이터 준비

import pandas as pd

wine = pd.read_csv('https://bit.ly/wine_csv_data')

data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()

from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
    data, target, test_size=0.2, random_state=42)

검증 세트를 이용한 모델 성능 확인

sub_input, val_input, sub_target, val_target = train_test_split(
    train_input, train_target, test_size=0.2, random_state=42)
    
from sklearn.tree import DecisionTreeClassifier

dt = DecisionTreeClassifier(random_state=42)
dt.fit(sub_input, sub_target)

print(dt.score(sub_input, sub_target))
print(dt.score(val_input, val_target))
# => 0.9971133028626413
# => 0.864423076923077

교차 검증을 이용한 모델 성능 확인

from sklearn.model_selection import cross_validate

# 교차 검증 함수로 기본적으로 5-폴드 교차 검증을 수행
scores = cross_validate(dt, train_input, train_target)
print(scores)
'''
=> {'fit_time': array([0.01130939, 0.01102304, 0.01342034, 0.01033998, 0.01032615]),
    'score_time': array([0.00168252, 0.00321698, 0.00149465, 0.00159431, 0.00147653]),
    'test_score': array([0.86923077, 0.84615385, 0.87680462, 0.84889317, 0.83541867])}
'''

import numpy as np

# 교차 검증의 최종 점수 == 검증 폴드의 점수 평균
print(np.mean(scores['test_score']))
# => 0.855300214703487

test_test_split() 함수 사용 없이 훈련 세트를 섞어 교차 검증 사용

from sklearn.model_selection import StratifiedKFold

# 교차 검증 수행 시에 분할기로 StratifiedKFold 지정
scores = cross_validate(dt, train_input, train_target, cv=StratifiedKFold())
print(np.mean(scores['test_score']))
# => 0.855300214703487

# 훈련 세트를 섞은 후 교차 검증 수행
splitter = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
scores = cross_validate(dt, train_input, train_target, cv=splitter)
print(np.mean(scores['test_score']))
# => 0.8574181117533719

그리드 서치를 이용한 하이퍼파라미터 튜닝

from sklearn.model_selection import GridSearchCV

# 탐색할 매개변수 값을 나열
params = {'min_impurity_decrease': [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}

gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
gs.fit(train_input, train_target)

# 가장 높은 점수의 매개변수 조합으로 훈련된 모델
dt = gs.best_estimator_
print(dt.score(train_input, train_target))
# => 0.9615162593804117

# 그리드 서치가 찾은 최적의 매개변수
print(gs.best_params_)
# => {'min_impurity_decrease': 0.0001}

# 5번의 교차 검증으로 얻은 점수
print(gs.cv_results_['mean_test_score'])
# => [0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]

# 넘파이 함수를 사용해 가장 높은 점수의 매개변수를 출력
best_index = np.argmax(gs.cv_results_['mean_test_score'])
print(gs.cv_results_['params'][best_index])
# => {'min_impurity_decrease': 0.0001}



# 조금 더 복잡한 매개변수 조합 탐색
params = {'min_impurity_decrease': np.arange(0.0001, 0.001, 0.0001),
          'max_depth': range(5, 20, 1),
          'min_samples_split': range(2, 100, 10)
          }
          
gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
gs.fit(train_input, train_target)

print(gs.best_params_)
# => {'max_depth': 14, 'min_impurity_decrease': 0.0004, 'min_samples_split': 12}
print(np.max(gs.cv_results_['mean_test_score']))
# => 0.8683865773302731

랜덤 서치를 이용한 하이퍼파라미터 튜닝

from scipy.stats import uniform, randint

# 매개변수 값 목록이 아닌 확률 분포 객체(radnint, uniform)를 전달
params = {'min_impurity_decrease': uniform(0.0001, 0.001),
          'max_depth': randint(20, 50),
          'min_samples_split': randint(2, 25),
          'min_samples_leaf': randint(1, 25),
          }
          
from sklearn.model_selection import RandomizedSearchCV

gs = RandomizedSearchCV(DecisionTreeClassifier(random_state=42), params, 
                        n_iter=100, n_jobs=-1, random_state=42)
gs.fit(train_input, train_target)

# 최적의 매개변수 조합 출력
print(gs.best_params_)
# => {'max_depth': 39, 'min_impurity_decrease': 0.00034102546602601173,
		'min_samples_leaf': 7, 'min_samples_split': 13}

# 최고의 교차 검증 점수
print(np.max(gs.cv_results_['mean_test_score']))
# => 0.8695428296438884

# 최적의 모델로 테스트 세트 성능 확인
dt = gs.best_estimator_

print(dt.score(test_input, test_target))
# => 0.86

이 장에서 사용된 핵심 패키지와 함수

scikit-learn

  • cross_validate()
    • 교차 검증을 수행하는 함수
    • 첫번째 매개변수에 교차 검증을 수행할 모델 객체를 전달, 두 번째에 특성 데이터, 세 번째에 타깃 데이터를 전달
    • scoring - 검증에 사용할 평가 지표를 지정할 수 있음
      • 분류 모델은 정확도를 의미하는 'accuracy', 회귀 모델은 결정 계수를 의미하는 'r2'
    • cv - 교차 검증 폴드 수나 스플리터 객체를 지정할 수 있음
      • 분류 모델은 StratifiedKFold 클래스를 사용, 회귀 모델은 KFold 클래스 사용
    • n_jobs - 교차 검증을 수행할 때 사용할 CPU 코어 수 지정(기본값 - 1, -1로 지정 시 모든 코어 사용)
    • return_train_score - True로 지정 시 훈련 세트의 점수도 반환 (기본값 - False)
  • GridSearchCV
    • 교차 검증으로 하이퍼파라미터 탐색을 수행 후 최상의 모델을 찾아 훈련 세트 전체를 사용해 최종 모델 훈련
    • 첫 번째 매개변수로 그리드 서치를 수행할 모델 객체 전달, 두 번째에는 탐색할 모델의 매개변수와 값 전달
    • scoring, cv, n_jobs, return_train_score 매개변수는 cross_validate() 함수와 동일
  • RandomizedSearchSV
    • 교차 검증으로 랜덤한 하이퍼파라미터 탐색을 수행 후 최상의 모델을 찾아 훈련 세트 전체를 사용해 최종 모델 훈련
    • 첫 번째 매개변수로 그리드 서치를 수행할 모델 객체 전달, 두 번째에는 탐색할 모델의 매개변수와 확률 분포 객체 전달
    • scoring, cv, n_jobs, return_train_score 매개변수는 cross_validate() 함수와 동일