Blog

2024.04.22

Engineering

BoTorchに依存しないGPSamplerの導入

Shuhei Watanabe

はじめに

今回のブログではOptuna v3.6で新たに導入されたGPSampler(ガウス過程を用いたベイズ最適化を行うSampler)を紹介します。

今までOptunaではガウス過程を用いたベイズ最適化を行うSamplerとして、BoTorchSamplerがありました。BoTorchSamplerは内部実装にBoTorchというベイズ最適化研究用フレームワークを利用することで実装の負担を軽減していましたが、一方で、BoTorchSamplerにはOptunaの設計との相性を起因としたいくつかの問題がありました。

1つ目の問題はBoTorchSamplerの実行速度がその他のSamplerと比較して非常に遅いことです。これは速度面で利用者の体験が悪くなるだけでなく、OptunaのユニットテストやCIにかかる時間も増大するという点で開発効率を下げる要因となっていました。

2つ目の問題としてOptuna全体としてガウス過程に依存したアルゴリズムは全てBoTorchに依存していたため、BoTorchを導入できない環境ではBoTorchSamplerだけでなく最適化を自動で終了するための機能であるTerminatorが利用できなくなってしまうという制約がありました。

今回のGPSampler導入に際して、ガウス過程自体をOptuna本体に実装することでこれらの問題を解決することを目標としました。また再実装に伴い離散最適化の強化も行いました。ただし、BoTorchSamplerはoptuna-integrationに移行されたため、v3.6以降でもoptuna-integrationをインストールすることでv3.5以前と同様のインターフェイスから引き続きBoTorchSamplerの機能を利用できます。

新機能追加背景とそれによる恩恵

新機能追加の主な目的は、BoTorchSamplerを自前実装のガウス過程を用いたSamplerに置き換えることで、実行速度と開発効率を改善し、またガウス過程を用いた機能追加の敷居を下げることでした。ここでは今まで生じていた問題と、それが今回の機能追加でどのように解決されたか説明します。

なぜBoTorchSamplerの実行速度が遅かったのか?

BoTorchはベイズ最適化の研究者向けのライブラリであり、柔軟なインターフェースから様々なベイズ最適化手法を試すことができます。柔軟なインターフェースを提供するためにBoTorchでは様々な手法への拡張性を意識した抽象化が行われています。一方で、Optuna側で利用しているガウス過程の機能は基本的かつシンプルなものが多いため、BoTorchの柔軟性はOptunaにとってオーバースペック気味でした。

またBoTorchはバッチ化とGPUを用いることで高速な最適化を実現するという設計思想があります。一方でOptunaの設計ではGPUを利用したサンプリングは想定されていないため、BoTorchのGPUを利用した高速化の恩恵が全く受けられていません。また各trialの生成もバッチで行わないため、バッチ最適化に適したBoTorchの設計上の恩恵は受けられず、結果としてBoTorchの速度上の利点が相殺されてしまうことがわかりました。

今回の開発ではBoTorchの最低限の機能のみに着目し、Optunaの設計に最適化したガウス過程の実装を行うことで、Optunaにあるガウス過程を利用したアルゴリズム群の高速化を図りました。

依存関係がどのように変化するか?

BoTorchSamplerはBoTorch, GPyTorch、Pyro、そしてPyTorchといった大きなライブラリをいくつか必要とするため、PythonやOS等のバージョンを原因にそのうちどれか一つでもインストールすることができない場合は利用することができません。一方で、実装したGPSamplerはPyTorchとSciPyさえあれば利用できます。

実装変更による性能悪化はあるのか?

我々が実施した多くの実験においてGPSamplerがBoTorchSamplerと比較して同等以上の性能を持つことを確認しました。特にベイズ最適化の研究では主に連続最適化を扱うことから、BoTorchSamplerは離散空間の扱いに長けておらずRandomSearchSamplerよりも性能が悪化してしまうという問題がありました。今回の機能追加では、獲得関数の最適化に工夫を施すことで、離散空間での性能改善も行いました。

獲得関数の最適化は、準モンテカルロ法によって得られた初期解から、連続変数に対する勾配法と各離散変数に対する局所最適化を順番に繰り返すことで行います。従来の実装では離散変数に対して離散的な観測しか得られないことを考慮していないため、離散空間での最適化がうまくいかない問題がありました。今回の変更によって離散空間における性能向上を確認しました。実験結果の一部は後述します。

GPSamplerの使用方法

Optuna v3.6では、GPSamplerは次の機能には対応していません。

  • 多目的最適化
  • 制約付き最適化
  • バッチ最適化

これらの機能が欲しい場合、BoTorchSamplerを引き続き使用してください。

GPSamplerはPyTorchとSciPyに依存するため、optunaをインストールした後に手動でPyTorchとSciPyをインストールする必要があります。

 $ pip install optuna>=3.6.0

# optunaの基本的な依存関係に加えてtorchとscipyが必要になります。

$ pip install scipy torch

インストール終了後の基本的な使い方は従来のOptunaと同様でsamplerを定義して、それをoptuna.create_study時に渡すだけです。

import optuna


def objective(trial):
  x = trial.suggest_float("x", -5, 5)
  return x**2


