Adam and Its Variants

Imagine training a deep network with varied parameters. Some — like the embedding row for a rare word — receive a gradient signal once every few thousand steps. Others — like a bias term in the first layer — are updated on every single batch. A single global learning rate is a bad compromise for both. This is the concrete problem Adam was built to solve. How do you pick a step size, automatically and per-parameter? This must work when the loss landscape has wildly different curvature and sparsity. That variation spans its millions of dimensions. Adam (Adaptive Moment Estimation) has become the default optimizer for a huge share of deep learning practice. It is used from fine-tuning transformers to training convolutional vision backbones. Its dominance is not an accident. It merges two ideas that each solved half the problem. These are Momentum and Nesterov Accelerated Gradient for smoothing noisy gradients, and AdaGrad and RMSProp for adapting the step size per coordinate. It also adds a bias-correction trick. This trick makes the whole thing well-behaved from step one. This article gives Adam a precise, from-scratch derivation. It covers what quantities it tracks, and why bias correction is mathematically necessary (not just a heuristic patch). It also shows how the update rule falls out of estimating the first and second moments of the gradient. It also covers the most consequential variant, AdamW. This variant fixes a subtle but important interaction. That interaction is between adaptive scaling and weight decay, which plain Adam gets wrong. By the end you will be able to write Adam's update loop from memory. You will be able to explain exactly why bias correction is needed at small tt. You will be able to diagnose the specific failure mode that motivated AdamW. You will also be able to reason about when Adam is the right choice. You will also know when it is not, compared to plain SGD with momentum.

Key takeaways
  • Adam maintains exponential moving averages of the gradient (first moment, "momentum") and the squared gradient (second moment, "adaptive scale").
  • Both moving averages are biased toward zero at early steps; Adam corrects this with an explicit division by (1−βt)(1-\beta^t).
  • The update divides a momentum-smoothed gradient by the square root of a smoothed second-moment estimate, giving each parameter its own effective learning rate.
  • Per-step cost is O(d)O(d) time and O(d)O(d) extra memory for dd parameters — two extra buffers, same asymptotic cost as SGD.
  • Plain Adam's L2L_2 penalty gets rescaled by the adaptive term, which is not true weight decay; AdamW decouples the two and fixes this.
  • Adam converges fast and robustly in practice but can generalize worse than tuned SGD+momentum on some vision tasks, motivating hybrid schedules and AdamW as the safer default.
The core insight

Adam is SGD with momentum whose step size for each parameter is automatically rescaled by a bias-corrected running estimate of that parameter's gradient variance.

The idea

Adam tracks two exponential moving averages (EMAs) of the gradient gt=∇θL(θt−1)g_t = \nabla_\theta L(\theta_{t-1}) at each step tt: the first moment mtm_t, an estimate of E[gt]\mathbb{E}[g_t], and the second raw moment vtv_t, an estimate of E[gt2]\mathbb{E}[g_t^2] (elementwise, per parameter). The first moment plays the role of classical momentum — it smooths out noise and lets consistent gradient directions accumulate speed. The second moment plays the role of AdaGrad/RMSProp scaling — it estimates how large and how noisy the gradient has typically been for each coordinate, so that parameters with large or erratic gradients get a smaller effective step and parameters with small, consistent gradients get a relatively larger one. The subtlety that distinguishes Adam from a naive combination of momentum and RMSProp is bias correction. Because mtm_t and vtv_t are initialized at zero and updated with a decay factor β1\beta_1 (for mm) and β2\beta_2 (for vv) close to 1, the raw EMAs are systematically biased toward zero, especially in the first few steps when there is little history to average over. Adam divides by (1−β1t)(1-\beta_1^t) and (1−β2t)(1-\beta_2^t) respectively to produce bias-corrected estimates m^t\hat m_t and v^t\hat v_t that are unbiased in expectation under a stationary-gradient assumption. Skipping this correction (as an early, naive combination of momentum + RMSProp effectively does) causes the first steps of training to take abnormally tiny steps, because vtv_t starts near zero and the ratio mt/vtm_t/\sqrt{v_t} behaves erratically. The final update rule divides the bias-corrected momentum by the square root of the bias-corrected second moment, plus a small constant ϵ\epsilon for numerical stability:

