神经网络的BP算法推导详解

神经网络 专栏收录该内容
31 篇文章 2 订阅

一个神经网络程序包含以下几部分内容。

  1.数据表达和特征提取。对于一个非深度学习神经网络,主要影响其模型准确度的因素就是数据表达和特征提取。同样的一组数据,在欧式空间和非欧空间,就会有着不同的分布。有时候换一种思考问题的思路就会使得问题变得简单。所以选择合适的数据表达可以极大的降低解决问题的难度。同样,在机器学习中,特征的提取也不是一种简单的事。在一些复杂问题上,要通过人工的方式设计有效的特征集合,需要很多的时间和精力,有时甚至需要整个领域数十年的研究投入。例如,PCA独立成分分析就是特征提取中常用的手段之一。但是很多情况下,人为都很难提取出合适的特征。

  由于不同问题下,可以提取不同的特征向量,这里将不做具体介绍。事实上深度学习解决的核心问题之一就是自动地将简单的特征组合成更加复杂的特征,并使用这些组合特征解决问题。

  2.定义神经网络的结构。由神经网络发展的历史可知,不同结构的神经网络在不同的问题下得到的效果不同。因此分析问题,选择与问题合适的神经网络结构也同样重要。

  3.训练神经网络的参数。使用训练数据集训练神经网络。主要是利用神经网络输出误差反向传播修正神经网络中的参数,甚至结构。反向传播过程中,步长选择对神经网络的训练有着重要的影响,在此基础上产生了多种训练方法。将在后面介绍。

  4.使用训练好的神经网络预测未知数据。训练神经网络的目的就是对未知数据预测。

  具体过程可以由如下流程图表示:

                               

一.BP算法的提出及其算法思想

神经网络主要是由三个部分组成的,分别是:1) 网络架构 2) 激活函数  3) 找出最优权重值的参数学习算法.

    BP算法就是目前使用较为广泛的一种参数学习算法.

    BP(back propagation)神经网络是1986年由Rumelhart和McClelland为首的科学家提出的概念,是一种按照误差逆向传播算法训练的多层前馈神经网络。    

    既然我们无法直接得到隐层的权值,能否先通过输出层得到输出结果和期望输出的误差来间接调整隐层的权值呢?BP算法就是采用这样的思想设计出来的算法,它的基本思想:学习过程由信号的正向传播(求损失)与误差的反向传播(误差回传)两个过程组成。如图所示为BP算法模型示意图:
                   

二.BP算法

 BP算法的一般流程:

  1. 正向传播FP(求损失).在这个过程中,我们根据输入的样本,给定的初始化权重值W和偏置项的值b, 计算最终输出值以及输出值与实际值之间的损失值.如果损失值不在给定的范围内则进行反向传播的过程; 否则停止W,b的更新.
  2. 反向传播BP(回传误差).将输出以某种形式通过隐层向输入层逐层反传,并将误差分摊给各层的所有单元,从而获得各层单元的误差信号,此误差信号即作为修正各单元权值的依据。

    由于BP算法是通过传递误差值δ进行更新求解权重值W和偏置项的值b, 所以BP算法也常常被叫做δ算法.

    下面我们将以下图所示的神经网络,该图所示是一个三层神经网络,两层隐藏层和一层输出层,输入层有两个神经元,接收输入样本,为网络的输出。

                        

三.前馈计算的过程

     为了理解神经网络的运算过程,我们需要先搞清楚前馈计算,即数据沿着神经网络前向传播的计算过程,以上图所示的网络为例,输入的样本为:  \vec{a}=(x_{1},x_{2})

三层网络的参数定义为:

第一层隐藏层的计算

                           

第一层隐藏层有三个神经元:neu₁、neu₂和。neu₃该层的输入为:

                                         

以单个神经元为例,则其输入为:

      假设我们选择函数f(x)作为该层的激活函数(图中的激活函数都标了一个下标,一般情况下,同一层的激活函数都是一样的,不同层可以选择不同的激活函数),那么该层的输出为:f_{1}(z_{1}) 、f_{2}(z_{2})f_{3}(z_{3})

