PyTorch自动微分基本原理
序言:在训练一个神经网络时,梯度的计算是一个关键的步骤,它为神经网络的优化提供了关键数据。但是在面临复杂神经网络的时候导数的计算就成为一个难题,要求人们解出复杂、高维的方程是不现实的。这就是自动微分出现的原因,当前最流行的深度学习框架如PyTorch、Tensorflow等都提供了自动微分的支持,让人们只需要很少的工作就能神奇般地自动计算出复杂函数的梯度。
PyTorch的autograd简介
Tensor是PyTorch实现多维数组计算和自动微分的关键数据结构。一方面,它类似于numpy的ndarray,用户可以对Tensor进行各种数学运算;另一方面,当设置.requires_grad = True之后,在其上进行的各种操作就会被记录下来,用于后续的梯度计算,其内部实现机制被成为动态计算图(dynamic computation graph)。
Variable变量:在PyTorch早期版本中,Tensor只负责多维数组的运算,自动微分的职责是Variable完成的,因此经常可以看到因而产生的包装代码。而在0.4.0版本之后,二者的功能进行了合并,使得自动微分的使用更加简单了。
autograd机制能够记录作用于Tensor上的所有操作,生成一个动态计算图。图的叶子节点是输入的数据,根节点是输出的结果。当在根节点调用.backward()的时候就会从根到叶应用链式法则计算梯度。默认情况下,只有.requires_grad和is_leaf两个属性都为True的节点才会被计算导数,并存储到grad中。
动态计算图本质上是一个有向无环图,因此“叶”和“根”的称呼是不太准确的,但是这种简称可以帮助理解,PyTorch的文档中仍然采用这种说法。
requires_grad属性
requires_grad属性默认为False,也就是Tensor变量默认是不需要求导的。如果一个节点的requires_grad是True,那么所有依赖它的节点requires_grad也会是True。换言之,如果一个节点依赖的所有节点都不需要求导,那么它的requires_grad也会是False。在反向传播的过程中,该节点所在的子图会被排除在外。
>>> x = torch.randn(5, 5) # requires_grad=False by default
>>> y = torch.randn(5, 5) # requires_grad=False by default
>>> z = torch.randn((5, 5), requires_grad=True)
>>> a = x + y
>>> a.requires_grad
False
>>> b = a + z
>>> b.requires_grad
True
Function类
我们已经知道PyTorch使用动态计算图(DAG)记录计算的全过程,那么DAG是怎样建立的呢?一些博客认为DAG的节点是Tensor(或说Variable),这其实是不准确的。DAG的节点是Function对象,边表示数据依赖,从输出指向输入。因此Function类在PyTorch自动微分中位居核心地位,但是用户通常不会直接去使用,导致人们对Function类了解并不多。
每当对Tensor施加一个运算的时候,就会产生一个Function对象,它产生运算的结果,记录运算的发生,并且记录运算的输入。Tensor使用.grad_fn属性记录这个计算图的入口。反向传播过程中,autograd引擎会按照逆序,通过Function的backward依次计算梯度。

