20分钟搞懂神经网络BP算法

简介: 通过一个具体的例子来说明神经网络中的BP算法,使大家能够很直观地感受BP算法的过程,对BP算法加深了解和认识。

在学习深度学习过程中,无意间发现一篇介绍BP算法的文章,感觉非常直观,容易理解。这篇文章的最大亮点是:不像其他介绍BP算法的文章,用一堆数据符号和公式来推导。文中通过使用一条具体的样本数据,为我们展示了模型训练中的参数迭代计算过程,为我们理解BP算法提供了很直观的理解视角;其次,作者也给出了使用python来实现BP的算法。只要你了解过传统神经网络结构以及大学微积分的知识,都可以毫不费力的在20分钟内完全理解BP算法。这里整理出来,供大家学习参考。要看原文的同学,直接跳到文末点击原文链接。


在开始之前,提醒下大家,注意公式中的下标,结合网络结构帮忙我们理解算法推导计算过程和细节。

网络结构和样本数据

跟所有训练神经网络或深度学习模型的流程一样,首先要先确定网络结构。这里为了介绍上的方便,以2个输入节点,2个隐藏节点,2个输出节点的网络(包括bias项)为例,展开对BP算法的介绍。如下图所示:
nn_BP_1.png
下面作者开始引入网络中参数的初始权重,以及一个训练样本,如下图中节点和边上的数值:
nn_BP_2.png

BP算法的目标就是优化神经网络的权重使得学习到的模型能够将输入值正确地映射到实际的输出值(也就是,希望模型能够模型真实数据产生的机制。在统计学中就是,我们要学习一个统计模型(统计分布函数),使得真实数据分布与统计模型产生的样本分布尽可能一致)。

如上图所示,下面的参数求解迭代过程,就是为了使得输入样本是0.05和0.10时(一个2维的样本数据),神经网络的输出与0.01和0.99接近。


前向传播过程

前向传播很简单,就是在已经给定的数据和参数下,按照网络结构来逐层传递数据,最后在输出层计算网络的输出与样本真实的目标值误差,这个误差就是模型的目标函数。

具体到这个case中,在给定模型输出权重和bias的条件下,我们需要把样本数据(0.05,0.10)通过图二中的网络逐步向后传递,看网络的输出与实际的输出的差异。

下面推导计算过程中,网络中使用的激活函数是logistic函数(或sigmoid函数):

$$ \sigma(x) = \frac{1}{1+e^{-x}} $$

首先来计算隐藏节点 $h_1$ 的输入值:

$$ net_{h1} = 0.15 * 0.05 + 0.2 * 0.1 + 0.35 * 1 = 0.3775 $$

得到$h_1$的输入值后,我们使用激活函数(logistic函数)来将输入值转化为为$h_1$的输出值:

$$ out_{h1} = \frac{1}{1+e^{-net_{h1}}} = \frac{1}{1+e^{-0.3775}} = 0.593269992 $$

按同样的方式,我们可以计算$h_2$的输出值:

$$ out_{h2} = 0.596884378 $$

类似于计算$h_1$和$h_2$的过程,我们可以计算输出层节点$o_1$和$o_2$的值。下面是$o_1$的输出值计算过程:

$$ net_{o1} = w_5 * out_{h1} + w_6 * out_{h2} + b_2 * 1 $$

$$ net_{o1} = 0.4 * 0.593269992 + 0.45 * 0.596884378 + 0.6 * 1 = 1.105905967 $$

$$ out_{o1} = \frac{1}{1+e^{-net_{o1}}} = \frac{1}{1+e^{-1.105905967}} = 0.75136507 $$

同样的方式,$o_2$的输出值为:

$$ out_{o2} = 0.772928465 $$

计算模型总误差

得到了网络的输出值后,就可以计算输出值与真实值之间的误差。这里我们使用平方误差来计算模型总误差:

$$ E_{total} = \sum \frac{1}{2}(target - output)^{2} $$

上式中的target就是样本目标值,或真实值。$\frac{1}{2}$只是为了计算上的整洁,对实际参数的估计没有影响 。(The $\frac{1}{2}$ is included so that exponent is cancelled when we differentiate later on. The result is eventually multiplied by a learning rate anyway so it doesn’t matter that we introduce a constant here。)

对于输出节点$o_1$的误差为:

$$ E_{o1} = \frac{1}{2}(target_{o1} - out_{o1})^{2} = \frac{1}{2}(0.01 - 0.75136507)^{2} = 0.274811083 $$

类似的计算方法,$o_2$的误差为:

$$ E_{o2} = 0.023560026 $$

最后,通过这个前向传递后,这个神经网络的总误差为:

$$ E_{total} = E_{o1} + E_{o2} = 0.274811083 + 0.023560026 = 0.298371109 $$

后向传播过程

