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

线性层:
损失函数:
前向传播:计算 logits
反向传播:损失函数先传回上游梯度
共享参数的梯度需要汇总各个样本的贡献。
requires_grad=True:让张量参与梯度追踪。backward() 才执行梯度计算。nn.Module 组织模型;普通 Tensor 运算也能自动求导。前向建立反向计算所需的图,反向计算梯度,优化器更新参数。
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() # 更新参数
.grad 与 .grad_fn:数值和计算规则| 属性 | 回答的问题 | 内容 |
|---|---|---|
t.grad_fn |
这个张量由什么运算产生,如何往回求导? | 反向节点对象,或 None |
t.grad |
本次目标对这个张量的梯度是多少? | 梯度 Tensor,或 None |
.grad_fn 在被追踪的前向运算中建立,不是梯度数值。.grad 在反向时写入并累加,shape 与对应张量相同。.grad;非叶子需先调用 retain_grad()。.grad is None 表示没有保存梯度值,不等于梯度为 0。
x = torch.tensor([1., 2., 3.], requires_grad=True)
y = 2 * x
loss = y.sum()
y.retain_grad() # 为了观察,保留非叶子 y 的梯度
print(x.grad, y.grad) # None, None:尚未反向
print(x.grad_fn) # None:x 是直接创建的叶子
print(type(y.grad_fn).__name__) # MulBackward0
loss.backward()
print(x.grad) # tensor([2., 2., 2.])
print(y.grad) # tensor([1., 1., 1.])
前向得到 y=[2,4,6]、loss=12;反向得到的是它们的梯度。
节点名称的数字后缀属于实现细节。
沿用上一页:
loss.grad_fn y.grad_fn x 的梯度累加节点
SumBackward0 → MulBackward0 → AccumulateGrad
1 → [1,1,1] [1,1,1] × 2 → [2,2,2] 写入 x.grad
| 张量 | 本例中的角色 | .grad_fn |
反向后的 .grad |
|---|---|---|---|
x |
直接创建的叶子 | None |
[2,2,2],默认保存 |
y |
运算产生的非叶子 | MulBackward0 |
[1,1,1],因调用了 retain_grad() |
不调用 y.retain_grad(),梯度仍经过 y 传回 x,只是不会保存在 y.grad。
上游梯度
nn.Linear/F.linear 的核心反向
nn.Linear 的权重 AddmmBackward0 等后缀变体transpose 的反向,是线性层实现里常见的辅助节点(例如 W.t())。反传中将梯度再转回原始维度Linear.weight/bias 或 requires_grad=True 的输入)的 .grad 中,并按步累加Autograd 引擎按依赖关系调度反向节点:局部反向规则计算梯度,
AccumulateGrad 将收到的梯度累加到叶子张量的 .grad。
# 接上例:第一次 backward 后,x.grad 已经是 [2,2,2]
(2 * x).sum().backward() # 新的前向与反向
print(x.grad) # [4,4,4]:旧梯度 + 本次梯度
x.grad = None # 清除已保存的梯度
(2 * x).sum().backward()
print(x.grad) # [2,2,2]
x.grad_fn 仍为 None:叶子 x 不是由这个累加节点生成的。backward() 不更新参数;训练时用 optimizer.zero_grad() 清零,optimizer.step() 更新参数。retain_grad() 保存中间梯度;retain_graph=True 保留反向所需的图数据。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())。无偏置线性变换:
这里 nn.Linear 保存的权重布局为
sum() 将全部元素归约成一个标量。前向:Y [B,H,E] → 平方、求和 → L []
反向:dL/dL = 1 → G = 2Y [B,H,E]
若 Loss 改为平方的均值,则
一个输入元素
把逐元素求和组织成矩阵乘法:
dX = G @ W.T # [B,H,E] @ [E,D] -> [B,H,D]
令
dW = X.reshape(-1, D).T @ G.reshape(-1, E)
# 例:B=2,H=3,D=4,E=5 → [4,6] @ [6,5] → [4,5]
矩阵乘法对
https://marp.app/

- `model.zero_grad(set_to_none=True)` 降低显存碎片与加速。
暂不展示:原第 29–30 页(inference_mode 及对比表) --- - `with torch.inference_mode()`:面向**纯推理/部署**的模式 - 与反向的关系:不构图 - 跳过版本计数与视图跟踪,进一步减少元数据与一致性检查的开销;用于无需梯度的快速前向,并非只读模式 - 适用场景:离线/在线推理、模型服务、导出前的快速验证 --- _class: compact(原页局部样式,恢复时改回 Marp 指令) | 特性 | `no_grad` | `inference_mode` | 说明 | |---|---|---|---| | 构图 | 关闭 | 关闭 | 两者都不记录计算图 | | 版本计数/视图跟踪 | 保留 | 跳过 | `inference_mode` 更省内存/检查更少 | | 内存/速度 | 省 | 更省/更快 | 大模型推理建议 `inference_mode` | | 训练期使用 | 可用于局部(如指标、EMA) | 不建议 | `inference_mode` 仅用于纯推理 | | 张量使用限制 | 可在之后的求导运算中使用 | 新建张量不能随意用于后续求导 | 推理张量在模式外原地写受限 |