← All Posts
Deep Learning · OptimizersChapter 06 / 11

Adam: Two Memories and Bias Correction

Build Adam from its first and second moments, derive why bias correction divides by $1-\beta^t$, and see why AdamW decouples weight decay.

Adam combines Momentum (an exponential moving average of signed gradients $m_t$) with RMSprop (an exponential moving average of squared gradients $v_t$), plus a bias correction that removes the startup shrinkage caused by initializing both buffers at zero.

1. Two memories with different jobs

Starting from $m_0=0$ and $v_0=0$, each step $t$ updates two coordinate-wise exponential moving averages of the gradient $g_t$ (typically with $\beta_1=0.9$, $\beta_2=0.999$):

$$\begin{aligned}m_t&=\beta_1m_{t-1}+(1-\beta_1)g_t,\\v_t&=\beta_2v_{t-1}+(1-\beta_2)g_t^2.\end{aligned}$$

Here $m_t$ tracks signed direction (opposite signs cancel), while $v_t$ tracks squared magnitude (always nonnegative). Adam first divides each buffer by $1-\beta^t$ to correct for zero initialization, then takes a normalized step:

$$\begin{aligned}\widehat m_t&=\frac{m_t}{1-\beta_1^t},&\widehat v_t&=\frac{v_t}{1-\beta_2^t},\\w_t&=w_{t-1}-\eta\frac{\widehat m_t}{\sqrt{\widehat v_t}+\epsilon}.\end{aligned}$$

2. Why zero initialization needs bias correction

Unrolling $m_t$ from $m_0=0$ writes $m_t$ as a weighted sum of past gradients $g_1,\dots,g_t$:

$$\begin{aligned}m_1&=(1-\beta_1)g_1,\\m_2&=\beta_1(1-\beta_1)g_1+(1-\beta_1)g_2=(1-\beta_1)(\beta_1g_1+g_2),\\m_t&=(1-\beta_1)\sum_{k=1}^t\beta_1^{t-k}g_k.\end{aligned}$$

Why don’t the $g_k$ terms block the geometric series? In $\sum_{k=1}^t\beta_1^{t-k}g_k$, each $g_k$ varies from step to step, so we first take the expected value $\mathbb E[m_t]$ (assuming the gradients have stationary mean $\mathbb E[g_k]=\mu$, or equivalently a steady gradient $g_k=\mu$). Because $\mathbb E[g_k]=\mu$ is the same at every step $k$, $\mu$ factors out in front of the sum, leaving only the powers of $\beta_1$ inside the geometric series $1+\beta_1+\cdots+\beta_1^{t-1}=\frac{1-\beta_1^t}{1-\beta_1}$:

$$\begin{aligned}\mathbb E[m_t]&=(1-\beta_1)\sum_{k=1}^t\beta_1^{t-k}\underbrace{\mathbb E[g_k]}_{=\,\mu}=\mu\,(1-\beta_1)\underbrace{\sum_{k=1}^t\beta_1^{t-k}}_{=\,\frac{1-\beta_1^t}{1-\beta_1}}=\mu\,(1-\beta_1)\frac{1-\beta_1^t}{1-\beta_1}=(1-\beta_1^t)\mu.\end{aligned}$$

By the exact same step for $v_t=(1-\beta_2)\sum_{k=1}^t\beta_2^{t-k}g_k^2$ with $\mathbb E[g_k^2]=\sigma^2$, $\sigma^2$ factors out to give $\mathbb E[v_t]=(1-\beta_2^t)\sigma^2$. Because $m_0=0$ and $v_0=0$ contributed the missing $\beta_1^t$ and $\beta_2^t$ weight, both raw buffers are biased toward zero; dividing by $1-\beta_1^t$ and $1-\beta_2^t$ cancels that factor:

$$\mathbb E[\widehat m_t]=\frac{\mathbb E[m_t]}{1-\beta_1^t}=\mu,\qquad\mathbb E[\widehat v_t]=\frac{\mathbb E[v_t]}{1-\beta_2^t}=\sigma^2.$$

As $t\to\infty$, $\beta_1^t\to 0$ and $\beta_2^t\to 0$, so $1-\beta_1^t\to 1$ and $1-\beta_2^t\to 1$ and the bias correction naturally fades away.

3. What the ratio $\widehat m_t/\sqrt{\widehat v_t}$ actually measures

Since $\widehat m_t\approx\mathbb E[g]$ estimates the mean gradient (signal) and $\widehat v_t\approx\mathbb E[g^2]=(\mathbb E[g])^2+\operatorname{Var}(g)$ estimates the uncentered second moment, the Adam ratio acts as a coordinate-wise signal-to-noise normalizer ($\epsilon\approx 0$):

$$\frac{\widehat m_t}{\sqrt{\widehat v_t}}\approx\frac{\mathbb E[g]}{\sqrt{(\mathbb E[g])^2+\operatorname{Var}(g)}}.$$

