深度学习框架 Pytorch 深入学习(2):自动求导 autograd 反向传播 backward 与计算图
Pytorch搭建神经网络(2)自动求导autograd、反向传播backward与计算图
参考 GitHub 的 pytorch-handbook 项目
笔记和代码存储在我的 GitHub 库中 github.com/isKage/pytorch-notes。
torch.autograd
提供了一套自动求导方式,它能够根据前向传播过程自动构建计算图,执行反向传播。
1 autograd 的数学原理:计算图
计算图原理可以查看 cs231n 课程讲解:【计算图的原理非常重要!】或者见后文分析
英文官网 https://cs231n.github.io/
b站 课程整理 BV1nJ411z7fe 【反向传播章节】
b站 中文讲解 【子豪兄】精讲CS231N斯坦福计算机视觉公开课(2020最新)
2 autograd 的使用:requires_grad & backward
2.1 requires_grad 属性
只需要对Tensor增加一个 requires_grad=True
属性,Pytorch就会自动计算 requires_grad=True
属性的 Tensor,并保留计算图,从而快速实现反向传播。
1 | # Method 1 |
2.2 backward 反向传播
反向传播函数的使用:其中第一个参数 tensors
传入用于计算梯度的张量,格式和各个参数
1 | torch.autograd.backward(tensors, grad_tensors=None, retain_graph=None, create_graph=False) |
tensors
:用于计算梯度的Tensor,如torch.autograd.backward(y)
,等价于y.backward()
。grad_tensors
:形状与tensors一致,对于y.backward(grad_tensors)
,grad_tensors相当于链式法则${\mathrm{d}z \over \mathrm{d}x}={\mathrm{d}z \over \mathrm{d}y} \times {\mathrm{d}y \over \mathrm{d}x}$中的${\mathrm{d}z} \over {\mathrm{d}y}$。【结合例子理解见后】retain_graph
:计算计算图里每一个导数值时需要保留各个变量的值,retain_graph 为 True 时会保存。【结合例子理解见后】
2.2.1 requires_grad 属性的传递
- 例:
a
需要求导,b
不需要,c
定义为a + b
的元素加和
1 | a = torch.randn(2, 3, requires_grad=True) |
2.2.2 is_leaf 叶子结点
对于计算图中的Tensor而言, is_leaf=True
的Tensor称为Leaf Tensor,也就是计算图中的叶子节点。
requires_grad=False
时,无需求导,故为叶子结点。- 即使
requires_grad=True
但是由用户创建的时,此时它位于计算图的头部(叶子结点),它的梯度会被保留下来。
1 | # 仍然是上面的例子 |
2.3 autograd 利用计算图计算导数
利用 autograd 计算导数,对于函数 $y=x^2e^x$,它的导函数解析式为
定义计算 y 函数和计算解析式导数结果函数
1 | # autograd 求导 |
- 例:随机赋值
1 | x = torch.randn(2, 3, requires_grad=True) |
1 | y.backward(gradient=torch.ones(y.size())) # 指定 dy/dx = dy/dx * 1 的 dy/dx |
1 | print(x.grad) # 反向传播后才能取到 y 关于 x 的导数(已经代入了此时 x 的值) |
x.grad & df(x)
二者是在数值上是一样的
3 反向传播与计算图
3.1 计算图原理:链式法则
根据链式法则
$dz/dy = 1,\ dz/db = 1$
$dy/dw = x,\ dy/dx = w$
$dz/dx = dz/dy \times dy/dx = 1 \times w,\ dz/dw = dz/dy \times dy/dw = 1 \times x$
只要存储结点的导数和值便可通过简单的乘法计算所有导数
按照上图构造
1 | # 计算图 |
3.2 grad_fn 查看反向传播函数
grad_fn
可以查看这个结点的函数类型
1 | z.grad_fn # <AddBackward0 at 0x7f96b951ba90> Add 加法,因为 z = y + b |
grad_fn.next_functions
获取 grad_fn 的输入,返回上一步的反向传播函数
1 | z.grad_fn.next_functions # z 前是 y 和 b |
3.3 retain_graph 的使用(仅叶子结点)
如果不指定 retain_graph=True
,则在反向传播后,会自动清除变量值。
例如:计算 w.grad
w 的梯度时,需要 x 的值 ($dy/dw = x$)
注意:x.requires_grad=False 不需要求导,故
x.grad
报错
1 | z.backward(retain_graph=True) |
1 | # 再次运行,梯度累加 |
3.4 关闭反向传播
某一个节点 requires_grad
被设置为 True
,那么所有依赖它的节点 requires_grad
都是 True
。有时不需要对所有结点都反向传播(求导),从而来节省内存。
1 | x = torch.ones(1) |
下面我们来关闭关于 y
的反向传播
- 法一:
with torch.no_grad():
1 | with torch.no_grad(): |
- 法二:设置默认
torch.set_grad_enabled(False)
1 | torch.set_grad_enabled(False) # 更改默认设置 |
3.5 .data
从计算图取出Tensor的值
修改张量的数值,又不影响计算图,使用 tensor.data
方法
1 | x = torch.ones(1, requires_grad = True) |
3.6 存储非叶子结点的梯度
在计算图流程中,非叶子结点求导后其导数值便立刻被清除。可以使用 autograd.grad
或 hook
方法保留
1 | # autograd.grad & hook |
1 | z.backward() |
若为叶子结点可以采用
z.backward(retain_graph=True)
的方式
- 法一:
torch.autograd.grad()
1 | # 使用 torch.autograd.grad() 直接取梯度 |
- 法二:
hook
标准格式
1 | # hook是一个函数,输入是梯度,不应该有返回值 |
4 案例:线性回归
1 | import torch |