Sam Babichenko

Reading notes / NanoGPT speedrun

Learning-rate schedules and batch size

Schaipp et al., 2025; McCandlish et al., 2018

Context. The schedule and the batch size are both governed by gradient noise that does not vanish at the optimum. Under warmup-stable-decay (WSD, or trapezoid), which holds the rate and then decays it to zero over roughly the last fifth of the run (the cooldown), the loss plateaus and then drops sharply at the cooldown, which had been explained by nonconvex geometry. Schaipp et al. show that a convex last-iterate bound for SGD has that shape; McCandlish et al. derive from one noisy step the gradient noise scale, which predicts the largest useful batch. At a constant step the last iterate fluctuates around the optimum by an amount proportional to the step, and annealing the step at the end makes it behave like an average, which produces the sharp drop. A minibatch gradient carries signal ∣G∣2|G|^2 and noise tr⁡Σ/B\operatorname{tr}\Sigma/B. Below their ratio a bigger batch gives proportionally more progress per step, so it parallelizes at no cost in examples; above it extra examples are wasted. As ∣G∣|G| shrinks the useful batch grows.

Step 1

The convex last-iterate bound has the shape of a WSD loss curve. For SGD xt+1=xt−γηtgtx_{t+1}=x_t-\gamma\eta_tg_t on a convex ff, with base rate γ\gamma, schedule shape ηt\eta_t, D=∥x1−x⋆∥D=\|x_1-x_\star\| and E∥gt∥2≤Gt2\mathbb E\|g_t\|^2\le G_t^2, the bound of Defazio et al. reads E[f(xT)−f(x⋆)]≤D2+γ2∑t≤Tηt2Gt22γ∑t≤Tηt+γ2∑k=1T−1ηk∑t=k+1Tηt⋅∑t=kTηt2Gt2∑t=kTηt.\mathbb E[f(x_T)-f(x_\star)]\le\frac{D^2+\gamma^2\sum_{t\le T}\eta_t^2G_t^2}{2\gamma\sum_{t\le T}\eta_t}+\frac\gamma2\sum_{k=1}^{T-1}\frac{\eta_k}{\sum_{t=k+1}^T\eta_t}\cdot\frac{\sum_{t=k}^T\eta_t^2G_t^2}{\sum_{t=k}^T\eta_t}. The first term is the classical bound for the averaged iterate; the sum is the extra error from reporting the last one. The right side is T1/γ+γT2\mathcal T_1/\gamma+\gamma\mathcal T_2, so γ⋆=T1/T2\gamma^\star=\sqrt{\mathcal T_1/\mathcal T_2}; truncated at step tt, it is the curve compared with loss curves. With constant GG, a constant schedule gives DGT1+HT−1\frac{DG}{\sqrt T}\sqrt{1+H_{T-1}} (HnH_n harmonic), while WSD with cooldown fraction cc gives at most 2DG(2−c)Tκ+ln⁡2−cc\frac{2DG}{\sqrt{(2-c)T}}\sqrt{\kappa+\ln\frac{2-c}{c}} with κ\kappa bounded, so there is no ln⁡T\ln T term. At T=105T=10^5 (my computation, G=D=1G=D=1), T\sqrt T times the bound is 3.62 for constant, 2.16 for WSD with 20% cooldown, 2.06 for cosine and 2.00 for linear decay.

Step 2

An online-gradient inequality holds against any reference point fixed by xkx_k. Expand ∥xt+1−u∥2\|x_{t+1}-u\|^2, use convexity in the cross term, take conditional expectations and sum from kk to TT (with γ\gamma absorbed into η\eta): ∑t=kTηt E[f(xt)−f(u)]≤12E∥xk−u∥2+12∑t=kTηt2 E∥gt∥2.\sum_{t=k}^T\eta_t\,\mathbb E[f(x_t)-f(u)]\le\tfrac12\mathbb E\|x_k-u\|^2+\tfrac12\sum_{t=k}^T\eta_t^2\,\mathbb E\|g_t\|^2. This is Zinkevich's online-gradient inequality. The reference point uu may be random, as long as it is determined by xkx_k: the step-tt gradient is unbiased given the past, so conditioning still turns the cross term into f(xt)−f(u)f(x_t)-f(u). With u=x⋆u=x_\star and k=1k=1 it is the averaged-iterate bound, the first term of Step 1.

