Learning Rate Decay › Decay Scheduling in Practice
Language AI HandbookAll chapters Learning Rate Decay › Decay Scheduling in PracticeOne section · Sections in this chapter7 / 15 ↓
Reading tools Definitions & inline links
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
The main schedules covered above can be augmented with additional techniques for practical training pipelines. This section covers the remaining standard variants, discusses how to compose multiple schedules, and addresses practical choices that come up repeatedly.
Linear Decay
Linear decay reduces the learning rate in equal steps across training:
where is the total number of training steps. Linear decay is easy to understand and widely used in language model fine-tuning. Many Hugging Face training scripts use linear warmup followed by linear decay as their default schedule.
The key property of linear decay is predictability: the learning rate at any step is a straightforward linear interpolation between the starting and ending values. This makes it easy to reason about training progress. If you are at 50% of total steps, the learning rate is exactly 50% of the initial value. If you extend or shorten training, you can immediately compute the new schedule without any complex parameter relationships.
Linear decay is also well-matched to fine-tuning pre-trained models. In fine-tuning, the goal is to adapt the model to a new task while preserving the general knowledge from pre-training. The learning rate needs to start small enough to not disrupt pre-trained weights too violently, and then decrease further to ensure precise convergence. A linear schedule that starts at a modest learning rate like and decreases to zero over, say, three epochs is exactly this kind of gentle, predictable refinement.
Polynomial Decay
Polynomial decay generalizes both linear and inverse square root decay:
where is the polynomial power and is the final learning rate. Setting gives linear decay. Setting gives a schedule that drops quickly initially and then slows, resembling inverse square root. Setting gives a quadratic curve that drops slowly at first and quickly near the end, the opposite behavior.
The power is rarely tuned in practice because linear and cosine are simpler and well-understood. Polynomial decay appears in some large-scale training recipes where precise control over the decay shape is desired. The key insight is that the exponent controls the skew of the decay: smaller means more reduction happens early, larger means more reduction happens late.
The Floor Matters
Every schedule should have a non-zero minimum learning rate. Without a floor, exponential or inverse square root schedules approach zero asymptotically. At very small learning rates, training effectively stops: gradients are multiplied by a negligible factor and weights barely move. A floor of or ensures that training continues to make at least some progress.
The floor is especially important for schedules used in fine-tuning over many epochs. If you accidentally set too aggressively for an exponential schedule, the learning rate could reach the floor very early in training, wasting compute on steps where the parameters barely change. Monitor the learning rate reached during training as well as the schedule configuration.
Composing Warmup with Decay
Most modern training pipelines compose a warmup phase with one of the main decay schedules. The warmup brings the learning rate from zero to its peak value over the first few hundred or thousand steps, and then one of the decay schedules takes it from the peak down to the floor.
The transition from warmup to decay is handled differently depending on the framework. In some implementations, warmup and decay are separate schedule objects that are chained together. In PyTorch's get_cosine_schedule_with_warmup from the Hugging Face transformers library, the two phases are unified into a single function that changes behavior at the warmup boundary.
def linear_warmup_then_decay(
t, eta_max, warmup_steps, total_steps, decay="linear", eta_end=0.0
):
if t < warmup_steps:
return eta_max * t / warmup_steps
progress = (t - warmup_steps) / max(total_steps - warmup_steps, 1)
if decay == "linear":
return eta_end + (eta_max - eta_end) * max(0.0, 1 - progress)
elif decay == "cosine":
return eta_end + 0.5 * (eta_max - eta_end) * (
1 + np.cos(np.pi * progress)
)
return eta_max
total_steps = 1000
warmup = 100
steps_fine = np.arange(0, total_steps + 1)
linear_schedule = [
linear_warmup_then_decay(
t,
eta_max=0.001,
warmup_steps=warmup,
total_steps=total_steps,
decay="linear",
)
for t in steps_fine
]
cosine_schedule_fine = [
linear_warmup_then_decay(
t,
eta_max=0.001,
warmup_steps=warmup,
total_steps=total_steps,
decay="cosine",
)
for t in steps_fine
]
The two schedules look similar during the main decay phase, but cosine decay is slightly more gradual at the start of decay and slightly steeper near the end. In practice, the difference between them is often smaller than the impact of the peak learning rate or warmup length.