2026-07-14 V14 cycle001 promotion 失敗の診断¶
状況¶
本書は、V14 の最初の 1000 game self-play cycle で作成した Candidate が Champion との promotion head-to-head に負け越した原因を調査した記録である。現行仕様ではなく、特定 run の分析結果と次の検証計画を扱う。
対象 run:
| 項目 | 値 |
|---|---|
| run name | v14-gumbel-iterative-10k |
| run ID | 20260714-005806-cycle001 |
| self-play | 1000 games、10 chunks、8 workers、CPU |
| train | 1 epoch、batch 64、AdamW、MPS |
| Candidate | checkpoints/policy_value_v14_gumbel_iterative_10k_20260714-005806-cycle001_best.pt |
| Champion | checkpoints/policy_value_latest_best.pt |
| MLflow cycle run ID | 0ba8addf22fa4b3bab8929424773ce52 |
2026-07-14 23:10 JST 時点で head-to-head は完了し、Candidate 対 rule-pool 評価は継続中だった。本書の promotion 数値は head-to-head だけを対象とする。
分析方法¶
この分析は学習処理を再実行せず、保存済み artifact と MLflow の metric history を読み取って行った。集計用の一時コードは artifact を変更しない read-only の Python 処理とし、勝敗は常に Candidate / Champion の向きへ正規化した。
1. 対象 run と checkpoint の特定¶
次の manifest、checkpoint manifest、学習後 checkpoint を照合し、cycle001 の親子関係を確認した。
logs/selfplay-train/v14-gumbel-iterative-10k-20260714-005806-cycle001.manifest.yamlcheckpoints/policy_value_latest.yamlcheckpoints/policy_value_v14_gumbel_iterative_10k_20260714-005806-cycle001_best.pt
manifest の入力 checkpoint を Champion、学習後の best checkpoint を Candidate とした。ファイル名の
latest だけから推測せず、manifest に記録された実体 path と照合した。
2. Promotion head-to-head の集計¶
次の2つの評価結果を読み、player位置を入れ替えた結果を合算した。
data/eval/promotion-gate/v14-gumbel-iterative-10k-20260714-005806-cycle001/head_to_head_forward.jsondata/eval/promotion-gate/v14-gumbel-iterative-10k-20260714-005806-cycle001/head_to_head_reverse.json
forward では Candidate を player 0、reverse では Candidate を player
1 として勝数を数えた。deck別集計も同様に向きを正規化し、同じ7
decksについて Candidate の勝数を合算した。決着勝率は
Candidate wins / finished games、95%信頼区間は二項比率の Wilson
interval で概算した。未完了数と Gumbel fallback 数も確認し、評価失敗を負けとして混ぜていない。
3. MLflow training metric の比較¶
MLflow cycle run 0ba8addf22fa4b3bab8929424773ce52 から MlflowClient.get_metric_history で train
metric の履歴を取得し、validation metric はrunの最終値を読んだ。主に次を比較した。
- total / policy / value loss
- policy KL と policy top-1 agreement
- predicted / target policy entropy
- gradient clipping 前の norm
policyの変化は最初に記録された累積window付近の batch 50 と epoch終了の batch 1820 を比較した。これらはbatch単体値ではなくrunning metricなので、差が小さいことは「改善の証拠が弱い」と解釈し、完全に更新されていないとは断定していない。
4. Deck sampling seed の再現¶
self-play script とparallel workerのseed生成箇所を読み、当時の式を次のように再現した。
chunk_seed = base_seed + chunk_index
worker_seed = chunk_seed + worker_index
各workerへ割り当てられたgame数と同じ回数だけ、実装と同じdeck候補・重み・乱数順で player 0 / player 1を選んだ。再現したdeck出現数をself-playのgame metricsと比較し、一致したことから、観測した偏りが集計ミスではなくseed系列の重複に由来すると判断した。
分布の確認には、30 decksへ2000 assignmentsが均等に配られた場合の期待値 2000 / 30 = 66.7
を基準にした。全体の最少・最多とpromotion対象7
decksの出現数を比較したが、分布偏りだけで勝敗差を説明しないよう、十分出現したdeckの退行も別に確認した。
5. Game ID とsplitの検査¶
10個のpart JSONLをstreamで読み、partごとの game_id
の最小値、最大値、unique数を集計した。続いてmerged JSONL全体のunique game_id
とrecord数を数え、train/validation split summaryと比較した。
part単位: game_id 0..99、各100 IDs
merged: 122,459 records、100 unique game IDs
split: total_games=100、train=95、validation=5
同じlocal IDを持つ10 gamesが同じsplit側へ入るため、record単位の即時leakageとは限らない。一方でgame数、replay ratio、game-level集計が誤ることは確認できるため、独立した不具合として扱った。
6. 判断時の制約¶
- rule-pool評価は分析時点で実行中だったため、promotionの結論には含めていない。
- validationは同じcycleのself-playから分割され、Championの学習前baselineがない。このため、低いvalidation lossだけでは能力向上を示せない。
- gradient norm、target entropy、deck偏りは相関する観測であり、単独原因とは断定していない。
- Gumbel teacherの良否を判断するには、同一checkpoint・同一dataでVisit、completed-Q、混合targetを比較する必要がある。
結論¶
Candidate は Champion に 60-80、決着勝率 42.9% で負け、52% の promotion 基準を満たさなかった。未完了と Gumbel fallback はともに 0 であり、評価処理の失敗ではない。
分析では、次の問題を確認した。
- chunk と worker の RNG seed が重複し、self-play の deck 出現数が大きく偏った。
- 修正前に生成した各 chunk の
game_idがすべて 0-99 で、1000 games が downstream では 100 個の game ID として認識された。 - 1 epoch を通して policy loss、policy KL、探索 target との top-1 agreement がほぼ改善しなかった。
- Gumbel completed-Q target は entropy 0.31 とかなり鋭い一方、モデル予測 entropy は 1.24 で、探索結果を十分に蒸留できていない。
- clipping 前 gradient norm は約 10-13 で、
gradient-clip-norm: 1.0によりほぼ全 batch の勾配が強く縮小された。
したがって、この run だけから Gumbel teacher 自体が弱いとは結論しない。まず deck sampling、game identity、学習前 baseline を直したうえで、更新強度と policy target を制御比較する。
昇格判定のための新旧モデル対戦¶
固定 7 decks、同一 deck mirror、player 位置入れ替えで 70 games を 2 方向実行した。
| 方向 | Candidate | Champion | 未完了 |
|---|---|---|---|
| Candidate p0 / Champion p1 | 30 | 40 | 0 |
| Champion p0 / Candidate p1 | 30 | 40 | 0 |
| 合計 | 60 | 80 | 0 |
Candidate の決着勝率は 42.9% で、Wilson 95% 信頼区間は概算 35.0-51.1% である。両 player 位置で同じ 30-40 だったため、今回の差を先後だけでは説明できない。
Deck 別¶
| Deck | Candidate | Champion | Candidate win rate |
|---|---|---|---|
| Archaludon | 9 | 11 | 45% |
| Garchomp | 7 | 13 | 35% |
| Dragapult | 7 | 13 | 35% |
| Iono / Bellibolt | 7 | 13 | 35% |
| Mega Abomasnow | 8 | 12 | 40% |
| Starmie | 8 | 12 | 40% |
| Mega Lucario | 14 | 6 | 70% |
Lucario だけ改善し、ほかの 6 decks は負け越した。単一の全体 Elo 低下だけでなく、deck ごとの更新方向が異なる可能性がある。
評価 artifact:
data/eval/promotion-gate/v14-gumbel-iterative-10k-20260714-005806-cycle001/head_to_head_forward.jsondata/eval/promotion-gate/v14-gumbel-iterative-10k-20260714-005806-cycle001/head_to_head_reverse.jsonlogs/selfplay-train/20260714-005806-cycle001-promotion-eval.log
MLflow training 分析¶
train/validation の最終指標:
| 指標 | 学習 | 検証 |
|---|---|---|
| total loss | 1.8300 | 1.8193 |
| policy loss | 1.2380 | 1.2091 |
| value loss | 0.4817 | 0.5102 |
| policy KL | 0.9257 | 0.9020 |
| policy top-1 agreement | 51.2% | 51.1% |
| predicted policy entropy | 1.2365 | 1.2096 |
| target policy entropy | 0.3123 | 0.3072 |
validation loss は train loss よりわずかに低く、通常の意味での過学習は見えない。ただし、同じ cycle の self-play から game 単位で分割した validation なので、teacher の誤りや過去能力の忘却は検出できない。
Policy はほぼ改善していない¶
MLflow の running metric を最初の batch window と epoch 終了で比較した。
| 指標 | バッチ50 | バッチ1820 |
|---|---|---|
| policy loss | 1.2397 | 1.2380 |
| policy KL | 0.9241 | 0.9257 |
| policy top-1 agreement | 51.4% | 51.2% |
running metric は累積平均であることを考慮しても、window metric に一方向の改善はなく、探索 target の蒸留が進んだ証拠は弱い。
target entropy 0.31 は実効候補数に直すと約 1.4、予測 entropy 1.24 は約 3.5 に相当する。これは teacher がほぼ 1 手へ集中する一方、student が複数手へ確率を残している状態である。
勾配クリップ¶
torch.nn.utils.clip_grad_norm_ が返す clipping 前 norm は、ほぼ全区間で 10-13 だった。設定は
gradient-clip-norm: 1.0
なので、各 update は概算 8-10% まで縮小される。clip 自体は発散防止に必要だが、learning-rate: 1e-5、1
epoch と組み合わせると更新不足になる可能性がある。
現状は clipping 後 norm、clip 適用率、学習前 validation を記録していないため、これだけで clip=1.0
を原因と断定しない。
train/running 全系列¶
MLflow child run 0ba8addf22fa4b3bab8929424773ce52 に保存された train/running/*
は20系列、各37点で、batch 50から1820までを記録している。running
は開始から当該batchまでの累積batch平均であるため、終盤の変化を薄める。以下では、最初と最後の累積値に加え、直近windowの値も併記した。通常windowは50
batchesだが、最後だけ20 batchesなので変動がやや大きい。
| Series | Running 50 | Running 1820 | Last window | 診断 |
|---|---|---|---|---|
loss |
1.8164 | 1.8300 | 1.8431 | 改善なし。複数headの増減が相殺されるため単独では判断しない |
policy_loss |
1.2397 | 1.2380 | 1.2605 | 累積は横ばい、終盤は悪化 |
policy_kl |
0.9241 | 0.9257 | 0.9638 | targetへの一致は改善せず、終盤は悪化 |
policy_target_entropy |
0.3156 | 0.3123 | 0.2967 | teacher targetは一貫して非常に鋭く、終盤はさらに鋭い |
policy_predicted_entropy |
1.2421 | 1.2365 | 1.2693 | studentはtargetより大幅に拡散し、終盤は差が拡大 |
policy_top1_agreement |
51.44% | 51.19% | 48.98% | 改善なし。終盤windowは50%を下回る |
policy_weight_mean |
0.4999 | 0.5160 | 0.4952 | sample構成の変化。lossはweight sumで正規化されるためbranch全体の倍率ではない |
value_loss |
0.4704 | 0.4817 | 0.4751 | 一方向の改善なし。validationは0.5102でtrainより悪い |
value_weight_mean |
1.0000 | 1.0000 | 1.0000 | 全sampleがvalue学習対象 |
turn_value_coverage |
100% | 100% | 100% | turn value targetは欠損なし |
aux_prize_loss |
0.1033 | 0.1073 | 0.1044 | 重み込み合計はほぼ横ばい |
this_turn_prize_loss |
0.4890 | 0.5213 | 0.4557 | 変動が大きい。最終windowだけは改善 |
own_prize_completion_loss |
0.5308 | 0.5475 | 0.5948 | 終盤で悪化 |
opp_prize_completion_loss |
0.5563 | 0.5556 | 0.5829 | 累積は横ばい、終盤で悪化 |
aux_prize_coverage |
92.41% | 92.95% | 95.00% | label coverageは十分。上昇は主にsample構成を表す |
prize_completion_coverage |
84.75% | 87.89% | 89.77% | label coverageは上昇したが、loss改善とは結び付いていない |
integrated_belief_loss |
0.003102 | 0.003009 | 0.002984 | わずかに低下。ただし重み込みかつ疎なbinary targetなので精度の根拠には弱い |
integrated_belief_coverage |
100% | 100% | 100% | belief targetは欠損なし |
grad_norm |
10.93 | 10.90 | 13.03 | clip前norm。上限1.0を継続的に大幅超過 |
lr |
4.72e-6 | 5.00e-6 | 9.77e-10 | runningは累積平均。実際の終端LRはほぼ0 |
policy_target_entropy=0.3123 の実効候補数は exp(entropy)=1.37、予測側は3.44である。したがって
policy_loss=1.2380 のうち約0.9257はtargetと予測のKL差であり、teacherの鋭いcompleted-Q
targetをstudentが蒸留できていないことがpolicy側の中心問題である。top-1も約51%のままなので、単に確率校正だけがずれている状態ではない。
total lossの最終値は、policy 1.2380 + value 0.4817 + aux 0.1073 + belief 0.0030 = 1.8300
である。policyが全体の約68%を占めるため、belief
lossの小ささだけでは行動性能を押し上げられない。またbelief lossはcard vocabulary全体に対するbinary
BCEを平均してから0.05の重みを掛けた値であり、非該当cardの多さによって低くなりやすい。belief品質の判定にはcard-level
precision/recall、AUPRC、count calibrationを別途記録する必要がある。
cosine scheduleは最終windowでLRをほぼ0まで落とす。streaming shuffle bufferは10,000 recordsで、全116,418 train recordsより小さいため、終盤に異なる難易度やtarget分布が現れても、その区間ではほぼ更新できない。終盤windowでtarget entropyが0.2967まで鋭くなり、policy KLが0.9638、top-1が48.98%へ悪化したことと整合する。ただしデータ順序との因果は、固定validationを学習途中にも測るか、同じデータで複数epochを回す比較実験が必要である。
このrunではclip後norm、clip scale、clip適用率、学習前validationをまだ保存していない。現在の実装にはこれらを追加済みで、次cycleから「更新が弱すぎる」のか「target自体を学べない」のかを直接切り分けられる。
Deck sampling の seed 重複¶
self-play は各 chunk の seed を base_seed + chunk とし、parallel worker はさらに
seed + worker_index を使っていた。このため次のように RNG 系列が重複する。
chunk 0 / worker 1 -> seed 1
chunk 1 / worker 0 -> seed 1
chunk 1 / worker 1 -> seed 2
chunk 2 / worker 0 -> seed 2
weighted_deck_choice は同一 RNG から player 0 / player 1 の deck を順番に選ぶ。10 chunks、各 100
games、8
workers の条件を上記 seed で再計算すると、実測した deck 出現数と一致した。したがって、偏りは偶然ではなく seed 設計から再現する。
30 decks、1000 games では両 player 合計 2000 deck assignments なので、均等なら 1 deck あたり約 66.7 回になる。promotion 対象 7 decks の実測は次の通りだった。
| Deck | 両 player 合計出現数 |
|---|---|
| Garchomp | 27 |
| Starmie | 60 |
| Iono / Bellibolt | 64 |
| Mega Lucario | 66 |
| Dragapult | 72 |
| Archaludon | 78 |
| Mega Abomasnow | 82 |
全 30 decks では両 player 合計の最少が 29、最多が 115 だった。特に Garchomp は期待値の約 40% しかなく、promotion でも 7-13 と大きく負け越した。ただし、十分出現した Dragapult、Iono、Abomasnow も負け越しているため、deck 偏りだけで 60-80 の全差を説明しない。
今後は global game ID から独立 seed を導出するか、30 x 30 ordered matchup を先に一巡させる balanced-pairs sampling を使う。
Game ID 重複¶
生成済みの各 part JSONL は、すべて game_id=0..99 だった。
| 生成物 | 実測 |
|---|---|
| game metrics JSONL | 1000 lines |
| merged training JSONL | 122,459 records |
unique game_id |
100 |
| split summary の total games | 100 |
| train records | 116,418 |
| validation records | 6,041 |
同じ local ID の 10 games が同じ split 側へ入ったため、record 数としてはおおむね 950 games 対 50 games に分かれており、直ちに train/validation record leakage が起きたとは限らない。一方、replay builder は recent games を 95 と認識して過去追加数を計算するため、将来 cycle の replay ratio と game-level 集計は壊れる。
現在の script と parallel self-play には PCA_SELFPLAY_STEP_OFFSET が追加されているが、今回の part
files はその修正前に生成された。chunk、--start-chunk、--resumeを含む回帰テストが必要である。
Config に関する補足¶
cycle manifest の train config は configs/v13/train-policy-unified-selfplay.yaml
だった。現在用意した configs/v14/train-policy-gumbel-selfplay.yamlとの差は、artifact path と
policy-target-temperature: 1.0
の明示が中心である。実行時の主要 optimizer/model 設定は一致しているため、現時点ではファイル名の V13/V14 差を主要因とはみなさない。ただし、再現性のため次回は V14
config を明示する。
原因の確度¶
確認済み¶
- Candidate は Champion に 60-80 で負けた。
- deck sampling の RNG seed が chunk/worker 間で重複し、deck 分布を偏らせた。
- 生成済み part 間で
game_idが重複した。 - policy loss、KL、top-1 agreement は 1 epoch でほぼ改善しなかった。
- gradient clipping 前 norm は上限を大幅に超えていた。
- train/validation gap に典型的な過学習は見えない。
有力だが未確定¶
- deck 不均衡が deck 別退行の一部を引き起こした。
- 1 epoch、低 learning rate、強い clipping の組み合わせが更新不足を引き起こした。
- 鋭い Gumbel completed-Q target を現在の student が十分に蒸留できていない。
未確認¶
- Gumbel completed-Q target 自体が Visit target より学習教師として悪い。
- policy/value/aux/belief の勾配競合が主因である。
- model capacity が不足している。
- 評価乱数だけで 60-80 の差が生じた。
次の実行順¶
- chunk/worker/game ごとに一意で再現可能な seed を使う。
- balanced-pairs deck sampling と deck 出現数検証を追加する。
game_idの chunk/resume 回帰テストを追加する。- optimizer 更新前の Champion を同じ validation で測る。
- deck 別の records、policy loss、KL、top-1 agreement、target entropy を MLflow に出す。
- clip 前後 norm、clip scale、clip 適用率を記録する。
- 同じ初期 checkpoint とデータで次を比較する。
- 1 epoch、LR
1e-5、clip1.0 - 1 epoch、LR
1e-5、clip5.0 - 最大 2-3 epochs、LR
5e-6、clip5.0、early stopping - completed-Q、Visit、両者の混合 target を同じデータで比較する。
- 小規模 cycle で teacher agreement と frozen validation が改善した候補だけ promotion に回す。
改善実装の進捗¶
同日、上記1-3に対して次を実装した。
- base seedとglobal game IDからdeck、search policy、oracle policy用のseedを分離して導出する。
- V14 self-playの既定を
deck-sampling: balanced_pairsとし、全ordered matchupを一巡してから再利用する。 - workerへ渡すglobal game offsetの二重加算を修正した。
- chunk分割、worker分割、途中resumeの境界が変わっても、同じglobal game IDのdeck matchupを維持する。
- 30対30 decksの先頭90 gamesで各deckが両側とも3回ずつ出現することを回帰テストで確認する。
- 実CABTの2-worker smokeで、offset 100から生成した4 gamesがrecordsとgame metricsの双方で
100, 101, 102, 103になることを確認する。
さらに、学習前checkpointを同じvalidation splitで測るpretrain_val_* baselineと、clip前後norm、clip
scale、clip適用率を追加した。これにより項目4と6の観測基盤まで完了した。次の改善対象はdeck別の学習metricと、target/optimizerの制御比較である。
今回の Candidate は latest Champion へ昇格させず、失敗分析用 artifact として保持する。