4. Lab: inspect what the update actually divides

On the shared quadratic dataset, toggle bias correction on and off to compare the raw moments $(m_t,v_t)$ against the corrected values $(\widehat m_t,\widehat v_t)$ used in the step.

Adam with visible moments

Turn bias correction off to run an ablation, then compare the first displacement from the same starting point.

5. Why AdamW separates weight decay from $\ell_2$ regularization

Adding an $\ell_2$ penalty $\tfrac\lambda2\|w\|^2$ to the loss changes the gradient to $g_t^{\text{reg}}=g_t+\lambda w_{t-1}$. In standard Adam, that penalty enters $m_t$ and gets divided by $\sqrt{\widehat v_t}+\epsilon$, whereas AdamW applies weight decay directly to $w_{t-1}$ outside the adaptive ratio:

$$\begin{aligned}\text{Adam + }\ell_2:\quad w_t&\approx w_{t-1}-\underbrace{\left(\frac{\eta}{\sqrt{\widehat v_t}+\epsilon}\right)\lambda w_{t-1}}_{\text{adaptive multiplier scales }\ell_2\text{ decay}}-\left(\frac{\eta}{\sqrt{\widehat v_t}+\epsilon}\right)\widehat m_t,\\\text{AdamW}:\quad w_t&=w_{t-1}-\underbrace{\eta\lambda w_{t-1}}_{\text{uniform decay}}-\left(\frac{\eta}{\sqrt{\widehat v_t}+\epsilon}\right)\widehat m_t.\end{aligned}$$

Notice that the adaptive multiplier $\frac{\eta}{\sqrt{\widehat v_t}+\epsilon}$ behaves the exact same way as in Adagrad and RMSprop—it boosts sparse/tiny-gradient coordinates ($\sqrt{\widehat v_t}\ll 1$) and reduces dense/large-gradient coordinates ($\sqrt{\widehat v_t}\gg 1$). That is helpful for the task gradient $\widehat m_t$, but harmful when it also multiplies the $\ell_2$ penalty $\lambda w_{t-1}$:

AdamW fixes this by decoupling weight decay from $\sqrt{\widehat v_t}$: every weight decays uniformly by $\eta\lambda w_{t-1}$, while the adaptive multiplier $\frac{\eta}{\sqrt{\widehat v_t}+\epsilon}$ is used purely for the loss gradient $\widehat m_t$.

6. Implementation and state memory

# One AdamW step (moments use only the task gradient)
t += 1
m = beta1 * m + (1 - beta1) * grad
v = beta2 * v + (1 - beta2) * grad.square()
m_hat = m / (1 - beta1 ** t)
v_hat = v / (1 - beta2 ** t)
parameter *= 1 - lr * weight_decay
parameter -= lr * m_hat / (v_hat.sqrt() + eps)

optimizer = torch.optim.AdamW(
    model.parameters(), lr=3e-4, betas=(0.9, 0.999),
    eps=1e-8, weight_decay=0.01
)

Optimizer state memory: For every model parameter $w_i$, Adam/AdamW stores two state values—the first moment $m_{t,i}$ and second moment $v_{t,i}$—plus the step count $t$. Every weight tensor W of shape $(d_{\text{out}}, d_{\text{in}})$ requires two matching tensors m and v of shape $(d_{\text{out}}, d_{\text{in}})$, adding $+8\text{ bytes/parameter}$ in FP32 ($2\times$ the state memory of SGD with Momentum).

7. Questions worth answering

Why does Adam divide $m_t$ by $1-\beta_1^t$ and $v_t$ by $1-\beta_2^t$?

Because $m_0=0$ and $v_0=0$ contribute a missing weight of $\beta_1^t$ and $\beta_2^t$, biasing $\mathbb E[m_t]=(1-\beta_1^t)\mu$ and $\mathbb E[v_t]=(1-\beta_2^t)\sigma^2$ toward zero during early steps.

Why are $\ell_2$ regularization and weight decay identical for plain SGD, but different for Adam?

In plain SGD, $w_t=w_{t-1}-\eta(g_t+\lambda w_{t-1})=(1-\eta\lambda)w_{t-1}-\eta g_t$ because every coordinate shares the same scalar $\eta$. In Adam, dividing by the coordinate-dependent $\sqrt{\widehat v_t}+\epsilon$ scales the $\lambda w_{t-1}$ term differently for every weight.

Should resuming training from a checkpoint reset $t$ in $1-\beta^t$?

No. If $m_t$ and $v_t$ are restored from the checkpoint, $t$ must also be restored so the bias correction matches the buffers.

Sources

Kingma and Ba, Adam: A Method for Stochastic Optimization; Loshchilov and Hutter, Decoupled Weight Decay Regularization; PyTorch AdamW.