Surrogate#

Layer 3: Extend Components

saealibは、目的関数を近似する予測モデルの責務をSurrogateという差し替え可能なコンポーネントに限定しています。 Surrogateはfit/predictだけを知り、予測値をどうスコアに変換するか(AcquisitionFunction)や、学習データをどこから集めるか(TrainingSet、SurrogateManager経由)は一切知りません。

Surrogateの役割#

Surrogateが実装を要求するメソッドは2つあります。

fit(train_x, train_y) -> None:形状(n_samples, n_features)の入力と、形状(n_samples, n_obj)(単目的では(n_samples,))の出力でモデルを学習します。

predict(test_x) -> SurrogatePrediction:形状(n_samples, n_features)の入力に対する予測を返します。

クラス属性provides_uncertainty: bool = Falseは、予測が不確実性(標準偏差)を伴うかどうかを示します。 既定はFalseで、ガウス過程の実装だけがTrueにオーバーライドします。

Surrogateとpredict()の間には2つのマーカー基底クラスがあります。

RegressionSurrogate:train_yが実数値の目的関数出力である回帰サロゲート向けのマーカー。

ComparisonSurrogate:train_yが{0, 1}の二値比較ラベルである比較サロゲート向け。 predict_proba(test_x) -> SurrogatePrediction(値は[0, 1]の勝率)が主インターフェースで、predict()は既定でpredict_proba()に委譲します。

SurrogatePrediction#

predict()の戻り値はSurrogatePredictionという統一データクラスです。

フィールド

内容

value

予測値。形状 (n_samples, n_obj)

std

不確実性(標準偏差)。提供しないサロゲートではNone

label

分類モデルのみが持つクラスラベル

metadata

SHAP値など実装固有の付加情報を格納するdict

valueとstdは目的関数チャンネル向けの便利なプロパティです。 サロゲートの予測が目的関数値ではない量(新規性スコアなど)を表す場合、この仕組みによってpbestのような値が汚染されるのを防ぎます。 NoveltyAcquisitionなどのアーカイブベースの基準は、候補とアーカイブの設計点を直接受け取ります。

組み込みSurrogate#

RBFSurrogate(kernel, polynomial_degree="auto", solver="lstsq", alpha=1e-8):RBF補間によるサロゲートです[16, 17, 18](RBF補間自体の起源はHardy, 1971)。 kernelは既定値を持たない必須のRBFKernelオブジェクトです(組み込みカーネルは以下に示します)。カスタムカーネルはevaluate(r)を実装するサブクラスとして作ります。 predict()はstd=Noneを明示的に返します(RBF補間は不確実性を提供しません)。

パラメータ

値

役割

polynomial_degree

"auto"(既定) / None / 0 / 1

補間系を多項式項で拡張します(kernel.auto_polynomial_degreeから決定 / なし / 定数 / 線形)。ThinPlateSplineKernelのような条件付き正定値カーネルには多項式項が必要です。GaussianKernelのような狭義正定値カーネルには不要ですが、"auto"は定数項に解決されます。目的関数値すべてに同じ定数を足しても予測が変わらないようにするためです。

solver

"lstsq"(既定) / "solve" / "tikhonov"

線形系(多項式項で拡張されている場合を含む)の解法:最小二乗法(ランク落ちした系にも対応) / 直接法 / alphaによるリッジ正則化。

alpha

1e-8(既定)

リッジ正則化の強さ。solver="tikhonov"の場合だけ使われます。

kernelとpolynomial_degreeは設定した値そのものです。resolved_kernelとresolved_polynomial_degreeは、fit()が実際に解決して使う値を公開し、設定したRBFKernelインスタンス自体は不変に保ちます。 kernel/polynomial_degree/solver/alphaを再設定すると、fit済みの状態は無効になり、次のfit()までpredict()はRuntimeErrorを送出します。

length_scaleフィールドを持つカーネル(入力データと同じ距離単位)は、Noneのままにすると訓練点間のペアワイズ距離の中央値へ自動的に解決されます。

各カーネルは独立した2つの次数を宣言します。min_polynomial_degreeはそのカーネルが受け付ける最小の次数で、これを下回る値(Noneを含む)を渡すとValidationErrorになります。auto_polynomial_degreeはpolynomial_degree="auto"の解決先で、最小次数より高いことがあります。

カーネル

式

min_polynomial_degree

"auto"の解決先

GaussianKernel

\(\exp\left(-\dfrac{r^2}{2\,\ell^2}\right)\)

なし

0(定数)

ThinPlateSplineKernel

\(r^2 \log r\)(length_scaleは未使用)

1(線形)

1(線形)

LinearKernel

\(r\)(length_scaleは未使用)

0(定数)

0(定数)

CubicKernel

\(r^3\)(length_scaleは未使用)

1(線形)

1(線形)

MultiquadricKernel

\(\sqrt{r^2 + \ell^2}\)

0(定数)

0(定数)

