Add brains/sovereign-v3/train_notes.md
Browse files
brains/sovereign-v3/train_notes.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SOVEREIGN v3 — Training Doctrine (~272M dense)
|
| 2 |
+
|
| 3 |
+
Exact recipe for **2× T4 (16 GB)**, shared shard format
|
| 4 |
+
`rl/shards_v2/*.npz`. Memory ≈9.4 GB/GPU at bs 16 + fp16 (see
|
| 5 |
+
ARCHITECTURE.md §3). Everything is implemented by `model.py` +
|
| 6 |
+
`data_adapters.py`.
|
| 7 |
+
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
## Stage A — Return-conditioned filtered BC + dynamics (offline, ~2–3 epochs)
|
| 11 |
+
|
| 12 |
+
### A.1 Sample preparation (`ShardPrepper`)
|
| 13 |
+
|
| 14 |
+
1. **Returns / RTG**: per-episode `R_t = r_t + γR_{t+1}`, γ=0.995;
|
| 15 |
+
+2 survive / +5 win terminal bonuses; z-scored per shard. **rtg = R̄** is
|
| 16 |
+
the conditioning input; `rtg_next` shifts within the episode (last
|
| 17 |
+
frame repeats).
|
| 18 |
+
2. **Weights** `w = clip(exp(R̄/τ), 0.2, 8)` τ=1; ×4 survivor episodes;
|
| 19 |
+
×2 ±2-frame kill windows; mean-normalized. (AWR soft filtering.)
|
| 20 |
+
3. **Free-label targets**: `lab64`, `lab64_next` (majority-vote 64×64),
|
| 21 |
+
`threat`/`expand` intent mirrors, `gate_mask`/`gate_valid`,
|
| 22 |
+
`win_lab` (survived=1), `nums_next`.
|
| 23 |
+
4. **Arena filtering**: drop >85%-water episodes.
|
| 24 |
+
5. Frame stacks / sequence windows: opt-in (`stack=`, `build_sequences`).
|
| 25 |
+
|
| 26 |
+
### A.2 Loss (exact)
|
| 27 |
+
|
| 28 |
+
```
|
| 29 |
+
L = w · [ λk CE(kind; ls=0.05)
|
| 30 |
+
+ λc CE(cell_logits[·,kind], cell)
|
| 31 |
+
+ λp −logBeta(α,β; pct)·1[kind≠bank]
|
| 32 |
+
+ λv Huber(value, R̄)
|
| 33 |
+
+ λw CE(win_logits, win_lab; ls=0.05)
|
| 34 |
+
+ λg BCE(gate, gate_mask)·1[valid]
|
| 35 |
+
+ λs CE(seg64, lab64)
|
| 36 |
+
+ λd CE(next_seg, lab64_next) # dynamics
|
| 37 |
+
+ λt BCE(threat, threat) + λx BCE(expand, expand) # intent mirrors
|
| 38 |
+
+ λe smoothL1(econ, cat(nums_next, rtg_next))
|
| 39 |
+
− λent (H(kind)+H(cell)+H(pct)) ]
|
| 40 |
+
λ = (0.5, 1.0, 0.25, 0.1, 0.1, 0.5, 0.5, 0.4, 0.2, 0.2, 0.05, 0.003)
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
**Value option (IQL-expectile)**: for the value head only, replace Huber
|
| 44 |
+
with the τ=0.9 expectile loss `|τ−1[δ<0]|·δ²` — more robust to the weak
|
| 45 |
+
farmer buffer. Both are supported; ablation #6 decides.
|
| 46 |
+
|
| 47 |
+
### A.3 Optimizer & schedule
|
| 48 |
+
|
| 49 |
+
- AdamW lr **3e-4 → 3e-5** cosine, **300-step warmup** (cortex), wd 1e-4
|
| 50 |
+
(Linear only), grad clip 5.0, **AMP fp16**, bs **16/GPU × accum 2**.
|
| 51 |
+
- 2–3 epochs. Smoke test first: `python model.py` (prints the 272M
|
| 52 |
+
breakdown), `python data_adapters.py` (adapter shapes).
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## Stage B — RPO: return-conditioned PPO (online, in sim)
|
| 57 |
+
|
| 58 |
+
### B.1 Rollouts
|
| 59 |
+
|
| 60 |
+
- `model.act(..., rtg=rtg_actual)` per tick; store `logprob`, `value`,
|
| 61 |
+
`win_prob`. **8 envs/GPU × 256 ticks** on CPU workers (2×T4 ⇒ ~2048
|
| 62 |
+
samples/round).
|
| 63 |
+
- Memory mode (optional): carry `state` through each episode
|
| 64 |
+
(truncated-BPTT); the env wrapper sketch:
|
| 65 |
+
|
| 66 |
+
```python
|
| 67 |
+
# env wrapper sketch (mem mode)
|
| 68 |
+
s = None
|
| 69 |
+
for step in range(ep_len):
|
| 70 |
+
rgb_t, nums_t = env.obs()
|
| 71 |
+
a = model.act(rgb_t, nums_t, rtg=rtg_est, state=s)
|
| 72 |
+
s = a["state"] # (1, B, 768)
|
| 73 |
+
obs, r, done = env.step(a["kind"], a["cell"], a["pct"])
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
### B.2 RPO specifics
|
| 77 |
+
|
| 78 |
+
- **RTG-consistent advantages**: `A_t = R_t − V(s_t, R_t)` (value is
|
| 79 |
+
rtg-conditioned — that is the whole point; the critic never has to
|
| 80 |
+
guess the aspiration).
|
| 81 |
+
- GAE(γ=0.995, λ=0.95) over `A_t`; z-normalize; clip ε=0.2 on the full
|
| 82 |
+
action logprob.
|
| 83 |
+
- Value loss: 0.5×clipped Huber (double clip ±0.2 in return space) +
|
| 84 |
+
**win head CE on episode outcomes** + optional cell-value auxiliary
|
| 85 |
+
(Huber vs per-cell utility bootstrap — PPO-friendly spatial critic).
|
| 86 |
+
- 4 epochs/round, minibatch 64/GPU, AdamW lr 3e-4 + cosine tail, grad
|
| 87 |
+
clip 5.0, **AMP fp16 + fp32 master**.
|
| 88 |
+
- **KL-to-pretrain guard β=0.1** (frozen Stage-A prior); roll-back if mean
|
| 89 |
+
KL > 0.15 for 3 rounds (β→0.02). ICLR 2025 evidence: pretrain-init +
|
| 90 |
+
guard, no offline replay retention — do NOT mix shards into the PPO
|
| 91 |
+
batches.
|
| 92 |
+
|
| 93 |
+
### B.3 Aspiration at play time
|
| 94 |
+
|
| 95 |
+
Eval/deploy with `rtg = +1.5` z-units (above-survival ambition; tune over
|
| 96 |
+
{0.5, 1.0, 1.5, 2.0} — ablation #1). The BC stage never saw rtg>2σ, so
|
| 97 |
+
stay inside the trained range.
|
| 98 |
+
|
| 99 |
+
### B.4 Reward shaping (flag the sim edit)
|
| 100 |
+
|
| 101 |
+
`+0.004/tick alive, +0.4/kill, +2 survive timeout, +8 win`. Keep
|
| 102 |
+
intermediates small; the win is the judged objective.
|
| 103 |
+
|
| 104 |
+
### B.5 League-lite (fictitious self-play, cheap)
|
| 105 |
+
|
| 106 |
+
Keep the top-3 checkpoints by win-rate. Every 10 rounds, evaluate the
|
| 107 |
+
current policy against the pool; promote if it wins >55%. Once promoted,
|
| 108 |
+
the sim runs our policy vs 7 bots + 2 frozen past selves — the only
|
| 109 |
+
"league" a 2×T4 budget can afford, and it directly targets the weakness
|
| 110 |
+
of the last-survivor objective (myopic bot-exploit policies).
|
| 111 |
+
|
| 112 |
+
---
|
| 113 |
+
|
| 114 |
+
## Stage C — Evaluation + wake-sleep loop
|
| 115 |
+
|
| 116 |
+
- 100+ episodes vs **9 mixed bots**, fixed seeds; metric **win-rate +
|
| 117 |
+
mean rank**; save best on rank; report binomial CI.
|
| 118 |
+
- **Wake-sleep distill**: after PPO plateaus, distill the 272M policy into
|
| 119 |
+
a ~30M dense student (same heads, distilled heat/kind/pct/value with KL;
|
| 120 |
+
`return_all=True` provides all teacher targets). Use the student for
|
| 121 |
+
2× faster rollouts and richer league self-play data; the big teacher
|
| 122 |
+
trains from it. Repeat (cap 3 cycles).
|
| 123 |
+
|
| 124 |
+
## Curriculum
|
| 125 |
+
|
| 126 |
+
Epoch 1 of Stage A on early-game frames (tick_frac < 0.35) → full data →
|
| 127 |
+
×1.5 weight on tick_frac > 0.6 in the final pass.
|
| 128 |
+
|
| 129 |
+
## Ablations (run before believing anything)
|
| 130 |
+
|
| 131 |
+
| # | Question | Change | Resolves |
|
| 132 |
+
|---|---|---|---|
|
| 133 |
+
| 1 | Aspiration rtg at play time | {0.5, 1.0, 1.5, 2.0} | the RPO knob |
|
| 134 |
+
| 2 | Return conditioning itself | rtg≡0 (frozen) vs conditioned | Bet 1's real value |
|
| 135 |
+
| 3 | Intent mirrors | λt=λx=0 vs default | opponent modeling |
|
| 136 |
+
| 4 | Dynamics head | λd=0 vs default | world-model value |
|
| 137 |
+
| 5 | Memory | GRU off vs on (seq BPTT) | temporal credit |
|
| 138 |
+
| 6 | Value objective | Huber vs τ=0.9 expectile | robustness |
|
| 139 |
+
| 7 | Win head | on/off in Stage B loss | endgame behavior |
|
| 140 |
+
| 8 | League-lite | pool vs no pool | self-play payoff |
|
| 141 |
+
| 9 | Wake-sleep | 0 vs 3 distill cycles | scaling loop |
|
| 142 |
+
|
| 143 |
+
## Expected compute (2× T4, fp16)
|
| 144 |
+
|
| 145 |
+
- Stage A: ~8–14 h for the current GB-scale shard set (272M, bs 16/accum 2).
|
| 146 |
+
- Stage B: ~2–3 updates/min at minibatch 128 → one full experiment per
|
| 147 |
+
~1–2 days. That is the honest cost of the mandate; the sanctioned trims
|
| 148 |
+
(in order): `head_hidden` 3072→2048, `cortex_layers` 6→4, `enc_ch` top
|
| 149 |
+
832→640. Each keeps the total >200M.
|
| 150 |
+
|
| 151 |
+
## Risk register
|
| 152 |
+
|
| 153 |
+
| Risk | Mitigation |
|
| 154 |
+
|---|---|
|
| 155 |
+
| fp16 NaN in attention | fp32 master, grad clip 5, warmup; fall back fp32 for Stage A |
|
| 156 |
+
| Unlearning the BC prior | KL guard + roll-back (B.2); no offline replay retention |
|
| 157 |
+
| Aspiration OOD (rtg>2σ) | clamp rtg to ±2.5; ablation #1 |
|
| 158 |
+
| Threat/expand labels are imbalanced | BCE weights 8:1 positive:negative |
|
| 159 |
+
| GRU rollout complexity | default off; env sketch included |
|
| 160 |
+
| Activation memory at bs 16 | `grad_ckpt=True` halves the heavy stages |
|
| 161 |
+
| Color-identity prior (red=me) | sim-specific; documented |
|