θt=θt−1−α⋅m^tv^t+ϵ.\theta_t = \theta_{t-1} - \alpha \cdot \frac{\hat m_t}{\sqrt{\hat v_t} + \epsilon}.

This single line encodes both ideas at once: the numerator is a momentum-smoothed gradient direction, and the denominator is a per-coordinate adaptive learning rate that shrinks steps in directions of historically large or noisy gradient magnitude.

How it works

Adam is a first-order, stochastic optimizer: at every step it only needs the current mini-batch gradient gtg_t, plus the two running-average buffers carried over from the previous step. There is no need to store or invert any curvature matrix — the "adaptive scaling" is purely diagonal and elementwise, which is what keeps it cheap enough for models with billions of parameters.

Step-by-step mechanism: 1. Initialize parameters θ0\theta_0, and set m0=0m_0 = 0, v0=0v_0 = 0, t=0t = 0. 2. At each step, sample a mini-batch and compute the gradient gt=∇θLt(θt−1)g_t = \nabla_\theta L_t(\theta_{t-1}). 3. Update the first moment (momentum): mt=β1mt−1+(1−β1)gtm_t = \beta_1 m_{t-1} + (1-\beta_1) g_t. 4. Update the second moment (uncentered variance): vt=β2vt−1+(1−β2)gt2v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2 (elementwise square). 5. Bias-correct both: m^t=mt/(1−β1t)\hat m_t = m_t / (1-\beta_1^t), v^t=vt/(1−β2t)\hat v_t = v_t / (1-\beta_2^t). 6. Update parameters: θt=θt−1−α m^t/(v^t+ϵ)\theta_t = \theta_{t-1} - \alpha\, \hat m_t / (\sqrt{\hat v_t} + \epsilon). 7. Repeat until convergence or a fixed number of steps. Typical defaults, from the original paper and confirmed as robust defaults across countless later architectures, are α=0.001\alpha = 0.001, β1=0.9\beta_1 = 0.9, β2=0.999\beta_2 = 0.999, ϵ=10−8\epsilon = 10^{-8}. ``` function ADAM(params θ, loss_fn, α=0.001, β1=0.9, β2=0.999, ε=1e-8, weight_decay λ=0): m ← zeros_like(θ) # first moment buffer v ← zeros_like(θ) # second moment buffer t ← 0 while not converged: t ← t + 1 batch ← sample_minibatch() g ← gradient(loss_fn, θ, batch) # ∇θ L(θ) on this batch if λ > 0 and variant == "Adam-L2": g ← g + λ θ # L2 penalty folded into gradient (plain Adam) m ← β1 m + (1 - β1) g # elementwise v ← β2 v + (1 - β2) (g ⊙ g) # elementwise square m_hat ← m / (1 - β1^t) # bias correction v_hat ← v / (1 - β2^t) if variant == "AdamW": θ ← θ - α ( m_hat / (sqrt(v_hat) + ε) + λ θ ) # decoupled decay else: θ ← θ - α m_hat / (sqrt(v_hat) + ε) # plain Adam step return θ ``` Every operation above — the moving averages, the elementwise square root, the division — is applied per parameter, independently. There is no cross-parameter coupling, which is exactly what makes the whole computation O(d)O(d) per step for dd parameters and trivially parallelizable across a parameter tensor on a GPU.

Figure
Adam's per-parameter update pipeline: gradient in, rescaled step out
g_t m_t = β1 m_{t-1} + (1-β1) g_t m̂_t = m_t / (1 - β1^t) v_t = β2 v_{t-1} + (1-β2) g_t² v̂_t = v_t / (1 - β2^t) θ_t = θ_{t-1} − α m̂_t/(√v̂_t+ε) momentum (direction) 2nd moment (scale)

Complexity & correctness

Time complexity. Each step performs a constant number of elementwise operations (multiply, add, square, square root, divide) over vectors of size dd, the total parameter count. This is O(d)O(d) per step, identical in order to plain SGD; the constant factor is larger (roughly 4–5x the elementwise arithmetic of vanilla SGD) but this is dwarfed in practice by the cost of the forward/backward pass through the network, which is typically Ω(d)\Omega(d) or much more for convolutional and attention layers. Over TT steps, total optimizer overhead is O(Td)O(Td).

