Edited at

ベイズ最適化によるハイパーパラメータチューニングをやってみた


概要


  • BayesianOptimizationというPythonライブラリを用いて、
    ベイズ最適化によるハイパーパラメータチューニングをやってみます。


背景・目的


  • 機械学習においてパラメータ探索する際、苦労するところとして下記があります。


    • 総当たりで行う場合、コストが膨大にかかることが予想される。

    • 各パラメータの値は、どのくらいにしたほうがいいのか迷う。



→ このような状況に対して、ベイズ最適化をパラメータ探索に利用することで、効率的なモデル作成を実現できます。


なぜ総当たり(グリッドサーチ)と比べ効率的にモデル作成ができるのか


  • グリッドサーチは(10, 10)のパラメータを与えられると、
    画像の各点のように10×10=100通りの学習を実行します。1

    grid-1.png


  • パラメータ探索にベイズ最適化を用いた場合、下記のgif画像のように、
    正答率が高くなりそうなパラメータを中心に、指定した回数(今回は50回)を学習するため、
    学習回数を抑えつつモデル作成を行うことが出来ます。1

    1111.gif


非常に簡単な理屈ですね。では実際にやってみましょう。


環境


  • Mac OS High Sierra

  • Python 3.7.2

  • Jupyter Notebook

  • pandas 0.24.2

  • scikit-learn 0.20.3


  • bayesian-optimization 1.0.1


    • 今回の肝です。こちらをインストールしていきます。




準備①:bayesian-optimizationのインストールと必要なライブラリのインポート

$ pip install bayesian-optimization

import pandas as pd

import numpy as np

from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier

from bayes_opt import BayesianOptimization

import warnings
warnings.filterwarnings('ignore')


準備②:データ準備


  • kaggleのタイタニックのデータを用います。2

  • 特徴量加工、前処理に関してはこちらのサイトを引用し作成しています。3


ハイパーパラメータチューニングの実行


学習

def randomforest_cv(n_estimators, max_depth, min_samples_split, min_samples_leaf):

# クロスバリデーションを行う
val = cross_val_score(
RandomForestClassifier(
n_estimators=int(n_estimators), #パラメータが整数を求めている場合int型にする
max_depth=int(max_depth),
min_samples_split=int(min_samples_split),
min_samples_leaf=int(min_samples_leaf),
random_state=0
),
xs, y, # 学習データ
scoring='accuracy',
cv=10,
n_jobs=-1
).mean() #平均値を返す
return val

# 探索範囲などの設定
rf_cv_bo = BayesianOptimization(
randomforest_cv, # 上の関数を与える
{'n_estimators': (10, 300), # (min,max)でパラメータの探索範囲を指定
'max_depth': (3, 30),
'min_samples_split': (2, 30),
'min_samples_leaf' : (1, 4),
},
verbose=2, # 0だと学習過程を表示をしない、デフォルトの2なら全て表示する、1は最高値が更新されたら表示
random_state=0
)

# ベイズ最適化によるハイパーパラメータチューニングの実行
# init_points=5・・・今回は5つの離れたポイントを初期値として学習をスタートします。
# n_iter=50・・・50パターンのパラメータで学習を行います。
# acq='ei'・・・現在の最大値との差の期待値が最も高い点を選ぶ。

rf_cv_bo.maximize(init_points=5, n_iter=50, acq='ei')

# 実行結果の内、最も精度の良かった評価値とパラメータの値をprint
print(rf_cv_bo.max['target'])
print('n_estimators:', rf_cv_bo.max['params']['n_estimators'])
print('max_depth:', rf_cv_bo.max['params']['max_depth'])
print('min_samples_split:', rf_cv_bo.max['params']['min_samples_split'])
print('min_samples_leaf:', rf_cv_bo.max['params']['min_samples_leaf'])

上記のコードを実行すると、下記の画像のように学習結果が表示されていきます。

スクリーンショット 2019-04-14 16.22.53.png



スクリーンショット 2019-04-14 16.23.03.png


最も精度の良かったパラメータの値を取得し、モデルを作成

rf = RandomForestClassifier(

n_estimators=int(rf_cv_bo.max['params']['n_estimators']),
max_depth=int(rf_cv_bo.max['params']['max_depth']),
min_samples_split=int(rf_cv_bo.max['params']['min_samples_split']),
min_samples_leaf=int(rf_cv_bo.max['params']['min_samples_leaf']),
random_state=0
)

rf.fit(xs, y) # 学習データ

Y_pred = rf.predict(xs_test) # テストデータ


結果・まとめ


  • submitスコア:0.80382

  • submit順位:1,100位(上位約10%)でした。

  • 特徴量作成が大切なことに変わりはありませんが、
    ベイズ最適化をパラメータチューニングに用いることで、少ない試行回数で十分な結果を出すことが出来ました。


参考