Atmosphere Engineering Lab.

ベイズ最適化を理解する — 線形回帰からガウス過程回帰、獲得関数まで

はじめに — ベイズ最適化の理論の難しさ

ベイズ最適化は非常に便利なフレームワークであり、機械学習のパラメータチューニングの手法として定番化しています。 また、少数の実験点(〜100点)でも非常に優秀な探索性能を示すことから、近年では評価に時間がかかるCFD等のシミュレーションとの組み合わせでも成果を上げています。

一方で、ベイズ最適化は、機械学習を併用した手法であることから非常に理論が複雑で、実際のところ理論的背景までしっかり押さえている人はそう多くありません。

今回は線形回帰から出発し、カーネルトリック、ガウス過程回帰、獲得関数という順に積み上げながら、ベイズ最適化がどう組み立てられているのかを辿ってみます。

出発点 — 単回帰、線形回帰、リッジ回帰

最も単純なモデルは単回帰です。 入力 xx と出力 yy の関係を

f(x)=wx+bf(x) = w x + b

という直線で近似し、観測データとの誤差(二乗誤差)が最小になるように w,bw, b を決めます。 実装も理解もしやすく、外挿の挙動も素直という利点があります。 前提はあくまで「入出力の関係が直線的である」ことと、入力が1つだけであることです。 複数入力に対応することも可能で、入力 xx を入力ベクトル x\mathbf{x} に置き換えたものが重回帰と呼ばれます。

φ(x)\varphi(x) は特徴量と呼ばれ、入力 xx をそのまま使うだけでなく、たとえば x,x2,x3,x, x^2, x^3, \dots のように非線形な形へ一般化することもできます。 出力がパラメータ ww と特徴量 φ(x)\varphi(x) との線形結合で表されるため、「線形回帰」と呼ばれます。

f(x)=wφ(x)+bf(x) = w^\top \varphi(x) + b

変換後の空間では相変わらず「線形」回帰ですが、元の xx から見ると曲線的な関数を表現できるようになり、CFDの形状最適化のように入出力の関係が滑らかに曲がっている現象も、φ(x)\varphi(x) を適切に選べば扱えます。

ただし特徴量の次元を増やして表現力を上げるほど、観測データに対してパラメータの自由度が増えすぎ、ww の推定が不安定になりがちです(過学習)。 そこで二乗誤差に ww の大きさへのペナルティを加えて学習するのがリッジ回帰です。

minw,b i=1n(yiwφ(xi)b)2+λw2\min_{w,\, b} \ \sum_{i=1}^n \bigl(y_i - w^\top \varphi(x_i) - b\bigr)^2 + \lambda \lVert w \rVert^2

正則化項 λw2\lambda \lVert w \rVert^2 を加えることで、ww が野放図に大きくなるのを抑制する効果があります。

カーネルトリック

リッジ回帰にも欠点があります。 特徴量の次元を増やすほど、φ(x)\varphi(x) そのものの計算量が膨らんでいくのです。

ここで使われるのがカーネルトリックです。 ポイントは、リッジ回帰の解 ww が、訓練データの特徴ベクトルの線形結合として

w=i=1nαiφ(xi)w = \sum_{i=1}^n \alpha_i \, \varphi(x_i)

という形に書けることです。 これを予測式に代入すると、

f(x)=i=1nαiφ(xi)φ(x)+bf(x) = \sum_{i=1}^n \alpha_i \, \varphi(x_i)^\top \varphi(x) + b

となります。 であれば、φ(x)\varphi(x) に何かしらの関数を選択しなくても、φ(xi)φ(x)\varphi(x_i)^\top \varphi(x) という2点間の内積を 直接計算する関数を用意してやれば解くことが可能です。 このような代理関数 k(x,x)=φ(x)φ(x)k(x, x') = \varphi(x)^\top \varphi(x') をカーネル関数と呼びます。

実際に代入すると、

f(x)=i=1nαik(xi,x)+bf(x) = \sum_{i=1}^n \alpha_i \, k(x_i, x) + b

