Schedule-Free Learning in PyTorch

repository·main·Indexed 25 days ago

https://github.com/facebookresearch/schedule_free

A PyTorch library providing optimizer implementations that eliminate the need for manual learning rate schedules through interpolation and averaging. It includes schedule-free variants of SGD, AdamW, and RAdam, as well as the AdamCScheduleFreePlusPaper research implementation. The library offers standard, closure-based, and reference versions of optimizers, along with a ScheduleFreeWrapper to apply schedule-free logic to any base optimizer.

Tokens
9.5K
Snippets
11
Records
51
Agent score
80%

What's inside schedulefree

  1. Hyperparameter tuning and best practices

    main

    When using Schedule-Free learning, consider the following guidance:

    • Learning Rate Warmup: Highly recommended. Use the warmup_steps parameter.
    • Momentum ($\beta$): Training is sensitive to $\beta$. The default is $0.9$, but for very long training runs, you may need to increase this to $0.95$ or $0.98$.
    • SGD Learning Rates: A good starting point is $10x$ to $50x$ larger than classical rates.
    • AdamW Learning Rates: A good starting point is $1x$ to $10x$ larger than schedule-based approaches.
    • Regularization: This method requires tuning; it may not outperform scheduled approaches without proper tuning of regularization and learning rate parameters.
  2. Run the Basic MNIST Example

    main

    To run the MNIST example provided in this repository, install the required dependencies and execute the main.py script. You can specify a specific GPU using the CUDA_VISIBLE_DEVICES environment variable.

    pip install -r requirements.txt
    python main.py
    # To specify a GPU ID (e.g., 2):
    # CUDA_VISIBLE_DEVICES=2 python main.py
  3. How to use Schedule-Free optimizers in training loops

    main

    Because Schedule-Free optimizers use two different points for gradient calls and test/val loss calculations, you must switch the parameter buffer between the two during training.

    To do this, call optimizer.train() at the same time you call model.train(), and optimizer.eval() at the same time you call model.eval(). Additionally, place the optimizer in eval() mode when storing checkpoints.

    If your training loop supports PyTorch Optimizer step closures, you can use the ScheduleFreeClosure versions of the optimizers, which do not require these manual .train() and .eval() calls.

  4. Switch between training and evaluation modes in AdamWScheduleFreeReference

    main

    The optimizer manages internal weight sequences (x, y, and z) through its train() and eval() methods:

    • .train(): Switches the optimizer to training mode. It restores the model parameters p from the y sequence (the sequence used during training steps).
    • .eval(): Switches the optimizer to evaluation mode. It updates the model parameters p by copying the x sequence (the averaged weights) into the parameters. This is the state used for inference/evaluation.

    Warning: If you call .step() without having called .train() first, the optimizer will raise an Exception.

  5. How AdamWScheduleFree manages train and eval modes

    main

    Unlike standard PyTorch optimizers, AdamWScheduleFree uses the .train() and .eval() methods to perform specific mathematical operations on the parameters to facilitate the 'schedule-free' averaging mechanism.

    • .train(): When called, it transitions the optimizer to training mode. If the optimizer was previously in .eval() mode, it performs a linear interpolation (lerp_) on the parameters to move them from the averaged state back toward the training state.
    • .eval(): When called, it transitions the optimizer to evaluation mode. If the optimizer was in .train() mode, it performs a linear interpolation to move parameters toward the averaged state (z).
    • .step(): Performs the actual optimization step. This method requires the optimizer to be in .train() mode. If called while in .eval() mode, it raises an Exception.

    This mechanism allows the optimizer to maintain a running average of weights that is used during evaluation without requiring a manual learning rate decay schedule during training.

  6. How AdamCScheduleFreePlusPaper works

    main

    This optimizer implements a research-oriented update rule combining three components:

    1. AdamC: An adaptive optimizer where decoupled weight decay is applied to the z sequence at the y point and scaled by lr**2. This prevents effective decay from blowing up during online learning rate adaptation. Note that weight_decay values should be significantly higher (e.g., 20-50) than standard AdamW values.
    2. Schedule-Free: Maintains three iterate sequences: x (averaged/evaluation iterate), y (gradient query point used during training), and z (unaveraged AdamW iterate). It uses a polynomial weighting power r and an optional annealing schedule for the Schedule-Free momentum sf_beta1.
    3. Polyak step size: Adaptively sets the per-step learning rate using the rule: polyak_lr = max(0, f + ip_term) / grad_l1_ema, where f is the objective value and ip_term is a Schedule-Free inner-product correction.
  7. Use RAdamScheduleFree optimizer

    main

    RAdamScheduleFree is a Schedule-Free RAdam optimizer that eliminates the need for manual warmup hyperparameters or learning rate schedulers.

    Critical Usage Requirement: This optimizer relies on switching between training and evaluation modes to manage its internal state. You must call .train() and .eval() on the optimizer instance at the appropriate times:

    1. Call optimizer.train() before starting the training phase.
    2. Call optimizer.eval() before starting the evaluation/validation phase.
    3. When saving checkpoints, the optimizer should be in .eval() mode.

    If step() is called while the optimizer is not in .train() mode, it will raise an exception.

  8. Manage training and evaluation modes with ScheduleFreeWrapper

    main

    To use ScheduleFreeWrapper correctly, you must switch between .train() and .eval() modes. This manages the interpolation between the weight points ($x$, $y$, and $z$).

    • Call optimizer.train() before the training loop/step to prepare the weights.
    • Call optimizer.eval() during evaluation to set the weights to the averaged version.

    Note: If you call .step() while the optimizer is not in .train() mode, it will raise an Exception.

  9. Handle BatchNorm when using Schedule-Free

    main

    If your model uses BatchNorm, standard test/val evaluations may not work correctly because the training_mean/training_var cache is updated during the training phase at the $y$ sequence. To ensure evaluations use values calculated at the $x$ sequence, you must perform a small number of forward passes in training mode before switching to eval mode.

    Alternatively, using PreciseBN will avoid this issue.

    model.train()
    optimizer.eval()
    with torch.no_grad():
      for batch in itertools.islice(train_loader, 50):
        model(batch)
    model.eval()
  10. Use ScheduleFreeWrapper to wrap any optimizer

    main

    The experimental ScheduleFreeWrapper allows you to apply schedule-free logic to any base optimizer. When using this wrapper, you can disable the base optimizer's momentum as the wrapper manages its own.

    If you set weight decay on the base optimizer, it is computed at $z$. You can use the weight_decay_at_y parameter to compute weight decay at $y$ instead, which may yield better results.

    There is also a ScheduleFreeWrapperReference version for increased numerical stability during research.

    base_optimizer = torch.optim.RMSprop(model.parameters(), lr=0.0025)
    optimizer = ScheduleFreeWrapper(
      base_optimizer, momentum=0.9, weight_decay_at_y=0.1
    )
  11. Configure ScheduleFreeWrapperReference parameters

    main

    When initializing ScheduleFreeWrapperReference, you can tune the following parameters:

    ParameterTypeDefaultDescription
    basetorch.optim.OptimizerRequiredThe underlying PyTorch optimizer.
    momentumfloat0.9Momentum applied to the outer optimizer.
    weight_decay_at_yfloat0.0If non-zero, weight decay is calculated at the y point. Set to 0 to calculate at z via the inner optimizer.
    rfloat0Power used for polynomial weighting in the average.
    weight_lr_powerfloat2During warmup, weights in the average are equal to lr raised to this power. Set to 0 for no weighting.