本篇博客主要介绍经典的三层BP神经网络的基本结构及反向传播算法的公式推导。

我们首先假设有四类样本,每个样本有三类特征,并且我们在输出层与隐藏层加上一个偏置单元。这样的话,我们可以得到以下经典的三层BP网络结构:

这里写图片描述

当我们构建BP神经网络的时候,一般是有两个步骤,第一是正向传播(也叫做前向传播),第二是反向传播(也就是误差的反向传播)。

Step1 正向传播
在正向传播之前,可以先给W,b赋初始值,最好不要全设置为0,不然后面会出现问题。赋完初值后,下面开始正向传播:

neth1=W11i1+W12i2+W13i3+b1neth1=W11∗i1+W12∗i2+W13∗i3+b1<script type="math/tex" id="MathJax-Element-3">net_{h_{1}}=W_{11}*i_{1}+W_{12}*i_{2}+W_{13}*i_{3}+b_{1}</script>
Outh1=11+eneth1Outh1=11+e−neth1<script type="math/tex" id="MathJax-Element-4">Out_{h_{1}}=\frac{1}{1+e^{-net_{h_{1}}}}</script> ——>激活函数为sigmoid函数:y=11+exy=11+e−x<script type="math/tex" id="MathJax-Element-5">y=\frac{1}{1+e^{-x}}</script>

