
优化目标:
梯度下降法 (Gradient descent): 求偏导(partial derivative)
核心算法: 反向传播(backpropagation)
假设深度学习模型为
优化目标
链式法则展开:
偏导的构建

线性层:
损失函数:
前向传播:
其中
nn.Module,Tensor以及用户实现的forward方法requires_grad=True即可启用对该Tensor的梯度计算backward() 自动计算梯度optimizer = torch.optim.SGD(net.parameters(), lr=0.01)
for epoch in range(100):
# 前向传播
y = net(x)
loss = criterion(y, target)
# 反向传播
optimizer.zero_grad() # 清零梯度
loss.backward() # 计算梯度
optimizer.step() # 更新参数
requires_grad=True 的参与者会被追踪。
.gradz=x+y, z中包含grad_fn,是用于记录的Function对象,描述生成该tensor的运算方式,以及反向传播时如何计算梯度import torch
x = torch.ones(2, 2, requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()
print(x.grad_fn) # None,因为x是叶子张量
print(y.grad_fn) # <AddBackward0>,表示 y = x + 2
print(z.grad_fn) # <MulBackward0>,表示 z = y * y * 3
print(out.grad_fn) # <MeanBackward0>,表示 mean 操作
y是通过加法操作得到的。z是通过乘法操作得到的。均值操作的结果。 out = z.mean() (grad_fn = MeanBackward0)
^
|
z = y*y*3 (grad_fn = MulBackward0)
^
|
y = x+2 (grad_fn = AddBackward0)
^
|
x (leaf, requires_grad=True, grad_fn=None)
nn.Linear/F.linear 的核心反向
AddmmBackward0 等后缀变体transpose 的反向,是线性层实现里常见的辅助节点(例如 W.t())。反传中将梯度再转回原始维度Linear.weight/bias 或 requires_grad=True 的输入)的 .grad 中,并按步累加当执行out.backward()时,PyTorch 会从out.grad_fn出发,沿着计算图往回追踪,调用每个grad_fn对应的backward()方法,逐步计算出各个叶子节点(比如参数x)的梯度。
几个注意点:
grad_fn一般是None。grad_fn会变成AccumulateGrad或者触发警告。.detach()或.data时,得到的新张量也不会有grad_fn,因为它被认为是从计算图中“断开”了。grad_fn,叶子张量的梯度写入 .grad。backward() 等价逐层执行 y.backward()
y=model(x),假设y.shape=[batch,dim],直接调用y.backward()会报错,因为torch内部不知道到底该对哪个方向做反向传播y.backward(gradient=v),v 与 y 同形(shape)表示上游grad权重。AddmmBackward(Linear/F.linear 反向)、TBackward(转置辅助)、AccumulateGrad(叶子梯度累积)。zero_grad() 防梯度累积;参数更新放优化器或 no_grad();避免对中间结果原地修改。create_graph=True 与 retain_graph=True;默认一次反向后释放图。torch.compile;结合 hooks 与 notebook 的图打印定位梯度流。注:理论上可把 Autograd 看成“雅可比链式法则”的高效实现,理解这一点即可,不必掌握雅可比的形式化定义再上手工程实践。
h = []
def log_grad(grad):
h.append((grad.mean().item(), grad.norm().item()))
out = net(x)
out.register_hook(log_grad) # 观察上游梯度
loss = criterion(out, y)
loss.backward()
.grad 默认累加;训练循环应先清零再 backward()。optimizer.zero_grad(set_to_none=True)
loss.backward()
optimizer.step()
create_graph=True 构建可微分的反向图。retain_graph=True。g = torch.autograd.grad(loss, params, create_graph=True)
g2 = torch.autograd.grad(sum(p.sum() for p in g), params)
x.detach():把张量从当前计算图中分离,后续关于该结果的计算不会把梯度回传到被分离的分支。与原张量共享存储(谨慎原地写)。
注意事项:
no_grad/inference_mode,否则无法计算梯度。detach() 会打断梯度流,误用会让模型学不动;仅在确需截断时使用。with torch.no_grad()::上下文内不记录计算图,不分配 grad_fn/中间量,因此这些计算不参与 loss.backward()。
model.eval())。with torch.inference_mode():面向纯推理/部署的模式
| 特性 | no_grad |
inference_mode |
说明 |
|---|---|---|---|
| 构图 | 关闭 | 关闭 | 两者都不记录计算图 |
| 版本计数/视图跟踪 | 保留 | 跳过 | inference_mode 更省内存/检查更少 |
| 内存/速度 | 省 | 更省/更快 | 大模型推理建议 inference_mode |
| 训练期使用 | 可用于局部(如指标、EMA) | 不建议 | inference_mode 仅用于纯推理 |
| 原地修改检测 | 有检查 | 检查更少/可能被绕过 | 训练期更安全用 no_grad |
Loss = (Y**2).sum()(标量,便于直接反传)import torch
torch.manual_seed(0)
B,H,D,E = 2,3,4,5
X = torch.randn(B,H,D, dtype=torch.float64, requires_grad=True)
W = torch.randn(D,E, dtype=torch.float64, requires_grad=True)
Y = X @ W
Loss = (Y**2).sum()
Loss.backward()
grad_X_auto, grad_W_auto = X.grad.clone(), W.grad.clone()
G = 2 * Y.detach()
dW = X.detach().reshape(-1,D).T @ G.reshape(-1,E)
dX = torch.matmul(G, W.detach().T)
assert torch.allclose(grad_W_auto, dW, rtol=1e-6, atol=1e-6)
assert torch.allclose(grad_X_auto, dX, rtol=1e-6, atol=1e-6)
contiguous() 后再 view/reshape,避免 stride 问题。db 对非通道维求和;检查维度顺序。backward() 会累加 .grad,对比前先 zero_grad()。torch.autograd.gradcheck。tensor.register_hook 打印关键梯度的均值/范数。https://marp.app/

- `model.zero_grad(set_to_none=True)` 降低显存碎片与加速。