Part of Language AI Handbook
Covers Adam optimizer: first and second moment estimates, bias correction, adaptive learning rates, hyperparameter tuning.
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Adam Optimizer
Training a deep neural network is fundamentally an optimization problem: you have a loss function and you want to find the weights that minimize it. As we saw in the Stochastic Gradient Descent chapter, SGD moves weights in the direction of steepest descent, and momentum adds a velocity term to smooth out oscillations and speed past shallow gradients. Both methods use a single global learning rate for every parameter, which creates a fundamental tension. Some parameters receive dense gradient signals and should move cautiously; others are updated rarely and need larger steps to converge at all. A single learning rate cannot serve both needs well.
Adam (Adaptive Moment Estimation), introduced by Diederik Kingma and Jimmy Ba in 2014, resolves this tension by maintaining an independent learning rate for every parameter, scaled automatically based on the history of gradients that parameter has seen. It does this by tracking two running statistics: the mean of past gradients (the first moment) and the uncentered variance of past gradients (the second moment). These two statistics together give Adam enough information to set an appropriate step size for each parameter individually. The result is an optimizer that requires almost no per-problem tuning and converges reliably across a wide range of architectures, from convolutional networks to transformers.
Understanding Adam deeply means understanding why the two moment estimates are needed, where the bias correction terms come from, why the epsilon term in the denominator matters more than its small value suggests, and what the practical failure modes look like in production training runs. This chapter covers all of that, from first principles through a full PyTorch implementation, a survey of Adam's most important variants, and a discussion of the memory implications that make Adam challenging at large scale.
Why Per-Parameter Learning Rates
To see why a single global learning rate is limiting, consider a sparse input problem like word embeddings. In language modeling, some words appear thousands of times per batch; others might appear once per epoch. When you compute gradients and average them over a minibatch, frequently occurring words generate large, consistent gradient signals. Rare words generate tiny gradients most of the time, with occasional large spikes. If you set the learning rate high enough for rare words to learn, frequent words overshoot. If you set it low enough for frequent words, rare words barely move.
The same tension appears in deeper networks. Parameters close to the output receive large, stable gradients; parameters in early layers receive small, noisy gradients that have been attenuated through many multiplications during backpropagation. We saw in the backpropagation chapter how the chain rule multiplies gradients across layers, which can leave early-layer parameters chronically under-updated or chronically over-updated depending on the global learning rate.
The Geometry of the Problem: Ill-Conditioned Loss Surfaces
The problem with a single learning rate becomes especially vivid when you think about the geometry of the loss surface. In a well-conditioned problem, the loss function is roughly spherical: the curvature is similar in every direction. SGD with a fixed learning rate moves at roughly the same speed in all directions. In an ill-conditioned problem, the loss surface looks more like a narrow canyon: very steep walls on one side, a very flat floor in another. The optimal path to the minimum runs along the canyon floor, but SGD with a fixed learning rate has to choose between two bad options. Use a large step size and you bounce back and forth off the steep walls, oscillating violently. Use a small step size and you crawl along the flat floor at a glacial pace.
This ill-conditioning is not a pathological edge case. It arises naturally in deep networks from weight initialization asymmetries, from the scale differences between input features, and from the structure of the weight matrices at different depths. A typical transformer training run will have gradient norms that vary by orders of magnitude across different parameter groups. The first embedding layer, the output projection, and the attention weight matrices all occupy different regions of the loss landscape with vastly different curvatures.
Adaptive learning rates address the ill-conditioning problem by measuring, for each parameter independently, how much curvature has been encountered. Parameters in steep dimensions accumulate large second-moment estimates and receive smaller effective learning rates. Parameters in flat dimensions accumulate small second-moment estimates and receive larger effective learning rates. The optimizer self-calibrates its step size to match the local geometry of each dimension simultaneously, without any manual tuning.
A Brief History: AdaGrad and RMSProp
The idea of parameter-specific step sizes had been explored before Adam. AdaGrad (Duchi, Hazan, and Singer, 2011) was the first major adaptive optimizer. It accumulated the sum of all squared gradients seen so far for each parameter, and used that sum in the denominator:
where is the cumulative sum of squared gradients. AdaGrad was a significant step forward for sparse problems: parameters that receive occasional large gradients quickly build up a large , causing their learning rate to shrink. Parameters that receive small gradients maintain a small and continue to learn quickly. This worked well for training word vectors and other models with sparse inputs.
However, AdaGrad has a critical flaw for long training runs: the accumulator only ever grows. It grows every time a gradient is non-zero, and it never forgets old gradient information. After enough training steps, becomes so large that the effective learning rate shrinks to near zero for all parameters. Training effectively stalls. In practice, AdaGrad works well for shallow models trained for a small number of epochs, but fails to converge on deep networks trained for hundreds of epochs.
RMSProp, proposed by Geoffrey Hinton in an unpublished lecture (2012), addressed the monotonic accumulation problem by replacing the cumulative sum with an exponential moving average:
By using a decaying average rather than a cumulative sum, RMSProp's denominator reflects recent gradient magnitudes rather than the entire training history. Parameters that were frequently updated early in training but are now quiet will see their decay, allowing their effective learning rate to recover. This makes RMSProp viable for long training runs where gradient statistics change substantially over time.
Adam combines RMSProp's adaptive second moment with momentum's first moment, wrapping both in a bias correction that makes the estimates accurate from the very first update. It is, in a sense, the synthesis of a decade of adaptive optimizer research into a single coherent algorithm.
The Two Moment Estimates
Adam names its running statistics after statistical moments. Recall that for a random variable :
- The first moment is the mean:
- The second moment is the mean of the square: (uncentered variance)
Adam does not compute these exactly. Instead it maintains exponential moving averages that approximate them. Let be the gradient of the loss with respect to a single parameter at step . Adam maintains:
where:
- : the first moment estimate (exponential moving average of gradients)
- : the second moment estimate (exponential moving average of squared gradients)
- : the decay rate for the first moment, typically 0.9
- : the decay rate for the second moment, typically 0.999
- : the gradient at time step
The first moment is recognizable as the velocity term from momentum. When gradients consistently point in one direction, builds up in that direction. When gradients oscillate, contributions cancel out. This is exactly the momentum behavior we saw in Chapter 7.
The second moment tracks the recent average of squared gradients. If a parameter has been receiving large gradients (either positive or negative), will be large. If it has received small or near-zero gradients, will be small. Squaring keeps positive and captures magnitude regardless of sign.
The Intuition Behind Dividing by the Second Moment
The Adam update will divide the first moment by the square root of the second moment. To see why this achieves adaptive learning rates, consider two parameters:
Parameter A (dense gradients): receives gradients like repeatedly. The second moment . The effective learning rate is scaled by . Not much amplification because this parameter is already getting strong updates.
Parameter B (sparse gradients): receives gradients like rarely. The second moment (most steps contribute zero). The effective learning rate is scaled by . This rare-but-important parameter gets a much larger effective step.
The division by naturally equalizes the learning pace: parameters with large historical gradients get a small effective learning rate, and parameters with small historical gradients get a large effective learning rate. The optimizer finds a reasonable pace for each parameter without any manual tuning.
There is a useful geometric interpretation of what Adam is doing. The second moment estimate approximates the root-mean-square gradient for each parameter. Dividing by this quantity normalizes the update so that each parameter receives a step whose size is roughly proportional to the inverse of its typical gradient magnitude. In a sense, Adam is performing a diagonal approximation to the natural gradient: it rescales the gradient to account for the curvature of the loss surface along each parameter axis independently. Full natural gradient methods (like K-FAC) use the full Fisher information matrix to perform this rescaling, which is computationally prohibitive at large scale. Adam's diagonal approximation is cheap to compute and surprisingly effective in practice, capturing the most important per-axis curvature information while staying computationally tractable.
Bias Correction
There is a subtle but important flaw in the raw moment estimates. Both and are initialized to zero at step . This means that early in training, the estimates are biased toward zero. At step :
With , we have even though our best estimate of the mean gradient is itself. The true mean should be , but our estimate is only 10% of it.
This zero-initialization bias decays over time. After steps, the first moment is a weighted sum of past gradients:
Taking the expectation (assuming are drawn from the same distribution with mean ):
where:
- : the true mean gradient
- : the decay factor raised to the -th power
- : the bias factor, which starts near 0 and approaches 1 as grows
The bias factor starts at at step (meaning the estimate is only 10% of the truth) and approaches 1 as (where it becomes accurate). To correct for this initialization bias, Adam divides each moment estimate by its bias correction factor:
where:
- : the bias-corrected first moment estimate
- : the bias-corrected second moment estimate
- : correction factor for the first moment at step
- : correction factor for the second moment at step
For , the second moment bias correction is even more severe early on: , so . Without this correction, the second moment estimate is far too small for the first thousand or so steps, which would cause Adam to take enormous steps with a tiny denominator. The bias correction stabilizes training in these early steps, making Adam's behavior in the first epoch much more predictable.
As training progresses, and , so and . The correction becomes negligible after enough steps. For , the first moment correction becomes less than 1% after about steps. For , the second moment correction becomes less than 1% after about steps.
Without bias correction, you would observe a characteristic artifact at the start of training: the learning rate would appear enormous for the first few hundred steps, because the second moment estimate would be near zero, making very large. This would cause gradient explosions or wildly oscillating loss values before eventually stabilizing. With bias correction, both moments are immediately calibrated to their true statistical values, and Adam's step sizes remain reasonable from step one. This is especially important for transformer training with learning rate warmup: the warmup schedule deliberately starts with a small learning rate and increases it over the first several thousand steps. During this warmup period, the bias correction is doing exactly its intended job. Both the scheduled learning rate and the moment estimates are warming up together, creating doubly conservative early steps that prevent gradient explosions. The two mechanisms are complementary, not redundant.
The Adam Update Rule
With bias-corrected moment estimates in hand, the Adam parameter update is:
where:
- : the parameter vector at step
- : the global learning rate (typically 0.001)
- : bias-corrected first moment (direction and momentum)
- : bias-corrected second moment (per-parameter scale)
- : a small constant for numerical stability, typically
The full Adam algorithm, written out step by step, is:
where:
- : the loss function
- All other variables are as defined above
The Role of Epsilon
The term in the denominator prevents division by zero when , which happens for parameters that have received no gradients. But has an additional effect on Adam's behavior that is often overlooked.
Consider a parameter where is very small, say . Then , and the effective learning rate is . This is enormous. In practice, if a parameter truly receives near-zero gradients consistently, its is also near zero, so the actual update remains small. The two small quantities cancel out.
However, if is set too small (e.g., ), numerical precision issues can cause to underflow to zero in floating-point arithmetic, resulting in infs or nans in the updates. If is set too large (e.g., 1.0), Adam degenerates toward vanilla SGD because the denominator becomes dominated by rather than , eliminating the adaptive behavior. The default is a pragmatic choice that avoids both failure modes in typical float32 computation.
PyTorch's torch.optim.Adam uses by default but exposes it as a tunable parameter. Some practitioners increase it to or even when training with mixed precision (float16), since smaller values can cause numerical instability at reduced precision.
Worked Example: Tracing One Parameter Through 5 Steps
Let's trace a single parameter through five Adam update steps to make the mechanics concrete. Suppose the parameter starts at , and receives gradients over five steps. We'll use the standard hyperparameters: , , , .
Initialize: , .
Step 1 (, ):
Notice that after bias correction, and . The bias correction brings the estimates in line with the actual gradient magnitude at step 1, giving an effective step of exactly . In other words, early in training the Adam update approximates a step of constant size in the gradient direction, regardless of gradient magnitude. This is the trust region interpretation of Adam.
Step 2 (, ):
The parameter continues to decrease, with each step sized close to . This is characteristic of Adam in early training: the per-parameter normalization makes step sizes roughly equal to the global learning rate , giving predictable training dynamics regardless of gradient scale.
By step 5, the moment estimates have accumulated enough history that the bias corrections become smaller. The first moment is now a smooth average of all five gradients, pointing consistently in the positive direction (since all gradients are positive). The second moment has accumulated a sense of the typical gradient magnitude squared, and the effective step size is settling toward a stable value. This accumulation phase is what makes Adam's convergence so reliable: within a few dozen steps, it has calibrated per-parameter step sizes that remain appropriate throughout the rest of training. If you were to plot the effective learning rate over time, you would see it start near during the bias-dominated early steps, then gradually settle as stabilizes around the true mean squared gradient. This settling process takes longer for parameters with smaller gradients, because their grows more slowly and the bias correction factor remains significant for more steps.
Adam vs SGD and RMSProp
Adam's design draws from two predecessors, and it helps to understand what each contributes.
Vanilla SGD uses a fixed learning rate:
SGD is simple and works well when gradients are well-scaled, but it has no memory of past gradients and no ability to adapt its step size per parameter.
Momentum adds a velocity term:
Momentum smooths oscillations and builds up speed in consistent gradient directions, which helps escape flat regions and ravines. But it still uses a single global learning rate.
RMSProp adapts the learning rate per parameter using the second moment:
RMSProp gives each parameter its own adaptive step size but applies the raw gradient rather than a momentum-smoothed version. This means the update direction can be noisy when individual gradients are noisy.
Adam combines both improvements:
The numerator provides the momentum benefit (smoothed direction), and the denominator provides the adaptive scale benefit (per-parameter step size). The bias correction makes both estimates reliable from the start.
| Property | SGD | Momentum | RMSProp | Adam |
|---|---|---|---|---|
| Per-parameter learning rate | No | No | Yes | Yes |
| Momentum smoothing | No | Yes | No | Yes |
| Bias correction | N/A | N/A | No | Yes |
| Memory per parameter | 0 | 1 (velocity) | 1 (v) | 2 (m, v) |
| Typical default LR | 0.01-0.1 | 0.01-0.1 | 0.001 | 0.001 |
The practical implication of this comparison is that Adam's 2x memory overhead over SGD (two tensors per parameter instead of one) is the price you pay for the two improvements. For most modern deep learning workloads where GPU memory is the binding constraint, this tradeoff is well worth it. The improvement in convergence speed typically more than compensates for the smaller effective batch size you can fit on a GPU. However, at very large model scales (billions of parameters), the memory overhead becomes a substantial engineering challenge, as discussed later in the chapter.
Adam Convergence Properties
The original Adam paper provides a convergence proof for the online learning setting (non-stationary objectives) under the assumption of convexity. In practice, neural network training is non-convex, and the theoretical guarantees do not directly apply. Nevertheless, several empirical and theoretical observations are useful.
Regret and Convergence Rates
For convex problems, Adam achieves regret, where is the number of steps. This is the same asymptotic rate as AdaGrad, though Adam's per-step constants are generally better because the exponential moving average avoids the excessive accumulation that slows AdaGrad in long runs.
For non-convex problems, Adam can fail to converge to a stationary point in theory. A paper by Reddi, Kale, and Kumar (2018) demonstrated specific synthetic examples where Adam oscillates between two solutions rather than converging. This led to the AMSGrad variant (discussed below), though in practice Adam converges reliably on most machine learning tasks.
When Adam Converges Faster Than SGD
Adam outperforms SGD most clearly in situations with:
- Sparse features: embedding tables, one-hot inputs, high-dimensional vocabularies. The adaptive per-parameter learning rate is decisive here. Embedding rows that are rarely activated can receive large effective learning rates because their second moment accumulates slowly. This is one reason Adam became dominant for NLP before it became dominant for vision.
- Non-stationary objectives: Adam's exponential weighting means it adapts to changing gradient distributions, which matters in curriculum learning or when the data distribution shifts during training.
- Noisy gradients: the momentum term smooths noise, and the adaptive denominator prevents large oscillations from dominating the update.
- Deep networks: parameters at different depths receive gradients of very different magnitude. Adam compensates for this discrepancy without requiring layer-wise learning rate tuning.
- Fast initial convergence requirements: because Adam normalizes step sizes to be approximately regardless of gradient scale, it tends to make rapid progress in early training. When compute is limited and you cannot afford to train to convergence, Adam often finds a better solution faster.
When SGD Matches or Beats Adam
Careful research has shown that carefully tuned SGD with momentum can match Adam's generalization performance on image classification benchmarks, and sometimes exceeds it. The intuition is that Adam's adaptive learning rates may allow it to find sharp, narrow minima that generalize less well than the broader minima that SGD tends to find. The sharp minimum hypothesis (from Keskar et al., 2017) suggests that minima discovered through large, consistently-directed updates tend to be broader and generalize better. SGD with a large, slowly decaying learning rate naturally finds such broad minima. Adam's adaptive normalization tends to use more cautious, consistent step sizes that can lock in on narrow minima earlier in training.
However, this tradeoff is highly architecture- and task-dependent. For language modeling and transformer training, Adam or its variants are essentially universal. The datasets are large enough, and the loss landscapes well-behaved enough, that the generalization gap between Adam and SGD rarely manifests.
The practical advice from researchers at Google, OpenAI, and DeepMind converges on: use Adam (or AdamW) by default; switch to carefully-tuned SGD with momentum only if you have the compute budget for extensive hyperparameter search and there is evidence from prior work that SGD generalizes better for your specific task.
Adam Hyperparameters
Adam has four hyperparameters: , , , and . Understanding what each controls helps you diagnose training problems.
Learning Rate
The learning rate remains the most important hyperparameter. Adam's default of works for a surprisingly wide range of tasks, but you should expect to tune it. Rules of thumb:
- Start with for new problems
- If training loss explodes or diverges immediately: reduce by 10x
- If training is too slow after 10% of planned epochs: increase by 3-10x
- Use a learning rate finder (warm-up scan) to identify the range of valid learning rates
One important difference from SGD: Adam's effective per-parameter learning rate is . When you change the global , you are scaling all per-parameter learning rates proportionally. The adaptive normalization means Adam is generally less sensitive to the precise choice of than SGD is, but the relative magnitudes still matter.
: First Moment Decay
controls how much past gradients influence the current update direction. With (default), the effective momentum window is approximately steps. This means Adam averages gradient direction over the last ~10 steps.
Lowering makes Adam respond more quickly to recent gradient changes, which can help in non-stationary settings but increases noise. The default 0.9 is almost never worth changing. Some practitioners use (effectively no momentum) for specific research experiments to isolate the adaptive learning rate effect, but this is rarely beneficial in practice.
: Second Moment Decay
controls how long Adam remembers past gradient magnitudes. With (default), the effective window is steps. The second moment tracks a much longer history than the first moment.
This asymmetry is intentional: you want the denominator to be a stable estimate of the gradient's typical magnitude, not one that jumps around with each new batch. However, can be slow to adapt if the gradient scale changes dramatically during training (for example, when learning rate warm-up causes a large jump in gradient norms). In such cases, or even can respond faster.
In transformer training, has been found to work better than the default 0.999 in some settings (the original "Attention Is All You Need" paper used ).
: Numerical Stability
As discussed above, is the default. For float16 training, or is often more stable.
Adam Variants
Adam has inspired a family of related optimizers, each addressing a specific limitation. Understanding the variants helps you choose the right optimizer for your use case.
AMSGrad
AMSGrad (Reddi, Kale, Kumar, 2018) was proposed to fix the theoretical non-convergence problem in Adam. Recall that Adam's second moment estimate is an exponential moving average, which can "forget" past large gradients. AMSGrad uses the maximum of all past second moment estimates instead:
By using in the denominator instead of , AMSGrad ensures that the effective learning rate never increases. Once a large gradient has been seen, the corresponding dimension's effective learning rate stays compressed. This monotonically non-increasing property is what enables the convergence guarantee.
In practice, AMSGrad often converges more slowly than Adam because the non-increasing effective learning rate can make it too conservative in the latter part of training. Most practitioners find that Adam with a well-tuned schedule outperforms AMSGrad, which is why AMSGrad has seen limited adoption despite its theoretical appeal. You can enable it in PyTorch with torch.optim.Adam(..., amsgrad=True).
Nadam
Nadam (Dozat, 2016) combines Adam with Nesterov momentum. Recall that Nesterov momentum modifies the standard momentum update by computing the gradient at the "lookahead" position rather than the current position. Nesterov momentum typically converges slightly faster than standard momentum because the gradient is evaluated at a more future-looking position, meaning the update step already accounts for where you will be after applying the current velocity.
Nadam incorporates this improvement into Adam by substituting the Nesterov first moment estimate into the update rule. The difference from Adam is subtle but can matter in settings where the gradient changes direction quickly. PyTorch provides Nadam as torch.optim.NAdam. For most applications, the improvement over Adam is marginal, but it is worth knowing about as the theoretically cleaner variant.
AdamW
AdamW (Loshchilov and Hutter, 2017) is arguably the most important Adam variant and deserves a full chapter of its own (which follows this one). The key insight is that Adam's weight_decay parameter does not implement true weight decay: it adds to the gradient before the Adam update, which means the regularization term is divided by along with the gradient. This weakens the regularization for frequently updated parameters and strengthens it for rarely updated ones, which is the opposite of what you want.
AdamW implements decoupled weight decay: it applies the weight decay step directly to the parameters after the Adam update, independently of the gradient:
This simple decoupling produces substantially better generalization in language modeling and has become the standard optimizer for training transformers. All modern large language models use AdamW rather than vanilla Adam.
Adafactor
Adafactor (Shazeer and Stern, 2018) addresses the memory overhead problem. For a weight matrix of shape , Adam stores two additional matrices of the same shape ( and ), tripling the memory for that parameter. Adafactor factors the second moment matrix into two rank-one vectors of shape and , reducing the optimizer state from to .
For large embedding tables or weight matrices, this can be a massive memory saving. A 50,000-word vocabulary with 1024-dimensional embeddings requires a matrix of shape . Adam stores two copies of this, totaling million floats for the optimizer state alone. Adafactor reduces this to floats. The tradeoff is that the factored approximation is less accurate than the true per-element second moment, which can slow convergence and require more careful hyperparameter tuning. Adafactor is used in training T5 and some other very large models where memory is the binding constraint.
Common Failures and Fixes
Adam is robust but not failure-proof. These are the most common issues and their solutions.
Divergence at the Start of Training
Symptom: Loss is NaN or increases rapidly in the first few hundred steps.
Cause: Gradients are very large at initialization, causing step sizes that blow up the parameters. With poorly initialized weights, the first gradient can be orders of magnitude larger than what Adam stabilizes to after warm-up.
Fix: Use learning rate warm-up. Start with at 1-10% of the target value and linearly increase it over the first steps (typically 1,000-10,000 steps). This gives Adam time to accumulate reliable moment estimates before taking large steps. Warm-up is essentially standard practice for transformer training.
Loss Plateaus Early
Symptom: Training loss stops decreasing after a few thousand steps and stays flat.
Cause: Either the learning rate is too low, the moment estimates have accumulated high curvature noise, or the model is stuck in a flat region.
Fix: Check the learning rate first. Then consider learning rate decay: cosine annealing or step decay after a plateau. A learning rate restart (cyclic warm-up) can also help escape flat regions.
Gradient-Accumulation Mismatch
Symptom: Training with gradient accumulation over steps gives different results than training with batch size times larger.
Cause: When you accumulate gradients over steps and then take one Adam step, you are mixing per-step gradients differently than if you averaged them into one batch gradient. The moment estimates see small updates instead of one averaged update, leading to subtly different dynamics.
Fix: When using gradient accumulation for large effective batch sizes, scale by (not ) and keep the moment estimates consistent by only calling the optimizer step once per accumulation cycle.
Adam Failing to Generalize on Vision Tasks
Symptom: Adam achieves lower training loss than SGD but higher test error.
Cause: The adaptive learning rate may cause Adam to focus on dimensions with low variance, which can correspond to spurious correlations in the training data rather than generalizable features.
Fix: Switch to SGD with momentum for tasks where this generalization gap matters (primarily image classification with standard architectures). For NLP tasks, this problem is much less pronounced.
PyTorch Adam Implementation
PyTorch provides torch.optim.Adam as a built-in optimizer. Understanding how to use it correctly, including how to set per-layer learning rates and integrate it with learning rate scheduling, is essential for practical deep learning.
Basic Setup
First, install the required libraries.
Define a simple model and configure the Adam optimizer with default hyperparameters.
torch.manual_seed(42)
# Simple two-layer network for binary classification
model = nn.Sequential(
nn.Linear(20, 64),
nn.ReLU(),
nn.Linear(64, 32),
nn.ReLU(),
nn.Linear(32, 1),
nn.Sigmoid(),
)
# Standard Adam configuration
optimizer = torch.optim.Adam(
model.parameters(), lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0.0
)Model parameters: 3,457 Optimizer: Adam lr: 0.001 betas: (0.9, 0.999) eps: 1e-08
Training Loop
A complete training loop using Adam over a synthetic binary classification dataset.
# Generate synthetic dataset
np.random.seed(42)
X = torch.randn(1000, 20)
w_true = torch.randn(20)
y = ((X @ w_true + torch.randn(1000) * 0.5) > 0).float().unsqueeze(1)
# Split into train/test
X_train, X_test = X[:800], X[800:]
y_train, y_test = y[:800], y[800:]
criterion = nn.BCELoss()
train_losses = []
test_losses = []
for epoch in range(200):
model.train()
optimizer.zero_grad()
y_pred = model(X_train)
loss = criterion(y_pred, y_train)
loss.backward()
optimizer.step()
train_losses.append(loss.item())
model.eval()
with torch.no_grad():
test_pred = model(X_test)
test_loss = criterion(test_pred, y_test).item()
test_losses.append(test_loss)Final train loss: 0.0159 Final test loss: 0.2141 Train accuracy: 1.000 Test accuracy: 0.910
The model converges within 200 epochs using Adam's default learning rate of 0.001. This shows that the default configuration works well without any tuning on this type of task. Notice the standard pattern: optimizer.zero_grad() clears gradients from the previous step, loss.backward() computes new gradients, and optimizer.step() applies the Adam update. Forgetting zero_grad() is one of the most common bugs in PyTorch training: gradients accumulate across steps, effectively multiplying the gradient by the number of steps since the last reset, which causes wildly incorrect updates.
Accessing Optimizer State
You can inspect Adam's internal state to understand what the optimizer has learned about each parameter.
# Inspect the Adam state for the first layer's weight matrix
first_group = optimizer.state_dict()["state"]
first_param_key = list(first_group.keys())[0]
param_state = first_group[first_param_key]
step_count = param_state["step"].item()
m_mean = param_state["exp_avg"].abs().mean().item()
v_mean = param_state["exp_avg_sq"].abs().mean().item()Steps taken: 200 Mean |m_t| (first moment): 0.000243 Mean |v_t| (second moment): 0.000000 Mean sqrt(v_t) (scale): 0.000666 Effective LR (approx): 1.502169
The exp_avg and exp_avg_sq tensors correspond to and in Adam's equations. After training, tells you how large the gradients have been for each parameter. Parameters with large exp_avg_sq values have been actively updated throughout training. This state information can be very useful for debugging: if exp_avg_sq is near zero for many parameters, those parameters have barely been updated, which often signals a problem in the gradient flow (dead neurons, disconnected components, or missing gradients through a non-differentiable operation).
When you save and restore a model checkpoint, you should save the optimizer state alongside the model weights. Resuming training from a checkpoint without the optimizer state means Adam loses all its accumulated moment estimates and must restart the calibration process from zero, causing a temporary dip in training efficiency and sometimes a brief spike in loss.
Per-Layer Learning Rates
Adam supports different learning rates for different parameter groups, which is useful when fine-tuning pretrained models where you want the base model to update slowly and the new head to update quickly.
# Different learning rates for each layer
optimizer_multilr = torch.optim.Adam(
[
{"params": model[0].parameters(), "lr": 1e-4},
{"params": model[2].parameters(), "lr": 1e-3},
{"params": model[4].parameters(), "lr": 1e-2},
],
lr=1e-3,
)Group 0: lr=0.0001, params=1,344 Group 1: lr=0.001, params=2,080 Group 2: lr=0.01, params=33
This is a common pattern for transfer learning, where the pretrained backbone should update at a small fraction of the head's learning rate to preserve learned representations. The intuition is that the backbone already encodes useful features; updating it too aggressively will destroy that knowledge before the head has a chance to use it. A common heuristic is to set backbone learning rates to 1/10th or 1/100th of the head learning rate, though optimal values vary by task and model architecture.
Learning Rate Warmup with Adam
Learning rate warmup is nearly universal in transformer training. The most common schedule is linear warmup followed by cosine or inverse-square-root decay.
def get_linear_warmup_cosine_decay(
optimizer, warmup_steps, total_steps, min_lr_ratio=0.1
):
"""Linear warmup then cosine decay schedule."""
def lr_lambda(step):
if step < warmup_steps:
return step / warmup_steps
progress = (step - warmup_steps) / (total_steps - warmup_steps)
cosine_decay = 0.5 * (1 + np.cos(np.pi * progress))
return min_lr_ratio + (1 - min_lr_ratio) * cosine_decay
return torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda)
torch.manual_seed(42)
model_sched = nn.Sequential(nn.Linear(20, 1))
optimizer_sched = torch.optim.Adam(model_sched.parameters(), lr=1e-3)
total_steps = 1000
warmup_steps = 100
scheduler = get_linear_warmup_cosine_decay(
optimizer_sched, warmup_steps, total_steps
)
lr_schedule = []
for step in range(total_steps):
lr_schedule.append(optimizer_sched.param_groups[0]["lr"])
scheduler.step()LR at step 0: 0.00e+00 LR at step 50: 5.00e-04 LR at step 100: 1.00e-03 (end of warmup) LR at step 500: 6.28e-04 LR at step 999: 1.00e-04
The warmup ramps the learning rate from zero to the target value . During this phase, Adam's moment estimates are also near zero (from their zero initialization), so the combined effect of small and small moment magnitudes means the first warmup steps are very conservative. As both the schedule and the moment estimates warm up together, training becomes stable.
Key Parameters
The key parameters for PyTorch's Adam optimizer are:
- lr: Global learning rate . Default 0.001. This is the most impactful hyperparameter.
- betas: Tuple for first and second moment decay. Default (0.9, 0.999). Rarely need changing.
- eps: Numerical stability constant . Default 1e-8. Increase to 1e-6 for float16 training.
- weight_decay: L2 regularization coefficient. Default 0. For true decoupled weight decay, use AdamW instead (covered in the next chapter).
- amsgrad: Whether to use AMSGrad variant. Default False. Rarely needed in practice.
Visualizing Adam's Adaptive Behavior
To understand how Adam adapts to different gradient patterns, let's build visualizations that trace the moment estimates and effective learning rates for parameters with different gradient histories.



