はじめに

https://rikiyat.github.io/blog/weight-space-programming/ でLLMにネットワークの重みを直接書き換えさせるのが話題になっていたので試してみた。

今回はWCSC36で使用したResNet36x512のモデルからResNet10x154への蒸留と強化を試してみた。 来年のWCSC37ではハードウェアの都合上大きなモデルを搭載できないため、知識蒸留等を行って小さいモデルで精度を上げる必要があるが、これがうまくいけばほとんど学習をさせずにモデルの精度を上げられるとの目論見がある。

実験条件

今回はGitHub CopilotでモデルをClaude Opus5.5をに設定して実験を行った。

ネットワークの重みはONNX形式で渡した。 ただしGitHub CopilotのClaude Opus5.5だとONNXファイルを直接読み込むことができなかったため、プロンプトでONNXファイルのパスを与えて読み込ませた。

機械学習ライブラリやcshogiなどのツール及び学習データ・棋譜の使用は学習用途以外での使用のみ認めた。

また、プロンプトで以下のリンクを渡し、この手法に則ってパラメータを編集するように指示した。

https://rikiyat.github.io/blog/weight-space-programming/

モデルの解析

手始めにResNet35x512とResNet10x154のモデルの解析をさせ、どのブロック・チャンネルでそれぞれどのような役割を担っているか解析させてみた。 それらしい解析結果が出てきたが検証のしようがないため正しいかどうかは不明である。

ResNet35x512

LLMの解析結果をそのまま貼る。

ResNet35x512 の解析結果

全体の構造

  • 構成は、入力層、BN(バッチ正規化)、残差ブロック35個(3×3畳み込み 512→512)、policy と value の2つのヘッドです。
  • 512本のうち320本は、入力層の BN の γ が 0 です。 生の入力を受け取るのは192本だけで、残りの320本は途中のブロックが計算結果を書き込むための空きチャンネルになっています。
  • ブロック6は重みがすべて0(バイアスのみ)で、実質的に何もしていません。
  • 書き込みの量はブロック0〜11では小さく(ノルム0.2〜0.7)、ブロック12以降で増え、ブロック27・33・34が最大(2.3〜2.9)です。

入力層:生の入力情報を受けるチャンネル

入力カテゴリ 1特徴あたりの重みの大きさ
利き数(自・敵) 3.7〜3.9(最大)
駒の配置 約3.0
利き 約2.7
王手 1.5
持ち駒 約0.5(歩の5枚目以降はほぼ0)

個別の特徴で最も重視されているのは、自玉と敵玉の利き、利き数1以上、歩と香です。代表的なチャンネルは次のとおりです(3×3カーネルは、ほぼ中央の1マスだけに重みがあります)。

  • ch470:自玉
  • ch237:敵玉
  • ch86:自金・と
  • ch418:敵金・成銀
  • ch17:敵の馬・角・竜
  • ch23:敵の竜・飛
  • ch263:自馬
  • ch346:自角・馬
  • ch340:自飛・竜
  • 持ち駒:香→ch12、桂→ch369、銀→ch117、金→ch177、角→ch384・ch441、飛→ch447、王手→ch247

玉と金のチャンネル(ch470、ch237、ch86、ch418)は、多くのブロックが読みに行く中心的なチャンネルです。後段で生まれる重要なチャンネルも、主にこの4本から作られています。

value(局面評価)に効くチャンネル

fc 層の ReLU を無視して線形近似した値です。正の値は手番側の勝率を上げる方向を意味します。

