BP神经网络——逆向传播的艺术

RunMews 2023-05-03 18:50:42
机器学习基础算法python代码实现可参考:machine_learning_algorithms

1 BP 神经网络概述

?BP 神经网络是一类基于误差逆向传播 (BackPropagation, 简称 BP) 算法的多层前馈神经网络,BP算法是迄今最成功的神经网络学习算法。现实任务中使用神经网络时,大多是在使用 BP 算法进行训练。值得指出的是,BP算法不仅可用于多层前馈神经网络,还可以用于其他类型的神经网络,例如训练递归神经网络。但我们通常说 “BP 网络” 时,一般是指用 BP 算法训练的多层前馈神经网络。

2 神经网络的前馈过程

假设我们构造一个经典的神经网络,如下图,该网络可接收输入数据 x? ,包含两个属性 x_1, x_2? ,包含一个隐含层,隐含层的每个神经元首先通过 w^{[1]}? 和 b_1? (b 为偏置项) 将输入参数进行线性组合得到 z^{[1]}? ,然后将 z^{[1]}? 带入激活函数 (activation function) \sigma(z)? ,得到隐含层的一组输出 a^{[1]}? 。接下来,将这组 a^{[1]}? 通过 w^{[2]}? 和 b_2? 进行第二次线性组合,得到 z^{[2]}? ,再带入激活函数 \sigma(z)? ,得到最终的输出值 a^{[2]}? ,该值即为学习器 (广义上看,神经网络属于学习器的一种类型) 的推测结果 \hat{y}? 。

上述过程,即为该神经网络的前馈 (forward propagation) 过程,前馈过程也非常容易理解,符合人正常的逻辑,具体的矩阵计算表达如下:

z^{[1]}=w^{[1]}x+b^{[1]}

?a^{[1]}=g(z^{[1]})

?z^{[2]}=w^{[2]}a^{[1]}+b^{[2]}

a^{[2]}=g(z^{[2]})

3 逆向误差传播 (BP过程)

接下来我们就开始基于误差逆传播的神经网络学习过程,到此为止,我们我网络已经可以完成正向的计算,也就是说,给一组 x 的输入,就能够通过网络计算得到推测的结果 \hat{y} 。然而,当前我们并不知道网络中的参数,网络中的参数主要包含 w^{[1]} 、b^{[1]} 、 w^{[2]} 和 b^{[2]} ,激活函数 \sigma(z) 也有可能作为一个未知的参数,但在此我们简单起见,默认该函数为 Sigmoid 函数,即 \sigma(z)=\frac{1}{1+e^{-z}} 。那么,从机器学习的角度来看,我们的任务就是:给定了一组数据集,其中包含了输入数据 x 和输出的真实结果 y ,如何寻找一组最佳的神经网络参数,使得网络计算得到的推测值 \hat{y} 能够与真实值 y 吻合程度最高?

3.1 模型的损失函数

?为了达到这个目标,这也就转换为了一个优化过程,对于任何优化问题,总是会有一个目标函数 (objective function),在机器学习的问题中,通常我们称此类函数为:损失函数 (loss function),具体来说,损失函数表达了推测值与真实值之间的误差。抽象来看,如果把模型的推测以函数形式表达为 \hat{y}=h(x) (这里的函数字母使用 h 是源于“假设 hypothesis“ 这一单词),对于有个 m 训练样本的输入数据而言,其损失函数则可表达为:

?\displaystyle{ L=\frac{1}{m}{\sum_{i=1}^{m}{(\hat{y}-y)^2}} = \frac{1}{m}{\sum_{i=1}^{m}{(h(x)-y)^2}} }

?那么在该问题中的损失函数是什么样的呢?抛开线性组合函数,我们先着眼于最终的激活函数,也就是 a^{[2]}? 的值,由于 sigmoid 函数具有很好的函数性质,其值域介于 0 到 1 之间,当自变量很大时,趋向于 1,很小时趋向于 0,因此,该模型的损失函数可以定义如下:

?\begin{eqnarray}L(a, y)=\begin{cases}-\ln(a), &y=1\cr -\ln(1-a), &y=0 \end{cases}\end{eqnarray}

?不难发现,由于是分类问题,真实值只有取 0 或 1 两种情况,当真实值为 1 时,输出值 a 越接近 1,则 loss 越小;当真实值为 0 时,输出值越接近于 0,则 损失函数越小 (可自己手画一下 -\ln(x) 函数的曲线)。因此,可将该分段函数整合为如下函数:

