autograd 自动微分

假如我们有一个向量 x=(1,1) 当成 input,经过一系列运算得到了 output 变量 y, 如下图所示:

pytorch入门-Autograd - 图1

如图所示,向量 x 经过与 4 和自身相乘之后得到向量 z,z 再求长度,得到 y

我们想要求 y 关于 x 的微分时,pytorch 会帮我们自动求解。

from torch.autograd import Variable
import torch
x = Variable(torch.ones(2), requires_grad = True) #vairable 是 tensor 的一个外包装
z=4xx
y=z.norm()
y
Variable containing:
5.6569
[torch.FloatTensor of size 1]
我们可以看到 y 的值与我们上图计算的结果一致
需要注意:autograd 是专门为了 BP 算法设计的,所以这 autograd 只对输出值为标量的有用,因为损失函数的输出是一个标量。如果 y 是一个向量,那么 backward() 函数就会失效。不知道 BP 算法是什么的同学,估计也不知道什么是深度学习,建议先看 Zen 君提供的教材。

autograd 的内部机理

我们之所以可以实现 autograd 多亏了 Variable 和 Function 这两种数据类型的功劳。要进行 autograd 必需先将 tensor 数据包成 Variable。Varibale 和 tensor 基本一致,所区别在于多了下面几个属性。

pytorch入门-Autograd - 图2

variable 是 tensor 的外包装,variable 类型变量的 data 属性存储着 tensor 数据,grad 属性存储关于该变量的导数,creator 是代表该变量的创造者

variable 和 function 它们是彼此不分开的,先上图:

pytorch入门-Autograd - 图3

数据向前传输和向后传输生成导数的过程示意图

如图,假设我们有一个输入变量 input(数据类型为 Variable)input 是用户输入的,所以其创造者 creator 为 null 值,input 经过第一个数据操作 operation1(比如加减乘除运算)得到 output1 变量(数据类型仍为 Variable),这个过程中会自动生成一个 function1 的变量(数据类型为 Function 的一个实例),而 output1 的创造者就是这个 function1。随后,output1 再经过一个数据操作生成 output2,这个过程也会生成另外一个实例 function2,output2 的创造者 creator 为 function2。

在这个向前传播的过程中,function1 和 function2 记录了数据 input 的所有操作历史,当 output2 运行其 backward 函数时,会使得 function2 和 function1 自动反向计算 input 的导数值并存储在 grad 属性中。

creator 为 null 的变量才能被返回导数,比如 input,若把整个操作流看成是一张图(Graph), 那么像 input 这种 creator 为 null 的被称之为图的叶子(graph leaf)。而 creator 非 null 的变量比如 output1 和 output2,是不能被返回导数的,它们的 grad 均为 0。所以只有叶子节点才能被 autograd。

计算图的引入

在平常的使用中,我们一般通过对PyTorch提供的算子进行组合即可完成我们期望的功能,对于这些提供的算子,PyTorch已经提供了前向和反向的计算方法,因此如果我们网络是通过PyTorch已有的算子搭建而成的,就不需要自己再实现其反向的过程,因为这一过程将由PyTorch的自动微分引擎替我们完成。为了实现上述的目的,PyTorch引入了计算图的概念。计算图是一个有向图,它的节点为已经实现的算子或者数据(叶子结点),箭头的方向表示数据流动的方向,从输入节点指向输出节点。假设我们想实现函数L(x)=mean(sin(x1*x2+x2)),翻译成计算图为:
pytorch入门-Autograd - 图4
从下往上即为前向传播的过程,如果想要计算L相对于x1和x2的梯度则可以从上往下进行求导,上述过程用PyTorch实现为:

  1. import torch
  2. x1 = torch.rand((2,2), requires_grad=True)
  3. x2 = torch.rand((2,2), requires_grad=True)
  4. y=torch.mm(x1,x2)
  5. z=torch.sin(y+x2)
  6. L=z.mean()
  7. L.backward()
  8. print(x1.grad, x2.grad)

https://zhuanlan.zhihu.com/p/29325963
https://zhuanlan.zhihu.com/p/29325963