6.计算图&反向传播(Computational Graph & Backpropagation)

6.1 Introduction 简介

  • 反向传播算法:一个有效率的计算梯度的方法
  • 前提
    • 前馈网络的反向传播
      • http://speech.ee.ntu.edu.tw/~tlkagk/courses/MLDS_2015_2/Lecture/DNN%20backprop.ecm.mp4/index.html
      • 简单的版本:https://www.youtube.com/watch?v=ibJpTrp5mcE
    • RNN的基于时间的反向传播算法:http://speech.ee.ntu.edu.tw/~tlkagk/courses/MLDS_2015_2/Lecture/RNN%20training%20(v6).ecm.mp4/index.html
  • 通过计算图来理解反向传播
    • Tensorflow, Theano, CNTK, etc.

6.2 计算图 定义

  所谓计算图,就是一种语言,这个语言是用来描述一个函数,我们知道neural network就是一个函数,所以我们需要描述函数的语言。其实graph有很多种定义方法,但是我们通常使用node来表示一个变量,他可以是一个scalar,vector甚至是tensor,这里的每一个edge代表了一个operation。


  看图上的例子
y = f(g(h(x)))
这个函数拆解成简单的形式
u = h(x)
,
v = g(u)
,
y = f(v)
,这里的
x
是一个变量,把
x
代入到
h
这个函数里面去,就得到了
u
这个output,箭头就代表了
h
这个function。接下来把
u
丢到
g
这个function里面得到
v
,把
v
丢到
f
这个function中就得到output
y
。有时候一个function他可以有不止一个input,如
a = f(b,c)
,也就是画两个箭头都指向一个输出
a
节点就行了。
  那计算图要怎么去画呢?

这个图的好处就是,我们在这个图上算变量的梯度是很容易的。
在去根据这个图算变量的梯度之前,我们需要先了解一下链式法则:

那我们还拿刚才那个为例子,加入链式法则:



把每一个箭头的偏微分都算出来。



如果我们想同时\frac{{\partial e}}{{\partial a}}\frac{{\partial e}}{{\partial b}}计算呢(在a=3.b=2的情况下)?


  如果同时算这两者的话,我们需要一个reverse mode(反向做法),我们刚才看的都是正向的做法,从
a
走到
e
,从
b
走到
e
,但如果要同时就算
\frac {\partial e}{\partial a}
\frac {\partial e}{\partial b}
我们就需要使用反向的做法,这样做的效率高,当然你是可以分两次算的,但是比较的慢,如果你反向算的话,你可以同时算出
\frac {\partial e}{\partial a}
\frac {\partial e}{\partial b}
。怎么做呢?从
e
开始走,把数字
1
放在
e
的这个节点上,然后从这个节点上散播出去。

  这样做的好处就是,如果今天是computational graph,你最终的函数的输出只有一个值,你又需要算大量的不同值的偏微分的时候,用reverse mode会比刚才的forward mode还要更有效率,而在做neural network的时候,我们需要的就是reverse mode。
  那为什么呢?因为我们要做neural network的时候,我们需要计算偏微分的对象其实是cost function(loss function),我们要对cost function算偏微分,而cost function的输出就是一个scalar,所以如果你把cost function用computational graph来描述的话,他只有一个输出,那今天我们又要同时算这个network里面所有参数对cost function的偏微分,所以我们从root开始做reverse mode,对cost function算偏微分是比较有效率的。

  那另外一个状况是有时候会有parameter sharing:

  我们可能会有parameter sharing的状况,也就是同样的变量,他出现在这个graph多个地方。这个时候要怎么计算他的偏微分呢?
  这件事情在neural network里面是常常的发生。比如,在CNN里面就有share variable,其实RNN他其实也是share variable,他把同一个function在整个RNN上反复的使用,他也是share variable。

  有share variable要怎么去做呢?
  举一个例子,假如有一个函数y = xe^{x^{2}} ,这个式子如果我们用computational graph来描述的话,就是上面图上的样子。接下来计算 \frac {\partial y}{\partial x} ,我们要把graph上的edge上的微分都算出来,我们需要把node上的x看成是不同的x来看待,就假设这些x都是有下标的。
  那算出了edge上的所有偏微分,那\frac {\partial y}{\partial x} 的值是什么呢?我们把那三个x,当成是不同的变量来看,那实际上 \frac {\partial y}{\partial x} 是什么呢?我们这里算出了三个偏微分,它的值并不是一样的,那么实际上 \frac {\partial y}{\partial x} ,意思就是把那算个对x的偏微分都加起来。所以今天要考虑share variable的case,我们就要把同样的参数,先当做不一样的参数来看,然后算完以后再加起来

6.3 前馈网络的计算图

  那接下来就说一说neural network的东西,上面讲的都是general的东西,那如果有一个fully connected feedforward network,那用computational graph来算他的微分的话,我们需要怎么去做呢?我们先复习一下,一般的讲法:
  一般我们说要算微分就用backpropagation,这个backpropagation里面分成两个阶段,一个是forward pass,一个是back pass。


求算:\frac{{\partial C}}{{\partial w_{ij}^l}} = \frac{{\partial z_i^l}}{{\partial w_{ij}^l}}\frac{{\partial {\rm{C}}}}{{\partial z_i^l}}

  那怎么去求error signal(误差信号)呢?