ch 寄与 内容 主に書き込むブロック
329 −43.2 途中で生成。主に自玉・敵玉・敵金・自金の情報から作られます。玉の危険度に相当する特徴だと推測されます 14、15、18
500 +32.5 ほぼ途中で生成。元は自玉・敵玉・自金・自銀です。玉の安全度か、相手玉を攻めている度合いと推測されます 13、8、7
263 +19.2 自馬(敵の香とは逆符号) 33、34、29
328、331、507 +11〜17 途中で生成 中盤〜後半のブロック
166 −15.0 自分の利き数が少ないほど評価が下がる(利きの薄さ) 18、7、17
17 −14.5 敵の馬・角・竜 34、28
375 +12.9 自分の持ち駒の桂 33、34
470 −10.9 自玉 33、32
4 +10.8 敵玉の利き 20、7
23 −9.6 敵の竜・飛 34、1

まとめると、評価は玉の安全度(ch329、ch500)が中心です。そこに、大駒の成り駒(自馬は+、敵の馬・竜は−)と自分の利きの厚み(ch166) が加わっています。盤上のマス別に見た value の感度は、四隅と盤の端が大きくなりました。これはゼロパディングによる影響を含むので、そのまま「隅が重要」とは読まないでください。

policy(指し手)に効くチャンネル

  • ch309 と ch397 は、ほぼすべての移動ラベルで上位に入ります。「何かが動ける」ことを表す汎用のチャンネルです。

  • 成るかどうかで使われるチャンネルが分かれます。不成では ch397 と ch309、成りでは ch317・ch469・ch448 が中心です。

  • 桂の移動では ch224・ch324・ch218 が専用に使われています。

  • 駒打ちは、持ち駒を表すチャンネルと直接つながっています。

    • 打香→ch12、打桂→ch369
    • 打銀→ch368・ch117
    • 打角→ch384、打飛→ch447
    • 打金は上位が ch424・ch40 で、持ち駒の金 ch177 は12位

    なお、打歩は ch174・ch172 で、持ち駒の歩 ch297 は59位と、つながりは弱めです。

  • value の上位30本と policy の上位30本は1本も重なりません。 最後の段階で、評価用と指し手用にチャンネルの役割がはっきり分かれています。

ResNet10x154

LLMの解析結果をそのまま貼る。

ResNet10x154 の解析結果

全体の特徴

  • 使われていない部品がほとんどない:残差ストリーム、中間チャンネル、value のマップに、使われていないものはありません。value の隠れユニットも、使われていないのは1個(93)だけです。
  • 重複した部品もない:同じブロックの中間チャンネルどうしの方向の類似度は最大0.54、value の隠れユニットどうしでも最大0.76でした。
  • 入力層で γ=0 の残差チャンネルが20本ある:ただしブロック0・1が値を書き込むので、層2以降はすべて働いています。
  • ブロック9の書き込みが最大:書き込み量は他の3〜4倍で、出力を最終的に組み立てる役割です。

残差ブロックの役割