Space complexity. Adam must store mm and vv alongside θ\theta, i.e. two extra copies of the parameter vector, giving O(d)O(d) extra memory — a real practical cost (3x the parameter memory instead of 1x for SGD, or 2x for SGD with momentum) that matters at billion-parameter scale, motivating memory-efficient variants such as Adafactor.

Why bias correction is exactly right, not just a heuristic. Assume the true gradient at each step is drawn i.i.d. (or at least stationarily) with E[gt]=g\mathbb{E}[g_t] = g. Unrolling the EMA recursion,

mt=(1−β1)∑i=1tβ1t−igi.m_t = (1-\beta_1)\sum_{i=1}^{t} \beta_1^{t-i} g_i .

Taking expectations under stationarity,

E[mt]=(1−β1)(∑i=1tβ1t−i)g=(1−β1t) g.\mathbb{E}[m_t] = (1-\beta_1)\Big(\sum_{i=1}^t \beta_1^{t-i}\Big) g = (1-\beta_1^t)\, g.

So E[mt]≠g\mathbb{E}[m_t] \ne g unless t→∞t \to \infty: the raw moving average systematically underestimates the true mean by a factor of (1−β1t)(1-\beta_1^t), which is severe when tt is small (e.g. at t=1t=1, β1=0.9\beta_1=0.9, the factor is only 0.10.1 — a 10x understatement). Dividing by (1−β1t)(1-\beta_1^t) exactly cancels this factor, giving E[m^t]=g\mathbb{E}[\hat m_t] = g: an unbiased estimator under the stationarity assumption. The identical argument applies to vtv_t and (1−β2t)(1-\beta_2^t). This is the precise mathematical justification for the correction — it is not an ad hoc rescaling but the unique factor that removes the geometric-series bias.

Why the adaptive denominator helps. v^t\sqrt{\hat v_t} estimates the root-mean-square magnitude of recent gradients for that coordinate. Dividing by it approximately normalizes the step so that every coordinate takes a step of comparable scale regardless of how large or small its raw gradients are — this is exactly the AdaGrad/RMSProp insight, inherited by Adam. Coordinates with sparse, rare, but informative gradients (e.g. an embedding row hit once every 10,000 steps) do not get drowned out by coordinates with dense, large gradients (e.g. shared bias terms); their smaller vv produces a relatively larger effective step size when they do fire.

Convergence. Adam's original convergence proof (in the online convex optimization setting) was later shown to contain a subtle flaw: a fixed β2\beta_2 can, in adversarial sequences, prevent the regret bound from holding, because vtv_t can decrease even as gradients grow, letting the effective learning rate increase at the wrong time. The fix used by the follow-up variant AMSGrad is to keep a running maximum of v^t\hat v_t so the denominator is monotonically non-decreasing, restoring a clean regret bound. In practice, plain Adam with standard hyperparameters converges reliably on essentially all standard deep learning benchmarks, and the pathological non-convergent sequences are rarely encountered; AMSGrad is used selectively rather than as a universal replacement.

Tradeoff, honestly stated. Adam's adaptivity buys robustness to learning-rate misspecification and fast early progress, especially valuable for the noisy, non-convex, highly heterogeneous-curvature landscape of deep networks. The cost is (a) 2x-3x optimizer memory versus SGD, and (b) empirically worse generalization than well-tuned SGD+momentum on some large-scale image classification tasks, an effect attributed partly to the L2L_2-vs-weight-decay coupling problem that AdamW addresses below.

Worked example

Trace two steps of Adam on a single scalar parameter, to make the bias-correction arithmetic concrete. Suppose θ0=1.0\theta_0 = 1.0, α=0.1\alpha = 0.1, β1=0.9\beta_1=0.9, β2=0.999\beta_2=0.999, ϵ=10−8\epsilon=10^{-8}, and the observed gradients are g1=2.0g_1 = 2.0, g2=1.8g_2 = 1.8 (consistently positive, as if the loss is θ2\theta^2-like locally).

