反向传播算法的理解(Nielsen版)
在学习standford大学机器学习在coursera上的公开课中,对于其中讲授的神经网络的反向传播算法不是很清楚,经过网上查找资料,觉得Michael Nielsen的「Neural Networks and Deep Learning」中的解释特别清楚,于是这份材料为主经过学习,现在说一下我的理解。
记忆BP算法的窍门
我总结记住反向传播算法的关键要素可以用三个数字来代表:214
- 两个假设
- 一个中间变量
- 四个公式
代价函数
在阐述反向传播算法之前,我们还是先明确代价函数的定义,我们定义代价函数的形式如下:
其中n是样本的总数,L是神经网络的总层数,
两个假设
总体的代价函数可以写成全部单个样本的代价总和的平均值。
也就是C=1n∑xCx ,其中Cx=12||y−aL||2 代价函数可以认为是神经网络输出的函数。
比如对于单独的一个样本来说,代价函数可以写成C=12||y−aL||=12∑j(yj−aLj)2
一个中间变量
我们定义神经网络中第
其中
凭什么这么定义?凭什么这么定义?凭什么这么定义?(重要的事情说三遍)
我认为理解这点是整个算法的核心,算法的全部内容都是基于这个假设展开的,如果这点不成立后面推导出花也没用!不幸的是,网上大多数文章都在这个问题都是一笔带过,在Nielsen的书里有对这点进行描述,我结合他的阐述以及我的理解表达一下我对这个假设的看法::如果我们在第
如果这个解释你还是理解不了,你就把它单纯的理解为是一种数学技巧吧,数学中我们经常会引入一些中间变量来方便计算。
我们为什么要引入这个中间变量,它非常重要,BP算法就是通过它来间接的计算
四个公式
BP1:输出层误差公式
推导过程:
- 我们之前定义了中间变量
δlj=∂C∂zlj
因此对于最后一层(输出层)则有δLj=∂C∂zLj ; - 由
C 是aL 的函数(第二个假设C=12||y−aL||=12∑j(yj−aLj)2 ),而a 是z 的函数(ali=σ(zli) ),根据链式求导法则有δLj=∑k∂C∂aLk∂aLk∂zLj ; - 为什么会有一个
∑k 呢?这是由于第二个假设C 可以看成aL 的函数C=12||y−aL||=12∑j(yj−aLj)2 ,也就是C 是由L层全部的输出构成的,因此在求导的时候需要对每一个L层的激励元进行求导,假设L层有k个,再加上链式求导法则于是∂C∂zLj 就写成∑k∂C∂aLk∂aLk∂zLj ; - 要注意
aLk 和zLj 的关系,一个是L层某个神经元的输出部分,一个是L层某个神经元的输入部分,只当这两个部分作用于同一神经元的时候才有意义,也就是k=j的时候aLk=σ(zLj) 这个式子才成立,或者说对于L层的第k个神经元来说,连到其他神经元的输入对它的输出没有意义啊,还可以这么理解对于z = x + 1 这样一个函数来说,你非要对y(一个不存在的自变量)求导(这个在复合函数求导中特别常见,经过第一次求偏导之后,第二次求偏导的自变量就没了因此得0),所以对于k≠j 的∂aLk∂zLj 都等于0,所以最后式子化为δLj=∂C∂aLj∂aLj∂zLj ; - 又因为
aLj=σ(zLj) ,所以式子就变成δLj=∂C∂aLjσ′(zLj) 这就是BP-1,BP-1是分量(elementwise)形式的表达方式。
再进一步由于C=12∑j(yj−aLj)2 ,所以∂C∂aLj=−∑j(yj−aLj)=∑j(aLj−yj) ,所以BP-1又可以写成δLj=∑j(aLj−yj)σ′(zLj) ,这个就是BP-1b的分量形式。 - BP-1a是BP-1的向量形式,
∇aC 表示C 在a 上的梯度,∇ 是梯度符号,梯度是一个向量,其中的每个分量就是∂C∂aLj ; - 而中间的普通乘积为什么会变成Hadamard乘积,这个其实就是公式从标量形式变成矢量形式的时候根据需要调整的,前两课的笔记里面有我是如何把通过标量形式推导出矢量形式的过程(基本上就是用几个小的数带进去观察一下规律总结出来的),吴恩达在standford大学机器学习讲义(CS229-notes-deeplearning)里面公式3.29到公式3.30也有类似的描述;
直观的解释
上面是从数学细节来证明的,我们再从直观的角度理解一下BP-1的含义:
BP2:相邻两层之间的误差关系
推导过程:
- 我们还是中间变量开始
δlj=∂C∂zlj - 这一步利用了一些数学技巧:对于第l+1层来说有
δl+1k=∂C∂zlj ,我们可以推导出,第l层的第j个神经元上的输入zlj 与第l+1层的的第k个神经元的输入zl+1k 之间存在着这样的关系,zl+1k=∑jwl+1kjσ(zlj)+bl+1k ,于是zl+1k 可以看成zlj 的函数,根据链式求导法则,我们重新改写一下δlj=∂C∂zlj 这个公式为δlj=∂C∂zlj=∑k∂C∂zl+1k∂zl+1k∂zlj ,而δl+1k=∂C∂zlj ,于是∑k∂C∂zl+1k∂zl+1k∂zlj=∑k∂zl+1k∂zljδl+1k -
∂zl+1k∂zlj=wl+1kjσ′(zlj)
为什么zl+1k 明明是求和公式怎么一求偏导就没有累加符号了?符号太多看着有点眼晕,那么我们用一个简化问题的大招——代几个小数进去把问题具体化再观察其规律。
比如这样的一个结构
j在l层,其取值范围为{1,2,3},k在l+1层,取值范围为{1,2},那么zl+1k=∑jwl+1kjσ(zlj)+bl+1k=wl+1k1σ(zl1)+wl+1k2σ(zl2)+wl+1k3σ(zl3)+bl+1k ,对于∂zl+1k∂zlj 我们令j=1 那么∂zl+1k∂zl1 相当于用上面展开的式子对zl1 求偏导,那么对于不含有zl1 的项自然为零了,结果就是wl+1k1σ(zl1) ;
所以∂zl+1k∂zlj=wl+1kjσ′(zlj) - 再回到式子
∑k∂zl+1k∂zljδl+1k 中把上一步的结果代入,得到δlj=∑kwl+1kjσ′(zlj) ,这个就是公式BP-2的分量形式。
直观的解释
还是以上面那个图为例
在原有的结构中,假设数据从输出层往输入层方向流动,我们可以知道利用原有的系数矩阵,将其转置就能计算从l+1层的输出到l层的输入。
注意这里visio画不出
BP3: 代价函数对任意一个神经元的偏置的偏导:
推导过程:
- 之前我们在推导相邻两层误差关系的时候利用了这个公式
zl+1k=∑jwl+1kjσ(zlj)+bl+1k ,所以有zlj=∑kwljkσ(zl−1k)+blj ,或者zlj=∑kwljkal−1k+blj (这里把l+1换成l,把j,k调换一下看着方便)。由这个公式我们可以知道zlj 是blj 的函数。 - 我们依然利用链式求导法则改写
∂C∂blj ,则有∂C∂blj=∂C∂zlj∂zlj∂blj - 注意到
∂C∂zlj 其实就是δlj ,而zlj 对blj 为1,也就是∑ 那一大堆被当成常数求导为0。所以∂C∂blj=δlj
直观的解释:
代价函数对某个神经元偏置的偏导就是这个神经元上的误差,这是一个特别好的性质。
BP4:代价函数对任意一个权重的偏导
推导过程:
- 我们依然使用
zlj=∑kwljkal−1k+blj 这个公式,这个公式我们可以知道zlj 是wljk 的函数 - 于是
∂C∂wljk=al−1kδlj 。注意这里依然涉及到累和求导的问题,最后只会留下一个。
直观解释
最后我们得到这样一个结论——代价函数对于某一条弧上的权重的偏导数等于这条弧左端的神经元的输出与弧右端神经元的误差的乘积。所以BP4有的时候也可以表示成
从BP4我们可以知道当**值(神经元的输出值)
学习缓慢的问题
下面我们我们从“学习缓慢”的角度来审视BP1到BP4这四个公式。
对于BP1,其中包含有
对于BP2也有类似的结果,因为BP2中也含有
对于这种由于高**值(
了解了BP算法的这种特性,有什么帮助呢?由于我们上面的推导其实不依赖于任何具体的函数,所以,我们可以设计一些具有特殊特性的学习函数来避免学习缓慢的情况。说白了还是为了优化计算速度。
小结一下反向传播的四个方程式
反向传播算法
-
输入x : 求输入层神经元的输出
a1 (这个一般来说就是样本输入) -
向前传播: 从第二层开始到最后一层,依次的计算各层的
zl 和al
forl in [2,L]
computezl (zl=wlal−1+bl )
computeal (al=σ(zl) ) -
计算输出层误差: 利用公式
δLj=∇aC⨀σ′(zLj) 计算出输出层的误差 -
反向传播误差:从L-1层开始一直到第二层,利用公式
δl=((wl+1)Tδl+1))⨀σ′(zl) 计算每一层的误差 -
输出:利用公式
∂C∂blj=δlj ,∂C∂wljk=al−1kδlj 计算代价函数对所有未知参数的偏导数