MediumOptimization

Learning Rate Scheduler (Linear Decay)

Optimization

Medium

Problem

Implement a learning-rate schedule with linear warmup followed by linear decay. The step index is zero-based, and the rate remains at the final value after training ends.

\operatorname{LR}(t) = \begin{cases} \eta_0 \dfrac{t}{W}, & W > 0 \text{ and } t < W \\ \eta_0 + \dfrac{t-W}{T-W}(\eta_f-\eta_0), & W \le t < T \\ \eta_f, & t \ge T \end{cases}

Here, t is step, W is warmup_steps, $$ is total_steps, \eta_0 is initial_lr, and \eta_f is final_lr. When total_steps is zero, return final_lr. Return the learning rate as a Python float.

Theory

The learning rate \eta controls how big of a step the optimizer takes at each training iteration:

w_t = w_{t-1} - \eta \cdot g_t

If the learning rate is too high:

If the learning rate is too low:

No single fixed learning rate is ideal for the entire training run. Early on, you want larger steps to make fast progress. Later, you want smaller steps to fine-tune the solution without overshooting. This is why we use learning rate schedulers.


The Warmup Phase

At the very start of training, the model's weights are randomly initialized. The gradients computed from these random weights can be unreliable and have high variance. Taking large steps based on these noisy early gradients can push the model into a bad region of the loss landscape that is hard to recover from.

Warmup solves this by starting with a very small learning rate and gradually increasing it:

During warmup, the model makes cautious, small updates while the optimizer's internal state (like Adam's moment estimates) stabilizes. Once those estimates are reliable, the full learning rate kicks in.

Warmup is especially important for:


Linear Decay

After warmup, the learning rate is at its peak \eta_0. For the rest of training, it decays linearly toward a final value \eta_f (often 0):

Why decay the learning rate?


The Three Phases

A linear schedule with warmup has three distinct phases:

Phase 1: Warmup (step t < W)

The learning rate increases linearly from 0 to \eta_0:

\text{LR}(t) = \frac{t \cdot \eta_0}{W}

For example, with W = 10 and \eta_0 = 0.001:

Phase 2: Decay (W \leq t \leq T)

The learning rate decreases linearly from \eta_0 to \eta_f:

\text{LR}(t) = \eta_f + (\eta_0 - \eta_f) \cdot \frac{T - t}{T - W}

This is a linear interpolation. At t = W, the fraction is \frac{T - W}{T - W} = 1, giving \eta_0. At t = T, the fraction is \frac{0}{T - W} = 0, giving \eta_f.

For example, with W = 10, T = 100, \eta_0 = 0.001, \eta_f = 0:

Phase 3: Post-training (t > T)

The learning rate stays fixed at \eta_f. No further changes.


Linear vs. Other Schedules

Linear decay is the simplest schedule, but there are alternatives:

Linear decay is popular because:


Where This Shows Up

Examples

Example 1

Input
step = 0, total_steps = 100, initial_lr = 0.001, final_lr = 0.0, warmup_steps = 10
Output
0.0
Explanation
The first warmup step uses zero percent of the initial learning rate.

Example 2

Input
step = 10, total_steps = 100, initial_lr = 0.001, final_lr = 0.0, warmup_steps = 10
Output
0.001

Example 3

Input
step = 50, total_steps = 100, initial_lr = 0.001, final_lr = 0.0, warmup_steps = 10
Output
0.000556

Hints

  1. Handle warmup, decay, and completed training as separate branches.
  2. For decay, use (step - warmup_steps) / (total_steps - warmup_steps) as the interpolation fraction.

Requirements

Constraints

Starter Code

def linear_lr(step: int, total_steps: int, initial_lr: float, final_lr: float = 0.0, warmup_steps: int = 0) -> float:
    """
    Returns the learning rate as a float.
    """
    # Write code here
    pass

Test Cases

CaseMatches
Startpublic
End warmuppublic
Mid decayExample 3public