ブロック 書き込み量 役割(推定) 無効化すると減る手(倍率) 無効化の影響(policy loss / value の誤差) 書き込みの大きさと相関する局面の特徴
0 0.141 局面の基本情報の組み立て。自分の利きの数を整理する(ch80「利き2以上」、ch23「利きちょうど1つ」)、飛・竜の利き(ch68) 打歩 0.67、上成 0.69 +0.702 / +0.531(policy への影響が9ブロック中最大) 盤上の駒の数 +0.50
1 0.118 入玉の検出(相手の利きのチャンネル ch52 に書き込む) 上成 0.77、打飛 0.78 +0.372 / +0.385 自玉の入玉 +0.36、敵玉の入玉 +0.37
2 0.123 両玉の周りの攻防の評価(敵玉と敵玉の利き ch125 に書き込む) 打金 0.74、打銀 0.78 +0.269 / +0.354 自玉の周りの相手の利き +0.38、敵玉の周りの自分の利き +0.38
3 0.124 両玉の周りの攻防(桂・香を使う攻め。自玉の利き ch37、相手の持ち桂 ch60 に書き込む) 打桂 0.83、打香 0.89 +0.209 / +0.369 自玉の周りの相手の利き +0.31
4 0.131 桂を使った攻防(ch60、ch73、両玉の利き ch62 に書き込む) 打角 0.84(桂左は1.33倍、打飛は1.24倍に増える) +0.289 / +0.437 自玉の周りの相手の利き +0.23
5 0.148 入玉・終盤での玉の位置(自玉 ch85)と相手の飛車(ch126) 打歩 0.70 +0.310 / +0.387(王手のかかった局面の value に大きく影響) 入玉 +0.29〜0.33、盤上の駒の数 −0.27
6 0.157 大駒が下がって成る手(相手の飛・竜の利き ch116、角・馬の利き ch102 に書き込む) 左下成・右下成 0.08 +0.299 / +0.122(value への影響は小さい) 目立った相関なし
7 0.170 駒打ちの種類の選択(自玉の利き ch108・ch117 に書き込む) 打角 0.51、打桂 0.61(打飛は1.48倍に増える) +0.277 / +0.137(value への影響は小さい) 相手の持ち駒の数 +0.21
8 0.190 王手の局面の処理と、横に動いて成る手(自玉の利き ch108・ch14・ch109 に書き込む) 右成 0.06、左成 0.08 +0.472 / +0.598(王手のかかった局面の value への影響が最大) 王手 −0.27
9 0.522 出力の最終組み立て。ブロック8が作った横成りの候補を絞り込む。value が最も強く読む ch79 への書き込みの大部分を担う 上 0.39、左上 0.29(左成・右成は約12倍に増える) +4.028 / +3.418 王手 −0.55
  • 書き込みの強さ:ブロック0〜5は、書き込みを0.9倍に弱めると policy が悪化しました。ブロック6〜9は、policy だけを見ると書き込みが強すぎます(0.9倍で改善)。ただし value はその強さを必要としています(ブロック9を0.9倍にすると value の誤差が +0.218)。
  • 複数のブロックからの重ねた書き込み:ch21・ch50 は、後ろのブロック(6〜9)から重ねて書き込まれています。

残差チャンネル(ブロック間の154本)の主なもの