Step t=1t=1:

quantityvalue
m1=0.9(0)+0.1(2.0)m_1 = 0.9(0) + 0.1(2.0)0.20.2
v1=0.999(0)+0.001(2.02)v_1 = 0.999(0) + 0.001(2.0^2)0.0040.004
m^1=0.2/(1−0.91)\hat m_1 = 0.2 / (1-0.9^1)0.2/0.1=2.00.2/0.1 = 2.0
v^1=0.004/(1−0.9991)\hat v_1 = 0.004 / (1-0.999^1)0.004/0.001=4.00.004/0.001 = 4.0
step =α m^1/(v^1+ϵ)= \alpha\, \hat m_1/(\sqrt{\hat v_1}+\epsilon)0.1×2.0/2.0=0.10.1 \times 2.0/2.0 = 0.1
θ1=1.0−0.1\theta_1 = 1.0 - 0.10.90.9

Notice the bias correction recovers exactly m^1=g1=2.0\hat m_1 = g_1 = 2.0 and v^1=g12=4.0\hat v_1 = g_1^2 = 4.0: with only one sample, the corrected estimate should simply equal that sample, and it does — a sanity check that the correction formula is exact at t=1t=1 (since ∑βt−i=1\sum \beta^{t-i} = 1 trivially when t=1t=1).

Step t=2t=2:

quantityvalue
m2=0.9(0.2)+0.1(1.8)m_2 = 0.9(0.2) + 0.1(1.8)0.18+0.18=0.360.18+0.18 = 0.36
v2=0.999(0.004)+0.001(1.82)v_2 = 0.999(0.004)+0.001(1.8^2)0.003996+0.003240=0.0072360.003996+0.003240=0.007236
m^2=0.36/(1−0.92)\hat m_2 = 0.36/(1-0.9^2)0.36/0.19≈1.89470.36/0.19 \approx 1.8947
v^2=0.007236/(1−0.9992)\hat v_2 = 0.007236/(1-0.999^2)0.007236/0.001999≈3.61980.007236/0.001999 \approx 3.6198
v^2\sqrt{\hat v_2}≈1.9026\approx 1.9026
step =0.1×1.8947/1.9026=0.1 \times 1.8947/1.9026≈0.0996\approx 0.0996
θ2=0.9−0.0996\theta_2 = 0.9 - 0.0996≈0.8004\approx 0.8004

Compare to what plain SGD with α=0.1\alpha=0.1 would have done: θ2SGD=1.0−0.1(2.0)−0.1(1.8)=0.62\theta_2^{SGD} = 1.0 - 0.1(2.0) - 0.1(1.8) = 0.62, a much larger, more volatile step. Adam's adaptive denominator (near g2≈∣g∣\sqrt{g^2}\approx |g| here, since gradients are consistently around 1.81.8–2.02.0) keeps the effective step close to α\alpha in magnitude regardless of the raw gradient scale — this is the adaptive-scaling behavior working exactly as designed: it normalizes step size to be comparable to α\alpha, not proportional to the raw gradient magnitude.

AdamW contrast. Suppose we add weight decay λ=0.01\lambda = 0.01. Plain "Adam-L2" folds λθ\lambda\theta into gg before the moment estimates are computed, so the decay term itself gets divided by v^t\sqrt{\hat v_t} — coordinates with large historical gradients have their weight decay shrunk, which is backwards (large-gradient parameters usually need more, not less, regularization pressure). AdamW instead applies θ←θ−αλθ\theta \leftarrow \theta - \alpha\lambda\theta as a separate, undivided term added directly to the parameter update, so every parameter decays toward zero at the same rate αλ\alpha\lambda regardless of its gradient history — matching the original intent of weight decay in Weight Decay and L2 Regularization.

Figure
Plain Adam-L2 vs AdamW: where the weight-decay term enters
Adam + L2 (coupled) g ← g + λθ → m,v from g → λθ divided by √v̂ ✗ wrong scale AdamW (decoupled) g (no λθ) → m,v from g → θ ← θ − α(m̂/(√v̂+ε) + λθ) ✓

