Initializer#

Layer 2: Compose

実行の開始時に、Optimizerは初期Population、Archive、ParetoArchive、OptimizationStateの構築をInitializerへ委譲します。 サンプリング方法を変える場合は、Optimizer全体ではなくInitializerを差し替えます。

現行の標準経路では、GenomeInitializer が Problem.space からGenomeをサンプリングします。 LHSInitializer、RandomInitializer、SobolInitializer は、ベクトルの x 配列を使う互換性用Initializerとして残っています。

Initializerの役割#

Initializer の実装境界は initialize(provider, problem) -> OptimizationState です。 provider は、AlgorithmやEvaluatorなど、すでに構築されたComponentへアクセスするためのProviderです。 problem は対象となる Problem です。

組み込みInitializer#

クラス

サンプリング方法

GenomeInitializer

Problem.space.sample() によるGenome生成

LHSInitializer

scipy.stats.qmc.LatinHypercube

RandomInitializer

rng.uniform

SobolInitializer

scipy.stats.qmc.Sobol(scramble=True)

GenomeInitializerは、SearchSpaceが提供するGenomeを直接Archiveへ登録します。 他の3クラスはコンストラクタ(n_init_archive, n_init_population, seed=None)を共有し、ベクトル形式で初期点を生成します。 4クラスとも、初期Archiveを評価し、Comparatorでランク付けしたうえで、上位の候補を初期Populationへ渡します。

Sample (n_init_archive points)
  -> Evaluate via provider.evaluator.evaluate_batch
  -> add to archive / pareto_archive
  -> Rank via problem.comparator.rank_population
  -> Feed the top n_init_population into population

これは、前回の選択の状態を再利用するのではなく新しく組み立てたArchiveをランク付けするため、sort_populationを直接ではなくrank_populationを呼び出します——両者の違いはComparatorを参照してください。 SPEA2ComparatorのようなComparatorにとっては、これが初期Populationにその永続化されたランキング状態(spea2_fitness)を最初に書き込む場所でもあります。

3クラスの実装はサンプリング方法の1行以外ほぼ重複していますが、これは意図的な単純さの選択であり、共通処理を過度に抽象化しない設計になっています。

基底クラスのヘルパーメソッド#

Initializer基底には、独自実装で再利用できる2つのヘルパーメソッドが用意されています。

_create_attrs(problem, provider):PopulationとArchive用のPopulationAttribute値を構築します。 レガシーなベクトル経路ではx/f/g/cv/idを使い、続けてAlgorithm(Algorithm.get_required_attrs)とComparator(Comparator.get_required_attrs)の両方が要求する補助属性を統合します——例えばSPEA2Comparatorのspea2_fitness属性がスキーマに加わるのはこの仕組みによるものです。 GenomeInitializerはGenomeを専用カラムで管理するため、標準属性にxは含まれません。

_create_context(problem, archive, pareto_archive, population, rng):OptimizationState を構築します。 Comparatorが NSGA3Comparator で内部乱数生成器をまだ持たない場合は、ここで rng.spawn(1)[0] を設定します。

独自Initializerの実装方法#

独自のベクトルサンプリングを追加する場合は Initializer を継承して initialize() を実装します。 新しいGenome表現を使う場合は、Problem.space.sample() と Problem.space.validate() を使うInitializerを実装します。

Genome-nativeな初期化では、次の順序を保ちます。

  1. Algorithmの population_class、archive_class、create_pareto_archive() で入れ物を作る。

  2. OptimizationState を構築する。

  3. problem.space.sample(n, rng) でGenomeを生成し、problem.space.validate() で検証する。

  4. provider.evaluator.evaluate_batch(genomes, problem) で評価する。

  5. 候補ID、Genome、評価結果をArchiveとParetoArchiveへ登録する。

  6. 評価回数を更新し、Comparatorで並べた候補をPopulationへ渡す。

次の骨格は、Genome-nativeなInitializerが使う入力と出力の境界を示しています。

from saealib import GenomeInitializer


class CustomGenomeInitializer(GenomeInitializer):
    def initialize(self, provider, problem):
        # Replace problem.space.sample() with custom sampling.
        # Validate each generated Genome with space.validate() before passing it to the Evaluator.
        ...

初期評価の開始と終了は CallbackManager から観測できます。 評価Requestの候補IDとGenomeを維持する必要があるため、Genomeを x 配列へ暗黙に変換して管理しません。

Optimizer.set_initializer(initializer) でInitializerを差し替えます。

関連コンポーネント#

  • OptimizationState:initialize()が最終的に返す状態オブジェクト

  • Population:構築対象のPopulation/Archive/ParetoArchive

  • Evaluator:初期サンプルの評価に使う

  • CallbackManager:InitialEvaluationStartEvent/InitialEvaluationEndEventの観察

  • Comparator:初期Archiveのランク付けに使うrank_population

参照#