最適化サイクルのカスタマイズ

最適化クラス Optimizer のメソッド optimize() を実行すると、最適化サイクルを構成する処理が順次繰り返し実行され、処理に応じた様々なモデルが考慮されます。

ここでは、これらのモデルと処理について説明し、独自の最適化サイクルを実装するための手順を解説します。

最適化サイクルの全容

下図は、以下で説明する各最適化サイクル内の処理ステップとそのステップで考慮されるモデルを図示したものです。

_images/optimizer_flow_step_models.drawio.svg

最適化サイクルで考慮される 3 つのモデル

各最適化サイクルでは、次の 3 つのモデルに基づく処理ステップが繰り返し実行されます。

  • ブラックボックス関数 (Optimizer.blackbox)

    目的関数の定義 で定義される最適化対象(シミュレーションや実験)の関数

  • サロゲートモデル (Optimizer.surrogate_model)

    学習データに基づき構築される、ブラックボックス関数に対するサロゲートモデル

  • Amplify モデル (Optimizer.amplify_model)

    サロゲートモデルを Amplify SDK で取り扱う論理モデル化したもの(数式上はサロゲートモデルと同一だが、入力変数が Amplify SDK の決定変数に置き換わったもの)

最適化サイクルにおける処理ステップ

optimize() メソッドで実行される各最適化サイクルは、次の処理ステップから構成されます。

Step 1: サロゲートモデル関数の構築

train_surrogate() メソッドを実行し、学習データからサロゲートモデル surrogate_model の構築を行います。

Step 2: サロゲートモデル関数の最適化

minimize_surrogate() メソッドを実行し、構築済みサロゲートモデル関数を最小化するような入力値(最適入力候補)をイジングマシンを用いて取得します。この最適化では、Optimizer のインスタンス時に引数で渡された制約条件も考慮します。戻り値は、解 (Solution) のリストと Amplify SDK の amplify.Result オブジェクトのタプルになります。解のリストは、良い解の順に格納されています。イジングマシンで得られたベスト解は、リストの最初の要素に格納されます。その他の要素は、以下注釈に記載の、重複解を避けるための処理に活用されます。

Note

イジングマシンにより得られた最適入力候補が、既に学習データの中にある(つまり、当該の入力値でブラックボックス関数を評価済みである)場合、同じ解を再評価するコストは無駄であり、また、学習データに重複するサンプルが格納されるのは、サロゲートモデル構築の観点からも相応しくない、と考えられます。

Amplify-BBOpt では、デフォルトの設定として、このような重複解をできるだけ避けるような次のような方法を採っています。

  • (a) ユニーク解の選択 (find_unique_solution() メソッド)

    find_unique_solution() メソッドを実行し、minimize_surrogate() が返却した解のリスト (Solution のリスト) から、サロゲートモデルのデータセットに含まれていない解 (=ユニーク解) の中での最良解を選択します。一つも当てはまらなかった場合、None を返却します。

  • (b) フォールバック解の生成 (fallback_solution() メソッド)

    find_unique_solution() でユニーク解がなかった場合に、fallback_solution() メソッドを実行します。fallback_solution() は、ユーザー指定の制約条件を充足し、さらに、引数で与えた解の近傍にあるユニークで実行可能な解 (フォールバック解) を生成します。生成にはイジングマシンを用います。

Step 3: データセットの更新

evaluate_objective() メソッドを用いて、新規解(最適入力候補)に対するブラックボックス関数の値を評価します。万が一、新規解が学習データセット内のサンプルと重複する場合、学習データセット内の当該の評価値を参照します。その後、add_solution() メソッドにより、入力値と得られた出力値を、新たな入出力ペアとして、学習データに追加します。

Step 4 (optional): 最適化履歴の更新

各イテレーションの最後に、そのイテレーションで得られた解の情報を IterationResult として history に記録することもできます。本処理は、最適化そのものには影響を与えません。

独自の最適化サイクル実装

ここまでで解説した Optimizer クラスメソッドを組み合わせることで、optimize() メソッドの代わりとなる、独自の最適化サイクルの実装が可能です。様々なブラックボックス最適化に関する工夫などを柔軟に適用・検証することができます。基本的な実装例は以下の通りです。


from amplify_bbopt import Optimizer

# 最適化クラスのインスタンス化
optimizer = Optimizer(
    blackbox_func,
    trainer,
    client,
)

# 10サンプルからなる初期学習データを生成・追加
optimizer.add_random_training_data(num_data=10)

# 最適化サイクルを 10 回実行する
# optimizer.optimize(10)

num_cycles = 10

for n_iter in range(num_cycles):
    # Step 1: サロゲートモデル関数の構築
    optimizer.train_surrogate()

    # Step 2: イジングマシンによるサロゲートモデル関数の最適化 (最小化)
    solutions, _ = optimizer.minimize_surrogate()
    if len(solutions) == 0:
        raise RuntimeError(f"No feasible solution was found in iteration {n_iter}")

    # Step 3: データセットの更新
    # (常に最良解 solution[0] を採用、ユニーク性のチェック・フォールバック処理は無し)
    new_solution = solutions[0]
    new_objective = optimizer.evaluate_objective(new_solution)
    optimizer.add_solution(new_solution, new_objective)