优化目标:
梯度下降法 (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
的参与者会被追踪。
.grad
z=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)` 降低显存碎片与加速。