Autograd 自动求导:PyTorch 训练模型的发动机

写 PyTorch 的人,最常见的一句代码是 loss.backward()。

看起来很短。

但它背后不是一个函数那么简单,而是一整套动态计算图系统。

前向时,它记录每一步计算。反向时,它沿着计算图倒着走,用链式法则把梯度传回每一个参数。

这就是 Autograd。

1. 先把自动求导说成人话

模型训练,本质上是在问一个问题:

参数应该往哪个方向改,才能让 loss 变小?

这个"方向",就是梯度。

以前手写梯度,很痛苦。模型一复杂,公式就爆炸。PyTorch 的做法很直接:你只管写前向计算,反向梯度它帮你算。

可以把 Autograd 想成一本账。

前向计算时,每一步都记账:谁参与了计算,产生了什么结果,反向时该怎么还梯度。

调用 backward() 时,它从 loss 开始倒着翻账本,一层层把梯度算回去。

2. Autograd 解决了什么问题

神经网络训练有三件事:

第一,前向计算,得到预测值。

第二,计算 loss,知道错了多少。

第三,反向传播,算出每个参数应该怎么改。

Autograd 负责第三件事。

它不负责设计模型。

它不负责更新参数。

它只负责一件事:根据前向计算过程,自动计算梯度。

3. requires_grad:自动求导的开关

Tensor 默认只是一块数据。

如果你希望 PyTorch 追踪它参与的运算,就要打开 requires_grad。

import torch

x = torch.tensor(2.0, requires_grad=True)

y = x * x + 3 * x

y.backward()

print(x.grad) # tensor(7.)

这里 x=2,y=x²+3x。

dy/dx = 2x+3。

所以当 x=2 时,梯度就是 7。

你没有手写求导公式。PyTorch 自动做了。

只要参与运算的输入里有一个 Tensor 需要梯度,输出通常也会被纳入计算图。

但如果你在 no_grad 或 inference_mode 里执行,PyTorch 就不会记录这段历史。

4. grad_fn:反向图的入口

打开 requires_grad 后,计算结果上通常会出现一个属性:grad_fn。

x = torch.tensor(2.0, requires_grad=True)

y = x * x + 3 * x

print(y.grad_fn)

<AddBackward0 ...>

这个 grad_fn 很关键。

它不是装饰品。

它指向创建这个 Tensor 的反向函数。

比如加法产生 AddBackward0,乘法产生 MulBackward0,矩阵乘法产生 MmBackward0。

前向计算像是在搭积木,grad_fn 就是每块积木背后的拆解说明。

反向传播时,Autograd Engine 就沿着这些 grad_fn 和 next_functions 往回走。

5. 叶子节点和非叶子节点

很多人第一次学 Autograd,会被 .grad 搞晕。

为什么有的 Tensor 有 grad,有的 Tensor 没有?

答案在叶子节点。

用户直接创建、需要优化的 Tensor,通常是叶子节点。模型参数就是典型叶子节点。

由运算产生的中间结果,是非叶子节点。它们参与反向传播,但默认不会把梯度留在 .grad 字段里。

一句话记住:

Autograd 会计算中间梯度,但默认只把叶子 Tensor 的梯度保存下来。

如果你确实想查看中间节点的梯度,可以调用 retain_grad()。

6. backward() 到底做了什么

loss.backward() 不是"重新跑一遍模型"。

它是从 loss 这个根节点开始,沿计算图反向遍历。

每经过一个节点,就调用这个节点对应的 backward 逻辑。

最后,把梯度累加到叶子 Tensor 的 .grad 里。

典型训练片段

optimizer.zero_grad() # 清空上一轮梯度

pred = model(x) # 前向:构建计算图

loss = loss_fn(pred, y) # loss:图的根节点

loss.backward() # 反向:计算梯度

optimizer.step() # 更新:优化器修改参数

注意:backward 只负责算梯度。

参数真正被修改,是 optimizer.step() 做的。

旧梯度清空,是 optimizer.zero_grad() 做的。

7. 从 Python 进入 C++ Engine

从源码角度看,Autograd 分两层。

上层是 Python API,负责让你调用起来方便。

底层是 C++ Autograd Engine,负责真正执行计算图。

一条典型链路是:

Tensor.backward() 调用 torch.autograd.backward()。

torch.autograd.backward() 整理输出 Tensor、外部梯度、retain_graph 等参数。

然后进入 _execution_engine.run_backward()。

这里开始从 Python 切到 C++。

C++ Engine 会创建 GraphTask,分析依赖关系,把可以执行的 Node 放进 ReadyQueue,再由 worker thread 调用 evaluate_function 执行每个反向节点。

最后,AccumulateGrad 节点把梯度写到叶子 Tensor 的 .grad 里。

所以,Autograd 不是简单递归。

它是一个带任务调度、依赖分析、线程队列和梯度累加的执行引擎。

