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

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



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

网络结构和样本数据

跟所有训练神经网络或深度学习模型的流程一样,首先要先确定网络结构。这里为了介绍上的方便,以2个输入节点,2个隐藏节点,2个输出节点的网络(包括bias项)为例,展开对BP算法的介绍。如下图所示:

下面作者开始引入网络中参数的初始权重,以及一个训练样本,如下图中节点和边上的数值:

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}}$$
这个公式可以对应到具体的相应网络结构:

为了得到$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}}$$
用网络结构图来表示如下,从图中可以更直观地理解这种分解的物理意义:

与输出层中对权重求偏导数不同的一个地方是,由于每个隐藏层节点都会影响所有的输出层节点,在求总误差对隐藏层的输出变量求偏导数时,需要对组成总误差的每个输出层节点误差进行分别求偏导数。具体如下:
$$\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 可以整体上了解神经网络结构以及训练过程中存在的问题。虽然是英文,但使用的词汇都比较简单,看起来很顺畅

时间: 2024-09-09 02:54:20

20分钟搞懂神经网络BP算法的相关文章

一文详解神经网络 BP 算法原理及 Python 实现

  什么是梯度下降和链式求导法则 假设我们有一个函数 J(w),如下图所示. 梯度下降示意图 现在,我们要求当 w 等于什么的时候,J(w) 能够取到最小值.从图中我们知道最小值在初始位置的左边,也就意味着如果想要使 J(w) 最小,w的值需要减小.而初始位置的切线的斜率a > 0(也即该位置对应的导数大于0),w = w – a 就能够让 w 的值减小,循环求导更新w直到 J(w) 取得最小值.如果函数J(w)包含多个变量,那么就要分别对不同变量求偏导来更新不同变量的值. 所谓的链式求导法则,

十分钟搞懂什么是CGI(转)

原文:CGI Made Really Easy,在翻译的过程中,我增加了一些我在学习过程中找到的更合适的资料,和自己的一些理解.不能算是严格的翻译文章,应该算是我的看这篇文章的过程的随笔吧. CGI真的很简单 在此之前,你或许听说过很多说CGI'晦涩难懂'的言论.如果你会写最基本的输入输出,那么你就可以写出一个CGI脚本.如果你已经是一个程序员,你只需要几分整就可以明白CGI到底是个什么东西.如果你还不是一个名程序员,对不起这篇文章不是很适合你,你可以先去学习一些编程的知识,例如脚本语言或者sh

10分钟搞懂分层实验原理

文/九德 1. 背景 想要同一时间做N个实验? 想要同一份流量不同实验之间不干扰? 想要每个实验都能得到100%流量? 那么你就需要分层实验. 1.1 什么是分层实验 分层实验概念:每个独立实验为一层,层与层之间流量是正交的. 简单来讲,就是一份流量穿越每层实验时,都会再次随机打散,且随机效果离散. 所有分层实验的奠基石--Goolge论文 <Overlapping Experiment Infrastructure More, Better, Faster Experimentation>

新手请问,Matlab里有关于卷积神经网络的BP算法可直接调用的函数或文件吗?麻烦大神求告知

问题描述 新手请问,Matlab里有关于卷积神经网络的BP算法可直接调用的函数或文件吗?麻烦大神求告知 对Matlab不熟,在学习卷积神经网络BP算法,想请问Matlab里有没有可调用的文件或函数直接拿来用的 解决方案 内置有这个函数的,具体实现的话我不是很了解.

中国人工智能学会通讯——一张图看懂BP算法 1.3 神经网络的 BP 算法

1.3 神经网络的 BP 算法 它描述了网络输出和目标输出之间的距离,刻画了网络性能的好坏.显然,J越小,网络性能越好.寻求J的极小点是BP算法的目标. 这一关系式表明只要能计算出敏感性,即可据此计算出梯度. 删除中间过程,有 通过计算网络最后一层神经元的敏感性,然后在网络中反向逐层计算其他层神经元敏感性,这就是所谓BP. 图4非常容易记忆和理解,这就是我们所谓的一张图看懂BP算法.

人工神经网络(Artificial Neural Netwroks)笔记-基本BP算法

单层的感知器并不能解决XOR问题 人工神经网络(Artificial Neural Netwroks)也因为这个问题而陷入了低潮,但是后来提出的多层感 知器却让人工神经网络(Artificial Neural Netwroks)再一次high起来 BP网络是最为广泛的一种.具体的原理介绍可以通过网络得到这里只描述算法流程 基本的BP算法 1 for k=1 toL do 初始化W^(k) 2初始化精度控制参数ε 3 E=ε+1 4 while E>ε do 4.1 E=0 4.2 对S中的每个样

中国人工智能学会通讯——一张图看懂BP算法 1.1 引言

1.1 引言 神经网络是实现人工智能的重要途径之一.近年来,深度神经网络在语音识别.图像理解.自然语言处理.智能博弈.智能医疗等领域内的大数据问题上取得一系列重大突破,引起了人们对神经网络的极大兴趣,并引领人工智能蓬勃向前发展.反向传播(backpropagation,BP)算法是深度神经网络最基本的算法之一,对深度神经网络的发展起着十分重要的作用,已成为深度神经网络理论的一个无可厚非的基石. BP算法的关键是反向计算敏感性,通过巧妙利用数学分析中求偏导数的链式法则,将敏感性反向传播,在数学上是

elasticsearch-请教:BP神经网络分词算法怎么整合到Elasticsearch中?急求解

问题描述 请教:BP神经网络分词算法怎么整合到Elasticsearch中?急求解 我想在Elasticsearch中使用BP神经网络分词算法,应该怎么样的步骤把算法嵌入到Elasticsearch的代码中,请大侠们指教

神经网络seo研究之BP算法

BP网络结构 &http://www.aliyun.com/zixun/aggregation/37954.html">nbsp;  对于多层前馈神经网络,目前广泛采用的是Romclhert和McClclland与1985年提出的误差逆向传播算法,即BP算法.这种网络结构如图所示: 它实际上是一个多层感知器,由输入节点.输出节点以及隐层节点(一层或多层)组成.同层节点之间没有连接,前一层和后一层之间相互充分连接. 激活函数 对于输入信号,先前向传播到隐节点,经激活函数作用后,最后由