2026-07-04¶
今日の作業は、v13 unified model の実験、rule-based bootstrap、aux / belief 統合、ISMCTS 高速化、model-vs-rule self-play、そして step が進み続ける問題の調査まで広く進めた。
全体サマリ¶
- v13 の
UnifiedTokenPolicyValueNetを、legacy model と並存する形で使えるようにした。 - 自分のデッキ情報、盤面 object token、履歴 token、合法手 token、カード静的特徴、攻撃 metadata を unified model の入力として扱う方針を整理した。
- self-play JSONL だけで v13 training に必要な card / attack
metadata を復元できるようにし、学習時に CABT API や別
attack_data.jsonに依存しない方針にした。 - aux prize heads と integrated belief heads を policy/value checkpoint に統合する設計を進めた。
- rule-based agent pool から弱い agent を除外した filtered config で 10k bootstrap データを集め、v13 unified model を学習した。
- v13 model を NN-only / ISMCTS / rule agent 相手で評価し、攻撃やサイド取得は増えた一方で、まだ online ISMCTS の挙動と速度に課題が残ることを確認した。
- local policy batching、ISMCTS leaf batching、observation encode cache を入れて速度改善を試した。
- step が盤面変化なしに進み続ける症状を調査し、
minCount=0optional selection を ISMCTS が skip できない可能性が高いと判断した。
v13統合トークンモデル¶
旧 ActionConditionedPolicyValueNet は残し、新しい UnifiedTokenPolicyValueNet
を別パッケージに追加する方針で進めた。
主な設計:
src/pca/models/policy_value.py- legacy path。
- 旧 checkpoint / eval / submission を壊さないため維持。
src/pca/models/policy_value_unified/- v13 unified model。
model_classにより checkpoint loader が旧新を振り分ける。static_card_embedding + dynamic_state + positionを 1 つの object token にする。- board object token は「何のカードか」「どこにあるか」「誰のものか」「何番目か」「HP / damage / energy / status など」を同じ token に持つ。
- own deck は初期実装では Set Transformer で pooled deck vector に圧縮する。
- 将来比較用に
deck_integration=pooled|tokensの考え方を残した。 - 盤面上の山札枚数、手札枚数、トラッシュ枚数、サイド枚数、turn flags は global token として入れる。
ドキュメント:
- docs/architecture/unified-token-policy-value.md
- v13 の入力層、Mermaid 構造図、入出力、JSONL metadata、belief heads を記載。
- docs/architecture/current-method.md
- legacy path と v13 unified path の併存を反映。
カード・ワザのメタデータ¶
v13 training では、カードや攻撃の静的特徴が重要になる。学習時に CABT API へ依存すると再現性が落ちるため、self-play JSONL に metadata を埋め込む方針にした。
方針:
- self-play 収集時に
--embed-attack-metadataを有効にする。 search.card_metadataに card id、名前、カテゴリ、HP、進化段階、feature tokens などを保存する。search.action_metadataに option ごとの attack id、技名、ダメージ、エネルギー要求、テキスト、feature tokens を保存する。- v13 training は JSONL だけを読み、card / attack feature table を復元する。
- metadata が無い場合に静かに fallback するのはやめる。
判断:
- v13 用には新しく self-play を集め直すのが安全。
- 既存 JSONL は policy/value の大枠には使えるが、v13 の static card / attack feature をフルに使うには metadata が不足する。
サイド取得を予測する補助ヘッド¶
勝敗 value だけでは「サイド取得に近い盤面」「継続的にサイドを取れる盤面」を学びにくいので、aux prize targets を追加する方針にした。
最終的に採用した Phase 1:
this_turn_prize_gain_logits- 実教師軌跡で、そのターンに自分が取ったサイド枚数
0..6。 - oracle 最大値ではなく、実際にその軌跡で起きた値を保存する。
own_prize_completion_logitfuture_own_gain / current_own_remaining_prize。- すでにサイドを取った分で単純に値が下がらないよう、残りサイドに対する completion rate とした。
opp_prize_completion_logit- 相手側の同じ指標。
- 守りや相手の進行も見られるようにする。
議論したが Phase 1 では見送ったもの:
- 28-class joint
own_prize_chain_profile - sparse class が多く、勝敗 value と相関しすぎるため見送り。
- 固定 N step / N turn 先の future prize gain
- horizon の裏付けが弱いため見送り。
deck_out_risk_now- 今回の目的とずれるため見送り。
remaining_prize_advantage- 勝ちや盤面の強さを十分表さないため見送り。
JSONL には aux を保存する。
this_turn_prize_gainthis_turn_prize_gain_maskown_prize_completionopp_prize_completionprize_completion_mask
unfinished game では completion target を mask する。
統合Beliefヘッド¶
standalone BeliefNet は残しつつ、v13 unified checkpoint から belief も出せるようにする方針にした。
追加する head:
opponent_hand_logitsopponent_deck_logitsopponent_prize_logitsnext_threat_logitsknockout_threat
推論時の切り替え:
--belief-source auto--belief-checkpointがあれば従来 BeliefNet を使う。- 無ければ v13 policy checkpoint の integrated belief heads を使う。
--belief-source separate- 従来 BeliefNet のみ。
--belief-source policy- v13 policy checkpoint の belief heads のみ。
今日の 10k training では belief_cov=1.0000 で、belief
target が全 record に入っていることを確認した。
ルールエージェント群と初期学習¶
notebooks/rule-ai-agent/ 由来の rule-based
agent を、提出形式に近い module として移植する方針を整理し、agent ごとに directory を分ける設計にした。
設計:
src/pca/rule_agents/base.pyBaseRuleAgent/RuleDecision。src/pca/rule_agents/agents/<agent_id>/agent.pymain.pydeck.csvconfigs/rule_agents.yaml- agent class、deck csv、archetype、policy weight、match weight、train flags を登録。
学習制御:
enabled- 完全除外。
match_weight- self-play に出す頻度。
policy_weight_scale- policy imitation の強さ。
train_policy- policy 学習に使うか。
train_value- value 学習に使うか。
train_belief- belief 学習に使うか。
弱い agent の扱い:
- 明らかに弱い / 壊れている agent は filtered config から除外。
moltres_ex_removalのように policy を低めにしたい agent は policy weight を下げる。dragapult_ex_tempo、mega_lucario_ex_v62は除外方針。generic_advanced_heuristicのような fallback agent は明示指定しない限り使わない。
10k bootstrap:
configs/rule_agents_v13_filtered.yamlを作成。scripts/run_rule_bootstrap_chunks.shで chunk 実行できるようにした。500 games x 20 chunks = 10,000 gamesを基本単位にした。--resumeにより既存 chunk は skip できる。- local CABT が使えるため Docker なし実行も可能にした。
v13のルールエージェントによる初期学習¶
data/selfplay/rule-bootstrap-10k.jsonl を使って v13 unified model を学習した。
実行例:
PYTHONPATH=src uv run python -m pca.training.train \
--config configs/v13/train-policy-unified-rule-bootstrap-10k.yaml \
--init-checkpoint checkpoints/policy_value_v12_warmup_existing_best.pt
学習開始時:
- records:
1,339,272 - epochs:
2 - batch size:
128 - batches/epoch:
10,464 - device:
mps - streaming:
true - prescan:
false
metadata scan:
- JSONL metadata scan は約 17 分かかった。
- cards:
121 - attacks:
58 records_with_card_metadata=1339272/1339272records_with_attack_metadata=1339272/1339272
最終ログ:
epoch=2/2 loss=2.2779 policy=1.1981 value=0.9140 aux=0.1630 belief=0.0028
policy_w=0.13 turn_value=0.00 aux_cov=0.98 belief_cov=1.00
保存:
checkpoints/policy_value_v13_unified_rule_bootstrap_filtered_10k_best.ptcheckpoints/policy_value_v13_unified_rule_bootstrap_filtered_10k_final.pt
解釈:
- policy loss と aux loss は下がっている。
- value loss は高めで、rule bootstrap の勝敗 target だけでは勝率 value の学習がまだ難しい可能性がある。
- belief loss はかなり小さくなったが、rule self-play の full observation target が単純すぎる可能性もあるため、過信しない。
policy_w=0.13は、rule teacher の勝敗・agent 別 weight・低品質 downweight などを反映した平均 policy weight。aux_cov=0.98は aux target が有効な record の割合。- 10k games は bootstrap として価値があるが、これは rule agent 同士の分布であり、最終的に使いたい
v13 model + ISMCTSの分布とは異なる。 - そのため、10k rule bootstrap だけで十分とは見なさない。次はこの checkpoint を初期値にして、ISMCTS 同士の self-play を集め、model/search の分布へ寄せる。
- 特に value loss が高めなので、勝敗 value と aux prize heads は rule bootstrap 後も self-play 分布で追加学習する必要がある。
評価と自己対戦¶
NN-only 評価:
- v13 unified checkpoint を v12 holdout 相手に NN-only で評価した。
- 150 games の結果:
- player0 wins:
86 - player1 wins:
35 - unfinished:
29 - player0 normal wins:
41 - player0 pokemon_out wins:
32 - player0 deck_out wins:
13 - player0 avg attacks:
8.11 - player1 avg attacks:
2.33 - player0 avg prizes:
3.23 - player1 avg prizes:
0.47
解釈:
- rule bootstrap 由来でも、NN-only としては攻撃・サイド取得に向かう挙動がかなり出ている。
- ただし deck-out / pokemon-out も多く、通常勝ちだけで見ればまだ十分ではない。
- ISMCTS 診断は NN-only なので
policy*_ismcts_simulations=0で正しい。
Model vs Rule:
configs/v13/evaluate-v13-ismcts-vs-rule.yamlで v13 ISMCTS と filtered rule agents を評価する方向を整理した。configs/v13/selfplay-v13-ismcts-vs-rule.yamlで model を p0、rule-pool を p1 にして self-play JSONL を集められるようにした。configs/v13/selfplay-rule-vs-v13-ismcts.yamlで逆向きも可能にした。- rule-pool 側は
configs/rule_agents_v13_filtered.yamlの有効 agent と deck を使う。
ISMCTS vs ISMCTS:
- rule-pool ではなく、両側を v13 model + ISMCTS にした self-play config を追加した。
configs/v13/selfplay-v13-ismcts-selfplay.yamlpolicy0: searchpolicy1: search- 両側
checkpoint: policy_value_v13_unified_rule_bootstrap_filtered_10k_best.pt - 両側
belief-source: policy - deck は
decks/opponents/train同士。 - search は
d4s80, root candidates 8, non-root candidates 6, progressive widening on。 search-value-profile: v13_aux_prize_raceで aux prize heads を leaf value に混ぜる。scripts/run_v13_selfplay_train.shの default self-play config も ISMCTS 同士に切り替えた。- rule-pool と対戦したい場合は、明示的に
--selfplay-config configs/v13/selfplay-v13-ismcts-vs-rule.yamlを指定する。
今回の ISMCTS 同士ログの解釈:
agents p0=- p1=-なので rule agent ではなく model 同士で対戦できている。sim=30720は96 decisions * d4 * s80と一致しており、設定通り探索されている。hidden=384/384も96 decisions * 4 determinizationsと一致し、belief determinization が使われている。fail=0,no_action=0,no_select=0なので、探索自体の失敗は出ていない。leaf_batch=4.0なので、determinizations 単位の leaf batching は効いている。enc_cache=77-78%程度で observation encode cache は効いている。- 一方で
attack_reached=0.23-0.24,prize_reached=0.07-0.11の例があり、探索がサイド取得まで到達する割合はまだ低い。 - したがって、探索は正常に動いているが、teacher search としてはまだサイド取得まで見切る力が弱い。
aux prize diagnostics:
例:
aux n=30720 used=30720/30720 value=+0.002/0.028 comp=0.60/0.60/d-0.00 turn_gain=0.12
aux n=36504 used=36504/36504 value=-0.015/0.037 comp=0.54/0.61/d-0.08 turn_gain=0.03
解釈:
nは aux を評価した leaf 数。usedは aux が実際に leaf value に使われた数。value=mean/mean_absは leaf value に足された aux 補正の平均と平均絶対値。comp=own/opp/deltaは root player 視点の own / opp prize completion 予測。turn_gainはこのターンのサイド取得期待値の正規化値。- 今回は
usedが全 leaf で立っており、aux は配線上きちんと使われている。 - ただし補正量は
mean_abs=0.028-0.037程度で、探索判断を大きく変えるほど強くは効いていない。 - 現時点で aux weight を大きく上げるより、まず現在の重みで ISMCTS self-play を集めて aux head 自体を self-play 分布で追加学習する方が安全。
- 上げる場合も最初は
completion 0.20 -> 0.30,this_turn 0.10 -> 0.15程度の短い smoke 比較に留める。
ISMCTSと探索の改善¶
v13 ISMCTS の速度と探索深度を見ながら、次を追加・確認した。
- aux value を search value に混ぜる profile。
search-value-profile: v13_aux_prize_race- candidate ranking に Q/value を少し混ぜる設定。
ismcts-candidate-value-weight- progressive widening。
- root / non-root candidate cap。
- duplicate equivalent action pruning。
- observation encode cache。
- ISMCTS leaf batching。
- local policy batching。
deviceを config でcpu/mps切り替え。
重要なログ指標:
leaf_batch=...- leaf policy/value をまとめて評価できているか。
enc_cache=...- observation encode cache の hit / miss。
tree_enc=...- search tree 内で observation を encoded observation に変換する時間。
reach turn_advance / attack_reached / prize_reached- 探索が実際にターン進行、攻撃、サイド取得まで到達できている割合。
root-actions sel ...- root で選ばれている action type の偏り。
observation encode cache:
- probe で hit rate が 80% 以上出たため、実 cache に置き換えた。
- 例:
enc_cache=84%(hit=32880,miss=6151)/6.3senc_cache=80%(hit=47909,miss=12180)/11.7s- ただし hit 率 80% は tree encode が 80% 速くなることを意味しない。
- cache key 計算や lookup の時間が残る。
- 全体時間では NN forward / CABT search_step / MCTS traversal も支配的。
leaf batching:
ismcts-leaf-batching: trueismcts-leaf-batch-size: auto- auto は determinizations に合わせる考え方。
- 大きくしすぎると leaf evaluation はまとまるが、探索木の分岐や待ち時間が増える可能性がある。
local policy batching:
- cross-worker の local batcher は動作する。
- ただし mac では worker が同期的に request を投げるため、batch size が想定ほど大きくならない場合がある。
- worker 内でさらに非同期 self-play を組む案も検討したが、複雑さが大きいため一旦見送り。
速度観察¶
v13 ISMCTS vs rule の例:
steps=123 elapsed=350.0s
search begin=13440 step=59918 end=42
nn calls=12264 total=210.4s fwd=156.6s enc=23.7s tensor=13.3s
ismcts time=337.3s tree_enc=59918/80.8s
depth avg=4.46
reach attack_reached=0.69 prize_reached=0.49
encode cache 後の別例:
steps=81 elapsed=155.6s
nn total=127.8s fwd=104.8s enc=7.7s tensor=6.5s
ismcts time=151.8s tree_enc=6151/6.1s
enc_cache=84%(hit=32880,miss=6151)/6.3s
解釈:
- encode cache により
tree_encの call 数と秒数は大きく改善した。 - それでも全体では NN forward と CABT search traversal が重い。
- MPS を使っても GPU 使用率が高く見えないのは、CABT search_step、Python 制御、cache lookup、少量 batch の待ちが多いため。
- local batcher の log で
avg_batch=4.0程度なら、GPU を埋め切るほどの batch にはなっていない。
Step が進み続ける問題¶
添付ログで、盤面がほぼ変化しないのに step だけ進む症状を調査した。
問題のログ:
turns [3,3,2,5,2,285]
root-actions sel play=0.01 attach=0.01 evolve=0.01 ability=0.32 choice=0.65
end_sel=0.00
reach turn_advance=0.04 attack_reached=0.01 prize_reached=0.00
解釈:
stepは CABT のbattle_select()を呼んだ回数。- 盤面要約が変わらなくても、choice / ability / forced selection などで step が増えること自体はありうる。
- しかし 1 ターンで 285 step は異常。
ability=0.32、choice=0.65に偏っており、END も攻撃もほぼ選ばれていない。- 探索が optional choice / ability に吸われて、ターンを終えられていない可能性が高い。
最も疑わしい原因:
- src/pca/search/mcts.py の
forced_selection()。 - ISMCTS は
minCount=0の optional 選択でもpick_count = max(encoded.min_count, 1)により最低 1 個を選ぶ。 - 通常 policy の
choose_from_scores()はminCount=0なら[]を選べる。 - つまり ISMCTS だけが「何も選ばない / skip」を探索木で表現できない。
今日入れた安全策:
--max-steps-per-turnを追加。- v13 self-play config では
max-steps-per-turn: 80に設定。 - progress log に次を追加。
turn_stepsselect type/context/min/max/options/turn_actionlast selected=...- cap に当たった場合は
[selfplay][turn-cap]とguard turn_cap_hit=1を出す。
検証:
PYTHONPATH=src:. python -m py_compile src/pca/training/selfplay.py
PYTHONPATH=src:. python -m unittest tests.test_selfplay_summary
どちらも通った。
根治案:
minCount=0のとき、[]を明示的な pseudo action として MCTS の action space に追加する。- 単に
pick_count = max(encoded.min_count, 0)に変えるだけだと optional action を常に skip しがちなので危険。 - skip も policy prior / visit / value で他 action と比較できる形にする必要がある。
コマンドと設定¶
よく使った v13 self-play:
PYTHONPATH=src python -m pca.training.selfplay \
--config configs/v13/selfplay-v13-ismcts-vs-rule.yaml
逆向き:
PYTHONPATH=src python -m pca.training.selfplay \
--config configs/v13/selfplay-rule-vs-v13-ismcts.yaml
10k rule bootstrap:
bash scripts/run_rule_bootstrap_chunks.sh \
--runtime local \
--chunks 20 \
--games-per-chunk 500 \
--workers 8 \
--resume
v13 unified training:
PYTHONPATH=src uv run python -m pca.training.train \
--config configs/v13/train-policy-unified-rule-bootstrap-10k.yaml \
--init-checkpoint checkpoints/policy_value_v12_warmup_existing_best.pt
未解決 / 次にやること¶
最優先:
- ISMCTS の optional selection 問題を根治する。
minCount=0の skip pseudo action を追加する。forced_selection()の挙動を direct policy と揃える。turns [..., 285]のような異常ターンが消えるか確認する。
次点:
- v13 metadata scan の高速化。
- 10k JSONL で 17 分かかっている。
- JSONL 先頭 meta summary、sidecar cache、または chunk summary を検討する。
- value loss が高止まりする原因調査。
- rule bootstrap の勝敗 target 分布。
- value head calibration。
- rule source 別 / deck 別の value error。
- aux heads の診断。
this_turn_prize_gainの calibration。- own / opp completion の相関。
- ISMCTS leaf value に混ぜる weight の感度。
- model vs rule の self-play を増やす。
- rule 側を p0/p1 両方に置く。
- filtered rule agent ごとの相性表を確認する。
- local policy batching の効果測定。
avg_batchfwd secondsrequests/s- worker 数ごとの比較。
今日の判断¶
- v13 は方向性として継続する。
- ただし、online ISMCTS の品質は model より先に action representation の問題を直す必要がある。
- rule bootstrap は NN-only の攻撃・サイド取得改善には効いている。
- しかし rule bootstrap だけで十分ではないため、今後は v13 model + corrected ISMCTS self-play で search teacher を改善する。
- optional selection skip 問題を直さないまま大量 self-play を回すと、無意味な choice / ability loop のデータが混ざるため危険。