The visualization confirms the core intuition: parameters that receive large, frequent gradients get a smaller effective learning rate, while parameters that receive rare or small gradients get a larger boost. The global learning rate is a reference, with the adaptive normalization adjusting each parameter up or down based on its gradient history.


The ill-conditioned quadratic (where the curvature in the -direction is 10x steeper than in ) illustrates where adaptive learning rates shine. SGD with momentum oscillates across the narrow valley because it must use a conservative learning rate to avoid overshooting in the steep -dimension. Adam's adaptive normalization effectively uses a different learning rate along each dimension, letting it move along the valley directly. The same geometry appears in transformer loss surfaces: some parameter directions are far stiffer than others, which is one important reason Adam dominates transformer training.

Gradient Noise and Adam's Robustness
One reason Adam dominates practical deep learning is its robustness to gradient noise. When training with small minibatches, the gradient estimate is noisy: each batch samples only a fraction of the training data, and the resulting gradient is a stochastic approximation of the true gradient. This noise has two distinct effects on optimization.
The first effect is noise in gradient direction. On any given step, the gradient might point in a slightly wrong direction due to the random batch composition. Momentum (the first moment ) mitigates this by averaging over recent steps. If the true gradient points consistently in one direction but is corrupted by zero-mean noise, the average over steps will be much closer to the true direction than any individual step.
The second effect is noise in gradient magnitude. Stochastic gradients have variable magnitude even when the direction is correct. A batch that happens to contain many hard examples will produce a large gradient; a batch of easy examples produces a small one. The second moment captures this variability. By dividing by , Adam normalizes away the magnitude variation, making step sizes more consistent even when individual batch gradients vary widely.
This dual smoothing is why Adam with small batches often converges faster in terms of wall-clock time than SGD with large batches. With large batches, SGD has low gradient variance and converges well; with small batches (which process more data per unit time on a single GPU), Adam's noise robustness is decisive. In language model training, where batch sizes are often constrained by memory and gradients are computed from short context windows, this property is particularly valuable.
Memory Implications for Large Model Training
At large scale, Adam's memory requirements become a significant engineering constraint. Consider a language model with parameters. A standard training setup in float32 requires:
- floats for the model parameters themselves
- floats for the parameter gradients (computed during backpropagation)
- floats for the Adam first and second moments
The total is floats, four times the model's parameter count. For float32 training, each float is 4 bytes, so a 7 billion parameter model requires GB of memory just for the training state. This far exceeds the capacity of any single GPU (which typically has 40-80 GB of HBM memory), requiring distributed training across many devices.
Several strategies reduce this memory footprint:
ZeRO (Zero Redundancy Optimizer) from Microsoft DeepSpeed shards the optimizer state, gradients, and parameters across all GPUs in a distributed training job. In the most aggressive mode (ZeRO-3), each GPU holds only of the model weights, gradients, and Adam moments. The memory per GPU scales inversely with the number of GPUs, making it possible to train models far larger than any single GPU's capacity.
8-bit Adam (Dettmers et al., 2022, available via the bitsandbytes library) quantizes the optimizer states to 8-bit integers, reducing the 8 bytes per parameter for Adam state (two float32 tensors) to 2 bytes per parameter. The quantization is done with a dynamic block-wise scheme that preserves the optimizer's behavior almost exactly, with negligible accuracy loss. This is perhaps the most practical approach for single-GPU training of large models.
Mixed precision training uses float16 for the forward and backward passes (saving memory on activations and intermediate computations) while keeping the optimizer state and master copy of the weights in float32. The Adam step itself is computed in float32 and then cast back to float16 for the next forward pass. This is the standard approach for training transformers on modern hardware and roughly halves memory usage compared to full float32 training.
Limitations and Practical Considerations
Adam is a powerful and broadly applicable optimizer, but it has several limitations that practitioners encounter regularly.
Memory Overhead
Adam stores two additional tensors per parameter: and . For large models, this triples the memory required for optimizer state compared to SGD (which stores one tensor per parameter for momentum, or zero for vanilla SGD). A 7 billion parameter model requires roughly 7B floats for the parameters themselves. Adam requires another 14B floats (two moments), for a total of 21B floats of optimizer state. At float32, that is 84 GB, which is prohibitive on a single GPU.
Remedies include:
- 8-bit Adam: The bitsandbytes library implements Adam with 8-bit quantized optimizer states, reducing memory by roughly 4x with minimal accuracy impact.
- ZeRO: Distributed optimizer state sharding across GPUs, so each GPU holds only a fraction of the moments.
- Adafactor: An adaptive optimizer with optimizer state that factors the second moment matrix, used for very large models.
Generalization Gap on Some Tasks
As noted in the comparison section, Adam can find sharper minima than SGD on some image classification benchmarks, leading to worse test accuracy despite better training accuracy. This problem is less prevalent in NLP, where the datasets are large enough that sharpness does not correlate strongly with generalization. If you observe a training-test gap that seems larger than expected, two practical remedies are AdamW (which decouples weight decay and tends to find broader minima) and data augmentation (which artificially widens the training distribution, making sharp minima less likely to perfectly fit the training set).
Non-Convergence in Theory
The theoretical convergence guarantee for Adam relies on assumptions (convexity, or specific non-convex conditions) that are not always met. AMSGrad modifies the second moment estimate to use the maximum of all past estimates, guaranteeing convergence in the online convex setting. However, empirical comparisons typically show Adam converging as well as or better than AMSGrad on practical tasks, so AMSGrad has seen limited adoption. The theoretical non-convergence of Adam in worst-case settings does not appear to manifest in the loss landscapes of practical neural networks, which have much more benign structure than the adversarial examples used in the theoretical analysis.
Adam and Weight Decay
A subtle but important issue is that when you add weight_decay to PyTorch's torch.optim.Adam, you are not getting true weight decay. You are getting L2 regularization, which adds to the gradient before the update. With Adam's adaptive scaling, this L2 penalty is also divided by , which means the actual regularization strength varies by parameter and is weaker for frequently updated parameters (which often need regularization most). This interaction defeats much of the purpose of weight decay as a regularizer. This is why AdamW, introduced by Loshchilov and Hutter in 2017, applies weight decay directly to the parameters after the Adam step rather than through the gradient. AdamW is the subject of the next chapter, and it is the recommended optimizer for essentially all modern neural language modeling.
Adam and Learning Rate Scheduling
A subtle practical issue is that Adam interacts with learning rate schedules in a non-obvious way. When the learning rate schedule reduces by a large factor late in training (as cosine annealing does), the moment estimates and were built up under the higher learning rate regime. The second moment reflects the gradient magnitudes that caused the Adam updates when was large. As decreases, the adaptive learning rates per parameter shrink proportionally, but the relationship between and the current training dynamics may no longer be well-calibrated. Some practitioners reset the Adam optimizer state when making large learning rate jumps (such as at the beginning of a second training stage) to avoid carrying over stale moment estimates. This is sometimes called "warm restarting" the optimizer, and it can produce a brief spike in loss followed by improved convergence in the new learning rate regime.
Summary
Adam brings together two key ideas that address fundamental limitations of fixed-learning-rate optimizers.
The first idea is per-parameter adaptive learning rates. By tracking the exponential moving average of squared gradients (), Adam determines how large gradients have been for each parameter. Parameters with historically large gradients get a compressed step size; parameters with historically small gradients get a boosted step size. This equalization means Adam can train models with very different gradient scales across layers and across dense versus sparse parameters without manual per-layer tuning. The mechanism is essentially a diagonal approximation to the natural gradient, rescaling each parameter's update by the inverse of its typical gradient magnitude.
The second idea is bias-corrected moment initialization. Starting both moment estimates at zero creates a cold-start problem where early updates are either too small (first moment) or divide by an incorrectly small estimate (second moment). The bias correction terms and remove this distortion, making Adam's behavior predictable and stable from the very first update. This is especially important because it makes Adam's early training well-behaved without requiring special initialization, which is one reason the optimizer feels so reliable out of the box.
Together with the momentum-smoothed first moment , which provides direction stability by averaging out gradient noise, these two mechanisms give Adam its characteristic reliability: it converges quickly on a wide range of architectures with minimal hyperparameter tuning, using the default , , .
The history from SGD to momentum to AdaGrad to RMSProp to Adam represents a steady accumulation of understanding about what makes gradients difficult to follow. SGD addressed the basic problem of finding a descent direction. Momentum addressed the noise problem by smoothing that direction. AdaGrad addressed the scale problem by normalizing by gradient history. RMSProp fixed AdaGrad's monotonic decay. Adam combined the smoothing and normalization advances with proper statistical initialization. Each step solved the most obvious remaining failure mode of its predecessor.
The key limitations to keep in mind are memory cost (2x more optimizer state than SGD), the distinction between L2 regularization and true weight decay (use AdamW for language models), potential generalization issues on some vision tasks, and the interactions with mixed-precision and distributed training that require careful configuration. The next chapter covers AdamW, which fixes the weight decay issue and has become the standard optimizer for training transformer models from BERT and GPT through to the most recent large language models.
- Adam maintains per-parameter learning rates by tracking exponential moving averages of gradients (first moment ) and squared gradients (second moment ).
- Bias correction terms and compensate for zero initialization of the moments. This ensures accurate estimates from step one.
- The effective per-parameter learning rate is : large for rarely-updated parameters, small for frequently-updated ones.
- Adam's defaults (, , , ) work well across a wide range of tasks without tuning.
- For language modeling, use AdamW (decoupled weight decay) rather than vanilla Adam. Combine with linear warmup and cosine or inverse-square-root learning rate decay.
- Adam's 2x memory overhead over SGD becomes a real constraint at scale; 8-bit Adam, ZeRO, and Adafactor are the main remedies.
Quiz
Ready to test your understanding? Take this quick quiz to reinforce what you've learned about the Adam optimizer.
Adam Optimizer Quiz
Reference
Citation details
Cite or share this article.
Continue with the full handbook
This chapter is part of Language AI Handbook. Use the handbook page to browse the complete table of contents and continue reading in sequence.
Explore Language AI HandbookStay up to date
Get articles, book updates, and news delivered to your inbox.
No spam, unsubscribe anytime.
Join the community
Sign in to remove popups, track your reading progress, and join the discussion.

Comments
No comments yet. Be the first to share your thoughts!