1️、背景:自动求导机制(Autograd)
-
PyTorch 使用 动态图机制 (Dynamic Computation Graph):
- 每一次
forward
操作都会在后台构建一张计算图(autograd graph
)。 - 计算图的节点:
Tensor
- 边:运算(
Function
)
- 每一次
-
调用
.backward()
时,PyTorch 会从 标量(loss)开始 ,沿着计算图反向传播,自动计算各个参数的梯度,并存储在tensor.grad
中。
2️、基本用法
python
import torch
# 定义张量,开启梯度跟踪
x = torch.tensor(2.0, requires_grad=True)
y = x**2 + 3*x + 1 # y = x^2 + 3x + 1
# 反向传播
y.backward()
print(x.grad) # dy/dx = 2x + 3 = 7
3️、.backward()
的常见参数
(1) gradient=None
- 适用于 标量张量(只含一个值)。
- 如果
tensor
不是标量(多元素张量),必须传入gradient
参数(同 shape 的权重),告诉 PyTorch 怎么把向量 → 标量。
例子:
python
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x**2
# y = [1,4,9],不是标量
# 如果直接 y.backward() 会报错
# 传入 gradient 向量,等价于对 sum(y) 求导
y.backward(torch.tensor([1.0, 1.0, 1.0]))
print(x.grad) # [2,4,6]
(2) retain_graph=False
- 默认情况下,
backward()
执行后会 释放计算图(节省内存)。 - 如果你需要对 同一个图多次反向传播 ,必须设
retain_graph=True
。
python
x = torch.tensor(2.0, requires_grad=True)
y = x**3
y.backward(retain_graph=True)
print(x.grad) # 12 (dy/dx=3x^2=12)
y.backward() # 第二次 backward,如果没有 retain_graph 会报错
print(x.grad) # 24 (累积梯度)
(3) create_graph=False
- 是否在反向传播时 构建计算图(用于高阶导数)。
- 默认 False(只算一次梯度)。
- 如果要继续对梯度求导,就需要
create_graph=True
。
python
x = torch.tensor(2.0, requires_grad=True)
y = x**3
dy_dx = torch.autograd.grad(y, x, create_graph=True)[0]
# dy/dx = 12
d2y_dx2 = torch.autograd.grad(dy_dx, x)[0]
print(d2y_dx2) # 12
4️、梯度累积机制
PyTorch 中 .grad
是 累积的,不会自动清零。
python
x = torch.tensor(2.0, requires_grad=True)
y1 = x**2
y1.backward()
print(x.grad) # 4
y2 = 3*x
y2.backward()
print(x.grad) # 4 + 3 = 7
# 解决方法:每次反向传播前清零
x.grad.zero_()
在训练循环中,通常这样写:
python
optimizer.zero_grad() # 清空旧梯度
loss.backward() # 计算新梯度
optimizer.step() # 更新参数
5️、.backward()
与 torch.autograd.grad
.backward()
:会把梯度 存储到 tensor.grad。torch.autograd.grad()
:返回梯度值 ,不会自动累积到.grad
。
例子:
python
x = torch.tensor(2.0, requires_grad=True)
y = x**3
# 用 backward
y.backward()
print(x.grad) # 12
# 用 autograd.grad
dy_dx = torch.autograd.grad(y, x)[0]
print(dy_dx) # 12
print(x.grad) # 12 (还是累积的)
6️、.backward()
的典型使用场景
-
训练神经网络
pythonfor data, target in dataloader: optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step()
-
自定义梯度计算(比如物理约束 loss)
pythonx = torch.randn(3, requires_grad=True) y = (x**2).sum() y.backward() print(x.grad) # 2x
-
高阶导数
pythonx = torch.tensor(1.0, requires_grad=True) y = x**3 dy_dx = torch.autograd.grad(y, x, create_graph=True)[0] d2y_dx2 = torch.autograd.grad(dy_dx, x)[0] print(d2y_dx2) # 6
7️、常见坑
- loss 不是标量 → 必须传
gradient
参数。 - 忘记清零梯度 → 梯度会累积,导致训练异常。
- 重复 backward → 必须
retain_graph=True
。 - requires_grad=False 的 tensor → 不会求导。
- in-place 操作 (如
x += 1
)可能破坏计算图,导致错误。
8、综合示例
示例 1:简单函数 + backward 验证
函数:
y=(x1⋅x2+x3)2 y = (x_1 \cdot x_2 + x_3)^2 y=(x1⋅x2+x3)2
python
import torch
# 定义输入
x1 = torch.tensor(2.0, requires_grad=True)
x2 = torch.tensor(3.0, requires_grad=True)
x3 = torch.tensor(1.0, requires_grad=True)
# 前向计算
y = (x1 * x2 + x3) ** 2
print("y =", y.item())
# 反向传播
y.backward()
# 查看梯度
print("dy/dx1 =", x1.grad.item()) # 2 * (x1*x2 + x3) * x2
print("dy/dx2 =", x2.grad.item()) # 2 * (x1*x2 + x3) * x1
print("dy/dx3 =", x3.grad.item()) # 2 * (x1*x2 + x3)
结果:
y = 49
dy/dx1 = 42
dy/dx2 = 28
dy/dx3 = 14
对应梯度推导:
- dy/dx1=2(x1x2+x3)⋅x2=2(2∗3+1)∗3=42dy/dx_1 = 2(x_1x_2 + x_3) \cdot x_2 = 2(2*3+1)*3 = 42dy/dx1=2(x1x2+x3)⋅x2=2(2∗3+1)∗3=42
- dy/dx2=2(x1x2+x3)⋅x1=2(6+1)∗2=28dy/dx_2 = 2(x_1x_2 + x_3) \cdot x_1 = 2(6+1)*2 = 28dy/dx2=2(x1x2+x3)⋅x1=2(6+1)∗2=28
- dy/dx3=2(x1x2+x3)=14dy/dx_3 = 2(x_1x_2 + x_3) = 14dy/dx3=2(x1x2+x3)=14
.backward()
自动完成了这些链式法则计算。
示例 2:神经网络训练一个回归任务
任务:用一个 简单的全连接网络 拟合函数 y=2x+3y = 2x + 3y=2x+3。
python
import torch
import torch.nn as nn
import torch.optim as optim
# 构造数据
x = torch.linspace(-5, 5, 100).unsqueeze(1) # shape [100,1]
y = 2 * x + 3 + 0.1 * torch.randn_like(x) # 加点噪声
**定义网络**
model = nn.Sequential(
nn.Linear(1, 10),
nn.ReLU(),
nn.Linear(10, 1)
)
**损失函数 + 优化器**
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
**训练**
for epoch in range(200):
optimizer.zero_grad() # 清空梯度
y_pred = model(x) # 前向
loss = criterion(y_pred, y) # 计算loss
loss.backward() # 🔹 反向传播,计算梯度
optimizer.step() # 更新参数
if epoch % 50 == 0:
print(f"Epoch {epoch}, Loss = {loss.item():.4f}")
训练过程中:
.backward()
会自动计算网络所有参数的梯度(通过计算图 + 链式法则)。optimizer.step()
利用梯度更新参数。
最终模型能学到近似 y=2x+3y = 2x + 3y=2x+3。
对比两例
- 示例 1 :展示了
.backward()
在 简单函数 中的梯度传播过程。 - 示例 2 :展示了
.backward()
在 实际深度学习训练 中的应用,自动处理整个网络的梯度。
总结
.backward()
是 PyTorch 自动求导的核心函数:
-
默认从 标量 loss 反向传播,计算所有叶子节点的梯度,存到
.grad
。 -
关键参数:
gradient
:非标量情况需要指定权重。retain_graph
:是否保留计算图,支持多次 backward。create_graph
:是否构建高阶导数的计算图。
-
梯度会 累积,必须在训练循环里手动清零。
-
.backward()
和torch.autograd.grad
可以互补使用。