tokenizer ablation: digit-split pre-tok = 0.2% -> 99.7% on TinyStories-1M; smallest arithmetic LLM now 3.75M
f117e6b verified |
Download STATUS.md from Atharva1232/tiny-math-1m: direct link, hf CLI and curl.
- Browser
- Download file 14.5 kB
-
https://huggingface.co/Atharva1232/tiny-math-1m/resolve/main/STATUS.md
- Command line
-
hf download hf://Atharva1232/tiny-math-1m/STATUS.md
-
curl -L -o STATUS.md https://huggingface.co/Atharva1232/tiny-math-1m/resolve/main/STATUS.md
14.5 kB
| # tiny-math β overnight run status log | |
| <!-- Autonomous agent: update this file at every decision point. --> | |
| ## Goal | |
| Find the smallest transformer that can do 4-digit addition. Sizes {1M, 5M, 8M}, | |
| from-scratch GPT, digit-level vocab (16 tokens), SFT on generated data, GRPO | |
| polish only if SFT plateaus. Headline number + phase diagram + honest model card. | |
| ## Key facts | |
| - Kaggle quota: 30h/week (checked at start: 0h used). Kernels run serially. | |
| - Baselines (local, n=5000, 4-digit l2r): bigram = 0.0%, untrained 1M = 0.0%. | |
| Floor is zero β any accuracy is real learning. | |
| - Local CPU speed: ~3100 tok/s (1M model). Kaggle T4 expected ~100-200x faster. | |
| - Data: train/eval contamination prevented BY CONSTRUCTION β both eval sets | |
| built upfront, stream rejects any (a,b) in either. (Bug found + fixed after | |
| local run crashed on the overlap assert; bakeoff v1 was deleted pre-run.) | |
| ## Formats (Phase 0b bakeoff picks winner) | |
| l2r = "47+68=\n115\n" | rtl = "47+68=\n511\n" (answer reversed) | | |
| cot = "47+68=\n7+8=15,4+6+1=11,115\n" (digit steps + answer, comma-sep) | |
| Paper (arXiv 2307.03381) predicts rtl and cot >> l2r for addition. | |
| ## Decision rules (pre-committed, no drift overnight) | |
| 1. Format winner = highest final acc at 1M/4-digit/10M tokens. Tie β prefer cot | |
| (most interesting), then rtl. | |
| 2. GRPO phase triggers ONLY if best SFT (8m, 50M tokens, 4-digit) < 90%. | |
| GRPO reuses the 150-line trainer pattern from grpo-gsm8k (shaped reward with | |
| format floor β zero-variance groups give zero advantage). | |
| 3. If bakeoff shows ALL formats ~0% at 1M/10M β raise 1M config to d=192 L=6 | |
| before sweeping (under-parameterized, don't burn sweep quota on a dead cell). | |
| 4. Extrapolation kernel runs after sweep; it mounts sweep outputs as a dataset. | |
| ## SWEEP RESULTS (n=5000, cot format, all 11 runs complete) | |
| 4-digit addition, exact-match on held-out problems: | |
| | model | 71k probs | 357k probs | 1.78M probs | | |
| |---|---|---|---| | |
| | 1M | 0.0% | 0.2% | **100.0%** | | |
| | 5M | 0.0% | 95.7% | 100.0% | | |
| | 8M | 0.0% | 98.8% | 100.0% | | |
| 2-digit ceiling: 1M and 8M both 100% at 1.78M probs. | |
| - Headline: **1M params does 4-digit addition perfectly** (0 errors / 5000). | |
| - Phase transition visible in the 1M row: 0.2% -> 100.0% between 357k and 1.78M probs. | |
| - Per rule 2: SFT hit 100% -> NO GRPO needed for 4-digit in-distribution. | |
| - Wall time: whole sweep β 50 min on one T4, ~0.7h quota. Quota check needed | |
| before extrapol push. | |
| ## MISSION UPDATE (user clarification) | |
| Part 1 (from-scratch 1M calculator) = good reference, KEEP AND SHIP IT, but | |
| the REAL target: **the smallest real LLM (pretrained, knows language) that is | |
| good at arithmetic.** Plan: | |
| - Stage A: zero-shot+1-shot quiz of real LLMs {TinyStories-1M/3M/33M, | |
| pythia-14m/70m, SmolLM2-135M} on the same 4-digit task β kernel READY | |
| (kernels/llmquiz/script.py, needs internet=true to pull HF weights). | |
| - Stage B: SFT ladder β fine-tune the promising ones on the same arithmetic | |
| lessons, find smallest to clear >=95%. | |
| - Stage C: verdict number + language-retention probe (does it still speak | |
| English after arithmetic training?) + final report. | |
| Open question worth measuring: BPE tokenizers split numbers badly, so a | |
| 14M LLM might LOSE to our 1M calculator β that contrast IS the story. | |
| - 01:0x extrapol v1 CANCELLED at 59min β MY push timeout (-t 3600) was the | |
| kill (cancel_acknowledged = Kaggle honoring the timeout). No outputs | |
| survived (jsonl written only at end). FIXES: (a) -t 7200 now, (b) incremental | |
| extrapolation.jsonl append after EVERY eval row β a kill can no longer eat | |
| results, (c) trunc_frac metric added β 5-6 digit cot needs >64 tokens, and | |
| truncation would otherwise masquerade as model failure, (d) make_eval_set | |
| attempts-guarded (1-digit space is only 100 pairs; unbounded while-loop | |
| would hang forever). | |
| - 01:3x extrapol v2 PUSHED + RUNNING, watcher armed (140min window). | |
| ## STAGE A RESULTS (zero/1-shot, n=2000, 1 worked cot example, complete) | |
| **All 6 real LLMs: strict 0.000, lenient 0.000.** From 3.7M to 134.5M params. | |
| Failure modes by family: | |
| - TinyStories (1M/3M/33M): ignore the task, TELL STORIES ("One day, a 3 | |
| year old boy was walking in the park...") | |
| - pythia-14m: attempts cot format, loops ("3+9=9,9+4=12,9+4=12,...") | |
| - pythia-70m: emits plausible cot chains with wrong digits, then repeats | |
| the worked example back | |
| - SmolLM2-135M: closest but loops / collapses to "100+100+100..." | |
| Reference: from-scratch 1M (trained) = 1.000. Zero-shot β trained; the LLM | |
| advantage (language) buys nothing on pure arithmetic below 135M. | |
| Saved: results/stage_a_results.jsonl. | |
| ## SHIPPED (part 1) | |
| - HF: Atharva1232/tiny-math-1m (model + card + all results + chart), public | |
| - GitHub: Its-Atharva-Gupta/tiny-math (public), pushed clean, token never | |
| touched git config (one-shot GIT_ASKPASS helper) | |
| - MODEL_CARD.md includes: 100% headline, phase-cliff finding, extrapolation | |
| 0%-everywhere table, bigram/untrained baselines at 0%, contamination-by- | |
| construction note, positional-pattern failure analysis with examples. | |
| ## STAGE B ROUND 1 (500k problems, complete) β NULL RESULT + DIAGNOSIS | |
| All LLMs ~0% after fine-tuning (TinyStories-1M: 0.001 strict = 2/2000 luck; | |
| others 0.000). But curves reveal THREE distinct bugs, not one failure: | |
| - pythia-14m/70m: loss NaN at step 1 (fp16 autocast instability) β models | |
| were dead on arrival, hence empty generations. NOT a real result. | |
| - TinyStories-1M/3M: loss fell properly (2.83->1.65, 2.30->1.05) β they | |
| learned the text but eval 0%: (a) BPE splits numbers inconsistently, no | |
| stable digit mapping; (b) my eval prompt had a worked example models never | |
| saw in training (format mismatch, my bug). | |
| - 33M/135M: OOM at batch 64 (50k-vocab logits vs 16GB) β never trained. | |
| - Also found: round-1 final eval was same-seed superset of tuning eval -> | |
| biased. Fixed in round 2 (independent seeds 7781/7782 + overlap assert). | |
| - Side finding: fine-tuning DESTROYS TinyStories language ("Once upon a | |
| time" -> digit chains). Language-retention probe kept for round 2. | |
| ## STAGE B ROUND 2 (RUNNING, watcher proc_9c5edaeb2e6f, -t 18000) | |
| Pre-registered configs: | |
| - TinyStories-1M: 1.78M problems (escalation: smallest LLM candidate gets | |
| same budget our from-scratch 1M model got) | |
| - pythia-14m/70m: 500k, pure fp32 (NaN fix) | |
| - TinyStories-33M: 500k, batch 16 (OOM fix) | |
| - SmolLM2-135M: 500k, batch 8 (OOM fix) | |
| - All: eval prompt = "a+b=\n" matching training format exactly. | |
| NaN guard aborts a model at first non-finite loss. | |
| ## STAGE B ROUND 2 RESULTS (complete, 142min) β BREAKTHROUGH | |
| | LLM | params | problems | strict | | |
| |---|---|---|---| | |
| | **TinyStories-33M** | **68.5M** | 500k | **98.5% PASSES** | | |
| | SmolLM2-135M | 134.5M | 500k | 76.9% | | |
| | TinyStories-1M | 3.7M | 1.78M | 0.2% (loss 1.27->0.74, eval flat 0: BPE wall confirmed at the budget our from-scratch 1M hit 100%) | | |
| | pythia-14m | 14.1M | 500k | NaN @ step 449 even in fp32 β GPT-NeoX instability, not fp16 | | |
| | pythia-70m | 70.4M | 500k | NaN @ step 899 even in fp32 | | |
| - TS-33M wrong answers: gets the sum RIGHT then hallucinates more problems | |
| ("...15989\\n8984+8984=...") β strict parses first line => 98.5%. | |
| lenient parses last number => 0. Disclosed, not hidden. | |
| - Language: TS-33M lost English ("Once upon a time" -> digit chains); | |
| SmolLM2-135M KEPT it ("...a little girl named Lily..."). Capacity helps | |
| retention. TS-1M also lost English. | |
| - Headline so far: smallest passing real LLM = 68.5M (vs 1.07M from scratch). | |
| ## STAGE B ROUND 3 (RUNNING, watcher proc pending, -t 10800) | |
| Two candidates that never got a fair shot: | |
| - TinyStories-3M (8.3M): round-1 0% was under broken eval prompt; now | |
| round-1-surviving recipe + fixed eval, 500k problems. | |
| - pythia-14m (14.1M): anti-NaN recipe v3 β LR 2e-5, embedding LR x0.1 | |
| (embedding = 46% of its params, spike suspect), no wd on norms/biases, | |
| 10% warmup, NaN auto-retry ladder [5e-6]. | |
| ## ROUND 3 RESULTS (partial β kernel crashed after TS-3M) | |
| - TinyStories-3M (8.3M) @ 500k: **strict 0.000** β half-learned carry pattern | |
| but BPE garbage ("cartridge1", "8irs") swallows digits. Below the floor. | |
| - pythia-14m: NaN'd at 2e-5 AND at the 5e-6 retry, then MY retry handler | |
| crashed (off-by-one: NAN_RETRIES[attempt] with attempt==len). Handler bug | |
| fixed by design in round 4 (no retry ladder there). | |
| - Verdict math: candidates below 68.5M are now 3.7M (0.2%), 8.3M (0%), | |
| 14.1M (untrainable so far). pythia-14m is the LAST one that could beat | |
| the 68.5M headline -> round 4. | |
| ## ROUND 4 (RUNNING, watcher armed, -t 7200) | |
| Recipe v5 for pythia-14m: freeze embed_in+embed_out (91% of its params, | |
| the NaN suspect β 50277-vocab matrices under full-param AdamW), train only | |
| the ~1.2M transformer blocks at lr 1e-4, grad-norm logged pre-clipping. | |
| If this NaNs too -> documented stability blocker, verdict stands at 68.5M. | |
| ## ROUND 4 + LOCAL NaN AUTOPSY (diagnosis in progress) | |
| - Round 4 (frozen "embed*" only): NaN at STEP 2, grad_norm=nan. Log revealed | |
| my filter froze only embed_in (6.44M) β lm_head (also 6.44M) kept the name | |
| "lm_head", not caught by "embed" substring. | |
| - Local autopsy (nan_autopsy.py, pythia-14m on CPU, exact Kaggle recipe): | |
| forward clean (loss 2.42, max|logit| 23), embedding rows used by cot data | |
| fine (max|w| 0.135), step-1 gradients ALL FINITE (no NaN/inf, total norm | |
| 60.09, top grad 28) -> then ONE AdamW step turns 46/76 tensors NaN. | |
| Finite grads + finite clip + one optimizer step = NaN weights means an | |
| optimizer-internals bug (suspect: foreach implementation), not data/LR. | |
| - LR-vs-time pattern across 5 recipes: 1e-4βstep2, 6e-5 fp16βstep1, | |
| 6e-5 fp32βstep449, 2e-5βlater, 5e-6βlater. Consistent with slow weight | |
| drift into an overflow threshold, OR two separate mechanisms. | |
| - nan_bisect.py running locally: 4 one-step variants (foreach default / | |
| foreach=False / wd=0 / SGD) to name the culprit. If foreach=False is | |
| clean -> that's the Kaggle round-5 fix (one flag). | |
| - nan_bisect RESULT β CASE CLOSED: **param dtype = float16!** transformers | |
| 5.19 loads pythia-14m in its native fp16; Kaggle's transformers 4.x loaded | |
| fp32. fp16 + AdamW explodes because eps=1e-8 is UNREPRESENTABLE in fp16 | |
| (min normal 6e-5): underflowed second-moment rows divide by ~0 -> inf -> | |
| NaN weights in ONE step. All 4 bisect variants: AdamW (any wd/foreach) | |
| = 51 NaN tensors; SGD = clean (no division) β confirms mechanism exactly. | |
| This explains every instant NaN (rounds 1/3-retry/4 locally). The SLOW | |
| Kaggle NaNs (step 449 @ 6e-5 fp32) are a separate real instability β | |
| round 5 recipe addresses both: explicit fp32 + blocks-only + lr 3e-4. | |
| - Skill patched: rlvr-finetuning now carries the fp16/AdamW eps lesson and | |
| the clip-can't-save-NaN-grads lesson. | |
| - round5_preflight.py running locally: 10-step fp32 blocks-only test; if | |
| CLEAN -> Kaggle round 5 with same recipe at 500k problems. | |
| ## ROUND 5 RESULT (complete, 10min) β NaN SAGA ENDS, REAL LEARNING | |
| pythia-14m blocks-only fp32: **strict 0.642 best / 0.6255 final** @ 500k | |
| problems. Stable the whole run (grad_norm_max 56.7, zero NaN). Same | |
| hallucinate-more-problems failure as TS-33M (gets 8857+7132 right -> 15989 | |
| then invents more problems; lenient=0 because last number is hallucination). | |
| - Sub-68.5M ladder now: 3.7M=0.2%, 8.3M=0%, 14.1M=64.2% (blocks-only, | |
| 1.19M trainable), 68.5M=98.5% PASSES, 134.5M=76.9%. | |
| - Per pre-committed escalation rule (10%<=acc<95% -> 1.78M problems): | |
| ROUND 6 launched β pythia-14m @ 1.78M, same proven recipe. | |
| If >=95%: headline drops 68.5M -> 14.1M. Else: verdict closes at 68.5M. | |
| - SmolLM2-135M also in the escalation band (76.9%) but ABOVE the 68.5M | |
| headline β escalating it cannot change the "smallest" answer, skipped | |
| (quota discipline, reason recorded). | |
| ## TOKENIZER ABLATION (user request, RUNNING β kernel tiny-math-tokablation) | |
| One-variable experiment: TinyStories-1M, native BPE scored strict 0.002 @ | |
| 1.78M problems (control r2_ts1m_1780k). Treatment = SMALLEST tokenizer | |
| change: number runs split PER DIGIT (pre-tok `?\\p{N}+` -> `?\\d`), same | |
| 50,257 vocab, SAME embedding matrix, no new tokens, no resize. Everything | |
| else frozen: same checkpoint, same 1.78M problems, same fp16/lr 6e-5/ | |
| batch-32 recipe, same eval sets (tune 7781 / final 7782). | |
| - Local validation done: OLD chops `1842 -> ['18','42']`, `9920 -> ['99','20']` | |
| (inconsistent boundaries β the mechanism we suspected); NEW gives one | |
| token per digit; 500-string round-trip check passed; alphabet closed, | |
| no spaces (chunk-interaction assumption holds). | |
| - Compute: digit-split = 1.23x tokens/problem (36.9 -> 45.4), disclosed as | |
| a consequence of the treatment (same problems, ~1.23x steps). | |
| - PRE-REGISTERED PREDICTIONS: BPE-chunking was THE wall -> big jump | |
| (>>10%, plausibly 90%+; from-scratch 806K hit 100% on same data); | |
| tokenization was not it -> <5% (wall = pretraining/capacity). | |
| - Implementation: per-char id dict == patched-pretokenizer for this corpus | |
| (single-char chunk = BPE identity), asserted in-kernel at startup. | |
| ## Timeline | |
| - 23:0x Phase 0 done: train.py + baselines.py + unit tests all pass. Local | |
| 2-digit 2M-token run proved learning (loss 2.08β1.34, acc 0β~2% on l2r; | |
| crashed only at final-eval overlap assert β the bug since fixed). | |
| - 23:1x bakeoff v1+v2 FAILED: Kaggle runs kernels as notebooks; sibling import | |
| `from train import train` not resolvable (ModuleNotFoundError at ~10s). | |
| sys.path patch also failed. FIX: single-file merged kernels via make_kernel.py | |
| (train.py core + driver concatenated, syntax-gated). No more sibling imports. | |
| - 23:5x bakeoff v3 COMPLETE (single-file, ~3 min, 6 configs): | |
| 2-digit: cot 16.6% > l2r 15.0% > rtl 8.7% | 4-digit: cot 0.5%, others 0. | |
| FORMAT = cot (pre-committed rule 1). 1M@10M tok is below phase transition | |
| for 4-digit β model learns procedure format (see wrong-examples: valid digit | |
| step chains with wrong sums) but not yet the arithmetic. | |
| - 00:0x sweep PUSHED + RUNNING (11 configs: 3 sizes Γ {2M,10M,50M} @ 4d cot | |
| + 2 ceiling 2d @ 50M). Watcher proc_069900fd4eb2 armed. | |
| - Also fixed: extrapol CHECKPOINTS tags now match sweep tag scheme | |
| (sw_1m_4d_50000k etc, format not in tag); extrapol metadata kernel_sources | |
| (was dataset_sources β would have failed at push). | |
| - Next: sweep results β extrapol β decide GRPO (rule 2: only if best SFT <90%). | |
| ## Kernel IDs | |
| - tiny-math-bakeoff (DONE v3): format winner = cot | |
| - tiny-math-sweep (RUNNING): {1m,5m,8m} Γ {2M,10M,50M} tok @ 4d + 2d ceiling | |
| - tiny-math-extrapol (pending, rebuilt single-file): ckpt eval at 1..6 digits | |