后向传播过程就是迭代网络参数的过程,通过误差的后向传播得到新的模型参数,基于这个新的模型参数,再经过下一次的前向传播,模型误差会减小,从而使得模型输出值与实际值越接近。

输出层(output layer)

我们先来看了离误差最近的输出层中涉及的参数。以$w_5$为例,我们想知道$w_5$的改变对整体误差的影响,那么我们自然会想到对模型总误差求关于$w_5$的偏导数$\frac{\partial E_{total}}{\partial w_{5}}$。这个值也称为误差在$w_5$方向上的梯度。
应用求导的链式法则,我们可以对偏导数$\frac{\partial E_{total}}{\partial w_{5}}$进行如下的改写:

$$ \frac{\partial E_{total}}{\partial w_{5}} = \frac{\partial E_{total}}{\partial out_{o1}} * \frac{\partial out_{o1}}{\partial net_{o1}} * \frac{\partial net_{o1}}{\partial w_{5}} $$

这个公式可以对应到具体的相应网络结构:
nn_BP_3.png
为了得到$\frac{\partial E_{total}}{\partial w_{5}}$的值,我们需要计算上式中的每个因子的值。首先我们来计算误差关于$o_1$输出值的偏导数,计算方式如下:

$$ E_{total} = \frac{1}{2}(target_{o1} - out_{o1})^{2} + \frac{1}{2}(target_{o2} - out_{o2})^{2} $$

$$ \frac{\partial E_{total}}{\partial out_{o1}} = 2 * \frac{1}{2}(target_{o1} - out_{o1})^{2 - 1} * -1 + 0 $$

$$ \frac{\partial E_{total}}{\partial out_{o1}} = -(target_{o1} - out_{o1}) = -(0.01 - 0.75136507) = 0.74136507 $$

下一步就是要计算$ \frac{\partial out_{o1}}{\partial net_{o1}}$,这个值的含义如上图中所示,就是激活函数对自变量的求导:

$$ out_{o1} = \frac{1}{1+e^{-net_{o1}}} $$

$$ \frac{\partial out_{o1}}{\partial net_{o1}} = out_{o1}(1 - out_{o1}) = 0.75136507(1 - 0.75136507) = 0.186815602 $$

logistic函数对自变量求导,可参考:https://en.wikipedia.org/wiki/Logistic_function#Derivative

现在还需要计算最后一个引子的值$ \frac{\partial net_{o1}}{\partial w_{5}}$,这里$net_{o1}$就是激活函数的输入值:

$$ net_{o1} = w_5 * out_{h1} + w_6 * out_{h2} + b_2 * 1 $$

那么对$w_5$求偏导就很直接了:

$$ \frac{\partial net_{o1}}{\partial w_{5}} = 1 * out_{h1} * w_5^{(1 - 1)} + 0 + 0 = out_{h1} = 0.593269992 $$

得到三个因子后,我们就得到了总误差关于$w_5$的偏导数:

$$ \frac{\partial E_{total}}{\partial w_{5}} = \frac{\partial E_{total}}{\partial out_{o1}} * \frac{\partial out_{o1}}{\partial net_{o1}} * \frac{\partial net_{o1}}{\partial w_{5}} $$

$$ \frac{\partial E_{total}}{\partial w_{5}} = 0.74136507 * 0.186815602 * 0.593269992 = 0.082167041 $$

为了减小误差,我们就可以类似于梯度下降的方式,来更新$w_5$的值:

$$ w_5^{+} = w_5 - \eta * \frac{\partial E_{total}}{\partial w_{5}} = 0.4 - 0.5 * 0.082167041 = 0.35891648 $$

上式中的$\eta$为学习率(learning rate),这里设为0.5. 在实际训练模型中,需要根据实际样本数据和网络结构来进行调整。

以类似的方式,我们同样可以得到 $w_6, w_7, w_8$的更新值:
$w_6^{+} = 0.408666186$
$w_7^{+} = 0.511301270$
$w_8^{+} = 0.561370121$
至此,我们得到了输出层节点中的参数更新值。下面我们以同样的方式来更新隐藏层节点中的参数值。

隐藏层 (hidden layer)

在隐藏层中,同样地,我们对总误差求关于$w_1, w_2, w_3, w_4$的偏导数,来获得更新值。首先还是应用求导的链式法则对总误差关于$w_1, w_2, w_3, w_4$的偏导数,以$w_1$为例,分解如下:

$$ \frac{\partial E_{total}}{\partial w_{1}} = \frac{\partial E_{total}}{\partial out_{h1}} * \frac{\partial out_{h1}}{\partial net_{h1}} * \frac{\partial net_{h1}}{\partial w_{1}} $$

用网络结构图来表示如下,从图中可以更直观地理解这种分解的物理意义:
nn_BP_4.png