となり、特徴量 φ\varphi は式から完全に姿を消します。 これがカーネルトリックの発想で、「内積 φ(xi)φ(x)\varphi(x_i)^\top \varphi(x) をカーネル関数 k(xi,x)k(x_i, x) に置き換える」というのが線形回帰からの本質的な変更点です。

代表的なRBFカーネル(動径基底関数カーネル)

k(x,x)=exp ⁣(xx222)k(x, x') = \exp\!\left(-\dfrac{\lVert x - x' \rVert^2}{2\ell^2}\right)

は、2点 x,xx, x' が近ければ大きな値、遠ければ小さな値を返します。 これは「入力が近ければ出力も近いはずだ」という滑らかさの仮定を、カーネル関数という形で埋め込んでいることに相当します。 (そのほかにも周期カーネルや線形カーネル、指数カーネルなどがあります。 対象のデータに合わせてこれらのカーネルの選定も重要です。)

この発想に基づく回帰はカーネル回帰(カーネルリッジ回帰)と呼ばれ、線形回帰よりずっと柔軟に曲線を表現できます。

点推定から確率分布へ — ガウス過程回帰

カーネル回帰は「その点での予測値はいくつか」という、1点の推定値を返しますが、「その予測はどれくらい信用できるのか」という不確実性も欲しくなります。 観測点が既にあるところの近くでは予測は信頼できますが、観測点から離れた場所では不確かなはずです。

ガウス過程回帰(Gaussian Process Regression, GPR)は、この不確実性を扱えるようにカーネル回帰を拡張したものです。 カーネルトリックが「明示的な特徴量 φ(x)\varphi(x) を消す」方向の一般化だったのに対し、 ガウス過程回帰は別の軸として「重みを決定的なものではなく、確率変数とみなす」方向の一般化にあたります。

ガウス過程は、平均関数(多くの場合ゼロ)と共分散関数(カーネル関数そのもの)によって定義される「関数の分布」で、 そこから関数をサンプリングすると、カーネルが表現する滑らかさを持った曲線がいくつも出てきます。 この事前分布に観測データを条件付けることで、事後分布——つまり観測データと矛盾しない関数群の分布——が得られます。

この事後分布から、任意の入力点における予測を「平均値と分散を持つ確率分布」として取り出せます。 具体的には、観測データ X={x1,,xn}X = \{x_1, \dots, x_n\}y=(y1,,yn)y = (y_1, \dots, y_n)^\top(観測ノイズの分散を σn2\sigma_n^2 とする)に対し、 カーネル行列 KKKij=k(xi,xj)K_{ij} = k(x_i, x_j))と、新しい入力点 xx_* と各観測点とのカーネルベクトル k=(k(x,x1),,k(x,xn))k_* = (k(x_*, x_1), \dots, k(x_*, x_n))^\top を用いると、 事後分布の平均 μ(x)\mu(x_*) と分散 σ2(x)\sigma^2(x_*) は次のように書けます。

μ(x)=k(K+σn2I)1y\mu(x_*) = k_*^\top (K + \sigma_n^2 I)^{-1} y σ2(x)=k(x,x)k(K+σn2I)1k\sigma^2(x_*) = k(x_*, x_*) - k_*^\top (K + \sigma_n^2 I)^{-1} k_*

μ(x)\mu(x_*) は観測データを重み付けした予測値、σ2(x)\sigma^2(x_*)xx_* が既存の観測点からどれだけ離れているかを表す不確実性です。 観測点の近くでは分散が小さく(自信を持って予測できる)、観測点から離れるほど分散が大きくなる(自信がない)、という直感的な挙動が自動的に得られるのがガウス過程回帰の強みです。

「次にどこを評価するか」を決める — 獲得関数

ガウス過程回帰によって、探索空間全体にわたって「予測平均」と「予測分散」が手に入りました。 次に必要なのは、この情報を使って「次にどの点を実際に評価すべきか」を決める仕組みです。 これを担うのが獲得関数(Acquisition Function)です。

