| --- |
| license: mit |
| datasets: |
| - HuggingFaceFW/fineweb-edu |
| language: |
| - en |
| tags: |
| - gpt |
| - baseline |
| - interpretability |
| - paritytransformer |
| --- |
| |
| # vanilla-small-20B β dense baselines (12L / d=1024, 202.53M) |
|
|
| Dense (`sparsity_mode=none`) counterparts to the `cayley-small-*-mlp_in-20B` |
| ParityTransformer siblings. **One 20B-token run, five checkpoints**: three |
| val-matched early stops plus the token-matched end of the run. |
|
|
| These are the dense comparators for the interpretability claim: if a |
| ParityTransformer is more interpretable than a dense model that is **at least as |
| good** on val, the comparison cannot be dismissed as a handicapped baseline. |
|
|
| ## Checkpoints |
|
|
| Files are named by **iter**. They were previously named by the `--save-at-val` |
| threshold that fired; those names misstated the actual loss, so they were renamed |
| (`ckpt_val_3.1584.pt` β `ckpt_val_1900.pt`, `3.1484` β `2100`, `3.1330` β `2300`). |
|
|
| | file | iter | tokens | **measured val_loss** | in-run trigger | |
| |---|---|---|---|---| |
| | `ckpt_val_1900.pt` | 1900 | 2.99B | **3.1477** | 3.1536 | |
| | `ckpt_val_2100.pt` | 2100 | 3.30B | **3.1285** | 3.1354 | |
| | `ckpt_val_2300.pt` | 2300 | 3.62B | **3.1116** | 3.1162 | |
| | `ckpt_best.pt` | 12200 | 19.19B | **2.8559** | 2.8586 | |
| | `ckpt_final_20B.pt` | 12716 | 20.001B | **2.8530** | 2.8664 | |
|
|
| Siblings, measured in the same pass on the same batches: |
|
|
| | reference | measured val_loss | published (in-run) | |
| |---|---|---| |
| | `cayley-small-2L-mlp_in-20B` | **3.1671** | 3.1584 | |
| | `cayley-small-3L-mlp_in-20B` | **3.1425** | 3.1330 | |
|
|
| ## Paired differences vs the siblings |
|
|
| Negative = dense is better. Every model saw byte-identical batches, so these are |
| exact paired comparisons, not error bars added in quadrature (paired SEM Β±0.0004). |
|
|
| | dense ckpt | vs `cayley-small-2L` (3.1671) | vs `cayley-small-3L` (3.1425) | |
| |---|---|---| |
| | `ckpt_val_1900` | **β0.0195** (45Ο better) | **+0.0052** (13Ο *worse*) | |
| | `ckpt_val_2100` | β0.0387 (86Ο better) | **β0.0140** (33Ο better) | |
| | `ckpt_val_2300` | β0.0556 (111Ο better) | β0.0309 (65Ο better) | |
| | `ckpt_final_20B` | β0.3141 (245Ο better) | β0.2894 (231Ο better) | |
|
|
| Note `ckpt_val_1900` clears 2L but **does not clear 3L**. Pick the checkpoint your |
| comparison requires from this table. |
|
|
| `ckpt_final_20B.pt` is also **better than `ckpt_best.pt`** by 0.0029 (110Ο) |
| despite the name: `ckpt_best` was selected by a noisy 4.9M-token in-run eval that |
| happened to dip at iter 12200. Prefer `ckpt_final_20B.pt` for everything; |
| `ckpt_best.pt` is kept only for provenance. |
| |
| ## How these numbers were measured |
| |
| `evals/language_modeling/eval_val_loss.py` over the **entire** FineWeb-Edu val |
| split β contiguous non-overlapping windows, bf16 autocast, batch 16 Γ 1024, |
| `fineweb-edu-25B`, seed 0. |
|
|
| | quantity | value | |
| |---|---| |
| | tokens per model | **25,161,728** (whole val split) | |
| | batches per model | 1,536, **byte-identical across all models** | |
| | absolute SEM (single model) | Β±0.0045 | |
| | paired SEM (difference of two models) | **Β±0.0004** | |
|
|
| The paired SEM is ~13Γ tighter because shared passage difficulty cancels. Quote |
| the paired differences. |
|
|
| Two reasons the run log disagrees with this table: |
|
|
| | source of disagreement | effect | |
| |---|---| |
| | In-run evals are master-rank only, `eval_iters Γ bs Γ seq_len` = **4.9M tokens** regardless of world size; adjacent evals wobble ~0.005 nats. | `--save-at-val` triggers inherit that noise. | |
| | `best_val_loss` inside a checkpoint is the **run's** best, not that checkpoint's val. | Meaningless to difference against a reference `ckpt.pt` saved at a different iter. | |
|
|
| Measured on this protocol, dense sits ~0.0025 nats *below* its in-run trigger, |
| while the cayley siblings sit ~0.011 *above* their published numbers. The |
| cayley siblings' published numbers are in-run values too, so **use the measured |
| column for any cross-model claim and do not mix the two** β a ~0.01 nat protocol |
| artifact otherwise lands inside the very gap being resolved. |
|
|
| One caveat we cannot yet separate: the cayley path must run bf16 through this |
| loader (it builds the backbone fp32 but forces the SAE to bf16 + triton), so its |
| offset could be protocol *or* genuine bf16 sensitivity in top-k selection. Dense |
| is bf16-insensitive here β its fp32 and autocast runs agree to 0.0002. |
|
|
| ## Alignment tax |
|
|
| **Equal budget (20B tokens both sides):** |
|
|
| | pair | dense | cayley | gap | |
| |---|---|---|---| |
| | vs 2L | 2.8530 | 3.1671 | **0.314 nats** | |
| | vs 3L | 2.8530 | 3.1425 | **0.289 nats** | |
|
|
| **Token efficiency** β where dense first reaches each sibling's measured val, |
| interpolated on the in-run eval curve shifted by the measured β0.0025 protocol |
| offset: |
|
|
| | sibling | its measured val | dense crossing | tokens | tax | |
| |---|---|---|---|---| |
| | 2L | 3.1671 | iter ~1683 | 2.65B | **~7.6Γ** | |
| | 3L | 3.1425 | iter ~2021 | 3.18B | **~6.3Γ** | |
|
|
| Both inside the <15Γ target. Eval spacing (100 iters = 157M tokens) caps |
| resolution, so treat as two significant figures. Note the large chassis gives |
| **~3.5Γ** on the same construction ([`markhenry/vanilla-large-20B`](https://huggingface.co/markhenry/vanilla-large-20B)) β |
| the ratio is not a constant across scale. |
|
|
| ### Corrections to earlier versions of this card / `report.md` |
|
|
| | claim (earlier) | corrected | |
| |---|---| |
| | iter 1900 beats 2L by 0.0048 β "a statistical tie" | beats by **0.0195 at 45Ο** β not a tie | |
| | iter 2300 beats 2L by 0.042, 3L by 0.017 | **0.0556** and **0.0309** | |
| | iter 2300 is the 3L match | **iter 2100** is the first to clear 3L | |
| | tax 6.9Γ (2L) / 5.7Γ (3L) | **7.6Γ** / **6.3Γ** | |
| | `ckpt_best` is the run's best model | `ckpt_final_20B` is better by 0.0029 (108Ο) | |
|
|
| All five corrections move the same way: measured consistently, the dense |
| baselines are **stronger** than the in-run numbers implied, so the alignment tax |
| is **larger**. That is the conservative direction for the interpretability claim. |
|
|
| ## Architecture and training |
|
|
| 12 layers, 8 heads, d=1024, block_size 1024, learned positional encoding, RMSNorm |
| (affine), no bias, vocab 50304, **202.53M params** β identical to the cayley-small |
| siblings, which add buffers but no `nn.Parameter`s. |
| |
| Muon + AdamW lockstep 1.2e-2 (min 1.5e-4), `linear_warmdown` wf=0.9, warmup 200, |
| `tokens/iter = 32 Γ 1024 Γ 48 = 1,572,864` β 12,716 iters. FineWeb-Edu |
| `sample-100BT`, GPT-2 tokenizer. 2Γ H200, 8h32m, ~695k tok/s. |
| Wandb [`9iezcto0`](https://wandb.ai/markhenrysoftware/sparse-nanogpt/runs/9iezcto0). |
|
|
| `--n-head 8` must be passed explicitly: `train.py` arch defaults are |
| mode-dependent, so `sparsity_mode=none` at d=1024 otherwise resolves `n_head=12` |
| and `1024 % 12 != 0` asserts. The cayley siblings hit the cayley default of 8. |
|
|
| Also in this repo: `report.md`, `eval_curve.csv` (full in-run eval trajectory), |
| `train.log`, and the training script. |
|
|
| ## Loading |
|
|
| ```python |
| import torch |
| ck = torch.load("ckpt_val_2100.pt", map_location="cpu", weights_only=False) |
| ck["iter_num"] # 2100 |
| cfg, sd = ck["model_config"], ck["model"] # GPTConfig fields, state dict |
| ``` |
|
|
| Checkpoints carry `optimizer_states`, so they are resumable, not inference-only. |
|
|
| ## Siblings |
|
|
| - [`markhenry/cayley-small-2L-mlp_in-20B`](https://huggingface.co/markhenry/cayley-small-2L-mlp_in-20B) β measured 3.1671 |
| - [`markhenry/cayley-small-3L-mlp_in-20B`](https://huggingface.co/markhenry/cayley-small-3L-mlp_in-20B) β measured 3.1425 |
| - [`markhenry/vanilla-large-20B`](https://huggingface.co/markhenry/vanilla-large-20B) β same construction, large chassis |
|
|
| --- |
|
|
| *Re-evaluation and card by Claude Code. Training run and `report.md` predate it; |
| where they disagree, the corrections table above states why.* |
|
|