AdamC

Why gradient norms climb at the end of training, and the one-line change to weight decay that stops it.

In long language model runs, people log the size of the gradient as a health check. Near the end of training that number often turns around and climbs. In the run that motivated AdamC it more than doubled. This is strange, because the learning rate is shrinking toward zero at that point and the loss looks fine.

The explanation turns out to be bookkeeping, not instability. Three ordinary ingredients interact. They are normalization layers, weight decay, and a learning rate schedule. AdamW ties the strength of weight decay to the learning rate in a way that makes the weights shrink as the schedule decays, and shrinking weights make gradients look bigger.

AdamC is the fix proposed by Aaron Defazio in a 2025 arXiv note. It multiplies the weight decay coefficient by the same factor as the learning rate schedule. The rest of this page builds up exactly why that works, starting from things you already know.

Roadmap

  1. AdamW in one line, and where weight decay meets the learning rate.
  2. A normalized layer cannot see the length of its weights.
  3. Two consequences for the gradient. It is perpendicular to the weights, and it scales like one over their length.
  4. Perpendicular steps always make the weights longer.
  5. Weight decay pulls back, and the two forces settle at an equilibrium.
  6. A learning rate schedule moves that equilibrium. This is the rising gradient norm.
  7. The same story for Adam, where it is stronger.
  8. The fix. Scale weight decay with the schedule.
  9. A lab where you run AdamW and AdamC side by side.
  10. What you actually buy. The schedule means what it says.

Notation

SymbolMeaning
wt\w_tThe weights of one layer at step tt, flattened into a vector with dd entries. Blue everywhere on this page.
gt\g_tThe minibatch gradient of the loss with respect to wt\w_t. Crimson.
ηt\lr_t, ηmax⁡\lr_{\max}Learning rate at step tt, and its peak value over the whole schedule. Amber.
λ\wdWeight decay coefficient. Teal.
∥⋅∥\|\cdot\|Euclidean length of a vector. ⟨a,b⟩\langle a,b\rangle is the dot product.
utu_tAdam's normalized update direction (introduced in Step 1).

I follow the usual AdamW write-up and use η\lr for the learning rate and λ\wd for weight decay. Defazio's paper writes γ\gamma for the learning rate and xx for the weights. Same objects, different letters.

Step 1

AdamW, and where weight decay meets the learning rate

You already know Adam. It keeps a running average of the gradient mtm_t and of the squared gradient vtv_t, and it steps along their ratio. Call that ratio the update direction.

ut=m^tv^t+ϵu_t=\frac{\hat m_t}{\sqrt{\hat v_t}+\epsilon}

Here m^t\hat m_t and v^t\hat v_t are the bias-corrected averages and ϵ\epsilon is a tiny constant that avoids division by zero. AdamW then does two things to the weights in each step. It moves along utu_t, and it shrinks the weights slightly toward zero.

wt+1=wt−ηt ut−ηt λ wt\w_{t+1}=\w_t-\lr_t\,u_t-\lr_t\,\wd\,\w_t

Look at the last term. The shrink per step is ηtλ\lr_t\wd, the product of learning rate and weight decay. This is how the AdamW paper writes it and how torch.optim.AdamW implements it.

Tiny example

A typical recipe for a small language model uses peak η=0.003\lr=0.003 and λ=0.1\wd=0.1. At the peak, each step multiplies the decayed weights by 1−0.003×0.1=0.99971-0.003\times0.1=0.9997. Late in the schedule, when ηt=0.0003\lr_t=0.0003, each step multiplies them by 0.999970.99997. So the schedule turns down weight decay along with the learning rate, by the same factor.

That sounds harmless, even sensible. The next few steps show that "the same factor" is the wrong amount.

Takeaway. In AdamW the per-step shrink is ηtλ\lr_t\wd, so a learning rate schedule is silently also a weight decay schedule.
Step 2

A normalized layer cannot see the length of its weights

