在训练神经网络时我们有很多的需求,比如我们在训练时需要冻结某一部分网络,再比如我们需要通过一个网络两次等等,这都涉及对计算图的操作,首先通过简单的demo来看一下pytorch是怎么计算梯度的,然后我们再通过一些实例对网络进行操作
一个简单的梯度示例
创建三个二维变量x,y,z,令
我们画出上述计算的简单图示
假设,
,
,计算梯度
,
,
,
,所以
在torch中计算时,我们需要知道一些tensor的属性:
- if_leaf:是否是叶子结点,例如x、y、z就是叶子节点
- requires_grad:是否需要计算梯度
- data:节点值
- grad:反向传播后的节点梯度
- grad_fn:节点的计算记录,从此可以得知该变量是否是一个计算结果,即是否是一个函数的输出,例如a的为Add,叶子节点xyz的为None
下面我们通过代码进行验证
注意其中有一句a.retain_grad(),这句代码是说最后也要得到a的梯度,因为torch在backward()之后只有叶子节点有梯度值,中间变量是没有的,如果想直接计算出来需要加上上述语句
1 | import torch |
最终输出结果与我们计算的是相同的,上述代码中b.backward(torch.ones_like(x))括号中的参数维度和b的维度相同,如果没有该参数会报下述错误
RuntimeError: grad can be implicitly created only for scalar outputs
这是因为默认的backward()希望是一个标量,但是我们的b是一个二维向量,所以我们将其中传入和b维度相同的1即可(应该是默认输出的每一维度对自己的梯度为1?因为如果传入的是torch.ones_like(x)*2的话最后的梯度会变为原来的2倍)如果最后输出的是scalar,backward()不需要传入参数,默认传入的应该是torch.ones_like(torch.tensor(1))
经过验证,如果输出的b是向量,b.backward(gradient=torch.ones_like(x))其实等价于下面两句,实际上还是将b变成了标量在进行的backward
1 | b = torch.sum(b*torch.ones_like(x)) |