Python 现代开发学习笔记

(十三)优化器与每批一次更新

# 项目文件:demos/sgd_loop.py
import torch
from torch import nn

model = nn.Linear(1, 1)
with torch.no_grad():
    model.weight.fill_(1)
    model.bias.zero_()
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
x, y = torch.tensor([[2.0]]), torch.tensor([[5.0]])
for epoch in range(3):
    optimizer.zero_grad(set_to_none=True)  # 清理上一轮梯度。
    loss = nn.MSELoss()(model(x), y)
    loss.backward()  # 只计算梯度;参数更新由 step 执行。
    optimizer.step()
    print("轮数", epoch + 1, "更新前损失", round(loss.item(), 6))
print("最终预测", round(model(x).item(), 6))

这份程序把线性层的初始权重设为 1、偏置设为 0。SGD 的基本更新仍为 θ←θ−η∇L\theta\leftarrow\theta-\eta\nabla L,所以第一轮与手算完全相同。三次更新前的损失依次为 9、2.25、0.5625,第三次更新后的预测为 4.625。

调用 这一步实际发生的动作
optimizer.zero_grad(set_to_none=True) 清理该优化器管理的参数梯度
model(x)、计算损失 用当前参数做前向,并建立本批计算图
loss.backward() 把梯度累积到参数的 .grad
optimizer.step() 按优化器规则修改参数
按清理梯度、前向、反向、更新排列一批的动作,参数更新后下一批重新计算,不把 backward 误当成参数修改

完整项目使用 torch.optim.Adam。它还为各参数维护梯度的平滑统计和平方统计,按这些状态调整更新幅度。调用顺序与上表相同,具体更新规则与 SGD 不同,因此恢复 Adam 训练时还要保存优化器状态,不能只装回权重。

一批中有多条样本,一轮(epoch)会把训练集安排的各批走完;更新次数由批次数决定。144 条训练记录、每批 24 条时,一轮执行六次参数更新。

笔记正文与全部题解 · Ctrl / ⌘ K