A normalization layer such as RMSNorm or LayerNorm rescales its input vector to a fixed size. Suppose a weight matrix WW produces h=Wxh=Wx, and the very next operation normalizes hh. If you double every entry of WW, then hh doubles, and the normalization divides the doubling right back out.

Tiny example

Take h=(3,4)h=(3,4). Its length is 55, so dividing by the length gives (0.6, 0.8)(0.6,\,0.8). Now double the weights so that h=(6,8)h=(6,8). Its length is 1010, and dividing gives (0.6, 0.8)(0.6,\,0.8) again. Everything downstream sees the identical vector, so the loss is identical.

This property has a name. A loss LL is scale invariant in w\w if stretching w\w by any positive factor cc leaves the loss unchanged.

L(c w)=L(w)for every c>0L(c\,\w)=L(\w)\quad\text{for every } c>0

Geometrically, the loss depends only on the direction of w\w. Picture rays coming out of the origin. The loss is constant along each ray and only changes when you rotate from one ray to another.

A caveat to carry with you. This is exact when a linear layer is immediately followed by normalization, as in a ResNet with BatchNorm. In a pre-norm transformer the matrices feed a residual stream that is normalized later, so the property holds only approximately. The paper handles this with a heuristic that I describe in Step 8.

Takeaway. For a normalized layer the loss depends on the direction of the weights and not on their length.
Step 3

Two consequences for the gradient

If moving along a ray never changes the loss, then the gradient has no reason to point along the ray. It must point sideways. And if you are far from the origin, you have to travel further sideways to rotate by the same angle, so the loss changes more slowly per unit of distance. Both facts drop out of one line of calculus.

Consequence A. The gradient is perpendicular to the weights.

Start from scale invariance and differentiate both sides with respect to the stretch factor cc.

ddcL(c w)=ddcL(w)\frac{d}{dc}L(c\,\w)=\frac{d}{dc}L(\w)
The right side does not contain cc, so its derivative is 00. The left side needs the chain rule. The input c wc\,\w changes at rate w\w as cc changes.
⟨∇L(c w), w⟩=0\big\langle \nabla L(c\,\w),\ \w\big\rangle=0
Now set c=1c=1 and write g=∇L(w)\g=\nabla L(\w).
⟨g,w⟩=0\langle \g,\w\rangle=0

Consequence B. Longer weights mean smaller gradients.

This time differentiate both sides of L(c w)=L(w)L(c\,\w)=L(\w) with respect to w\w, holding cc fixed.

c ∇L(c w)=∇L(w)c\,\nabla L(c\,\w)=\nabla L(\w)
Chain rule again. Each entry of the input is multiplied by cc, so a factor of cc comes out.
∇L(c w)=1c ∇L(w)\nabla L(c\,\w)=\frac{1}{c}\,\nabla L(\w)

Double the weights and the gradient halves. So the product ∥g∥⋅∥w∥\|\g\|\cdot\|\w\| stays the same along a ray. I will call that product κ\kappa. It measures how steep the loss is in direction space, with the arbitrary length of w\w divided out.

Tiny example

Use the toy loss L(w)=1−cos⁡(angle between w and (1,0))L(\w)=1-\cos(\text{angle between }\w\text{ and }(1,0)), which only depends on direction. At w=(3,4)\w=(3,4) its gradient is g=(−0.128, 0.096)\g=(-0.128,\ 0.096).

Check A. ⟨g,w⟩=−0.128×3+0.096×4=−0.384+0.384=0\langle\g,\w\rangle=-0.128\times3+0.096\times4=-0.384+0.384=0.

Check B. ∥g∥=0.16\|\g\|=0.16. At the doubled point (6,8)(6,8) the gradient is (−0.064, 0.048)(-0.064,\ 0.048) with length 0.080.08, exactly half. The product κ=∥g∥∥w∥\kappa=\|\g\|\|\w\| is 0.80.8 both times.