补充一个我的知识盲点:在微积分里面,对多元函数的参数求∂偏导数,把求得的各个参数的偏导数以向量的形式写出来,就是梯度

  1. 首先将原来的整个network进行逆转,那这个network的input就是Cy的偏导,这个network的input是一个vector,这个vector里面存放的是Cy的梯度;
  2. 把这个vector丢进一排的neural里面,这个一排的neural他们的激活函数并不是sigmoid,而是一排OPA(放大器),放大的倍数就是原来没有逆转时候的network的那个neural的激活函数的微分值,得到一组output,这个时候的output就是最后一层的error signal,也就是\delta^{l}
  3. 然后在把这组output乘上一组weight,这一组weight是原来layer L的那组matrix weight的transpose(在forward的时候乘上的是matrix weight [Z = WX+b],在backward乘上的就是weight的transpose)。然后丢到下一组的OPA里面,然后就可以算出error signal的值。
  4. 红色框框值和绿色框框值相乘就可以得到结果。

  那接下来用computational graph来微分,当然在计算微分之前,先来看看network的computational graph,那他长什么样子呢?


  那接下来我们要算gradient descent,也就是微分,那我们算微分的对象并不是y,其实是cost function。这儿有另外一个变量
\widehat y
, 代表标准答案,我们把
\widehat y
代入
L(y,\widehat y)
,得到
C
,此处的
L
就是loss function,看你们怎么evaluate
y
\widehat y


那么,接下来,我们要计算
W^1,W^2,b^1,b^2
对最后的
C
的偏微分,注意下,
C
是一个Scaler。对所有的network来说,最后cost function的output value都是一个scalar。
为了计算gradient,首先,我们把每个edge上的微分都算出来,然后用reverse mode逆向走回来,你就可以把所有的参数对c的偏微分都算出来。

接下来我们的问题就是我的az都是vector,那他是怎么去算微分的呢?

我们把vector对vector做微分其实就是jacobian matrix(雅可比行列式)。
我们现在就从c这个地方一路往回走:


我们算
C
y
的偏微分,
C
只有一维,而
y
是一个vector,求出的偏微分就是一个扁的长条状的东西,用 jacobian matrix 来表示的话,他的宽的长度和
y
的长度一致,他的长度和
C
的长度一样,也就是形成一个
1*y
维的vector。
如果
i=r
,说明第i(即r)维是1,即偏微分是
-1/y_r
;如果
i\ne r
的话,
y
和cost function没有关系。
因此可以求出:

再继续往回走:


因为z^{2}是一个vector,y也是一个vector,那么z^{2}y的偏微分所生成的jacobian matrix是一个方阵(因为他们两个vector的维度一定是相等的)。 如果i\ne j,他们之间没有关系就是0
如果今天用的不是sigmoid function使用的是softmax,那他还是diagonal matrix(对角阵)吗?
答案:不是了,因为在做softmax的时候,z的每一个dimension都会影响到y的每一个dimension,那就没有i\ne j 微分 =0的状况。

然后继续算下去,为了简单,下面把biases去掉:
我们把他们(_^2_^1)的关系写出来,如下图左下所示,因此可以求出偏微分为{W_{ij}}^2

然后我们算z^{2}W^{2}的jacobian matrix:


先假设
W^{2}
是一个
(m * n)
维的matrix,那显然
z^{2}
就是
m
维,
a^{1}
就是
n
维。我们可以把
W^{2}
z^{2}
的偏微分看做是一个tensor(三维),我们没有这样的思维去运算,所以把
W^{2}
当做 vector来看,应当做是二维的。

接下来:



然后就可以得到结果:



结果就是最左边的长度是 结果的长度1,最后一个matrix宽是结果的element的数目

好像用computational graph用reverse mode算所有参数的偏微分,感觉好像只有back pass没有forward pass。右边传统的讲法有forward pass和back pass。那右边那个forward pass在哪里?你还是需要forward pass,你想想看,我们要得到这些matrix的真正的值,我们需要a是多少,需要知道节点的value是多少,所以你还是需要先算一个forward pass,把所有节点的值都算出来,你才能算出所有的edge上的偏微分,才能够把一路上的东西都算出来,所以你还是先需要forward pass才能够跑reverse mode,所以和右边那个是一样的。

你想要检查一下左边和右边是不是一样的,右边有transpose,左边没有transpose。为什么?我们算出zc的偏微分以后还要和wz的偏微分相乘。\delta 是一个vector,a也是一个vector,你把这两个vector相乘以后,要得到一个matrix,这个matrix的size和weight matrix的哪个size是一样的,这个时候你需要对backward pass得到的\delta 先做transpose,才能和左边的a乘在一起,所以做完transpose以后, \delta 里面的transpose就不见了,左边和右边算出的结果会是一样的。

6.4 循环网络的计算图(Computational Graph for Recurrent Network)

前面说的是fully connected feedforward network,接下来要说Recurrent network,其实原理都是一样的:

  1. 画出computational graph;
  2. 把偏微分都算出来;
  3. 然后reverse mode跑一把。

先看一下Recurrent network function:


那如果我们把recurrent network转换为computational graph,他会长什么样子呢?

首先有一个input xw相乘得到n(没有在式子中出现)。 这个只是一部分。

如果我们把recurrent network完整的画出来的话(简化):


这是一个时间点,在下一个时间点:


当RNN考虑的是total cost,所以你要把
c^{1}\ c^{2}\ c^{3}
统统的加起来得到
c
,这个
c
就是total cost。

接下来我们就需要把每一个edge上的偏微分都算出来:


我们从c开始reverse mode


1,2,3
这三个节点算的偏微分,他们其实是共用参数的,所以你需要把
\frac{\partial C}{\partial W^{h}}
都算出来,然后全部加起来,才是真正的
\frac{\partial C}{\partial W^{h}}
的偏微分。

你可能感兴趣的:(6.计算图&反向传播(Computational Graph & Backpropagation))