TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

【dlshogi】SPSAによる探索パラメータのチューニング

チェスAIのStockfishでは、SPSAという方法で、探索パラメータのチューニングが行われている。

今年の世界コンピュータ将棋選手権の打ち上げで、Qhapaqチームの澤田さんからKaggleのFIDE & Google Efficient Chess AI Challengeで、SPSAが効果的だったという話を聞いて、試そうと思いつつ時間がたってしまった。

やねうら王でも、最新のStockfishのソースを取り込んだ後、SPSAでチューニングを行う方法が取り入れられている。
SPSA、探索パラメーターの自動調整手法について | やねうら王 公式サイト

電竜戦も近いこともあり、そろそろやらねばと思い、今回ひとまずチューニングのスクリプトの実装を行った。

SPSA

目的関数の形式が不明で、勾配を直接計算することが不可能な場合の最適化アルゴリズムである。

dlshogiは、これまではOptunaを使用して探索パラメータの最適化を行っていたが、安定して更新するには信頼できる勝率を求めるために1更新につき100回近い対局が必要だった。

SPSAは、対局結果のようにノイズの多い状況でも使用可能で、少ない対局回数で更新できる。

アルゴリズム自体はシンプルで、すべてのパラメータを±方向に同じ量だけ摂動して評価した結果から勾配を近似して、パラメータ更新を繰り返すだけである。
摂動の幅は、イテレーションが進むにつれて、減衰する。
詳細は、Wikipediaを参照

SPSA in Fishtest

Stockfishでは、原論文にあるSPSAの式はそのまま使用しておらず、チェスに特化した式が使用されている。
Stockfish Docs

元になる論文は探しても見つからないため、経験則から導かれた式のようである。
摂動して勾配を近似し、摂動幅を減衰するという基本的な考え方はSPSAと同じである。

FishtestのSPSAは、OpenBenchに実装されている。

今回は、OpenBenchの実装を参考にして実装した。

dlshogi向けの改良

基準ソフト

dlshogiの探索パラメータの更新用に実装するにあたり、OpenBenchでは、摂動したプラスとマイナスを対局させているが、基準となる固定のソフトがある方が安定するため、基準ソフトを加えた3組の対局結果から勾配を求めるようにした。

並列実行

8GPUを使用して並列実行できるように、ワーカーのID(0~7)をUSIパラメータに埋め込みできるようにした。

開始局面

SFEN形式で記述した開始局面集をファイルで与えられるようにした。
3組×先後の6対局のセットでは、同じ開始局面を使用する。

使い方

コマンドラインから以下のように実行する。

python -m dlshogi.utils.spsa_usi_tuner --baseline D:\src\hayabusa\bin\hayabusa.exe --candidate D:\src\DeepLearningShogi2\x64\Release\dlshogi_tensorrt.exe --options-baseline EvalDir:20250416,FV_SCALE:24,ResignValue:3000,NetworkDelay2:0 --options-candidate "DNN_Model:F:\model\model-pre55-012.onnx,UCT_Threads:{2 if id==0 else 0},UCT_Threads{'' if id==0 else id+1}:2" --params C_init:100~200:127,C_base:20000~40000:27126:10:5,C_fpu_reduction:10~40:31,C_init_root:100~200:112,C_base_root:20000~40000:33311:10:5,Softmax_Temperature:100~200:140 --openings R:\openings.sfen

「--params」が摂動するUSIパラメータの定義で、以下の形式で入力する。

name:min~max:start[:c_end][:r_end][:type]

minは、最小値、maxは最大値、startは初期値を指定する。
c_endは、最終的な摂動の幅を指定する(省略時は1)。
r_endは、最終的な更新率を指定する(省略時は0.5)。
typeは、intまたはfloatを指定する(USIパラメータなので指定なし(int)でよい)。

実行結果

実行すると以下のように表示される。

Loaded 1 openings from R:\openings.sfen.
Parameters:
  C_init: range=(100.0,200.0), start=127.0, c_end=1.0, r_end=0.5, int=True
  C_base: range=(20000.0,40000.0), start=27126.0, c_end=10.0, r_end=5.0, int=True
  C_fpu_reduction: range=(10.0,40.0), start=31.0, c_end=1.0, r_end=0.5, int=True
  C_init_root: range=(100.0,200.0), start=112.0, c_end=1.0, r_end=0.5, int=True
  C_base_root: range=(20000.0,40000.0), start=33311.0, c_end=10.0, r_end=5.0, int=True
  Softmax_Temperature: range=(100.0,200.0), start=140.0, c_end=1.0, r_end=0.5, int=True
[Iter   1] sets=1, games=6
  theta     : C_init=131, C_base=26712, C_fpu_reduction=27, C_init_root=108, C_base_root=33725, Softmax_Temperature=136
  theta+    : C_init=125, C_base=27143, C_fpu_reduction=33, C_init_root=114, C_base_root=33294, Softmax_Temperature=142
  theta-    : C_init=129, C_base=27109, C_fpu_reduction=29, C_init_root=110, C_base_root=33328, Softmax_Temperature=138
  (+) vs base : W-L-D = 0-2-0  (S=-2.0)
  (-) vs base : W-L-D = 1-0-1 (S=1.5)
  (+) vs (-)  : W-L-D = 1-1-0
  magnitude   : -3.50
  updates     : C_init=+4.145, C_base=-414.487, C_fpu_reduction=-4.145, C_init_root=-4.145, C_base_root=+414.487, Softmax_Temperature=-4.145