Try this. Drag the blue point outward along a ray and watch the crimson gradient arrow shrink while staying at a right angle. Then drag it around a circle and watch the gradient size change with direction but the right angle stay.
Real computation of the toy loss above. Grey rays are lines of equal loss. The dashed ray is the best direction. The gradient arrow is drawn 3 times its true length so it stays visible.
Takeaway. For a scale invariant layer, g⊥w\g\perp\w and ∥g∥=κ/∥w∥\|\g\|=\kappa/\|\w\|. The raw gradient norm is partly a statement about how long the weights are.
Step 4

Perpendicular steps always make the weights longer

Take one plain gradient step with no weight decay. You stand at w\w and move at a right angle to it. By Pythagoras, the new point is further from the origin than the old one. It does not matter which sideways direction you chose.

∥w−ηg∥2=∥w∥2−2η⟨g,w⟩+η2∥g∥2\|\w-\lr\g\|^2=\|\w\|^2-2\lr\langle\g,\w\rangle+\lr^2\|\g\|^2
This is just expanding the square ∥a−b∥2=∥a∥2−2⟨a,b⟩+∥b∥2\|a-b\|^2=\|a\|^2-2\langle a,b\rangle+\|b\|^2. By Consequence A the middle term is zero.
∥w−ηg∥2=∥w∥2+η2∥g∥2\|\w-\lr\g\|^2=\|\w\|^2+\lr^2\|\g\|^2
Tiny example

From w=(3,4)\w=(3,4) with g=(−0.128,0.096)\g=(-0.128,0.096), take a deliberately huge step η=10\lr=10 so the numbers are visible. The new point is (4.28, 3.04)(4.28,\ 3.04). Its squared length is 18.32+9.24=27.5618.32+9.24=27.56. The formula predicts 25+100×0.0256=27.5625+100\times0.0256=27.56. The length grew from 55 to 5.255.25.

So without any opposing force the weights of a normalized layer grow forever. That growth is not free. By Consequence B a longer w\w means a smaller gradient, and the same step now rotates w\w by a smaller angle. Van Laarhoven (2017) calls η/∥w∥2\lr/\|\w\|^2 the effective step size, and here it decays in a way nobody asked for.

Takeaway. Every gradient step on a normalized layer adds η2∥g∥2\lr^2\|\g\|^2 to the squared length of the weights.
Step 5

Weight decay pulls back, and the two settle at an equilibrium

Weight decay is the opposing force. Each step it multiplies the weights by a number slightly below one. Now there is a tug of war. Gradient steps push the length up, and decay pulls it down.

The new idea in this step is equilibrium, also called a steady state. Think of a bathtub with the tap running and the drain open. The drain lets out more water when the tub is fuller. So the level rises until outflow matches inflow, and then it stays there. If you start too full, it drains down to that same level. An equilibrium is a value that the process keeps returning to because the two opposing effects cancel exactly there.

Our tug of war has the same shape. Decay removes more length when the weights are long. Gradient steps add more length when the weights are short, because short weights have large gradients. So there is one length where the two cancel. To keep the algebra clean I use plain SGD with weight decay first and return to Adam in Step 7.

Derivation

wt+1=wt−ηgt−ηλwt=(1−ηλ) wt−ηgt\w_{t+1}=\w_t-\lr\g_t-\lr\wd\w_t=(1-\lr\wd)\,\w_t-\lr\g_t
The SGD update with decay, with the two wt\w_t terms grouped.
∥wt+1∥2=(1−ηλ)2∥wt∥2−2(1−ηλ)η⟨gt,wt⟩+η2∥gt∥2\|\w_{t+1}\|^2=(1-\lr\wd)^2\|\w_t\|^2-2(1-\lr\wd)\lr\langle\g_t,\w_t\rangle+\lr^2\|\g_t\|^2
Expand the square exactly as in Step 4.
∥wt+1∥2=(1−ηλ)2∥wt∥2+η2∥gt∥2\|\w_{t+1}\|^2=(1-\lr\wd)^2\|\w_t\|^2+\lr^2\|\g_t\|^2
The middle term vanishes because gt⊥wt\g_t\perp\w_t.