与输出层中对权重求偏导数不同的一个地方是,由于每个隐藏层节点都会影响所有的输出层节点,在求总误差对隐藏层的输出变量求偏导数时,需要对组成总误差的每个输出层节点误差进行分别求偏导数。具体如下:

$$ \frac{\partial E_{total}}{\partial out_{h1}} = \frac{\partial E_{o1}}{\partial out_{h1}} + \frac{\partial E_{o2}}{\partial out_{h1}} $$

我们先来求第一项$\frac{\partial E_{o1}}{\partial out_{h1}}$的值,过程如下:

$$ \frac{\partial E_{o1}}{\partial out_{h1}} = \frac{\partial E_{o1}}{\partial net_{o1}} * \frac{\partial net_{o1}}{\partial out_{h1}} $$

$$ \frac{\partial E_{o1}}{\partial net_{o1}} = \frac{\partial E_{o1}}{\partial out_{o1}} * \frac{\partial out_{o1}}{\partial net_{o1}} = 0.74136507 * 0.186815602 = 0.138498562 $$

这一步可以利用输出层的计算结果。

$$ net_{o1} = w_5 * out_{h1} + w_6 * out_{h2} + b_2 * 1 $$

$$ \frac{\partial net_{o1}}{\partial out_{h1}} = w_5 = 0.40 $$

因此,

$$ \frac{\partial E_{o1}}{\partial out_{h1}} = \frac{\partial E_{o1}}{\partial net_{o1}} * \frac{\partial net_{o1}}{\partial out_{h1}} = 0.138498562 * 0.40 = 0.055399425 $$

类似地,我们可以求得$\frac{\partial E_{o2}}{\partial out_{h1}}$的值:

$$ \frac{\partial E_{o2}}{\partial out_{h1}} = -0.019049119 $$

那么我们就可以得到$\frac{\partial E_{total}}{\partial out_{h1}}$的值:

$$ \frac{\partial E_{total}}{\partial out_{h1}} = \frac{\partial E_{o1}}{\partial out_{h1}} + \frac{\partial E_{o2}}{\partial out_{h1}} = 0.055399425 + -0.019049119 = 0.036350306 $$

我们还需要计算$ \frac{\partial out_{h1}}{\partial net_{h1}}$和$\frac{\partial net_{h1}}{\partial w}$就可以得到$\frac{\partial E_{total}}{\partial w_{1}}$的值了。这两个值的计算方法跟输出层的完全类似,过程如下:

$$ out_{h1} = \frac{1}{1+e^{-net_{h1}}} $$

$$ \frac{\partial out_{h1}}{\partial net_{h1}} = out_{h1}(1 - out_{h1}) = 0.59326999(1 - 0.59326999 ) = 0.241300709 $$

$$ net_{h1} = w_1 * i_1 + w_3 * i_2 + b_1 * 1 $$

$$ \frac{\partial net_{h1}}{\partial w_1} = i_1 = 0.05 $$

最后把三个因子相乘就是我们需要计算的值:

$$ \frac{\partial E_{total}}{\partial w_{1}} = \frac{\partial E_{total}}{\partial out_{h1}} * \frac{\partial out_{h1}}{\partial net_{h1}} * \frac{\partial net_{h1}}{\partial w_{1}} $$

$$ \frac{\partial E_{total}}{\partial w_{1}} = 0.036350306 * 0.241300709 * 0.05 = 0.000438568 $$

$w_1$的更新值为:

$$ w_1^{+} = w_1 - \eta * \frac{\partial E_{total}}{\partial w_{1}} = 0.15 - 0.5 * 0.000438568 = 0.149780716 $$

同样的方式,$ w_2, w_3, w_4$的更新值为:

$$ w_2^{+} = 0.19956143 $$

$$ w_3^{+} = 0.24975114 $$

$$ w_4^{+} = 0.29950229 $$

从上面更新隐藏层节点参数的过程中,我们可以看到,这里的更新并没有用到输出层节点更新后的参数的值,还是基于老的参数来进行的。这个不能搞混。

上面的计算中,并没有对bias项的权重进行更新,更新方式其实也很简单。可以类似操作。

至此,我们已经完成了一轮BP的迭代。经过这轮迭代后,基于新的参数,再走一遍前向传播来计算新的模型误差,这时已经下降到0.291027924,相比第一次的误差 0.298371109貌似没减少太多。但是我们重复这个过程10000次后,误差已经下降到0.0000351085,下降了很多。这时模型的输出结果为0.015912196和0.984065734,跟实际的结果0.01和0.99已经很接近了。


这里只是一个样本数据,那么我们有很多样本呢?很多样本的情况下的计算跟这一个样本数据相比,有什么不同呢?自己比划比划吧~


原文链接地址:https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/
pyhont代码:https://github.com/mattm/simple-neural-network/blob/master/neural-network.py