Common pitfalls & traps

PitfallWhy it happensFix
Forgetting bias correctionm,vm,v start at 0, so early steps take erratically tiny or huge stepsAlways divide by (1−β1t)(1-\beta_1^t), (1−β2t)(1-\beta_2^t); recompute tt correctly (starts at 1, not 0)
Using plain Adam's L2L_2 term as if it were weight decayThe penalty gets rescaled by v^t\sqrt{\hat v_t}, so large-gradient params are under-regularizedUse AdamW's decoupled decay term instead
ϵ\epsilon too large relative to gradient scaleSwamps the adaptive term, making Adam behave like plain SGD with a tiny learning rateUse the standard 10−810^{-8} (or 10−610^{-6}–10−710^{-7} for fp16 mixed precision) and tune only if diverging
Reusing the same tt counter across restarted training runs / mismatched checkpointsResuming with t=0t=0 re-triggers strong bias correction on a "warm" vv buffer, causing a spikeCheckpoint and restore tt, mm, vv together with θ\theta
Assuming Adam always generalizes as well as SGD+momentumAdaptive scaling can converge to sharper minima on some vision benchmarksConsider SGD+momentum with a tuned schedule, or AdamW with proper weight decay, as an ablation
Setting β2\beta_2 too low for very noisy/sparse gradientsShort effective averaging window for vv makes the denominator jumpy, destabilizing stepsRaise β2\beta_2 toward 0.9990.999–0.99990.9999 for very sparse-gradient regimes (e.g. large embedding tables)

Pioneers & history

Adam was introduced by Diederik P. Kingma and Jimmy Ba as a synthesis of ideas that had been developed separately over the preceding years: Momentum and Nesterov Accelerated Gradient provided the notion of accumulating a velocity vector, while AdaGrad and RMSProp provided the idea of a per-coordinate adaptive learning rate driven by accumulated squared gradients. Adam's specific contribution was combining both moving averages with an explicit, mathematically motivated bias-correction step, which proved decisive for stable behavior at the start of training and for robustness across a very wide range of problems with minimal hyperparameter tuning.

Origins · 1 minDiederik P. Kingma and how this was discovered
DP
Diederik P. Kingma
1988–present · Adam optimizer, variational autoencoders, deep generative modeling

Kingma co-authored Adam during his PhD at the University of Amsterdam, alongside foundational work on variational autoencoders. His research broadly bridges probabilistic modeling and practical, scalable optimization, and Adam itself grew out of the same push to make gradient-based training of deep probabilistic models tractable at scale.

History
2011
AdaGrad introduces per-parameter adaptive learning rates via accumulated squared gradients.
2012
RMSProp (Hinton, unpublished lecture notes) fixes AdaGrad's ever-shrinking step size with an exponential moving average.
2014
Kingma and Ba publish Adam, combining momentum with RMSProp-style scaling and bias correction.
2017
Loshchilov and Hutter identify the L2L_2/weight-decay coupling flaw in Adam and propose AdamW.
2018–2019
AMSGrad and related analyses probe Adam's convergence guarantees and propose fixes for pathological non-convergent cases.

Where this matters

Adam and AdamW are the default optimizers behind the majority of modern deep learning training runs: transformer language models (including large-scale pretraining pipelines), vision transformers, diffusion models, and most fine-tuning workflows use AdamW as the standard choice, typically paired with a warmup-then-decay learning rate schedule. Recommendation systems and NLP models with huge, sparse embedding tables particularly benefit from Adam's per-coordinate adaptivity, since most embedding rows are touched rarely and need individualized step sizes. Frameworks such as PyTorch, TensorFlow, and JAX ship Adam and AdamW as built-in, heavily optimized primitives (including fused CUDA kernels), reflecting how central this optimizer has become to the practical deep learning stack. Understanding its mechanics is also the natural gateway to Hyperparameter Tuning, since α\alpha, β1\beta_1, β2\beta_2, and the weight-decay coefficient are among the most consequential knobs in any training recipe.

