loss.backward()
与模型之间的关系是通过计算图(computation graph)和自动求导系统(autograd system)建立的。在PyTorch中,当你对张量进行操作时,这些操作会被记录在一个动态构建的计算图中。如果这些张量中的任何一个具有 requires_grad=True
属性,那么所有对该张量的操作都会被跟踪,以便之后可以计算梯度。
下面是loss.backward()
如何与模型扯上关系的具体机制:
-
模型定义:当你定义一个继承自
nn.Module
的类来创建你的模型时,你通常会在模型内部定义各种层和参数。这些层和参数通常是torch.nn.Parameter
类型的对象,它们默认会设置requires_grad=True
。 -
前向传播:在前向传播阶段,输入数据通过模型的各个层,每一层都执行特定的操作。由于模型的参数设置了
requires_grad=True
,所以所有的计算都会被跟踪,并且构建了从输入到输出的计算图。 -
损失计算:前向传播的结果(预测值)和真实标签一起被送入损失函数计算损失。这个损失是一个标量值,它同样也是一个带有计算历史的张量。
-
反向传播:当你调用
loss.backward()
时,PyTorch会从损失开始,沿着计算图反向遍历,使用链式法则计算每个参数相对于损失的梯度。因为模型的参数参与了前向传播过程中的计算,所以它们自然地成为了计算图的一部分,因此它们的梯度也会在这个过程中被计算出来。 -
梯度更新:一旦
loss.backward()
执行完毕,所有可求导的参数都会在其.grad
属性中存储对应的梯度。然后你可以使用优化器(如SGD、Adam等)来更新模型参数,从而最小化损失函数。 -
梯度清零:为了防止梯度累积,通常在每次迭代开始时需要调用
optimizer.zero_grad()
来清除上次迭代留下的梯度信息。
简而言之,loss.backward()
是通过计算图将损失与模型参数关联起来,使得我们可以计算出损失对于每个模型参数的梯度,进而实现模型参数的更新。这是训练神经网络的核心步骤之一,它允许我们根据训练数据调整模型参数以改进模型性能。
在PyTorch中,默认情况下,普通的张量(tensor)不会记录任何操作,因此也不会自动计算梯度。只有当一个张量的 requires_grad
属性被显式地设置为 True
时,PyTorch 才会开始跟踪对该张量执行的所有操作,并构建计算图,以便之后可以调用 .backward()
来计算梯度。
具体来说:
-
参数张量:模型中的参数(例如权重和偏置),通常是由
torch.nn.Parameter
创建的,它们默认有requires_grad=True
,所以它们的操作会被跟踪。 -
输入数据:通常输入数据(如来自训练集的特征)是不需要计算梯度的,所以它们通常是不带
requires_grad
或者requires_grad=False
的。但是如果你希望对输入也计算梯度(比如在某些特定任务中),你可以手动设置requires_grad=True
。 -
中间变量:在
forward
函数内部创建的张量如果是由带有requires_grad=True
的张量衍生出来的,那么这些中间变量也会被跟踪,因为它们继承了requires_grad
属性。然而,一旦计算完成,你通常不需要保留这些中间变量,所以在反向传播后它们可以被释放。
在前向传播过程中,只有那些 requires_grad=True
的张量及其衍生出的张量会被记录下来以供后续的梯度计算。如果你不想让某个张量的操作被跟踪,确保它的 requires_grad
设置为 False
。如果你正在调试或者想要检查哪些张量参与了自动求导,你可以遍历你的代码并检查每个张量的 requires_grad
属性。
查看requires_grad:
import torch.nn as nnclass MyModel(nn.Module):def __init__(self):super(MyModel, self).__init__()self.fc = nn.Linear(10, 1)def forward(self, x):return self.fc(x)model = MyModel()# 遍历模型的所有参数,检查 requires_grad 属性
for name, param in model.named_parameters():print(f"Parameter {name} requires_grad: {param.requires_grad}")
线性优化小案例:
import torch
import torch.nn as nn# 创建一个简单的线性模型
class LinearModel(nn.Module):def __init__(self):super(LinearModel, self).__init__()# 定义一个单一的线性层self.linear = nn.Linear(1, 1)def forward(self, x):return self.linear(x)# 初始化模型、损失函数和优化器
model = LinearModel()
criterion = nn.MSELoss() # 均方误差损失
optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 随机梯度下降优化器# 构造一些假数据
x_data = torch.tensor([[1.0], [2.0], [3.0]]) # 输入
y_data = torch.tensor([[2.0], [4.0], [6.0]]) # 目标输出# 前向传播:计算预测值
pred_y = model(x_data)# 计算损失
loss = criterion(pred_y, y_data)# 反向传播:计算梯度
loss.backward()# 打印权重和偏置的梯度
for param in model.parameters():print(param.grad) #wx+b# 更新参数:权重和偏置
optimizer.step()# 清空梯度
optimizer.zero_grad()# 第二次优化# 前向传播:计算预测值
pred_y = model(x_data)# 计算损失
loss = criterion(pred_y, y_data)# 反向传播:计算梯度
loss.backward()# 打印权重和偏置的梯度
for param in model.parameters():print(param.grad) #wx+b# 更新参数
optimizer.step()# 清空梯度
optimizer.zero_grad()