(この記事はGemini 3.0 Pro (2025年12月10日)によって作成しました。)
上記の記事でGlobalSearchについて説明しました。この記事では上記の記事内でのサンプルコードについての説明とGlobalSearchのプロパティについて説明します。
A. サンプルコード解説
GlobalSearch を使うには、問題を記述した構造体(createOptimProblem)を作成し、それをソルバーに渡すという手順を踏みます。
以下は、複数の谷を持つ関数(Peaks関数に近いイメージ)の最小値を求めるサンプルコードです。
%% 1. 目的関数の定義(Rastrigin関数のような多峰性関数)
% 多数の谷(局所解)を持つ関数です
fun = @(x) x(1)^2 + x(2)^2 - 10*cos(2*pi*x(1)) - 10*cos(2*pi*x(2));
%% 2. 最適化問題の作成
x0 = [3, 3]; % あえて解から遠い、別の谷を初期値に設定してみます
lb = [-5, -5];
ub = [5, 5];
problem = createOptimProblem('fmincon', ...
'objective', fun, ...
'x0', x0, ...
'lb', lb, ...
'ub', ub);
%% 3. GlobalSearch オブジェクトの作成と実行
gs = GlobalSearch;
% 実行中のログを表示させたい場合は以下をコメントアウト解除
gs.Display = 'iter';
fprintf('GlobalSearchを実行中...\n');
[x_sol, f_sol] = run(gs, problem);
%% --- ここから可視化コード ---
% 1. 描画用のグリッドデータを作成
grid_num = 100; % 分解能
x_range = linspace(lb(1), ub(1), grid_num);
y_range = linspace(lb(2), ub(2), grid_num);
[X, Y] = meshgrid(x_range, y_range);
% 2. グリッド上の各点での関数値を計算
Z = zeros(size(X));
for i = 1:numel(X)
Z(i) = fun([X(i), Y(i)]);
end
% 3. プロットの作成
figure('Position', [100, 100, 1000, 500], 'Color', 'w');
% --- 左側:3Dサーフェスプロット(全体像) ---
subplot(1, 2, 1);
% surfcの戻り値(ハンドル)を変数 h_surf に受け取る
h_surf = surfc(X, Y, Z, 'EdgeColor', 'none');
hold on;
% ★★★ ここで start_val を計算します ★★★
start_val = fun(x0);
% プロットのハンドルもそれぞれ変数に受け取る
% GlobalSearchで見つけた解のプロット
h_sol = plot3(x_sol(1), x_sol(2), f_sol + 20, 'rp', 'MarkerSize', 15, 'MarkerFaceColor', 'r');
% 初期値のプロット
h_start = plot3(x0(1), x0(2), start_val + 20, 'bo', 'MarkerSize', 10, 'MarkerFaceColor', 'b');
hold off;
title('3D景観:目的関数の全体像');
xlabel('x(1)'); ylabel('x(2)'); zlabel('f(x)');
colorbar;
colormap jet;
view(-30, 45);
% legend関数に「表示したいハンドル」を配列で渡す
% h_surf(1) が曲面本体を表します(h_surf(2)は等高線なので凡例から除外)
legend([h_surf(1), h_sol, h_start], '関数形状', 'GlobalSearchの解', '初期値(x0)', 'Location', 'best');
% --- 右側:等高線プロット(真上から見た図) ---
subplot(1, 2, 2);
contourf(X, Y, Z, 20); % 20段階の等高線
hold on;
% 解と初期値をプロット
plot(x_sol(1), x_sol(2), 'rp', 'MarkerSize', 15, 'MarkerFaceColor', 'r');
plot(x0(1), x0(2), 'bo', 'MarkerSize', 10, 'MarkerFaceColor', 'b');
hold off;
title('等高線:解の位置確認');
xlabel('x(1)'); ylabel('x(2)');
grid on;
axis square;
% 結果をコマンドウィンドウに表示
fprintf('\n--- 結果 ---\n');
fprintf('初期値 x0: [%.2f, %.2f]\n', x0(1), x0(2));
fprintf('大域的最適解: [%.4f, %.4f]\n', x_sol(1), x_sol(2));
fprintf('最小値: %.4f\n', f_sol);
(コードの解説)
提示されたMATLABコードは、**「多峰性(たほうせい)関数と呼ばれる、凸凹がたくさんある難しい関数に対して、GlobalSearchがどのように正解(大域的最小値)を見つけるかを可視化するデモンストレーション」**です。
一言で言えば、**「普通のfminconなら失敗するような『意地悪な関数』でも、GlobalSearchなら正解できることを目で見て確認するための実験コード」**です。
以下に、このコードが何を行っているか、セクションごとに専門的な視点で解説します。
1. 攻略対象:Rastrigin関数(ラストリギン関数)の定義
fun = @(x) x(1)^2 + x(2)^2 - 10*cos(2*pi*x(1)) - 10*cos(2*pi*x(2));
- 正体: これは最適化アルゴリズムの性能評価によく使われる**ベンチマーク関数(Rastrigin関数)**の変形です。
-
特徴: 全体としてはお椀型(放物面)をしていますが、
cos(コサイン)項が入っているため、表面が**「卵のパック」のように無数の凸凹(局所解)**で覆われています。 -
難しさ: 通常の勾配法(
fmincon単体など)は、近くの凹みにすぐ落ちてまって動けなくなるため、一番深い底(大域的最小値)にたどり着くのが非常に困難です。
2. ワナの設置:悪い初期値
x0 = [3, 3];
-
意図: 正解(大域的最小値)は
[0, 0]付近にあるのですが、あえて遠く離れた[3, 3]という場所をスタート地点にしています。 -
fmincon単体だと: この
[3, 3]のすぐ近くにある小さな谷(局所解)にハマってしまい、そこで計算が終了してしまいます。
3. 解決策:GlobalSearchの実行
gs = GlobalSearch;
[x_sol, f_sol] = run(gs, problem);
-
動作: ここで前述の「スキャッターサーチ」が動きます。初期値
[3, 3]にとらわれず、定義域[-5, 5]全体に何千もの点をばら撒き、一番深い谷([0, 0]付近)を自動的に探し当てます。
4. 結果の可視化(ここがコードのメイン)
このコードの後半は、結果を人間が直感的に理解するためのグラフ描画です。
左側の図:3Dサーフェスプロット(全体像)
- 地形: 関数の形状を立体的(山と谷)に表示します。
- 青い丸(●): スタート地点(初期値)。山の中腹にある小さな谷にいます。
-
赤い星(★):
GlobalSearchが見つけた最終的な答え。無数の小さな谷を飛び越えて、一番深い底に到達していることが確認できます。- ※
+20しているのは、マーカーがグラフに埋もれないように少し浮かせて表示するためです。
- ※
右側の図:等高線プロット(真上から見た図)
- 地図: 真上から見た地形図です。
- 位置確認: スタート地点(青)とゴール地点(赤)がどれだけ離れているか、そして赤丸がちゃんと中心(一番低いところ)にあるかを確認できます。
研究への示唆
実際の実験データに理論モデルをフィッティングさせる際、目的関数(誤差の二乗和)の形状は、このRastrigin関数のように凸凹している可能性が高いです(ノイズやモデルの複雑さによる)。
-
もし
fminconだけを使っていたら: 青い丸の位置(間違ったパラメータ)で「解析完了」としてしまい、正しい組織性状診断ができないリスクがあります。 -
GlobalSearchを使えば: たとえ初期値が適当であっても、赤い星(真のパラメータ)を見つけ出せる可能性が格段に上がります。
B. GlobalSearchのプロパティ解説
MATLABのGlobalSearchオブジェクトには、**「計算の徹底度(精度)」と「計算時間」**のバランスを調整するためのプロパティがいくつか用意されています。
これらはデフォルト設定でも十分に機能しますが、「計算時間がかかりすぎる」あるいは「まだ局所解にハマっている気がする」という場合に調整が必要です。
主要なプロパティを、探索の広さ・フィルタリング・表示の3つのカテゴリに分けて解説します。
1. 探索の広さを決めるプロパティ(重要)
これらは「最初にどれだけ手広く探すか」を決定します。値を大きくすれば大域的最小値を見つける確率は上がりますが、計算時間は長くなります。
| プロパティ名 | デフォルト値 | 解説 | 研究への応用 |
|---|---|---|---|
NumTrialPoints |
1000 |
総トライアル点数 探索空間(lbからubの間)にばら撒く点の総数です。この中から有望な候補が選抜されます。 |
最も調整頻度が高いです。 パラメータ数が多かったり、探索範囲が広い場合は 2000 や 5000 に増やすと精度が向上します。 |
NumStageOnePoints |
200 |
第1ステージ候補点数NumTrialPoints の中から、「とりあえず評価してみる」候補の数です。これ以外の残りの点は、さらに厳しいフィルタリングにかけられます。 |
基本はデフォルトでOKですが、初期値依存性が極めて高い場合は少し増やすこともあります。 |
2. 無駄な計算を省く(フィルタリング)プロパティ
GlobalSearchは、すべての点に対して重いfminconを実行するわけではありません。「すでに計算した解と同じ場所に収束しそうな点」をスキップする機能があります。その判定基準を設定します。
| プロパティ名 | デフォルト値 | 解説 | 研究への応用 |
|---|---|---|---|
BasinRadiusFactor |
0.2 (0〜1) |
吸引領域の推定半径 「ある局所解が見つかったとき、その周りどれくらいの範囲(半径)の点は同じ解に落ち込むとみなすか」の係数です。 ・大きい値: スキップが多くなり高速。 ・小さい値: 細かく探索するため高精度。 |
解が密集している場合(似たようなパラメータで微妙に誤差が変わる場合)、この値を 0.1 程度に小さくすると、見逃しが減ります。 |
DistanceThresholdFactor |
0.75 (0〜1) |
解の同一視判定 新しく見つけた解が、既存の解とどれくらい近ければ「同じ解」とみなすかの基準です。 |
基本的にデフォルトのままで大丈夫です。 |
3. 実行制御・表示に関するプロパティ
| プロパティ名 | デフォルト値 | 解説 |
|---|---|---|
MaxTime |
Inf (無限) |
最大計算時間(秒) この時間を超えたら、探索が終わっていなくても強制終了し、その時点での最良解を返します。膨大なデータをバッチ処理する際に「1データあたり最大60秒」と区切るのに便利です。 |
Display |
'final' |
ログ表示設定'iter' にすると、新しい局所解が見つかるたびにコマンドウィンドウに進捗が表示されます。デバッグ時は 'iter' が推奨です。 |
StartPointsToRun |
'all' |
'bounds'などに変更すると、制約条件の境界上の点だけを探索するなど特殊な挙動をさせられますが、通常は'all'(全範囲)を使います。 |
プロパティの変更方法(コード例)
プロパティの変更方法は2通りあります。
方法A:オブジェクト作成時に指定する(推奨)
gs = GlobalSearch('NumTrialPoints', 3000, 'Display', 'iter', 'MaxTime', 300);
方法B:作成後に書き換える
gs = GlobalSearch;
gs.NumTrialPoints = 5000; % 念入りに探したいので増やす
gs.BasinRadiusFactor = 0.1; % 谷が狭そうなので判定を厳しくする
推奨セッティング
もし以下のような課題があるなら、設定をこう変えてみてください。
ケース1:解析結果がバラつく、たまに変な値が出る
(探索不足の可能性が高い)
gs.NumTrialPoints = 5000; % デフォルト(1000)の5倍にして網羅性を高める
ケース2:画像1枚(数百ピクセル)の解析に時間がかかりすぎる
(無駄な探索が多い可能性)
gs.NumTrialPoints = 500; % 点数を減らす
gs.BasinRadiusFactor = 0.5; % 「だいたい同じ場所なら計算しない」と判定を緩くする
gs.MaxTime = 10; % 1ピクセルあたり10秒で打ち切る
まずは Display を 'iter' にして実行し、ログを見ながら「どれくらいの頻度で新しい解が見つかっているか」を確認するのがベストプラクティスです。
アルゴリズムの概略について知りたい方はこちらもよろしくお願いします。