AdamW
Adam, with the one change that matters: weight decay applied to the parameter directly rather than smuggled in as an L2 term that the adaptive step then rescales.
Standing
Current standardPart of the default recipe. Present in most frontier models trained today, and the thing a new design departs from rather than argues for.
Decoupled weight decay is the default everywhere. The challengers are real but none has displaced it.
judged as of 2026-09 · what the labels mean
Theory
Adam keeps two exponential moving averages per parameter — of the gradient and of its square — and steps along the first divided by the root of the second.
Both are initialised at zero, so early estimates are biased toward it. Dividing by removes the bias exactly; with the correction is still a factor of two at step 700, which is why omitting it produces the famous need for a longer warmup.
The decoupling
Classic L2 adds to the gradient, so it enters and and is then divided by along with everything else. The effective decay becomes per-parameter and inversely proportional to gradient history. AdamW applies outside the adaptive step, as in eq. 2, so every parameter decays at the same rate.
The consequence for tuning is that and decouple too: under L2 they interact, and a learning-rate sweep silently sweeps the regularisation strength with it.
Implementation
import torch
from torch import Tensor
@torch.no_grad()
def adamw_step(
p: Tensor, grad: Tensor, m: Tensor, v: Tensor, step: int,
lr: float = 3e-4, betas: tuple[float, float] = (0.9, 0.95),
eps: float = 1e-8, weight_decay: float = 0.1,
) -> None:
b1, b2 = betas
m.mul_(b1).add_(grad, alpha=1 - b1)
v.mul_(b2).addcmul_(grad, grad, value=1 - b2)
m_hat = m / (1 - b1**step)
v_hat = v / (1 - b2**step)
# decay the weight itself, untouched by the adaptive scale
p.mul_(1 - lr * weight_decay)
p.addcdiv_(m_hat, v_hat.sqrt().add_(eps), value=-lr)rather than Adam’s default is near-universal for language models: the shorter window reacts faster to the variance spikes that precede a loss divergence. Keep the moments in fp32 whatever the compute dtype — in bf16 underflows to zero for small gradients, and the step then divides by .