第二层隐藏层的计算:
                      

第二层隐藏层有两个神经元:neu₄和neu₅。该层的输入为:

                               

即第二层的输入是第一层的输出乘以第二层的权重,再加上第二层的偏置。因此得到z_{4}z_{5}的输入分别为:

该层的输出分别为:f_{4}(z_{4})f_{5}(z_{5})

输出层的计算

                              

输出层只有一个神经元:neu₆。该层的输入为:

                              

即:

                           

因为该网络要解决的是一个二分类问题,所以输出层的激活函数也可以使用一个Sigmoid型函数,神经网络最后的输出为f_{6}(z_{6})

四.反向传播的计算

      我们已经了解了数据沿着神经网络前向传播的过程,这一节我们来介绍更重要的反向传播的计算过程。假设我们使用随机梯度下降的方式来学习神经网络的参数,损失函数定义为L(y,\hat{y}),其中y是该样本的真实类标。使用梯度下降进行参数的学习,我们必须计算出损失函数关于神经网络中各层参数(权重w和偏置b)的偏导数。

    假设我们要对第k层隐藏层的参数W^{(k)}和求偏导数b^{(k)}。假设z^{(k)}代表第k层神经元的输入,即

                                                   

    其中n^{(k-1)}为前一层神经元的输出,则根据链式法则有:

                                                  

                                                   

计算偏导数1:

前面说过,第k层神经元的输入为:

                                                  

因此可以得到:

                                 

                              

上式中,

 代表第k层神经元的权重矩阵的第m行,代表第k层神经元的权重矩阵的第m行中的第n列。

假设我们要计算第一层隐藏层的神经元关于权重矩阵的导数,则有:

                                     

                                             

计算偏导数2:

            偏导数 \frac{\partial L(y,\hat{y})}{\partial z^{(k)}}  又称为误差项( 也称为"灵敏度"),其值的大小代表了第一层神经元对于最终总误差的影响大小。根据第一节的前向计算,我们知道第k + 1层的输入与第k层的输出之间的关系为:

                               

其中:n^{(k)}=f_{k}(z_{k}),根据链式法则,我们可以得到:

                                    

     由上式我们可以看到,第k层神经元的误差项\delta^{(k)}是由第k + 1层的误差项乘以第k + 1层的权重,再乘以第k层激活函数的导数(梯度)得到的。这就是误差的反向传播。

现在我们已经计算出了偏导数,则分别表示为:

                         

                         

说实话,看到这我有点蒙了,上面的推导不太能看懂,涉及到迭代。。。。。

     假设每一层网络激活后的输出为f_{i}(x),其中i 为第i 层, x代表第i层的输入,也就是第i−1层的输出,f是激活函数,那么得出 :,记为

                                              \frac{\partial Loss}{\partial w_n}=\frac{\partial Loss}{\partial f_{n}}*\frac{\partial f_{n}}{\partial w_n}=\frac{\partial Loss}{\partial f_{n}}*f'*f_{n-1}

                                                            \frac{\partial Loss}{\partial f_n}=\frac{\partial Loss}{\partial f_{n+1}}*\frac{\partial f_{n+1}}{\partial f_n}

可以看出,这是一个类似迭代的公式,前一层的参数更新,有赖于后一层的损失。


下面这种推导可能更好理解一些:

BP 神经网络分为两个过程 
1. 工作信号正向传递子过程 
2. 误差信号逆向传递过程

在一般的BP神经网络中,单个样本有m个输入和n个输出,在输入层和输出层之间还有若干个隐藏层,实际上 1989年时就已经有人证明了一个万能逼近定理 : 
所以说一个三层的神经网络就可以实现一个任意从m维到n维的一个映射。这三层分别是 输入层、隐藏层、输出层 . 在BP神经网路中,输入层和输出层的节点数目都是固定的,关键的就是在于隐藏层数目的选择,隐藏层数目的选择决定了神经网络工作的效果,一般而言,有一个关于隐藏层数目的经验公式