Step 3

The last iterate is the average plus one correction per suffix (the steps from kk to TT), each taking the suffix's first iterate as reference point. For any sequence qtq_t and positive weights wtw_t, a telescoping identity (Defazio et al.) gives exactly qT=∑t≤Twtqt∑t≤Twt+∑k=1T−1wk∑t>kwt⋅∑t≥kwt(qt−qk)∑t≥kwt.q_T=\frac{\sum_{t\le T}w_tq_t}{\sum_{t\le T}w_t}+\sum_{k=1}^{T-1}\frac{w_k}{\sum_{t>k}w_t}\cdot\frac{\sum_{t\ge k}w_t(q_t-q_k)}{\sum_{t\ge k}w_t}. Take qt=E[f(xt)−f(x⋆)]q_t=\mathbb E[f(x_t)-f(x_\star)] and w=ηw=\eta. The first piece is Step 2 at x⋆x_\star. Each correction ∑t≥kηtE[f(xt)−f(xk)]\sum_{t\ge k}\eta_t\mathbb E[f(x_t)-f(x_k)] is Step 2 with u=xku=x_k, where the distance term vanishes and only the suffix's noise 12∑t≥kηt2Gt2\frac12\sum_{t\ge k}\eta_t^2G_t^2 is left, divided by its total step. This completes the proof.

Step 4

The sharp drop at the cooldown comes from the last-iterate term. With a constant step the kk-th correction is G2/(T−k)G^2/(T-k): short suffixes take full steps and do no averaging, and the corrections sum to G2HT−1≈G2ln⁡TG^2H_{T-1}\approx G^2\ln T. Under WSD a suffix starting inside the cooldown has steps shrinking to zero and a correction of about 43cT\frac4{3cT}, a constant over all cTcT of them, and the stable-phase suffixes add at most a constant plus about ln⁡2−cc\ln\frac{2-c}{c}, a logarithm of cc alone. At fixed γ\gamma, during the stable phase the distance term falls like 1/t1/t while the last-iterate term grows like γG2ln⁡t\gamma G^2\ln t, so the bound flattens; once the step shrinks, the last-iterate term falls quickly. In my decomposition of the exact bound (T=400T=400, 20% cooldown), over the cooldown the last-iterate term falls from 0.081 to 0.042 while the other terms barely move. The drop needs gradients that do not vanish (with Gt∝t−1/2G_t\propto t^{-1/2} or t−1t^{-1} it disappears), and the worst-case lower bound has the same shape, so the drop is not an artifact of the bounding.

Step 5

Transferring a learning rate across schedules and horizons. With constant GG, γ⋆\gamma^\star is D/GD/G times a function of the schedule shape alone, so the unknown DD and GG cancel in ratios, and only the ratios transfer. The bound reproduces the 1/T1/\sqrt T scaling of the best rate and the factor of about 2 between cosine and WSD (0.92 T−0.490.92\,T^{-0.49} against 0.47 T−0.490.47\,T^{-0.49}) found in LLM sweeps. Retuned per cooldown fraction, longer cooldowns are better all the way to linear decay, since γ⋆\gamma^\star grows with the fraction and compensates for the smaller steps; at fixed γ\gamma the best fraction is interior. From 20% cooldown to linear decay it predicts γ⋆(1)≈e0.7γ⋆(0.2)\gamma^\star(1)\approx e^{0.7}\gamma^\star(0.2) (my check: e0.67e^{0.67}), which a 124M grid confirms. To extend a WSD run, keep the stable phase and lower the rate at the old cooldown start by ρ=0.525\rho=0.525 for twice the horizon or 0.375 for four times, about 0.01 better than continuing at the same rate.

Step 6