隐含层到输出层:
netO1=W11h1+W12h2+W13h3+W14h4+b1=4j=1W1jOuthj+b1netO1=W11′∗h1+W12′∗h2+W13′∗h3+W14′∗h4+b1′=∑j=14W1j′∗Outhj+b1′<script type="math/tex" id="MathJax-Element-6">net_{O_{1}}=W_{11}^{'}*h_{1}+W_{12}^{'}*h_{2}+W_{13}^{'}*h_{3}+W_{14}^{'}*h_{4}+b_{1}^{'}= \sum_{j=1}^{4}W_{1j}^{'}*Out_{h_{j}}+b_{1}^{'}</script>
OutO1=enetO1enetO1+enetO2+enetO3+enetO4OutO1=enetO1enetO1+enetO2+enetO3+enetO4<script type="math/tex" id="MathJax-Element-7">Out_{O1} = \frac{e^{net_{O1}}}{e^{net_{O1}}+e^{net_{O2}}+e^{net_{O3}}+e^{net_{O4}}}</script>
——>激活函数为Softmax型、常用于多分类问题

到这里我们已经完成了正向传播,这里我之所以没给出向量的形式,是因为我觉得标量的形式容易理解。下面我们开始反向传播。

Step2 反向传播
1.计算总误差
这里我们采用交叉熵损失函数,我看到网上大部分都是采用均方误差形式的损失函数,使用交叉熵的相对较少,且使用交叉熵损失函数具有较多优点。

Etotal=4i=1targetOilnOutOiEtotal=−∑i=14targetOi∗lnOutOi<script type="math/tex" id="MathJax-Element-8">E_{total}=-\sum_{i=1}^{4}target_{O_{i}}*lnOut_{O_{i}}</script>
这里其实只有一项,因为targetOitargetOi<script type="math/tex" id="MathJax-Element-9">target_{O_{i}}</script>无论在何时都只有一项为1,也就是说,要么第一类,此时Etotal=targetO1lnOutO1Etotal=−targetO1∗lnOutO1<script type="math/tex" id="MathJax-Element-10">E_{total}=-target_{O_{1}}*lnOut_{O_{1}}</script>. 要么第二类,此时Etotal=targetO2lnOutO2Etotal=−targetO2∗lnOutO2<script type="math/tex" id="MathJax-Element-11">E_{total}=-target_{O_{2}}*lnOut_{O_{2}}</script>,要么是第三类,第四类等情况。

2.隐含层到输出层的权值更新
W11W11′<script type="math/tex" id="MathJax-Element-12">W_{11}^{'}</script>为例,我们想知道W11W11′<script type="math/tex" id="MathJax-Element-13">W_{11}^{'}</script>对整体误差产生了多少影响,可用整体误差对W11W11′<script type="math/tex" id="MathJax-Element-14">W_{11}^{'}</script>求偏导得出。
EtotalW11=EtotalOutO1OutO1netO1netO1W11∂Etotal∂W11′=∂Etotal∂OutO1∗∂OutO1∂netO1∗∂netO1∂W11′<script type="math/tex" id="MathJax-Element-15">\frac{\partial E_{total}}{\partial W_{11}^{'}}= \frac{\partial E_{total}}{\partial Out_{O_{1}}}*\frac{\partial Out_{O_{1}}}{\partial net_{O_{1}}}*\frac{\partial net_{O_{1}}}{\partial W_{11}^{'}}</script>

下面我们依次来计算每个式子(不要着急,一步一步算):
EtotalOutO1=targetO11OutO1∂Etotal∂OutO1=−targetO1∗1OutO1<script type="math/tex" id="MathJax-Element-16">\frac{\partial E_{total}}{\partial Out_{O_{1}}}= -target_{O_{1}}*\frac{1}{Out_{O_{1}}}</script>
OutO1netO1=OutO1(1OutO1)∂OutO1∂netO1=OutO1∗(1−OutO1)<script type="math/tex" id="MathJax-Element-17">\frac{\partial Out_{O_{1}}}{\partial net_{O_{1}}}= Out_{O_{1}}*(1-Out_{O_{1}})</script>
netO1netW11=Outh1∂netO1∂netW11′=Outh1<script type="math/tex" id="MathJax-Element-18">\frac{\partial net_{O_{1}}}{\partial net_{W_{11}^{'}}}= Out_{h_{1}}</script>

然后将三者相乘,就可以了嘛??
答案是否定的,之前我也是这么推导的,结果在迭代时发现,权值一直在增大,后来经过很长时间的分析才发现,原来这里的EtotalW11∂Etotal∂W11′<script type="math/tex" id="MathJax-Element-19">\frac{\partial E_{total}}{\partial W_{11}^{'}}</script>我求错了。

问题出在哪里呢?

是因为采用了交叉熵的损失函数,在更新W11W11′<script type="math/tex" id="MathJax-Element-160">W_{11}^{'}</script>时,误差不仅仅来自于O1O1<script type="math/tex" id="MathJax-Element-161">O_{1}</script>,还与其他所有的输出层的节点有关系。咋一看非常不可思议,但是仔细一想,你会发现因为在计算 OutOiOutOi<script type="math/tex" id="MathJax-Element-162">Out_{O_{i}}</script>是,分母中e的指数涉及到了其他所有的神经元的输出,即netO2netO2<script type="math/tex" id="MathJax-Element-163">net_{O_{2}}</script>、netO3netO3<script type="math/tex" id="MathJax-Element-164">net_{O_{3}}</script>等。
所以,我们对W11W11′<script type="math/tex" id="MathJax-Element-165">W_{11}^{'}</script>的偏导就应该是:
EtotalW11=EtotalOutO1OutO1netO1netO1W11+EtotalOutO2OutO2netO1netO1W11+EtotalOutO3OutO3netO1netO1W11+EtotalOutO4OutO4netO1netO1W11∂Etotal∂W11′=∂Etotal∂OutO1∗∂OutO1∂netO1∗∂netO1∂W11′+∂Etotal∂OutO2∗∂OutO2∂netO1∗∂netO1∂W11′+∂Etotal∂OutO3∗∂OutO3∂netO1∗∂netO1∂W11′+∂Etotal∂OutO4∗∂OutO4∂netO1∗∂netO1∂W11′<script type="math/tex" id="MathJax-Element-166">\frac{\partial E_{total}}{\partial W_{11}^{'}}= \frac{\partial E_{total}}{\partial Out_{O_{1}}}*\frac{\partial Out_{O_{1}}}{\partial net_{O_{1}}}*\frac{\partial net_{O_{1}}}{\partial W_{11}^{'}} + \frac{\partial E_{total}}{\partial Out_{O_{2}}}*\frac{\partial Out_{O_{2}}}{\partial net_{O_{1}}}*\frac{\partial net_{O_{1}}}{\partial W_{11}^{'}} + \frac{\partial E_{total}}{\partial Out_{O_{3}}}*\frac{\partial Out_{O_{3}}}{\partial net_{O_{1}}}*\frac{\partial net_{O_{1}}}{\partial W_{11}^{'}} + \frac{\partial E_{total}}{\partial Out_{O_{4}}}*\frac{\partial Out_{O_{4}}}{\partial net_{O_{1}}}*\frac{\partial net_{O_{1}}}{\partial W_{11}^{'}}</script>

因为我这里有四类,所以显得式子很长,但是经过化简,可以得到以下式子:

EtotalW11=(OutO1targetO1)Outh1∂Etotal∂W11′=(OutO1−targetO1)∗Outh1<script type="math/tex" id="MathJax-Element-167">\frac{\partial E_{total}}{\partial W_{11}^{'}}= (Out_{O_{1}}-target_{O_{1}})*Out_{h_{1}}</script>

我们可以令OutO1targetO1OutO1−targetO1<script type="math/tex" id="MathJax-Element-168">Out_{O_{1}}-target_{O_{1}}</script>为δO1δO1<script type="math/tex" id="MathJax-Element-169">\delta _{O_{1}}</script>,意思是在该神经元输出点的误差值,那么我们就可以很容易的得到权值的更新公式:
W11=W11ηδO1Outh1W11′=W11′−ηδO1Outh1<script type="math/tex" id="MathJax-Element-170">W_{11}^{'} = W_{11}^{'} - \eta \delta _{O_{1}}Out_{h{1}}</script>

同理,我们可以得到偏置的更新公式:
b1=b1ηδO1b1′=b1′−ηδO1<script type="math/tex" id="MathJax-Element-171">b_{1}^{'} = b_{1}^{'} - \eta \delta _{O_{1}}</script>

其中,ηη<script type="math/tex" id="MathJax-Element-172">\eta</script>表示学习率,这是一个可以自己调节的变量,看自己的数据分布情况,可设为0.1、0.05等等。

从输入层到隐含层的权值更新:
EtotalW11=EtotalOuth1Outh1neth1neth1W11∂Etotal∂W11=∂Etotal∂Outh1∗∂Outh1∂neth1∗∂neth1∂W11<script type="math/tex" id="MathJax-Element-173">\frac{\partial E_{total}}{\partial W_{11}} = \frac{\partial E_{total}}{\partial Out_{h_{1}}}*\frac{\partial Out_{h_{1}}}{\partial net_{h_{1}}}*\frac{\partial net_{h_{1}}}{\partial W_{11}}</script>

其中,EtotalOuth1=EO1Outh1+EO2Outh1+EO3Outh1+EO4Outh1∂Etotal∂Outh1=∂EO1∂Outh1+∂EO2∂Outh1+∂EO3∂Outh1+∂EO4∂Outh1<script type="math/tex" id="MathJax-Element-174">\frac{\partial E_{total}}{\partial Out_{h_{1}}} = \frac{\partial E_{O_{1}}}{\partial Out_{h_{1}}} + \frac{\partial E_{O_{2}}}{\partial Out_{h_{1}}} + \frac{\partial E_{O_{3}}}{\partial Out_{h_{1}}} + \frac{\partial E_{O_{4}}}{\partial Out_{h_{1}}}</script> 是因为输出层的每一个神经元都对隐藏层的第一个神经元有误差的传递。

由此我们可以得到:
EtotalW11=(δO1W11+δO2W12+δO3W13+δO4W14)Outh1(1Outh1)i1∂Etotal∂W11=(δO1∗W11′+δO2∗W12′+δO3∗W13′+δO4∗W14′)∗Outh1∗(1−Outh1)∗i1<script type="math/tex" id="MathJax-Element-175">\frac{\partial E_{total}}{\partial W_{11}} = (\delta _{O_{1}}*W_{11}^{'}+\delta _{O_{2}}*W_{12}^{'}+\delta _{O_{3}}*W_{13}^{'}+\delta_{O_{4}}*W_{14}^{'})*Out_{h_{1}}*(1-Out_{h_{1}})*i_{1}</script>
我们将
(δO1W11+δO2W12+δO3W13+δO4W14)Outh1(1Outh1)(δO1∗W11′+δO2∗W12′+δO3∗W13′+δO4∗W14′)∗Outh1∗(1−Outh1)<script type="math/tex" id="MathJax-Element-176">(\delta _{O_{1}}*W_{11}^{'}+\delta _{O_{2}}*W_{12}^{'}+\delta _{O_{3}}*W_{13}^{'}+\delta_{O_{4}}*W_{14}^{'})*Out_{h_{1}}*(1-Out_{h_{1}})</script>
记作δh1δh1<script type="math/tex" id="MathJax-Element-177">\delta_{h_{1}}</script>

那么,我们的输入层到隐含层的权值及偏置的更新策略为:
W11=W11ηδh1i1W11=W11−ηδh1i1<script type="math/tex" id="MathJax-Element-178">W_{11} = W_{11} - \eta \delta _{h_{1}}i_{1}</script>

b1=b1ηδh1b1=b1−ηδh1<script type="math/tex" id="MathJax-Element-179">b_{1}= b_{1} - \eta \delta _{h_{1}}</script>

至此,我们已经将两层之间的权值和偏置都更新完了,可以根据以上写成向量或者矩阵的形式,方便后面的运算。

最后,我想说,虽然现在很多机器学习的框架用的如火如荼,但是我觉得对于刚入门的同学来说,一些基本的公式推导和证明还是要掌握的。如有错误,欢迎交流和指正。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