模型的参数(具有requires_grad=True的权重和偏差)在其.grad属性中包含已计算的梯度。这些梯度是在损失计算并使用loss.backward()执行反向传播时生成的。它们,例如$\nabla_{\theta} L$,表示在参数空间中会使损失增长最快的方向。为了使损失最小化,我们需要朝着相反的方向调整参数。这正是优化器的作用。在第4章中,您学习了如何实例化优化器,例如torch.optim.SGD或torch.optim.Adam,并传入模型的参数(model.parameters())以及学习率等配置信息。现在,在训练循环中,您使用优化器的step()方法来执行参数更新。# 假设模型、损失函数和优化器已定义 # optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # ... 在一个批次的训练循环内部 ... # 前向传播 outputs = model(inputs) # 计算损失 loss = criterion(outputs, labels) # 反向传播 - 计算梯度 loss.backward() # 使用优化器更新权重 optimizer.step()调用optimizer.step()会遍历在优化器初始化时注册的所有参数。对于每个参数p,它会使用存储在p.grad中的梯度来更新参数值p.data。随机梯度下降(SGD)使用的最基本的更新规则是: $$ \text{参数} = \text{参数} - \text{学习率} \times \text{梯度} $$ 或者更正式地,对于参数$\theta$: $$ \theta_{new} = \theta_{old} - \eta \nabla_{\theta} L $$ 其中$\eta$是学习率(类似于创建优化器时传入的lr参数),而$\nabla_{\theta} L$是通过loss.backward()计算的梯度,优化器通过parameter.grad在内部访问它。不同的优化器实现了更复杂的更新规则。例如,像Adam这样的优化器对每个参数使用自适应学习率并融入动量思想,但核心思想保持不变:使用计算出的梯度调整参数以最小化损失。optimizer.step()调用将所选优化算法定义的具体更新逻辑进行了封装。在调用optimizer.step()之前,必须先调用loss.backward()。backward()调用计算梯度,而step()调用则使用这些梯度来更新权重。如果不先调用backward(),.grad属性将不会被填充(或者会包含来自上一次迭代的旧值),优化器也就无法知道如何有效地调整参数。更新权重后的下一个重要步骤是在处理下一个批次之前清除梯度。我们将在下一节“梯度清零”中介绍这一点。