vanilla-small-20B-valmatched β dense baselines for the small 20B cell
12L/d=1024 GPT, sparsity_mode=none, trained on 20B tokens of FineWeb-Edu. Produces
both arms of the small-cell comparison in one run: the val-matched dense
baselines (early stops) and the token-matched dense baseline (full budget).
- Best val_loss: 2.8586 (iter 12200); final at 20B 2.8664
- 12,716 iters, 1,572,864 tok/iter β 20,000,538,624 tokens
- 2Γ NVIDIA H200 (143 GB), wall clock 8h32m (12:29 β 21:01 PDT, 2026-07-24)
- Wandb:
9iezcto0 - HF: markhenry/vanilla-small-20B-valmatched
Headline numbers
| Run | Val loss | Ξ vs dense@20B |
|---|---|---|
vanilla-small-20B (this, iter 12200) |
2.8586 | β |
vanilla-small-20B (this, final 20B) |
2.8664 | +0.008 (noise) |
cayley-small-3L-mlp_in-20B |
3.1330 | +0.274 (worse) |
cayley-small-2L-mlp_in-20B |
3.1584 | +0.300 (worse) |
At matched 20B tokens the dense model is 0.274β0.300 nats better. That is the alignment tax in loss terms at this scale, and it is the expected direction: the CayleySAE bottleneck buys disentanglement by spending capability.
Alignment tax (token efficiency)
Dense reaches each sibling's final val far inside the budget:
| Sibling | its val | dense first eval β€ | interpolated crossing | tax |
|---|---|---|---|---|
| 2L | 3.1584 | iter 1900 (3.1536) | ~iter 1840, 2.89B | ~6.9Γ |
| 3L | 3.1330 | iter 2300 (3.1162) | ~iter 2215, 3.48B | ~5.7Γ |
Both well inside the <15Γ target. Crossings are linearly interpolated between adjacent evals; eval spacing (100 iters = 157M tokens) caps resolution at ~Β±5%, so treat these as one significant figure. Quoting the saved-checkpoint iter instead would understate the tax (6.7Γ / 5.5Γ).
The save-at-val ladder, and what it taught us
Four rungs were requested: each sibling's final val plus a companion 0.01 nats below (3.1584 / 3.1484 / 3.1330 / 3.1230). All four fired inside 400 iters.
| rung | iter | tokens | val at trigger | overshoot |
|---|---|---|---|---|
| 3.1584 | 1900 | 2.99B | 3.1536 | 0.005 |
| 3.1484 | 2100 | 3.30B | 3.1354 | 0.013 |
| 3.1330 | 2300 | 3.62B | 3.1162 | 0.017 |
| 3.1230 | 2300 | 3.62B | 3.1162 | β |
The 3L pair collapsed into one model. 3.1330 and 3.1230 fired on the same
eval; weights are bit-identical (md5 4d7c75acd798 over the sorted state dict).
Only 3 distinct checkpoints exist, and only 3 were uploaded.
Root cause is eval spacing, not noise: dense descends ~0.017β0.020 nats per
100-iter window in this region, wider than the 0.01-nat companion gap. A
companion can only be distinct if the per-window descent is smaller than the
gap. For the large sibling, either drop companions or tighten
--eval-interval to ~25β50 in the crossing region.
The 2L pair did stay distinct, and earns its keep for a different reason than
designed. ckpt_val_3.1584.pt beats 2L by only 0.0048 nats β a statistical tie
at 4.9M eval tokens, so it does not support "dense was at least as good."
ckpt_val_3.1484.pt beats it by 0.0230 (~5Γ the noise floor) and does. The tight
rung is for the tax crossing; the companion is for capability parity.
Eval noise floor
The run-end sequence: 2.8603, 2.8676, 2.8665, 2.8652, 2.8641, 2.8664 β spread 0.0073 with no trend, so ~0.005β0.008 nats at 150 eval iters Γ bs 32 Γ 1024. In-crossing region the same wobble shows as 3.1354 β 3.1359 β 3.1162.
Note in-run eval is master-rank only (train.py:899), so eval tokens do not
scale with world size: 150 Γ 32 Γ 1024 = 4.9M, not the 47M the parent
script's header claimed (it multiplied by 8 ranks). Any threshold decision from
in-run evals inherits the 4.9M noise floor.
Training health
- Throughput 688β697k tok/s from iter 10 to the end; no drift, no warmup pathology (compile warmup cost only iter 0 at 77k). Exactly half the 4ΓH200 1,389k of report 37, i.e. clean linear scaling in world size.
- grad_norm fell to ~0.20 by iter 3400 and ~0.09 at the end; monotone, no spikes. Peak VRAM 27.6 GB / 143 GB per GPU β bs could go far higher, but was held at 32 to keep tok/iter identical to the recorded recipe.
- Loss monotone modulo the noise floor throughout.
Node-shape delta (2Γ H200, not 4Γ)
Recipe unchanged from train_vanilla_small_20B_valmatched_4xh200.sh. Only
--nproc_per_node 4β2. Because ga is the global micro-step count, tok/iter
stays 1,572,864 and the LR schedule occupies the same 12,716-iter space; each rank
runs 24 micro-steps instead of 12. Wall clock doubled, nothing else changed.
Script: mh/scripts/train_vanilla_small_20B_valmatched_2xh200.sh.
Two defects found in the existing scripts
1. The vanilla-small script family cannot launch. train_vanilla_small_20B.sh,
..._valmatched.sh, and ..._valmatched_4xh200.sh all pass --n-embd 1024
without --n-head. train.py applies mode-dependent arch defaults
(train.py:1131): cayley β n_head=8, else n_head=12. With
sparsity_mode=none that resolves to 12, and assert n_embd % n_head == 0 fails
instantly since 1024 % 12 β 0. The cayley-small siblings silently took the cayley
branch and got n_head=8, which is why only the vanilla side is broken. Fixed in
the 2xh200 script with an explicit --n-head 8 (verified 202.53M params, matching
the siblings exactly). The three parents remain un-runnable.
2. quick_eval silently skipped for the entire run β 6 attempts, all
import failed (No module named 'evals'); skipping. The script does cd deeptopk
then uv run, which resolves to deeptopk/.venv; the evals package is declared
by the root pyproject.toml and is only present in the root .venv. So no
pile/hellaswag/lambada during training. Harmless here (val_loss unaffected,
report 42 does these post-hoc) but it means the quick-eval columns are absent
from this wandb run.
Artifacts
Five checkpoints in one HF repo, all also at
sparse-nanogpt-private/out/vanilla-small-20B/ (md5-verified against /dev/shm):
| file | iter | tokens | val |
|---|---|---|---|
ckpt_val_3.1584.pt |
1900 | 2.99B | 3.1536 |
ckpt_val_3.1484.pt |
2100 | 3.30B | 3.1354 |
ckpt_val_3.1330.pt |
2300 | 3.62B | 3.1162 |
ckpt_final_20B.pt |
12716 | 20.001B | 2.8664 |
ckpt_best.pt |
12200 | 19.19B | 2.8586 |
Recommended use
- Interpretability comparison vs 2L β
ckpt_val_3.1484.pt(unambiguously better than the sibling, so no handicapped-baseline objection). - Interpretability comparison vs 3L β
ckpt_val_3.1330.pt. - Single comparator for both β
ckpt_val_3.1330.pt(val 3.1162 beats 2L by 0.042 and 3L by 0.017). - Equal-budget tax in nats β
ckpt_final_20B.pt. - Token-efficiency tax β interpolated crossings above, not the saved iters.
All val numbers above are in-run trigger values at 4.9M eval tokens. Re-evaluate with a large eval before quoting any as a val-matched number; the purpose of that pass is to confirm the dense β₯ cayley inequality still holds, not to find the closest match.
Data note
fineweb-edu-25B was re-tokenized on this node from sample-100BT
(24,975,629,888 train / 24,433,651 val tokens, 40 streams, ~6 min). Same subset
and distribution as the siblings, but a different stream count means a different
shardβtoken layout, so this is not a byte-identical data replay. Val comparisons
hold; exact-trajectory comparisons do not.