駒の配置を表すチャンネル(入力層での意味)
ch 入力層での意味 備考
57 自玉 ブロック0・2・3・4・5・9が読む。大きいモデルの ch470 に相当(方向の類似度 0.92)
41 敵玉 ほぼすべてのブロックが読む中心的なチャンネル。大きいモデルの ch237 に相当(0.93)
100 自金・と・成香 大きいモデルの ch86 に相当(0.92)。ブロック9が読む
148 敵金・と・馬 大きいモデルの ch418 に相当(0.93)。ブロック3が読む
12 敵竜・飛 大きいモデルの ch23 に相当(0.94)
107 敵角・馬 大きいモデルの ch17 に相当(0.79)
64 自馬・竜 大きいモデルの ch263 に相当(0.73)
133 自角・馬 大きいモデルの ch346 に相当(0.95)。ブロック7が読む
88 自飛・竜 大きいモデルの ch340 に相当(0.96)。ブロック6・7が読む
20 敵玉の利き 大きいモデルの ch4 に相当(0.72)
82 相手の利き1以上・自飛 ブロック4〜8が読む
98 自分の銀・香・金 ブロック9が読む。policy では左下(−0.54)・桂左(−0.52)を抑える
policy に直接効くチャンネル
ch 入力層での意味 policy での使われ方 補足
0 自分の歩の利き 上 +0.61、上成 +0.38 歩を突く手を支える。大きいモデルの ch220 に相当
58 敵の飛・角・銀・桂・金 上 +0.50、左上 +0.44、右上 +0.42 敵の駒の前に上がる手を押し上げる
15 途中で生成 上 +0.32(上の重みは全チャンネル中4位)、上成 −0.19 銀や金が前の空きマスへ上がれることを表す。ブロック8・9が強く書き込む
136 自分のと・との利き 打角 −0.44、打金 −0.37、打銀 −0.27、打飛 −0.27、打香 −0.15 最終層では「敵玉に隣接する空きマス」で特に強く出る(隣接マス 1.04 / 遠いマス 0.23)。敵玉の隣への駒打ちを抑える向きに働く
123 自分の持ち金がない(自持金1 −0.18) 打金 −1.36 持っていない金を打つ反則手を抑える部品
8 相手の利きなし・敵玉の利き 打銀 +0.42、打金 +0.41、左下成 +0.31 支えのある、敵玉に隣接したマスで強く出る
83 自分の利きあり・相手の歩の利きなし 打銀 +0.56、打金 +0.49、上 +0.35、打歩 +0.29 駒打ちの候補のマスを示す
21 相手の持ち金・自金・敵玉の利き 打歩 +0.17、打銀 +0.13、上成 −0.28、下 −0.26 ブロック6〜8が重ねて書き込む
50 相手の歩が利いていないマス 打桂 −0.30、打飛 −0.26、打金 −0.12、打銀 −0.12 ブロック7・8が書き込む
23 自分の利きがちょうど1つ(入力層) 打歩 +0.32 最終層では、利きの数による値の違いがほぼなく、入力層での意味を失っている
7 相手の歩 打桂 −0.38、打銀 −0.33 ブロック4が書き込む
91 相手の香の利き 打歩 +0.30、打桂 +0.27
112 自分の銀・飛 打歩 −0.62
37 自玉の利き(負) 上成 −0.24、左 −0.22 ブロック3・5が書き込む
11 相手の香・歩の利き 打銀 −0.50、打金 −0.45
72 敵飛・敵飛の利き 打金 −0.40
34、93、135、137 途中で生成 34:上・上成を抑える/93:右上・左を抑える/135:斜めに上がって成る手/137:斜めに下がる手
value に効くチャンネル
ch 入力層での意味 役割
79 敵玉の利き +0.26、自分の利き1以上 +0.22、相手の利き1以上 −0.21 value ヘッドが最も強く読むチャンネル(value_conv1 からの読み込みの重みが全チャンネル中1位)。最終層の合計は駒の点数差(+0.60)・自分の持ち駒の数(+0.40)と相関し、優勢度に相当する。書き込みの大部分はブロック9(平均 +15.5)で、ブロック0は負(−4.4)に書き込む
3、7、11 3:利きの数の差/7:相手の歩/11:相手の香・歩の利き value_conv1 からの読み込みが強い(平均の1.7〜2.0倍)

中間チャンネル(各ブロックの conv1 出力)で役割が特定できたもの

ブロック・中間ch 読む残差ch 書き込む先と効果 役割
b7 中間ch104 相手の歩の利き(ch120)、相手の利き(ch63)、敵玉の利き(ch43)、自玉の利き(ch96)をいずれも負で読む。周りに利きの少ない、静かなマスで働く 自玉の利き(ch96)+1.28、飛・竜の利き(ch149)+1.25、ch27 +1.13、ch142 +1.05 ほか。policy への合計効果は、左下成 −2.05、右上成 −1.95、左上成 −1.86、桂右成 −1.54、左 −1.45 静かな空きマスで成る手を抑える部品。両玉入玉の局面ではこの抑えが強すぎ、無効化すると両玉入玉の局面で policy が確かに改善する(基準の最善手への log 確率 +0.010 / +0.023)。入玉のない局面への影響はほぼ0
b9 中間ch139 敵玉の利き(ch67)を負で読む。相手の駒(ch41、ch58)を正で読む ch24 −3.20、ch77 +2.66、ch51 +2.55 ほか(主に駒打ちの経路)。ch79 にはわずかに負(−0.18) 持ち駒を持って攻めている状態に反応(攻めとの相関 +0.30、自分の持ち駒の数との相関 +0.36)。主な役割は駒打ち
b9 中間ch62、ch131 — ch79 に正(+0.29、+0.17) 攻めに反応する(相関 +0.26〜0.34)。ただし攻めだけに反応するわけではない
  • 一度も働かない中間チャンネル:0本です(1540本のうち)。
  • 単独で無効化しても影響が小さいもの:43本(floodgate 系で、KL と value の誤差の変化がどちらも0.0005未満)。
  • 探索での最善手との一致で見ると:無効化の候補311本のうち227本で policy が確かに悪化しました。ほぼすべての中間チャンネルが役に立っています。