if __name__ == "__main__":
  sampler = optuna.samplers.GPSampler()
  study = optuna.create_study(sampler=sampler)
  study.optimize(objective, n_trials=100)

なお、GPSamplerで決定的な目的関数を最適化するときに`deterministic_objective=True`を指定すると性能が良くなることがあります。決定的な目的関数とは上の例のような同じパラメータで複数回実行しても結果が変わらない目的関数を指します。

sampler = optuna.samplers.GPSampler(deterministic_objective=True)

なお、v3.6にてBoTorchSamplerはoptuna-integrationパッケージに移動されました。これまでと変わらずoptuna.integration.BoTorchSamplerとして利用することができますが、そのためにはoptuna-integrationパッケージをインストールする必要があります。

 
# BoTorchSamplerを利用するにはoptuna-integrationとbotorchをインストールする必要があります。
$ pip install optuna-integration botorch

GPSamplerの実行速度と性能

最後に簡単なベンチマーク関数でGPSamplerをBoTorchSamplerと比較した際の実行速度改善と性能評価を行いたいと思います。

実験に使用したのは「1. 5次元のRotated Ellipsoid関数」と「2. HPOLibのNaval Propulsion [1](9次元の離散とカテゴリの混合空間)」です。5次元のRotated Ellipsoid関数は以下のような関数です。

import numpy as np
import optuna

cos30 = np.cos(np.pi / 6)
sin30 = np.sin(np.pi / 6)
DIM = 5
ROTATION = np.identity(DIM)
for i in range(DIM - 1):
  rotate = np.identity(DIM)
  rotate[i : i + 2, i : i + 2] = np.array([[cos30, -sin30], [cos30, sin30]])
  ROTATION = ROTATION @ rotate

def rotated_ellipsoid(trial): 
  X = np.array([trial.suggest_float(f"x{i}", -5, 5) for i in range(DIM)])
  RX = ROTATION @ X
  weights = np.array([5**i for i in range(DIM)])
  return weights @ ((RX - 2) ** 2)

なお、実験にはCore i7-10700 8コアのUbuntu 18.04マシンとBoTorch v0.10.0を利用しました。

まず、Figure 1に実行時間の評価を示します。図からわかるように連続空間と離散空間の両方においてGPSamplerが2倍以上速くなったことがわかります。次にFigure 2に性能評価を示します。冒頭で述べた通り、BoTorchは連続空間を解くために設計されたライブラリであるため、Figure 2(右側)の離散空間ではRandomSamplerの方がBoTorchSamplerよりも性能が良いです。一方で、GPSamplerは離散空間における最適化に工夫を施すことで性能を改善することができました。また、Figure 2(左側)にあるとおり、今回の変更で連続空間においてもGPSamplerの性能が悪化しないことがわかります。

Figure 1. それぞれの実験における実行時間の比較。横軸は評価したtrialの数で縦軸は対応するtrialの数だけ評価するのに要した時間を表します。各結果は10回の独立したstudyの平均をとっており、半透明な帯は10回のstudyの標準誤差を表します。左側: 5次元のRotated Ellipsoid関数の実験における実行時間比較。この実験から連続空間での実行速度が2倍以上短縮されたことがわかります。右側: HPOLibの実験における実行時間比較。この実験から離散とカテゴリの混合空間での実行速度が5倍以上短縮されたことがわかります。

 

Figure 2. それぞれの実験における性能比較。横軸は評価したtrialの数で縦軸は対応するtrialの数だけ評価したときの最も良い目的関数値を表します。各結果は10回の独立したstudyの平均をとっており、半透明な帯は10回のstudyの標準誤差を表します。左側: 5次元のRotated Ellipsoid関数の実験における性能比較。この実験では連続空間で最適化性能がBoTorchから悪化していないことを示しています。右側: HPOLibの実験における性能比較。この実験から離散とカテゴリの混合空間でBoTorchSamplerがRandomSamplerよりも悪化している一方でGPSamplerは離散最適化に対応しているためRandomSamplerから大きく性能改善しています。

おわりに

今回のブログではGPSamplerの導入に関して歴史的な背景とユーザが得られる恩恵について説明しました。ガウス過程の内部実装をシンプルに保つことで従来のBoTorchSamplerから速度を向上させてかつ依存関係を減らすことに成功しました。現状は最もシンプルな機能のみをサポートしており、多目的最適化や制約付最適化といった応用的な機能はサポートしていません。こうした機能に対するご要望があればIssueやDiscussion等でお知らせください。

Optuna v3.6では他にも様々な機能を追加しています。ぜひリリースブログをご覧ください。また、我々のRustを利用した最近の野心的な取り組みや、統計的検定を用いた枝刈り機能についてのブログもぜひ合わせてご確認ください。

また、Optuna開発チームではより多くのユーザーにより便利にOptunaをお使いいただけるよう、様々な可能性を模索し日々改善を続けています。一緒にOptunaを開発するパートタイムエンジニアを随時募集しています。本記事を通して、ご興味を持っていただけたという方はぜひ下記ページをご確認ください。

Software engineer (Optuna) / ソフトウェアエンジニア(Optuna) / 株式会社Preferred Networks 

[1] Klein, A. and Hutter, F. (2019). Tabular benchmarks for joint architecture and hyperparameter optimization. arXiv:1905.04970.

  • Twitter
  • Facebook