分享

PyTorch(三)——自动求导

 taotao_2016 2019-09-11

Autograd模块

PyTorch的Autograd模块是应用所有神经网络的核心内容,在张量(Tensor)上的所有操作,Autograd都能为他们自动提供微分,也就是自动求导的方法,从而简化了手动计算导数的复杂过程。

在0.4之前的版本中,PyTorch通过使用Variable类来自动计算所有的梯度,该类主要包含三个属性:

  1. data:保存Variable所包含的Tensor。
  2. grad:保存data对应的梯度,类型为Variable,形状与data一致。
  3. grad_fn:指向一个Function对象,我们可以通过它使用反向传播计算输入的梯度。

而从0.4版本开始,为了简化使用,PyTorch就将Variable类合并到Tensor中去了,因此我们现在可以直接通过Tensor来使用Autograd模块提供的功能。要使用Autograd,我们只需在创建Tensor时设置属性requries_grad为True即可。一旦我们将requries_grad属性设置为True,就表明我们需要对该Tensor进行自动求导,PyTorch会记录该Tensor的每一步操作历史并自动计算。

PyTorch(三)——自动求导

requries_grad属性

当我们将requries_grad属性设置为True时,PyTorch会自动追踪和记录对张量Tensor的所有操作,当计算完成后调用backward( )方法自动计算梯度并且将计算结果保存到grad属性中。Tensor进行相关操作后,grad_fn已经被赋予了一个新的函数,这个函数引用了Tensor类的Function对象。Tensor和Function互相连接生成了一个非循环图,它记录并且编码了完整的计算历史。每个张量都有一个grad_fn属性,如果这个张量是用户手动创建的,那么这个张量的grad_fn是None。

自动求导

自动求导使用backward( )函数进行反向传播来计算指定Tensor的梯度,在调用backward( )函数前,Tensor的grad属性为None。

  • 简单的自动求导

如果Tensor表示的是一个标量(只有一个元素的Tensor),则不需要为backward( )指定任何参数,但是如果它有更多的元素,则需要指定一个gradient参数,它是形状匹配的张量。

上述代码中的张量z只有一个元素,因此使用backward( )函数不需要传任何参数。

PyTorch(三)——自动求导

简单自动求导

以上的z.backward( )相当于是z.backward(torch.tensor(1.))的简写。这种参数常出现在图像分类中的单标签分类,输出一个标量代表图像的标签。

复杂的自动求导

当返回值不是一个标量时,我们在调用backward( )函数时需要传入一个与待自动求导的Tensor大小相同的Tensor作为backward( )函数的参数。

PyTorch(三)——自动求导

复杂自动求导

我们可以使用with torch.no_grad()上下文管理器临时禁止对已设置requires_grad=True的张量进行自动求导。这个方法在测试集计算准确率的时候会经常用到,例如:

PyTorch(三)——自动求导

禁止自动求导

在使用with torch.no_grad()进行嵌套后,PyTorch就不会再跟踪历史记录了,从而释放内存的使用量,同时会加快少许的运算速度。

自动求导原理

要明白自动求导的原理,首先需要知道计算图的概念。

  • 计算图

计算图是现代深度学习框架的核心,为高效自动求导算法——反向传播提供了理论支持。计算图具有如下两个优势:

  1. 使用一个简单函数就可以组合成一个极其复杂的模型。
  2. 可以实现自动微分(自动求导)。

例如对于某个表达式y=wx+b,其中,w、x、b是变量,*、+、=是算子。PyTorch通过记录算子与变量之间的关系可以生成如下图所示的计算图。

PyTorch(三)——自动求导

前向计算图

其中,我们称w、x、b是叶子节点,这些通常是手动创建的、而非运算得到的变量,因此也叫做创建变量。y为根节点,是计算图的最终目标,也就是通过计算后得到的结果,因此也称为结果变量。

判断一个变量是创建变量还是结果变量可以通过Tensor的is_leaf属性获取。例如对于上述代码中的三个张量Tensor:x、y、z,通过访问各自的is_leaf属性可以获取它们的变量属性。

PyTorch(三)——自动求导

is_leaf属性

x、y为创建变量,z为结果变量。

  • backward( )实质

通过上述代码,我们知道执行z.backward( )方法会更新x.grad和y.grad的值,这一点我们可以从grad_fn属性中进行探索,grad_fn记录并编码了整个计算历史。对于z=x**2+y**3来说,计算结果z的grad_fn为AddBackward0类型的变量,AddBackward0里面有一个next_functions,这个next_functions就是整个grad_fn的精华。

PyTorch(三)——自动求导

next_functions

打印next_functions可以看到,它是包含两个元素的元组tuple。其中,第一个元素表示x相关的操作记录,第二个元素表示y相关的操作记录。AddBackward0表示的是相加,而这个tuple中的PowBackward0则分别表示x**2与y**3的操作记录。以x为例,我们继续使用next_functions属性最终得到一个AccumulateGrad。在PyTorch的反向图计算中,AccumulateGrad类型代表的就是叶子节点类型。AccumulateGrad类中有一个variable属性指向叶子节点,这个variable属性就是我们最初的创建变量x。

PyTorch(三)——自动求导

backward( )实质

总结整个backward( )函数的执行过程(以上述代码中的z.backward( )为例)如下:

  1. 调用z中的grad_fn属性。
  2. 遍历grad_fn的next_functions属性,分别取出其中的Function(AccumulateGrad),执行求导操作。这个操作是一个递归的过程直到最后类型为叶子节点为止。
  3. 将计算结果保存到对应的variable所引用的对象(x和y)的grad属性里。
  4. 更新对应变量的grad属性。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多