---
type: reference
title: "Pokémon TCG AI Battle — Train Config Reference"
description: "Every field of TrainConfig and every CLI flag of bc_train_mlx.py: type, default, semantics, current baseline, deprecations. Config hierarchy: CLI > --config file > configs/train_config.json > defaults."
tags: [pokemon-tcg, train-config, cli, flags, defaults, deprecation, configuration]
timestamp: "2026-08-15T16:31:30-03:00"
---

# Pokémon TCG AI Battle — Train Config Reference

## Boundary

Every value that steers the trainer `scripts/bc/bc_train_mlx.py`. This page is exhaustive for the current schema (no historical fields, no ghosts). Cross-refs use [[pokemon_tcg_glossary]] for term meaning and [[pokemon_tcg_training_pipeline]] for how the values are consumed.

## Current source correction at `20d7d0d`

The field meanings below remain useful, but the runtime dtype contract changed after the August 7 snapshot. The current trainer sets model parameters and forward activations to strict FP32 and validates every model leaf. The current PyTorch inference converter also requires FP32. Historical FP16 defaults and artifacts remain documented in [[pokemon_tcg_torch_inference]].

The exact scratch-register count is configuration-dependent: the dataclass default is 16, while the session JSON and some ablation runs use 32. A checkpoint's `arch_config` and resolved run manifest are authoritative for that run.

## Config hierarchy

```text
CLI args  >  --config file  >  configs/train_config.json  >  hardcoded defaults
```

Any CLI flag left as `None` falls back to the JSON config, which falls back to the dataclass default. All fields on `TrainConfig` are overridable via CLI. When both a CLI flag and a config JSON provide a value, CLI wins.

The default JSON at `configs/train_config.json` is a **session** file — it captures the last-used training strategy, not the recommended defaults. The recommended defaults are in the dataclass `TrainConfig` at `rl/train_config.py`. Ablations override strategy values from the CLI.

## Model architecture

| flag / field | type | default | description |
|---|---|---|---|
| `--d-model / d_model` | int | 128 | Transformer hidden dim. `enum [64, 128, 256, 512]`. 128 ≈ 1M params, 256 ≈ 3.5M, 512 ≈ 20M. Current baseline: 128. |
| `--nhead / nhead` | int | 4 | Attention heads per layer; must divide `d_model`. |
| `--nlayers / nlayers` | int | 3 | Stacked encoder layers. Current session JSON runs 4. Range 1–12. |
| `--ff / ff_dim` | int | 512 | FFN width. Typically `4 × d_model`. |
| `--static / static` | bool | true | Enable static card features from `EN_Card_Data.csv` (learned linear projection). Table is a buffer, not trainable. |
| `--split-heads / split_heads` | bool | true | Dedicated `VALUE_TOK` + `SUBMIT_TOK` with separate heads. |
| `--structured / structured` | bool | false | Verb-conditioned action head with per-`OptionType` query and bias embeddings. Off in BC baseline. |
| `--scratch-registers / scratch_registers` | int | 16 (dc default) / 32 (session JSON) | Learned workspace tokens between state and options. Carry inter-decision memory when TBPTT is on. Range 0–64. See [[pokemon_tcg_agent_architecture]]. |
| `--value-atoms / value_atoms` | int | 51 | Categorical value head atom count. Only used when the model is instantiated with `value_categorical=True`. |
| `--value-vmax / value_vmax` | float | 1.0 | Support half-width for the categorical value head. |

## Training loop

| flag / field | type | default | description |
|---|---|---|---|
| `--epochs / epochs` | int | 10 | Epochs for THIS invocation. When resuming, `run_end_epoch = start_epoch + epochs`. |
| `--batch / batch_size` | int | 128 (dc) / 1024 (session) | Row budget per temporal batch under TBPTT; classic batch size otherwise. |
| `--accum-steps / accum_steps` | int | 1 | Gradient accumulation microbatches per optimizer step. Effective batch = `batch_size × accum_steps`. FP32 accumulation, clip and update once. |
| `--lr / lr` | float | 5e-4 (dc) / 8.7e-5 (session) | Peak learning rate. |
| `--lr-schedule / lr_schedule` | choice | `cosine` | `cosine \| linear \| none`. |
| `--warmup-steps / warmup_steps` | int | 1500 (dc) / 150 (session) | Linear warmup from 0 → peak over N optimizer steps. Clamped to `max(total_steps/5, 1)`. |
| `--lr-min-ratio / lr_min_ratio` | float | 0.1 (dc) / 0.2 (session) | LR floor after decay = `lr × lr_min_ratio`. |
| `--max-grad-norm / max_grad_norm` | float | 1.0 | Post-accumulation clip. 0 disables. |
| `--seed / seed` | int | 0 (dc) / 13 (session) | Seeds numpy AND `mx.random`. Both must be seeded — an older bug where only numpy was seeded caused untracked variance across runs. |
| `--optimizer / optimizer` | choice | `muon_adamw` | Only value supported. Routes matrix params to Muon and everything else to AdamW. See [[pokemon_tcg_training_pipeline]]. |
| `--optimizer-state / optimizer_state` | choice | `reset` | `reset \| resume`. `resume` requires an identical optimizer contract in the checkpoint. |
| `--scheduler-state / scheduler_state` | choice | `reset` | `reset \| resume`. `resume` restores `scheduler_phase_step` and the checkpoint horizon. |
| `--scheduler-total-steps / scheduler_total_steps` | int | 0 | Explicit horizon override for the scheduler phase. `0` = use the current run's optimizer-step count. Resume ignores this and keeps the checkpoint horizon. |
| `--muon-momentum / muon_momentum` | float | 0.95 | Hidden-matrix Muon momentum. |
| `--muon-weight-decay / muon_weight_decay` | float | 0.01 | Hidden-matrix Muon decay. |
| `--adamw-betas / adamw_betas` | 2×float | `[0.9, 0.999]` | AdamW first- and second-moment coefficients. |
| `--adamw-eps / adamw_eps` | float | 1e-8 | AdamW epsilon. |
| `--adamw-weight-decay / adamw_weight_decay` | float | 0.01 | AdamW decoupled decay for the non-matrix leaves. |
| `structured_weight_decay` (config-only) | float | 0.1 | Higher decay applied specifically to `type_query` / `type_bias`. Only relevant when `--structured true`. |