Ask the tutor: Can you walk me through why Adam's adaptive denominator can sometimes lead to worse generalization than SGD, and how gradient noise interacts with v^t\sqrt{\hat v_t} near a sharp minimum?

Why does Adam need two separate moving averages instead of one?

The first moment mtm_t tracks the direction the gradient tends to point in, smoothing out mini-batch noise the same way classical momentum does — this speeds convergence along consistent downhill directions. The second moment vtv_t tracks the typical magnitude of the gradient (squared), independent of sign, which is used purely to rescale the step size per coordinate. These are conceptually different quantities: one is about direction and speed, the other about scale and normalization. Using only one of them recovers either plain momentum (no adaptivity) or plain RMSProp/AdaGrad (no smoothing of direction) — Adam's contribution is combining both, plus the bias correction that makes the combination well-behaved from the very first step.

Is Adam always a better choice than SGD with momentum?

No. Adam typically converges faster and is more robust to learning-rate misspecification, which is extremely valuable during early experimentation and for architectures like transformers where the loss landscape is poorly understood. However, several empirical studies on image classification with convolutional networks found that well-tuned SGD with momentum and a hand-crafted learning-rate schedule can reach better final test accuracy, sometimes attributed to Adam's adaptive scaling biasing optimization toward sharper minima with worse generalization. In practice, many production transformer-training pipelines still prefer AdamW, while some vision pipelines prefer SGD+momentum; the right choice depends on architecture, data regime, and how much tuning budget is available.

What does the epsilon term in the denominator actually do?

ϵ\epsilon prevents division by zero when v^t\hat v_t is extremely small, which can happen early in training or for parameters that have received almost no gradient signal yet. But its effect is not merely a numerical safeguard: if ϵ\epsilon is set too large relative to typical v^t\sqrt{\hat v_t} values, it dominates the denominator and effectively suppresses the adaptive scaling, making Adam behave like plain SGD with a small, fixed learning rate. Conversely, too small an ϵ\epsilon can cause instability when v^t\hat v_t is near zero, producing huge steps. The default 10−810^{-8} works well in float32; in float16/mixed-precision training a larger value like 10−610^{-6} or 10−710^{-7} is often used to avoid underflow-related instability.

How does AdamW change the actual parameter update compared to Adam?

In plain Adam with an L2L_2 penalty, the term λθ\lambda\theta is added directly to the gradient gg before computing mtm_t and vtv_t, so the penalty gets absorbed into, and rescaled by, the adaptive denominator v^t\sqrt{\hat v_t}. In AdamW, the gradient used for mtm_t and vtv_t excludes the decay term entirely; instead, αλθ\alpha\lambda\theta is subtracted directly from the parameter update as a separate term. The result is that every parameter decays toward zero at a uniform rate proportional to αλ\alpha\lambda, regardless of its gradient history — matching what weight decay is supposed to do, and empirically improving generalization and making the decay coefficient easier to tune independently of the learning rate.

Why is Adam's memory overhead sometimes a real practical concern?

Adam stores mm and vv as full copies of the parameter tensor shape, so training requires roughly three times the parameter memory (parameters, mm, vv), compared to roughly one time for plain SGD or two times for SGD with momentum. For a model with billions of parameters, this tripling can be the difference between fitting on available GPU/TPU memory and needing to shard optimizer state across multiple devices. This overhead motivated memory-efficient alternatives such as Adafactor, which factorizes the second-moment estimate to avoid storing a full-size buffer, at some cost in adaptivity precision, particularly relevant for very large-scale model training.

Does Adam guarantee convergence to a minimum?

For general non-convex deep learning losses, no optimizer has a guarantee of finding a global minimum; Adam, like SGD, is only guaranteed (under suitable assumptions) to converge to a stationary point or to have bounded regret in convex online-learning settings. The original Adam paper's convex convergence proof was later shown to have a gap, since a fixed β2\beta_2 can allow v^t\hat v_t to decrease at a bad moment, which is what motivated AMSGrad's fix of taking a running maximum of v^t\hat v_t. In practice, across standard deep learning benchmarks, plain Adam with default hyperparameters converges reliably, and the theoretical counterexamples are rarely encountered outside adversarially constructed sequences.

Before this

Where this leads