一文讲清 PyTorch 优化器(Optimizer)

196 阅读4分钟

我们用生活化比喻 + 图解 + 代码示例 + 场景对比,向初学者彻底讲清楚:


🎯 PyTorch 优化器(Optimizer)—— 通俗易懂完全指南

💡 一句话总结:优化器 = 根据“梯度”自动调整“模型参数”的智能教练,目标是让损失函数越来越小!


一、生活化比喻:健身教练 🏋️‍♂️

你请了一位AI健身教练,目标是减脂增肌(最小化损失)。

  • 🧠 梯度 = 教练告诉你:“你上次练腿不够,手臂练过头了”
  • 🎯 优化器 = 教练根据这些反馈,制定下周训练计划
    • 腿部训练量 +20%
    • 手臂训练量 -10%
    • 饮食蛋白质 +5g/天
  • 🔄 循环:训练 → 反馈 → 调整计划 → 再训练 → 直到身材达标!

优化器 = 自动帮你“调参”的智能算法


二、为什么需要优化器?

在训练神经网络时:

  1. 前向传播 → 得到预测
  2. 计算损失 → 衡量错误
  3. 反向传播 → 得到梯度(每个参数该往哪调、调多少)
  4. 怎么用梯度更新参数?

👉 优化器就是解决第4步的!

没有优化器,你得手动写:

w = w - learning_rate * w.grad  # 原始梯度下降

有了优化器 → 一行代码搞定,还支持高级算法!


三、优化器核心原理(以最基础的 SGD 为例)

📐 数学公式:

参数更新 = 旧参数 - 学习率 × 梯度
# 手动更新(不推荐)
w = w - lr * w.grad
b = b - lr * b.grad

✅ 用优化器(推荐):

optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
optimizer.step()  # 自动更新所有参数!

🧠 优化器内部会遍历所有 requires_grad=True 的参数,按公式更新!


四、常用优化器对比与使用场景

优化器全称特点适用场景
SGD随机梯度下降最基础,可加 momentum教学、简单任务、需要精细调参
AdamAdaptive Moment Estimation自适应学习率,收敛快,鲁棒性强默认推荐!90%场景都可用
RMSpropRoot Mean Square Prop自适应学习率,适合非稳态目标RNN、非凸优化
AdagradAdaptive Gradient学习率自适应,对稀疏特征友好NLP、稀疏数据
AdamWAdam + Weight DecayAdam 的改进版,更好处理权重衰减最新SOTA模型常用

五、代码示例:完整训练循环

import torch
import torch.nn as nn
import torch.optim as optim

# 定义简单模型
model = nn.Linear(10, 1)

# 选择优化器(这里用 Adam)
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 损失函数
criterion = nn.MSELoss()

# 假数据
x = torch.randn(100, 10)
y_true = torch.randn(100, 1)

# 训练循环
for epoch in range(100):
    optimizer.zero_grad()          # 1️⃣ 清空梯度
    
    y_pred = model(x)              # 2️⃣ 前向传播
    loss = criterion(y_pred, y_true)  # 3️⃣ 计算损失
    
    loss.backward()                # 4️⃣ 反向传播 → 计算梯度
    optimizer.step()               # 5️⃣ 优化器更新参数 ← 关键!

    if epoch % 20 == 0:
        print(f"Epoch {epoch}, Loss: {loss.item():.4f}")

📌 黄金五步
zero_grad()forward()lossbackward()step()


六、不同优化器代码对比

# 1. 基础 SGD(可加动量)
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

# 2. Adam(推荐默认)
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 3. AdamW(最新推荐)
optimizer = optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.01)

# 4. RMSprop
optimizer = optim.RMSprop(model.parameters(), lr=0.01)

# 5. Adagrad
optimizer = optim.Adagrad(model.parameters(), lr=0.01)

✅ 初学者直接用 Adam,效果通常很好!


七、优化器高级功能

✅ 1. 学习率调度(Learning Rate Scheduler)

训练后期降低学习率,让模型更精细收敛:

optimizer = optim.Adam(model.parameters(), lr=0.001)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)

for epoch in range(100):
    train(...)
    scheduler.step()  # 每轮更新学习率
    print(f"当前学习率: {scheduler.get_last_lr()[0]:.6f}")

✅ 2. 权重衰减(Weight Decay)— 防止过拟合

optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)
# 相当于在损失函数中加了 L2 正则化

✅ 3. 不同参数组不同学习率

optimizer = optim.Adam([
    {'params': model.base.parameters(), 'lr': 1e-4},
    {'params': model.classifier.parameters(), 'lr': 1e-3}
])

八、初学者常见误区 & 注意事项

❌ 误区1:忘记 zero_grad()

# ❌ 错误:梯度会累加!
loss.backward()
optimizer.step()

# ✅ 正确:每次更新前清零
optimizer.zero_grad()
loss.backward()
optimizer.step()

🧠 比喻:不清空旧训练计划,新计划会和旧计划叠加 → 训练混乱!


❌ 误区2:在 optimizer.step() 后访问梯度

optimizer.step()
print(w.grad)  # ❌ 梯度可能已被清空或无效!

✅ 如果要查看梯度,在 backward() 后、step() 前查看!


❌ 误区3:学习率设太大或太小

  • 太大 → 震荡不收敛
  • 太小 → 收敛太慢

常用学习率范围

  • SGD: 0.01 ~ 0.1
  • Adam: 0.001 ~ 0.0001

九、优化器选择建议(初学者版)

场景推荐优化器学习率建议
第一次跑模型Adam0.001
图像分类(ResNet等)SGD + momentum0.1 → 逐步衰减
NLP / TransformerAdamW5e-5 ~ 3e-4
研究对比实验SGD(更稳定)需仔细调参
不知道用啥Adam0.001

十、总结:优化器四步使用法

  1. 创建optimizer = optim.XXX(model.parameters(), lr=...)
  2. 清零optimizer.zero_grad() ← 每轮开始!
  3. 反向loss.backward() ← 计算梯度
  4. 更新optimizer.step() ← 根据梯度更新参数

🎁 给初学者的终极口诀:

“优化器是调参手,
梯度来了它就走,
清零反向再更新,
Adam 默认不用愁!”


🧠 动手小练习:

  1. SGD 优化器训练一个线性回归模型
  2. 对比 SGDAdam 在相同学习率下的收敛速度
  3. 尝试添加 weight_decay=1e-4,观察过拟合是否改善

🎉 恭喜你!现在你已经掌握了优化器的核心原理和实战技巧!
它是连接“梯度”和“参数更新”的桥梁 —— 没有它,模型就学不会!