概要

ベイズ最適化のライブラリを使って、
実際、良い感じに最適化ができるのか試してみました。
使用するライブラリは「GPyOpt」です。

理論について参考としたのは、下記書籍です。
「ガウス過程と機械学習」(著者: 持橋大地、 大羽成征)

実験概要

目的関数を作成し、その最小値(指定する定義域内)をベイズ最適化で求める。
詳細設定は下記の通りです。

– ベイズ最適化ライブラリ : GPyOpt (.methods.BayesianOptimization)
– 目的関数 :

y=f(x)=(x-300)(x-200)(x-15)(x-5)(x+6)(x+10)(x+100)

– 最適化の方針 : 最小化
– 定義域(x) : [-100 , 300]
– 獲得関数 : EI (改善期待値)
– 初期データ(x,y) :

x=−50,0,50,100,150,200,250

– カーネル : 未指定。(ライブラリのデフォルト設定は調べ切れておりません。)
     今回はカーネルは置いといて、結果を見ます。

前提

  • ベイズ最適化
    ガウス過程回帰を用いて、目的関数の最小値(最大値)を求める手法。
    ガウス過程回帰より得られた予測(確率分布)に対して、
    獲得関数を適用し、その関数値が最大になる点を次点として探索していく。
  • ガウス過程回帰
ベイズ推定の一種。\\
各入力点xに対する目的関数値f(x)を一つ一つ確率変数と見なし、\\
( f(x_{1}) , f(x_{2}) , … , f(x_{n}) , …) \\
を、多変量正規分布 N(μ , Σ) と見なす。\\ 
 \\
取得済みの入力点を\\
x_{1} , x_{2} , … , x_{n}\\
とした時、新たな入力点の目的関数値(正規分布)を\\
f(x_{n+1}) 〜 p( f(x_{n+1}) | x_{n+1} , f(x_{1}) , f(x_{2}) , …  ,f(x_{n}) )\\
と見なす回帰手法。\\
 \\
目的関数値(正規分布)の期待値を予測値とする形で用いられたりする。\\
(上記のような関数 f を「ガウス過程」と呼ぶ。)\\
  • カーネル
ガウス過程回帰にて、\\
( f(x_{1}) , f(x_{2}) , … , f(x_{n}) , …)\\
の分散共分散行列 Σ の各成分の指定用関数。\\
 \\
(i , j)成分、つまり、f(x_{i}) と f(x_{j}) の共分散を\\
 x_{i} と x_{j} に依存する関数 k(x_{i} , x_{j}) で定め、 \\
その関数 k を「カーネル」と呼ぶ。\\
 \\
一般に使われるカーネルはいくつか存在するが、\\
基本性質としては、\\
"入力点 x_{i} , x_{j} が近ければ、f(x_{i}) , f(x_{j}) も近い。" \\

実験詳細

1. ライブラリインポート

#pip install Gpyopt
import GPyOpt

import matplotlib.pyplot as plt
import numpy as np

2. 目的関数

#目的関数定義
def f(x):
    y = (x-300)*(x-200)*(x-15)*(x-5)*(x+6)*(x+10)*(x+100)
    return y

#定義域の定義
xlim_fr = -100
xlim_to = 300

#グラフ
x = [i for i in range(xlim_fr , xlim_to + 1)]
y = [f(_x) for _x in x]

figsize = (10 , 5)
fig , ax = plt.subplots(1 , 1 , figsize=figsize)
ax.set_title('Outline of f')
ax.grid()
ax.plot(x , y)
ax.set_xlim(xlim_fr , xlim_to)
plt.show()

グラフより、定義域内では、
x が 270 あたりで最小値をとる形のようだ。
ベイズ最適化によって、その点が得られるか確認する。

3. 初期データ

#初期データ
init_X = [i for i in range(-50 , 300 , 50)]
init_X_np = np.array(init_X).reshape((len(init_X) , 1))

init_Y = [f(i) for i in init_X]
init_Y_np = np.array(init_Y).reshape((len(init_Y) , 1))

print(len(init_X))
print(len(init_Y))

print(init_X_np[:5])
print(init_Y_np[:5])

#初期データの位置をplot
figsize = (10 , 5)
fig , ax = plt.subplots(1 , 1 , figsize=figsize)
ax.set_title('Outline of f and Initial Data')
ax.grid()
ax.plot(x , y , label="f" , color="y")

#初期データ
for init_x , init_y in zip(init_X , init_Y):
    ax.plot(init_x , init_y , marker="o" , color="r")

ax.set_xlim(xlim_fr , xlim_to)
plt.show()

4. ベイズ最適化_モデル初期化

# 定義域
bounds = [{'name': 'x', 'type': 'continuous', 'domain': (xlim_fr,xlim_to)}]

# X , Y : 初期データ
# initial_design_numdata : 設定する初期データの数。上記 X , Yを指定した場合は設定不要。 
# normalize_Y : 目的関数(ガウス過程)を標準化する場合はTrue。(今回は予測を真値と比較しやすくするためFalse)
myBopt = GPyOpt.methods.BayesianOptimization(f=f
                                             , domain=bounds
                                             , X=init_X_np
                                             , Y=init_Y_np
                                             , normalize_Y=False
                                             , maximize=False
                                             #, initial_design_numdata=50
                                             , acquisition_type='EI')

5. ベイズ最適化_train

myBopt.run_optimization(max_iter=10)

6. ベイズ最適化_学習結果・過程

# 得られた最適解
x_opt = myBopt.x_opt
fx_opt = myBopt.fx_opt
print("x_opt" , ":" , x_opt)
print("fx_opt" , ":" , fx_opt)

# 最適化の軌跡
print("X.shape" , ":" , myBopt.X.shape)
print("Y.shape" , ":" , myBopt.Y.shape)

print("-" * 50)
print("X[:5]" , ":")
print(myBopt.X[:5])
print("-" * 50)
print("Y[:5]" , ":")
print(myBopt.Y[:5])

7. 予測_グラフ化

# ガウス過程回帰モデル
model = myBopt.model.model

#予測(第一成分:mean、第二成分:std)
np_x = np.array(x).reshape(len(x) , 1)
pred = model.predict(np_x)

model.plot()
myBopt.plot_acquisition()

model.plot()
plt.plot(x , y , label="f" , color="y")
plt.plot(x_opt , fx_opt , label="Optimal solution" , marker="o" , color="r")
plt.xlim(xlim_fr , xlim_to)
plt.legend()
plt.grid()
plt.title("f of True & Predict")
plt.show()

3つ目のグラフにおける赤点が、得られた最適解である。
グラフを見るに最小値をちゃんと見つけられている形となった。

まとめ

(詳細な誤差は置いといて、)
ベイズ最適化によって実際に最小値を求めることができた。

ただ、今回は初期データとして、
x=250x=250 という最適解(最小値)に近い値を取っており、
それで最適化しやすい形になったかもしれない。

初期データが、”最適解から離れたデータのみ”であったら、
試行回数を増やす必要があるかもしれない。

By clear

データエンジニア・機械学習・分析等を主とし、Webアプリ開発も行っているフリーランスです。