At equilibrium the length stops changing, so set ∥wt+1∥=∥wt∥=∥w∥\|\w_{t+1}\|=\|\w_t\|=\|\w\|.

∥w∥2=(1−2ηλ+η2λ2)∥w∥2+η2∥g∥2\|\w\|^2=(1-2\lr\wd+\lr^2\wd^2)\|\w\|^2+\lr^2\|\g\|^2
Expanded (1−ηλ)2(1-\lr\wd)^2.
(2ηλ−η2λ2) ∥w∥2=η2∥g∥2(2\lr\wd-\lr^2\wd^2)\,\|\w\|^2=\lr^2\|\g\|^2
Moved the ∥w∥2\|\w\|^2 terms to the left.
2ηλ ∥w∥2≈η2∥g∥22\lr\wd\,\|\w\|^2\approx\lr^2\|\g\|^2
Dropped η2λ2\lr^2\wd^2 because it is tiny next to 2ηλ2\lr\wd. With the example values from Step 1, 2ηλ=0.00062\lr\wd=0.0006 and η2λ2=0.00000009\lr^2\wd^2=0.00000009.
∥g∥∥w∥=2λη\frac{\|\g\|}{\|\w\|}=\sqrt{\frac{2\wd}{\lr}}
Divided both sides by η2∥w∥2\lr^2\|\w\|^2 and took the square root.

This is the central formula of the paper. At equilibrium the ratio of gradient length to weight length is pinned by the two hyperparameters and by nothing else. Since gt\g_t is a noisy minibatch gradient, read it as holding on average.

We can go one step further using Consequence B, ∥g∥=κ/∥w∥\|\g\|=\kappa/\|\w\|. Substituting gives each length separately.

∥w∥=(η κ22λ)1/4∥g∥=κ (2λη)1/4\|\w\|=\Big(\frac{\lr\,\kappa^2}{2\wd}\Big)^{1/4}\qquad\qquad \|\g\|=\sqrt{\kappa}\,\Big(\frac{2\wd}{\lr}\Big)^{1/4}

This second line needs an extra assumption, that κ\kappa stays roughly constant over time. That is reasonable in single-pass training where minibatch noise keeps the gradient from vanishing. It is the route taken by van Laarhoven in 2017. Defazio's main argument avoids it by only talking about the ratio.

Tiny example

Take η=0.1\lr=0.1, λ=0.05\wd=0.05, κ=8\kappa=8. Then ∥w∥4=0.1×64/0.1=64\|\w\|^4=0.1\times64/0.1=64, so ∥w∥=2.83\|\w\|=2.83 and ∥g∥=8/2.83=2.83\|\g\|=8/2.83=2.83. The ratio is 11, and the formula says 2×0.05/0.1=1\sqrt{2\times0.05/0.1}=1.

Pause and predict.

The plot below starts the same recurrence from two lengths, one far too short and one far too long. Before you look, where do you expect each curve to end up? And if you double λ\wd, does the final length go up or down, and by a lot or a little?

Try this. Move the sliders and check that both starting points always land on the dashed prediction. Notice how slowly the level responds to weight decay. That is the fourth root at work.
Real iteration of ∥wt+1∥2=(1−ηλ)2∥wt∥2+η2κ2/∥wt∥2\|\w_{t+1}\|^2=(1-\lr\wd)^2\|\w_t\|^2+\lr^2\kappa^2/\|\w_t\|^2 with κ=8\kappa=8. No noise and no neural network, just the recurrence.

One more fact from this picture matters later. Reaching equilibrium takes time, on the order of 1/(ηλ)1/(\lr\wd) steps. A smaller learning rate means a slower approach.

Takeaway. With a fixed learning rate, a normalized layer settles where ∥g∥/∥w∥=2λ/η\|\g\|/\|\w\|=\sqrt{2\wd/\lr}.
Step 6

