pytorch-lr-finder

repository·master·Indexed 21 days ago

https://github.com/davidtvs/pytorch-lr-finder

A PyTorch implementation of the learning rate range test. It supports both the exponential approach used by fastai and the linear evaluation approach proposed by Leslie Smith. The library includes features for gradient accumulation, mixed precision training support via apex.amp and torch.amp, and custom data handling through TrainDataLoaderIter and ValDataLoaderIter wrapper classes.

Tokens
5.4K
Snippets
21
Records
25
Agent score
67%

What's inside pytorch-lr-finder

  1. Important usage notes for LRFinder

    master

    When using LRFinder, keep the following constraints and behaviors in mind:

    • Optimizer State: The optimizer passed to LRFinder must not have an LRScheduler attached to it.
    • Weight Modification: LRFinder.range_test() modifies model weights and optimizer parameters. Always call lr_finder.reset() to restore them to their initial state.
    • Data Format: range_test() expects DataLoader objects to return a pair of (input, label). The input must be ready for the model and label must be ready for the criterion without further processing. If processing is required, use the TrainDataLoaderIter and ValDataLoaderIter wrapper classes.
    • History Access: You can access the results via lr_finder.history, which returns a dictionary with lr and loss keys.
  2. Configure gradient accumulation in LRFinder

    master

    If your hardware constraints require a smaller batch size than your desired training batch size, use the accumulation_steps parameter in LRFinder.range_test().

    Note: Ensure the batch_size in your DataLoader is set to the smaller real_batch_size used for the actual iterations.

    from torch.utils.data import DataLoader
    from torch_lr_finder import LRFinder
    
    desired_batch_size, real_batch_size = 32, 4
    accumulation_steps = desired_batch_size // real_batch_size
    
    dataset = ...
    trainloader = DataLoader(dataset, batch_size=real_batch_size, shuffle=True)
    
    model = ...
    criterion = ...
    optimizer = ...
    
    lr_finder = LRFinder(model, optimizer, criterion, device="cuda")
    lr_finder.range_test(trainloader, end_lr=10, num_iter=100, step_mode="exp", accumulation_steps=accumulation_steps)
    lr_finder.plot()
    lr_finder.reset()
  3. Install torch-lr-finder

    master

    Install the package using pip. To include support for mixed precision training (Apex), use the --global-option="apex" flag during installation.

    # Standard installation
    pip install torch-lr-finder
    
    # Installation with mixed precision (Apex) support
    pip install torch-lr-finder -v --global-option="apex"
  4. Use TrainDataLoaderIter and ValDataLoaderIter for custom batch formats

    master

    By default, LRFinder.range_test() expects DataLoader objects to return batches in the format input, label, *. If your DataLoader or Dataset.__getitem__() returns a dictionary, a container, or requires additional processing to prepare inputs and labels, you must use TrainDataLoaderIter (for training sets) and ValDataLoaderIter (for validation sets).

    To use these, subclass them and override the inputs_labels_from_batch(self, batch_data) method. This method must return a tuple containing the input tensor and the label tensor.

    # Example: Handling a dictionary-based DataLoader
    class CustomTrainIter(TrainDataLoaderIter):
        def inputs_labels_from_batch(self, batch_data):
            # batch_data is what the DataLoader returns
            return batch_data["img"], batch_data["target"]
    
    # Wrap your existing trainloader
    custom_train_iter = CustomTrainIter(trainloader)
    
    # Pass the custom iterator to range_test
    lr_finder.range_test(custom_train_iter, end_lr=100, num_iter=100, step_mode="exp")
  5. Handle custom validation loaders with ValDataLoaderIter

    master

    When performing a range_test that includes validation loss (using the val_loader argument), the validation loader must also be wrapped in a ValDataLoaderIter if it does not follow the standard input, label return format.

    If you provide a standard DataLoader for val_loader while using a custom TrainDataLoaderIter, the test will fail. You must ensure both iterators are compatible with the data format.

    # 1. Define the custom validation iterator
    class CustomValIter(ValDataLoaderIter):
        def inputs_labels_from_batch(self, batch_data):
            return batch_data["img"], batch_data["target"]
    
    # 2. Wrap the test/validation loader
    custom_val_iter = CustomValIter(testloader)
    
    # 3. Run range_test with both custom iterators
    lr_finder.reset()
    lr_finder.range_test(
        custom_train_iter, 
        val_loader=custom_val_iter, 
        end_lr=100, 
        num_iter=100, 
        step_mode="exp"
    )
  6. Use mixed precision training with torch.amp

    master

    To use PyTorch's native torch.amp, provide an amp_config dictionary (containing device_type and dtype) and a grad_scaler instance to the LRFinder constructor, and set amp_backend='torch'.

    from torch_lr_finder import LRFinder
    
    amp_config = {
        'device_type': 'cuda',
        'dtype': torch.float16,
    }
    grad_scaler = torch.cuda.amp.GradScaler()
    
    lr_finder = LRFinder(
        model, optimizer, criterion, device='cuda',
        amp_backend='torch', amp_config=amp_config, grad_scaler=grad_scaler
    )
    lr_finder.range_test(trainloader, end_lr=10, num_iter=100, step_mode='exp')
    lr_finder.plot()
    lr_finder.reset()
  7. Use mixed precision training with apex.amp

    master

    To use NVIDIA's apex.amp, initialize your model and optimizer with amp.initialize before passing them to LRFinder, and set amp_backend='apex'.

    from torch_lr_finder import LRFinder
    from apex import amp
    
    # Add this line before running `LRFinder`
    model, optimizer = amp.initialize(model, optimizer, opt_level='O1')
    
    lr_finder = LRFinder(model, optimizer, criterion, device='cuda', amp_backend='apex')
    lr_finder.range_test(trainloader, end_lr=10, num_iter=100, step_mode='exp')
    lr_finder.plot()
    lr_finder.reset()
  8. Use Leslie Smith's linear evaluation learning rate search

    master

    This approach increases the learning rate linearly and computes evaluation loss. It typically produces more precise curves because evaluation loss is more sensitive to divergence, but it is slower. When using step_mode="linear", ensure the learning rate range is within the same order of magnitude.

    from torch_lr_finder import LRFinder
    
    model = ...
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.1, weight_decay=1e-2)
    
    lr_finder = LRFinder(model, optimizer, criterion, device="cuda")
    lr_finder.range_test(trainloader, val_loader=val_loader, end_lr=1, num_iter=100, step_mode="linear")
    lr_finder.plot(log_lr=False)
    lr_finder.reset()
  9. Use the fastai-style exponential learning rate search

    master

    This approach increases the learning rate exponentially and computes training loss. It is the default behavior. Use lr_finder.plot() to visualize the training loss versus the logarithmic learning rate.

    from torch_lr_finder import LRFinder
    
    model = ...
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=1e-7, weight_decay=1e-2)
    
    lr_finder = LRFinder(model, optimizer, criterion, device="cuda")
    lr_finder.range_test(trainloader, end_lr=100, num_iter=100)
    lr_finder.plot() # to inspect the loss-learning rate graph
    lr_finder.reset() # to reset the model and optimizer to their initial state
  10. Run a learning rate range test (fastai style)

    master

    To perform a learning rate range test using the training loss (following the fastai procedure), initialize LRFinder with your model, optimizer, and criterion. Call range_test() on your training dataloader.

    Key parameters:

    • end_lr: The maximum learning rate to reach.
    • num_iter: The number of iterations to run the test.
    • step_mode: Use "linear" for small ranges or "exp" (exponential) for larger ranges.

    After the test, use .plot() to visualize the training loss vs. learning rate. Use .reset() to restore the model and optimizer to their initial states.

    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=1e-7, weight_decay=1e-2)
    lr_finder = LRFinder(model, optimizer, criterion, device="cuda")
    lr_finder.range_test(trainloader, end_lr=100, num_iter=100, step_mode="exp")
    lr_finder.plot()
    lr_finder.reset()