深度学习:CNN 模型训练中的学习率调整(基于 PyTorch)
目录
一、为什么要调整学习率?
二、PyTorch 学习率调整核心接口
三、3 类学习率调整策略详解
3.1 有序调整:按预设规则更新
(1)StepLR:等间隔调整
(2)MultiStepLR:多间隔调整
(3)ExponentialLR:指数衰减
(4)CosineAnnealingLR:余弦退火调整
3.2 自适应调整:按训练指标更新
ReduceLROnPlateau:指标停滞时衰减
3.3 自定义调整:按业务需求定制
LambdaLR:自定义 lambda 函数
四、CNN 训练中学习率调整的实战建议
在卷积神经网络(CNN)训练过程中,学习率是影响模型收敛速度与最终性能的关键超参数。不合适的学习率可能导致模型训练停滞、收敛过慢或陷入局部最优解。本文将从学习率的核心作用出发,结合 CNN 训练场景,详细讲解 PyTorch 中 3 类主流学习率调整策略的原理与实现。
一、为什么要调整学习率?
学习率(Learning Rate,LR)控制着模型参数更新的步长。在 CNN 训练中,固定学习率存在明显局限性:
- 初始学习率过大:可能导致参数更新幅度过大,模型损失震荡不收敛,甚至出现梯度爆炸。
- 初始学习率过小:参数更新缓慢,训练周期大幅延长,且易陷入局部最优解,无法达到理想性能。
理想的学习率策略应满足 “先快后慢”:训练初期用较大学习率让模型快速逼近最优解区域,后期用较小学习率精细调整参数,确保稳定收敛到全局最优。
二、PyTorch 学习率调整核心接口
PyTorch 通过torch.optim.lr_scheduler
模块提供统一的学习率调整接口,所有调度器均需配合优化器(如 Adam、SGD)使用,且需在每个 epoch(或迭代)后调用scheduler.step()
更新学习率。
核心使用流程如下:
import torch
import torch.optim as optim
from torch.optim.lr_scheduler import StepLR# 1. 定义模型与优化器
model = YourCNNModel()
optimizer = optim.Adam(model.parameters(), lr=0.01)# 2. 定义学习率调度器
scheduler = StepLR(optimizer, step_size=10, gamma=0.1)# 3. 训练循环中更新学习率
for epoch in range(50):model.train()for batch in train_dataloader:# 前向传播、计算损失、反向传播、参数更新optimizer.zero_grad()output = model(batch)loss = loss_fn(output, labels)loss.backward()optimizer.step()# 每个epoch后更新学习率scheduler.step()# 打印当前学习率(可选)print(f"Epoch {epoch+1}, Current LR: {scheduler.get_last_lr()[0]}")
三、3 类学习率调整策略详解
PyTorch 的学习率调整策略可分为有序调整、自适应调整和自定义调整,各类策略适用于不同的 CNN 训练场景。
3.1 有序调整:按预设规则更新
有序调整是指根据 epoch 数量按固定规则调整学习率,无需依赖训练过程中的指标(如损失、准确率),适合训练目标明确、数据分布稳定的 CNN 任务(如 MNIST 手写数字识别、CIFAR 图像分类)。
(1)StepLR:等间隔调整
原理:每经过step_size
个 epoch,学习率乘以gamma
(衰减系数),实现等间隔衰减。
适用场景:模型训练过程稳定,损失随 epoch 均匀下降的场景。
关键参数:
step_size
:学习率衰减间隔(单位:epoch)gamma
:衰减系数,默认 0.1(即每次衰减为原来的 1/10)
代码实现:
from torch.optim.lr_scheduler import StepLR# 初始学习率0.01,每10个epoch衰减为原来的0.5
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
scheduler = StepLR(optimizer, step_size=10, gamma=0.5)# 训练过程中,每个epoch后调用step()
for epoch in range(50):train_one_epoch(model, optimizer, train_dataloader)scheduler.step()print(f"Epoch {epoch+1}, LR: {scheduler.get_last_lr()[0]}")
学习率变化示例:
- Epoch 1-10:LR=0.01
- Epoch 11-20:LR=0.005(0.01×0.5)
- Epoch 21-30:LR=0.0025(0.005×0.5)
(2)MultiStepLR:多间隔调整
原理:根据milestones
列表中的 epoch 节点,在指定 epoch 后触发学习率衰减,灵活性高于 StepLR。
适用场景:已知模型在特定 epoch 后会进入收敛平台期(如训练后期损失下降缓慢),需针对性衰减学习率。
关键参数:
milestones
:学习率衰减的 epoch 列表(如[10, 30, 50]
表示在 10、30、50 epoch 后衰减)gamma
:衰减系数,默认 0.1
代码实现:
from torch.optim.lr_scheduler import MultiStepLR# 初始学习率0.1,在15、30、45 epoch后衰减为原来的0.1
optimizer = optim.Adam(model.parameters(), lr=0.1)
scheduler = MultiStepLR(optimizer, milestones=[15, 30, 45], gamma=0.1)# 训练过程
for epoch in range(50):train_one_epoch(...)scheduler.step()
学习率变化示例:
- Epoch 1-15:LR=0.1
- Epoch 16-30:LR=0.01(0.1×0.1)
- Epoch 31-45:LR=0.001(0.01×0.1)
- Epoch 46-50:LR=0.0001(0.001×0.1)
(3)ExponentialLR:指数衰减
原理:学习率随 epoch 呈指数级衰减,公式为LR = initial_lr × gamma^epoch
。
适用场景:需要学习率持续缓慢衰减,且衰减幅度逐渐减小的场景(如细粒度图像分类)。
关键参数:
gamma
:指数衰减底数,需满足 0 < gamma < 1(通常取 0.95~0.99)
代码实现:
from torch.optim.lr_scheduler import ExponentialLR# 初始学习率0.01,每个epoch衰减为原来的0.98
optimizer = optim.Adam(model.parameters(), lr=0.01)
scheduler = ExponentialLR(optimizer, gamma=0.98)# 训练过程
for epoch in range(100):train_one_epoch(...)scheduler.step()
注意:gamma
不宜过小(如小于 0.9),否则后期学习率会快速趋近于 0,导致模型停止更新。
(4)CosineAnnealingLR:余弦退火调整
原理:学习率随 epoch 按余弦函数周期变化,先从初始值下降到最小值(eta_min
),再回升,模拟 “退火” 过程,有助于跳出局部最优解。
适用场景:模型易陷入局部最优(如复杂 CNN 模型 ResNet、VGG 训练),或训练数据存在噪声的场景。
关键参数:
T_max
:余弦周期的一半(即学习率从初始值下降到最小值的 epoch 数)eta_min
:学习率最小值,默认 0
代码实现:
from torch.optim.lr_scheduler import CosineAnnealingLR# 初始学习率0.01,T_max=20(20个epoch下降到0,再20个epoch回升)
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
scheduler = CosineAnnealingLR(optimizer, T_max=20, eta_min=0.0001)# 训练过程
for epoch in range(80):train_one_epoch(...)scheduler.step()
学习率变化特点:
- Epoch 1-20:LR 从 0.01 下降到 0.0001(余弦下降)
- Epoch 21-40:LR 从 0.0001 回升到 0.01(余弦上升)
- 周期循环,帮助模型在后期重新探索参数空间
3.2 自适应调整:按训练指标更新
自适应调整依赖训练过程中的指标(如验证集损失、准确率),当指标不再提升时自动调整学习率,适合数据分布复杂、模型收敛不稳定的场景(如目标检测、图像分割)。
ReduceLROnPlateau:指标停滞时衰减
原理:监测指定指标(如val_loss
),若指标在patience
个 epoch 内无显著变化(超过threshold
),则触发学习率衰减。
适用场景:无法预设衰减 epoch,需动态判断模型收敛状态的任务(如迁移学习微调、小样本 CNN 训练)。
关键参数:
mode
:指标优化方向(min
表示指标越小越好,如损失;max
表示指标越大越好,如准确率)patience
:指标无变化的最大 epoch 数,超过则衰减factor
:衰减系数,默认 0.1threshold
:指标变化的最小阈值,低于该值视为 “无变化”
代码实现:
from torch.optim.lr_scheduler import ReduceLROnPlateau# 监测验证集损失,若5个epoch无下降则衰减学习率为原来的0.5
optimizer = optim.Adam(model.parameters(), lr=0.001)
scheduler = ReduceLROnPlateau(optimizer,mode='min', # 优化损失(越小越好)factor=0.5, # 衰减系数patience=5, # 5个epoch无改善则衰减verbose=True, # 打印衰减信息threshold=1e-4 # 损失变化小于1e-4视为无改善
)# 训练过程:每个epoch后需传入监测指标
for epoch in range(50):train_one_epoch(...)val_loss = validate(model, val_dataloader)# 传入验证集损失,调度器根据指标判断是否调整scheduler.step(val_loss)
优势:无需手动设定衰减节点,完全由模型训练状态决定,避免过早或过晚衰减。
3.3 自定义调整:按业务需求定制
当预设策略无法满足需求时,可通过LambdaLR
自定义学习率调整规则,支持为不同层设置不同学习率(如微调预训练 CNN 时,对浅层和深层采用不同学习率)。
LambdaLR:自定义 lambda 函数
原理:通过lr_lambda
函数定义学习率与 epoch 的关系,函数输入为当前 epoch,输出为学习率调整倍数。
适用场景:
- 迁移学习中,对预训练层(如 ResNet 的 conv1-conv4)用小学习率微调,对新增全连接层用大学习率训练;
- 需特殊学习率变化规则(如前期快速衰减,后期固定)的场景。
代码实现 1:全局自定义调整
from torch.optim.lr_scheduler import LambdaLR# 自定义规则:前10个epoch学习率线性下降到0.001,之后保持0.001
def lr_lambda(epoch):if epoch < 10:return 0.01 - 0.0009 * epoch # 0.01 → 0.001else:return 0.001optimizer = optim.Adam(model.parameters(), lr=1.0) # 初始lr会被lambda函数覆盖
scheduler = LambdaLR(optimizer, lr_lambda=lr_lambda)# 训练过程
for epoch in range(30):train_one_epoch(...)scheduler.step()
代码实现 2:分层自定义调整
# 为CNN的不同层设置不同学习率规则
optimizer = optim.SGD([# 预训练卷积层:学习率调整倍数为0.1×(0.95^epoch){'params': model.conv_layers.parameters(), 'lr': 0.01},# 新增全连接层:学习率调整倍数为0.5×(0.9^epoch){'params': model.fc_layers.parameters(), 'lr': 0.1}
], momentum=0.9)# 为不同参数组定义lambda函数
def lr_lambda_group(epoch):return [0.1 * (0.95 ** epoch), 0.5 * (0.9 ** epoch)]scheduler = LambdaLR(optimizer, lr_lambda=lr_lambda_group)
四、CNN 训练中学习率调整的实战建议
-
初始学习率选择:
- 新模型训练:建议从
0.01
(SGD)或0.001
(Adam)开始,避免初始值过大导致损失爆炸; - 迁移学习微调:预训练层初始学习率建议缩小 10~100 倍(如
0.0001
),新增层用正常学习率(如0.01
)。
- 新模型训练:建议从
-
调度器选择优先级:
- 简单任务(如 MNIST):优先用
StepLR
或MultiStepLR
,配置简单; - 复杂任务(如 ResNet 训练):优先用
CosineAnnealingLR
或ReduceLROnPlateau
,提高收敛稳定性; - 分层训练(如迁移学习):必须用
LambdaLR
实现分层调整。
- 简单任务(如 MNIST):优先用
-
避免常见误区:
- 不要在每个迭代(iteration)后调用
scheduler.step()
(除非特殊需求),通常在每个 epoch 后调用; ReduceLROnPlateau
需传入监测指标(如val_loss
),不可省略;- 训练中断后恢复时,需确保调度器的 epoch 计数与模型训练进度一致,避免学习率错乱。
- 不要在每个迭代(iteration)后调用
-
学习率可视化:
可通过matplotlib
绘制学习率变化曲线,验证调度器是否符合预期:import matplotlib.pyplot as pltlr_list = [] optimizer = optim.SGD(model.parameters(), lr=0.01) scheduler = StepLR(optimizer, step_size=10, gamma=0.5)for epoch in range(50):scheduler.step()lr_list.append(scheduler.get_last_lr()[0])plt.plot(range(50), lr_list) plt.xlabel("Epoch") plt.ylabel("Learning Rate") plt.title("StepLR Learning Rate Curve") plt.show()