学习率范围测试(LR Finder)脚本

简介

深度学习中的学习率是模型训练中至关重要的超参数之一。合适的学习率可以加速模型的收敛,提高训练效率,而不恰当的学习率可能导致训练过慢或者无法收敛。为了找到合适的学习率,LR Finder成为了一种强大的工具。

学习率范围测试(LR Finder)是一种通过逐渐增加学习率来观察模型在不同学习率下的性能变化的方法。这个过程可以帮助我们找到一个合适的初始学习率,有助于训练过程的稳定和加速。在本文中,我们将深入探讨 LR Finder 的原理、实现和应用,以及如何在实际的分类项目中充分利用这个强大的工具。

学习率在深度学习中的关键

深度学习当中,学习率是一个至关重要的超参数,直接影响了模型的训练性能和收敛速度。因为它决定了每一次参数更新的步长,过大的学习率可能会导致模型在训练过程中发散,而过小的学习率则可能导致模型训练缓慢甚至停滞。

学习率调整方法

下面是一些常见的学习率调整方法,它们在不同的场景和问题上表现出色。

1.常数学习率

常数学习率是最简单的学习率调整方法,即在整个训练过程中,学习率始终保持不变,但是它只是比较适用于数据集较小、模型已经比较稳定的情况。

用法很简单,就是在训练开始时选择一个合适的学习率,比如0.001或0.01,后续的训练过程中就不再变化了。这个值的设定就比较依赖于的经验了。

2.学习率衰减

学习率衰减是随着训练的进行逐渐减小学习率。它可以分为定期调整和根据模型性能调整两种方式,它适用于需要在训练初期更加收敛,而在后期更加稳定的情况。

  • 定期调整:每隔一段训练轮次或步骤,学习率按照一定的衰减率进行调整。
  • 性能调整:根据模型在验证集上的性能进行调整,性能停滞时降低学习率,性能提高时保持学习率不变或轻微提高。

3.自适应学习率

自适应学习率方法根据模型参数的梯度信息和历史信息来自动调整学习率。主要应用于不同参数具有不同特性或数据分布不均匀的情况。

  • Adam: 结合了动量法和自适应学习率的方法,对不同参数应用不同的学习率。
  • Adagrad: 根据参数的历史梯度信息自适应地调整学习率。
  • RMSProp: 在 Adagrad 的基础上加入了一个衰减系数,以防止学习率过早地降低。

学习率范围测试(LR Finder)

学习率范围测试是一种通过逐渐增加学习率来观察模型性能的方法,从而找到一个合适的初始学习率,适合在训练初期选择合适的学习率范围。

实现的方法:

逐渐增加学习率,从一个较小的学习率开始,逐渐增加学习率直到性能开始下降,在学习率范围内观察模型的性能曲线,找到性能峰值对应的学习率。

class FindLR(_LRScheduler):
    """
    exponentially increasing learning rate

    Args:
        optimizer: optimzier(e.g. SGD)
        num_iter: totoal_iters
        max_lr: maximum  learning rate
    """
    def __init__(self, optimizer, max_lr=10, num_iter=100, last_epoch=-1):

        self.total_iters = num_iter
        self.max_lr = max_lr
        super().__init__(optimizer, last_epoch)

    def get_lr(self):
        return [base_lr * (self.max_lr / base_lr) ** (self.last_epoch / (self.total_iters + 1e-32)) for base_lr in self.base_lrs]

在 get_lr 方法中,通过指数增长的方式计算当前迭代次数下每个参数的学习率。这个方法返回一个学习率列表,其中每个元素对应网络中的一个参数。这个学习率列表将被用于更新优化器中的学习率。

import torch
import torch.optim as optim
from pyzjr.core.lr_scheduler import _LRScheduler
import matplotlib.pyplot as plt

class FindLR(_LRScheduler):
    def __init__(self, optimizer, max_lr=10, num_iter=100, last_epoch=-1):
        self.total_iters = num_iter
        self.max_lr = max_lr
        super().__init__(optimizer, last_epoch)

    def get_lr(self):
        return [base_lr * (self.max_lr / base_lr) ** (self.last_epoch / (self.total_iters + 1e-32)) for base_lr in self.base_lrs]

x = torch.arange(0, 100, 1)
y = torch.sin(0.1 * x) + 0.1 * torch.randn(100)

model = torch.nn.Linear(1, 1)
optimizer = optim.SGD(model.parameters(), lr=0.01)

