torch.autograd是pytorch训练模型的必要条件,backward()必须作用于标量因链式法则要求单一输出;非标量需显式传gradient参数;requires_grad=true仅记录计算图,梯度只存于参与标量loss路径的叶子节点。

torch.autograd 不是“锦上添花”的功能,而是深度学习框架能跑起来的必要条件。没有它,PyTorch 就只剩下一个张量运算库,没法训练模型。
为什么 backward() 必须作用于标量?
反向传播的本质是链式法则,而链式法则要求从一个**单一输出值**出发,才能定义“对每个输入的偏导”。loss.backward() 能工作,是因为 loss 是标量(比如 torch.tensor(2.3));如果直接对形状为 (2, 3) 的张量调用 .backward(),PyTorch 会报错:RuntimeError: grad can be implicitly created only for scalar outputs。
- 解决办法不是“绕开”,而是显式提供
gradient参数:它必须和目标张量同 shape,相当于告诉 PyTorch:“请把这张量当做一个加权和,权重就是我给的这个gradient” - 常见场景:多任务 loss 加权、自定义梯度掩码、RL 中的 advantage 加权
- 错误写法:
y = x @ w; y.backward()(y是向量)→ 必崩 - 正确写法:
y = (x @ w).sum(); y.backward()或y.backward(torch.ones_like(y))
requires_grad=True 的实际影响范围
它只控制“是否记录计算图”,不等于“一定会存梯度”。很多初学者误以为设了 requires_grad=True 就万事大吉,结果发现 .grad 是 None。
- 只有参与了最终标量 loss 计算路径的张量,反向传播时才会被赋值
.grad - 中间变量默认不保留计算图(除非显式调用
.retain_grad()),所以即使y = x * 2且x.requires_grad=True,y.grad也为空 —— 它不是叶子节点,梯度只存到x.grad - 叶子节点(如模型参数)的
.grad会累积:多次loss.backward()后不zero_grad(),梯度就叠加了
动态计算图带来的灵活性与陷阱
PyTorch 的图是运行时构建的,这让你能写 if/else、for、甚至递归,但也会导致一些隐蔽问题:
- 每次前向都建新图,内存开销比静态图(如早期 TensorFlow)高;
torch.no_grad()不仅省显存,还跳过图构建 - 同一段代码在训练/推理模式下可能产生不同图结构(比如 dropout、batch norm 的行为差异),但图本身不会“记忆模式”,全靠 op 实现逻辑
- 原地操作(
x.add_(1))可能破坏图依赖,触发RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation
真正难的从来不是“怎么让 .backward() 跑通”,而是理解梯度流经哪条路径、哪些张量该存梯度、哪些不该存,以及为什么某个中间变量的 .grad 是空的——这些细节在复杂模型里一出错,debug 成本远高于写模型本身。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