value ヘッド

value_conv1 の出力マップ(27枚)
マップ 役割
24 自玉が敵陣にいることの目印。敵陣3段の合計は、自玉が入玉していると中央値18.1、入玉していないと3.5(95%点 9.7)。1〜2段目に絞るとより分かれる(13.8 / 1.7)。自玉の位置との相関 0.526
21 敵玉が自陣にいることの目印。自陣3段の合計は、入玉していると18.2、入玉していないと1.8(8〜9段目に絞ると 13.5 / 0.9)
15 盤上の駒の数(局面の段階)。駒の数との相関 0.739(合計の平均は、駒26枚未満で10.3、39枚以上で33.6)。敵玉の位置とも相関(0.487)
18 自玉の位置(相関 0.418)
6 敵玉の位置(相関 0.355)。9段目に絞ると敵玉の入玉の目印になる
20、14 王手がかかると小さくなる(王手 8.3 / 王手なし 19.4)
12 駒の損得に反応する隠れユニットが主に読むマップ
value_fc1 の隠れユニット(154個)
ユニット 役割
25、9、104、141、27、50、142、137、41、66、74、7(value_fc2 が負) 駒の点数差に反応(駒の点数差との相関 −0.62〜−0.71、相手の持ち駒の数との相関 +0.43〜+0.56)。入玉局面では入玉なしの2〜5倍強く働き、評価を大差に振りすぎる原因になる
29、72、71、2、68、135、79、61、122、118、51、116(value_fc2 が正) 上と同じく、入玉局面で強く働き、評価を振りすぎる側に寄与する
131、96、44、24、134、43、81、31、98、76、17、69(value_fc2 が正) 攻めの評価。自分の持ち駒の数(相関 +0.33〜+0.50)と敵玉の周りの自分の利き(+0.23〜+0.43)に反応。入玉局面や攻めていない局面でも同じように働く
146、42、93 使われていない(value_fc2 の重みが約 1e−40)。93は一度も働かない
その他 154個のうち103個は、1個だけ無効化しても value の誤差の変化が0.001未満
  • 線形近似の精度:value ヘッドを ReLU を無視して線形化した評価値は、実際の評価値と相関 0.983 です(z = 3 ↔ 近似値 約7.0)。

ネットワークパラメータの書き換え

上記の解析結果を基にネットワークパラメータの書き換えを行った。

書き換え内容は以下の通り。

  • 入玉ゲートの書き換え
    • 入玉局面で駒の損得に過剰反応するvalue_fc1の24ユニットにおいて、入玉の目印(マップ24の敵陣1〜3段、マップ21の自陣7〜9段)の重みを0.02減らした
      • 入玉局面での駒の損得への過剰反応を抑える狙い
  • バイアス補正
    • 悲観的な評価(−0.11)を打ち消すため、value_fc2.biasを +0.1 にした
  • 評価値の補正
    • value_fc2 の重みとバイアスを1.2倍にして、中程度の評価値を広げた
    • 使われていなかった隠れユニット146・42で、大差の局面における評価値の伸びを抑える折れ線を作った

この書き換えを行い、書き換え前とResNet35x512に対するvalue・loss及びテストデータに対するaccuracyの計測を行った。 テストデータにはDL将棋本付属のテストデータを使用した。(floodgate_test_2017-2018_r3500_eval5000.hcpe)

ResNet35x512に対する計測結果

モデル policy loss value loss(logit の L1) policy accuracy
元のモデル(resnet10x154-144) 1.4436 0.6178 69.37%
書き換え後モデル 1.4436 0.5787 69.37%

