前言
在训练神经网络时,如果学习率过大,优化算法可能会在最优解附近震荡而无法收敛;如果学习率过小,优化算法的收敛速度可能会非常慢。因此,一种常见的策略是在训练初期使用较大的学习率来快速接近最优解,然后逐渐减小学习率,使得优化算法可以更精细地调整模型参数,从而找到更好的最优解。
通常学习率衰减有以下的措施:
- 指数衰减:学习率按照指数的形式衰减,每次乘以一个固定的衰减系数,可以使用 torch.optim.lr_scheduler.ExponentialLR 类来实现,需要指定优化器和衰减系数。
- 固定步长衰减:学习率每隔一定步数(或者epoch)就减少为原来的一定比例,可以使用 torch.optim.lr_scheduler.StepLR 类来实现,需要指定优化器、步长和衰减比例。
- 多步长衰减:学习率在指定的区间内保持不变,在区间的右侧值进行一次衰减,可以使用 torch.optim.lr_scheduler.MultiStepLR 类来实现,需要指定优化器、区间列表和衰减比例。
- 余弦退火衰减:学习率按照余弦函数的周期和最值进行变化,可以使用 torch.optim.lr_scheduler.CosineAnnealingLR 类来实现,需要指定优化器、周期和最小值。
- 自适应学习率衰减:这种策略会根据模型的训练进度自动调整学习率,可以使用 torch.optim.lr_scheduler.ReduceLROnPlateau 类来实现。例如,如果模型的验证误差停止下降,那么就减小学习率;如果模型的训练误差上升,那么就增大学习率。
- 自适应函数实现学习率调整:不同层不同的学习率。
1、指数衰减
指数衰减是一种常用的学习率调整策略,其主要思想是在每个训练周期(epoch)结束时,将当前学习率乘以一个固定的衰减系数(gamma),从而实现学习率的指数衰减。这种策略可以帮助模型在训练初期快速收敛,同时在训练后期通过降低学习率来提高模型的泛化能力。
在PyTorch中,可以使用 torch.optim.lr_scheduler.ExponentialLR 类来实现指数衰减。该类的构造函数需要两个参数:一个优化器对象和一个衰减系数。在每个训练周期结束时,需要调用ExponentialLR 对象的 step() 方法来更新学习率。
以下是一个使用 ExponentialLR代码示例:
import torch from torch.optim import SGD from torch.optim.lr_scheduler import ExponentialLR # 假设有一个模型参数 model_param = torch.nn.Parameter(torch.randn(2, 2, requires_grad=True)) # 使用SGD优化器,初始学习率设置为0.1 optimizer = SGD([model_param], lr=0.1) # 创建ExponentialLR对象,衰减系数设置为0.9 scheduler = ExponentialLR(optimizer, gamma=0.9) # 在每个训练周期结束时,调用step()方法来更新学习率 for epoch in range(100): # 这里省略了模型的训练代码 # ... # 更新学习率 scheduler.step()
在这个例子中,初始的学习率是0.1,每个训练周期结束时,学习率会乘以0.9,因此学习率会按照指数的形式衰减。
2、固定步长衰减
固定步长衰减是一种学习率调整策略,它的原理是每隔一定的迭代次数(或者epoch),就将学习率乘以一个固定的比例,从而使学习率逐渐减小。这样做的目的是在训练初期使用较大的学习率,加快收敛速度,而在训练后期使用较小的学习率,提高模型精度。
PyTorch提供了 torch.optim.lr_scheduler.StepLR 类来实现固定步长衰减,它的参数有:
- optimizer:要进行学习率衰减的优化器,例如 torch.optim.SGD 或 torch.optim.Adam等。
- step_size:每隔多少隔迭代次数(或者epoch)进行一次学习率衰减,必须是正整数。
- gamma:学习率衰减的乘法因子,必须是0到1之间的数,表示每次衰减为原来的 gamma倍。
- last_epoch:最后一个epoch的索引,用于恢复训练的状态,默认为-1,表示从头开始训练。
- verbose:是否打印学习率更新的信息,默认为False。
下面是一个使用 torch.optim.lr_scheduler.StepLR 类的具体例子,假设有一个简单的线性模型,使用 torch.optim.SGD 作为优化器,初始学习率为0.1,每隔5个epoch就将学习率乘以0.8,训练100个epoch:
import torch import matplotlib.pyplot as plt # 定义一个简单的线性模型 class Net(torch.nn.Module): def __init__(self): super(Net, self).__init__() self.fc = torch.nn.Linear(1, 1) def forward(self, x): return self.fc(x) # 创建模型和优化器 model = Net() optimizer = torch.optim.SGD(model.parameters(), lr=0.1) # 创建固定步长衰减的学习率调度器 scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.8) # 记录学习率变化 lr_list = [] # 模拟训练过程 for epoch in range(100): # 更新学习率 scheduler.step() # 记录当前学习率 lr_list.append(optimizer.param_groups[0]['lr']) # 绘制学习率曲线 plt.plot(range(100), lr_list, color='r') plt.xlabel('Epoch') plt.ylabel('Learning Rate') plt.show()
学习率在每个5个epoch后都会下降为原来的0.8倍,直到最后接近0。
固定步长衰减和指数衰减都是学习率衰减的策略,但它们在衰减的方式和速度上有所不同:
- 固定步长衰减:在每隔固定的步数(或epoch)后,学习率会减少为原来的一定比例。这种策略的衰减速度是均匀的,不会随着训练的进行而改变。
- 指数衰减:在每个训练周期(或epoch)结束时,学习率会乘以一个固定的衰减系数,从而实现学习率的指数衰减。这种策略的衰减速度是逐渐加快的,因为每次衰减都是基于当前的学习率进行的。
3、多步长衰减
多步长衰减是一种学习率调整策略,它在指定的训练周期(或epoch)达到预设的里程碑时,将学习率减少为原来的一定比例。这种策略可以在模型训练的关键阶段动态调整学习率。
在PyTorch中,可以使用 torch.optim.lr_scheduler.MultiStepLR 类来实现多步长衰减。以下是一个使用 MultiStepLR 的代码示例:
import torch import torch.optim as optim from torch.optim.lr_scheduler import MultiStepLR # 假设有一个简单的模型 model = torch.nn.Linear(10, 2) optimizer = optim.SGD(model.parameters(), lr=0.1) # 创建 MultiStepLR 对象,设定在第 30 和 80 个 epoch 时学习率衰减为原来的 0.1 倍 scheduler = MultiStepLR(optimizer, milestones=[30, 80], gamma=0.1) # 在每个训练周期结束时,调用 step() 方法来更新学习率 for epoch in range(100): # 这里省略了模型的训练代码 # ... # 更新学习率 scheduler.step()
在这个例子中,初始的学习率是0.1,当训练到第30个epoch时,学习率会变为0.01(即0.1*0.1),当训练到第80个epoch时,学习率会再次衰减为0.001(即0.01*0.1)。
4、余弦退火衰减
余弦退火衰减是一种学习率调整策略,它按照余弦函数的周期和最值来调整学习率。在PyTorch中,可以使用 torch.optim.lr_scheduler.CosineAnnealingLR 类来实现余弦退火衰减。
以下是一个使用 CosineAnnealingLR 的代码示例:
import torch import torch.nn as nn from torch.optim.lr_scheduler import CosineAnnealingLR import matplotlib.pyplot as plt # 假设有一个简单的模型 class SimpleModel(nn.Module): def __init__(self): super(SimpleModel, self).__init__() self.linear = nn.Linear(10, 2) model = SimpleModel() optimizer = torch.optim.SGD(model.parameters(), lr=0.1) # 创建 CosineAnnealingLR 对象,周期设置为 10,最小学习率设置为 0.01 scheduler = CosineAnnealingLR(optimizer, T_max=10, eta_min=0.01) lr_list = [] # 在每个训练周期结束时,调用 step() 方法来更新学习率 for epoch in range(100): # 这里省略了模型的训练代码 # ... # 更新学习率 scheduler.step() lr_list.append(optimizer.param_groups[0]['lr']) # 绘制学习率变化曲线 plt.plot(range(100), lr_list) plt.xlabel('Epoch') plt.ylabel('Learning Rate') plt.title("Learning rate schedule: CosineAnnealingLR") plt.show()
在这个例子中,初始的学习率是0.1,学习率会按照余弦函数的形式在0.01到0.1之间变化,周期为10个epoch。
5、自适应学习率衰减
自适应学习率衰减是一种学习率调整策略,它会根据模型的训练进度自动调整学习率。例如,如果模型的验证误差停止下降,那么就减小学习率;如果模型的训练误差上升,那么就增大学习率。在PyTorch中,可以使用 torch.optim.lr_scheduler.ReduceLROnPlateau 类来实现自适应学习率衰减。
以下是一个使用 ReduceLROnPlateau 的代码示例:
import torch import torch.nn as nn from torch.optim.lr_scheduler import ReduceLROnPlateau # 假设有一个简单的模型 model = nn.Linear(10, 2) optimizer = torch.optim.SGD(model.parameters(), lr=0.1) # 创建 ReduceLROnPlateau 对象,当验证误差在 10 个 epoch 内没有下降时,将学习率减小为原来的 0.1 倍 scheduler = ReduceLROnPlateau(optimizer, 'min', patience=10, factor=0.1) # 模拟训练过程 for epoch in range(100): # 这里省略了模型的训练代码 # ... # 假设有一个验证误差 val_loss = ... # 在每个训练周期结束时,调用 step() 方法来更新学习率 scheduler.step(val_loss)
6、自定义函数实现学习率调整:不同层不同的学习率
可以通过为优化器提供一个参数组列表来实现对不同层使用不同的学习率。每个参数组是一个字典,其中包含一组参数和这组参数的学习率。
以下是一个具体的例子,假设有一个包含两个线性层的模型,想要对第一层使用学习率0.01,对第二层使用学习率0.001:
import torch import torch.nn as nn import torch.optim as optim # 定义一个包含两个线性层的模型 class SimpleModel(nn.Module): def __init__(self): super(SimpleModel, self).__init__() self.layer1 = nn.Linear(10, 2) self.layer2 = nn.Linear(2, 10) model = SimpleModel() # 创建一个参数组列表,每个参数组包含一组参数和这组参数的学习率 params = [{'params': model.layer1.parameters(), 'lr': 0.01}, {'params': model.layer2.parameters(), 'lr': 0.001}] # 创建优化器 optimizer = optim.SGD(params) # 现在,当调用 optimizer.step() 时,第一层的参数会使用学习率 0.01 进行更新,第二层的参数会使用学习率 0.001 进行更新
在这个例子中,首先定义了一个包含两个线性层的模型。然后,创建一个参数组列表,每个参数组都包含一组参数和这组参数的学习率。最后创建了一个优化器SGD,将这个参数组列表传递给优化器。这样,当调用 optimizer.step() 时,第一层的参数会使用学习率0.01进行更新,第二层的参数会使用学习率0.001进行更新。
参考:深度图学习与大模型LLM