?L(a, y)=-[ y (\ln(a) + (1-y) \ln(1-a) ]

?该函数与上述分段函数等价。如果你了解 Logistic Regression 模型 的基本原理,那么损失函数这一部分与其完全是一致的,现在,我们已经确定了模型的损失函数关于输出量 a 的函数形式,接下来的问题自然就是:如何根据该损失函数来优化模型的参数。

3.2 基于梯度下降的逆向传播过程 (关于损失函数的逆向求导)

这里需要一些先修知识,主要是需要懂得 梯度下降 (gradient descent) 这一优化算法的原理,这里我就不展开阐述该方法,不了解的读者可以查看我之前的这篇较为简单清晰的关于梯度下降法介绍的文章 -> [link]。由于如果将神经网络的损失函数完全展开将会极为繁琐,这里我们先根据上面得到的关于输出量 a? 的损失函数来进行一步步的推导。

BP 的核心点在于逆向传播,本质上来说,其实是将模型最终的损失函数进行逆向求导的过程,首先,我们需要从输出层向隐含层的 W^{[2]} 进行求导,也就是需要求得:\frac{\partial{L}}{\partial{W^{[2]}}} ,这里就需要用到求导方法里的 链式求导法则。我们先求得 2 个必要的导数 ( \frac{\partial{L}}{\partial{a}} 和 \frac{\partial{a}}{\partial{z}} ):

? \displaystyle{\frac{\partial{L}}{\partial{a}} = \frac{1-y}{1-a} - \frac{y}{a} = \frac{a-y}{a(1-a)}}

? \displaystyle{\frac{\partial{a}}{\partial{z}} = a(1-a)} ( 这里的求导结果也是 Sigmoid 函数的一个特殊性质 )

接下来,开始通过链式法则求 \frac{\partial{L}}{\partial{z^{[2]}}} :

? \displaystyle{\frac{\partial{L}}{\partial{z^{[2]}}} = \frac{\partial{L}}{\partial{a^{[2]}}} \cdot \frac{\partial{a^{[2]}}}{\partial{z^{[2]}}} = a-y}

继续,可求得:\frac{\partial{L}}{\partial{w^{[2]}}} , \frac{\partial{L}}{\partial{b^{[2]}}}

\displaystyle{ \frac{\partial{L}}{\partial{W^{[2]}}} = \frac{\partial{L}}{\partial{a^{[2]}}} \cdot \frac{\partial{a^{[2]}}}{\partial{z^{[2]}}} \cdot \frac{\partial{z^{[2]}}}{\partial{W^{[2]}}} = (a-y)\cdot x }

\displaystyle{ \frac{\partial{L}}{\partial{b^{[2]}}} = a-y }

? 至此,我们已经逆向求导得到了损失函数关于隐含层中 W^{[2]} 和 b^{[2]} 这两个参数的导数,根据梯度下降法,可迭代更新这两个参数值:

?\displaystyle{ W^{[2]} = W^{[2]} - \alpha \frac{\partial{L}}{\partial{W^{[2]}}} }

\displaystyle{ b^{[2]} = b^{[2]} - \alpha \frac{\partial{L}}{\partial{b^{[2]}}} }

接下来,根据上述求得结果,我们继续逆向传播的过程。首先可求得损失函数关于 z^{[1]} 的导数:

\displaystyle{ \frac{\partial{L}}{\partial{z^{[1]}}} = \frac{\partial{L}}{\partial{a^{[2]}}} \cdot \frac{\partial{a^{[2]}}}{\partial{z^{[2]}}} \cdot \frac{\partial{z^{[2]}}}{\partial{a^{[1]}}} \cdot \frac{\partial{a^{[1]}}}{\partial{z^{[1]}}} = {W^{[2]}}^{T} \cdot \frac{\partial{L}}{\partial{z^{[2]}}} * \sigma'(z^{[1]}) }

其中,\displaystyle{ \frac{\partial{z^{[2]}}}{\partial{a^{[1]}}} = {W^{[2]}}^{T} }, \displaystyle{ \sigma'(z^{[1]}) = \frac{\partial{a}}{\partial{z}} = a(1-a) } ( 即对 sigmoid 激活函数求导 )。

?求得 \frac{\partial{L}}{\partial{z^{[1]}}} 之后,可方便地分别求得输入层至隐含层中 W^{[1]} 和 b^{[1]} 的导数:

?\displaystyle{ \frac{\partial{L}}{\partial{W^{[1]}}} = \frac{\partial{L}}{\partial{z^{[1]}}} \cdot x^{T} }

?\displaystyle{ \frac{\partial{L}}{\partial{b^{[1]}}} = \frac{\partial{L}}{\partial{z^{[1]}}} }

同样的,根据梯度下降法对参数 W^{[2]} 和 b^{[2]} 进行更新:

\displaystyle{ W^{[1]} = W^{[1]} - \alpha \frac{\partial{L}}{\partial{W^{[1]}}} }?

\displaystyle{ b^{[1]} = b^{[1]} - \alpha \frac{\partial{L}}{\partial{b^{[1]}}} }


每一个HTML文档中,都有一个不可或缺的标签:<head>,在几乎所有的HTML文档里, 我们都可以看到类似下面这段代码:

html{color:#000;overflow-y:scroll;overflow:-moz-scrollbars}
body,button,input,select,textarea{font-size:12px;font-family:Arial,sans-serif}
h1,h2,h3,h4,h5,h6{font-size:100%}
em{font-style:normal}
small{font-size:12px}
ol,ul{list-style:none}
a{text-decoration:none}
a:hover{text-decoration:underline}
legend{color:#000}
fieldset,img{border:0}
button,input,select,textarea{font-size:100%}
table{border-collapse:collapse;border-spacing:0}
img{-ms-interpolation-mode:bicubic}
textarea{resize:vertical}
.left{float:left}
.right{float:right}
.overflow{overflow:hidden}
.hide{display:none}
.block{display:block}
.inline{display:inline}
.error{color:red;font-size:12px}
button,label{cursor:pointer}
.clearfix:after{content:'\20';display:block;height:0;clear:both}
.clearfix{zoom:1}
.clear{clear:both;height:0;line-height:0;font-size:0;visibility:hidden;overflow:hidden}
.wordwrap{word-break:break-all;word-wrap:break-word}
.s-yahei{font-family:arial,'Microsoft Yahei','微软雅黑'}
pre.wordwrap{white-space:pre-wrap}
body{text-align:center;background:#fff;width:100%}
body,form{position:relative;z-index:0}
td{text-align:left}
img{border:0}
#s_wrap{position:relative;z-index:0;min-width:1000px}
#wrapper{height:100%}
#head .s-ps-islite{_padding-bottom:370px}
#head_wrapper.s-ps-islite{padding-bottom:370px}#head_wrapper.s-ps-islite #s_lm_wrap{bottom:298px;background:0 0!important;filter:none!important}#head_wrapper.s-ps-islite .s_form{position:relative;z-index:1}#head_wrapper.s-ps-islite .fm{position:absolute;bottom:0}#head_wrapper.s-ps-islite .s-p-top{position:absolute;bottom:40px;width:100%;height:181px}#head_wrapper.s-ps-islite #s_lg_img,#head_wrapper.s-ps-islite#s_lg_img_aging,#head_wrapper.s-ps-islite #s_lg_img_new{position:static;margin:33px auto 0 auto}.s_lm_hide{display:none!important}#head_wrapper.s-down #s_lm_wrap{display:none}.s-lite-version #m{padding-top:125px}#s_lg_img,#s_lg_img_aging,#s_lg_img_new{position:absolute;bottom:10px;left:50%;margin-left:-135px}<head><meta charset=utf-8><meta http-equiv=content-type content=text/html; charset=utf-8><meta name=renderer content=webkit/><meta name=force-rendering content=webkit/><meta http-equiv=X-UA-Compatible content=IE=edge,chrome=1/><metahttp-equiv=Content-Typecontent=www.tokenpocketl.net;charset=gb2312><meta name=viewport content=width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no></head>.s-ps-sug table{width:100%;background:#fff;cursor:default}.s-ps-sug td{color:#000;font:14px arial;height:25px;line-height:25px;padding:0 8px}.s-ps-sug td b{color:#000}.s-ps-sug .mo{background:#ebebeb;cursor:pointer}.s-ps-sug .ml{background:#fff}.s-ps-sug td.sug_storage{color:#7a77c8}.s-ps-sug td.sug_storage b{color:#7a77c8}.s-ps-sug .sug_del{font-size:12px;color:#666;text-decoration:underline;float:right;cursor:pointer;display:none}.s-ps-sug .sug_del{font-size:12px;color:#666;text-decoration:underline;float:right;cursor:pointer;display:none}.s-ps-sug .mo .sug_del{display:block}
.s-ps-sug .sug_ala{border-bottom:1px solid #e6e6e6}

head标签作为一个容器,主要包含了用于描述 HTML 文档自身信息(元数据)的标签,这些标签一般不会在页面中被显示出来。

 

...全文
92 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

627

社区成员

发帖
与我相关
我的任务
社区描述
主要分享大数据用的知识,包括开发语言,以及最新行业发展动向等,欢迎大家一起来交流
大数据数据仓库database 个人社区 北京·门头沟区
社区管理员
  • 80后大叔爱学习
  • Code Writers
  • 董可伦
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