Python与人工神经网络(4)——反向传播算法

来源:互联网 发布:网络军事评论员 编辑:程序博客网 时间:2024/05/21 06:32
上期的文章写的比较晦涩难懂,也留下了一个问题,就是反向传播算法(backpropagation algorithm)。写的这么晦涩一来是因为我确实看的比较艰难,从看书,到敲代码、调试运行代码,用了太长时间,要保证定期发送,写文章的时间就不够了,加上看的艰难,写起来就更艰难,写加修改竟然花了十个小时左右。另一方面我的数学确实太渣,文中好多数学公式,我解释的并不是很清楚。所以这一期有两个任务,第一个是稍微补充点上期的内容,让上期的内容更容易理解,第二个就是介绍反向传播算法。

上一篇文章留下的一个最迷的问题,就是神经网络中每条线权重的计数方法,我稍微提了一下,既没有详细解释,也没有证明结论。把上一篇文章说的弄通用一点,就是我们是用W[l][j][k]来表示从第(l-1)层的第k个神经元到第l层上第j个神经元之间的权重w值。如图所示:

图中的表示和我的的表示可能有点不同,因为在微信公众号里面没法输入上下标,不过意思是一样的。
同样的,b[l][j]表示第l层第j个神经元的偏移量b值。另外引入一个之前没用的的变量a,a[l][j]表示第l层第j个神经元的神经信号,其实也就是他代表的值,如果是输入神经元,a就代表输入值,如果是输出神经元,a就代表输出值,如图。

避免混淆,我说明一下,虽然上图的b和a都放在神经元里面,他们表示的是不同的东西。
还记得我们在第二期说过的S曲线神经元么σ(z)或者说σ(w·a,b)么,下一层神经元的值,是由上一层的值加上两层之间的w再加本层的b计算来的。所以对于某个神经元的值a[l][j](l>1):

这里的求和,是指第l-1层里面所有的k个神经元。不过,我们得把他们全部定义层向量,一个个算实在是费事了。所以第(l-1)层与l层之前所有连线的权重w定义成向量wl,第l层的的所有偏移量b定义层向量bl,当然第l层所有神经元的信号(值)a就定义成al。我们现在需要将上面的方程式改写成向量形式。也就是说,再前面的一个方程里面,我们是对向量中的每个值运用函数σ(w·a,b),现在我们要整体用。实际上,对于一个函数f(x),v表示一个矢量,vj表示矢量的第j个值,有

也就是说,对整个矢量使用函数,就是对函数里面的每个元素使用,然后组成矢量。
所以上面的方程就可以改成矢量形式:

al,wl,bl上面都已经解释了。既然原始方程是σ(z),我们不如顺手再定义一个矢量zl:

应该注意到,这里面的矩阵相乘,不是线性代数里面的矩阵相乘,而是两个矩阵对应位置元素的乘积,叫哈达玛乘积(hadamard product),记作s⊙t。用公式表示就是:

举个例子:

当然在我们使用的数学运算库Numpy已经提供了这样的方法。
好了,接下来进入正题,反向传播算法。上期说到反向传播算法主要用于计算成本函数对于每一个w和b值的偏导数∂C/∂w和∂C/∂b。要计算这个,我们先说四个方程。
首先,对于每一个神经元中w和b的值变化,或者说z值的变化,都会引起成本函数C的变化,所以有:

这里的Δz[l][j]当然是指第l层的第j个神经元引起的变化。我们用一个值来描述这个变化的幅度δ[l][j]。当然δ[l][j]的定义如下:

这个的意义在哪里呢?当然就是当他大的时候,表明改变这个神经元的w和b的值对C改变的贡献大,否则就很小。在原作者书中δ的名称是error,这里我就真的不知道该怎么翻译,所以以后就直接用符号δ表示好了。
好了,现在可以说第一个方程了:

第一个方程的向量形式:

第二个方程:

第三个方程:

第四个方程:

我不准备证明这四个方程,其中第一个和第二个原作者是证明了的,网址:http://neuralnetworksanddeeplearning.com/chap2.html#proof_of_the_four_fundamental_equations_(optional)。第三个和第四个的证明方法跟第一个和第二个类似,过程很简单。
那么这四个方程有啥用呢。第一个当然是计算δ啦,根据输入值,根据随机取得的w和b,我们一定是能过算出最后一层的输出值aL的。有了aL,有C和σ的方程,利用其导数求个δ对计算机来说还是很容易的。有了最后一层的δ,就可以使用第二个方程来计算倒数第二层的δ啦,以此类推,一直可以算到证书第二层。现在是不是优点明白反向传播算法是什么了。至于第三个和第四个方程,不就是我们的反向传播算法需要算的东西么。
基础的四个方程说完,正式说反向传播算法了,反向传播算法分为五个步骤:
1、输入。也就是传入输入值,也就是让神经网络的第一层的神经元全有信号值。
2、推导。有了第一层的数据,加上随机给定的w值和b值,使用函数σ(w·a,b)计算第二层,第三层一直到最后一层的值。
3、计算最后一层的δ。根据我们前面说的四个方程的第一个,计算最后一层的δ值。
4、反向传播。根据四个方程中的第二个,计算倒数第二层,倒数第三次,直到第一层的δ值。
5、输出。根据四个方程中的第三个和第四个,计算梯度值∂C/∂w和∂C/∂b。
搞定!
实际在使用的过程中,每次都是有几个输入值,然后算平均的梯度值。上一篇文章在这方面已经有论述。
现在去看看代码,其中的backprop方法就是实现了反向传播算法,update_mini_batch实现了平均值的计算。
反向传播算法讲完了,但是有个问题得说明一下。因为机智的小伙伴会发现,我们完全是可以通过倒数的定义来求偏导数∂C/∂w和∂C/∂b的。以W为例:

这个ε对精确计算来说,怎么选取是个问题,但我们其实只需要一个近似值,所以只要选的足够小就可以了(文中关于导数的部分很多都应该是约等于,我全部写成了等于号)。
然而这么算意味着什么呢?意味着对于每个∂C/∂w和∂C/∂b,我们都得运算两次成本函数C,而C又是一个比较复杂的函数。注意是每个∂C/∂w和∂C/∂b哟,实际上我们还有那么多个训练样本,每个训练样本都得去算一次梯度值,往往一次仅仅几万个训练样本数据,就得计算上千万次成本函数。而反向传播算法高明的地方在于一次推导一次反向传播,所有的梯度值都出来了,极大的提高了运算速度。实际上直到反向传播算法发明,神经网络特别是有好多隐藏层的深度神经网络才真正开始实用起来。

欢迎关注我的微信公众号获取最新文章:


1 0
原创粉丝点击