其中 h 为隐藏层节点数目,m为输入层节点数目,n为输出层节点数目 a 为 1-10 之间的调节常数。一般而言如果数据多的话我们可以设a稍微大一点,而数据不是太多的时候就设置的小一点防止过拟合。

正向传递过程 : 

设节点 i 与 节点 j 之间的权值 为 w_{i,j},每个节点的输出值为 y_{j},具体的计算方法如下 :

                                                           S_{j}=\sum^{m-1}_{i=0} w_{i,j}x_{i}+b_{j}

                                                             y_{j}=f(S_{j})
其中f为激活函数 ,一般选择sigmoid函数或者线性函数.

BP神经网络的关键之处就在于反向误差的传播

假设我的第j个输出结果为d_{j}则误差函数如下所示 : 

                                                     E(w,b)=\frac{1}{2}\sum^{n-1}_{j=0} (d_{j}-y_{j})^{2}
BP神经网络的墓地就是通过不断修改w值和b值使得误差达到最小(一旦 w b 值全部确定以后,一个输入就对应着一个输出,所以只要让误差最小就可以了)而调整误差的方法就是使用梯度下降法不断减小误差。

我们选择激励函数为 

                                                   
对于介于隐藏层与输出层之间的权值w_{i,j}由偏微分公式我们可以得到 

                                                     

对于激励函数求导 (为了方便我们得到整体的导数)我们可以得到

                                            f^{'}(x)=\frac{f(x)[A-f(x)]}{AB}

然后对于w_{i,j}的偏导数我们也可以求出 

                                        
其中 :

                                              \delta _{i,j}=(d_{j}-y_{j})*\frac{f(S_{j})[A-f(S_{j})]}{AB}

对于bj的导数为 :

                                                  
       这就是著名的学习规则,通过改变神经元之间的连接权值来减少系统实际输出和期望输出的误差,这个规则又叫做Widrow-Hoff学习规则或者纠错学习规则。

    对最后一层处理完毕之后我们开始对前一层进行处理,首先将误差通过权值向前传递得到上一层的误差,那么同样的对于上一层使用梯度下降法最小化误差就可以了:

                               

        其中        \delta _{k,i}= \sum ^{n-1}_{j=0} \delta _{i,j}*w_{i,j}*\frac{f(S_{j})[A-f(S_{j})]}{AB}

剩下的就是根据梯度下降法更新w与b的值,以使得误差最小

                                            w_{i,j}=w_{i,j}-\eta _{1}*\frac{\partial E(w,b)}{\partial w_{i,j}}=w_{i,j}-\eta _{1}*\delta _{i,j}*x_{i}

                                                    b_{j}=b_{j}-\eta _{2}*\frac{\partial E(w,b)}{\partial b_{j}}=b_{j}-\eta _{2}*\delta _{i,j}

BP算法的示例

已知如图所示的网络结构.

                          

    设初始权重值w和偏置项b为:

    w=(0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.45,0.5,0.55,0.6,0.65)

    b = (0.35,0.65)-->这里为了便于计算,假设从输入层到隐层之间, 隐层到输出层之间的偏置项b恒定.

    1) FP计算过程

    ① 从输入层到隐层(其实这里的b可以看成一个特征)

    h1 = w1*l1 +w2*l2 +b1*1 = 0.1*5+0.15*10+0.35*1 = 2.35

    h2 = w3*l1 +w4*l2 +b1*1 = 0.2*5+0.25*10+0.35*1 = 3.85

    h3 = w5*l1 +w6*l2 +b1*1 = 0.3*5+0.35*10+0.35*1 = 5.35

    则各个回归值经过激活函数变换后的值为:

                                             

    ② 隐层到输出层

    net_o1 = outh1*w7 +outh2*w9 +outh3*w11+ b2*1= 2.35*0.4 + 3.85*0.5 +5.35 *0.6+0.65 = 2.10192   

    net_o2 = outh1*w8 +outh2*w10 +outh3*w12 +b2*1= 2.35*0.45 + 3.85*0.55 +5.35 *0.65 +0.65= 2.24629 

    则经过激活函数变换得到:

             out_o1 = 0.89109 (真实值0.01) ,  out_o2 = 0.90433 (真实值0.99)

    此时的平方和误差为:

                 

    与真实值不符,需要进行BP反馈计算.

    2) BP计算过程

    这里的BP计算我们分为两个部分.  ①隐层到输出层的参数W的更新   ②从输入层到隐层的参数W的更新.

   在这里,我们主要讲述第一部分隐层到输出层的参数W的更新. 

                                           

   首先,运用梯度下降法求解W7的值.

    目标函数:   ,由于此时求解W7,所以只与有关,则此时的损失函数为:

    可以看出, 为凸函数(开口向上), 有最小值且最小值在导数为0的点上.

    又有 net_o1 = outh1*w7 +outh2*w9 +outh3*w11+ b2*1, 

            outo1 =  f(net_o1)

    则对W7求偏导数得到:

