Hook、Stage、Callbackを選ぶ#
前提は、既存の実行経路に観測や後処理を追加したいことです。
このページでできるようになること
このページを終えると、データを変更するHook、実行単位を置き換えるStage、観測するCallbackを区別できます。
構成要素の索引は 最適化の構成要素 で、独自の契約が必要なら 独自Component に進みます。
境界を比較する#
手段 |
目的 |
実行時点 |
状態変更の可否 |
適した用途 |
|---|---|---|---|---|
|
既存Operatorの出力を後処理する |
CrossoverまたはMutationの後 |
戻り値で候補を変更できる |
修復、丸め、範囲内への変換 |
|
Surrogateのfit後に処理する |
|
Hookの処理でモデルや外部状態を更新できる |
fit記録、モデル後処理 |
|
互換実行単位を実装または置換する |
Pipeline内のStage位置 |
|
世代処理の追加、Stage差替え |
|
イベントを観測する |
登録したEventの発火時 |
|
ログ、履歴、条件判断 |
Hookでデータを変更する#
Operatorの候補配列を実際に変えるなら、CallbackManager ではなく with_post() を使います。 with_post() は元のインスタンスを変更せず、Hookを追加したコピーを返します。
import numpy as np
from saealib.operators import MutationUniform
def snap_to_grid(offspring, parents, rng, ctx):
return np.round(offspring * 10.0) / 10.0
mutation = MutationUniform(0.3).with_post(snap_to_grid)
Surrogateのfit後処理は、fn(train_x, train_y, ctx) -> None を with_post_fit() に渡します。 このHookを使う具体的な契約は Surrogateの概念 を参照してください。
Stageで実行単位を置き換える#
Stage は execute(state) -> state の互換性用境界です。 状態を更新するときは state.replace() で新しいStateを返します。 構造化FrameworkのComponentやCompilerを直接実装する境界ではないため、それが必要なら フレームワーク拡張 を参照します。
from saealib import Stage
class LogGenerationStage(Stage):
name = "log_generation"
def execute(self, state):
print(state.gen)
return state
Pipelineの構成とStageの契約は Stageの概念 に委ねます。
Callbackで観測する#
CallbackManager.register() は、Event型と event を受け取る関数を登録します。 ハンドラから読む event.ctx は、世代や評価回数などの公開された読み取り境界です。
import numpy as np
from saealib import GenerationEndEvent
from saealib import Optimizer, Problem
def objective(x):
return np.sum(x**2)
problem = Problem(
objective,
dim=3,
n_obj=1,
direction=np.array([-1.0]),
lb=[-5.0] * 3,
ub=[5.0] * 3,
)
optimizer = Optimizer(problem, seed=0)
history = []
def record(event):
history.append((event.ctx.gen, event.ctx.fe))
optimizer.cbmanager.register(GenerationEndEvent, record)
候補配列を差し替える、または状態を書き換える処理はCallbackの責務ではありません。 Callbackはイベントの観測、ログ、履歴の記録に使います。
独自Componentの契約を実装する必要があるときは 独自Component を使い、契約やRuntimeそのものを変更するときは フレームワーク拡張 を参照してください。