deep learning · optimizers · adamw

Optimizer state: the memory bank of deep learning

Vanilla SGD forgets every step the instant it takes one, and it pays for that amnesia in oscillations. Modern optimizers keep records: a smoothed direction, a smoothed volatility, and a full-precision master copy of the weights. Here is what those records contain and why each one earns its bytes.

Training a neural network is an exercise in navigation. You are dropped into a massive, foggy landscape with millions of valleys and ridges, and your only goal is to find the lowest point, the absolute minimum loss. How you choose to take your steps matters just as much as where you are trying to go.

To appreciate why modern networks train successfully at all, we have to look closely at the mathematical memory that drives them: the optimizer state.


1Why vanilla SGD fails

In the early days of deep learning, models relied on vanilla stochastic gradient descent (SGD). It is a purely reactive algorithm. It looks at the slope right under its feet, takes a step down that slope, and immediately forgets everything else.

The formula is entirely memoryless:

θt+1 = θt − η · ∇t

Where θ represents the model weights, η is the learning rate, and ∇t is the gradient, the current slope.

The ravine problem

Imagine a landscape that looks like a long, narrow canyon. The floor slopes very gently toward the exit (the true minimum), but the walls of the canyon are incredibly steep. Drop a vanilla SGD optimizer into this canyon and here is what happens:

  1. It calculates the gradient. The slope pointing down the steep canyon walls is massive compared to the gentle slope leading toward the exit.
  2. SGD takes a massive step based on that steep slope. It violently hurtles across the canyon floor and smashes into the opposite wall.
  3. On the next step, it sees a massive slope pointing back the other way. It bounds across the floor again.

Because SGD has no memory of its past trajectory, it spends all its energy oscillating wildly between the canyon walls. It makes almost zero progress down the length of the canyon toward the exit. In complex modern AI models, this exact behavior causes training to stall out completely.1

fn 1The oscillation is not bad luck. A reactive rule always lets the steepest local direction dominate the step, even when that direction is the useless one.
Lesson

A memoryless optimizer pays for every oscillation twice: once in wasted motion across the canyon, once in progress never made along it.


2AdamW: the optimizer with history

To solve this lack of awareness, modern training relies on AdamW, Adam with decoupled weight decay. AdamW succeeds because it keeps records. For every single parameter in your model, the optimizer state tracks historical statistics and creates a custom, adaptive update using two historical metrics: the first momentum and the second momentum.2

fn 2β1 = 0.9 and β2 = 0.999 are the PyTorch defaults for AdamW. The mechanics hold for any choice in (0, 1); only the smoothing timescales change.
What the optimizer state holds per parameter (typical mixed-precision setup)
RecordJobPrecisionBytes
Weights θwhat gets updatedbf16 / fp162
First moment mtsmoothed directionfp324
Second moment vtsmoothed volatilityfp324
Master weight copyfull-precision target for tiny updatesfp324

3The mechanics of first and second momentum

The core of the optimizer state consists of two vectors that act as filters for the noisy gradients generated during training.

The first momentum (mt): the direction manager

The first momentum tracks the directional trend of the gradients over time. It functions exactly like physical momentum:

mt = β1 · mt−1 + (1 − β1) · ∇t

The second momentum (vt): the scale manager

The second momentum tracks the volatility, or the sheer variance, of the gradients for each parameter:

vt = β2 · vt−1 + (1 − β2) · (∇t)2

4Bias correction and the full update

Because mt and vt are initialized at zero, the early estimates are artificially pulled toward zero. Bias correction compensates:

m̂t = mt1 − β1t ,   v̂t = vt1 − β2t

As t grows large, β1t → 0 and β2t → 0, so the corrections fade and the estimates converge to their true values. In the early steps, however, they provide an essential rescaling that prevents the optimizer from taking negligibly small steps at the start of training.3

fn 3The figure assumes a constant unit gradient so the effect is isolated: nothing about the model changes, only how fast the running average catches up to reality.
1.0 0.5 0 500 1000 1500 2000 2500 TRAINING STEP t raw second moment v(t) bias-corrected sqrt(v-hat)
step t2500
raw v(t)0.92
corrected v̂(t)1.00
β₂0.999
bias correction restores full step size from step one; the raw estimate needs thousands of steps to catch up
Fig. 1. Constant unit gradient, AdamW default β₂ = 0.999. The dark curve is the raw running estimate v(t) = 1 − 0.999ᵣ, still below 0.92 after 2500 steps. The green line is the bias-corrected value, which equals 1 at every single step.

The full update rule

Combine the direction manager and the scale manager and you get the unified mathematical step that updates the weights:

θt+1 = θt − η · m̂t√v̂t + ε − ηλθt
opt = torch.optim.AdamW(model.parameters(), lr=1e-3,
                        betas=(0.9, 0.999), weight_decay=0.01)   # defaults
Lesson

An optimizer is a tiny database with an update policy. What it remembers (direction, volatility) decides how it moves; make sure you know which memories your training run is paying for.


5FP32 master weights in mixed precision

When scaling up large language models, storing everything in 32-bit floating-point numbers (FP32) consumes too much GPU memory. Mixed-precision training runs the active forward and backward passes in 16-bit brain floats (BF16) or half-precision floats (FP16).

This introduces a massive problem called underflow. Because 16-bit floats have limited precision, they cannot represent incredibly small numbers. During an update, the step size is often a tiny fraction. Subtracting a tiny 16-bit fraction from a 16-bit weight rounds that fraction down to absolute zero. The weight never changes, and the model stops learning.4

fn 4BF16 trades mantissa bits for range, FP16 keeps more mantissa but risks overflow. Either way, increments far below the representable spacing near the weight's magnitude vanish when added.

The solution: the FP32 master weight copy

To bypass underflow, the optimizer state holds a hidden, full-precision FP32 copy of all the model weights:

  1. The model does its fast calculations in 16-bit to save memory.
  2. The gradients are computed and passed to the optimizer.
  3. The optimizer calculates the first and second moments.
  4. The resulting tiny update step is applied directly to the FP32 master weights. Full 32-bit precision preserves the tiny numbers instead of rounding them to zero.
  5. The freshly updated FP32 weights are cast back down to 16-bit for the next round of training.
Lesson

Precision is a budget. Spend 16-bit where the math is bulk, keep 32-bit where tiny increments land. Without the FP32 master copy, low-precision training of modern architectures would be mathematically impossible.


6References

  1. torch.optim.AdamW documentation: defaults for lr, betas, and weight decay.
  2. Fixing Weight Decay Regularization in Adam (Loshchilov and Hutter, 2017): the decoupled decay argument behind AdamW.
  3. Adam: A Method for Stochastic Optimization (Kingma and Ba, 2014): original bias correction derivation.
  4. Mixed Precision Training (Micikevicius et al., 2017): FP16 compute with FP32 master weights.
  5. Local source: bin/blogs/optimizer-state.md, the original markdown note this page was rewritten from.