,写错了吧

    又有W的更新公式为:

                                                                      

    这样将上式带入W的更新公式(5)就可以求得更新后的W值.最后,得到了W7的更新值为:

                 

    同理可以求得W9,W11,b2的更新值,这里就不在一一叙述了.

代码实现:

from numpy import *
class CBpnet:
#第一阶段 数据预处理阶段
    #构造函数,输入训练数据trainx,输出数据y
    def __init__(self,trainx,trainy):
        self.hidenum=2
        self.error=1;
        self.e=0;     
        self.learningrata=0.9#默认学习率为0.9
        self.trainy=self.__normalize__(trainy)#训练输出数据归一化
        self.data1=self.__normalize__(trainx)#训练输入数据归一化
        mx,nx=shape(trainx)
 
 
        #方案1 用0作为初始化参数 测试
        self.weight1=zeros((self.hidenum,mx));#默认隐藏层有self.hidenum个神经元,输入层与隐藏层的链接权值
        self.b1=zeros((self.hidenum,1));
        my,ny=shape(trainy)
        self.weight2=zeros((my,self.hidenum))#隐藏层与输出层的链接权值
        self.b2=zeros((my,1));
        #方案2、采用随机初始化为-1~1之间的数 测试
        self.weight1=2*random.random((self.hidenum,mx))-1
        self.weight2=2*random.random((my,self.hidenum))-1#隐藏层与输出层的链接权值
    #训练数据归一化至0~1
    def __normalize__(self,trainx):
        minx,maxx=self.__MaxMin__(trainx)
        return (trainx-minx)/(maxx-minx)
    def __MaxMin__(self,trainX):
        n,m=shape(trainX)
        minx=zeros((n,1))
        maxx=zeros((n,1))
        for i in range(n):
            minx[i,0]=trainX[i,:].min();
            maxx[i,0]=trainX[i,:].max();
        return minx,maxx
 
 
 
 #第二阶段 数据训练阶段
    def Traindata(self):
        mx,nx=shape(self.data1)
        #随机梯度下降法     
        for i in range(mx):
          #第一步、前向传播
            #隐藏层
            outdata2=self.__ForwardPropagation__(self.data1[:,i],self.weight1,self.b1)#隐藏层节点数值            
            #输出层   outdatatemp为隐藏层数值
            outdata3=self.__ForwardPropagation__(outdata2,self.weight2,self.b2)
 
            self.e=self.e+(outdata3-self.trainy[:,i]).transpose()*(outdata3-self.trainy[:,i])
            self.error=self.e/2.
            #计算每一层的残差
            sigma3=(1-outdata3).transpose()*outdata3*(outdata3-self.trainy[:,i])
            sigma2=((1-outdata2).transpose()*outdata2)[0,0]*(self.weight2.transpose()*sigma3)
            #计算每一层的偏导数
            w_derivative2=sigma3*outdata2.transpose()
            b_derivative2=sigma3
 
            w_derivative1=sigma2*self.data1[:,i].transpose()
            b_derivative1=sigma2
            #梯度下降公式
            self.weight2=self.weight2-self.learningrata*w_derivative2
            self.b2=self.b2-self.learningrata*b_derivative2
 
            self.weight1=self.weight1-self.learningrata*w_derivative1
            self.b1=self.b1-self.learningrata*b_derivative1
 
    def __ForwardPropagation__(self,indata,weight,b):
        outdata=weight*indata+b
        outdata=1./(1+exp(-outdata))
        return outdata</span>