テストデータに対する計測結果

モデル policy accuracy value accuracy
元のモデル(resnet10x154-144) 0.4883 0.7567
書き換え後モデル 0.4883 0.7546
ResNet35x512(参考) 0.5469 0.7840

ResNet35x512に対するvalueのlossを低くし、評価値のスケールを近づけることはできたがpolicyの分布を近づけることができなかった。 また、テストデータに対するvalue accuracyは書き換え前に比べてわずかに悪化した。

対局実験

パラメータ書き換え前と書き換え後でどれくらい強くなったか計測するための対局実験を行った。

思考エンジンにはやねうら王(YANEURAOU_ENGINE_DEEP_TENSOR_RT_UBUNTU)を使用し、探索パラメータはデフォルト値を使用した。 やねうら王互角局面集2025を初期局面とし、1分2秒フィッシャーで200局対局した結果を以下に示す。 なお勝率において引き分けは0.5勝0.5敗として計算している。

パラメータ書き換え前と書き換え後の対局結果

書き換え後モデルの勝敗 書き換え後モデルの勝率(%)
77勝91敗32分 46.5

レート差は-24.4±44.4となり、有意差はなくネットワークパラメータ書き換えの効果はなかった。

対局実験の棋譜解析

対局実験の棋譜解析を行い、パラメータ書き換え後モデルの敗因を分析した。 その結果を以下に示す。

入玉の有無ごとの勝敗・勝率内訳

局の種類 局数 書き換え後モデルの勝敗 書き換え後モデルの勝率(%)
入玉なし 138 48勝60敗30分 45.7
書き換え後モデルだけ入玉 13 10勝3敗0分 76.9
元のモデルだけ入玉 16 9勝6敗1分 59.4
相入玉 33 10勝22敗1分 31.8
  • 宣言勝ち: 元のモデル 17局、書き換え後モデル 6局
  • 相入玉: うち19局は両方入玉した時点で点数負けしていた

この表から相入玉の将棋の勝率が著しく低く、正確に局面を評価できていないことがわかった。 これは入玉ゲートを書き換えた影響であると考えられる。 入玉局面の駒の損得に対する過剰反応はあくまでResNet35x512と比べたときの話であり、実際にはこれによって入玉将棋の点数勝負で有利に働いていたようだ。

対局実験の棋譜解析を踏まえたパラメータの書き換え

相入玉の勝率が著しく低くなったことを踏まえ、再度パラメータの書き換えを行った。 書き換え内容は以下の通り。

  • これまでの書き換え内容の破棄
  • ブロック7の中間ch104 の残差チャンネルへの書き込み要素をすべて0に
    • 相手の利きや自玉・相手玉の利きが少ないマスで働き、成る手を抑制しないように

2回目の書き換え後のDL将棋本付属のテストデータに対する計測結果は以下の通り。

テストデータに対する計測結果

モデル policy accuracy value accuracy
元のモデル(resnet10x154-144) 0.4883 0.7567
書き換え後のモデル 0.4883 0.7546
2回目の書き換え後のモデル 0.4882 0.7569
ResNet35x512(参考) 0.5469 0.7840

元のモデルに対し誤差の範囲内でしか精度が向上しておらず、ネットワークパラメータ書き換えの効果が薄いと判断したため、対局実験は行わずここで打ち切った。

まとめ

Weight-Space Programmingの記事を参考に将棋AIのモデルで知識蒸留・改善ができないかを検証した。 その結果、モデル内のどのブロックのどのチャンネルでどのような役割を担っているかを解析することはできたが、ネットワークパラメータの書き換えにより元のモデルより強くすることはできなかった。

今回参考にした記事がミスリーディングをしているという記事もあり、効果は限定的であると考えられる。

https://honnibal.dev/blog/astra-read-write-misleading

今後は実際に知識蒸留を試し、知識蒸留を行ったモデルと行っていないモデルでどのような差があるかLLMに解析させてみる予定である。