Main Content

並列ベイズ最適化

並列的な最適化

ベイズ最適化を並列実行すると、時間を節約できます。並列実行には Parallel Computing Toolbox™ が必要です。bayesopt は並列ワーカー上で、目的関数の並列評価を同時実行します。

最適化を並列で行うには、

  • bayesopt — 名前と値の引数 UseParalleltrue に設定します。たとえば、以下のようにします。

    results = bayesopt(fun,vars,'UseParallel',true);
  • 近似関数 — 構造体 HyperparameterOptimizationOptionsUseParallel フィールドを true に設定します。たとえば、以下のようにします。

    Mdl = fitcsvm(X,Y,'OptimizeHyperparameters','auto',...
        'HyperparameterOptimizationOptions',struct('UseParallel',true))

並列ベイズ アルゴリズム

並列ベイズ最適化アルゴリズムは、ベイズ最適化のアルゴリズムで説明されている逐次アルゴリズムに似ています。相違点は、

  • bayesopt は評価対象の点 (通常は一度に 1 つずつの点) を並列ワーカーに割り当てます。bayesopt はどの点を割り当てるかを決定するための計算をクライアントで行います。

  • bayesopt は、初期の無作為な点を評価した後で、ガウス過程 (GP) モデルを当てはめることにより評価対象の点を選択します。bayesopt は、まだ点を評価しているワーカーがある状態で GP モデルを当てはめるため、まだワーカー上にある各点に値を割り当て (補定し) ます。補定値は、評価対象の点における GP モデル値の平均、または bayesopt の名前と値の引数 'ParallelMethod' によって指定された他の値です。近似関数の並列最適化の場合、bayesopt は補定値として ParallelMethod の既定値を使用します。

  • bayesopt は、評価対象の点を割り当てた後で、新しい割り当て対象の点を計算する前に、アイドル状態のワーカーが過剰にあるかどうかをチェックします。アクティブ ワーカーのしきい値は名前と値の引数 MinWorkerUtilization によって決定されます。アイドル状態のワーカーが過剰にある場合、bayesopt はすべてのアイドル ワーカーに対し、範囲内で一様に選択した無作為な点を割り当てます。このステップによりワーカーがより短時間でアクティブになりますが、当てはめられた点ではなく無作為な点がワーカーに割り当てられます。アイドル ワーカーの個数がしきい値を超えていない場合、bayesopt は GP モデルを当てはめて獲得関数を最大化することにより評価対象の点を通常どおり選択します。

メモ

並列でのタイミングに再現性がないため、並列ベイズ最適化で再現性のある結果が生成されるとは限りません。

最適な並列実行のための設定

近似関数には、並列実行のパフォーマンスを向上させるための特別な設定はありません。対照的に、bayesopt には最適化の高速化に役立つ設定がいくつかあります。

ソルバーのオプション

GPActiveSetSize オプションを既定 (300) より小さい値に設定すると、計算が高速になる可能性があります。代償として、大きい値の場合より目的関数の GP モデルの精度が低下する可能性があるため、bayesopt が評価対象として選択する点の精度が低下する可能性があります。このオプションを大きい値に設定すると、GP モデルは正確になる可能性がありますが、モデルの作成に必要な時間が長くなります。

ParallelMethod オプションを 'max-observed' に設定すると、bayesopt は大域的な最適値をより幅広く探索するようになります。これにより、より適切な解がより短い時間で得られるようになる可能性があります。ただし、多くの場合で既定値の 'clipped-model-prediction' が最適です。

MinWorkerUtilization オプションを大きい値に設定すると、並列の使用率が増加する可能性があります。ただし、この設定では評価対象として完全に無作為な点の個数が増えるので、解の精度が低下する可能性があります。この場合の大きい値は、使用するワーカーの個数によって異なります。既定の設定は floor(0.8*N) です。N は並列ワーカーの個数です。このオプションを小さい値に設定すると、並列の使用率が低下する可能性がありますが、点の品質が向上するというメリットがあります。

ワーカーへの目的関数の配置

3 つの方法のいずれかにより、目的関数を並列ワーカーに配置できます。一部の方法ではパフォーマンスが向上しますが、より複雑な設定が必要になります。

