1. PyTorch梯度计算机制解析
1.1 张量的梯度追踪特性
在PyTorch中,张量(tensor)是构建神经网络的基本单位。当我们手动创建张量时,默认情况下requires_grad=False,这意味着PyTorch不会自动追踪该张量的计算历史。这个设计选择非常合理——大多数情况下我们只需要对模型参数进行梯度计算,而不需要对所有中间变量都进行追踪。
要启用梯度追踪,有几种常见方式:
python复制# 方式1:创建时指定
x = torch.tensor([1.0, 2.0], requires_grad=True)
# 方式2:后续启用
x = torch.tensor([1.0, 2.0])
x.requires_grad_() # 注意带下划线的in-place操作
重要提示:启用梯度追踪会显著增加内存消耗,因为PyTorch需要保存完整的计算图用于反向传播。在不需要梯度计算的场景(如模型推理阶段),应该使用
torch.no_grad()上下文管理器来禁用梯度追踪。
1.2 反向传播与梯度累积
调用.backward()方法时,PyTorch会自动计算梯度并存储在对应张量的.grad属性中。这里有一个关键特性容易被忽视:梯度是累积的。也就是说,每次调用.backward()时,新计算的梯度会加到现有的.grad值上,而不是替换它。
这种设计在某些场景下很有用(如RNN中处理变长序列),但大多数情况下我们需要在每次反向传播前手动清零梯度:
python复制optimizer.zero_grad() # 标准做法
loss.backward()
optimizer.step()
实战经验:忘记清零梯度是初学者常犯的错误,会导致训练过程不稳定甚至完全失败。建议在训练循环开始处就写上
optimizer.zero_grad(),养成良好习惯。
1.3 梯度分离技术
有时我们需要"冻结"模型的一部分参数,或者从计算图中分离某些中间结果。这时可以使用.detach()方法:
python复制# 分离张量示例
intermediate = model(input).detach() # 停止梯度追踪
output = another_model(intermediate) # 仅对another_model计算梯度
等效的操作是使用with torch.no_grad()块:
python复制with torch.no_grad():
intermediate = model(input)
性能提示:
.detach()比创建新张量更高效,因为它共享底层数据而只是移除梯度追踪。
1.4 矢量函数的雅可比矩阵
对于矢量函数 y = f(x),梯度实际上是雅可比(Jacobian)矩阵。PyTorch的autograd引擎会