附:神经网络入门材料:http://neuralnetworksanddeeplearning.com/index.html 可以整体上了解神经网络结构以及训练过程中存在的问题。虽然是英文,但使用的词汇都比较简单,看起来很顺畅

相关文章
|
7天前
|
机器学习/深度学习 人工智能 算法
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
车辆车型识别,使用Python作为主要编程语言,通过收集多种车辆车型图像数据集,然后基于TensorFlow搭建卷积网络算法模型,并对数据集进行训练,最后得到一个识别精度较高的模型文件。再基于Django搭建web网页端操作界面,实现用户上传一张车辆图片识别其类型。
20 0
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
|
9天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于贝叶斯优化CNN-LSTM网络的数据分类识别算法matlab仿真
本项目展示了基于贝叶斯优化(BO)的CNN-LSTM网络在数据分类中的应用。通过MATLAB 2022a实现,优化前后效果对比明显。核心代码附带中文注释和操作视频,涵盖BO、CNN、LSTM理论,特别是BO优化CNN-LSTM网络的batchsize和学习率,显著提升模型性能。
|
19天前
|
机器学习/深度学习 算法 数据挖掘
基于GWO灰狼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
本项目展示了基于分组卷积神经网络(GroupCNN)和灰狼优化(GWO)的时间序列回归预测算法。算法运行效果良好,无水印展示。使用Matlab2022a开发,提供完整代码及详细中文注释。GroupCNN通过分组卷积减少计算成本,GWO则优化超参数,提高预测性能。项目包含操作步骤视频,方便用户快速上手。
|
20天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于WOA鲸鱼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
本项目展示了一种基于WOA优化的GroupCNN分组卷积网络时间序列预测算法。使用Matlab2022a开发,提供无水印运行效果预览及核心代码(含中文注释)。算法通过WOA优化网络结构与超参数,结合分组卷积技术,有效提升预测精度与效率。分组卷积减少了计算成本,而WOA则模拟鲸鱼捕食行为进行优化,适用于多种连续优化问题。
|
20天前
|
机器学习/深度学习 人工智能 算法
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
玉米病害识别系统,本系统使用Python作为主要开发语言,通过收集了8种常见的玉米叶部病害图片数据集('矮花叶病', '健康', '灰斑病一般', '灰斑病严重', '锈病一般', '锈病严重', '叶斑病一般', '叶斑病严重'),然后基于TensorFlow搭建卷积神经网络算法模型,通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型文件。再使用Django搭建Web网页操作平台,实现用户上传一张玉米病害图片识别其名称。
43 0
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
|
22天前
|
机器学习/深度学习 算法 5G
基于BP神经网络的CoSaMP信道估计算法matlab性能仿真,对比LS,OMP,MOMP,CoSaMP
本文介绍了基于Matlab 2022a的几种信道估计算法仿真,包括LS、OMP、NOMP、CoSaMP及改进的BP神经网络CoSaMP算法。各算法针对毫米波MIMO信道进行了性能评估,通过对比不同信噪比下的均方误差(MSE),展示了各自的优势与局限性。其中,BP神经网络改进的CoSaMP算法在低信噪比条件下表现尤为突出,能够有效提高信道估计精度。
31 2
|
14天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于贝叶斯优化卷积神经网络(Bayes-CNN)的多因子数据分类识别算法matlab仿真
本项目展示了贝叶斯优化在CNN中的应用,包括优化过程、训练与识别效果对比,以及标准CNN的识别结果。使用Matlab2022a开发,提供完整代码及视频教程。贝叶斯优化通过构建代理模型指导超参数优化,显著提升模型性能,适用于复杂数据分类任务。
|
11天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
29天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于MSER和HOG特征提取的SVM交通标志检测和识别算法matlab仿真
### 算法简介 1. **算法运行效果图预览**:展示算法效果,完整程序运行后无水印。 2. **算法运行软件版本**:Matlab 2017b。 3. **部分核心程序**:完整版代码包含中文注释及操作步骤视频。 4. **算法理论概述**: - **MSER**:用于检测显著区域,提取图像中稳定区域,适用于光照变化下的交通标志检测。 - **HOG特征提取**:通过计算图像小区域的梯度直方图捕捉局部纹理信息,用于物体检测。 - **SVM**:寻找最大化间隔的超平面以分类样本。 整个算法流程图见下图。
|
8天前
|
人工智能 算法 数据安全/隐私保护
基于遗传优化的SVD水印嵌入提取算法matlab仿真
该算法基于遗传优化的SVD水印嵌入与提取技术,通过遗传算法优化水印嵌入参数,提高水印的鲁棒性和隐蔽性。在MATLAB2022a环境下测试,展示了优化前后的性能对比及不同干扰下的水印提取效果。核心程序实现了SVD分解、遗传算法流程及其参数优化,有效提升了水印技术的应用价值。

热门文章

最新文章