Making small-scale tuning survive scale the drifting optimum · muP · the coordinate check · what the labs do
One run, no sweeps
Everything in this chapter was tuned by sweeping — fine when a run costs seconds.
A frontier model is trained once: thousands of accelerators, months. No grid search at that price.
Industry answer: tune something small, transfer up.
This section: when transfer fails, how muP repairs it, how to verify it, what labs actually do.
A family of widths
Fashion-MNIST MLP, 784 \to n \to n \to n \to 10, width n from 128 to 1,024. Only the two middle matrices are n \times n: that is where scale lives. Initialization pinned explicitly: variance 1/\text{fan-in}, biases zero — standard parametrization (SP).
class MLP(nn.Module):"""A three-hidden-layer ReLU network under standard parametrization."""def__init__(self, width):super().__init__()self.fc_in = nn.Linear(784, width)self.fc_h1 = nn.Linear(width, width)self.fc_h2 = nn.Linear(width, width)self.fc_out = nn.Linear(width, 10)for lin in (self.fc_in, self.fc_h1, self.fc_h2, self.fc_out): nn.init.normal_(lin.weight, std=lin.in_features **-0.5) nn.init.zeros_(lin.bias)def features(self, X): h = F.relu(self.fc_h1(F.relu(self.fc_in(X))))return F.relu(self.fc_h2(h))def forward(self, X):returnself.fc_out(self.features(X))def configure_adam(self, lr):return torch.optim.Adam(self.parameters(), lr)
400 Adam steps at batch 512; score = final loss on the whole training set.
The sweep: the optimum moves
Eight learning rates × four widths, 32 runs, about a minute:
widths = [128, 256, 512, 1024]lrs = [2** k for k inrange(-12, -4)]sp_loss = {w: [train_mlp(MLP, w, lr) for lr in lrs] for w in widths}for w in widths:print(f'width {w:4d}: best lr {min(zip(sp_loss[w], lrs))[1]:.1e}')d2l.plot(lrs, [sp_loss[w] for w in widths], 'learning rate','training loss', xscale='log', ylim=[0.25, 0.8], legend=[f'width {w}'for w in widths])
width 128: best lr 7.8e-03
width 256: best lr 2.0e-03
width 512: best lr 2.0e-03
width 1024: best lr 9.8e-04
Every width: a U. The minima do not line up — the best learning rate falls about 8× as width grows 8×.
Wider is never worse at its own optimum; the narrow model’s optimum sits on the wide model’s unstable branch.
The best learning rate is a property of the model size, not the task.
Why: one step scales with fan-in
Single-example gradient of a hidden matrix = outer product \mathbf{g} = \boldsymbol{\delta}\mathbf{h}^\top; Adam’s first update is a sign step, and signs of outer products factorize:
All n terms add coherently: double the width, double the kick, halve the stable \eta.
Per layer: input weights (fan-in 784, fixed) and biases don’t scale. One global \eta = a compromise between layers that scale differently.
muP: the rules (Adam, width multiplier m)
Yang & Hu et al., 2022 — Tensor Programs V
parameters
init
Adam LR
forward
input weights, biases
unchanged
\eta
unchanged
hidden matrices
\propto 1/\text{fan-in} (unchanged)
\eta/m
unchanged
output matrix
unchanged
\eta
logits \times 1/m
Derived from the infinite-width limit where activations stay O(1)and every layer keeps learning — the maximal update. Embeddings count as input-like; attention needs 1/d; SGD has its own column.
Two rules, one subclass
class MuMLP(MLP):"""The same network under muP, relative to a width-128 base."""def__init__(self, width, base_width=128):super().__init__(width)self.m = width / base_widthdef forward(self, X):# rule 1: scale the output matrix's logits; the bias is untouchedreturnself.fc_out(self.features(X) /self.m)def configure_adam(self, lr): hidden = [self.fc_h1.weight, self.fc_h2.weight] rest = [p for p inself.parameters()ifnotany(p is q for q in hidden)]return torch.optim.Adam([ # rule 2: hidden LR / m {'params': rest, 'lr': lr}, {'params': hidden, 'lr': lr /self.m}])
At the base width m=1: muP changes nothing about the model you tune.
The coordinate check
Mean |activation| per layer, one Adam step, widths 128 → 4,096. Correct scaling = flat curves. Under SP:
Fixed fan-in layer flat; the layers behind square matrices grow, compounding with depth; logits blow up ~100× — the first step already writes the instability.
All layer curves flat: no width-dependent update scale left.
Logits fall by design (the 1/m multiplier); they grow to O(1) by learning, not by size. Growth is the unambiguous failure — the check reads activation size, not feature learning.
Run this before trusting any muP integration — it catches missed multipliers, mislabeled layers, framework defaults sneaking back.
Learning-rate transfer
The payoff: the same sweep, under muP:
mup_loss = {w: [train_mlp(MuMLP, w, lr) for lr in lrs] for w in widths}for w in widths:print(f'width {w:4d}: best lr {min(zip(mup_loss[w], lrs))[1]:.1e}')d2l.plot(lrs, [mup_loss[w] for w in widths], 'learning rate','training loss', xscale='log', ylim=[0.25, 0.8], legend=[f'width {w}'for w in widths])
width 128: best lr 7.8e-03
width 256: best lr 7.8e-03
width 512: best lr 7.8e-03
width 1024: best lr 7.8e-03
The optimum stops sliding: scatter within a grid step or two across 8× width (SP: slid three). Reusing the base optimum at width 1,024: within a couple % of that width’s best loss (SP: misses by 15–20%).
Tune small, ship big: TP5 tuned GPT-3 6.7B from a 40M proxy at ~7% of pretraining cost — and beat the original.
Caveat: at a width you can sweep, retuned SP matches; muP buys the sweep you cannot afford.
muP \equiv this spectral condition (Yang–Simon–Bernstein, 2023): the per-layer LRs make Adam’s updates land at the right spectral scale.
muP = bookkeeping for an optimizer using the wrong norm; Muon’s shape-scaled updates build much of that control in (its RMS-match convention is not the \sqrt{n_{\text{out}}/n_{\text{in}}} scale).
What the big runs do
Cerebras: muP in production; family tuned from a ~40M proxy.
DeepSeek: don’t remove the drift — fit it. Power laws for best LR and batch vs compute, extrapolated to the target run.
Meta: “MetaP” per-layer LRs/init for Llama 4 (undisclosed).
Moonshot / Kimi K2: no parametrization; every Muon update scaled to an RMS matched empirically to AdamW’s. 15.5T tokens.
Live debate: weight decay, not muP, may drive transfer in long runs (Kosson et al., 2025). muP: one mechanism, not settled law.
Recap
Best LR drifts with width: one Adam step perturbs a layer \propto fan-in.
muP: init unchanged, hidden LR \eta/m, logits \times 1/m — optimum transfers from the base width.
Coordinate check: flat activation scales across width, in seconds. Run it.
Spectral view ties muP to Muon; labs mix parametrization, scaling-law fits, and matched update sizes. Shared goal: tune small, transfer big.