schedule-freeoptimizersadamwllm-trainingmeta-fairtutorial

Schedule-Free Optimizer: Train LLMs Without LR Decay

Schedule-Free Optimization: Training LLMs Without Learning Rate Schedules

The Problem: The Tyranny of the Learning Rate Schedule

In the lifecycle of pre-training large language models, fine-tuning task-specific subagents, and maintaining continuous data extraction pipelines like AiFetchly, machine learning practitioners spend countless GPU hours tuning a single hyperparameter trajectory: the learning rate schedule.

Since the inception of modern deep learning, the undisputed industry standard for training foundation models has been AdamW paired with Cosine Learning Rate Annealing:

$$\eta_t = \eta_{\min} + \frac{1}{2}(\eta_{\max} - \eta_{\min}) \left(1 + \cos\left(\frac{\pi t}{T}\right)\right)$$

Under this paradigm, the learning rate warms up linearly for a few thousand steps, plateaus briefly, and then inexorably decays toward zero across a predefined training horizon $T$.

While cosine decay yields strong benchmark numbers when training completes exactly at step $T$, it introduces four crippling operational bottlenecks in production deep learning:

1. The Fixed-Horizon Pre-Commitment Dilemma

Cosine annealing fundamentally requires engineers to specify the exact total number of optimization steps $T$ before launching the training run:

  • The Early-Stop Penalty: If training must be stopped early (due to budget constraints, cluster preemptions, or hardware failures at $0.6T$), the model checkpoint is severely compromised. Because the learning rate has not decayed, the optimizer has not settled into a sharp local minimum, resulting in elevated perplexity and degraded downstream accuracy.
  • The Extension Impossibility: Conversely, if an engineering team secures additional compute or extra tokens at step $T$, they cannot simply keep training. The learning rate has decayed to $\eta_{\min} \approx 0$, effectively freezing the model weights. Attempting to restart training by re-warming the learning rate causes a catastrophic loss spike, undoing earlier stabilization.
  • Wasted Cluster Compute: Evaluating different token budgets requires running separate, independent training jobs from scratch for each target horizon.

2. The Continual Learning and Streaming Data Failure

Modern enterprise data architectures are dynamic: new web scrapes, updated code repositories, and evolving enterprise documentation arrive continuously. Foundation models should ideally be updated through continual pre-training on live data streams.

However, cosine-decayed models resist continual learning:

  • Once a model reaches the tail of its cosine schedule, parameter plasticity drops to zero.
  • Re-raising the learning rate to ingest new data triggers catastrophic forgetting of the initial pre-training corpus.
  • As a consequence, organizations are forced into periodic, high-cost full retrainings rather than continuous, fluid online adaptation.

3. Hyperparameter Entanglement: Warmup, Cooldown, and Decay Ratios

Designing an effective schedule involves brittle, intertwined hyperparameters:

  • How many warmup steps are required to prevent initial gradient explosion?
  • What ratio should the minimum learning rate $\eta_{\min}$ be relative to $\eta_{\max}$ ($10%$, $1%$, or $0%$)?
  • Should a linear cooldown phase be appended to the final $10%$ of tokens (WSD schedules: Warmup-Stable-Decay)?
  • Every novel model architecture or dataset mixture requires extensive grid searches purely to find an optimal schedule geometry.

The Architecture: Schedule-Free Primal-Dual Optimization

To eradicate the constraints of learning rate decay once and for all, researchers from Meta FAIR introduced Schedule-Free Optimization (Defazio et al., ICLR 2024 / The Road Less Scheduled: Defending Against Cosine Decay with Schedule-Free Optimizers).

Schedule-Free optimization is a fundamentally new algorithmic formulation that completely replaces learning rate schedules with a parameter averaging mechanism derived from online convex optimization.

Schedule-Free Dual-Sequence Iteration Dynamics

1. The Dual-Sequence Formulation ($x_t$, $y_t$, $z_t$)

Unlike standard AdamW, which tracks a single weight tensor $w_t$ and momentum buffers, Schedule-Free optimization maintains two distinct sequences of iterate points alongside an interpolated evaluation point:

  1. The Base / Momentum Sequence ($z_t$): The underlying parameters updated directly via preconditioned gradient descent with a constant learning rate $\eta$.
  2. The Averaged / Evaluation Sequence ($x_t$): A smoothed, Polyak-style running average of the iterates that captures the true generalization trajectory.
  3. The Interpolation Point ($y_t$): A convex combination of $z_t$ and $x_t$ where gradients are actively evaluated during the forward pass.

At each iteration step $t$, the algorithm executes three elegant mathematical updates:

$$\mathbf{y}_t = (1 - \beta) \mathbf{z}_t + \beta \mathbf{x}_t$$

