Learning-Rate Schedules
Imagine training a deep network with a single fixed learning rate for a hundred epochs. If the rate is too large, the loss oscillates wildly or diverges. This happens once the network reaches a narrow ravine in the loss surface. If it is too small, training crawls. This wastes compute and GPU-hours before ever reaching a good minimum. The uncomfortable truth is that no single constant learning rate is optimal across the whole training run. The ideal step size early in training is very different from the ideal step size late in training. Early on, weights are far from any minimum. Late in training, the model is fine-tuning inside a narrow basin. Learning-rate schedules resolve this tension. They treat the learning rate not as a fixed hyperparameter but as a function of time. This is a plan that changes the step size as training proceeds. This idea is one of the most consequential and cheap tricks in deep learning. Changing a single scalar's trajectory over training routinely improves final accuracy. It also shortens training time and stabilizes optimization. All this happens without touching the model architecture or the loss function. Nearly every state-of-the-art result in vision, language modeling, and speech uses some nontrivial schedule. This is often combined with a warmup phase and momentum. The big picture is that a learning-rate schedule sits at the interface between optimization theory and practical engineering. Optimization theory tells us that for convex problems, decaying learning rates are necessary for provable convergence. This applies to a stationary point when using noisy stochastic gradients. An example is a rate proportional to . Practice tells us that deep, non-convex loss landscapes have extra quirks. These include sharp regions early in training, plateaus, and sensitivity to the initial few iterations. These quirks motivate warmup and cyclical decay patterns. Theory alone would not predict such patterns. This article builds the schedule toolbox from first principles. It covers step decay, exponential decay, cosine annealing, and warmup. It explains precisely how each is computed, why it helps, and where it can go wrong. It assumes familiarity with Gradient Descent for Deep Learning and its use of momentum. It also sets up later material on Hyperparameter Tuning and Debugging the Training Process. In that material, schedule choice becomes one of the first things to inspect when a model underperforms.
- A learning-rate schedule is a function that changes the step size over training instead of holding it constant.
- Step decay drops by a fixed factor at pre-set milestones; simple, but introduces sudden loss jumps.
- Exponential decay shrinks smoothly and continuously, giving predictable but sometimes overly aggressive decay.
- Cosine annealing follows a smooth cosine curve to zero (or a floor), widely used in modern vision/NLP training.
- Warmup linearly ramps up from near zero at the start, preventing early instability from large gradients.
- Schedules interact with momentum and batch size, and picking one well is a core skill covered further in Hyperparameter Tuning.
The learning rate should be large when the model is far from a good solution and small when it is close, and a schedule is simply an explicit, pre-planned function that implements this idea over training time.
The idea
A learning-rate schedule is a deterministic function mapping the current training step (or epoch) to a learning rate , used in the update rule
where is the (possibly momentum-adjusted) gradient estimate at step . The schedule is fixed before training starts — it does not adapt to the observed loss (that would be an adaptive method like ReduceLROnPlateau, a related but distinct idea) — although some schedules are parameterized by quantities like total number of steps , which must be known or estimated in advance. The reason a schedule helps is rooted in the shape of the loss landscape and the statistics of stochastic gradients. Early in training, the model is typically far from any minimum, gradients tend to be large and informative, and a larger step size lets the model make rapid progress across broad, low-curvature regions. Late in training, the model is near a minimum (or a flat basin containing one), the loss surface is locally more like a bowl with possibly high curvature in some directions, and stochastic gradient noise (from mini-batching) has variance that does not shrink as training proceeds. A large learning rate at this stage causes the parameters to bounce around the bottom of the bowl instead of settling into it — the update variance, roughly , must shrink for the iterate to converge rather than oscillate around a noise floor. Formally, in stochastic approximation theory (Robbins–Monro conditions), a decaying schedule satisfying
is sufficient (together with standard smoothness and noise assumptions) for SGD iterates to converge almost surely to a stationary point of the objective. The first condition ensures the schedule does not decay so fast that the algorithm cannot travel arbitrarily far (it must be able to escape a bad starting point and reach any region of the space). The second ensures the accumulated noise injected by stochastic gradients is summable, so the random fluctuations eventually die out and the sequence stabilizes. A constant learning rate satisfies neither condition properly: it can travel infinitely far (, fine) but the noise term diverges too, meaning the iterate never truly settles — it converges only to a noise ball around the minimum whose radius is proportional to . Schedules like decay or step decay satisfy both conditions and hence give a much stronger convergence guarantee, at the cost of needing to travel less late in training. Deep learning practice departs from this classical picture in three important ways, motivating the specific schedules covered here. First, training runs for a finite, known horizon , so schedules can be shaped to hit a target minimum learning rate exactly at (as cosine annealing does) rather than decaying forever. Second, the very first few hundred to thousand iterations are often the most fragile part of training — activations and gradient scales have not yet equilibrated, and a full-sized learning rate can cause instability or even divergence — motivating warmup, a short initial phase where ramps up rather than down. Third, non-convexity means the "right" trajectory of is partly empirical: cosine schedules, for instance, were popularized less by a convergence proof and more by consistently strong empirical results across architectures.
How it works
We now specify each schedule precisely as a function of the training step (or epoch ), assuming a base learning rate and, where relevant, a total training horizon .
Step decay multiplies by a fixed factor (commonly or ) every time training crosses one of a fixed set of milestone epochs :
So is a piecewise-constant staircase: full rate until , then until , then , and so on.
Exponential decay shrinks the learning rate smoothly and continuously at every step, governed by a decay rate :
for a decay factor applied every steps. Unlike step decay, there are no discontinuities — the rate glides down monotonically at every single update.
Cosine annealing follows one half-period of a cosine curve from down to a floor (often ) over the horizon :
At this gives ; at it gives . The curve decays slowly at first, drops fastest through the middle of training, and flattens out again as it approaches — an S-shaped decay that spends more time near both the top and the bottom than a linear ramp would. A common variant, cosine with warm restarts (SGDR), resets to periodically, creating a sawtooth of cosine cycles used to encourage the optimizer to explore multiple basins and produce ensembles of snapshots.
Warmup is not a full schedule on its own but a short prefix, typically the first steps, during which ramps linearly (or sometimes with a low-order polynomial) from a small value (often ) up to :
After , control passes to a main schedule (step, exponential, or cosine) that takes over with as its own starting value. Warmup is essential for very deep networks, large batch sizes, and Transformer architectures, where the raw gradient signal at initialization can be poorly scaled and a full-size step causes the loss to spike or the optimizer's momentum/adaptive-moment estimates (as in Adam) to lock onto a bad early direction. ```
# Generic training loop with a pluggable learning-rate schedule.
# schedule(t) returns eta_t given the current global step t. function TRAIN(model, data, T, schedule, momentum_beta): velocity = zeros_like(model.parameters) t = 0 for epoch in 1..num_epochs: for batch in data: g = compute_gradient(model, batch) # stochastic gradient estimate eta_t = schedule(t) # <-- the only thing that changes velocity = momentum_beta velocity + g model.parameters -= eta_t velocity t = t + 1 return model.parameters
# --- Schedule implementations --- function STEP_DECAY(t, eta0, gamma, milestones): k = count(m in milestones such that m <= t) return eta0 gamma^k function EXPONENTIAL_DECAY(t, eta0, gamma, step_size): return eta0 gamma^(t / step_size) function COSINE_ANNEALING(t, eta0, eta_min, T): t_clamped = min(t, T) # hold at eta_min after t = T cos_term = cos(pi t_clamped / T) return eta_min + 0.5 (eta0 - eta_min) (1 + cos_term) function WARMUP_THEN(t, T_warmup, eta0, main_schedule): if t < T_warmup: return eta0 (t + 1) / T_warmup # linear ramp, avoids eta=0 at t=0 else: return main_schedule(t - T_warmup) # main schedule restarts its clock at 0 ``` Each `schedule(t)` function is a pure function of the step counter, queried once per update; it adds negligible overhead ( time, no extra memory) compared to the gradient computation itself.
Complexity & correctness
Time and space complexity. Every schedule discussed is time and extra space per step: computing requires a handful of arithmetic operations (a power, an exponential, or a cosine evaluation) independent of model size, dwarfed by the cost of the gradient step itself. Storing the schedule requires only a few scalars (, , milestones, or ), not per-parameter state — this is in contrast to adaptive optimizers like Adam, which maintain auxiliary state. Consequently, a schedule is essentially "free": it changes what number multiplies the gradient, not the cost of computing that gradient. The only indirect cost is that a poorly chosen schedule can lengthen the number of steps needed to converge, which is a statistical/optimization cost, not a computational one per step.
Correctness argument for decaying schedules. Consider SGD on a smooth, possibly non-convex objective with -Lipschitz-continuous gradient, using unbiased stochastic gradients with bounded variance . A standard descent-lemma argument gives, for one step with learning rate ,
Summing over and rearranging, the average squared gradient norm is bounded by a telescoping term (which is finite because is bounded below) plus a noise term proportional to . For the right-hand side to vanish as , we need (so the useful first term's denominator grows, letting us divide it out and still get a shrinking bound) while (so the accumulated noise stays bounded). This is exactly the Robbins–Monro condition stated earlier. Step decay, exponential decay (over a finite horizon, effectively), and cosine annealing (whose learning rate integrates to a finite but large value and whose squared values are summable since it is bounded and eventually near zero) all satisfy versions of this condition in the finite-horizon setting used in practice, giving a principled reason they outperform a constant rate on the same total noise budget.
Why warmup does not break this. Warmup only affects a short prefix of steps out of a much longer horizon ; since in practice, its contribution to and is negligible, and it does not change the asymptotic convergence argument. Its benefit is a separate, more practical one: at initialization, the loss landscape's local curvature and gradient scale are poorly conditioned (weights have not adapted to the data distribution), and using the full immediately can cause an update large enough to move parameters into a region where gradients blow up (particularly acute for deep networks, large batch sizes, or Transformer layer-normalization interactions). Ramping up avoids taking a large step until the local geometry has stabilized somewhat, after which the full learning rate is safe.
Honest tradeoffs.
- Step decay is simple and interpretable but the milestone epochs are extra hyperparameters that must be tuned per dataset/architecture; a badly placed milestone wastes epochs at a rate that is already too small, or drops too late and never lets the model settle.
- Exponential decay has just one decay-rate hyperparameter but can decay the rate to near-zero well before training ends if is even slightly too aggressive, effectively freezing learning early.
- Cosine annealing needs the total horizon known in advance; extending training (e.g., resuming with more epochs later) requires recomputing the schedule, unlike step or exponential decay which naturally continue.
- Warmup adds one more hyperparameter () and, for very short training runs, can consume a nontrivial fraction of the whole budget in what is initially near-zero learning rate.
Worked example
Consider training a network with base learning rate , total horizon epochs, warmup length epochs, and each schedule's own decay parameters, tracked epoch by epoch.
| Epoch | Step decay (, milestones ) | Exponential (/epoch) | Cosine () | Warmup + Cosine |
|---|---|---|---|---|
| 0 | 0.1000 | 0.1000 | 0.1000 | 0.0200 |
| 1 | 0.1000 | 0.0960 | 0.0998 | 0.0400 |
| 5 | 0.1000 | 0.0783 | 0.9755·0.1 = 0.0951 | 0.1000 (warmup ends) |
| 29 | 0.1000 | 0.0294 | 0.5714 | 0.5626 (cosine restarts clock at e=5) |
| 30 | 0.0100 | 0.0283 | 0.5556 | 0.5460 |
| 59 | 0.0100 | 0.0088 | 0.0245 | 0.0206 |
| 60 | 0.0010 | 0.0085 | 0.0219 | 0.0182 |
| 89 | 0.0010 | 0.0026 | 0.0001 | 0.00005 |
| 99 | 0.0001 | 0.0018 | ≈0.0000 | ≈0.0000 |
Reading this table: step decay stays completely flat at until epoch , then instantaneously drops to — a shock that typically causes a visible bump in training loss curves (the model was still making large steps and suddenly its exploration radius shrinks). Exponential decay is already down to about by epoch and continues shrinking every single epoch without any jump, reaching (below step decay's final value) by epoch — for this , it decays faster in the long run than the -stage step schedule with . Cosine annealing starts identically to the others at , decays slowly for the first fifth of training, drops through its steepest region in the middle third, and flattens smoothly to nearly zero by epoch , with no discontinuities anywhere. The warmup+cosine column shows the ramp from at epoch to the full at epoch (note is not exactly zero — using avoids a literal zero-rate first step which would produce no update at all — a common off-by-one detail), after which cosine annealing takes over with its clock reset to zero at that point, following the same S-shape but over the remaining epochs. A second, smaller trace makes the discontinuity of step decay concrete on a per-batch loss curve. Suppose at epoch , just before the first milestone, the running loss is and decreasing steadily by about per epoch. At epoch , the learning rate drops ; empirically the loss often increases slightly for one or two epochs (say to ) because the optimizer's momentum buffer, built for the old step size, briefly overshoots relative to the new tiny step size before re-equilibrating, then resumes a steeper, more stable decrease than before the drop (because a smaller step size lets it settle into finer local structure). This transient bump-then-improve pattern is the classic visual signature of a step-decay milestone in a training log, and recognizing it is a key diagnostic skill developed further in Debugging the Training Process.
Common pitfalls & traps
| Pitfall | Why it happens | Fix |
|---|---|---|
| Setting cosine's to the wrong total step count | Confusing epochs with mini-batch steps, or forgetting gradient accumulation multiplies effective steps | Compute as `steps_per_epoch × num_epochs`, verified against the actual training loop's step counter |
| No warmup with a large batch size or Transformer architecture | Assuming any schedule works the same regardless of batch size or model depth | Add a short linear warmup (hundreds to a few thousand steps) whenever batch size or depth is large |
| Step-decay milestones tuned once and reused on a new dataset | Milestones are dataset/architecture-specific; blindly copying them mis-times the drops | Re-tune milestones (or switch to cosine, which needs only the horizon ) per new setup |
| Forgetting the schedule resumes from step on checkpoint restart | Restarting training re-initializes the step counter instead of continuing it | Persist and restore the global step counter alongside model weights |
| Exponential decay rate too aggressive | A tuned per-step accidentally applied per-epoch (or vice versa), causing to vanish almost immediately | Always state and check the unit of decay (per-step vs per-epoch) explicitly |
| Combining warmup with cosine but double-counting the warmup steps in | Off-by-one: including or excluding inconsistently when computing the cosine phase length | Explicitly define the cosine phase as steps, with its own clock starting at right after warmup |
Pioneers & history
The theoretical foundation for decaying step sizes predates deep learning by decades, rooted in stochastic approximation theory developed for sequential statistical estimation.
Origins · 1 minHerbert Robbins and how this was discovered
Where this matters
Learning-rate schedules are embedded in essentially every major deep learning framework and training recipe used in industry and research. Computer vision training pipelines (ResNet, EfficientNet, and successors) historically relied on step decay with milestones tied to a fixed epoch budget, while more recent vision and self-supervised recipes default to cosine annealing, often with a linear warmup of a few epochs. Large language model training — GPT-style and encoder-only Transformer pretraining alike — universally uses a warmup phase (typically thousands of steps) followed by either linear or cosine decay to a small floor value, a recipe now considered essential rather than optional for stable large-batch, large-model training. Reinforcement learning and speech recognition pipelines frequently borrow the same cosine-with-warmup recipe, and hyperparameter-search tools (as covered in Hyperparameter Tuning) often treat the schedule family and its parameters — not just a single learning rate — as part of the search space. Cloud training frameworks (e.g., learning-rate scheduler modules in major deep learning libraries) implement all four schedules described here as first-class, composable objects, reflecting how central this simple idea has become to reliably training deep networks at scale.
Ask the tutor: How would you design a learning-rate schedule for a training run whose total number of steps isn't known in advance?
Why not just tune a single constant learning rate really carefully instead of using a schedule?
A carefully tuned constant learning rate can only be a compromise between the needs of early and late training. If it is small enough to avoid oscillation once near a minimum, it will be too small to make fast progress early on, wasting compute. If it is large enough for fast early progress, it will keep injecting noise-proportional-to- near the minimum, forcing the iterate to hover in a "noise ball" rather than settling, which the Robbins–Monro analysis makes precise. A schedule decouples these two needs by allowing large steps early and small steps late, typically reaching a better final loss in the same or fewer total steps than any single constant rate could achieve.
How do I choose between step decay, exponential decay, and cosine annealing in practice?
Step decay is a reasonable default when you have strong prior knowledge of the right epochs to decay at, from prior experiments on a similar model/dataset, and want interpretable, easily-explained schedules for a report or comparison. Exponential decay is convenient when you want smoothness with a single hyperparameter and do not know the total training horizon in advance. Cosine annealing is the most common modern default because it needs only the total horizon , decays smoothly with no jumps, and has been empirically validated across many architectures and tasks; when in doubt, cosine with a short warmup is a strong starting point.
Does warmup need to be linear, or can it use other shapes?
Linear warmup, ramping from near zero to over steps, is the most common choice because it is simple and works well in practice. Some recipes use other monotonically increasing shapes, such as a low-order polynomial or an exponential ramp, but the empirical difference from linear warmup is usually small as long as the warmup duration is chosen well. What matters most is that the ramp is smooth and gradual (avoiding a large immediate jump) and that is long enough for the model's activation and gradient statistics to stabilize before the full learning rate is applied.
How does a learning-rate schedule interact with momentum or Nesterov acceleration?
Momentum accumulates a running velocity from past gradients, effectively amplifying the influence of the current learning rate over several subsequent steps rather than just one. When changes abruptly (as in step decay), the velocity buffer, calibrated to the old step size, briefly produces update magnitudes inconsistent with the new learning rate, which is why step-decay milestones sometimes show a small transient bump in the loss curve before improvement resumes. Smooth schedules like cosine or exponential decay avoid this mismatch because the learning rate at each step is close to the learning rate at the previous step, keeping the momentum buffer roughly consistent with the current step size throughout training.
Can a schedule ever hurt training compared to a well-chosen constant rate?
Yes. If the schedule decays too fast relative to the amount of progress the model still needs to make, it effectively freezes learning prematurely, leaving the model undertrained even though the loss curve looks "converged" simply because the steps became too small to move the parameters meaningfully. This is common with an overly aggressive exponential decay rate or step-decay milestones placed too early. Conversely, a schedule that decays too slowly behaves almost like a constant rate and forfeits the fine-tuning benefit near the minimum. Schedule hyperparameters therefore still require tuning, which is why schedule choice is itself a subject of Hyperparameter Tuning.
Is a learning-rate schedule the same thing as an adaptive optimizer like Adam?
No, though they are complementary. A schedule is a pre-planned function fixed before training begins and applied uniformly to all parameters. An adaptive optimizer like Adam or RMSProp instead adjusts an effective per-parameter step size on the fly, based on running statistics of each parameter's own gradient history, without knowing the future training horizon. In practice, Adam is very commonly combined with an external schedule (often warmup plus cosine or linear decay) applied on top of its base learning rate, so the two mechanisms operate at once rather than being alternatives to each other.
Why does cosine annealing need to know the total number of training steps in advance, and what happens if I train longer than planned?
Cosine annealing is defined as one half-period of a cosine curve stretched exactly across , so it is constructed to hit precisely at the last planned step; this requires as an input parameter. If you decide to train longer than the originally planned , the naive schedule will have already reached (often near zero) and stay there, effectively halting learning for any extra steps. The usual fixes are to either recompute the schedule with a new, larger from the start of a fresh run, or use warm restarts (SGDR), which periodically resets the cosine cycle so training can be extended in discrete chunks without redefining a single fixed horizon.