在深度学习中,学习率调整策略(Learning Rate Scheduling)用于在训练过程中动态调整学习率,以实现更快的收敛和更好的模型性能。选择合适的学习率策略可以避免模型陷入局部最优、震荡不稳定等问题。下面介绍一些常见的学习率调整策略:
1. Step Decay(分步衰减)
原理
Step Decay 是一种分段衰减策略,每隔一定的训练周期或步骤,学习率会缩减一个固定的因子。这可以在训练中途降低学习率,从而让模型在训练末期更加稳定地收敛。
公式
其中:
initial_lr
是初始学习率factor
是每次衰减的因子,一般小于 1(例如 0.1)k
是衰减次数
适用场景
适合训练中需要逐步收敛的模型,如卷积神经网络。在一定训练轮次后,降低学习率有助于模型以更稳定的步伐接近最优解。
优缺点
- 优点:可以逐步收敛,适合比较平稳的优化任务。
- 缺点:由于步长是固定的,可能会导致过早或过晚调整学习率。
代码示例
在 PyTorch 中实现 Step Decay 可以使用 StepLR
:
import torch
import torch.optim as optim
import torch.nn as nn# 假设我们有一个简单的模型
model = nn.Linear(10, 2)
optimizer = optim.SGD(model.parameters(), lr=0.1) # 初始学习率 0.1
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5) # 每10个epoch衰减一半# 模拟训练过程
for epoch in range(30):# 假设进行前向和后向传播optimizer.zero_grad()output = model(torch.randn(10))loss = nn.MSELoss()(output, torch.randn(2))loss.backward()optimizer.step()# 更新学习率scheduler.step()print(f"Epoch {epoch+1}, Learning Rate: {scheduler.get_last_lr()[0]}")
2. Exponential Decay(指数衰减)
原理
指数衰减策略的学习率会以指数方式逐渐减少,公式为:
其中 decay_rate
控制学习率下降的速度。指数衰减适合需要平稳下降的任务,因为这种衰减是连续的且平滑。
适用场景
适合长时间训练或训练数据复杂的模型,能让模型在训练后期继续保持较好的收敛效果。
优缺点
- 优点:平滑衰减,适合长时间训练。
- 缺点:如果
decay_rate
设置不当,可能会导致过早或过晚下降。
代码示例
在 PyTorch 中使用 ExponentialLR
实现:
scheduler = optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.9) # 指数衰减因子 0.9for epoch in range(30):optimizer.zero_grad()output = model(torch.randn(10))loss = nn.MSELoss()(output, torch.randn(2))loss.backward()optimizer.step()# 更新学习率scheduler.step()print(f"Epoch {epoch+1}, Learning Rate: {scheduler.get_last_lr()[0]}")
3. Cosine Annealing(余弦退火)
原理
Cosine Annealing 利用余弦函数使学习率周期性下降,并在周期末期快速降低至接近 0 的值。
其中 T_max
是控制余弦周期的最大步数,周期性下降可以使模型在接近全局最优时表现更稳定。
适用场景
适合在有一定噪声的数据集上进行多轮次训练,使模型在每个周期内都能充分探索损失函数的不同区域。
优缺点
- 优点:自然周期下降,易于模型在训练中后期稳定收敛。
- 缺点:周期设置需要与任务匹配,否则可能在全局最优时过早结束。
代码示例
在 PyTorch 中使用 CosineAnnealingLR
实现:
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=30)for epoch in range(30):optimizer.zero_grad()output = model(torch.randn(10))loss = nn.MSELoss()(output, torch.randn(2))loss.backward()optimizer.step()# 更新学习率scheduler.step()print(f"Epoch {epoch+1}, Learning Rate: {scheduler.get_last_lr()[0]}")
4. Reduce on Plateau(基于验证集表现动态调整)
原理
当验证集的损失在一段时间(耐心期)内没有显著下降,则将学习率按一定因子减少。这样可以防止模型陷入局部最优。
适用场景
特别适合那些验证集损失不稳定或在收敛后期趋于平稳的模型,比如需要细致调整的分类任务。
优缺点
- 优点:自适应调整学习率,使训练在收敛后期更稳定。
- 缺点:依赖验证集表现,调整耐心期参数复杂。
代码示例
在 PyTorch 中使用 ReduceLROnPlateau
实现:
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=5)for epoch in range(30):optimizer.zero_grad()output = model(torch.randn(10))loss = nn.MSELoss()(output, torch.randn(2))loss.backward()optimizer.step()# 模拟验证损失val_loss = loss.item() + (epoch % 10) * 0.1 # 可调节该值scheduler.step(val_loss)print(f"Epoch {epoch+1}, Learning Rate: {optimizer.param_groups[0]['lr']}")
5. Cyclical Learning Rate (CLR)
原理
CLR 设定了上下限值,让学习率在两者之间循环,探索损失空间不同区域,防止陷入局部最优。
适用场景
适合包含复杂损失结构的任务,如图像分类中的较大卷积网络。
优缺点
- 优点:避免陷入局部最优,提高全局搜索能力。
- 缺点:调整范围较难控制,适用性有限。
代码示例
可以使用 torch.optim
库实现自定义的 CLR:
import numpy as np# 计算CLR的函数
def cyclical_learning_rate(step, base_lr=0.001, max_lr=0.006, step_size=2000):cycle = np.floor(1 + step / (2 * step_size))x = np.abs(step / step_size - 2 * cycle + 1)lr = base_lr + (max_lr - base_lr) * np.maximum(0, (1 - x))return lr# 训练过程
for step in range(10000):lr = cyclical_learning_rate(step)optimizer.param_groups[0]['lr'] = lroptimizer.zero_grad()output = model(torch.randn(10))loss = nn.MSELoss()(output, torch.randn(2))loss.backward()optimizer.step()if step % 1000 == 0:print(f"Step {step}, Learning Rate: {optimizer.param_groups[0]['lr']}")
6. One Cycle Policy(单周期策略)
原理
One Cycle Policy 从较低学习率开始,逐渐增加到最大值,然后再逐步减小到较低值。适合需要快速探索和稳定收敛的任务。
适用场景
适合迁移学习和较小数据集。
优缺点
- 优点:适合迁移学习,能快速稳定收敛。
- 缺点:对于较长训练任务效果一般。
代码示例
在 PyTorch 中实现 One Cycle Policy:
from torch.optim.lr_scheduler import OneCycleLRscheduler = OneCycleLR(optimizer, max_lr=0.1, steps_per_epoch=100, epochs=10)for epoch in range(10):for i in range(100): # 假设一个 epoch 有 100 个 batchoptimizer.zero_grad()output = model(torch.randn(10))loss = nn.MSELoss()(output, torch.randn(2))loss.backward()optimizer.step()scheduler.step() # 每步更新学习率print(f"Epoch {epoch+1}, Step {i+1}, Learning Rate: {scheduler.get_last_lr()[0]}")
7.如何选择合适的学习率调整策略?
(1). 数据规模和训练时长
-
小数据集且训练时间短:
使用 One Cycle Policy 或 Cyclical Learning Rate。这类策略能够快速调整学习率,在有限的时间内加速训练并避免局部最优。 -
中等数据集且训练时间适中:
可以选择 Step Decay 或 Exponential Decay。这些策略在收敛过程中平稳下降,适合中等规模的任务。 -
大数据集且长时间训练:
选择 Cosine Annealing 或 Reduce on Plateau。这类策略能够适应较长的训练周期,避免学习率下降过快,从而保持稳定的收敛效果。
(2). 模型类型和复杂度
-
简单模型(如浅层神经网络):
使用 Step Decay 或 Exponential Decay。这些简单的衰减策略适合训练时间不长且模型复杂度低的情况。 -
深度模型(如卷积神经网络、递归神经网络):
选择 Cosine Annealing、Reduce on Plateau 或 One Cycle Policy。这些策略在后期能平滑衰减,有助于复杂模型更好地探索损失函数的不同区域。 -
预训练模型的微调:
One Cycle Policy 是一个理想选择。它从较低的学习率开始,快速升至最大,再衰减回较小值,适合在微调过程中稳定调整参数。
(3). 任务类型
-
分类任务:
分类任务中常用 Step Decay、Cosine Annealing 或 Cyclical Learning Rate,特别是在图像分类任务中,余弦退火可以在训练后期更好地收敛,CLR 则有助于探索不同的损失空间。 -
回归任务:
Exponential Decay 或 Reduce on Plateau,回归任务通常要求模型在后期保持较稳定的收敛效果,因此指数衰减和基于验证集表现的动态调整策略更为合适。 -
时间序列预测:
使用 Reduce on Plateau 或 Exponential Decay,因为时间序列预测中数据较为复杂,不同时间段的学习率需求变化大,可以使用验证集损失表现来决定学习率的动态调整。
(4). 模型对学习率敏感性
-
学习率敏感模型:
对学习率波动敏感的模型适合使用 Cosine Annealing 或 Reduce on Plateau。这类模型需要学习率逐步下降的过程来平稳收敛,不易受到过大的学习率波动影响。 -
对学习率不敏感的模型:
使用 Cyclical Learning Rate 或 One Cycle Policy,这两种策略适合让学习率在一个范围内波动,从而让模型更快跳出局部最优,快速找到全局最优解。
(5). 损失函数表现与收敛性
-
损失波动较大(不稳定收敛):
选择 Reduce on Plateau,让模型在验证集损失长时间不下降时再降低学习率,避免过早或频繁地调整学习率。 -
损失逐渐收敛(平稳下降):
使用 Step Decay 或 Exponential Decay,这些策略更适合平稳下降的场景,且能在训练后期提供更小的学习率。
任务场景 | 推荐学习率调整策略 |
---|---|
小数据集,快速训练 | One Cycle Policy,CLR |
大数据集,长时间训练 | Cosine Annealing,Reduce on Plateau |
微调预训练模型 | One Cycle Policy |
简单模型 | Step Decay,Exponential Decay |
深层复杂模型 | Cosine Annealing,Reduce on Plateau |
分类任务 | Step Decay,Cosine Annealing,CLR |
时间序列或自然语言处理 | Exponential Decay,Reduce on Plateau |
高波动的验证集损失 | Reduce on Plateau |
以下是一个综合示例,展示了如何在 PyTorch 中动态选择并应用学习率调整策略:
import torch
import torch.optim as optim
import torch.nn as nn
from torch.optim.lr_scheduler import StepLR, ExponentialLR, CosineAnnealingLR, ReduceLROnPlateau, OneCycleLR# 假设我们有一个简单的模型
model = nn.Linear(10, 2)
optimizer = optim.SGD(model.parameters(), lr=0.1) # 初始学习率 0.1# 根据需求选择合适的学习率调整策略
def get_scheduler(optimizer, strategy='step_decay'):if strategy == 'step_decay':return StepLR(optimizer, step_size=10, gamma=0.5)elif strategy == 'exponential_decay':return ExponentialLR(optimizer, gamma=0.9)elif strategy == 'cosine_annealing':return CosineAnnealingLR(optimizer, T_max=30)elif strategy == 'reduce_on_plateau':return ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=5)elif strategy == 'one_cycle':return OneCycleLR(optimizer, max_lr=0.1, steps_per_epoch=100, epochs=10)else:raise ValueError("Unknown strategy type")# 选择策略
scheduler = get_scheduler(optimizer, strategy='cosine_annealing')# 模拟训练过程
for epoch in range(30):optimizer.zero_grad()output = model(torch.randn(10))loss = nn.MSELoss()(output, torch.randn(2))loss.backward()optimizer.step()# 调整学习率if isinstance(scheduler, ReduceLROnPlateau):# 如果是 Reduce on Plateau,使用验证集的损失作为依据val_loss = loss.item() + (epoch % 10) * 0.1 # 模拟验证损失scheduler.step(val_loss)else:scheduler.step()print(f"Epoch {epoch+1}, Learning Rate: {optimizer.param_groups[0]['lr']}")