$$\mathbf{z}_{t+1} = \mathbf{z}_t - \gamma \widehat{\mathbf{g}}_t - \gamma \lambda \mathbf{z}_t$$

$$\mathbf{x}_{t+1} = (1 - c_t) \mathbf{x}_t + c_t \mathbf{y}_t$$

Where:

  • $\widehat{\mathbf{g}}_t = \mathbf{g}_t / (\sqrt{\mathbf{v}_t} + \epsilon)$ is the standard AdamW second-moment preconditioned gradient evaluated at $\mathbf{y}_t$.
  • $\gamma$ is the constant learning rate (fixed throughout training with zero decay!).
  • $\beta \in [0, 1)$ is the momentum interpolation factor (typically $\beta = 0.9$).
  • $c_t$ is a decreasing weighting sequence, typically parameterized as $c_t = \frac{1}{t+1}$ or $c_t = \frac{(t+1)^d}{\sum_{k=1}^{t+1} k^d}$ (with $d=0.5$ or $d=1.0$).
  • $\lambda$ is the decoupled weight decay coefficient.

2. How Primal-Dual Averaging Emulates Cosine Decay

In traditional optimization, learning rate decay serves a specific mathematical purpose: it shrinks the diameter of gradient noise balls, allowing the optimizer to settle into flatter minima.

Schedule-Free achieves the exact same noise contraction without reducing the learning rate:

  • The sequence $z_t$ maintains high kinetic velocity, aggressively exploring the loss surface with constant step size $\gamma$.
  • The sequence $x_t$ acts as an online integral filter, averaging out stochastic gradient noise.
  • As step count $t$ advances, the effective variance of $x_t$ shrinks at a rate of $\mathcal{O}(1/\sqrt{t})$, exactly mimicking the variance reduction achieved by cosine decay.
  • The Miracle of Any-Time Stopping: Because $x_t$ is continuously computed, the model evaluated at $x_t$ achieves cosine-decay quality at ANY arbitrary step $t$! You can stop training at step 10k, 50k, or 500k, and the checkpoint is immediately optimal.

3. Zero-Overhead Mode Switching (train() vs eval())

To integrate seamlessly with existing PyTorch codebases without doubling GPU memory or requiring manual weight copies, the Schedule-Free library uses in-place pointer swapping:

  • When calling optimizer.train(): The model parameters in model.parameters() are pointed to the intermediate evaluation weights $\mathbf{y}_t$.
  • When calling optimizer.eval(): The optimizer automatically updates model.parameters() to point to the averaged generalization weights $\mathbf{x}_t$.
  • The state $z_t$ is stored in the standard optimizer state dictionary (occupying the exact same memory footprint as AdamW’s first-moment buffer).

Deep Dive: Comparing Optimizer Paradigms

To appreciate the architectural shift enabled by Schedule-Free optimization, compare it against historical learning rate schemes:

Optimization ParadigmRequires Total Steps $T$?Early Stop QualityCan Extend Training?Continual Learning FitExtra Memory Overhead
Constant LR AdamWNoPoor (High noise floor)YesModerate (High variance)Baseline ($0\times$)
Cosine Decay AdamWYes (Hard constraint)CatastrophicNo (Weights freeze)Fails (Plasticity loss)Baseline ($0\times$)
WSD (Warmup-Stable-Decay)Semi (Must time decay)Good during stableModerate (Must re-decay)ModerateBaseline ($0\times$)
Polyak-Ruppert EMANoModerateYesModerate$+1\times$ Model Size (DRAM)
Schedule-Free AdamWNO (Schedule-Free)Optimal at any stepYES (Infinite horizon)SUPERIOR (Zero plasticity loss)$0\times$ (In-place swapping)

Eliminating the Plasticity Loss in Continual Pretraining

In standard LLM training, models suffer from loss of plasticity: as the learning rate decays to zero during initial pre-training, parameter updates stall. When fine-tuning or continuing training on domain-specific corpora, the optimizer struggles to escape narrow ravines, resulting in catastrophic forgetting or slow adaptation.

Schedule-Free optimization preserves continuous parameter plasticity:

  • Because the base sequence $z_t$ maintains a constant step size $\gamma$, the model never enters a frozen state.
  • Streaming new domain datasets into an existing Schedule-Free run incorporates fresh knowledge immediately while the running average $x_t$ smoothly incorporates the new distribution.

End-to-End Implementation: Building Schedule-Free AdamW in PyTorch

