PyTorch 的优化器模块 torch.optim,是神经网络训练中负责更新模型参数的核心模块。它根据自动求导得到的梯度,按照一定的优化算法调整权重和偏置,使模型的损失函数逐步下降。
简单地说,torch.optim 模块回答的是:模型参数应该朝哪个方向更新、每次更新多少,以及如何让训练过程更稳定、更高效。
如果说 torch.nn 模块负责构建模型,torch.autograd 模块负责计算梯度,那么 torch.optim 模块就负责把梯度真正转化为参数更新。没有优化器,模型即使能够计算损失和梯度,也无法通过训练不断改进。
一、认识 torch.optim 模块
torch.optim 是 PyTorch 中专门用于优化模型参数的模块。它提供了多种常见优化算法,如 SGD、Adam、AdamW、RMSprop 等。
![]()
图 1:优化器模块在 PyTorch 训练流程中的位置
在一个典型训练过程中,优化器通常位于反向传播之后、下一轮前向传播之前:
→ 进入下一轮训练一个最简单的优化器使用方式如下:
这里的核心是:
• model.parameters() 提供需要更新的模型参数
• optim.Adam(...) 指定使用 Adam 优化算法
• lr=0.001 指定学习率
优化器本身并不定义模型结构,也不负责计算梯度。它只根据参数已有的 .grad 信息执行更新。
二、优化器在训练闭环中的作用
神经网络训练的目标,是让损失函数尽可能小。优化器的作用,就是根据梯度信息调整参数,使模型向更小的损失方向移动。
![]()
图 2:从梯度到参数更新的基本过程
一个简化的参数更新过程可以理解为:
其中:
• θ 表示模型参数
• L 表示损失函数
• ∇θL 表示损失函数对参数 θ 的梯度
• η 表示学习率
• ← 表示用新值替换旧值
这条公式体现了梯度下降的基本思想:如果梯度指出损失上升最快的方向,那么参数更新就朝相反方向移动,从而尽量降低损失。
在 PyTorch 中,这个过程通常由三行代码完成:
optimizer.step()它们分别表示:
• 清空旧梯度
• 反向传播计算新梯度
• 根据梯度更新参数
这三步是理解 PyTorch 优化器的关键。
三、创建优化器:把参数交给优化算法
创建优化器时,通常需要把模型参数传入优化器:
这里的 model.parameters() 会返回模型中所有需要训练的参数,包括各层的权重和偏置。
可以查看这些参数:
print(name, param.shape, param.requires_grad)输出结果类似:
2.bias torch.Size([3]) True优化器只会更新传入给它的参数。如果某个参数没有被传入优化器,即使它有梯度,也不会被这个优化器更新。
这也是为什么自定义模型时,网络层通常要定义在 __init__() 中,并正确注册为 nn.Module 的子模块。只有被模型管理的参数,才能通过 model.parameters() 被优化器找到。
四、zero_grad():清空上一轮梯度
在 PyTorch 中,梯度默认会累积,而不是每次自动覆盖。因此,每次反向传播前通常需要清空旧梯度。
例如:
optimizer.zero_grad()如果不清空梯度,当前批次计算出的梯度会与上一轮梯度相加,导致参数更新不符合预期。
一个简单例子如下:
第二次打印的 x.grad 并不是只包含 y2 对 x 的梯度,而是前后两次梯度的累加。
在普通训练循环中,通常应写成:
optimizer.step()这个顺序很重要:
• 先清空旧梯度
• 再计算当前梯度
• 最后更新参数
也可以写成:
optimizer.zero_grad(set_to_none=True)这种写法会把梯度设为 None,在某些场景下可以减少内存占用。但初学阶段可以先使用默认写法,理解梯度清零的作用更重要。
五、backward() 与 step() 的关系
loss.backward() 和 optimizer.step() 经常连在一起出现,但它们的作用完全不同。
loss.backward()表示:根据当前计算图,计算损失函数对模型参数的梯度。
optimizer.step()表示:根据参数中的 .grad,按照优化算法更新参数。
例如:
这里可以理解为:
• pred = model(x) 构建前向计算图
• loss = criterion(pred, target) 得到损失
• loss.backward() 计算梯度
• optimizer.step() 使用梯度更新参数
需要注意的是,optimizer.step() 不会自动执行反向传播。如果没有先调用 loss.backward(),参数通常没有可用梯度,优化器也就无法完成有效更新。
六、学习率:控制每次参数更新的步长
学习率(Learning Rate)是优化器中最重要的超参数之一。它决定每次参数沿梯度方向更新的幅度。
学习率过大,可能导致训练震荡,甚至损失无法下降;学习率过小,训练会非常缓慢,甚至长时间停留在效果较差的位置。
例如:
optimizer = optim.SGD(model.parameters(), lr=0.01)其中 lr=0.01 就是学习率。
可以把学习率理解为参数更新的“步长”:
其中 η 就是学习率。
常见经验是:
• SGD 常使用相对较大的学习率,例如 0.1、0.01、0.001
• Adam 常使用较小的学习率,例如 0.001、0.0001
• 学习率没有固定最优值,需要结合任务、模型和数据调整
在实际训练中,如果损失剧烈震荡,可以尝试降低学习率;如果损失下降很慢,可以尝试适当增大学习率。
七、SGD:最基础的梯度下降优化器
SGD 是随机梯度下降(Stochastic Gradient Descent)的缩写,是最基础、也最重要的优化算法之一。
基本写法如下:
SGD 的基本思想是:根据当前批次数据计算出的梯度,更新模型参数。
加入动量(momentum)后,SGD 可以在一定程度上减少震荡,并加快沿稳定方向的更新:
动量可以理解为给参数更新加入“惯性”。如果多个批次的梯度方向比较一致,参数会沿这个方向更稳定地前进;如果梯度方向频繁变化,动量可以缓解短期波动。
SGD 的优点是:
• 原理清晰
• 行为可控
• 在许多任务中泛化表现较好
SGD 的不足是:
• 对学习率较敏感
• 训练速度可能较慢
• 通常需要更仔细地调参
因此,在教学和理解优化原理时,SGD 非常重要;在实际工程中,Adam 和 AdamW 也非常常用。
八、Adam:自适应学习率优化器
Adam 是深度学习中非常常用的优化器。它会根据梯度的一阶矩和二阶矩估计,为不同参数自适应地调整更新幅度。
常见写法如下:
optimizer = optim.Adam(model.parameters(), lr=0.001)Adam 的优势是:
• 对学习率相对不那么敏感
• 收敛通常较快
• 适合许多神经网络任务
• 初学和实验阶段使用方便
一个完整示例如下:
这里要注意:Adam 只是参数更新算法,不改变模型结构,也不改变损失函数的定义。
在很多入门实验中,Adam 是一个稳妥的默认选择;但在一些任务中,SGD、AdamW 或带学习率调度的方案可能表现更好。
九、AdamW:带解耦权重衰减的 Adam
AdamW 是 Adam 的常用改进版本,尤其在 Transformer、视觉模型和大规模预训练模型中非常常见。
常见写法如下:
AdamW 中的 weight_decay 通常用于控制模型参数不要过大,从而起到一定的正则化作用。
需要注意的是,weight_decay 并不等同于学习率。它控制的是参数衰减强度,而学习率控制的是梯度更新步长。
可以简单理解为:
• lr 决定每次更新走多远
• weight_decay 限制参数不要无限变大
• AdamW 把权重衰减与 Adam 的梯度更新机制更清晰地分离
在现代深度学习实践中,如果模型比较复杂,尤其是使用 Transformer 类结构,AdamW 往往比普通 Adam 更常见。
十、RMSprop 与其他优化器
除了 SGD、Adam 和 AdamW,PyTorch 还提供了其他优化器,例如:
• optim.RMSprop
• optim.Adadelta
• optim.Adagrad
• optim.NAdam
• optim.LBFGS
其中,RMSprop 也是一种自适应学习率优化器,曾在循环神经网络等任务中较常见。
示例:
不同优化器的差异,主要体现在如何利用梯度历史信息、如何调整不同参数的更新幅度,以及是否引入动量、权重衰减等机制。
初学阶段不必一次掌握所有优化器。更重要的是先理解:
• 优化器接收模型参数
• 反向传播得到梯度
• 优化器根据梯度更新参数
• 不同优化器只是更新策略不同
十一、参数组:为不同参数设置不同学习率
有时,一个模型中不同部分需要使用不同的学习率。例如,在迁移学习中,预训练主干网络可以使用较小学习率,新加的分类头可以使用较大学习率。
这时可以使用参数组:
完整示例如下:
这里可以理解为:
• feature 部分学习率较小,避免大幅改变已有特征表示
• classifier 部分学习率较大,使新任务头更快适应当前任务
参数组还可以为不同部分设置不同的 weight_decay、momentum 等选项。
十二、冻结参数与优化器
在迁移学习中,经常需要冻结部分参数,只训练某些层。
例如:
冻结后,最好只把仍然需要训练的参数交给优化器:
这样做可以让训练目标更清晰,也避免优化器持有不需要更新的参数。
完整示例:
需要注意的是,设置 requires_grad=False 会阻止梯度计算,但如果优化器早已创建,仍可能持有旧参数引用。因此,冻结参数后,通常应重新创建优化器或确认优化器中的参数组是否正确。
十三、学习率调度器:动态调整学习率
训练过程中,学习率不一定始终固定。有时训练前期需要较大学习率快速下降,后期需要较小学习率精细调整。
PyTorch 提供了学习率调度器,例如:
• StepLR
• MultiStepLR
• ExponentialLR
• CosineAnnealingLR
• ReduceLROnPlateau
一个简单示例:
在训练循环中通常写成:
这里的含义是:每经过 10 个 epoch,学习率乘以 0.1。
学习率调度器的作用不是直接更新模型参数,而是调整优化器中的学习率,从而影响后续参数更新幅度。
需要注意的是,不同调度器的调用时机可能不同。有些按 epoch 调整,有些按 batch 调整,有些根据验证集指标调整。实际使用时应根据调度器设计选择合适的位置。
十四、保存与恢复优化器状态
训练模型时,有时不仅要保存模型参数,还要保存优化器状态。因为 Adam、AdamW、RMSprop 等优化器内部会维护梯度历史信息。
保存方式如下:
恢复训练时:
如果只是推理,通常只需要加载模型参数;如果要从中断处继续训练,最好同时恢复优化器状态。
原因是:优化器状态会影响后续参数更新。如果只恢复模型参数而不恢复优化器状态,训练虽然可以继续,但优化过程可能与中断前不完全一致。
十五、一个完整示例:用优化器训练简单分类模型
![]()
图 3:优化器驱动的神经网络训练闭环
下面用一个完整示例,把模型、损失函数、自动求导和优化器串起来:
这个训练闭环可以概括为:
前向传播 → 计算损失 → 清空梯度 → 反向传播 → 参数更新
其中:
• torch.nn 负责模型结构和损失函数
• torch.autograd 负责自动计算梯度
• torch.optim 负责根据梯度更新参数
优化器并不是单独工作的,它必须与模型、损失函数和自动求导机制配合,才能完成真正的训练过程。
十六、使用优化器时应注意的问题
1、不要忘记 zero_grad()
训练循环中通常要写:
optimizer.step()如果忘记 zero_grad(),梯度会不断累积,导致参数更新异常。
2、不要把 step() 放在 backward() 前面
错误写法:
loss.backward()正确写法:
optimizer.step()因为优化器需要根据已经计算好的梯度更新参数。
3、优化器只会更新传入的参数
如果某些参数没有传给优化器,它们不会被更新。
例如:
optimizer = optim.Adam(model.classifier.parameters(), lr=0.001)这表示只更新 classifier 部分,而不是整个模型。
4、冻结参数后要检查优化器
如果先创建优化器,再冻结参数,优化器中可能仍然保存了原来的参数组。更稳妥的做法是:冻结参数后重新创建优化器。
5、学习率过大或过小都会影响训练
学习率过大,损失可能震荡或发散;学习率过小,训练可能非常缓慢。遇到训练不稳定时,学习率通常是最先需要检查的超参数之一。
6、Adam 不等于一定更好
Adam 使用方便,收敛速度通常较快,但并不意味着在所有任务中都优于 SGD。不同优化器适合不同任务,实际效果需要通过实验验证。
7、权重衰减不是学习率
lr 控制参数更新步长,weight_decay 控制参数衰减强度。二者作用不同,不应混淆。
8、继续训练时应保存优化器状态
如果要从中断处继续训练,建议同时保存:
optimizer.state_dict()否则模型参数虽然恢复了,但优化器内部状态没有恢复,后续训练轨迹可能发生变化。
小结
torch.optim 模块负责根据梯度更新模型参数,是神经网络训练闭环中的关键环节。学习优化器,重点是理解 zero_grad() 清空旧梯度、backward() 计算新梯度、step() 执行参数更新,以及学习率、权重衰减、参数组和学习率调度器如何影响训练过程。
“点赞有美意,赞赏是鼓励”
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.