SurvivorSelection#
GA(saealib.GA)は、次世代に残す個体を選択プールから選ぶ処理を、SurvivorSelectionという差し替え可能な演算子に委ねています。 世代交代の方式を変えたいときは、GA本体ではなくこのSurvivorSelectionだけを差し替えればよいです。
SurvivorSelectionの役割#
SurvivorSelectionが実装を要求するメソッドはselect(ctx, pool, n_survivors) -> np.ndarrayの1つだけです。 poolは、親個体群と子個体群などAlgorithm側が構築した統合済みのPopulationで、その中から生き残るn_survivors件のインデックスを返します。
\((\mu+\lambda)\)方式(親と子を合わせたプールから選ぶ)か\((\mu,\lambda)\)方式(子だけのプールから選ぶ)かは、poolに何を含めるかというAlgorithm側の構築方法で決まります。 SurvivorSelectionのインターフェース自体はどちらの方式かを区別しません。
組み込みのSurvivorSelection#
クラス |
パラメータ |
特徴 |
|---|---|---|
|
|
打ち切り選択: |
randomize_ties=Trueを設定すると、打ち切り境界でタイになった個体(compare_populationが0を返す場合)を、打ち切る前にシャッフルします。 既定のFalseでは、rank_populationが返す順序をそのまま使う決定的な打ち切りになります。 このタイブレークはctx.rngを消費するため、randomize_ties=Trueの使用はチェックポイント再開時の乱数状態にも影響することに注意してください。
TruncationSelectionは@register()済みです。
独自SurvivorSelectionの実装方法#
独自の世代交代方式が必要な場合は、SurvivorSelectionを継承してselect()だけを実装すればよいです。 次の例は、最良個体1件を必ず残し、残りはランダムに選ぶ生存選択です。
import numpy as np
from saealib import SurvivorSelection
class ElitistSurvivorSelection(SurvivorSelection):
"""Always keeps the single best individual, choosing the rest at random."""
def select(self, ctx, pool, n_survivors):
sorted_idx = ctx.comparator.rank_population(pool)
best = sorted_idx[:1]
rest_pool = sorted_idx[1:]
rest = ctx.rng.choice(rest_pool, size=n_survivors - 1, replace=False)
return np.concatenate([best, rest])
poolは呼び出しごとにAlgorithmが新しく組み立てるため、独自のselect()はsort_populationを直接ではなくrank_populationでランク付けすべきです——理由はComparatorを参照してください。 トーナメント方式の生存者選択や年齢ベースの入れ替えのように、そもそもランキングを前提としない方式も同じselect()シグネチャの中で実装できます。
関連コンポーネント#
Algorithm:
GA.tell()がpoolをどう構築しSurvivorSelectionをどう呼ぶかComparator:
rank_population/compare_populationによる個体のランク付け