コンテンツにスキップ

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=0 optional 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 として入れる。

ドキュメント:

カード・ワザのメタデータ

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_logit
  • future_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_gain
  • this_turn_prize_gain_mask
  • own_prize_completion
  • opp_prize_completion
  • prize_completion_mask

unfinished game では completion target を mask する。

統合Beliefヘッド

standalone BeliefNet は残しつつ、v13 unified checkpoint から belief も出せるようにする方針にした。

追加する head:

  • opponent_hand_logits
  • opponent_deck_logits
  • opponent_prize_logits
  • next_threat_logits
  • knockout_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.py
  • BaseRuleAgent / RuleDecision
  • src/pca/rule_agents/agents/<agent_id>/
  • agent.py
  • main.py
  • deck.csv
  • configs/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_tempomega_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/1339272
  • records_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.pt
  • checkpoints/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.yaml
  • policy0: search
  • policy1: 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=3072096 decisions * d4 * s80 と一致しており、設定通り探索されている。
  • hidden=384/38496 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.3s
  • enc_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: true
  • ismcts-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.32choice=0.65 に偏っており、END も攻撃もほぼ選ばれていない。
  • 探索が optional choice / ability に吸われて、ターンを終えられていない可能性が高い。

最も疑わしい原因:

  • src/pca/search/mcts.pyforced_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_steps
  • select type/context/min/max/options/turn_action
  • last 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_batch
  • fwd seconds
  • requests/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 のデータが混ざるため危険。