lr_finder = FindLR(optimizer, max_lr=10, num_iter=100)
lr_values = []

for epoch in range(100):
    outputs = model(x.unsqueeze(1).float())
    loss = torch.nn.functional.mse_loss(outputs.squeeze(), y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    lr_finder.step()

    current_lr = optimizer.param_groups[0]['lr']
    lr_values.append(current_lr)

    print(f"Iteration: {epoch}, Loss: {loss.item():.4f}, LR: {current_lr:.8f}")

plt.switch_backend('TkAgg')
plt.plot(lr_values)
plt.xlabel('Iteration')
plt.ylabel('Learning Rate')
plt.title('Learning Rate Range Test')
plt.show()


迭代与学习率的曲线图:

下面我们将采用pyzjr中定义的 lr_finder 对CIFAR-100进行学习率范围测试:

import torch.nn as nn

import matplotlib
matplotlib.use('Agg')
from utils import get_network
from dataset import get_train_loader
from pyzjr.dlearn.learnrate import lr_finder

CIFAR100_TRAIN_MEAN = (0.5070751592371323, 0.48654887331495095, 0.4409178433670343)
CIFAR100_TRAIN_STD = (0.2673342858792401, 0.2564384629170883, 0.27615047132568404)

if __name__ == '__main__':
    class parser_args():
        def __init__(self):
            self.net = "vgg16"
            self.batch_size = 64
            self.base_lr = 1e-7
            self.max_lr = 10
            self.num_iter = 100
            self.Cuda = True

    args = parser_args()

    mean = CIFAR100_TRAIN_MEAN
    std = CIFAR100_TRAIN_STD
    train_loader = get_train_loader(mean, std, batch_size=4)

    net = get_network(args)  # 网络模型定义

    loss_function = nn.CrossEntropyLoss()
    lrfinder = lr_finder(net, train_loader, loss_function)
    lrfinder.update()   # 不断迭代
    lrfinder.plotshow()   # 绘制图像并显示

    lrfinder.save(path="result.jpg")   # 保存图像到指定路径

输出图像result.jpg:

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个使用 PyTorch 实现学习率范围测试的示例代码: ```python import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader # 定义一个简单的模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.fc1 = nn.Linear(10, 10) self.fc2 = nn.Linear(10, 1) def forward(self, x): x = torch.relu(self.fc1(x)) x = self.fc2(x) return x # 定义一个数据集 class MyDataset(torch.utils.data.Dataset): def __init__(self, data): super(MyDataset, self).__init__() self.data = data def __len__(self): return len(self.data) def __getitem__(self, idx): return self.data[idx] # 定义 LR Range Test def lr_range_test(model, dataset, loss_func, optimizer, init_lr=1e-7, final_lr=10, num_iter=100, device=torch.device('cpu')): lr_schedule = torch.linspace(init_lr, final_lr, num_iter) losses = [] for lr in lr_schedule: # 重置优化器的学习率 optimizer.param_groups[0]['lr'] = lr # 训练一个 Epoch for i, data in enumerate(DataLoader(dataset, batch_size=4, shuffle=True)): x, y = data x, y = x.to(device), y.to(device) optimizer.zero_grad() y_pred = model(x) loss = loss_func(y_pred, y) loss.backward() optimizer.step() # 记录每个学习率下的损失函数值 losses.append(loss.item()) # 返回学习率和损失函数值 return lr_schedule, losses # 测试 data = torch.randn(100, 10) labels = torch.randn(100, 1) dataset = MyDataset(list(zip(data, labels))) model = Net() loss_func = nn.MSELoss() optimizer = optim.SGD(model.parameters(), lr=1e-3) lr_schedule, losses = lr_range_test(model, dataset, loss_func, optimizer, init_lr=1e-7, final_lr=10, num_iter=100, device=torch.device('cpu')) ``` 在上面的代码中,我们首先定义了一个简单的神经网络模型 `Net`,接着定义了一个数据集 `MyDataset`,然后定义了一个 `lr_range_test` 函数来实现学习率范围测试。在测试时,我们需要提供一个初始学习率,一个最终学习率,和一个迭代次数,函数会根据这些参数在给定的数据集上进行训练并记录每个学习率下的损失函数值。最后,函数会返回学习率和损失函数值两个列表,我们可以使用这些值来绘制损失函数随学习率变化的图表。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏天是冰红茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值