今回から「ベイズ最適化」関連は、ベイズ最適化シリーズと題して
お送りしたいと思います。今回は第3回目です。
ベイズ最適化シリーズ(1) -ベイズ最適化の可視化-
ベイズ最適化シリーズ(2) -アンサンブル学習(Voting)の最適化-
今回は、やる♪やる♪といいながら、一か月ほど保留になっていた
XGBoostのハイパーパラメータ探索を行います。
実は、ずっと放置していたわけではなく、しばらくハマっていました。
詳細は、後ほど述べます。
#XGBoostとは
私が説明するよりも良い記事がたくさんありますので、そちらをご覧ください。
例えば、以下の記事が挙げられます。
https://qiita.com/yh0sh/items/1df89b12a8dcd15bd5aa
#ハイパーパラメータ
XGBoostには10種類以上のハイパーパラメータがあります。
ハイパーパラメータの設定によって、精度が数パーセント変わってくる
と言われています。
このハイパーパラメータの探索は、総当たりによる探索や、徐々に範囲を絞っていく手法も
ありますが、時間がかかり過ぎたり、面倒くさい作業が増えたりするデメリットもあります。
#ベイズ最適化によるハイパーパラメータ探索
ベイズ最適化を使えば、短時間で良いハイパーパラメータを探してくれます。
ただし、ベイズ最適化はガウス過程に基づいて探索するため、その過程から
外れる場合は、うまくいかない可能性があります。例えば、多峰性のある
ノイズが入る場合は苦手です。
いずれにしても、デフォルトのモデルで学習させるよりは、ベイズ最適化で
見つけたモデルで学習させた方が、はるかに良い精度が出ると思います。
さっそく実装します。使うデータはお馴染みのMNISTです。
import numpy as np
import pandas as pd
from keras.datasets import mnist
#MNIST データロード
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(60000, 784)
X_test = X_test.reshape(10000, 784)
x_train = X_train.astype('float32')
x_test = X_test.astype('float32')
x_train /= 255
x_test /= 255
x_train = x_train[:1000]
x_test = x_test[:500]
y_train = y_train[:1000]
y_test = y_test[:500]
XGBoostを定義し、デフォルトのハイパーパラメータで精度を出してみます。
import xgboost as xgb
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
#XGBoost
Xgb = xgb.XGBClassifier()
Xgb.fit(x_train,y_train)
xgb_predict = Xgb.predict(x_test)
print("Default XGBoost")
print(accuracy_score(y_test, xgb_predict))
Default XGBoost
0.842
精度は84.2%と出ました。
次に、GPyOptのベイズ最適化を使って、ハイパーパラメータを探索します。
#ベイズ最適化
import GPy
import GPyOpt
import matplotlib.pyplot as plt
def f(x):
model = xgb.XGBClassifier(num_class = 10,
eta = float(x[:,0]),
max_depth = int(x[:,1]),
min_child_weight = float(x[:,2]),
subsample = float(x[:,3]),
colsample_bytree = float(x[:,4]),
gamma = float(x[:,5]),
n_estimators = int(x[:,6]),
learning_rate = float(x[:,7]),
reg_lambda = float(x[:,8]),
reg_alpha = float(x[:,9]),
num_boost_round = int(x[:,10]))
# CV
kfold = KFold(n_splits=10, random_state=7)
results = cross_val_score(model, x_train, y_train, cv=kfold)
score = results.mean()*100
print("Accuracy: %.2f%%" % (results.mean()*100))
return -score
bounds = [{'name': 'eta', 'type': 'continuous', 'domain': (0,5)},
{'name': 'max_depth', 'type': 'continuous', 'domain': (150,300)},
{'name': 'min_child_weight', 'type': 'continuous', 'domain': (1,5)},
{'name': 'subsample', 'type': 'continuous', 'domain': (0,1)},
{'name': 'colsample_bytree', 'type': 'continuous', 'domain': (0.1,1)},
{'name': 'gamma', 'type': 'continuous', 'domain': (0,2)},
{'name': 'n_estimators', 'type': 'continuous', 'domain': (10,50)},
{'name': 'learning_rate', 'type': 'continuous', 'domain': (0,1)},
{'name': 'reg_lambda', 'type': 'continuous', 'domain': (0,1.1)},
{'name': 'reg_alpha', 'type': 'continuous', 'domain': (0,1.1)},
{'name': 'num_boost_round', 'type': 'continuous', 'domain': (200,400)}]
print("Bayesian Optimization")
myBopt = GPyOpt.methods.BayesianOptimization(f=f, domain=bounds)
myBopt.run_optimization(max_iter=50)
#探索履歴描画
result_z = myBopt.Y
plt.figure()
plt.plot(-result_z)
plt.xlabel("epochs")
plt.ylabel("CV Accuracy")
plt.show()
#最適なパラメータの表示
print("best parameters =")
print(myBopt.x_opt)
x = myBopt.x_opt
Xgb = xgb.XGBClassifier(num_class=10, eta=x[0], max_depth=int(x[1]),min_child_weight=x[2],
subsample=x[3], colsample_bytree=x[4], gamma=x[5], n_estimators=int(x[6]),
learning_rate=x[7], reg_lambda=x[8], reg_alpha=x[9],num_boost_round=int(x[10]))
Xgb.fit(x_train,y_train)
#最適なパラメータで再度学習、結果表示
xgb_predict = Xgb.predict(x_test)
print("Optimized XGBoost")
print(accuracy_score(y_test, xgb_predict))
Optimized XGBoost
0.848
精度は84.8%となり、デフォルト値に対し0.6%改善しました。
改善幅は思った以上に小さかったです。
#学習のコツ
MNISTを使ってハイパーパラメータ探索をしましたが、いくつか注意すべきポイントが
あったので記しておきます。
・探索するパラメータの範囲を大きく、そしてイタレーションの数を大きくとれば、
一発でベストなパラメータを見つけてくれると思ったのですが、現実は甘くなかったです。
結論から言うと、(パラメータの範囲を大きく、イタレーションは200くらい)→
(範囲を狭めていく、イタレーションは50くらい)と段階的に絞っていかないと、
デフォルトのパラメータに負けてしまいます。(かれこれ50回以上は負けました。)
理想像からすれば、手間は増え、かつ探索時間も増えますが、既存の手法に比べれば
手間は減ります。感覚的には、(既存:20回作業)→(ベイズ最適化:5回作業)になる
レベルです。
・ベイズ最適化で観察する関数は、クロスバリテーションの分類精度を使いました。
具体的には、下記のscikit-learnのcross_val_scoreを使いました。学習データを
分けても良いのですが、クロスバリテーションのスコアを使えば間違いないです。
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
kfold = KFold(n_splits=10, random_state=7)
results = cross_val_score(model, x_train, y_train, cv=kfold)
score = results.mean()*100