3 minute read

교차 검증과 그리드 서치

검증 세트

검증세트

테스트 세트를 사용하지 않고 훈련 세트를 더 나누어서 이를 측정하는 방법

검증 세트는 훈련 세트의 양에 따라서 20% ~30% 정도 나눈 후에 매개변수튜닝을 해준다.

검증 세트로 best parameter를 찾으면 검증 세트와 훈련세트를 합친(데이터의 양이 많을 수록 좋기 때문에) 후에 최종 훈련을 한다.

이후 테스트 세트로 최종적으로 점수를 확인한다.

테스트 세트는 실제 서비스에 투입하기 전에 사용한다.

실전에서는 타깃이 없는 처음 본 데이터를 분석해야 한다 검증 세트는 이미 훈련 한 데이터가 되기 때문에 테스트 세트를 남겨놓고 훈련이 다 끝난 후에 사욯한다.

최종적으로 실전에 투입되었을 때 어느 정도의 점수를 갖는지 확인하는 용도이다

Data=wine

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)

image-20220510145206975

검증 세트를 만드는 방법은 간단하다 train_test_split을 두 번 해주는 것이 끝이다.

.shape로 결과를 확인해보면 train_input값이 나눠진 것을 확인할 수 있다.

$ 5197=4157+1040 $

교차 검증

교차 검증 훈련

데이터가 많이 않은 경우에 검증 세트가 20%이상 차지하는게 부담일 수 있다. 이 경우에 훈련세트를 n개의 폴드로 나누어서 각 폴드마다 검증세트를 전부 해보는 것을 교차 검증이라 한다.

훈련 세트의 비율을 여러 비율로 수행하여 검증 점수를 평균내어서 가장 최적의 검증 점수를 가늠해 볼 수 있다.

훈련 세트를 3 부분으로 나누어서 교차 검증을 하면 3-폴드 교차 검증이라고 한다.

주로 5-폴드 교차 검증이나 10-폴드 교차 검증을 많이 사용한다.

교차 검증을 사용하면서 파라미터를 조금씩 바꾸어서 최적의 파라미터를 찾아 좋은 모델을 만들어내는 것이 목표이다

교차 검증은 train_test_split을 여러번 사용하는 것이 아닌 cross_validate함수를 사용한다.

from sklearn.model_selection import cross_validate

scores = cross_validate(dt, train_input, train_target)
print(scores)

이 함수는 검증세트를 분리해서(기본 5 폴드)훈련한 후 각강의 검증점수를 출력해준다.

image-20220510150503116

이것을 평균내면

import numpy as np

print(np.mean(scores['test_score']))

image-20220510150526630

검증점수의 평균값을 구할 수 있다.

이 검증점수의 평균값으로 매개변수를 바꾸면 된다.

전통적인 머신러닝에서 사용하는 방법 딥러닝에서는 다른 방법을 사용한다.

딥러닝에서는 데이터가 풍부하기 때문에 한 번만 덜어낸 검증세트도 양이 충분하기도 하고 5번이나 나눠서훈련하는 데 들어가는 비용이 크기 때문에 사용하지 않는다.

분할기를 사용한 교차 검증

from sklearn.model_selection import StratifiedKFold

scores = cross_validate(dt, train_input, train_target, cv=StratifiedKFold())
print(np.mean(scores['test_score']))

# 수동으로 splitter를 사용하여 하는 것
splitter = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
# 10개의 폴드 / 랜덤 / 
scores = cross_validate(dt, train_input, train_target, cv=splitter)
print(np.mean(scores['test_score']))

cross_validate는 기본적으로 5개를 사용했지만 cv=n를 지정해서 폴드를 n개 지정할 수 있다.

cv=splitter를 지정해서 나누는 동작을 상세하게 제어할 수 있다.

StratifiedKFold() : 분류 모델(안에 있는 클래스들이 골고루 나누어지도록)

KFold() : 회귀 모델

분류/회귀 모델의 객체를 전달하면 자동으로 StratifiedKFold/KFold 를 인식한다.

하이퍼파라미터 튜닝

하지만 이 하이퍼파라미터가 여러 개 있을 경우에 이것을 전부 값을 바꿔가면서 테스트하기는 어렵다.

이 하이퍼파라미터들의 최적값을 구해주는 도구가 Scikit learn의 GreedSerchCV 클래스인데 이 클래스는 교차 검증과 하이퍼파라미터 튜닝을 동시에 해준다.

그리드 서치

from sklearn.model_selection import GridSearchCV

#매개변수를 dictionary로 지정
params = {'min_impurity_decrease': [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]} 
#min_impurity_decrease:정보이득 (큰 값일수록 분할을 잘 한 것)
# '파라미터' : [테스트 해볼 값들] 리스트로 지정


gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
# GridSearchCV의 첫번째 : 결정트리 객체, 두번째 : 매개변수 사전으로 지정한 것 
# 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} 파라미터 확인

print(gs.cv_results_['mean_test_score'])
#[0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]
# 교차검증 점수 확인
params = {'min_impurity_decrease': np.arange(0.0001, 0.001, 0.0001),
          'max_depth': range(5, 20, 1),
          # 5~20까지 1단위
          'min_samples_split': range(2, 100, 10)
          # 분할하기위한 최소 샘플 개수 2~100 10단위
          # 총 훈련 9*15*10*5(교차검증5회)=6750개의 모델 훈련
          }

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

너무나 많은 모델을 훈련해야 한다.

랜덤 서치

매개변수 값이 수치일 때 범위나 간격을 정하기 어려운 문제가 있다. 또한 매개변수가 너무 많아서 너무나 많은 모델을 테스트해야 하는 문제가 있다. 이럴 때 랜덤 서치를 사용한다.

scipy / uniform,randint

from scipy.stats import uniform, randint
# scipy의 확률분포 클래스 사용 
# 균등분포 샘플링 
# uniform : 실수 샘플링
# randint : 정수 샘플링

rgen = randint(0, 10)
rgen.rvs(10) #(0~9개 샘플링)
# rvs:random value sampling
# array([0, 6, 1, 7, 4, 1, 4, 9, 7, 0])

np.unique(rgen.rvs(1000), return_counts=True)
# 중복된 요소 제거하는?
# (array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
#  array([100, 114, 115,  88, 108,  80, 104,  91, 101,  99]))

ugen = uniform(0, 1)
ugen.rvs(10)
# array([0.51779717, 0.98001995, 0.74399202, 0.42051715, 0.10599781,
#       0.03354555, 0.20682412, 0.96337699, 0.43563022, 0.78230147])

범위를 크게 주고 그 안에서 랜덤하게 샘플링하여 촘촘한 매개변수값들을 전투 테스트해보지 않아도 된다

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)
# RandomizedSearchCV (모델 객체 / 파라미터 / 모델 개수 랜덤 샘플링 100번 / 코어 전부사용 / 시드)

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']))
# best_params_로 찾은 검증세트의 점수
# 0.8695428296438884 

dt = gs.best_estimator_
print(dt.score(test_input, test_target))
# 테스트 세트 결과
# 0.86

참고

박해선,혼자 공부하는 머신러닝, 한빛미디어, 2021,242~259p