A schedule moves the equilibrium

Everything so far used a constant learning rate. Real runs use a schedule, usually warmup and then a decay toward zero. Put ηt\lr_t into the formula and the equilibrium becomes a moving target.

target ratio at step t=2ληt\text{target ratio at step } t=\sqrt{\frac{2\wd}{\lr_t}}

The learning rate sits in the denominator. As the schedule decays, the target ratio rises, and it rises without bound if the schedule goes all the way to zero.

Tiny example

With λ=0.1\wd=0.1, a learning rate of 0.0030.003 gives a target of 66.7=8.2\sqrt{66.7}=8.2. A learning rate of 0.00030.0003 gives 667=25.8\sqrt{667}=25.8. Ten times less learning rate, about 3.23.2 times more ratio.

How does the ratio actually rise? From the two formulas at the end of Step 5, the weights shrink like ηt1/4\lr_t^{1/4} and the gradient grows like ηt−1/4\lr_t^{-1/4}. Mechanically, a lower learning rate makes the outward push η2∥g∥2\lr^2\|\g\|^2 collapse faster than the inward pull 2ηλ∥w∥22\lr\wd\|\w\|^2. Decay wins, the weights shrink, and by Consequence B shorter weights have bigger gradients.

That is the whole phenomenon. The gradient norm rises at the end of training because the schedule is dragging the equilibrium, and the weights are following it inward.

Pause and predict.

Suppose the learning rate has decayed to one sixteenth of its peak and the layer has kept up with its equilibrium. By what factor has the gradient norm grown under SGD with weight decay?

Reveal

The gradient scales like ηt−1/4\lr_t^{-1/4}, and 161/4=216^{1/4}=2. So it has doubled. Step 7 shows that for AdamW the exponent is 1/21/2, which gives a factor of 44.

Try this. Switch between cosine and linear decay. Look at how late the blow-up happens under cosine, and compare the three curves in the bottom panel.
Computed from the equilibrium formulas, assuming the layer tracks its target perfectly. Real training lags behind near the very end, as the next paragraph explains. The vertical axis is cut off at 10.

Two honest caveats. First, the equilibrium takes about 1/(ηtλ)1/(\lr_t\wd) steps to reach, and that time grows as the learning rate falls. So near the very end the layer cannot keep up, and the real ratio rises less than the target. Defazio's ResNet plot shows exactly this. The prediction tracks the measurement closely until the final stretch. Second, short runs may never reach equilibrium at all. The paper notes that a 10 billion token run was too short to show the uptick that a 200 billion token run showed clearly.

Takeaway. A decaying learning rate raises the target 2λ/ηt\sqrt{2\wd/\lr_t}, which shrinks the weights and inflates the gradient norm.
Step 7

The same story for Adam, where it is stronger

Adam changes one thing in the argument. Its step does not get smaller when the gradient gets smaller. Adam divides the gradient by its own running size, so each entry of the update utu_t is roughly +1+1 or −1-1. The paper calls this the sign-descent regime. With dd entries, ∥ut∥2≈d\|u_t\|^2\approx d no matter how long the weights are.

Rerun the tug of war with this constant-size push. I treat utu_t as uncorrelated with wt\w_t on average, which plays the role that exact perpendicularity played for SGD.

∥wt+1∥2≈(1−ηλ)2∥wt∥2+η2d\|\w_{t+1}\|^2\approx(1-\lr\wd)^2\|\w_t\|^2+\lr^2 d
Same expansion as Step 5, with ∥ut∥2≈d\|u_t\|^2\approx d in place of ∥gt∥2\|\g_t\|^2.
2ηλ ∥w∥2≈η2d2\lr\wd\,\|\w\|^2\approx\lr^2 d
Set the two lengths equal and dropped the tiny η2λ2\lr^2\wd^2 term, as before.
∥w∥≈η d2λ\|\w\|\approx\sqrt{\frac{\lr\,d}{2\wd}}

