ベイズ最適化入門:ガウス過程と獲得関数で評価回数を抑えて最適化する
はじめに
1回の評価に数時間かかる関数を最適化したいとします。ハイパーパラメータ探索、シミュレーション、材料やプロセスの条件出し。こうした「評価が高コスト」な問題では、グリッドサーチで総当たりする余裕はありません。評価のたびに、次にどこを測れば最も学びが大きいかを考えて点を選びたいところです。
今回、最小値があらかじめ分かっている2次元のテスト関数を、評価回数25回という制約で最小化しました。ベイズ最適化はほぼ最小値まで到達し、95%の試行で最適解の近くに届きます。同じ25回でも、ランダムサーチとグリッドサーチは、最小値から大きく離れたところで止まりました。この記事では、ガウス過程による代理モデルと獲得関数という枠組みを、まず1次元の可視化で仕組みから追い、2次元で効率を数値で確かめます。
対象読者:
- ハイパーパラメータ探索・実験計画・シミュレーション最適化に関わる方
- 1回の評価コストが高く、総当たりが現実的でない問題に取り組む方
- 遺伝的アルゴリズムや焼きなましといった確率的最適化を試したことのある方
記事のポイント:
- 代理モデル(surrogate)と獲得関数(acquisition)という枠組みを理解します
- ガウス過程回帰で予測平均と不確実性を得る仕組みを可視化で確認します
- ベイズ最適化がランダム・グリッドより少ない評価で最適解に近づく様子を測ります
問題設定:ブラックボックスかつ高コスト
対象とする目的関数は、微分が取れず、評価にコストがかかり、しばしばノイズを含みます。この条件では勾配法は使えず、グリッドやランダムのような総当たり系は評価回数を無駄に使います。焼きなましや遺伝的アルゴリズムも微分不要の最適化ですが、こちらは多数の評価を前提とします。評価が本当に高価な場面では、1回1回をもっと大切に使う戦略が必要です。
ベイズ最適化の発想はこうです。これまでの観測から目的関数の姿を確率的に推定する代理モデルを作り、その推定を頼りに「次にどこを測るか」を決めます。測ったら代理モデルを更新し、また次の点を選びます。この繰り返しで、少ない評価で最小値に迫ります。
代理モデルとしてのガウス過程
代理モデルにはガウス過程回帰(GP)を使います。GPは関数そのものに対する確率分布で、観測を与えると各地点での予測平均 と不確実性 を返します。観測点の近くでは が小さく、まだ測っていない領域では大きくなります。この「どこがまだ分かっていないか」を定量化できる点が、ベイズ最適化の心臓部になります。
カーネル(RBFやMatérn)が関数の滑らかさの仮定を決めます。長さスケールを短く取れば細かい変化を許し、長く取ればなだらかな関数を仮定します。ここが実データと合っていないと、GPは変化に追随できなかったり、逆にノイズを拾いすぎたりします。
下の図の上段が、6点を観測したあとのGPです。黒が真の関数、赤が予測平均、薄い帯が95%の不確実性で、観測点の間で帯が膨らんでいるのが分かります。

