Adia
Back to research
[ Reproduction · Power et al. 2022 ]

Grokking, reproduced

A network that has already scored 100% on the training set, and still cannot answer a question it hasn't seen. Then, thousands of steps later, it can.

Seven runs, one variable apart

Does it grok at the published setting?

memorized 81 · generalized 1.350 · 16.7× delay

Showing canonical: weight decay 1 · 30% of the data · init ×1 · seed 0. Memorized at step 81, generalized at step 1350, a 16.7 times delay.

0501001101001k10k40kchance 1/113memorized 81generalized 1350optimization steps (log)
traintest (held out)
memorized at
81
generalized at
1350
delay
16.7×
test floor
25.6× chance

Training hit 99% at step 81 and held there. Held-out accuracy stayed put for 16.7× longer before it moved. The shaded band is the gap — that gap is the whole phenomenon.

Test floor is measured only across steps where training accuracy is at or above 99%. The plotted curve dips below it before memorization, and during the brief training collapses the floor deliberately excludes.

The setup is small enough to hold in your head.

Every pair (a + b) mod 113 — 12,769 examples — written as four tokens, [a] [+] [b] [=], with the answer read off the last position. A one-layer decoder-only Transformer, 128-dimensional, four heads, written from scratch rather than imported. 30% of the pairs are used for training; the split is drawn once and frozen, so nothing leaks. Full-batch AdamW for 40,000 steps. Chance on a held-out question is 1 in 113 — 0.88%.

Memorizing and understanding are separate events.

Training accuracy reaches 99% at step 81. If you stopped there — which is what you do when your stopping rule is “training loss stopped improving” — you would file this as a textbook overfit and move on. Held-out accuracy doesn't reach 90% until step 1,350. That is 16.7× later, and in between the model is sitting on the answer without being able to state it. A second seed does the same thing on its own schedule: memorized at 82, generalized at 1,100, a 13.4× delay.

Weight decay isn't a tuning knob here. It's the cause.

This is the run worth staring at. Set weight decay to zero, change nothing else, and generalization never arrives — not late, not slowly, not at all inside 40,000 steps. Training is perfect from step 80 onward. Held-out accuracy ends at 36.0%. And the held-out loss climbs to 19.9, against 4.73 for a model that has learned nothing whatsoever — so it isn't merely failing to generalize, it's becoming catastrophically more confident about wrong answers, for 39,000 steps. The pressure toward smaller weights is what converts a memorized lookup table into the rule.

Less data, longer wait — and the trend is monotone.

Holding everything else fixed and varying only how much of the dataset the model may see: at 50% it generalizes essentially as it memorizes (1.1×, steps 79 → 87). At 30%, a 16.7× delay. At 20%, a 77.1× delay — memorized at step 70, generalized at 5,400. The scarcer the examples, the longer memorization remains the better bargain.

The 50% run is why my first grid found nothing.

My first attempt at this used 50% of the data on mod 97and reported a reproduction. It wasn't one. At that setting the model generalizes 8 steps after it memorizes, and held-out accuracy never drops below 81.9× chance while training holds — there is no plateau, so there is nothing to see and nothing to explain. I had matched the paper's modulus and its architecture but not its operation: Power et al.'s headline figure is division, which is not commutative and is much harder than the addition I was training. So I kept that run in this grid as frac0.5, deliberately. It reproduces the old null result exactly, which is what makes it a control rather than an embarrassment.

Bigger initial weights dig the hole deeper.

Scaling the initialization up by 8× produces the most extreme run in the grid: held-out accuracy falls to 0.2× chance — genuinely worse than guessing — while training accuracy is already perfect, and held-out loss peaks at 12.5against 4.73 for total ignorance. This is what a confidently-wrong memorizer looks like when you measure it instead of describing it. It still recovers to 100% by step 3,800. The direction matches Omnigrok's account, where the size of the weights you start with governs how long the detour through memorization takes.

What I had to fix before any of this was true.

The first grid logged every 100 steps, which put the first observation at step 100 — after memorization had already finished, so the event I was trying to time was invisible. It had no learning-rate warmup despite my own notes calling for one. It computed grokStepfrom the downsampled export rather than the full history, quantizing the headline number to the plot's resolution. And it applied weight decay uniformly, including to LayerNorm gains, which produced recurring collapses that I nearly wrote up as a finding about weight decay destroying the learned circuit. It wasn't: the run had simply been truncated mid-spike, and every earlier spike had recovered on its own. A measurement that is wrong in an interesting direction is the most expensive kind.

100% on the training set was never the same claim as “it learned the rule.”

[ Honest scope ]

This reproduces the phenomenon and both of its levers on modular addition; it is not a replication of the paper's figures, whose step counts are far larger and whose headline operation is division. One seed per cell except the canonical run, which has two — enough to show the effect isn't a single lucky seed, not enough to put error bars on any number here. Of the seven runs, five show a delay (11.2–77.1×), one is the 50% control that shows none (1.1×), and one never generalizes at all. That 11–77× band sits at the short end of the 10–1000× reported in the literature. Every figure on this page is read from grokkingRuns.json, which the repository regenerates end to end.