Python: BayesianOptimizationによるベイズ最適化
お仕事で、時間のかかる学習のパラメータ選定に、ベイズ最適化を用いる機会がありましたので、備忘録として整理します。
ベイズ最適化
ベイズ最適化 (Bayesian Optimization) は、過去の実験結果から次の実験パラメータを、確率分布から求めることで最適化する手法です。機械学習では、可能な限り少ない試行回数で筋の良いハイパーパラメータを選定するために用いられます。
キーとなる概念は2つです。
- 未知の関数fは、ガウス過程に従うと仮定する
- 次に試行するパラメータを、獲得関数で選択する
ガウス過程を事前分布として導入することで、観測済みのパラメータから、未知のパラメータxにおけるf(x)の期待値・分散をほとんどコストなしで計算できるようになります (ガウス過程についてはよく理解できなかったので、改めて整理して記事を書くと思います)。
各点の期待値・分散が明らかになったところで、次にどこのパラメータを使うのかを決めるのが、獲得関数 (acquisition function) です。
期待値と分散をいかにミックスさせるかがキモとなります。期待値が高い点ほど、既知のデータからf(x)の値が大きくなることが予想できますが、局所解に陥っている可能性もあります。適度に、分散が大きい点、つまり周りが探索されていないためにf(x)がどういった値になるのかわからない点を調べる必要もあります。
この獲得関数にはいくつか種類がありますが、代表的なものは以下の3つです。このあと紹介するBayesianOptimizationでもこの3つが実装されています。
- PI (Probability of Improvement)
- 現在の最大値を超える確率が最も高い点を選ぶが、局所解に陥りやすい
- EI (Expected Improvement)
- 現在の最大値との差の期待値が最も高い点を選ぶ
- UCB (Upper Confidence Bound)
- 上側信頼区間が最も高い点を選ぶ
参考
ベイズ最適化の理論的な話は、こちらが参考になります。
www.slideshare.net
BayesianOptimization
Pythonでも、ベイズ最適化を行うライブラリはいくつかあります。今回はBayesianOptimizationを使ってみます (他にはGPyOptやscikit-optimizeなどがあります) 。
事前にインストールしておきます。
$ pip install bayesian-optimization
1変数関数の最適化
簡単な例として、以下の1変数関数のベイズ最適化を行います。
2つの極大値を持つ関数となっています。
BayesianOptimizationで最適化する実装は以下です。
BayesianOptimizationを生成し、maximizeメソッドで最適化します。
- BayesianOptimizationでは最適化する関数
f
と各パラメータの値域pbounds
を渡しています - maximizeメソッドには4つの引数を渡します
init_points
は、最初に取得する関数f(x)の数で、ランダムに選択されます (デフォルトは5)- この値が少ないと、局所解に陥りやすくなります
n_iter
は、試行回数です(デフォルトは25)acq
は、獲得関数の選択です (デフォルトは"ucb"で、"ei"と"poi"があります)kappa
は、ucbの場合のみ必要な引数で、値が大きいほど分散を重視した積極的な探索が行われます
from bayes_opt import BayesianOptimization # 1変数関数の定義 def f(x): return -(x**4 - 20 * x**2 + 10 * x) + 300 # 最適化するパラメータの下限・上限 (xのみ) pbounds = { 'x': (-5.0, 5.0) } # 関数と最適化するパラメータを渡す optimizer = BayesianOptimization(f=f, pbounds=pbounds) # 最適化 optimizer.maximize(init_points=3, n_iter=10)
上のコードを実行すると、例えばこんな結果となります。
- Step 1〜5で、初期値5点を計算してます
- Step 6〜15で、パラメータ (x) を選択して試行を繰り返してます
- x=-3.2807のとき最大値432.22484となっていることがわかります (Step 14)
Initialization ----------------------------------------- Step | Time | Value | x | 1 | 00m00s | 416.37121 | -3.8321 | 2 | 00m00s | 331.24223 | -1.0481 | 3 | 00m00s | 415.17122 | -3.8511 | 4 | 00m00s | 322.92473 | 1.4443 | 5 | 00m00s | 403.84389 | -4.0018 | Bayesian Optimization ----------------------------------------- Step | Time | Value | x | 6 | 00m02s | 431.97998 | -3.3539 | 7 | 00m02s | 432.22002 | -3.2909 | 8 | 00m04s | 432.22471 | -3.2823 | 9 | 00m06s | 432.22484 | -3.2803 | 10 | 00m05s | 432.22470 | -3.2824 | 11 | 00m07s | 432.22482 | -3.2813 | 12 | 00m05s | 432.22468 | -3.2787 | 13 | 00m06s | 432.22477 | -3.2818 | 14 | 00m05s | 432.22484 | -3.2807 | 15 | 00m06s | 432.22459 | -3.2829 |
結果はresプロパティが持っています。
optimizer.res['max'] # {'max_val': 432.2248419599048, 'max_params': {'x': -3.280735302927517}} # optimizer.res['all']で全ての試行結果が取得できます
SVMのハイパーパラメータの最適化
ベイズ最適化は、パラメータの数が多かったり、値域が広かったりするときに、真価を発揮します。
もうちょっと実用的な例として、irisデータセットをSVMで学習する場合のハイパーパラメータの最適化を行います。
与えられたパラメータ(Cとgamma)で10-Foldの交差検証を行ったときの平均スコアを返す関数を定義します。
- Cは、誤分類によるペナルティで、値を大きくすればペナルティは大きくなります (→過学習しやすい)
- gammaは、ガウスカーネルのパラメータで、値を大きくすれば決定境界が複雑になります (→過学習しやすい)
from sklearn import datasets, svm, model_selection # irisデータセットのロード iris = datasets.load_iris() # 独立変数・従属変数の分離 data = iris.data target = iris.target def validate(C, gamma): # 与えられたパラメータでSVMのモデルを初期化 model = svm.SVC(C=C, gamma=gamma, degree=1, random_state=0) # 10-Foldで交差検証 result = model_selection.cross_validate(model, data, target, cv=10) # 精度を返す return np.mean(result['test_score'])
Cとgammaの値域をそれぞれ0.0001〜10000に設定して、最適化を行います。
もしグリッドサーチでパラメータチューニングするとなった場合、10n (0.0001, 0.001, ... , 1000, 10000) に絞ったとしても、82=64パターンの試行が必要となります。
# 最適化するパラメータの下限・上限 (Cとgamma) pbounds = { 'C': (0.0001, 10000), 'gamma': (0.0001, 10000) } # 関数と最適化するパラメータを渡す optimizer = BayesianOptimization(f=validate, pbounds=pbounds) # 最適化 optimizer.maximize(init_points=5, n_iter=10, acq='ucb')
Step 12で最高スコア0.98が得られていることがわかります (データ数が150なので誤分類はわずか3つです)。
たった12回の試行で (おそらく) 最高スコアを出せるパラメータに到達できました。
Initialization ----------------------------------------------------- Step | Time | Value | C | gamma | 1 | 00m00s | 0.60000 | 8961.1186 | 119.0645 | 2 | 00m00s | 0.36667 | 3397.2021 | 3041.1179 | 3 | 00m00s | 0.36667 | 1494.0017 | 2237.3522 | 4 | 00m00s | 0.36667 | 5771.8602 | 7315.8688 | 5 | 00m00s | 0.36667 | 1126.9929 | 3907.0304 | Bayesian Optimization ----------------------------------------------------- Step | Time | Value | C | gamma | 6 | 00m03s | 0.36667 | 9999.7367 | 3193.1401 | 7 | 00m02s | 0.40000 | 0.0001 | 10000.0000 | 8 | 00m02s | 0.95333 | 9985.2013 | 3.1480 | 9 | 00m03s | 0.36667 | 10000.0000 | 10000.0000 | 10 | 00m03s | 0.36667 | 4126.7382 | 10000.0000 | 11 | 00m03s | 0.90667 | 0.0001 | 0.0001 | 12 | 00m03s | 0.98000 | 2836.3260 | 0.0001 | 13 | 00m03s | 0.41333 | 0.0001 | 7060.3988 | 14 | 00m03s | 0.98000 | 4626.0169 | 0.0001 | 15 | 00m03s | 0.36667 | 10000.0000 | 6978.0628 |
まとめ
今回はPythonでベイズ最適化を行うBayesianOptimizationについて使い方を整理しました。