獲得関数
次の評価点は、獲得関数を最大化して選びます。獲得関数は「そこを測る価値」をスコア化したもので、予測平均の良さ(活用)と不確実性の大きさ(探索)のバランスを一本の式に落とし込みます。
期待改善(EI, Expected Improvement)は、現在の最良値をどれだけ更新できそうかの期待量です。最小化の場合、現在の最良を として
は標準正規の累積分布と密度、 は探索の強さを微調整する小さな定数です。実装は数行で済みます。
def expected_improvement(mu, sigma, f_best, xi=0.01):
sigma = np.maximum(sigma, 1e-9)
imp = f_best - mu - xi # 最小化
z = imp / sigma
return imp * norm.cdf(z) + sigma * norm.pdf(z)
もう一つよく使うのがUCB(Upper Confidence Bound)で、最小化なら が小さい点を選びます。 を大きくするほど不確実な領域を優先し、探索寄りになります。上の図の下段がEIで、その最大点(青い破線)が次の評価点になります。真の最小値付近に鋭いピークが立っているのが見て取れます。
ベイズ最適化のループ全体も短いです。GPを当て、EIの最大点を評価し、観測に加えます。これを予算まで繰り返します。
for _ in range(budget - n_init):
gp = make_gp(noise).fit(U, yv)
mu, sd = gp.predict(candidates, return_std=True)
u_next = candidates[np.argmax(expected_improvement(mu, sd, yv.min()))]
yv = np.append(yv, objective(u_next) + observation_noise())
U = np.vstack([U, u_next])
2次元Braninで比べる
効率の検証は2次元のBranin関数で行います。最適解が3か所にある既知のベンチマークで、評価回数を25に固定し、ベイズ最適化(EI)・ランダムサーチ・5×5グリッドを比較します。代理モデルはノイズ入りの観測で学習させ、「見つけた最良点」はその点の真の関数値で評価しました。乱数シードは42に固定し、確率的な手法は60シードで平均しました。
評価回数に対する到達値の推移を見ると、ベイズ最適化だけが真の最小値へ向かって下がり続けます。

25回時点の結果は次のとおりです。
| 手法 | 到達した最良値 | 真の最小値との差 | 最適解到達率 |
|---|---|---|---|
| ベイズ最適化(EI) | 0.617 | 0.219 | 95% |
| ランダムサーチ | 2.711 | 2.313 | 33% |
| グリッド(5×5) | 2.501 | 2.103 | 到達せず |
なぜ差がつくのでしょうか。評価点の分布を並べると一目で分かります。ベイズ最適化は3つの谷の周りに点を集中させているのに対し、グリッドは等間隔ゆえに谷を外し、平坦な高い領域に多くの評価を費やしています。

ベイズ最適化が効く場面
次元が効きます。1次元なら20点程度のグリッドでも十分に空間を覆えて、実はベイズ最適化と大差ありません。差が開くのは次元が上がったときで、グリッドは1軸あたりの点数が次元乗で効くため、2次元でもう5×5=25点まで粗くなります。今回グリッドが谷を外したのはこのためで、3次元4次元と上がれば総当たり系は急速に力を失います。ベイズ最適化の価値は、この「次元と評価コストの壁」に直面したときに立ちます。
平均値だけでなく、到達率にも目を向けるべきです。ベイズ最適化は95%の試行で最適解近傍に届いたのに対し、ランダムは33%でした。ランダムは運がよければ早く当てますが、外すことも多いです。再現性が求められる本番のチューニングでは、この安定性の差が効きます。
獲得関数が探索と活用を自動で調停している点も見逃せません。図の下段のEIは、予測が良い領域(活用)と不確実な領域(探索)の両方でスコアが立ち、その折り合いで次の点が決まります。序盤は広く探り、良い谷が見えてくると周辺に点を集めます。この振る舞いを人手のルールで書かずに済むのが、代理モデルベースの手法の利点です。
使いどころには制約もあります。GPは計算量が観測数の3乗で増え、目安として20次元程度を超えると効きにくくなります。カーネルや長さスケールの誤設定に敏感で、ここを外すと探索が的外れになります。離散変数や条件付きの探索空間、多目的・制約付きの問題では、それぞれ専用の拡張が必要です。実務では、まず数十回の評価予算で回せるかを見積もり、次元が高ければTuRBOのような高次元向け手法や、探索空間の絞り込みを先に検討します。
まとめ
評価が高コストな最適化では、次にどこを測るかを賢く選ぶことがそのまま効率になります。ベイズ最適化は、ガウス過程で関数の姿と不確実性を推定し、獲得関数で探索と活用を調停しながら点を選びます。今回の2次元Braninでは、25回の評価でベイズ最適化が最小値0.398へ0.62まで迫り、到達率95%とランダム・グリッドを大きく上回りました。手元の高コストな関数に対しては、まずガウス過程とEIで数十回の予算を回し、カーネルの長さスケールが関数の変化と合っているかを1次元の断面で目視してから、本番の探索に入るとよいでしょう。