単純に予測平均が良い点(活用: Exploitation)ばかりを選ぶと、まだ調べていない領域に本当はもっと良い解があっても見逃してしまいます。 逆に予測分散が大きい点(探索: Exploration)ばかりを選ぶと、いつまで経っても収束しません。 獲得関数はこの両者のバランスを取るための指標で、代表的なものに期待改善量(Expected Improvement, EI)や上側信頼限界(Upper Confidence Bound, UCB)があります。

それまでの観測点における最良値を fbest=miniyif_{\text{best}} = \min_i y_i とし、点 xx での「改善量」を

I(x)=max(fbestf(x), 0)I(x) = \max\bigl(f_{\text{best}} - f(x),\ 0\bigr)

と定義します(f(x)f(x)fbestf_{\text{best}} を下回れば改善、下回らなければ改善量はゼロ)。 ここで、EIを例にとると、計算式は以下のようになり、

EI(x)={(fbestμ(x))Φ(z)+σ(x)ϕ(z)(σ(x)>0)0(σ(x)=0),z=fbestμ(x)σ(x)\mathrm{EI}(x) = \begin{cases} (f_{\text{best}} - \mu(x)) \, \Phi(z) + \sigma(x) \, \phi(z) & (\sigma(x) > 0) \\ 0 & (\sigma(x) = 0) \end{cases} , \qquad z = \dfrac{f_{\text{best}} - \mu(x)}{\sigma(x)}

ここで Φ\Phiϕ\phi はそれぞれ標準正規分布の累積分布関数・確率密度関数です。

次に評価する点は、このEIを最大化する点

xnext=argmaxxEI(x)x_{\text{next}} = \arg\max_x \mathrm{EI}(x)

として選ばれます。 EIの第1項 (fbestμ(x))Φ(z)(f_{\text{best}} - \mu(x)) \Phi(z) は予測平均が良い点(活用)を、第2項 σ(x)ϕ(z)\sigma(x) \phi(z) は予測分散が大きい点(探索)を評価に反映しており、 予測平均が良くても予測分散がある程度あればEIは高くなるため、自然と活用と探索が両立します。

組み合わせると — ベイズ最適化

ここまでの部品を組み合わせると、ベイズ最適化のループが完成します。

  1. 初期の数点をランダムまたはDOEで評価し、入出力のデータを得る
  2. そのデータにガウス過程回帰をフィットし、探索空間全体の予測平均・予測分散を得る
  3. 獲得関数を計算し、それを最大化する点を「次に評価すべき点」として選ぶ
  4. その点で実際にブラックボックス関数(CFDなど)を評価し、データに追加する
  5. 2〜4を評価回数の予算が尽きるまで繰り返す

ガウス過程回帰が「今わかっていること」を確率分布として表現し、獲得関数がその不確実性を活かして「次に何を知るべきか」を決める。 この役割分担こそが、ベイズ最適化が少ない評価回数で効率よく最適解に迫れる理由です。

おわりに

理屈上は非常によく作りこまれたベイズ最適化ですが、実務で使うにはいくつか気をつける点があります。

まず、ガウス過程回帰はカーネル行列の逆行列計算が必要で、その計算量はデータ点数の3乗で増加します。 評価回数が数十〜百程度で収まる問題には向きますが、データが増えるほど急速に重くなっていきます。 カーネル関数のハイパーパラメータ(長さスケールなど)の選び方によって滑らかさの仮定が変わるため、事前に対象とする目的関数の性質を見て判断する必要があります。 こうした細部を理解しておくと、ライブラリが収束しないときや、パラメータの意味がわからず困ったときに、どこを疑えばよいかの見当がつきやすくなります。

また、個人的にはベイズ最適化の真価は、最適解そのものの導出ももちろんそうですが、 逐次学習により徐々に精度が向上していく機械学習モデル(ガウス過程回帰)や、適応的実験計画(Adaptive sampling)への繋がりにあると考えています。

このように、ベイズ最適化を深く掘り下げていくと様々な周辺技術とのかかわりが見えてきます。 便利な手法ほど中身の積み上げを追っておく価値があると感じています。