Python, Optunaで多目的最適化を試してみた

Source Code Software Computer機械学習

Optunaのver1.4.0より、多目的最適化(Multi-objective)機能が試験的に実装されたようなので、試しに使ってみました。

optuna.multi_objective — Optuna 2.3.0 documentation

環境

Optuna 1.5.0

概観

単目的の時と同様に、以下のような流れで最適化が可能なようです。

  1. パラメータ範囲の設定とスコアの計算を行う関数objectivesを用意
  2. multi_objective.create_studyで最適化の設定
  3. optimizeで最適化を実行
def objectives(trial):
    # optunaでのパラメータサーチ範囲の設定
    # スコアの計算
    return # スコアを返す

# optunaによる最適化呼び出し
study = optuna.multi_objective.create_study()
# 最適化の実行
study.optimize(objectives)

コード例

0から30の値をとる変数aとbからなる関数y1、y2において、y1を最小化し、y2は最大化する多目的最適化を試します。

y1とy2はトレードオフの関係のため、a=0で、bが適当な値がパレート解で望ましい値となる感じでしょうか。

変数範囲とスコアの計算

変数a,bの範囲とスコアを設定するobjectivesは以下のように書けます。

import optuna

def objectives(trial):
    
    # optunaでのパラメータサーチ範囲の設定
    a = trial.suggest_int('a', 0, 30)
    b = trial.suggest_int('b', 0, 30)
    
    # 評価関数
    y1 = a**2 + b
    y2 = b
    
    return y1,y2

最適化の実行

optuna.multi_objective.create_studyで最適化の設定を行うようです。

directionsでスコア(y1,y2)は、最大化または最小化するかを設定します。

samplerには、遺伝的アルゴリズムなど以下のアルゴリズムが用意されているようです。

  • optuna.multi_objective.samplers.BaseMultiObjectiveSampler
  • optuna.multi_objective.samplers.NSGAIIMultiObjectiveSampler
  • optuna.multi_objective.samplers.RandomMultiObjectiveSampler

optimizeで最適化を実行し、n_trialsで試行回数が設定されています。

# optunaによる最適化呼び出し
study = optuna.multi_objective.create_study(directions=["minimize", "maximize"],
                                          sampler=optuna.multi_objective.samplers.NSGAIIMultiObjectiveSampler())
study.optimize(objectives, n_trials=200)

得られたパレート解の取得

get_pareto_front_trials()で試行結果のうちのパレート解を取得できるようです。

a=0または1で、b=0~30で、パレート解が得られているような感じがします。

# パレート解の表示
trials = {str(trial.values): trial for trial in study.get_pareto_front_trials()}
trials = list(trials.values())
trials.sort(key=lambda t: t.values)

for trial in trials:
    print("Trial#{}".format(trial.number))
    print("  Values: y1={}, y2={}".format(trial.values[0], trial.values[1]))
    print("  Params: {}".format(trial.params))
実行結果
Trial#156
  Values: y1=1.0, y2=0.0
  Params: {'a': 1, 'b': 0}
Trial#194
  Values: y1=2.0, y2=1.0
  Params: {'a': 1, 'b': 1}
Trial#123
  Values: y1=3.0, y2=3.0
  Params: {'a': 0, 'b': 3}
Trial#172
  Values: y1=7.0, y2=6.0
  Params: {'a': 1, 'b': 6}
Trial#131
  Values: y1=11.0, y2=10.0
  Params: {'a': 1, 'b': 10}
Trial#193
  Values: y1=15.0, y2=14.0
  Params: {'a': 1, 'b': 14}
Trial#164
  Values: y1=20.0, y2=19.0
  Params: {'a': 1, 'b': 19}
Trial#130
  Values: y1=25.0, y2=25.0
  Params: {'a': 0, 'b': 25}
Trial#152
  Values: y1=30.0, y2=30.0
  Params: {'a': 0, 'b': 30}

コード例 全体

import optuna

def objectives(trial):
    
    # optunaでのパラメータサーチ範囲の設定
    a = trial.suggest_int('a', 0, 30)
    b = trial.suggest_int('b', 0, 30)
    
    # 評価関数
    y1 = a**2 + b
    y2 = b
    
    return y1,y2

# optunaによる最適化呼び出し
study = optuna.multi_objective.create_study(directions=["minimize", "maximize"],
                                          sampler=optuna.multi_objective.samplers.NSGAIIMultiObjectiveSampler())
study.optimize(objectives, n_trials=200)

# パレート解の表示
trials = {str(trial.values): trial for trial in study.get_pareto_front_trials()}
trials = list(trials.values())
trials.sort(key=lambda t: t.values)

for trial in trials:
    print("Trial#{}".format(trial.number))
    print("  Values: y1={}, y2={}".format(trial.values[0], trial.values[1]))
    print("  Params: {}".format(trial.params))

コメント

タイトルとURLをコピーしました