相信对于神经网络大家都已经不陌生了,这篇文章主要介绍一下神经网络参数优化的时候反向传播算法的原理。
我们使用的神经网络
为了简单起见,我们使用只有三层的神经网络(包括输入层)如下图所示
神经网络中的参数表示
我们用wijl来表示第l−1层的中第j个结点到第l层中第i个结点的偏置
用bil表示第l层中第i个结点的偏置
用ail表示第l层中第i个结点通过**函数之后的输出值
用zil表示第l层中第i个结点没有经过**函数时的输出
基于上面的约定,我们可以给出上述神经网络的数学表示
从输入层到隐藏层可以表示为
从隐藏层到输出层可以表示为
当然,这样表示可能有点乱,我们可以将上述两个式子写成喜闻乐见的矩阵形式
zi3同理这里就不写了。
损失函数
我们使用均方误差C作为损失函数,其数学表达式为
c=21∗((t1−a13)2+(t2−a23)2)
其中t1为输出层中第一个结点的标记值;t2为输出层中第二个结点的标记值
我们的优化目标就是求出让损失函数的值最小的各个参数的值
神经网络中的参数非常多(包括wijl和bil),如果我们使用求导的方式来求最小值的话就很不现实。为了解决这个问题,我们可以使用反向传播算法,这个算法的神奇之处在于可以把优化参数时的求导操作变成求数列的地推式。
容易发现,我们很容易求得误差c对于a13和a23的导数,这样一来我们便可以根据a13和a23的导数来求ai2的导数,如此传递下去便能完成一轮优化的过程,是不是很神奇呢。下面我们来具体介绍一下反向传播算法的过程。
神经单元误差δ
这一块就是我们的重头戏了。不过在介绍反向传播算法前,我们需要先介绍神经单元误差δil的概念。
我们定义神经单元误差δil=∂zil∂c
有了这个误差,我们可以化简之前用链式求导法则得出的结果,比如我们要求 wij3的变化值Δwij3,其中Δwij3=−η∗∂wij3∂c。其求导路径如下图所示
我们根据链式求导法则得到
∂wij3∂c=∂a23∂c∂z23∂a23∂w233∂z23
其中∂a23∂c可以根据损失函数
c=21∗((t1−a13)2+(t2−a23)2)求出来∂a23∂c=(a23−t2)
其中∂z23∂a23可以根据**函数来求导,比如我们用sigmoid函数来举例,sigmoid函数的函数图像如下所示
其函数表达式为σ(x)=1+e−x1,对这个函数求导得到其导数为σ(x)(1−σ(x))
那么就有∂z23∂a23=σ(z23)(1−σ(z23))。
最后,为了求得∂w233∂z23,我们先来写一下z23的表达式
z23=w213a12+w223a22+w233a32+b23
这是一个线性函数,很容易可以得到∂w233∂z23=a32
如果我们令∂wij3∂c=∂z23∂c∂w233∂z23
那么就有∂wij3∂c=δ23a32
同理可得∂b23∂c=∂z23∂c∂b23∂z23=δ23
有了上面的理论基础,我们就可以继续介绍反向传播算法了。
反向传播算法
下面我们在来讨论一下神经单元误差δ的性质,也就是传递性。
我们可以将上一节的求导结果抽象化为任意一个参数的形式
∂wijl∂c=δilajl−1
∂bil∂c=δil 其中l=(2,3)
根据上面的式子,我们可以得到隐藏层中神经元的单元误差
δi2=(δ13w1i3+δ23w2i3)a′(zi2)(i=1,2,3),这样子我们便得到了第二层与第三层神经元δ之间的关系
其中δi3=∂zi3∂c=∂ai3∂c∂zi3∂ai3,这里面∂ai3∂c通过损失函数就可以算出来,因为损失函数为c(a13,a23)
并且∂zi3∂ai3通过**函数可以算出来,以sigmoid函数举例
∂zi3∂ai3=σ(zi3)(1−σ(zi3)),这样一来,我们就可以算出δ13和δ23了。
有了这两个值,我们根据递推式δi2=(δ13w1i3+δ23w2i3)a′(zi2)(i=1,2,3)就可以算出δi2(i=1,2,3)了
同理,有了δi2(i=1,2,3),我们就可以继续算出δi1(i=1,2,3,4),再将δil带入到∂wijl∂c和∂bil∂c的式子中就可以计算出各个参数的导数值,有了这个导数值,我们乘上学习率−η就能得到各个参数在这一轮训练中的变化率Δwijl和Δbil。
于是,在这一轮训练中我们可以更新我们的参数为wijl=wijl+Δwijl和bil=bil+Δbil
在理想状态下,经过很多很多次优化后,我们的模型中的参数最终收敛了,我们便得到了一个可用的神经网络,是不是很简单呢,哈哈哈。
总结
数学真神奇。