A second-order expansion of one noisy step gives the noise scale. Expand the loss after θ↦θ−ϵGest\theta\mapsto\theta-\epsilon G_{est} to second order and average over the batch, using E GestGest⊤=GG⊤+Σ/B\mathbb E\,G_{est}G_{est}^\top=GG^\top+\Sigma/B (GestG_{est} has mean GG and covariance Σ/B\Sigma/B): E L(θ−ϵGest)=L(θ)−ϵ∣G∣2+ϵ22(G⊤HG+tr⁡(HΣ)B).\mathbb E\,L(\theta-\epsilon G_{est})=L(\theta)-\epsilon|G|^2+\frac{\epsilon^2}2\Big(G^\top HG+\frac{\operatorname{tr}(H\Sigma)}B\Big). The estimate is unbiased, so the noise enters only through the curvature-weighted variance in the quadratic term, which lowers the safe step and with it the gain. Minimizing over ϵ\epsilon gives ϵopt(B)=ϵmax/(1+Bnoise/B)\epsilon_{opt}(B)=\epsilon_{max}/(1+\mathcal B_{noise}/B) and ΔLopt(B)=ΔLmax/(1+Bnoise/B)\Delta L_{opt}(B)=\Delta L_{max}/(1+\mathcal B_{noise}/B), with Bnoise=tr⁡(HΣ)/G⊤HG\mathcal B_{noise}=\operatorname{tr}(H\Sigma)/G^\top HG and the noiseless values ϵmax=∣G∣2/G⊤HG\epsilon_{max}=|G|^2/G^\top HG and ΔLmax=12∣G∣4/G⊤HG\Delta L_{max}=\frac12|G|^4/G^\top HG. Below Bnoise\mathcal B_{noise} the gain is linear in BB, so kk small steps match one kk times larger; above it the gain saturates, and linear learning-rate scaling breaks down. With H∝IH\propto I this becomes the simple noise scale Bsimple=tr⁡Σ/∣G∣2\mathcal B_{simple}=\operatorname{tr}\Sigma/|G|^2, the batch at which the gradient's noise equals its signal.

Step 7

Steps trade against examples on a hyperbola. A batch BB needs 1+B/B1+\mathcal B/B steps and B+BB+\mathcal B examples per unit of progress, so reaching a fixed loss in SS steps and EE examples obeys (SSmin−1)(EEmin−1)=1,Bcrit=EminSmin≈Bnoise,\Big(\frac S{S_{min}}-1\Big)\Big(\frac E{E_{min}}-1\Big)=1,\qquad\mathcal B_{crit}=\frac{E_{min}}{S_{min}}\approx\mathcal B_{noise}, and training at Bcrit\mathcal B_{crit} costs twice the minimum of each. If the noise scale varies along the run, the best schedule keeps B2/BB^2/\mathcal B at a constant rr, the number of examples one step is worth, so B(s)=r B(s)B(s)=\sqrt{r\,\mathcal B(s)}, and the square root is why adaptive batches gain little unless the noise scale varies a lot (about 4% predicted on SVHN). Across eight tasks, Bsimple\mathcal B_{simple} tracks the measured Bcrit\mathcal B_{crit} within a factor of about 20 while both span six orders of magnitude and grow during a run. It scales like B/ϵB/\epsilon (a run with a larger step per batch size fluctuates higher up the walls of the loss valley, where ∣G∣|G| is larger), so it must be read off a well-tuned run, and for Adam the tuned rate rises like BαB^\alpha, α∈[0.5,1]\alpha\in[0.5,1], then flattens. For Adam-trained OLMo, Merrill et al. (2025) find it underestimates the directly measured critical batch by orders of magnitude, though the shape (small, rising, then flat) survives.

Step 8
The simple noise scale can be estimated at no extra cost in data-parallel training. Since E∣Gest∣2=∣G∣2+tr⁡Σ/B\mathbb E|G_{est}|^2=|G|^2+\operatorname{tr}\Sigma/B, squared norms at two batch sizes, one GPU's gradient and the all-reduced one, give unbiased estimates of both pieces.
# b_small: one GPU's batch; b_big: the global batch (after all-reduce)
sq_small = (g_local ** 2).sum()    # in practice averaged over GPUs
sq_big = (g_global ** 2).sum()
G2 = (b_big * sq_big - b_small * sq_small) / (b_big - b_small)  # estimates |G|^2
S = (sq_small - sq_big) / (1 / b_small - 1 / b_big)             # estimates tr(Sigma)
ema_G2 = 0.99 * ema_G2 + 0.01 * G2
ema_S = 0.99 * ema_S + 0.01 * S
b_simple = ema_S / ema_G2
The ratio of unbiased estimates is biased and ∣G∣2|G|^2 comes from a near-cancellation, which is why both are smoothed before dividing.
Step 9