## TBPTT and aux losses

| flag / field | type | default | description |
|---|---|---|---|
| `--tbptt-chunk / tbptt_chunk` | int | 0 (dc) / 64 (session) | Decisions per chunk. 0 disables. Typical values: 8/16/32. Requires `episode_id`/`side` metadata (always present in current Parquet). See [[pokemon_tcg_tbptt_training_contract]]. |
| `--aux-ko-weight / aux_ko_weight` | float | 0.5 | Weight of the `ko_head_aux` BCE loss added to CE. `0` disables. Target: `aux_ko` masked by `aux_valid`. |
| `--aux-prize-weight / aux_prize_weight` | float | 0.5 | Weight of the `prize_head_aux` MSE loss. Target: `aux_prize_delta`. |
| `--aux-terminal-weight / aux_terminal_weight` | float | 0.5 | Weight of the `terminal_head_aux` BCE loss. Target: `aux_terminal`. |
| `--aux-return-weight / aux_return_weight` | float | 1.0 (dc) / 0.5 (session) | Weight of the `return_head_aux` MSE loss. Target: `aux_return`. Bumped to 1.0 in the current ablation baseline. |

## Data selection (all CLI-only, no config fallback)

| flag | type | description |
|---|---|---|
| `--days` | str | Comma-separated ISO dates: `2026-07-30,2026-08-01`. |
| `--last-n-days` | int | N most recently registered days in the SQLite `datasets` catalog. |
| `--all-days` | flag | Every day in the catalog. |
| `--db` | path | Override the SQLite catalog path. Default `model/results.db`. |
| `--max-rows-per-day` | int | Cap rows PER DAY, rounded down to the nearest episode boundary. 0 = off. |
| `--top-elo` | int | Filter to episodes where BOTH `player_name` and `opponent_name` are in the day's top-N agents by `agent_elo_daily WHERE source='remote'`. 0 = off. See [[pokemon_tcg_top_elo_curriculum_filter]]. |
| `--max-rows / max_rows` | int | Global row cap across all resolved days. 0 = no cap. |
| `--val-frac / val_frac` | float | Fraction reserved for validation. Split falls on episode boundary (deterministic given seed). |
| `--val-batch-size / val_batch_size` | int | Max rows per validation temporal batch. |

## Would-KO / dataset contract

| flag / field | type | default | description |
|---|---|---|---|
| `--bc-would-ko / bc_would_ko` | bool | false (dc) / true (session) | Require or disable the would-KO contract on the dataset (`aux_valid` etc.). Startup fails if the dataset does not match. |
| `--bc-wk-nvar / bc_wk_nvar` | int | 10 | Would-KO variable-attack determinizations. Higher = more accurate, more expensive at build time. |
| `--zero-wouldko` | flag | off | Zero out the would-KO trio at load time (useful for ablating the signal). |
| `--dedup` | flag | off | Load the `opt_group` column and collapse equivalent options at training time. |
| `bc_workers` | int | 8 | Dataset builder parallelism (not used by trainer). |
| `bc_flush` | int | 200 | Episodes per builder shard (not used by trainer). |
| `bc_ep_timeout` | int | 60 | Per-episode build timeout in seconds. |
| `bc_both_sides` | bool | true | Encode both sides per episode. Present in the current corpus. |
| `bc_self_aliases` | list[str] | `["FitaLabs","Alef Oliveira"]` (dc) / +`["Fitalabs","aleffita"]` (session) | Names that mark a row as `is_self=1`. Provenance only; not a model feature. |

## Output and resume

