[파머완] ch4.8 HyperOpt 하이퍼 파라미터 튜닝

­반소희·2022년 7월 8일
1

지금까지는 GridSearchCV를 이용하여 하이퍼 파라미터 튜닝을 수행했다. 하이퍼 파라미터의 수가 많은 XGBoost나 LightGBM과 같은 모델은 GridSearchCV를 이용하여 튜닝 시 많은 수행 시간이 요구된다. (저 모델들이 하이퍼 파라미터가 상대적으로 많기 때문...!)

그렇다면 더 효율적인 하이퍼 파라미터 방식이 있을까? 바로 베이지안 최적화 기반HyperOpt가 있겠다!

ch4.8 베이지안 최적화의 개요

  • 목적 함수의 식을 제대로 알 수 없는 함수에서, 최대 또는 최소의 함수 반환 값을 만드는 최적 입력값을 가능한 적은 시도를 통해 빠르고 효과적으로 찾아주는 방식

  • 베이지안 확률에 기반을 두고 있는 최적화 기법

    • 베이지안 확률이 새로운 데이터를 기반으로 사후 확률을 개선해 나가듯이, 베이지안 최적화는 새로운 데이터를 입력받았을 때, 최적 함수를 예측하는 사후 모델을 개선해 나가며 최적 함수 모델을 만들어 낸다.
  • 베이지안 최적화의 주요 요소

    1. 대체 모델 (Surrogate Model)
    2. 획득 함수 (Acquisition Function)

    대체 모델은 획득 함수로부터 최적 함수를 예측할 수 있는 입력값을 추천받고 이를 기반으로 최적 함수 모델 개선, 획득 함수는 개선된 대체 모델을 기반으로 최적 입력값을 계산하는 프로세스

    이때, 입력값은 하이퍼 파라미터에 해당함, 즉, 대체 모델은 획득 함수로부터 하이퍼 파라미터를 추천받아 모델 개선 수행, 획득 함수는 개선된 모델을 바탕으로 더 정확한 하이퍼 파라미터를 계산함!

ch4.8 베이지안 최적화 프로세스

  1. 랜덤하게 하이퍼 파라미터를 샘플링하고 성능 결과 관측
  2. 관측된 값을 기반으로 대체 모델은 최적 함수 및 신뢰 구간 (= 결과 오류 편차 = 추정 함수의 불확실성을 의미) 를 추정
  3. 추정된 최적 함수를 기반으로 획득 함수는 다음으로 관찰할 하이퍼 파라미터를 계산 후 이를 대체 모델에 전달
  4. 획득 함수로부터 전달된 하이퍼 파라미터를 수행하여 관측된 값을 기반으로 대체 모델 다시 갱신
  5. 2번 ~ 4번을 반복하며 대체 모델의 불확실성 개선 및 점차 정확한 최적 함수 추정 가능

ch4.8 HyperOpt 사용법 익히기

위에서 정리한 베이지안 최적화 프로세스 2단계에서 대체 모델은 최적 함수를 추정할 때 다양한 알고리즘을 사용할 수 있다. 일반적으로는 가우시안 프로세스 (Gaussian Process) 를 적용하지만, HyperOpt는 트리 파르젠 Estimator (TPE, Tree-structure Parzen Estimator) 를 사용한다.

HyperOpt는 다음과 같은 프로세스를 통해 사용할 수 있다.

  1. 검색 공간 설정
  2. 목적 함수 절정
  3. fmin() 함수를 통해 베이지안 최적화 기법에 기반한 최적의 입력 값 찾기

  • HyperOpt 설치
!pip install hyperopt
  • 검색 공간 설정
  • hp 모듈로 입력 변수 및 검색 범위 설정
## 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
  • 목적 함수의 반환 값이 최소가 될 수 있는 최적의 입력 값을 베이지안 최적화 기법에 기반하여 찾아야 함
  • HyperOpt는 이러한 기능을 fmin() 함수를 통해 제공함
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 및 vals 확인
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()

ch4.8 HyperOpt을 사용하여 XGBoost 하이퍼 파라미터 튜닝하기

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

  • HyperOpt는 입력 값과 반환 값이 모두 실수형이기 때문에 정수형 하이퍼 파라미터 입력 시 형변환 필요
  • HyperOpt는 목적 함수의 최솟값을 반환할 수 있도록 최적화하는 것이기 때문에 성능 값이 클수록 좋은 성능 지표일 경우 -1을 곱해주어야 함

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))
  • HyperOpt 설정 1 - 검색 공간 설정
    • 하이퍼 파라미터의 검색 공간을 설정함
## 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),
}
  • HyperOpt 설정 2 - 목적 함수 설정
    • 검색 공간에서 설정한 하이퍼 파라미터들을 입력 받아서 XGBoost를 학습시키고, 평가 지표를 반환하도록 구성되어야 함
## 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
  }
  • HyperOpt 설정 3 - fmin()을 사용하여 최적 하이퍼 파라미터 찾기
## 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이 붙어서 실수형으로 반환됨을 유의하기!
  • 획득한 최적의 하이퍼 파라미터를 이용하여 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

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 인자로 입력하여 모델 학습 및 평가 순으로 진행할 수 있겠다.

profile
행복한 소히의 이것저것

0개의 댓글

관련 채용 정보