1.自動: 目的関数として関数ハンドルを与えた場合、bayesopt は実行の最初にハンドルをすべての並列ワーカーに送ります。たとえば、以下のようにします。

load ionosphere
splits = optimizableVariable('splits',[1,100],'Type','integer');
minleaf = optimizableVariable('minleaf',[1,100],'Type','integer');
fun = @(params)kfoldLoss(fitctree(X,Y,'Kfold',5,...
    'MaxNumSplits',params.splits,'MinLeaf',params.minleaf));

results = bayesopt(fun,[splits,minleaf],'UseParallel',true);

この方法は、ハンドルが小さい場合または最適化を 1 回だけ実行する場合に効果的です。しかし、最適化を複数回実行する場合は、他の 2 つの方法のいずれかを使用すると時間を節約できます。

2.並列定数: 最適化を複数回実行する場合は、目的関数を 1 回だけワーカーに転送することにより時間を節約します。関数ハンドルに大量のデータが含まれている場合、この方法は特に効果的です。次の例のように関数ハンドルを構造体 parallel.pool.Constant (Parallel Computing Toolbox) に設定して、目的関数を 1 回だけ転送します。

load ionosphere
splits = optimizableVariable('splits',[1,100],'Type','integer');
minleaf = optimizableVariable('minleaf',[1,100],'Type','integer');
fun = @(params)kfoldLoss(fitctree(X,Y,'Kfold',5,...
    'MaxNumSplits',params.splits,'MinLeaf',params.minleaf));

C = copyFunctionHandleToWorkers(fun);

results1 = bayesopt(C,[splits,minleaf],'UseParallel',true);
results2 = bayesopt(C,[splits,minleaf],'UseParallel',true,...
    'MaxObjectiveEvaluations',50);
results3 = bayesopt(C,[splits,minleaf],'UseParallel',true,...
    'AcquisitionFunction','expected-improvement');

この例では、copyFunctionHandleToWorkers は関数ハンドルを 1 回だけワーカーに送ります。

3.ワーカー上での目的関数の作成: ワーカーに送るデータが大量にある場合は、spmd (Parallel Computing Toolbox) を使用してワーカーにデータを読み込むことにより、クライアントにおけるデータの読み込みを回避できます。分散された目的関数へのアクセスには、Composite (Parallel Computing Toolbox)parallel.pool.Constant を使用します。

% makeFun is at the end of this script
spmd
    fun = makeFun();
end

% ObjectiveFunction is now a Composite. Get a parallel.pool.Constant
% that refers to it, without copying it to the client:
C = parallel.pool.Constant(fun);

% You could also use the line
% C = parallel.pool.Constant(@MakeFun);
% In this case, you do not use spmd

% Call bayesopt, passing the Constant
splits = optimizableVariable('splits', [1 100]);
minleaf = optimizableVariable('minleaf', [1 100]);
bo = bayesopt(C,[splits minleaf],'UseParallel',true);

function f = makeFun()
load('ionosphere','X','Y');
f = @fun;
    function L = fun(Params)
        L = kfoldLoss(fitctree(X,Y, ...
            'KFold', 5,...
            'MaxNumSplits',Params.splits, ...
            'MinLeaf', Params.minleaf));
    end
end

この例では、関数ハンドルはワーカーのみに存在します。ハンドルがクライアントに現れることはありません。

並列ベイズ最適化の出力の違い

bayesopt を並列実行する場合、ベイズ最適化の出力には次のような違いがあります。

  • 反復表示 — アクティブ ワーカーの個数を示す列が反復表示に含まれます。これは、bayesopt がジョブを次のワーカーに割り当てた後の値です。

  • プロット関数

    • 目的関数モデルのプロット (@plotObjectiveModel) には、保留中の点 (並列ワーカー上で実行中の点) が示されます。点の高さは名前と値の引数 ParallelMethod によって変化します。

    • 経過時間のプロット (@plotElapsedTime) には、合計経過時間が [リアルタイム] というラベルで、すべてのワーカーについて合計した目的関数の総評価時間が [目的関数の評価時間 (すべてのワーカー)] というラベルで表示されます。目的関数の評価時間には、ジョブでワーカーを起動する時間が含まれます。

参考

(Parallel Computing Toolbox) | (Parallel Computing Toolbox)

関連するトピック