Under SGD the push weakened as the weights shrank, which softened the effect to a fourth root. Under Adam the push has fixed size, so the weights respond with a full square root. Then Consequence B gives the gradient.

∥g∥=κ∥w∥≈κ2λη d\|\g\|=\frac{\kappa}{\|\w\|}\approx\kappa\sqrt{\frac{2\wd}{\lr\,d}}
Tiny example

Take d=64d=64, η=0.01\lr=0.01, λ=0.5\wd=0.5. The predicted length is 0.01×64/1=0.8\sqrt{0.01\times64/1}=0.8. The lab in Step 9 uses exactly these values and measures about 0.780.78. If the learning rate falls by 44, the predicted length halves to 0.40.4 and the gradient norm doubles.

How this relates to the sources. The formula ηd/(2λ)\sqrt{\lr d/(2\wd)} matches the AdamW equilibrium norm in Kosson, Messmer and Jaggi's rotational equilibrium paper. Defazio states his Adam result differently. He shows the ratio 2λ/η\sqrt{2\wd/\lr} holds when both lengths are measured in norms reweighted by Adam's preconditioner, and then simplifies it to say the largest weight entry is about η/(2λ)\sqrt{\lr/(2\wd)}. That is the per-entry version of the formula above. He labels that simplification as non-rigorous, and the derivation ignores momentum. Adam's update is also not exactly perpendicular to the weights. So treat this step as a good approximation and not a theorem.

Takeaway. Under AdamW the equilibrium weight length scales like ηt\sqrt{\lr_t}, so the gradient norm scales like 1/ηt1/\sqrt{\lr_t}.
Step 8

The fix

The target ratio is 2λ/ηt\sqrt{2\wd/\lr_t}. It moves because the denominator moves and the numerator does not. So let the numerator move too. Replace the constant λ\wd with a scheduled value that follows the learning rate.

λt=λ⋅ηtηmax⁡\wd_t=\wd\cdot\frac{\lr_t}{\lr_{\max}}

Plug it into the target.

2λtηt=2ληt⋅ηtηmax⁡=2ληmax⁡\sqrt{\frac{2\wd_t}{\lr_t}}=\sqrt{\frac{2\wd}{\lr_t}\cdot\frac{\lr_t}{\lr_{\max}}}=\sqrt{\frac{2\wd}{\lr_{\max}}}
The ηt\lr_t cancels. The target is now a constant for the whole run.

Written as an update rule, AdamC changes only the decay term, and only for normalized layers.

wt+1=wt−ηt ut−ηt 2ηmax⁡ λ wt\w_{t+1}=\w_t-\lr_t\,u_t-\frac{\lr_t^{\,2}}{\lr_{\max}}\,\wd\,\w_t

Compare with Step 1. The per-step shrink was ηtλ\lr_t\wd. Now it is ηt2λ/ηmax⁡\lr_t^2\wd/\lr_{\max}. When the schedule halves the learning rate, AdamW halves the shrink and AdamC quarters it.

Tiny example

Same values as Step 1, ηmax⁡=0.003\lr_{\max}=0.003 and λ=0.1\wd=0.1. At the peak both methods shrink by 0.00030.0003 per step. When ηt=0.0003\lr_t=0.0003, AdamW shrinks by 0.000030.00003 and AdamC by 0.00032×0.1/0.003=0.0000030.0003^2\times0.1/0.003=0.000003. Ten times gentler, which is what it takes to stop the weights from collapsing inward.

Three design details

Why divide by the peak. Dividing by ηmax⁡\lr_{\max} makes AdamC identical to AdamW at the peak learning rate. So λ\wd keeps its old meaning and a value tuned for AdamW is a sensible starting point.

Why not make decay independent of the learning rate. You might guess the cure is to stop multiplying decay by ηt\lr_t at all. Redo Step 5 with a shrink of λ\wd per step and you get a ratio of 2λ/ηt\sqrt{2\wd}/\lr_t. That depends on the schedule even more strongly. The paper makes this point explicitly.