MaternKernel(nu=0.5/1.5/2.5)

Matérn共分散。length_scale = \(\ell\)

なし

0(定数)

6種類のカーネルと3種類のsolverはいずれもサポート対象です。それぞれテストで検証されており、そのまま利用できます。GaussianKernelやMaternKernelにpolynomial_degree=Noneを渡すこともサポートしており、"auto"が追加する定数項を落とせます。

PerObjectiveSurrogate(surrogates):RegressionSurrogateのサブクラスで、目的ごとに異なるサロゲートを割り当てる合成クラス。 fit時にtrain_yの列数とlen(surrogates)が一致しないとValueErrorになります。 provides_uncertaintyは、構成する全サロゲートがTrueの場合のみTrueを返す複合判定になっています。

外部ライブラリアダプタ#

scikit-learn互換API経由の回帰サロゲートにはSklearnという接頭辞が付きます。

クラス

モデル

SklearnGPRSurrogate

ガウス過程[18, 19]。provides_uncertainty=Trueの唯一の実装

SklearnRFRSurrogate

ランダムフォレスト回帰

SklearnSVMSurrogate

SVM

SklearnNNSurrogate

MLP

SklearnXGBSurrogate

XGBoost(xgboost 追加機能)

SklearnLGBMSurrogate

LightGBM(lightgbm 追加機能)

TorchSurrogate

PyTorchベースのモデル(torch 追加機能)

SklearnGPRSurrogateはreturn_std=TrueでGPカーネルから標準偏差を計算し、provides_uncertainty=Trueを返します。

実行可能性予測向けの分類サロゲートには次のクラスがあります。

クラス

モデル

SklearnClassificationSurrogate

scikit-learn互換の分類モデル全般

SklearnRFCClassificationSurrogate

ランダムフォレスト分類

SklearnSVCClassificationSurrogate

SVM分類

これらの分類サロゲートの学習データ抽出方法はTrainingSetのFeasibilityClassificationSetを参照してください。 ペア比較には、これらの分類サロゲートではなくComparisonSurrogate系の専用実装を使い、SurrogateManagerのPairwiseSurrogateManagerおよびPairwiseComparisonSetと組み合わせます。

各追加機能のインストール方法はインストールを参照してください。

注釈

scikit-learn/XGBoost/LightGBM/PyTorch以外のBoTorch/SMTサロゲート用アダプターは、現時点で saealib に実装されていません。pyproject.toml に対応する追加機能もありません。pymooにはアダプターがありますが、Problem/Crossover/Mutation/Algorithm のレベルであり、Surrogate としてではありません。pymooはサロゲートモデルを提供しません。

拡張フック#

フィット後の後処理だけを足したい場合、サブクラスを新設せずにwith_post_fit(fn)で既存のSurrogateインスタンスへ処理を追加できます。 with_post_fitは元のインスタンスを変更せず、fnを追加したコピーを返します。

from saealib import GaussianKernel, RBFSurrogate


def log_fit(train_x, train_y, ctx=None):
    print(f"fit on {len(train_x)} samples")


base = RBFSurrogate(kernel=GaussianKernel())
logged = base.with_post_fit(log_fit)

fnのシグネチャはfn(train_x, train_y, ctx) -> Noneです。

独自Surrogateの実装方法#

独自の予測モデルが必要な場合は、Surrogateを継承してfit()/predict()を実装します。 回帰ならRegressionSurrogate、比較ならComparisonSurrogate(predict_proba()を実装)を継承先に選びます。

次の例は、最近傍点の目的関数値をそのまま予測値として返す単純な回帰サロゲートです。

import numpy as np
from saealib import RegressionSurrogate, SurrogatePrediction


class NearestNeighborSurrogate(RegressionSurrogate):
    """A simple surrogate that returns the nearest point's objective value directly as the prediction."""

    def fit(self, train_x, train_y):
        self.train_x = np.asarray(train_x, dtype=float)
        self.train_y = np.asarray(train_y, dtype=float)

    def predict(self, test_x):
        test_x = np.atleast_2d(test_x)
        dists = np.linalg.norm(self.train_x[None, :, :] - test_x[:, None, :], axis=2)
        nearest = dists.argmin(axis=1)
        value = self.train_y[nearest]
        return SurrogatePrediction(value=value)

不確実性対応表#

不確実性ベースのAcquisitionFunctionを使うには、Surrogateがstdを返す必要があります。

クラス

provides_uncertainty

SklearnGPRSurrogate

True

RBFSurrogate / SklearnRFRSurrogate / SklearnSVMSurrogate / SklearnNNSurrogate / SklearnXGBSurrogate / SklearnLGBMSurrogate / TorchSurrogate

False

PerObjectiveSurrogate

構成する全サロゲートがTrueの場合のみTrue

Optimizer.validate()は、AcquisitionFunctionのrequires_uncertaintyとSurrogateのprovides_uncertaintyの不整合を検出して警告します。

関連コンポーネント#

参照#