scikit-optimize の BayesSearchCV を用いたベイズ最適化によるハイパーパラメータ探索

ベイズ最適化でハイパーパラメータをチューニングする

ハイパーパラメータのチューニングではパラメータを様々に振りながら学習を繰り返し、一番パフォーマンスが良いところを探し出します。 学習時間はモデルやデータサイズなどによって異なりますが、効率よく探索するかしないかで良いパラメータを得るまでの時間が大きく変わります。 探索方法には様々あり、基本的なところではグリッドサーチ(全探索)、ランダムサーチ(ランダム探索)があります。

今回紹介するものは、ベイズ最適化の手法を用いて効率よく探索する枠組みです *1

BayesSearchCV

ここで紹介するのは CrossValidation で汎化精度を確かめながら、ハイパーパラメータを探索する scikit-optimize の BayesSearchCV です。

github.com

https://scikit-optimize.github.io/#skopt.BayesSearchCV

ハイパーパラメータの汎化精度は交差検証でわかります(*2)。 そのため、交差検証で汎化精度を見ながら最適なハイパーパラメータを探索します。

手順で言うと次のようになります。

  1. ハイパーパラメータをランダムにピックアップする.
  2. 交差検証で精度を見る
  3. ベイズ最適化で次に探索するハイパーパラメータを定める(→2 へ)

これを全部やってくれるのが BayesSearchCV で、以下がサンプルコードとなります。

サンプル実装

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
import numpy as np
import scipy as sp
import pandas as pd
from skopt import BayesSearchCV
from skopt.space import Real, Categorical, Integer
from sklearn.model_selection import train_test_split
import xgboost as xgb


# データセットを読み込み
from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.75, random_state=0)

estimator = xgb.XGBClassifier(
    booster='gbtree',
    silent=True,
    objective='binary:logistic',
    base_score=0.5,
    eval_metric='auc',
    n_jobs=-1
)

# パラメータ探索範囲設定
# 探索範囲 min,max,distribution を space クラスでで指定(min, max,(distribution))
from skopt.space import Real, Categorical, Integer
param_grid = {
    'n_estimators': Integer(1, 4),
    'learning_rate': Real(0.01, 0.5, 'log-uniform'),
    'max_depth': Integer(0, 3)
}

# 参考
# https://www.kaggle.com/shaz13/santander-xgboost-bayesian-optimization/code
# https://www.kaggle.com/nanomathias/bayesian-optimization-of-xgboost-lb-0-9769
#

'''
GridSearchCV 系と比べた固有の API(一部)
- search_spaces : パラメータ探索範囲設定
- optimizer_kwargs : 最適化パラメータ。探索戦略やガウス過程パラメータなど。- 
- fit_params : clf.fit(X, y) に設定するパラメータ。学習実行時のパラメータ指定.
'''
clf = BayesSearchCV(
    estimator=estimator,
    search_spaces=param_grid,
    scoring='roc_auc',
    cv=3,
    n_jobs=-1,
    n_iter=10,
    verbose=1,
    refit=True
)
def status_print(optim_result):
    '''
    ベイズ最適化のイテレーションで呼ばれるコールバック
    '''
    results    = pd.DataFrame(clf.cv_results_)
    new_params = results.tail(1).params.values[0]
    new_score  = results.tail(1).mean_test_score
    print('Model #%d/%d : ROC-AUC(newest/best) = %.4f / %.4f' % (
        len(results),
        clf.n_iter,
        new_score,
        clf.best_score_
    ))

# BayesSearchCV は sklearn の他の交差検証サーチ系クラス(RandomSearchCVなど)と同じAPIを持っています
clf.fit(X, y, callback=status_print)

print(pd.DataFrame(clf.cv_results_))

# 検証データのパフォーマンス
print(clf.score(X_test, y_test))

探索の結果は clf.cv_resuts_ フィールドに格納されていて、pd.DataFrame にすると以下のようなデータとなっています。 この中には以下のデータが入っています(一部分列挙)。

  • param_* は探索パラメータ
  • mean_test_score は交差検証スコアの平均
  • split{n}_test_score は交差検証の各分割でのスコア(これらを平均したものが mean_test_score)
  • std_test_score は交差検証スコアの偏差
mean_fit_time mean_score_time mean_test_score split0_test_score split1_test_score split2_test_score std_fit_time std_score_time std_test_score param_colsample_bylevel param_colsample_bytree
0 0.005673011 0.00219202 0.832636822 0.827494378 8.28E-01 0.842857143 0.000176986 0.00030934 0.007207978 0.432490483 0.101987568
1 0.005337397 0.002478361 0.5 0.5 0.5 0.5 0.000566812 0.000611297 0 0.45403928 0.928587396
2 0.005687555 0.002456427 0.5 5.00E-01 0.5 0.5 0.000361052 6.48E-05 0 0.471067626 0.880029631
3 0.005241315 0.00255092 0.5 5.00E-01 0.5 0.5 0.000284118 0.000263844 0 0.24114647 0.786112636
4 0.006174246 0.002198935 0.834788739 7.56E-01 0.832465381 0.915966387 0.000542449 0.000200357 0.065150251 0.699309626 0.752064985
5 0.007870754 0.002580643 0.5 0.5 0.5 0.5 0.000749173 0.000241813 0 0.932821363 0.339574308
6 0.004701535 0.00221777 0.5 0.5 0.5 0.5 0.000507002 0.000167713 0 0.818819988 0.79140138
7 0.004785299 0.002165318 0.5 0.5 5.00E-01 0.5 0.000287597 0.000235932 0 0.999741524 0.381359767
8 0.005346219 0.002468665 0.5 5.00E-01 0.5 0.5 0.000562654 0.000213206 0 0.55477075 0.31067584
9 0.005452951 0.00238506 0.5 5.00E-01 0.5 0.5 0.000326225 0.000221597 0 0.586112963 0.589381284

ベイズ最適化探索で得られた最適な結果は次のフィールドに格納されます。

  • best_estimator_ : 最適なモデル
  • best_params_ : 最適なハイパーパラメータ
  • best_index : cv_results の最適なパラメータのインデックス
  • best_score : 最適なモデルのスコア

まとめ

scikit-optimize の BayesSearchCV を用いて、ベイズ最適化によるハイパーパラメータ探索を試してみましたが、scikit-learn の RandomSearchCV や GridSearchCV と同じ使い方で、簡単にベイズ最適化を使えることがわかりました。

探索戦略のパラメータなどについては今後調べてみようと思います。