Which parameters. The argument only applies to scale invariant weights. Algorithm 1 in the paper applies the corrected decay to normalized layers and ordinary AdamW decay to everything else. For the transformer experiments the paper treats every linear layer as normalized except the output layer. That is a heuristic, since transformer layers are not literally followed by a norm.

In PyTorch you do not need a new optimizer class. AdamW already multiplies weight_decay by lr, so scheduling weight_decay with the same factor as the learning rate gives the AdamC update. This is my own sketch and not code from the paper.

# groups[0]: weight matrices treated as normalized
# groups[1]: output layer and anything else that gets plain AdamW decay
opt = torch.optim.AdamW(groups, lr=lr_max, weight_decay=wd)

for step in range(total_steps):
    f = schedule(step)                 # lr_t / lr_max, between 0 and 1
    for group in opt.param_groups:
        group["lr"] = lr_max * f
    opt.param_groups[0]["weight_decay"] = wd * f   # the AdamC correction
    ...
    opt.step()
Takeaway. AdamC sets λt=λ ηt/ηmax⁡\wd_t=\wd\,\lr_t/\lr_{\max} on normalized layers, which freezes the equilibrium at 2λ/ηmax⁡\sqrt{2\wd/\lr_{\max}} for the whole schedule.
Step 9

Lab. Run both and watch

This lab trains a real, tiny, scale invariant problem in your browser. The weight vector has d=64d=64 entries. The loss is half the squared distance between the normalized weights and a noisy target direction, so only the direction of w\w matters. Each step draws fresh noise, like a new minibatch, so the gradient never dies out. Both optimizers see identical noise. The error panel reports 1−cos⁡1-\cos of the angle between w\w and the true direction, so zero means perfectly aligned.

Pause and predict.

With a cosine schedule, sketch in your head the weight length and gradient norm for AdamW and for AdamC. Which one keeps a flat weight length? Then guess which one rotates the weights more per step in the middle of training.

Try this. Start with the defaults. Then switch the schedule to constant and confirm the two methods coincide. Then drag weight decay to its minimum and see the effect fade, because the layer no longer has time to reach equilibrium.
━ uncorrected   ━ corrected   ┄ predicted equilibrium
Real training run of 4000 steps, recomputed on every change. Curves are averaged in blocks of 20 steps. "Uncorrected" is AdamW or SGD with standard decay. "Corrected" is AdamC or SGDC. Adam uses β1=0.9\beta_1=0.9, β2=0.95\beta_2=0.95. This is a toy with one layer, so it shows the mechanism and says nothing about how large the benefit is in a real model.

With the defaults you should see the uncorrected gradient norm more than double while the corrected one stays flat. The uncorrected weight length follows its dashed prediction inward. The error panel usually ends a little lower for the corrected run, though that gap moves around when you draw new noise.

Takeaway. In a problem that satisfies the assumptions, the predicted equilibria match the simulation closely, and the correction flattens both the weight length and the gradient norm.
Step 10

What you actually buy. The schedule means what it says

A flat gradient norm plot is pleasant, but it is not the point. The point is in the "rotation per step" panel of the lab.

For a scale invariant layer, the only thing that changes the network's function is the direction of w\w. So the honest measure of learning speed is how far the direction rotates per step. For a step of size η∥u∥\lr\|u\| taken sideways from a point at distance ∥w∥\|\w\|, the rotation angle is about their ratio.

rotation per step≈ηt ∥ut∥∥wt∥\text{rotation per step}\approx\frac{\lr_t\,\|u_t\|}{\|\w_t\|}

Under AdamW, substitute ∥u∥≈d\|u\|\approx\sqrt d and the equilibrium length from Step 7.

ηtdηtd/(2λ)=2 ηt λ\frac{\lr_t\sqrt d}{\sqrt{\lr_t d/(2\wd)}}=\sqrt{2\,\lr_t\,\wd}
The d\sqrt d cancels and ηt/ηt=ηt\lr_t/\sqrt{\lr_t}=\sqrt{\lr_t}.

