前回の続きです。
目次
ベイズ最適化が良く用いられる例だと思います。他にはハイパーパラメータの探索にはグリッドサーチや最適化計算を行う方法もあります。ハイパーパラメータの束縛条件を設定し、とりあえず試すだけなので簡単です。
xgboostのハイパーパラメータを下記手法
- Randomized Search
- Grid Search
- Bayesian Optimization
- Nevergrad
で探索し、結果を比較してみようと思います。データセットはsklearn.datasetsにあるdiabetesです。結論から言うとPSOが最も性能が良かったです。場合によるのでしょうが。
全体のソースコードは以下です。
baseline
まずは探索無しのデフォルトの場合です。
from sklearn import datasets from sklearn.model _selection import GridSearchCV, cross_val_score from sklearn.preprocessing import StandardScaler from xgboost import XGBRegressor import numpy as np import matplotlib.pyplot as plt X,y = datasets.load_diabetes(return_X_y=True) scaler = StandardScaler() xgb = XGBRegressor() pipe = Pipeline([('scaler': scaler), ('xgb', xgb)]) scoring = 'neg_mean_squared_error' baseline = cross_val_score(pipe, X, y, scoring=score, cv=5)
必要なパッケージを呼び出して、パイプラインに通します。前処理は特に必要ないのですが、正規化しています。 デフォルトの結果を示します。
print(baseline.mean()) > -3435.44
Algorithm
Randomized Search
適当に探索する方法です。
param = { 'xgb__gamma' range(0, 5), 'xgb__max_depth': range(1, 50), 'xgb__min_child_weight': range(1, 10), 'xgb__n_estimators' : range(0, 300) rs = RandomizedSearchCV(pipeline, param_distributions=param, scoring=scoring, n_iter=25)
Grid Search
ハイパーパラメータをグリッド上にして網羅的に計算しますが、ハイパーパラメータが多いと時間がかかるのとドメイン知識が必要となります。
param = { 'xgb__gamma': [0.01, 0.1] , 'xgb__max_depth': [1, 3, 5], 'xgb__min_child_weight': [1, 3, 10], 'xgb__n_estimators' : [50, 100, 500]} gs = GridSearchCV(pipe, param_distributions=param, cv=5, scoring=scoring, iid=False)
GPyOpt
ベイズ最適化を用いてハイパーパラメータを探索します。GPでは、自作kernelも渡すことができます。獲得関数も複数から選択できます。 目的関数ですが、交差検証でのスコア値が最大になるようにベイズ最適化を行っていきます。
import GPy import GPyOpt from GPyOpt.methods import BayesianOptimization bounds = [ {'name': 'learning_rate', 'type': 'continuous', 'domain': (0, 0.1)}, {'name': 'gamma', 'type': 'continuous', 'domain': (0, 5)}, {'name': 'max_depth', 'type': 'discrete', 'domain': (1, 50)}, {'name': 'n_estimators', 'type': 'discrete', 'domain': (1, 500)}, {'name': 'min_child_weight', 'type': 'discrete', 'domain': (1, 10)}, ] def cv_score(*args): args = args[0] score = cross_val_score( XGBRegressor(learning_rate=args[0], gamma=args[1], max_depth = int(args[2]), n_estimators=int(args[3]), min_child_weight=int(args[4)), X, y, scoring=score, cv=5).mean() return score optimizer = BayesianOptimization(f=cv_score, domain=bounds, model_type='GP', acquisition_type='EI') optimizer.run_optimization(max_iter=20)
Nevergrad
勾配を必要としない最適化アルゴリズムが多数実行できるようなパッケージになります。現時点で使えるアルゴリムは 100個ほどあり下記で確認できます。
from nevergrad.optimization import registry print(sorted(registry.keys()))
BOも入っているようで、nervergradでもベイズ最適化できます。githubを見るとTwoPointsDE
が良いみたいです。worker数の選択が大事になりそうです。まずはnevergrad用に目的関数を設計します。
def cv_score2(*args): lr, gamma, max_depth, n_est, min_child = args score = cross_val_score( XGBRegressor(learning_rate=lr[0], gamma=gamma[0], max_depth = int(max_depth), n_estimators=int(n_est), min_child_weight=int(min_child)), X, y, scoring=score, cv=5).mean() return -score
各ハイパーパラメータごとにサンプリング分布の設定や境界条件などを設定し最適化計算を行います。
from nevergrad.optimization import optimizerlib from nevergrad import instrumentation as inst # log distributed between 0.001 and 1 lr = inst.var.Array(1).bounded(0, 3).exponentiated(base=10, coeff=-1) gamma = inst.var.Array(1).bounded(0, 3) max_depth = inst.var.OrderedDiscrete(range(1, 50)) n_estimators = inst.var.OrderedDiscrete(range(1, 500)) min_child = inst.var.OrderedDisrete(range(1, 10)) instrumentation = inst.Instrumentation(lr, gamma, max_depth, n_estimators, min_child, num_workers=5) optimizer = optimizerlib.TwoPointsDE(instrumentation, budget = 100) args, kwargs = instrumentation.data_to_argument([0] * instrumentation.dimension) for _ in range(optimizer.budget): x = optimizer.ask() y = cv_score2(*x.args) optimizer.tell(x, y) recommendation = optimizer.provide_recommendation() print(recommendation.args, cv_score2(*recommendation.args))
budgetがイテレーション数です。ask
で計算をし、tell
で結果を返し、stepを更新していきます。
色々な最適化計算ができるのですが、ほかの手法を使うときは
optimizer = optimizerlib.PSO(instrumentation, budget = 100)
となります。
結果
baselineではneg MSEは -3435.44です。CV=5, イテレーションは30です。Grid searchは比較できないですが参考に載せています。
Algorithm | time (sec) | neg-MSE |
---|---|---|
Random | 47.98 | -3241.93 |
Grid | 74.47 | -3193.28 |
BO | 65.08 | -3218.64 |
RS | 52.64 | -3284.53 |
2ptsDE | 47.62 | -3338.47 |
FastGA1+1 | 35.08 | -3196.43 |
PSO | 56.65 | -3161.58 |
基本的にベースラインは超えるので、ハイパーパラメータサーチは行った方がいいですが、手法間でそこまで大差があるとは思えませんね。
所感
多数手法があるので適当にアルゴリズムを選択しましたが、PSOが最もよかったです。最適化計算は大体PSO使ったときが性能が高い気がします。それにしても物凄く簡単にハイパーパラメータ探索ができました。分子生成の最適化計算時にも試してみたいです。
最適化計算の問題は、やはり計算量が膨大となることですね。TorqueなどMPIは必須でしょう。 アニーラーやゲート型コンピューターが発展してきたとき、どうなるか楽しみです。
引用・参考元
ベイズ最適化のところは、主に下記のブログから引用しています。