Skip to content

​


概念 ​

在神经网络中,我们通常使用反向传播来求梯度,这需要求解微分。

使用计算机求解微分的方法有很多,如符号微分,首先将目标函数转换为closed-form表达式,再利用求导法则,对表达式自动计算,求解梯度。该方案计算结果精确,但是要求表达式为closed-form(限制了使用场景),且表达式较为复杂时,可能会产生表达式膨胀(Expression swell)的问题。

另一种典型的求微分方法如数值微分,通过差分逼近。该方法适用于各种情况,但是需要权衡截断误差(truncation error)和舍入误差(Rounding error),对高维度函数计算速度较慢。

自动微分不要求函数的表达式为closed-form,算法和符号微分相同精度,直接获得梯度结果而不得到导数表达式。

自动微分利用表达式追踪机制,记录每一次计算过程中的中间变量,再利用链式求导法则组合各部分中间变量,生成梯度结果。

自动微分有两个主要版本,Forward mode and Reverse mode

以下列的函数为例

f(x1,x2)=ln(x1)+x1x2−sin(x2)

接下来我们要求函数对x1的偏导数,因此先定义中间变量的偏导数

v˙i=∂vi∂x1

Forward Primal Trace如下

v−1=x1=2v0=x2=5v1=lnv−1=ln2v2=v−1×v0=2×5v3=sinv0=sin5v4=v4+v2=0.693+10v5=v4−v3=10.652+0.959y=v5=11.652

对于上述函数,自动微分方法分成 Forward mode 和 Reverse mode

Forward mode ​

Forward Tangent (Derivative) Trace如下

v˙−1=x˙1=1v˙0=x˙2=0v˙1=v˙−1/v−1=1/2v˙2=v˙−1×v0+v˙0×v−1=1×5+0×2v˙3=v˙0×cosv0=0×cos5v˙4=v˙1+v˙2=0.5+5v˙5=v˙4−v˙3=5.5−0y˙=v˙5=5.5

Reverse mode ​

定义与算法 ​

这里我们首先定义

v¯i=∂yj∂vi

然后我们需要考虑Forward Primal Trace的树形图,利用如下性质,建立中间变量之间的依赖关系(有向图)

v¯i=∑j=child of imv¯j∂vj∂vi

具体到本例中,有向图如下

函数有向图

我们得到如下计算式子,需要指出的是,以下是从下往上计算,也就是先有v¯=y¯=1,最后再得到x¯1,x¯2.

x¯1=v¯−1=5.5x¯2=v¯0=1.716v¯−1=v¯−1+v¯1∂v1∂v−1=v¯−1+v¯1/v−1=5.5v¯0=v¯0+v¯2∂v2∂v0=v¯0+v¯2×v−1=1.716v¯−1=v¯2∂v2∂v−1=v¯2×v0=5v¯0=v¯3∂v3∂v0=v¯3×cosv0=−0.284v¯2=v¯4∂v4∂v2=v¯4×1=1v¯1=v¯4∂v4∂v1=v¯4×1=1v¯3=v¯5∂v5∂v3=v¯5×(−1)=−1v¯4=v¯5v¯v5∂v4=v¯5×1=1v¯5=y¯=1

详细流程 ​

详细来说反向计算导数值时,顺序如下:

  1. 计算y对v5的导数值,因为y=v5,所以 ∂y∂v5=v¯5=1 .

  2. 计算y对v4的导数值,因为v4在图上只有一个后续节点v5,并且v5=v4−v3,所以依据链式法则得到下式,并将结果写在v4指向v5的边上

∂y∂v4=∂y∂v5×∂v5∂v4=v¯5∂v5∂v4=v¯5∂(v4−v3)∂v4=v¯5×1=1
  1. 计算y对v3的导数值,因为v3在图上只有一个后续节点v5,并且v5=v4−v3。所以依据链式法则得到下式,并将结果写在v3指向v5的边上
∂y∂v3=∂y∂v5×∂v5∂v3=v¯5∂v5∂v3=v¯5∂(v4−v3)∂v3=v¯5×(−1)=−1
  1. 计算y对v2的导数值,因为v2在图上只有一个后续节点v4,并且v4=v1+v2,所以依据链式法则得到下式,并将结果写在v2指向v4的边上
∂y∂v2=∂y∂v4×∂v4∂v2=v¯4∂v4∂v2=v¯4∂(v1+v2)∂v2=v¯4×1=1
  1. 计算y对v1的导数值,因为v1在图上只有一个后续节点v4,并且v4=v1+v2,所以依据链式法则得到下式,并将结果写在v1指向v4的边上
∂y∂v1=∂y∂v4×∂v4∂v1=v¯4∂v4∂v1=v¯4∂(v1+v2)∂v1=v¯4×1=1
  1. 接下来要计算y对v0和v−1的导数值,因为v0和v−1都是有两个后续节点,所以我们只能分开计算。

  2. 计算下式,并将结果写在v0指向v3的边上

∂v3∂v0=∂sinv0∂v0=cosv0=0.284
  1. 计算下式,并将结果写在v0指向v2的边上
∂v2∂v0=∂(v−1v0)∂v0=v−1=2
  1. 计算下式,并将结果写在v−1指向v2的边上
∂v2∂v−1=∂(v−1v0)∂v−1=v0=5,
  1. 计算下式,并将结果写在v−1指向v1的边上
∂v1∂v−1=∂(lnv−1)∂v−1=1v1=0.5

到日前为止,我们已经计算出来了所有步骤的偏导数的数值。现在需要计算∂y∂x1和∂y∂x2。

计算∂y∂x1就是从y开始从后向前走,走回到x1,因为有多条路径,所以对于每一条路径,需要将这个路径上的数值连乘起来得到一个乘积数值,然后将这多条路径的乘积数值相加起来,就得到了∂y∂x1的数值。∂y∂x2的计算方法与此相同

从y到x1的路径有两条,分别是:

· v5→v4→v1→v−1:其数值乘积是1∗1∗0.5=0.5

· v5→v4→v2→v−1:其数值乘积是1∗1∗5=5

因此,∂y∂x1=0.5+5=5.5

从y到x2的路径有两条,分别是:

· v5→v4→v2→v0:其数值乘积是1∗1∗2=2.0

· v5→v3→v0:其数值乘积是−1∗0.284=−0.284

因此,∂y∂x2=2.0−0.284=1.716

Auto Diff 与 Jacobian ​

如果f的输出也是多维的,比如

f: Rn→ Rm

其Jacobian矩阵如下

Jf=[∂y∂x1⋯∂y∂xn]=[∂y1∂x1⋯∂y1∂xn⋮⋱⋮∂ym∂x1⋯∂ym∂xn]

具体而言,我们有:

δy=Jfδx

则每次Forward mode得到的将是Jacobian矩阵Jf的一列。

每次Reverse mode得到的将是Jacobian矩阵Jf的一行。

在实际的神经网络中,损失函数的输出为标量,但是参数矩阵很多。即如下形式

f: Rn→ R

因此神经网络中通常选择Reverse mode.

相应的,如果输入维度小于输出维度,即Jacobian矩阵的行数大于列数,比如n>m=1,则选择Forward mode。