admin管理员组

文章数量:1627453

PyTorch 的 Autograd

原创 AlanBupt 发布于2019-06-15 22:16:21 阅读数 1175 收藏 更新于2019-06-15 22:16:21 分类专栏: Python PyTorch 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn/byron123456sfsfsfa/article/details/92210253 收起

PyTorch 作为一个深度学习平台,在深度学习任务中比 NumPy 这个科学计算库强在哪里?我觉得一是 PyTorch 提供了自动求导机制,二是对 GPU 的支持。由此可见,自动求导 (autograd) 是 PyTorch,乃至其他大部分深度学习框架中的重要组成部分。

了解自动求导背后的原理和规则,对我们写出一个更干净整洁甚至更高效的 PyTorch 代码是十分重要的。但是,现在已经有了很多封装好的 API,我们在写一个自己的网络的时候,可能几乎都不用去注意求导这些问题,因为这些 API 已经在私底下处理好了这些事情。现在我们往往只需要,搭建个想要的模型,处理好数据的载入,调用现成的 optimizer 和 loss function,直接开始训练就好了。仔细一想,连需要设置 requires_grad = True 的地方好像都没有。有人可能会问,那我们去了解自动求导还有什么用啊?

原因有很多,可以帮我们更深入地了解 PyTorch 这些宽泛的理由我就不说了,我举一个例子:当我们想使用一个 PyTorch 默认中并没有的 loss function 的时候,比如目标检测模型 YOLO 的 loss,我们可能就得自己去实现。如果我们不熟悉基本的 PyTorch 求导机制的话,对于实现过程中比如 tensor 的 in-place 操作等等很容易出错,导致需要话很长时间去 debug,有的时候即使定位到了错误的位置,也不知道如何去修改。相反,如果我们理清楚了背后的原理,我们就能很快地修改这些错误,甚至根本不会去犯这些错误。鉴于现在官方支持的 loss function 并不多,而且深度学习领域日新月异,很多新的效果很好的 loss function 层出不穷,如果要用的话可能需要我们自己来实现。基于这个原因,我们了解一下自动求导机制还是很有必要的。

本文所有代码例子都基于 Python3 和 PyTorch 1.1, 也就是不会涉及 0.4 以前的 Variable 这个数据结构。在文章中我们不会去分析一些非常底层的代码,而是通过一系列实例来理解自动求导机制。在举例的过程中我尽量保持场景的一致性,不用每个例子都需要重新了解假定的变量。如果发现文章中有错误或者没有讲清楚的地方,欢迎大家在评论区指正或讨论。

计算图

首先,我们先简单地介绍一下什么是计算图(Computational Graphs),以方便后边的讲解。假设我们有一个复杂的神经网络模型,我们把它想象成一个错综复杂的管道结构,不同的管道之间通过节点连接起来,我们有一个注水口,一个出水口。我们在入口注入数据的之后,数据就沿着设定好的管道路线缓缓流动到出水口,这时候我们就完成了一次正向传播。想象一下输入的 tensor 数据在管道中缓缓流动的场景,这就是为什么 TensorFlow 叫 TensorFlow 的原因!emmm,好像走错片场了,不过计算图在 PyTorch 中也是类似的。至于这两个非常有代表性的深度学习框架在计算图上有什么区别,我们待会再说。

计算图通常包含两种元素,一个是 tensor,另一个是 Function。张量 tensor 不必多说,但是大家可能对 Function 比较陌生。这里 Function 指的是在计算图中某个节点(node)所进行的运算,比如加减乘除卷积等等之类的,Function 内部有 forward()backward() 两个方法,分别应用于正向、反向传播。

a = torch.tensor(2.0, requires_grad=True)
b = a.exp()
print(b)
# tensor(7.3891, grad_fn=<ExpBackward>)

       
       
       
       
  • 1
  • 2
  • 3
  • 4

在我们做正向传播的时候,需要求导的变量除了执行 forward() 操作之外,还会同时会为反向传播做一些准备,为反向计算图添加一个 Function 节点。在上边这个例子中,变量 b 在反向传播中需要进行的操作是 ExpBackward

一个具体的例子

了解了基础知识之后,现在我们来看一个具体的计算例子,并画出它的正向和反向计算图。假如我们需要计算这么一个模型:

l1 = input x w1
l2 = l1 + w2
l3 = l1 x w3
l4 = l2 x l3
loss = mean(l4)

       
       
       
       
  • 1
  • 2
  • 3
  • 4
  • 5

这个例子比较简单,涉及的最复杂的操作是求平均,但是如果我们把其中的加法和乘法操作换成卷积,那么其实和神经网络类似。我们可以简单地画一下它的计算图:

下面给出了对应的代码,我们定义了inputw1w2w3 这三个变量,其中 input 不需要求导结果。根据默认规则,对于 l1 来说,因为输入中有一个需要求导(也就是 w1),所以它自己也需要求导,即 requires_grad=True(如果对这个规则不熟悉,欢迎参考 我上一篇博文的第一部分 或者直接查看 官方 Tutorial 相关部分)。在整张计算图中,只有 input 一个变量是不需要求导的。正向传播过程的具体代码如下:

input = torch.ones([2, 2], requires_grad=False)
w1 = torch.tensor(2.0, requires_grad=True)
w2 = torch.tensor(3.0, requires_grad=True)
w3 = torch.tensor(4.0, requires_grad=True)

本文标签: PytorchAutograd