注意:代码中使用的激活函数为f(x)=\frac{1}{1+e^{-x}},即上述公式推导中的A,B全为1的情况。
 

from:https://www.cnblogs.com/zk71124720/p/8551811.html

from :https://www.jianshu.com/p/97795a193272

from:https://blog.csdn.net/hjimce/article/details/45457181

from:https://blog.csdn.net/zhelong3205/article/details/78688476

from:https://blog.csdn.net/qq_32241189/article/details/80305566

相关推荐
<p> 需要学习Windows系统YOLOv4同学请前往《Windows版YOLOv4目标检测实战:原理与源码解析》, </p> <p> 课程链接 https://edu.csdn.net/course/detail/29865 </p> <h3> <span style="color:#3598db;">【为什么要学习这门课】</span> </h3> <p> <span>Linux</span>创始人<span>Linus Torvalds</span>有一句名言:<span>Talk is cheap. Show me the code. </span><strong><span style="color:#ba372a;">冗谈不够,放码过来!</span></strong> </p> <p> <span> </span>代码阅读是从基础到提高必由之路。尤其对深度学习,许多框架隐藏了神经网络底层实现,只能在上层调包使用,对其内部原理很难认识清晰,不利于进一步优化和创新。 </p> <p> YOLOv4是最近推出基于深度学习端到端实时目标检测方法。 </p> <p> YOLOv4实现darknet是使用C语言开发轻型开源深度学习框架,依赖少,可移植性好,可以作为很好代码阅读案例,让我们深入探究其实现原理。 </p> <h3> <span style="color:#3598db;">【课程内容与收获】</span> </h3> <p> 本课程将解析YOLOv4实现原理和源码,具体内容包括: </p> <p> - YOLOv4目标检测原理<br /> - 神经网络及darknetC语言实现,尤其是反向传播梯度求解和误差计算<br /> - 代码阅读工具及方法<br /> - 深度学习计算利器:BLAS和GEMM<br /> - GPUCUDA编程方法及在darknet应用<br /> - YOLOv4程序流程 </p> <p> - YOLOv4各层及关键技术源码解析 </p> <p> 本课程将提供注释后darknet源码程序文件。 </p> <h3> <strong><span style="color:#3598db;">【相关课程】</span></strong> </h3> <p> 除本课程《YOLOv4目标检测:原理与源码解析》外,本人推出了有关YOLOv4目标检测系列课程,包括: </p> <p> 《YOLOv4目标检测实战:训练自己数据集》 </p> <p> 《YOLOv4-tiny目标检测实战:训练自己数据集》 </p> <p> 《YOLOv4目标检测实战:人脸口罩佩戴检测》<br /> 《YOLOv4目标检测实战:中国交通标志识别》 </p> <p> 建议先学习一门YOLOv4实战课程,对YOLOv4使用方法了解以后再学习本课程。 </p> <h3> <span style="color:#3598db;">【YOLOv4网络模型架构图】</span> </h3> <p> 下图由白勇老师绘制 </p> <p> <img alt="" src="https://img-bss.csdnimg.cn/202006291526195469.jpg" /> </p> <p>   </p> <p> <img alt="" src="https://img-bss.csdnimg.cn/202007011518185782.jpg" /> </p>
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值