详解神经网络的前向传播和反向传播(从头推导)
日期: 2018-08-06 分类: 个人收藏 301次阅读
详解神经网络的前向传播和反向传播
本篇博客是对Michael Nielsen所著的《Neural Network and Deep Learning》第2章内容的解读,有兴趣的朋友可以直接阅读原文Neural Network and Deep Learning。
对神经网络有些了解的人可能都知道,神经网络其实就是一个输入 X X 到输出的映射函数: f(X)=Y f ( X ) = Y ,函数的系数就是我们所要训练的网络参数 W W ,只要函数系数确定下来,对于任何输入我们就能得到一个与之对应的输出 yi y i ,至于 yi y i 是否符合我们预期,这就属于如何提高模型性能方面的问题了,本文不做讨论。
那么问题来了,现在我们手中只有训练集的输入 X X 和输出,我们应该如何调整网络参数 W W 使网络实际的输出与训练集的 Y Y 尽可能接近?
在开始正式讲解之前,让我们先对反向传播过程有一个直观上的印象。反向传播算法的核心是代价函数对网络中参数(各层的权重 w w 和偏置)的偏导表达式 ∂C∂w ∂ C ∂ w 和 ∂C∂b ∂ C ∂ b 。这些表达式描述了代价函数值 C C 随权重或偏置 b b 变化而变化的程度。到这里,BP算法的思路就很容易理解了:如果当前代价函数值距离预期值较远,那么我们通过调整和 b b 的值使新的代价函数值更接近预期值(和预期值相差越大,则和 b b 调整的幅度就越大)。一直重复该过程,直到最终的代价函数值在误差范围内,则算法停止。
BP算法可以告诉我们神经网络在每次迭代中,网络的参数是如何变化的,理解这个过程对于我们分析网络性能或优化过程是非常有帮助的,所以还是尽可能搞透这个点。我也是之前大致看过,然后发现看一些进阶知识还是需要BP的推导过程作为支撑,所以才重新整理出这么一篇博客。
前向传播过程
在开始反向传播之前,先提一下前向传播过程,即网络如何根据输入得到输出 Y Y 的。这个很容易理解,粗略看一下即可,这里主要是为了统一后面的符号表达。
记为第 l−1 l − 1 层第 k k 个神经元到第层第 j j 个神经元的权重,为第 l l 层第个神经元的偏置, alj a j l 为第 l l 层第个神经元的激活值(激活函数的输出)。不难看出, alj a j l 的值取决于上一层神经元的激活:
利用 (2) ( 2 ) 式一层层计算网络的激活值,最终能够根据输入 X X 得到相应的输出。
反向传播过程
反向传播过程中要计算 ∂C∂w ∂ C ∂ w 和 ∂C∂b ∂ C ∂ b ,我们先对代价函数做两个假设,以二次损失函数为例:
假设1:总的代价函数可以表示为单个样本的代价函数之和的平均:
这个假设的意义在于,因为反向传播过程中我们只能计算单个训练样本的 ∂Cx∂w ∂ C x ∂ w 和 ∂Cx∂b ∂ C x ∂ b ,在这个假设下,我们可以通过计算所有样本的平均来得到总体的 ∂C∂w ∂ C ∂ w 和 ∂C∂b ∂ C ∂ b
假设2:代价函数可以表达为网络输出的函数 costC=C(aL) c o s t C = C ( a L ) ,比如单个样本 x x 的二次代价函数可以写为:
反向传播的四个基本方程
权重 w w 和偏置的改变如何影响代价函数 C C 是理解反向传播的关键。最终,这意味着我们需要计算出每个和 ∂C∂blj ∂ C ∂ b j l ,在讨论基本方程之前,我们引入误差 δ δ 的概念, δlj δ j l 表示第 l l 层第个单元的误差。关于误差的理解,《Neural Network and Deep Learning》书中给了一个比较形象的例子。
如上图所示,假设有个小恶魔在第 l l 层第个单元捣蛋,他让这个神经元的权重输出变化了 Δzlj Δ z j l ,那么这个神经元的激活输出为 σ(zlj+Δzlj) σ ( z j l + Δ z j l ) ,然后这个误差向后逐层传播下去,导致最终的代价函数变化了 ∂C∂zljΔzlj ∂ C ∂ z j l Δ z j l 。现在这个小恶魔改过自新,它想帮助我们尽可能减小代价函数的值(使网络输出更符合预期)。假设 ∂C∂zlj ∂ C ∂ z j l 一开始是个很大的正值或者负值,小恶魔通过选择一个和 ∂C∂zlj ∂ C ∂ z j l 方向相反的 Δzlj Δ z j l 使代价函数更小(这就是我们熟知的梯度下降法)。随着迭代的进行, ∂C∂zlj ∂ C ∂ z j l 会逐渐趋向于0,那么 Δzlj Δ z j l 对于代价函数的改进效果就微乎其微了,这时小恶魔就一脸骄傲的告诉你:“俺已经找到了最优解了(局部最优)”。这启发我们可以用 ∂C∂zlj ∂ C ∂ z j l 来衡量神经元的误差:
1. 输出层的误差方程
(BP1)方程中两项都很容易计算,如果代价函数为二次代价函数 C=12∑j(yj−aLj)2 C = 1 2 ∑ j ( y j − a j L ) 2 ,则 ∂C∂aLj=aLj−yj ∂ C ∂ a j L = a j L − y j ,同理,对激活函数 σ(z) σ ( z ) 求 zLj z j L 的偏导即可求得 σ′(zLj) σ ′ ( z j L ) 。将(BP1)重写为矩阵形式:
2. 误差传递方程
证明过程如下:
3. 代价函数对偏置的改变率
4. 代价函数对权重的改变率
从上面的推导我们不难发现,当输入神经元没有被激活,或者输出神经元处于饱和状态,权重和偏置会学习的非常慢,这不是我们想要的效果。这也说明了为什么我们平时总是说激活函数的选择非常重要。
当我计算得到 ∂C∂wljk ∂ C ∂ w j k l 和 ∂C∂blj ∂ C ∂ b j l 后,就能愉悦地使用梯度下降法对参数进行一轮轮更新了,直到最后模型收敛。
反向传播为什么快
回答这个问题前,我们先看一下普通方法怎么求梯度。以计算权重为例,我们将代价函数看成是权重的函数 C=C(w) C = C ( w ) ,假设现在网络中有100万个参数,我们可以利用微分的定义式来计算代价函数对其中某个权重 wj w j 的偏导:
再反观反向传播算法,如方程(BP4)所示,我们只要知道 al−1k a k l − 1 和 δlj δ j l 就能计算出偏导 ∂C∂wljk ∂ C ∂ w j k l 。激活函数值 al−1k a k l − 1 在一次前向传播后就能全部得到,然后利用(BP1)和(PB2)可以计算出 δlj δ j l ,反向传播和前向传播计算量相当,所以总共只需2次前向传播的计算量就能计算出所有的 ∂C∂wljk ∂ C ∂ w j k l 。这比使用微分定义式求偏导的计算量少了不止一点半点,简直是质的飞跃。
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
精华推荐