So under AdamW the rotation per step follows the square root of your schedule. If you ask the learning rate to fall by a factor of 100, the layer only slows down by a factor of 10. The shrinking weights quietly undo half of your schedule, in log terms.

Under AdamC the length is frozen at ηmax⁡d/(2λ)\sqrt{\lr_{\max}d/(2\wd)}. Substitute that instead.

ηtdηmax⁡d/(2λ)=ηt2ληmax⁡\frac{\lr_t\sqrt d}{\sqrt{\lr_{\max} d/(2\wd)}}=\lr_t\sqrt{\frac{2\wd}{\lr_{\max}}}

Now the rotation is proportional to ηt\lr_t. A hundredfold decay in the schedule is a hundredfold decay in learning speed.

Provenance. The AdamW expression 2ηλ\sqrt{2\lr\wd} appears in Kosson, Messmer and Jaggi's table of equilibrium quantities, with an extra factor for momentum that I have left out. The AdamC line is my own algebra using the same recipe. Defazio's note does not phrase its result in terms of rotation. It argues from the gradient to weight ratio and from the observed loss curves.

Is the square-root behaviour of AdamW actually bad? That is an empirical question and not something the derivation settles. One could tune a schedule under AdamW that already compensates for it. What AdamC offers is that the learning rate schedule you write down is the schedule the normalized layers experience.

Takeaway. Under AdamW the effective learning rate of a normalized layer decays like ηt\sqrt{\lr_t}. Under AdamC it decays like ηt\lr_t.

What the evidence shows, and its limits

Defazio's experiments. The language model run is a 120 million parameter model with the Llama 3 architecture, trained on FineWeb-Edu for 200 billion tokens with a cosine schedule, a batch of 256 thousand tokens and weight decay 0.05. The learning rate was swept from 0.001 to 0.02 for both optimizers. AdamC largely removes the gradient norm increase in the second half of training, keeps the weight norm far more stable, and reaches lower loss. For ResNet-50 on ImageNet with SGD, the corrected version removes the sharp rise at the end, but a slower upward drift across the whole run remains and the theory does not explain it.

Limits. This is a short single-author arXiv note with two test problems. The transformer application relies on the heuristic from Step 8. The Adam analysis is approximate. The paper itself says that many standard benchmarks do not show the uptick at all, because they lack normalized layers, are too short, or overfit.

Follow-up work. Jason Chou's "Correction of Decoupled Weight Decay" (arXiv, December 2025) revisits the question. From its abstract, it agrees that the per-step decay should scale like η2\lr^2 to keep weight norms stable, but it disputes the perpendicularity argument as the reason. It derives the same scaling from a different assumption, that updates become independent of the weights at steady state. I have only verified that paper at the level of its abstract.

Summary of the whole chain

  1. AdamW shrinks weights by ηtλ\lr_t\wd per step, so the schedule also schedules the decay.
  2. Normalization makes the loss blind to the length of the weights feeding it.
  3. That forces g⊥w\g\perp\w and ∥g∥=κ/∥w∥\|\g\|=\kappa/\|\w\|.
  4. Perpendicular steps lengthen w\w by Pythagoras.
  5. Decay shortens it. The two balance where ∥g∥/∥w∥=2λ/η\|\g\|/\|\w\|=\sqrt{2\wd/\lr}.
  6. A decaying ηt\lr_t raises that target. Weights shrink and the gradient norm climbs.
  7. Under Adam the weights scale like ηt\sqrt{\lr_t}, so the effect is stronger.
  8. Setting λt=λ ηt/ηmax⁡\wd_t=\wd\,\lr_t/\lr_{\max} cancels the schedule out of the target.
  9. The result is a constant weight length, a flat gradient norm, and an effective learning rate that follows the schedule you wrote.

Common misconceptions

Blind spots worth knowing about

Sources and further reading