HyperOpt를 이용한 XGBoost 하이퍼 파라미터 튜닝

HyperOpt를 이용하여 XGBoost 하이퍼 파라미터를 최적화 해본다. 주의사항은 다음과 같다.

  • HyperOpt는 입력값과 반환값이 모두 실수형이기 때문에 정수형 하이퍼 파라미터 입력 시 형변환 필요
  • HyperOpt의 목적 함수는 최소값을 반환할 수 있도록 최적화하기 때문에 성능 값이 클수록 좋은 성능 지표일 경우 -1을 곱해주어야 함(분류는 성능 값이 클수록 좋기때문에 -1을 곱해주고, 회귀는 성능 값이 작을수록 좋기 때문에 -1을 곱하지 않고 그대로 사용) 

○필요한 패키지 및 모듈 불러오기

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

○데이터 로드

# 유방암 데이터셋 
dataset = load_breast_cancer()

cancer_df = pd.DataFrame(data=dataset.data, columns=dataset.feature_names)
cancer_df['target'] = dataset.target
cancer_df.head()

○데이터 분리

# 피처와 타겟 분리
X_features = cancer_df.iloc[:, :-1]
y_label = cancer_df.iloc[:, -1]
# 전체 데이터 중 80%는 학습용 데이터, 20%는 테스트용 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(X_features, y_label, test_size=0.2, random_state=156)

# 학습 데이터를 다시 학습과 검증 데이터로 분리(학습:9 / 검증:1)
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 *

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))

○HyperOpt 설정 1 - 검색 공간 설정

  • 하이퍼 파라미터 검색 공간 설정(정수형 하이퍼 파라미터 → hp.quniform() 사용)
# HyperOpt 검색 공간 설정
from hyperopt import hp

xgb_search_space = {'max_depth':hp.quniform('max_depth', 5, 20, 1),
                   'min_child_weight':hp.quniform('min_child_weight', 1, 2, 1),
                   'learning_rate':hp.uniform('learning_rate', 0.01, 0.2),
                   'colsample_bytree':hp.uniform('colsample_bytree', 0.5, 1)}

○HyperOpt 설정 2 - 목적 함수 설정

  • 검색 공간에서 설정한 하이퍼 파라미터들을 입력받아서 XGBoost를 학습하고, 평가 지표를 반환하도록 구성

한 가지 아쉬운 점은 XGBoost, LightGBM에서는 cross_val_score() 적용할 경우 조기 중단(early stopping)이 지원되지 않음. 따라서 조기 중단을 하기 위해서 KFold로  학습과 검증용 데이터 세트를 만들어서 직접 교차검증을 수행해야 함

(참고:https://www.inflearn.com/questions/794506/hyperopt%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%95%98%EC%9D%B4%ED%8D%BC%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%ED%8A%9C%EB%8B%9D)

# HyperOpt 목적 함수 설정
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)
    
    # accuracy는 cv=3 개수만큼의 결과를 리스트로 가짐. 이를 평균하여 반환하되 -1을 곱함
    return {'loss':-1 * np.mean(accuracy), 'status':STATUS_OK}

○HyperOpt 설정 3 - fmin()을 이용해 최적 하이퍼 파라미터 도출

# HyperOpt 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,
           # rstate=np.random.default_rng(seed=9)
           )

print('best:', best)

정수형 하이퍼 파라미터(max_depth, min_child_weight) 값이 실수형으로 도출됨을 유의하기

○추출된 최적 하이퍼 파라미터를 이용하여 XGBoost의 인자로 입력하여 학습 및 평가

  • 입력하기 전에 정수형 하이퍼 파라미터 형변환 필수
# 도출된 최적 하이퍼 파라미터를 이용하여 모델 선언
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) - 50
evals = [(X_tr, y_tr), (X_val, y_val)]
xgb_wrapper.fit(X_tr, y_tr, early_stopping_rounds=50, eval_metric='logloss',
               eval_set=evals)

# 예측
preds = xgb_wrapper.predict(X_test)
pred_proba = xgb_wrapper.predict_proba(X_test)[:, 1]

# 모델 평가
get_clf_eval(y_test, preds, pred_proba)
[0]	validation_0-logloss:0.56908	validation_1-logloss:0.60656
[1]	validation_0-logloss:0.47637	validation_1-logloss:0.53729
[2]	validation_0-logloss:0.40361	validation_1-logloss:0.48320
... ... ...
[130]	validation_0-logloss:0.01499	validation_1-logloss:0.24451
[131]	validation_0-logloss:0.01494	validation_1-logloss:0.24374
[132]	validation_0-logloss:0.01490	validation_1-logloss:0.24530
... ... ...
[179]	validation_0-logloss:0.01335	validation_1-logloss:0.24535
[180]	validation_0-logloss:0.01332	validation_1-logloss:0.24475
[181]	validation_0-logloss:0.01330	validation_1-logloss:0.24549

오차 행렬
[[34  3]
 [ 2 75]]
정확도:0.9561, 정밀도:0.9615, 재현율:0.9740, F1:0.9677, AUC:0.9933

 

+ Recent posts