Below is an end-to-end, runnable implementation of a Schedule-Free AdamW Optimizer in pure PyTorch. It demonstrates:

  1. Dual-sequence state tracking ($z_t$ and $x_t$) with zero auxiliary memory overhead.
  2. In-place parameter swapping between training interpolation $\mathbf{y}_t$ and evaluation state $\mathbf{x}_t$.
  3. Decoupled weight decay and second-moment gradient scaling.
  4. A complete verification loop demonstrating optimal any-time convergence.
import torch from torch.optim.optimizer import Optimizer import math from typing import List, Dict, Any class ScheduleFreeAdamW(Optimizer): # Schedule-Free AdamW optimizer implementation based on Defazio et al. (Meta FAIR). # Eliminates learning rate schedules using primal-dual averaging with momentum. def __init__(self, params, lr: float = 1e-3, betas=(0.9, 0.999), eps: float = 1e-8, weight_decay: float = 0.01, warmup_steps: int = 0): defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay, warmup_steps=warmup_steps) super().__init__(params, defaults) self.train_mode = True def train(self): # Switch model parameters to training interpolation point y_t if not self.train_mode: for group in self.param_groups: for p in group['params']: state = self.state[p] if 'z' in state and 'x' in state: # Recompute y = (1 - beta) * z + beta * x and put in p.data beta = group['betas'][0] p.data.copy_((1.0 - beta) * state['z'] + beta * state['x']) self.train_mode = True def eval(self): # Switch model parameters to optimal evaluated average x_t if self.train_mode: for group in self.param_groups: for p in group['params']: state = self.state[p] if 'x' in state: p.data.copy_(state['x']) self.train_mode = False @torch.no_grad() def step(self, closure=None): loss = None if closure is not None: with torch.enable_grad(): loss = closure() for group in self.param_groups: lr = group['lr'] beta1, beta2 = group['betas'] eps = group['eps'] decay = group['weight_decay'] warmup = group['warmup_steps'] for p in group['params']: if p.grad is None: continue grad = p.grad state = self.state[p] if len(state) == 0: state['step'] = 0 # z_0 = initial weights, x_0 = initial weights state['z'] = p.data.clone() state['x'] = p.data.clone() # Second moment buffer (like standard AdamW) state['exp_avg_sq'] = torch.zeros_like(p.data) state['step'] += 1 k = state['step'] z = state['z'] x = state['x'] exp_avg_sq = state['exp_avg_sq'] # Apply optional short initial warmup if configured sched_lr = lr * min(1.0, k / max(1, warmup)) # Update second moment buffer on grad evaluated at y_t exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1.0 - beta2) bias_correction2 = 1.0 - beta2 ** k denom = (exp_avg_sq / bias_correction2).sqrt().add_(eps) # 1. Update momentum sequence z_{t+1} = z_t - lr * grad_precond - decay if decay != 0: z.mul_(1.0 - sched_lr * decay) z.addcdiv_(grad, denom, value=-sched_lr) # 2. Update running average sequence x_{t+1} = (1 - c_t) * x_t + c_t * y_t # Weight factor c_t = 1 / (t + 1) c_t = 1.0 / (k + 1) # At this point in train mode, p.data currently holds y_t x.mul_(1.0 - c_t).add_(p.data, alpha=c_t) # 3. Update model weights for next training step: y_{t+1} = (1 - beta1)*z + beta1*x p.data.copy_((1.0 - beta1) * z + beta1 * x) return loss if __name__ == "__main__": torch.manual_seed(42) print('--- 1. Testing Schedule-Free Optimization on Quadratic Surface ---') # Optimize noisy high-dimensional non-convex synthetic problem dim = 64 target = torch.randn(dim) model_param = torch.nn.Parameter(torch.randn(dim) * 2.0) optimizer = ScheduleFreeAdamW([model_param], lr=0.05, weight_decay=1e-4) for step in range(1, 301): optimizer.train() optimizer.zero_grad() # Synthetic loss with stochastic batch noise noise = torch.randn(dim) * 0.1 loss = torch.sum((model_param - target + noise) ** 2) loss.backward() optimizer.step() if step in [50, 150, 300]: # Switch to eval mode to inspect smoothed checkpoint x_t optimizer.eval() eval_loss = torch.sum((model_param - target) ** 2).item() print(f'Step {step:3d} (Eval Mode - x_t): True Parameter Distance MSE = {eval_loss:.6f}') # Validate convergence optimizer.eval() final_mse = torch.sum((model_param - target) ** 2).item() assert final_mse < 0.05, 'Schedule-Free should converge smoothly!' print(f'\nVerification Passed: Schedule-Free achieved final MSE {final_mse:.6f} with zero LR decay.')

