지금까지는 GridSearchCV를 이용하여 하이퍼 파라미터 튜닝을 수행했다. 하이퍼 파라미터의 수가 많은 XGBoost나 LightGBM과 같은 모델은 GridSearchCV를 이용하여 튜닝 시 많은 수행 시간이 요구된다. (저 모델들이 하이퍼 파라미터가 상대적으로 많기 때문...!)
그렇다면 더 효율적인 하이퍼 파라미터 방식이 있을까? 바로 베이지안 최적화 기반의 HyperOpt가 있겠다!
목적 함수의 식을 제대로 알 수 없는 함수에서, 최대 또는 최소의 함수 반환 값을 만드는 최적 입력값을 가능한 적은 시도를 통해 빠르고 효과적으로 찾아주는 방식
베이지안 확률에 기반을 두고 있는 최적화 기법
베이지안 최적화의 주요 요소
대체 모델은 획득 함수로부터 최적 함수를 예측할 수 있는 입력값을 추천받고 이를 기반으로 최적 함수 모델 개선, 획득 함수는 개선된 대체 모델을 기반으로 최적 입력값을 계산하는 프로세스
이때, 입력값은 하이퍼 파라미터에 해당함, 즉, 대체 모델은 획득 함수로부터 하이퍼 파라미터를 추천받아 모델 개선 수행, 획득 함수는 개선된 모델을 바탕으로 더 정확한 하이퍼 파라미터를 계산함!
위에서 정리한 베이지안 최적화 프로세스 2단계에서 대체 모델은 최적 함수를 추정할 때 다양한 알고리즘을 사용할 수 있다. 일반적으로는 가우시안 프로세스 (Gaussian Process) 를 적용하지만, HyperOpt는 트리 파르젠 Estimator (TPE, Tree-structure Parzen Estimator) 를 사용한다.
HyperOpt는 다음과 같은 프로세스를 통해 사용할 수 있다.
!pip install hyperopt
## hp 모듈을 사용하여 입력 변수명 및 검색 공간 설정
from hyperopt import hp
search_space = {'x':hp.quniform('x', -10, 10, 1), ## dictionary 형태
'y':hp.quniform('y', -15, 15, 1)}
from hyperopt import STATUS_OK
## 목적 함수 생성
## 변수, 검색 공간을 입력으로 받고, 특정 값을 반환하는 구조
def objective_func(search_space):
x = search_space['x']
y = search_space['y']
retval = x ** 2 - 20 * y
return retval
from hyperopt import fmin, tpe, Trials
import numpy as np
## 입력 결과를 저장할 객체 생성
trial_val = Trials()
## 목적 함수의 최솟값을 반환하는 최적 입력 변수를 5번 시도로 찾아냄
## fmin() 함수는 아래의 주요 인자를 가짐
best_01 = fmin(fn=objective_func, ## 목적 함수
space=search_space, ## 검색 공간
algo=tpe.suggest, ## 베이지안 최적화 적용 알고리즘
max_evals=20, ## 입력 시도 횟수
trials=trial_val, ## 시도한 입력 값 및 입력 결과 저장
#rstate=np.random.default_rng(seed=0) ## fmin()을 시도할 때마다 동일한 결과를 가질 수 있도록 설정하는 랜덤 시드
)
print('best:', best_01)
trial_val.results
trial_val.vals
## trial_val을 dataframe 형태로 변환하여 확인
import pandas as pd
losses = [loss_dict['loss'] for loss_dict in trial_val.results]
result_df = pd.DataFrame(
{
'x':trial_val.vals['x'],
'y':trial_val.vals['y'],
'losses':losses
}
)
result_df.head(5)
result_df.plot()
HyperOpt를 사용하여 XGBoost 하이퍼 파라미터를 최적화 해본다. 주의사항은 아래와 같다.
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
## 유방암 데이터셋 로드
dataset = load_breast_cancer()
features = dataset.data
labels = dataset.target
## 데이터를 Pandas DataFrame으로 로드
cancer_df = pd.DataFrame(data=features, columns=dataset.feature_names)
cancer_df['target'] = labels
cancer_df.head(5)
## 학습 및 검증 데이터셋으로 데이터 분리
X_features = cancer_df.iloc[:, :-1]
y_label = cancer_df.iloc[:, -1]
## 학습, 검증용 데이터셋 비율 8 : 2
X_train, X_test, y_train, y_test = train_test_split(X_features, y_label, test_size=0.2, random_state=156)
## X_train, y_train을 다시 9 : 1 비율로 분리
## => XGBoost가 제공하는 교차 검증 성능 평가 및 조기 중단을 수행하기 위함
X_tr, X_val, y_tr, y_val = train_test_split(X_train, y_train, test_size=0.1, random_state=156)
## 모델 성능 평가 함수 선언
from sklearn.metrics import accuracy_score, confusion_matrix, precision_score, recall_score, f1_score, roc_auc_score
def get_clf_eval(y_test, pred=None, pred_proba=None):
confusion = confusion_matrix(y_test, pred)
accuracy = accuracy_score(y_test, pred)
precision = precision_score(y_test, pred)
recall = recall_score(y_test, pred)
f1 = f1_score(y_test, pred)
roc_auc = roc_auc_score(y_test, pred_proba)
print('오차 행렬')
print(confusion)
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f}, F1: {3:.4f}, AUC: {4:.4f}'.format(accuracy, precision, recall, f1, roc_auc))
## 1. 검색 공간 설정
from hyperopt import hp
xgb_search_space = {
'max_depth':hp.quniform('max_depth', 5, 20, 1), ## 정수형 하이퍼 파라미터 => quniform 사용
'min_child_weight':hp.quniform('min_child_weight', 1, 2, 1), ## 정수형 하이퍼 파라미터 => quniform 사용
'learning_rate':hp.uniform('learning_rate', 0.01, 0.2),
'colsample_bytree':hp.uniform('colsample_bytree', 0.5, 1),
}
## 2. 목적 함수 설정
## 검색 공간에서 설정한 하이퍼 파라미터들을 입력 받아서 XGBoost를 학습시키고, 평가 지표를 반환하도록 구성되어야 함
from sklearn.model_selection import cross_val_score ## 교차 검증
from xgboost import XGBClassifier
from hyperopt import STATUS_OK
def objective_func(search_space):
xgb_clf = XGBClassifier(
n_estimators=100,
max_depth=int(search_space['max_depth']), ## int형으로 형변환 필요
min_child_weight=int(search_space['min_child_weight']), ## int형으로 형변환 필요
learning_rate=search_space['learning_rate'],
colsample_bytree=search_space['colsample_bytree'],
eval_metric='logloss'
)
accuracy = cross_val_score(xgb_clf, X_train, y_train, scoring='accuracy', cv=3) ## 3개의 교차 검증 세트로 정확도 반환
## acc는 cv=3 개수만큼의 결과를 리스트로 가짐, 이를 평균하여 반환하되, -1을 곱함
return {
'loss':(-1) * np.mean(accuracy),
'status':STATUS_OK
}
## 3. fmin()을 사용하여 최적 하이퍼 파라미터 찾기
from hyperopt import fmin, tpe, Trials
trial_val = Trials() ## 결과 저장
best = fmin(
fn=objective_func,
space=xgb_search_space,
algo=tpe.suggest,
max_evals=50, ## 최대 반복 횟수 지정
trials=trial_val,
)
best ## 정수형 하이퍼 파라미터 값이 뒤에 .0이 붙어서 실수형으로 반환됨을 유의하기!
## 획득한 최적의 하이퍼 파라미터를 이용하여 모델 선언
xgb_wrapper = XGBClassifier(
n_estimators=400,
learning_rate=round(best['learning_rate'], 5),
max_depth=int(best['max_depth']),
min_child_weight=int(best['min_child_weight']),
colsample_bytree=round(best['colsample_bytree'], 5)
)
## early stopping
evals = [(X_tr, y_tr), (X_val, y_val)]
## model train
xgb_wrapper.fit(
X_tr, y_tr,
early_stopping_rounds=50,
eval_metric='logloss',
eval_set=evals,
verbose=True
)
## eval
preds = xgb_wrapper.predict(X_test)
pred_proba = xgb_wrapper.predict_proba(X_test)[:, 1]
get_clf_eval(y_test, preds, pred_proba)
GridSearchCV를 이용하여 XGBoost, LightGBM 하이퍼 파라미터를 튜닝할 경우 수행 시간이 오래 걸린다는 단점이 있다. 이때 생각할 수 있는 것이 바로 베이지안 최적화 기반의 HyperOpt이다. HyperOpt는 1) 검색 공간 설정 2) 목적 함수 설정 3) fmin() 함수를 이용한 최적 하이퍼 파라미터 찾기 순서로 진행된다. 가령 HyperOpt를 이용하여 XGBoost 하이퍼 파라미터를 최적화 할 경우, 1) 데이터 로드 및 분리 2) HyperOpt로 최적 하이퍼 파라미터 찾기 3) 최적 하이퍼 파라미터를 XGBoost 인자로 입력하여 모델 학습 및 평가 순으로 진행할 수 있겠다.