8. Saved Tensors:为什么反向要占显存

反向传播不可能凭空算梯度。

有些算子的 backward 需要用到前向阶段的中间结果。

比如 y=x²,反向公式是 dy/dx=2x。

如果反向时不知道前向的 x 是多少,就算不出梯度。

所以某些操作会在前向阶段保存必要的 Tensor。

这也是训练显存比推理显存高的重要原因。

推理只要前向结果。

训练还要保存反向所需的中间值。

后面讲混合精度、梯度检查点、FSDP 时,这个概念会反复出现。

9. detach、no_grad、retain_graph:三个容易混的点

学 Autograd,最容易踩坑的不是 backward,而是"什么时候不该记录图"。

detach() 是对某个 Tensor 切断历史。

no_grad() 是对一段代码关闭建图。

retain_graph=True 是 backward 后不释放图。

zero_grad() 是清空上一轮已经累积的梯度。

这几个东西看起来都和梯度有关,但作用完全不同。

训练代码里最常见的 bug,就是在错误的位置用了 no_grad 或 detach,导致模型不更新。

10. 为什么梯度会累加

PyTorch 的梯度默认是累加的。

也就是说,连续调用两次 backward,如果中间不清空梯度,.grad 会把两次结果加起来。

x = torch.tensor(2.0, requires_grad=True)

y = x * x

y.backward()

print(x.grad) # tensor(4.)

y = x * x

y.backward()

print(x.grad) # tensor(8.),不是 4,因为累加了

这不是 bug。

这是设计。

因为梯度累加可以支持梯度累计训练。

但普通训练中,每一轮更新前通常要清空旧梯度。

optimizer.zero_grad()

loss.backward()

optimizer.step()

11. Autograd 和动态图的关系

PyTorch 的计算图是动态生成的。

你每执行一次 forward,都会生成一张新的反向图。

这就是为什么你可以在 forward 里写 if、for、递归,甚至根据输入内容改变模型路径。

优点是灵活。

缺点是每次都要重新建图,性能上会有一定开销。

后面讲 torch.compile 时,你会看到 PyTorch 2.x 是怎么把动态图捕获成图,再交给编译器优化的。

12. 从源码角度看 Autograd 的核心对象

源码里,Autograd 的核心不是"Tensor 本身",而是 Tensor 背后的计算历史。

可以抓住这几个对象:

Tensor:保存数据,也带着 autograd metadata。

Node:反向图里的执行节点,每个 Node 知道自己的 backward 逻辑。

Edge:连接 Node 和 Node,表示梯度往哪里流。

GraphTask:一次 backward 的任务上下文。

ReadyQueue:已经满足依赖、可以执行的反向节点队列。

AccumulateGrad:负责把梯度累加到叶子 Tensor 的 .grad。

理解这些,loss.backward() 就不再神秘。

13. 总结

Autograd 是 PyTorch 训练的发动机。

requires_grad 决定是否记录计算。

grad_fn 指向反向图入口。

backward 从 loss 出发反向遍历。

叶子 Tensor 的 .grad 会被填充。

中间节点的梯度默认不保留。

某些算子会保存前向中间值,用于反向计算。

Python API 负责入口,C++ Autograd Engine 负责真正执行。

一句话:

前向建图,反向执行,梯度累加,优化器更新。


内容来源:Autograd 自动求导:PyTorch 训练模型的发动机:功能变化与行业影响解析_热闻岛

相关推荐
云和数据.ChenGuang1 小时前
大模型厂商常用的数据库有哪些?
数据库·人工智能·pytorch·深度学习·numpy
旅僧1 小时前
Bert理论讲解
人工智能·深度学习·bert
邵宇然1 小时前
编译期阻断 Bug:Rust 类型系统如何将运行时错误消灭在编译阶段
人工智能
ACP广源盛139246256731 小时前
GSV6155@ACP#DP 1.4a 重定时器芯片,物理 AI 信号长距传输的稳定保障
大数据·人工智能·分布式·嵌入式硬件·spark
明志数科1 小时前
数据标注自动化 vs 人工——4D时序标注场景谁靠谱?
人工智能
深圳市晶科鑫实业有限公司1 小时前
AI服务器为何对低抖动差分晶振如此挑剔?
服务器·人工智能·单片机·物联网·车载系统·云计算·信息与通信
FL16238631291 小时前
基于CNN深度学习算实现手写字母识别系统python源码+训练好的模型+说明文档
python·深度学习·cnn
geneculture1 小时前
中文信息处理的词边界重构:基于融智学的汉英结构计算模型
人工智能·语言学·融智学应用场景·中文信息处理·融智时代(杂志)·言和语·言本位
极创信息1 小时前
信创产品适配测试认证,域名和SSL是必须的吗?
java·开发语言·网络·python·网络协议·ruby·ssl