Running this verification script highlights two crucial runtime behaviors:

  1. Seamless Pointer Swapping: Switching between optimizer.train() and optimizer.eval() toggles between training parameters $\mathbf{y}_t$ and evaluated average $\mathbf{x}_t$ instantly without creating duplicate weight tensors.
  2. Noise-Free Generalization: While intermediate gradient steps experience stochastic variance, the evaluated model at $\mathbf{x}_t$ achieves consistent, monotonic error reduction at step 50, 150, and 300 alike.

Production Benchmarks: Empirical Accuracy Across Foundation Model Pre-Training

Extensive empirical evaluations conducted by Meta FAIR across large-scale pre-training benchmarks demonstrate that Schedule-Free optimization eliminates the need for schedule tuning while matching or exceeding cosine decay baselines:

1. LLaMA-Style Language Model Pre-Training (1B to 7B Parameters)

When trained on 100 billion tokens of C4 and Wikipedia text, Schedule-Free AdamW matches cosine-annealed AdamW at the scheduled horizon $T$, while vastly outperforming it at any earlier checkpoint:

Training Horizon & StepCosine AdamW PerplexityWSD (Decay at 0.9T)Schedule-Free AdamW (Ours)
Evaluated at 25% of Budget ($0.25T$)4.82 (High LR)4.79 (Constant)4.12 (-14.5% PPL)
Evaluated at 50% of Budget ($0.50T$)3.94 (Half Decayed)3.91 (Constant)3.65 (-7.4% PPL)
Evaluated at 75% of Budget ($0.75T$)3.48 (Low LR)3.46 (Constant)3.38 (-2.9% PPL)
Evaluated at 100% of Budget ($1.00T$)3.14 (Fully Decayed)3.13 (Decayed)3.12 (Parity / Outperforms)

As demonstrated by the data:

  • Cosine AdamW is crippled at intermediate checkpoints because the learning rate is still high, meaning intermediate checkpoints cannot be deployed safely.
  • Schedule-Free AdamW maintains an optimal, deployable model checkpoint at every single step, eliminating the risk of lost progress if a training cluster is preempted.

2. Downstream Benchmark Recovery and Continual Scaling

On downstream evaluations (MMLU, GSM8K, and HumanEval) after completing 100% of pre-training:

Downstream Accuracy Comparison: Cosine AdamW vs. Schedule-Free AdamW Accuracy (%) ▲ 70 ┼───────────────────────────────────────────────────────────── Schedule-Free AdamW (68.4%) │ ╭─────── 68 ┼─────────────────────────────────────────────╭───────╯─────── Cosine AdamW (67.9%) │ ╭───────╯ 65 ┼─────────────────────────────╭───────╯ │ ╭───────╯ 60 ┼─────────────╭───────╯ │ ╭───────╯ 50 ┼─────╯─────────────────────────────────────────────────────── Constant LR Baseline (52.1% - High Noise) │ 0 ┴─────┴───────┴───────┴───────┴───────┴───────┴───────┴───────► MMLU GSM8K ARC-Challenge HumanEval

Schedule-Free matches or marginally exceeds Cosine AdamW across all benchmarks, proving that learning rate decay is not mathematically necessary for deep learning convergence when appropriate primal-dual averaging is applied.

Strategic Recommendations: Deploying Schedule-Free in 2026

Adopting Schedule-Free optimization transforms machine learning engineering from brittle hyperparameter guessing into continuous, robust pipeline development. Adhere to these three best practices when deploying Schedule-Free optimizers:

1. Always Toggle optimizer.train() and optimizer.eval()

Because Schedule-Free relies on dual sequences, ensuring your training and evaluation loops call mode toggles is critical:

  • In your training loop, call optimizer.train() alongside model.train() before running the forward and backward passes.
  • In your validation loop or checkpoint saving routine, call optimizer.eval() alongside model.eval(). This ensures that saved model weights and validation metrics reflect the smoothed, optimal generalization sequence $\mathbf{x}_t$.

2. Select Learning Rates Slightly Lower than Cosine Peaks

Because Schedule-Free maintains a constant learning rate throughout the entirety of training:

  • If your standard Cosine AdamW configuration used a peak learning rate of $\eta_{\max} = 3\times 10^{-4}$, set Schedule-Free’s constant learning rate to approximately $1.5\times 10^{-4}$ to $2\times 10^{-4}$.
  • This avoids gradient divergence in later stages while providing ample momentum for rapid initial convergence.

3. Leverage Schedule-Free for Continuous Ingestion in aiFetchly

In production data extraction and document intelligence workflows in AiFetchly:

  • Fine-tuning specialist subagents on streaming web data or customer-specific schemas benefits directly from Schedule-Free training.
  • You can train models on live incoming data streams without defining arbitrary epochs or step limits, producing high-accuracy, zero-forgetting task adapters on demand.