[Iter   2] sets=1, games=6
  theta     : C_init=132, C_base=26774, C_fpu_reduction=28, C_init_root=107, C_base_root=33787, Softmax_Temperature=135
  theta+    : C_init=129, C_base=26696, C_fpu_reduction=25, C_init_root=110, C_base_root=33709, Softmax_Temperature=138
  theta-    : C_init=133, C_base=26728, C_fpu_reduction=29, C_init_root=106, C_base_root=33741, Softmax_Temperature=134
  (+) vs base : W-L-D = 0-1-1  (S=-0.5)
  (-) vs base : W-L-D = 1-1-0 (S=0.0)
  (+) vs (-)  : W-L-D = 1-1-0
  magnitude   : -0.50
  updates     : C_init=+0.621, C_base=+62.080, C_fpu_reduction=+0.621, C_init_root=-0.621, C_base_root=+62.080, Softmax_Temperature=-0.621
[Iter   3] sets=1, games=6
  theta     : C_init=129, C_base=26458, C_fpu_reduction=25, C_init_root=110, C_base_root=34103, Softmax_Temperature=138
  theta+    : C_init=134, C_base=26790, C_fpu_reduction=30, C_init_root=105, C_base_root=33771, Softmax_Temperature=133
  theta-    : C_init=130, C_base=26758, C_fpu_reduction=26, C_init_root=109, C_base_root=33803, Softmax_Temperature=137
  (+) vs base : W-L-D = 0-2-0  (S=-2.0)
  (-) vs base : W-L-D = 0-1-1 (S=-0.5)
  (+) vs (-)  : W-L-D = 0-1-1
  magnitude   : -2.50
  updates     : C_init=-3.164, C_base=-316.371, C_fpu_reduction=-3.164, C_init_root=+3.164, C_base_root=+316.371, Softmax_Temperature=+3.164
[Iter   4] sets=1, games=6
  theta     : C_init=125, C_base=26012, C_fpu_reduction=29, C_init_root=106, C_base_root=33657, Softmax_Temperature=142
  theta+    : C_init=127, C_base=26443, C_fpu_reduction=27, C_init_root=108, C_base_root=34088, Softmax_Temperature=140
  theta-    : C_init=131, C_base=26473, C_fpu_reduction=23, C_init_root=112, C_base_root=34118, Softmax_Temperature=136
  (+) vs base : W-L-D = 0-0-2  (S=1.0)
  (-) vs base : W-L-D = 0-1-1 (S=-0.5)
  (+) vs (-)  : W-L-D = 2-0-0
  magnitude   : +3.50
  updates     : C_init=-4.464, C_base=-446.446, C_fpu_reduction=+4.464, C_init_root=-4.464, C_base_root=-446.446, Softmax_Temperature=+4.464
[Iter   5] sets=1, games=6
  theta     : C_init=126, C_base=25884, C_fpu_reduction=30, C_init_root=107, C_base_root=33529, Softmax_Temperature=143
  theta+    : C_init=124, C_base=26027, C_fpu_reduction=28, C_init_root=105, C_base_root=33672, Softmax_Temperature=141
  theta-    : C_init=126, C_base=25997, C_fpu_reduction=30, C_init_root=107, C_base_root=33642, Softmax_Temperature=143
  (+) vs base : W-L-D = 0-1-1  (S=-0.5)
  (-) vs base : W-L-D = 0-1-1 (S=-0.5)
  (+) vs (-)  : W-L-D = 0-1-1
  magnitude   : -1.00
  updates     : C_init=+1.278, C_base=-127.828, C_fpu_reduction=+1.278, C_init_root=+1.278, C_base_root=-127.828, Softmax_Temperature=+1.278
[Iter   6] sets=1, games=6
  theta     : C_init=126, C_base=25884, C_fpu_reduction=30, C_init_root=107, C_base_root=33529, Softmax_Temperature=143
  theta+    : C_init=125, C_base=25899, C_fpu_reduction=29, C_init_root=106, C_base_root=33544, Softmax_Temperature=144
  theta-    : C_init=127, C_base=25869, C_fpu_reduction=31, C_init_root=108, C_base_root=33514, Softmax_Temperature=142
  (+) vs base : W-L-D = 1-1-0  (S=0.0)
  (-) vs base : W-L-D = 1-1-0 (S=0.0)
  (+) vs (-)  : W-L-D = 1-1-0
  magnitude   : +0.00
  updates     : C_init=-0.000, C_base=+0.000, C_fpu_reduction=-0.000, C_init_root=-0.000, C_base_root=+0.000, Softmax_Temperature=+0.000

まとめ

SPSAによるUSIパラメータのチューニングを行うスクリプトを作成した。
次は、dlshogの最新モデルでチューニングして強くなるか測定したい。