多目的最適化#
saealib を使って、目的間のトレードオフがある多目的最適化問題を解きます。
minimize()は目的関数の数に応じたデフォルトのコンポーネントを使います。
アルゴリズム、代理モデル、評価戦略を個別に設定する場合は Optimizer.set_*() を使います。
このページでは、2つ以上の目的を持つ問題に固有の内容として、Comparatorの選択とParetoフロントの取得を扱います。
このページでできるようになること
このページを終えると、多目的問題でComparatorを選び、得られたParetoフロントを取り出せます。
単目的問題の基本手順は 単目的最適化 を使い、独自の Comparator が必要な場合は 独自コンポーネント を参照してください。
このページでは、多目的 Problem を実行し、Comparatorを選択してParetoフロントを取得します。
問題を設定する#
複数の目的関数がトレードオフの関係にあると、一方を改善すると他方が悪化する解が存在します。
この関係において、すべての目的で他の解に支配されない解の集合を Paretoフロント と呼びます。
ここでは例として、saealib に組み込まれたZDT1関数を最小化します。
from saealib.benchmarks import zdt1
problem = zdt1(n_var=10)
zdt1 は、凸型のParetoフロントを持つ2目的のベンチマーク問題を返す Problem インスタンスです。
minimizeを実行する#
Problem インスタンスを直接渡すと、そこから目的数が引き継がれます。
from saealib import minimize
result = minimize(problem, max_fe=2000, seed=0)
print(result.x.shape) # (n_pareto, dim)
print(result.f.shape) # (n_pareto, n_obj)
単目的の場合に1点だった result.x/result.f は、多目的の場合にはParetoフロントを形成する複数の解になります。
Optimizerを構成する#
Optimizer(problem)では、各コンポーネントを独立して設定できます。
set_*()は連鎖呼び出しのために同じ Optimizer を返し、run()または iterate()で最適化を実行します。
from saealib import Optimizer, Termination, max_fe
optimizer = Optimizer(problem, seed=0).set_termination(Termination(max_fe(2000)))
ctx = optimizer.run()
pareto_f = ctx.pareto_archive.get_array("f")
未設定のコンポーネントはデフォルトに解決されます。 多目的の順位付けは、次のComparatorの選択で設定します。
Comparatorを選択する#
多目的最適化では、Comparator が候補解同士の相対的な優劣を決めます。
Problem の comparator 引数を省略すると、目的数に基づいて自動選択されます(n_obj == 1 では SingleObjectiveComparator、n_obj > 1 では NSGA2Comparator)。
クラス |
動作 |
|---|---|
|
非劣ソートと混雑度距離による多様性維持(既定) |
|
支配の強さと近傍密度に基づく適応度 |
|
ハイパーボリューム寄与で優劣を判定 |
|
ε支配で優劣を判定 |
|
参照点による多様性維持。 |
|
指定した参照点の近くに解を集中させます。 |
Problem インスタンスの属性として comparator を交換できます。
from saealib.comparators import HypervolumeComparator
problem.comparator = HypervolumeComparator()
result = minimize(problem, max_fe=2000, seed=0)
パレートフロントを抽出する#
実行後、最終的なParetoフロントは result.ctx.pareto_archive に保持されます。
pareto_x = result.ctx.pareto_archive.get_array("x")
pareto_f = result.ctx.pareto_archive.get_array("f")
任意の目的値配列からParetoフロントを計算するには、non_dominated_sort を直接使えます。
from saealib.comparators import non_dominated_sort
archive_f = result.ctx.archive.get_array("f")
ranks, fronts = non_dominated_sort(archive_f, direction=problem.direction)
front0_f = archive_f[fronts[0]] # first non-dominated front