| flag | type | default | description |
|---|---|---|---|
| `--out` | path | (required at run time) | Best-checkpoint output path. |
| `--resume` | path | none | Resume from this checkpoint pickle. Loads model weights and, when `--optimizer-state resume` / `--scheduler-state resume`, restores the optimizer and scheduler state too. |
| `--phase-id` | str | none | Stable orchestration phase identity stored in every checkpoint (used by `tcg-train-sequence`). |
| `--export-final` | path | none | Explicit copy of the best checkpoint to this path at end of run. Without this, no implicit `model/bc_model/` write happens. |
| `--checkpoint-every-epochs / checkpoint_every_epochs` | int | 1 | Save a numbered snapshot every N epochs. The final epoch is always saved regardless. A rolling `_latest.pkl` is written every epoch. |

## Trainer runtime

| flag / field | type | default | description |
|---|---|---|---|
| `--compile / compile` | bool | false | `mx.compile` the loss function. First call takes minutes; steady-state faster. Incompatible with dynamic clip fallbacks. |
| `--log-interval / log_interval` | int | 100 | Print training stats every N optimizer steps. `-1` disables the running printout (tensorboard remains live). |

## Deprecated / removed

- `--prefetch` / `prefetch` (field, schema, CLI flag) — **removed 2026-08-07**. The pyarrow Scanner reads ahead intrinsically, and cross-batch retention is owned by the hierarchical KV cache. See [[pokemon_tcg_kv_cache_hierarchical]].
- `--slab-rows` / `slab_rows` — **removed 2026-08-07**. The MLX trainer has no fixed-size mmapped slabs anymore; each pyarrow batch is its own I/O unit and cache retention is by row group. The concept still exists in the historical PyTorch trainer at `scripts/bc/bc_train.py`, which is not part of the current runtime.
- `model_dir` field — **removed 2026-08-07**. Nothing read it. Best-model export is explicit via `--export-final`.
- The positional `data` CLI argument — hidden with `argparse.SUPPRESS`. Use `--days` / `--last-n-days` / `--all-days` instead. Kept in argparse so old invocations show the SUPPRESSED help rather than a hard error.

## Non-trainer fields

Kept on `TrainConfig` because the dataset builder and `data_manager` consume them:

- `data_dir` — where day-partitioned Parquet files live. Default `data/bc_data`.
- `replay_zip_dir` — where downloaded Kaggle replay zips live. Default `data/bc_replay_zip`.
- `checkpoint_dir` — where the trainer writes checkpoints. Default `model/checkpoint`.
- `kaggle_competition` — competition slug for submission uploads.
- `kaggle_episodes_prefix` — dataset name prefix for daily episode datasets.
- `bc_workers`, `bc_flush`, `bc_ep_timeout`, `bc_would_ko`, `bc_wk_nvar`, `bc_both_sides`, `bc_self_aliases`, `max_episodes` — all consumed by the dataset builder.

## Current recommended baseline (BC curriculum ablation regime)

```bash
uv run tcg-train --days 2026-07-28,2026-07-29,2026-07-30,2026-07-31,2026-08-01 \
  --max-rows-per-day 30000 --epochs 10 \
  --batch 1024 --tbptt-chunk 16 --val-batch-size 1024 \
  --lr 2.46e-4 --warmup-steps 20 --aux-return-weight 1.0 \
  --out model/checkpoint/<tag>/<tag>.pkl \
  --checkpoint-every-epochs 999
```

Rationale for each non-default:

- `--max-rows-per-day 30000` — cap for ablation parity across scales. Not a training preference. See [[pokemon_tcg_bc_curriculum_ablation]] for why. Larger scaled-up training will raise or drop it.
- `--batch 1024` + `--tbptt-chunk 16` — TBPTT row budget and chunk size that fit comfortably in 24 GiB unified memory with the hierarchical cache.
- `--warmup-steps 20` — small run length: with ~150–1400 optimizer steps a longer warmup wastes the schedule.
- `--aux-return-weight 1.0` — restored from the smoke config after an earlier experiment lowered it to 0.5 (which appeared to weaken the terminal-return signal). Not tuned formally.
- `--checkpoint-every-epochs 999` — the rolling `_latest.pkl` is what resumes actually need; numbered per-epoch checkpoints just waste disk during ablations.

## Cross-references

- [[pokemon_tcg_glossary]] — meaning of `chunk`, `gstep`, `opt_step`, `warmup`, `source='remote'`
- [[pokemon_tcg_training_pipeline]] — how these fields flow through the trainer
- [[pokemon_tcg_kv_cache_hierarchical]] — why `--prefetch` and `--slab-rows` are gone
- [[pokemon_tcg_top_elo_curriculum_filter]] — semantics of `--top-elo`
- [[pokemon_tcg_tbptt_training_contract]] — how `--tbptt-chunk` interacts with `--batch` and `accum-steps`
- [[pokemon_tcg_agent_architecture]] — where `--d-model`/`--nlayers`/`--scratch-registers` land structurally
- [[pokemon_tcg_bc_curriculum_ablation]] — the ablation the baseline command above ran
