【PyTorch】PyTorch自动求导
PyTorch自动求导
PyTorch的自动求导在torch.autograd包中实现。torch.Tensor和torch.Function为autograd包中的两个核心类,他们互相连接并生成一个有向非循环图。
1.1 自动求导要点
autograd包在自动求导时,需要考虑以下事项:
- 创建叶子结点的
Tensor,使用requires_grad参数指定是否记录对其的操作,以便之后利用backward函数求解。requires_grad参数默认为False,当其为True时,与这个节点相依赖的其他节点也会变成True(这条路径都被污染了) - 通过
requires_grad_()方法修改Tensor汇总的requires_grad属性,可以调用.detach()或者with torch.no_grad()不再计算张量的梯度,便于进行评估、测试模型。 - 通过运算创建的非叶子结点,会被自动赋予
grad_fn属性,表示梯度函数,叶子结点的grad_fn为None - 对最后得到的
Tensor执行backward函数(反向传播中作为根节点),会自动计算各变量的梯度,并将累加结果放在grad属性中,当计算完成后,非叶子结点的grad属性会被自动释放 backward函数接收参数,该参数应该与调用该函数的Tensor维度相同,或者是可以广播的维度。如果求导的Tensor是一个标量,backward中的参数可以省略掉- 反向传播的中间缓存会被清空,如果需要进行多次传播,需要指定函数中的参数
retain_graph为True。在多次反向传播过程中,梯度是累加的。 - 非叶子结点的梯度在被
backward()函数调用后就会被清空。 - 可以通过
torch.no_grad()包裹代码块来阻止autograd去跟踪哪些标记为.requesgrad=True的张量的历史记录。
1.2 计算图
在整个过程中,PyTorch采用计算图的形式进行组织,该计算图为动态图,它的计算图在每次正向传播过程中都将重新构建。而其他的架构(TF是后面才引进的),一般都是静态图。
计算图是一种有向无环图(DAG),用来表示算子与变量之间的关系,直观且高效。
一般来说,圆形表示变量,矩形表示算子。例如表达式z=wx+b,变量为x, w, b,这些变量是用户所创建的,不依赖于其他变量,因此是叶子结点。该表达式的计算图如下:
1 | graph BT; |
1 | graph TB |
在这个过程中,叶子节点为x,w,b,非叶节点为z, y。
当嗲用backward()函数后,autograd会从根节点z进行反向溯源,并根据链式法则计算每个叶子结点的梯度,并累加到grad属性汇总。对于非叶子结点或算子的操作记录在grad_fn中,叶子结点的grad_fn为None。
1.3 反向传播
标量反向
1 | torch.autograd.backward(tensor,grad_tensors=None,retain_graph=None,create_graph=False,grad_variables=None) |
对标量的backward无需指定参数。
1 | import torch |
非标量反向
PyTorch不允许张量对张量求导,需要采用标量对张量进行求导,因此,如果目标张量对一个非标量调用backward,需要传入一个gradient参数,该参数也是张量,形状要跟调用backward的张量相同。
这个参数负责乘以需要求导参数的雅可比矩阵。
举个例子,我们有:
那么对张量求雅可比矩阵(一阶偏导),得到的结果为:
当x=[2,3]时,有:
由于不支持Tensor对Tensor的求导,所以我们借助额外的向量,将其转化为标量对向量的求导。例如向量,我们可以得到,这表示对求导的梯度。同样,采用向量表示对的梯度。
用个例子看一下:
1 | import torch |
1.4 生命周期
1.5 切断分支的反向传播
训练过程中,有时候我们想保持一部分的网络参数不变,而支队其中一部分的参数进行调整,只训练部分分支,那么这时候就可以通过detach()函数来切断一些分支的反向传播。
1 | detach_() |
这个怎么说呢,比如,,此时我们想把视为常数,也就是,可以用以下的方法:
1 | import torch |
如果我们不用detach,得到的结果就应该是:3*2+3=6了。
1 | import torch |



