테스트 세트를 사용하지 않으면 일반화 성능을 올바르게 예측할 수 없다.
이것을 해결하기 위해 훈련 세트를 80 : 20으로 훈련, 테스트로 나누고 그 80을 다시 80 : 20으로 훈련, 검증 세트로 나눈다.
그러면 최종적으로 훈련, 검증, 테스트가 60 : 20 : 20으로 나뉘게 된다.
import pandas as pd
from sklearn.model_selection import train_test_split
wine = pd.read_csv('https://bit.ly/wine_csv_data')
data = wine[['alcohol', 'sugar', 'pH']]
target = wine['class']
# train 80 : test 20
train_input, test_input, train_target, test_target = train_test_split(data, target, test_size=0.2, random_state=42)
# train 80 : val 20
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))
이렇게 원래는 학습 데이터를 학습시키고 훈련 점수, 테스트 점수를 확인하지만 훈련 점수, 검증 점수를 보게 된다.
훈련 세트를 검증 세트로 나눠서 데이터가 줄어들었다.
데이터가 적기 때문에 학습을 잘 못하기 때문에 이때 교차 검증을 사용한다.
교차 검증은 검증 세트에서 일부분 때서 평가하고, 이 과정을 여러번 반복한다.
이 교차 검증을 원하는 수 만큼 할 수 있고 이것을 K-폴드 교차 검증이라고 한다.
from sklearn.model_selection import cross_validate
scores = cross_validate(dt, train_input, train_target)
print(scores)
# {'fit_time': array([0.0040555 , 0.00372982, 0.00385141, 0.00396609, 0.00490022]), 'score_time': array([0.0008688 , 0.00079441, 0.00080442, 0.00135827, 0.00124073]), '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
이렇게 하면 검증 점수의 평균을 얻을 수 있다.
from sklearn.model_selection import StratifiedKFold
splitter = StratifiedKFold(
n_splits=10, # K개
shuffle=True, # 섞음
random_state=42
)
scores = cross_validate(dt, train_input, train_target, cv=splitter)
print(np.mean(scores['test_score']))
모델을 학습시킬 때 모델이 학습할 수 없어서 사용자가 지정해야만 하는 파라미터를 하이퍼파라미터라고 한다.
머신러닝 라이브러리를 사용할 때의 하이퍼파라미터는 모두 클래스나 메서드의 매개변수로 표현된다.
하지만 만약 결정 트리 모델에서 max_depth
와 min_samples_split
을 변경할 수 있는데, 만약 모델을 여러번 학습을 시도해서 최적의 max_depth
를 찾았을 때 그 값을 고정하고 min_samples_split
의 최적의 값을 찾으려고 한다면, min_samples_split
을 변경하는 순간 최적의 max_depth
도 변경하게 된다.
그래서 여러 매개변수의 최적의 값을 쉽게 찾기 우해서 그리드 서치를 사용한다.
from sklearn.model_selection import GridSearchCV
# min_inpurity_decrease의 최적값을 찾아보기 위해 여러 값 시도
params = {
'min_impurity_decrease': [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]
}
gs = GridSearchCV(
DecisionTreeClassifier(random_state=42),
params,
n_jobs=-1 # CPU 코어 수
)
gs.fit(train_input, train_target)
dt = gs.best_estimator_
print(dt.score(train_input, train_target)) # 0.9615162593804117
print(gs.best_estimator_) # min_impurity_decrease=0.0001
print(gs.cv_results_['mean_test_score']) # [0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]
print(gs.cv_results_['params'][gs.best_index_]) # {'min_impurity_decrease': 0.0001}
이렇게 수동으로 고르지 않고 자동으로 고를 수 있다.
min_impurity_decrease
from sklearn.model_selection import GridSearchCV
import numpy as np
params = {
'min_impurity_decrease': np.arange(0.0001, 0.001, 0.0001), # 9개
'max_depth': range(5, 20, 1), # 15개
'min_samples_split': range(2, 100, 10) # 10개
} # 총 9 * 15 * 10 = 1350개 * 5(K-Fold 기본 값) = 6750
gs = GridSearchCV(
DecisionTreeClassifier(random_state=42),
params,
n_jobs=-1 # CPU 코어 수
)
gs.fit(train_input, train_target)
dt = gs.best_estimator_
print(dt.score(train_input, train_target)) # 0.892053107562055
print(gs.best_params_) # {'max_depth': 14, 'min_impurity_decrease': np.float64(0.0004), 'min_samples_split': 12}
print(np.max(gs.cv_results_['mean_test_score'])) # 0.8683865773302731
매개변수의 범위를 너무 크게 잡으면 학습이 굉장히 오래 걸린다. 이럴때 랜덤 서치를 사용한다.
랜덤 서치에서는 매개변수 값의 목록을 전달하는 것이 아닌, 확률 분포 객체를 전달한다.
from scipy.stats import uniform, randint
rgen = randint(0, 10)
print(rgen.rvs(10)) # [1 8 9 3 1 1 3 3 7 2]
print(np.unique(rgen.rvs(1000), return_counts=True))
# (array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
# array([105, 86, 103, 109, 92, 91, 98, 118, 109, 89]))
ugen = uniform(0, 1)
print(ugen.rvs(10))
# [0.99885329 0.38056558 0.47262844 0.55087183 0.03469407 0.51688101 0.45908212 0.54994372 0.73262376 0.89946438]
from sklearn.model_selection import RandomizedSearchCV
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)
}
rs = RandomizedSearchCV(
DecisionTreeClassifier(random_state=42),
params,
n_iter=100,
n_jobs=-1,
random_state=42
)
rs.fit(train_input, train_target)
print(rs.best_params_) # {'max_depth': 39, 'min_impurity_decrease': np.float64(0.00034102546602601173), 'min_samples_leaf': 7, 'min_samples_split': 13}
print(np.max(rs.cv_results_['mean_test_score'])) # 0.8695428296438884
dt = rs.best_estimator_
print(dt.score(test_input, test_target)) # 0.86