介绍
因为张量不是“死的数据”,它会记录自己参与过的所有计算,形成一张“计算图”,反向传播时沿着这张图一步步算出梯度。
张量会“记住自己从哪来”(记录计算历史)
当你写:
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2
z = 2 * y + 3
张量 z 并不是只知道自己的值是 11,它还知道:
- 我是由 2 * y + 3 得到的
- y 是由 x ** 2 得到的
- x 是一个叶子节点(用户输入)
也就是说,每个张量都保存着自己的“诞生过程”。
这就是自动求导的基础。
每个张量都有一个 grad_fn:它的“求导函数”
当你打印 z.grad_fn,会看到类似:
<AddBackward0 object at 0x...>
这说明 z 是通过一个加法操作产生的。
同理,y.grad_fn 会显示:
<PowBackward0 object at 0x...>
说明 y 是通过幂运算产生的。
这些 grad_fn 就是反向传播时用来计算梯度的函数。
它们构成了一条链:
z → AddBackward → y → PowBackward → x
这就是“计算图”。
调用 backward() 时,会沿着计算图反向“链式求导”
当你执行:
z.backward()
它会从 z 开始,沿着 grad_fn 链,反向一步步计算梯度。
过程像这样:
- z 对 y 的导数是 2
- y 对 x 的导数是 2x
- 所以 z 对 x 的导数是 2 * 2x = 4x
- 代入 x=2,得到梯度 8
所以 x.grad 最终是:
tensor(8.)
这就是自动求导的全部秘密。
总结
张量能自动求导,是因为它具备三件事:
- 记录计算历史(知道自己怎么来的)
- 保存 grad_fn(知道怎么求导)
- 调用 backward() 时沿计算图反向链式求导
这三件事组合在一起,就实现了“自动求导”。
更形象的比喻(非常好理解)
你可以把张量想象成一个“有记忆的计算器”:
- 你每按一次按钮(做一次运算),它都会记录“我刚才做了什么操作”
- 当你说“算梯度”时,它会根据记录的操作,从结果倒推每一步的导数
- 最终把梯度存到叶子节点(如 x.grad)里