こんにちは!ぼりたそです!今回はベイズ最適化における獲得関数についてまとめた記事を作成しました。
この記事は以下のポイントでまとめています。
それでは詳細にご説明していきます。
獲得関数とは?
まず獲得関数について説明いたします。
獲得関数とは、ベイズ最適化においてガウス過程回帰で得られた平均と分散から表せる関数であり、次の候補点を決める指標です。
獲得関数はガウス過程回帰により得られた事後分布から候補点を選択する際に登場します。
一つの例としてy=sin(x)で表せる5点からガウス過程回帰を行った結果を上にグラフとして示します。
ガウス過程回帰からグラフのように平均値(黒線)と分散(青帯)が表されています。
獲得関数はこの平均値と分散から探索領域を評価して、最も期待できる候補点を決める指標となってくれます。
上のグラフでは獲得関数が赤線として示されており、最も評価が高い点を次の候補点として決めることができます。(今回の例ではyの最小化を行なっています)
獲得関数の種類と特徴
察しがつく方もいるかと思いますが、獲得関数はいくつか種類があり、目的に応じて使い分けることができます。
代表的な獲得関数は以下の通りです。
- PI(Probability of Improvement)
- PTR(Probability in Target Range)
- EI(Expected Improvement)
- UCB/LCB(Upper/Lower Confidence Bound)
PI(Probability of Improvement)
PIは現状の最大値(最小値)を更新する確率を表しています。
図を上に示していますが、ある特徴量Xが与えるYがガウス過程回帰による事後分布で表されるとき、ある点X1において右上のようにY1が正規分布で表せるとすると赤枠の領域がPIになります。
PIの特徴としては先述の通り最大値を更新する確率の高い候補を選択します。それ故に現状の最大値付近の候補を選択しやすく、局所最適解に陥りやすいとも言えるので注意が必要ですね。幅広い領域を「探索」するというよりは今あるデータを「活用」する関数と言えるでしょう。
PTR(Probability in Target Range)
PTRはPIの派生型のような獲得関数になります。PIは現状の最大値を超える確率を表していますが、PTRはある範囲内で最高値を超える確率を表しています。
図にすると上のようになり、赤枠で囲った領域がPTRを表しています。
PTRもPIと同様に幅広い領域を「探索」するというよりは今あるデータを「活用」する関数と言えます。また、局所最適解に陥りやすいというデメリットもあるので注意が必要です。
EI(Expected Improvement)
EIは最大値の改善値の期待値を算出します。PIやPTRと似たような気がしますが、PIやPTRは最大値を更新する確率を表すのに対してEIは改善値の期待値を表します。
なので、EIの場合は最大値を更新する確率が低くても、最大値を更新する幅が大きい候補を選択することができるのです。
しかし、学習データの改善を目的とした関数としてはPIやPTRと同様であり、探索の幅が狭くなりがちなため局所最適解に陥る可能性もあります。
UCB/LCB(Upper/Lower Confidence Bound)
UCB/LCBは探索と活用を使い分けできる獲得関数となっています。図やグラフで示すのが難しいのですが、以下のような式で表すことができます。
UCB(x) = μ(x) + κσ(x)
LCB(x) = μ(x) – κσ(x)
μ(x)は平均値、σ(x)は分散、κはハイパーパラメータとなっており任意の値とすることができます。UCBは分散を加算することで最大化を目指し、LCBは分散を弾くことで最小化を目指すことができます。
また、κの値が大きければ分散値の重みが大きくなり「探索」の意味合いが強くなります。逆にκが小さければ平均値の重みが大きくなり「活用」の意味合いが強くなります。「探索」も「活用」もできる器用な関数と言えるでしょう。
獲得関数ごとの探索数比較
それでは実際に獲得関数ごとに探索数がどの様に変わるか実証してみます。
今回は以下の関数の最大化を目指していきます。最大点はx=0になります。
$$f(x) = \sin(x) \cdot \cos(x) \cdot x^{-1}$$
使用する獲得関数はEI、PI、LCBの3つになります。
最適化はGPyOptというPythonライブラリを使用して行いました。
※プログラムの都合上、-f(x)を最小化することで最適化をおこなっています。
import GPy
import GPyOpt
import numpy as np
import matplotlib.pyplot as plt
trial = 1
# -2から2まで0.1刻みの数値を含む配列を作成
pre_x = np.arange(-7, -4, 0.2)
pre_x = pre_x.reshape(-1,1)
# 関数の計算
pre_y = np.cos(pre_x) * pre_x**-1 * np.sin(pre_x) # 例としてsin関数を使用
pre_y = pre_y.reshape(-1,1)
# 目的関数の定義
def objective_function(x):
global pre_y, pre_x, trial
print(f'trial : {trial}')
trial += 1
#候補のxを出力して学習データに追加する
print('x :',x)
pre_x = np.append(pre_x,x)
pre_x = pre_x.reshape(-1,1)
#候補のxからyを計算して学習データに追加する
y = np.cos(x) * x**-1 * np.sin(x)
print('y :',y)
pre_y = np.append(pre_y,y)
pre_y = pre_y.reshape(-1,1)
return y
# 定義域の設定
domain = [{'name': 'x', 'type': 'continuous', 'domain': (-10, 10)}]
# BayesianOptimizationオブジェクトの作成
bo = GPyOpt.methods.BayesianOptimization(f=objective_function,
model_type='GP',
domain=domain,
acquisition_type='MPI',#EI or MPI or LCB
maximize = True,
X=pre_x,
Y=pre_y)
# ベイズ最適化の実行(yが0.99以上になるまで繰り返す)
while np.max(pre_y)<0.99:
# 1つの候補点を出力
bo.run_optimization(max_iter=1)
bo.plot_acquisition(f'graph_{trial}.png')#ガウス過程回帰のプロットと獲得関数のグラフを表示
#plt.savefig(f'graph_{trial}.png')
結果は…
■EI(9回)
■PI(14回)
■LCB(5回)
となりました!今回のお題ではLCBが一番探索数が少なく最適解にたどり着くことができましたね。
どの獲得関数が適しているかはその課題によって異なりますので、獲得関数の特徴を踏まえて選択する様にしましょう!
オススメの書籍
最後に獲得関数やベイズ最適化についてもっと知りたい方に向けてオススメの書籍をご紹介します。
以下に紹介する「Pythonで学ぶ実験計画法入門 ベイズ最適化によるデータ解析」では初心者にも非常にわかりやすいようにベイズ最適化について説明されています。
もちろん、獲得関数やガウス過程回帰についても書かれており、実際にGithubからPythonコードとデータセットを取得できるので、自分で実践しながらベイズ最適化について勉強することができます。
また、ベイズ最適化以外についても機械学習の手法が記載されているので、ご興味のある方はぜひ購入いただければと思います。
ベイズ最適化のオススメ参考書については以下の記事でもまとめていますので、興味のある方はご参照いただければと思います。
終わりに
いかがでしたでしょうか。それぞれの獲得関数でも目的や表す数値がかなり違うことがわかると思います。ベイズ最適化は学習データが少ないうちは「探索」して、ある程度目星が付いたら「活用」に切り替えると試行回数が回数が少なくすむとも言われています。場合分けして適切な獲得関数を使えるようにしたいですね。