backward函数
backward函数是反向传播的入口点,在需要被求导的节点上调用backward函数会计算梯度值到相应的节点上。backward需要一个重要的参数grad_tensor,但如果节点只含有一个标量值,这个参数就可以省略(例如最普遍的loss.backward()与loss.backward(torch.tensor(1))等价),否则就会报如下的错误:
Backward should be called only on a scalar (i.e. 1-element tensor) or with gradient w.r.t. the variable
要理解这个参数的内涵首先要从数学角度认识梯度运算。如果有一个向量函数$\vec{y}=f(\vec{x})$,那么$\vec{y}$相对于$\vec{x}$的梯度是一个雅克比矩阵(Jacobian matrix):
$$\begin{split}J=\left(\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}}\\ \vdots & \ddots & \vdots\\ \frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{array}\right)\end{split}$$
本文讨论的主角torch.autograd本质上是一个向量-雅克比乘积(*vector-Jacobian product*)的计算引擎,即计算$v^{T}\cdot J$,而所谓的参数grad_tensor就是这里的$v$。由定义易知,参数grad_tensor需要与Tensor本身有相同的size。通过恰当地设置grad_tensor,容易计算任意的$\frac{\partial y_{m}}{\partial x_{n}}$求导组合。
反向传播过程中一般用来传递上游传来的梯度,从而实现链式法则,简单的推导如下所示:
$$\begin{split}J^{T}\cdot v=\left(\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}}\\ \vdots & \ddots & \vdots\\ \frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{array}\right)\left(\begin{array}{c} \frac{\partial l}{\partial y_{1}}\\ \vdots\\ \frac{\partial l}{\partial y_{m}} \end{array}\right)=\left(\begin{array}{c} \frac{\partial l}{\partial x_{1}}\\ \vdots\\ \frac{\partial l}{\partial x_{n}} \end{array}\right)\end{split}$$
(注:这里的计算结果被转置为列向量以方便查看)
注意:梯度是累加的
backward函数本身没有返回值,它计算出来的梯度存放在叶子节点的grad属性中。PyTorch文档中提到,如果grad属性不为空,新计算出来的梯度值会直接加到旧值上面。
为什么不直接覆盖旧的结果呢?这是因为有些Tensor可能有多个输出,那么就需要调用多个backward。叠加的处理方式使得backward不需要考虑之前有没有被计算过导数,只需要加上去就行了,这使得设计变得更简单。因此我们用户在反向传播之前,常常需要用zero_grad函数对导数手动清零,确保计算出来的是正确的结果。
PyTorch自动微分基本原理的更多相关文章
- PyTorch 自动微分示例
PyTorch 自动微分示例 autograd 包是 PyTorch 中所有神经网络的核心.首先简要地介绍,然后训练第一个神经网络.autograd 软件包为 Tensors 上的所有算子提供自动微分 ...
- PyTorch 自动微分
PyTorch 自动微分 autograd 包是 PyTorch 中所有神经网络的核心.首先简要地介绍,然后将会去训练的第一个神经网络.该 autograd 软件包为 Tensors 上的所有操作提供 ...
- pytorch学习-AUTOGRAD: AUTOMATIC DIFFERENTIATION自动微分
参考:https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html#sphx-glr-beginner-blitz-autog ...
- <转>如何用C++实现自动微分
作者:李瞬生转摘链接:https://www.zhihu.com/question/48356514/answer/123290631来源:知乎著作权归作者所有. 实现 AD 有两种方式,函数重载与代 ...
- MindSpore:自动微分
MindSpore:自动微分 作为一款「全场景 AI 框架」,MindSpore 是人工智能解决方案的重要组成部分,与 TensorFlow.PyTorch.PaddlePaddle 等流行深度学习框 ...
- 附录D——自动微分(Autodiff)
本文介绍了五种微分方式,最后两种才是自动微分. 前两种方法求出了原函数对应的导函数,后三种方法只是求出了某一点的导数. 假设原函数是$f(x,y) = x^2y + y +2$,需要求其偏导数$\fr ...
- 自动微分(AD)学习笔记
1.自动微分(AD) 作者:李济深链接:https://www.zhihu.com/question/48356514/answer/125175491来源:知乎著作权归作者所有.商业转载请联系作者获 ...
- (转)自动微分(Automatic Differentiation)简介——tensorflow核心原理
现代深度学习系统中(比如MXNet, TensorFlow等)都用到了一种技术——自动微分.在此之前,机器学习社区中很少发挥这个利器,一般都是用Backpropagation进行梯度求解,然后进行SG ...
- 【tensorflow2.0】自动微分机制
神经网络通常依赖反向传播求梯度来更新网络参数,求梯度过程通常是一件非常复杂而容易出错的事情. 而深度学习框架可以帮助我们自动地完成这种求梯度运算. Tensorflow一般使用梯度磁带tf.Gradi ...
随机推荐
- Write-up-CH4INRULZ_v1.0.1
关于 下载地址:点我 哔哩哔哩:哔哩哔哩 信息收集 网卡:vboxnet0,192.168.56.1/24,Nmap扫存活主机发现IP为192.168.56.101 ➜ ~ nmap -sn 192. ...
- 算法设计与分析 - AC 题目 - 第 5 弹(重复第 2 弹)
PTA-算法设计与分析-AC原题 - 最大子列和问题 (20分) 给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+, ..., Nj },其中 ≤i ...
- redis(笔记)
./redis-cli -a root 带密码链接 root 为我的密码del key 删除key keys * 查询 所有keyexist key key是否存在 成功返回1 失败Wie0 set ...
- redis之Set(无序)类型常用方法总结
redis之Set(无序)类型常用方法总结 存--sadd key member [member ...] 取--SMEMBERS key sadd key member [member ...] 向 ...
- A. Hotelier
A. Hotelier 题意:一家有10间房间的旅馆(10个房间排成一排),在旅馆的左右两端都有一个办理入住的前台,L代表在左端办理入住,R代表在右端办理入住,顾客都会挑选距离最近的空房间入住,数 ...
- 【转载】将Centos的yum源更换为国内的阿里云源
自己的yum源不知道什么时候给改毁了--搜到了个超简单的方法将yum源更换为阿里的源 完全参考 http://mirrors.aliyun.com/help/centos?spm=5176.bbsr1 ...
- 十三、web应用中路径总结
1.路径的编写形式:1)<a href="/项目名/资源路径"></a> <!--超链接--> <form action=" ...
- Vue--爬坑
1.路由变化页面数据不刷新问题: 出现这种情况是因为依赖路由的params参数获取写在created生命周期里面,因为相同路由二次甚至多次加载的关系 没有达到监听,退出页面再进入另一个文章页面并不会运 ...
- PCIE_DMA:xapp1052学习笔记
Moselsim仿真: EP为Endpoint部分实现代码,即例程主代码.其他的是搭建的仿真环境,主要目的是仿照驱动的行为,将PCIE软核用起来,主要是做PC端的行为仿真,如DMA配置,DMA读写操作 ...
- java学习-循环结构-递归练习1-汉诺塔问题
相传在印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏.该游戏是在一块铜板装置上,有三根杆(编号A.B.C),在A杆自下而上.由大到小按顺序放置64个金盘(如下图).游戏的目标:把A杆上的金盘全部移 ...