The speedrun's batch and schedule records. On fixed hardware, time is roughly a cost per token plus a cost per step; with rr the per-step cost in tokens, Step 7 makes time proportional to r(1+B/B)+(B+B)r(1+\mathcal B/B)+(B+\mathcal B), minimized at B⋆=r BnoiseB^\star=\sqrt{r\,\mathcal B_{noise}}, the paper's square-root rule with rr set by the hardware (my reading, not from any record). Three kinds of record fit it: smaller batches (record 21, 64K to 48K tokens per GPU, about 3.5 s; again at record 39), which help whenever the old batch sat above B⋆B^\star; a batch ramp as the noise scale grows (record 46, 65 steps and about 1.8 s, though that record also moved to a newer PyTorch nightly, so part may be the upgrade); and Adam stepping every other step for the embeddings and head (record 39), a larger batch for rows with their own noise scale. On the schedule side, record 2 adopted the trapezoid along with other changes (45 to 31.4 minutes, not separable), and later records lengthened the cooldown (0.45 of the run at record 26, 0.80 at record 92), as the bound predicts once the rate is retuned. The speedrun decays to a floor of 0.1 to 0.2 of peak (with a weight EMA over the last ~300 steps in ANVIL2), where the bound favors zero; averaging the tail is another way to cancel the last-iterate penalty, a reading the paper does not test.

Consequences. Neither theory covers the speedrun's optimizers (both are for SGD), the noise-scale derivation is local, greedy and quadratic, and only ratios and shapes transfer.

Terms

WSD / trapezoid
warmup, constant rate, then decay to zero over the final fraction cc of steps
last-iterate bound
averaged-iterate bound plus one noise-only correction per suffix of the run
T1/γ+γT2\mathcal T_1/\gamma+\gamma\mathcal T_2
shape of the bound; γ⋆=T1/T2\gamma^\star=\sqrt{\mathcal T_1/\mathcal T_2}, D/GD/G times a function of the schedule shape
constant vs WSD
1+HT−1\sqrt{1+H_{T-1}} factor against κ+ln⁡2−cc\sqrt{\kappa+\ln\frac{2-c}{c}}: the cooldown removes the ln⁡T\ln T
γ⋆(1)/γ⋆(c)\gamma^\star(1)/\gamma^\star(c)
schedule-only ratio, about e0.7e^{0.7} from 20% cooldown to linear decay
continuation factor ρ\rho
lower the rate by 0.525 (2x horizon) or 0.375 (4x) to reuse the stable phase
Bnoise\mathcal B_{noise}, Bsimple\mathcal B_{simple}
tr⁡(HΣ)/G⊤HG\operatorname{tr}(H\Sigma)/G^\top HG; with H∝IH\propto I, tr⁡Σ/∣G∣2\operatorname{tr}\Sigma/|G|^2, cheap to measure
Bcrit\mathcal B_{crit}
Emin/SminE_{min}/S_{min} on the hyperbola; twice the minimum steps and examples
r B\sqrt{r\,\mathcal B}
adaptive batch when one step is worth rr examples; with rr a per-step cost in tokens, the fastest batch
two-batch estimator
∣Gest∣2|G_{est}|^2 at the per-GPU and global batch gives unbiased ∣G∣2|G|^2 and tr⁡Σ\operatorname{tr}\Sigma

Quiz

  1. If the gradient-norm bounds shrink as Gt∝t−1G_t\propto t^{-1}, what happens to WSD's sudden drop in the bound?
  2. A WSD run tuned for T1T_1 steps is extended to 2T12T_1 by continuing at the same base rate. What does the bound say goes wrong?
  3. Doubling a batch that is already far above Bnoise\mathcal B_{noise} barely improves the best loss decrease per step. Why?
  4. A run measures Bsimple\mathcal B_{simple} with a learning rate four times smaller than optimal. What does the note's scaling of Bsimple\mathcal B_{simple} predict?

Sources