CallbackManager#

Layer 2: Compose

Optimizerは、実行経過を外部へ通知する処理をCallbackManagerに委譲します。 CallbackManagerは、ログ記録、収束履歴の収集、条件判断などの観測をサポートします。 Eventは実行境界を観測するものであり、Eventのフィールドを変更してもPipelineの入力は差し替わりません。

CallbackManagerの役割#

CallbackManagerは、イベント型ごとにハンドラのリストを保持します。

Method

Description

register(event_type, func)

イベント型event_typeのイベントが発生するたびに呼び出すfunc(event)を登録します

dispatch(event)

登録順に、登録されたすべてのハンドラを呼び出します

unregister(event_type, func)

登録されたハンドラを削除します

replace(event_type, old, new)

登録されたハンドラを別のハンドラに置き換えます

Eventのctxは、archive、comparator、n_obj、gen、feなどの値を読み取るための_EventContext Protocolです。 通常のsequential実行では実体がOptimizationStateですが、Eventの公開契約はそれらを読み取る能力に限定されます。 ctxは読み取り専用として扱い、値を変更するときはwith_post、Component、Stageなどの正式な境界を使います。

利用可能なイベント一覧#

Event

Fired when

Main fields

RunStartEvent

実行開始時に1回

—

RunEndEvent

実行終了時に1回

—

GenerationStartEvent

各世代の開始時

—

GenerationEndEvent

各世代の終了時(stateがyieldされる前)

—

SurrogateStartEvent / SurrogateEndEvent

Surrogateベースのスコアリングの前後

offspring

AcquisitionStartEvent / AcquisitionEndEvent

acquisitionスコアリングの前後

開始時はoffspring、終了時はoffspringとresult

PostCrossoverEvent

crossoverとrepairの後

candidates

PostMutationEvent

mutationとrepairの後

candidates

PostAskEvent

ask()(crossoverとmutation)のすべての処理の後

candidates

PostSurrogateFitEvent

surrogateのfit後

surrogate、train_xとtrain_fはoptional

PostEvaluationEvent

選択された候補のtrue evaluation後

offspring

InitialEvaluationStartEvent

初期サンプリング後、初期評価前

candidates_x

InitialEvaluationEndEvent

初期評価後、archiveのソート前

archive

PostSurrogateFitEventは、Surrogateのfit後に組み込みStageから発火します。 組み込みStageが発火するイベントでは、train_xとtrain_fが設定されない場合があります。 PostEvaluationEventには、評価済みのoffspringに加えてrequest_id、candidate_ids、statusが含まれる場合があります。

既定のログ出力#

Optimizerは構築時にlogging_generationをGenerationStartEventへ自動登録します。 標準ライブラリのloggingモジュールを設定すると、世代ごとの進捗(評価回数、最良目的値、または多目的の場合は第一フロントのサイズと範囲)がログに出力されます。

カスタムハンドラの登録と収束履歴の記録#

cbmanager.register(EventType, handler)で任意のハンドラを登録します。 収束履歴を記録するには、クロージャで蓄積するリストを保持するハンドラを登録します。

from saealib import GenerationEndEvent

history = []


def record_best(event):
    f = event.ctx.archive.get_array("f")
    history.append(float(f.min()))


optimizer.cbmanager.register(GenerationEndEvent, record_best)
ctx = optimizer.run()
print(history)

ハイパーボリュームの追跡#

logging_generation_hv(reference_point)は、指定したreference pointに対する第一フロントのhypervolumeを世代ごとにログへ出力するハンドラを返します。

from saealib import GenerationStartEvent, logging_generation_hv

optimizer.cbmanager.register(
    GenerationStartEvent,
    logging_generation_hv(reference_point=np.array([1.1, 1.1])),
)

既定ハンドラの差し替え#

自動登録されたlogging_generationはunregister(event_type, func)で削除でき、replace(event_type, old, new)で別のハンドラに置き換えられます。

from saealib import GenerationStartEvent, logging_generation

optimizer.cbmanager.unregister(GenerationStartEvent, logging_generation)

candidatesフィールドは観測専用#

PostCrossoverEvent/PostMutationEvent/PostAskEventのcandidatesフィールドは観測用です。ハンドラ内で再代入しても(event.candidates = new_array)、Pipelineの出力には影響しません。 Event発火後もGAは自身のローカルな配列参照を使い続けるため、インプレースの変更(event.candidates[:] = ...)はGAに反映されます。 一方、PSOはPopulation.extend()が候補をコピーし終えた後にこのイベントを発火するため、インプレースの変更でも遅すぎてまったく影響しません。

候補配列そのものを差し替えたい場合は、CallbackManagerではなくCrossover/Mutationのwith_post(fn)を使ってください。 CallbackManagerは設計上、観測(ログ、記録、条件付きの分岐判断)のための仕組みであり、Pipelineデータを書き換える手段ではありません。

実行時の構成切り替え#

Eventは観測用であり、CallbackクロージャからOptimizerの内部を直接変更するのは標準的な手順ではありません。 データフローを変更するにはwith_post()またはwith_post_fit()を使い、実行構成はiterate()またはrun()のステップ境界で切り替えます。 ステップ境界でoptimizer.set_*()またはコンポーネント属性が変更されると、実行環境が変更を検出してプランを再コンパイルし、次の世代から適用します。 この手順はStage互換パスとgraph-nativeパスの両方で機能します。 Componentが要求する再コンパイル経路については、OptimizationStrategyの「実行時の差し替えの動作」を参照してください。 CallbackハンドラはRuntimeCommandを返せません。 Callbackクロージャでoptimizer.set_*()を呼び出した変更は上述の経路で適用されますが、構成変更はiterate()側の手順として文書化されています。

CallbackManagerとiterate()の使い分け#

Aspect

CallbackManager

iterate()

呼び出しの粒度

特定のイベントが発生したとき

世代ごと(ホスト側のforループ)

主な用途

ログ、観測、条件付きの副作用

ループ構造そのものへの介入(例:surrogateの精度に基づくコンポーネントの切り替え)

run()との関係

run()とiterate()のどちらでも動作

run()の代わりに使用

Surrogate accuracy evaluation and dynamic switchingのswitcherクラスは、iterate()ループ内で使うことを想定しています。

チェックポイントコールバック#

CheckpointCallbackはCallback実装の例です。 npz形式ではOptimizationState.save()を使い、pickle形式ではOptimizer.save_pickle()を使います。 形式によって保存対象の値と再開条件が異なるため、詳細はCheckpointingを参照してください。 このCallbackをカスタムCallback登録の参考にしてください。

関連コンポーネント#

参照#