BoTorch 入門 1.

BoTorchの実装を追っていた自分が知識整理のためにまとめた記事です。
理論については別シリーズでまとめていきたいです（つもりです）。

---
BoTorch 入門 1.
[BoTorch 入門 2.](https://qiita.com/tr-author/items/45a2671aa342f9fab680)
[BoTorch 入門 3.](https://qiita.com/tr-author/items/fcf960bf45986e86b2a3)

[BoTorch 実践 3. ノイズの扱い](https://qiita.com/tr-author/items/4ef1164dd36a6375e668)
[BoTorch 実践4. 制約付きベイズ最適化](https://qiita.com/tr-author/items/94461a3ef5fa61dc2338)

---
# はじめに
今回がBoTorchの紹介とチュートリアルです。細かいところは立ち入りません。
# BoTorch
BoTorch(ボートーチ)はFacebook社が開発するPyTorchをベースとしたベイズ最適化のフレームワークです。主な利用者は研究者やハイレベルな実務者で、実務でベイズ最適化を行う際は同じくFacbook社が開発しているAxを使うことが推奨されています。BoTorchの主なモジュールはPyTorchの`torch.nn.Module`であり、自動微分やGPUなどPyTorchがもつ利点を活かすことができます。

ベイズ最適化は主にSurrogate Modelの更新と獲得関数の最大化という２つのプロセスが鍵となります。BoTorchではSurrogate Modelにガウス過程を利用しますが、これはGPyTorchを利用しているため高度なガウス過程(例えばSVGPやKISS-GP)も利用可能です.また獲得関数の最大化については、reparametrization-trickと準モンテカルロ法の利用により解析的に表現できない獲得関数でも最適化を行うことが可能です。

公式ドキュメントのIntrodunctionに載っているBoTorchの特徴について簡単に説明します

- #### Improved Developer Efficiency
  BoTorchはModel,posterior,Acquisition Function, Optimizerといったコンポーネントで構成されており,拡張が比較的容易な設計になっています。例えばBoTorchの開発者は研究者でもあり、最新の研究が次々に実装されていきます。BoTorchが実装している査読前の論文の一部[こちら](https://botorch.org/v/latest/docs/papers)にあります。この記事を書いている2022年12月現在でも今年の論文が多くあります。

- #### State-of-the-art Modeling
  前述したようにガウス過程はGPyTorchを利用しているため多くの高度なガウス過程を簡単にBoTorchから使うことができます。

- #### Harnessing the Features of PyTorch
  これも前述したようにBoTorchはPyTorchを利用しているためその恩恵を多く受けることができます。例えばガウス過程のハイパーパラメータの最適化や獲得関数の最大化は自動微分やバックプロパゲーションを利用しています。またGPUによる並列計算なども大きなメリットです。

## チュートリアル
まずは公式docに従ってやってみましょう。
適宜コメント等を入れてます。

1. Fit a Gaussian Process model to data
  最初はデータを準備してガウス過程によるフィッティングを行います。少し気をつけることは`X`の形状が`[data_num, dim]`に対して`Y`の形状が`[data_num]`ではなく`[data_num, 1]`であることです。詳細はまた先にも話しますがこの`SingleTaskGP`は複数の出力にも対応しているため出力の次元を1と明示してやる必要があります。

    ```python
    import torch
    from botorch.models import SingleTaskGP
    from botorch.fit import fit_gpytorch_mll
    from gpytorch.mlls import ExactMarginalLogLikelihood
    
    train_X = torch.rand(10, 2)
    Y = 1 - (train_X - 0.5).norm(dim=-1, keepdim=True)  # explicit output dimension
    Y += 0.1 * torch.rand_like(Y)
    train_Y = (Y - Y.mean()) / Y.std()
    
    gp = SingleTaskGP(train_X, train_Y)  #ガウス過程のモデルを定義
    mll = ExactMarginalLogLikelihood(gp.likelihood, gp) #周辺尤度を定義
    fit_gpytorch_mll(mll); #モデルのハイパーパラメータを最適化
    ```
2. Construct an acquisition function
  獲得関数を定義します。この場合はUCBですね。

    ```python
    from botorch.acquisition import UpperConfidenceBound
    
    UCB = UpperConfidenceBound(gp, beta=0.1)
    ```
3. Optimize the acquisition function
  獲得関数を最大化します。
  `candidates`には獲得関数を最大化したことによって得られる次の候補点が格納されます。

    ```python
    from botorch.optim import optimize_acqf

    bounds = torch.stack([torch.zeros(2), torch.ones(2)])
    candidate, acq_value = optimize_acqf(
        UCB, bounds=bounds, q=1, num_restarts=5, raw_samples=20,
    )
    ```
チュートリアルはここで終了していますが、これだけでは一回しか更新していません。実際はこの1~3のプロセスを数回~数百回と回して最適解を逐次的に探索していきます.

とっつきにくい書き方ですね。ちなみにAxだとこんな感じです。

```python
from ax import optimize
from ax.utils.measurement.synthetic_functions import branin

best_parameters, values, experiment, model = optimize(
    parameters=[
        {
            "name": "x1",
            "type": "range",
            "bounds": [-5.0, 10.0],
        },
        {
            "name": "x2",
            "type": "range",
            "bounds": [0.0, 10.0],
        },
    ],
    evaluation_function=lambda p: (branin(p["x1"], p["x2"]), 0.0),
    minimize=True,
)

```


#
BoTorchの公式ドキュメントを見た人の何割が理解できることやら。。研究者向けなのでしかたないかもしれませんが