数据蛙数据分析每周作业

神经网络

2019-01-26  本文已影响4人  怀柔小龙虾
文章结构

感知机和神经网络

感知机

鉴于感知机和神经网络的相似性,我们从感知机的角度来出发,先来看看感知机的结构是什么样的 感知机

我们对照感知机的公式
f ( x ) = \operatorname { sign } ( w \cdot x + b )

可以发现,从输入层到输出层,其实就是感知机的函数模型f ( x ) = \operatorname { sign } \left( w _ { 1 } x _ { 1 } + w _ { 2 } x _ { 2 } + x _ { 0 } \right),当然了,这个x_{0}f(x)的权重就是b,或者是直接把x_{0}看出是一个偏置项b(当然,x_{0}本身就是偏置项),因此我们就将感知机模型转化成了这样的网络结构,而这样的网络结构,其实也就是没有隐藏层的神经网络,待会我们再介绍,现在先来看一下感知机是如何解决逻辑问题的

因为sign是一个符号函数,它对于小于0的x都会输出为-1,并且我们做逻辑问题的输入和输出都是0 or 1,所以我们将其进行简单的修改,让其对小于0的x输出都为0,于是得到如下:\operatorname { sign } ( x ) = \left\{ \begin{array} { l l } { + 1 , } & { x \geqslant 0 } \\ { 0 , } & { x < 0 } \end{array} \right.

与此同时,对于x_{0}的问题,我们将x_{0}统一设定为+1,其传入下一层的权重为\theta,此时,我们便可以开始着手解决逻辑“与”,“或”,“非”的问题了

而对于异或问题,按书上介绍所说,感知机是不能解决以异或问题为代表的非线性可分问题的,所以我们先来看一下什么是异或问题

异或问题与多层感知机(MLP)

异或问题形式化成数学公式是x _ { 1 } \oplus x _ { 2 } = \left( \neg x _ { 1 } \wedge x _ { 2 } \right) \vee \left( x _ { 1 } \wedge \neg x _ { 2 } \right),即x_{1},x_{2}取值相同时为0,取值不同时为1。而异或问题实际上是一个非线性可分问题,即不存在一条曲面可以将数据集正确的划分,如下图所示:

来自西瓜书

我们可以看出,对于图上的4个点(0,0),(1,0),(0,1),(1,1),感知机是不可能生成一条直线可以将其正确划分的,但是我们是否可以发现,因为异或问题形式化公式是这样的x _ { 1 } \oplus x _ { 2 } = \left( \neg x _ { 1 } \wedge x _ { 2 } \right) \vee \left( x _ { 1 } \wedge \neg x _ { 2 } \right),我们完全可以将其拆成一个个子问题来解决,比如令A = \neg x _ { 1 } \wedge x _ { 2 } ,B= x _ { 1 } \wedge \neg x _ { 2 },此时x _ { 1 } \oplus x _ { 2 } = A \vee B,而A中,我们又可以将\neg x _ { 1 }视为一个整体,B中的\neg x _ { 2 }也是,所以我们可以发现,其实要实现异或问题,也就是多用几个感知机的问题,即先用感知机生成\neg x _ { 1 }\neg x _ { 2 },然后再用感知机生成\neg x _ { 1 } \wedge x _ { 2 }x _ { 1 } \wedge \neg x _ { 2 },最后再用感知机生成x _ { 1 } \oplus x _ { 2 },这样,我们就解决了异或问题,如下图所示

异或问题的实现

图上没有标出权重,不然就太乱了,大致是这样的过程,从输入层x_{0}=1,x_{1},x_{2}是输入值,每个值都会用一条有权重的边指向下一层的点,其中,权重值是不一样的,比如我们要实现\neg x _ { 1 },假设a_{11}就是\neg x _ { 1 },则从x_{0},x_{1},x_{2}射来的边的权重就分别为10,-20,0,以此类推,a_{12}就是x _ { 2 }a_{13}就是x _ { 3 }a_{14}就是\neg x _ { 2 },到了第二个隐含层,a_{21}就是\neg x _ { 1 } \wedge x _ { 2 }a_{22}就是x _ { 1 } \wedge \neg x _ { 2 },最终,我们会在输出层f(x)中得到异或问题的答案。(注意:这里的异或问题解决的方法比西瓜书上稍微复杂了一点,但我觉得这样的解决方案似乎更贴近之前感知机在逻辑“与”,“或”,“非”上的解决,所以就自己画了个图)

多层前馈神经网络

此时,我们应该能够直观地了解到多层感知机( Multi-layer Perceptron)的效果,而多层感知机其实也就是我们的神经网络,在西瓜书中它有一个确切的定义,即每层神经元与下一层神经元全互连,神经元之间不存在同层链接,也不存在跨层链接,这样的神经网络结构通常称为“多层前馈神经网络”。这里我们还必须补充一下相关概念的知识点:


基于反向传播(BP)算法的神经网络

基于以上,我们了解了多层感知机的由来和一点点神经网络的雏形,下边我们来介绍一下神经网络学习中著名的BP(error Backpagation)算法,即反向传播算法(也称误差逆传播算法)。另外,这个算法的工作是先根据一个样本点进行前向传播,寻找到该样本点的输出函数和预测值,然后进行反向传播,利用代价函数计算每一层的神经元的误差值,进而更新每个边的连接权和阈值,重新进行下一个样本点的前向传播,一直让目标朝着代价函数最小化的方向前进,直到找到迭代次数达到上限or代价函数降低的某一个设定的最小值以下结束

前向传播

在之前多层感知机的介绍中,我们了解到了多层感知机的工作机能,其大致过程就是从输入层开始,一层层地通过隐含层的计算,最终到达输出层输出预测值,而这个过程就是前向传播的整个流程,但是,以上我们讲解的都过于粗糙,所以,接下来我们需要从下图仔细讲解整个前向传播的过程:

反向传播

正如一开始所说,反向传播是利用代价函数计算每一层神经元的误差值,从而更新参数,那么我们就需要看一下,到底有哪些参数是需要更新的,并且是如何更新的。当然,我们必须先有损失函数才可以,这里我们先假设代价函数是用均方误差来计算
E _ { k } = \frac { 1 } { 2 } \sum _ { j = 1 } ^ { l } \left( \hat { y } _ { j } ^ { k } - y _ { j } ^ { k } \right) ^ { 2 }

整个神经网络有哪些参数需要更新:

现在,我们以隐层到输出层的连接权W _ { h j }为例,基于梯度下降的方法进行推导

类似地,我们可以得到其他参数的更新式\begin{aligned} \Delta \theta _ { j } & = - \eta g _ { j } \\ \Delta v _ { i h } & = \eta e _ { h } x _ { i } \\ \Delta \gamma _ { h } & = - \eta e _ { h } \end{aligned}

BP算法伪代码

摘自西瓜书

算法实现需要注意的要点

下面都是一些在算法实现需要注意的地方,目前自己都还没有做实验,所以都只是理论上理解下来的,所以整理在下面

参数的随机初始化

根据吴恩达老师在视频中所说,如果一开始我们假设所有参数为0,那么这样的参数初始化方法虽然对逻辑斯蒂回归行得通,但是对于神经网络来说是行不通的。因为如果我们令所有的初始参数为0(或全部参数都为一样的值),那么这就意味着第二层的所有神经元都会有相同的值,(注意:因为吴恩达在视频中没有讲到神经元的阈值,所以这里的所有参数指的是上一层到下一层的连接权,而神经元得到的相同值是指上一层经过连接权输入给下一层神经元的输入值),如下所示:

激活函数的选择

在整个神经网络中有两种激活函数,一种是用于隐含层上的激活函数,一种是用于输出层上的激活函数,这两层神经元的激活函数的选择是有一些些不同的,比如:

In regression, the output remains as f(x) ; therefore, output
activation function is just the identity function.

另外,我们在使用反向传播更新参数权重时,计算过程中会涉及到激活函数的求导计算,所以激活函数的选择将会影响收敛速度,并且,使用sigmoid函数的过程本身就会有一定的问题,问题如下:

为什么会出现上面的问题,我们先看一下sigmoid函数的图像

摘自网上的博客
左边是函数本身,右边是sigmoid函数的导函数图像,基于此,我们可以发现sigmoid导函数的最大值只有0.25,也就是说我们的神经网络如果隐含层比较多的话,那么梯度的数值会每经过一层下降四分之一,经过10层就只能为原先的1/1048576,这就是梯度消失的主要原因;对于为什么是函数输出不是zero-centered会造成梯度下降很慢,这个原因知乎上一些答主说是在斯坦福CS231n深度视觉识别课程视频第六集第9分钟左右,这里贴出链接,以后再补(传送门

至于为什么幂运算比较耗时,其实这是sigmoid函数自身的问题,可以很明显的看到

因此,我们的另一种选择是ReLU函数,也是目前来说用的最多的激活函数,详细的介绍都在下面的链接博客里,我们可以通过一张图来直观的感受以下速度 摘自网上资料

图中实线是ReLu函数,虚线是sigmoid函数,sigmoid函数需要35轮迭代才下降到0.25的程度,对于ReLU仅仅需要5轮,可见速度之快

损失函数的选择

根据sklearn官方文档所说,对于分类问题,我们使用的是交叉熵(Cross-Entropy)代价函数,对于回归问题,则是使用均方误差损失函数。在上面的参数更新的梯度推导中,我们假设的是均方误差作为损失函数,而事实上,如果是分类问题的话,使用交叉熵代价函数得到的效果会更好,梯度下降的速度会更快

至于为什么交叉熵要比均方误差更好,简单说来是因为交叉熵有一个特性,它会在模型误差较大的时候梯度下降得更快,误差较小的时候梯度下降得更慢,而具体的我们可以参考如下的博客,写得极为详细! (传送门)

实际使用中的其他一些小技巧

关于数据预处理方面,因为多层感知机对特征放缩(feature scaling)是十分敏感的,因此我们在进行数据预处理时必须先对数据集进行特征放缩,如下所示 摘自sklearn官方文档

关于收敛速度方面,我们以上所讨论的激活函数的选择和损失函数的选择,它们的目的都是为了加快收敛速度,但以上都是基于梯度下降的前提来去进行的,其实根据资料所说,我们甚至还有更多的选择,比如sklearn官方文档上给出了一些在实际应用中的建议,如下

参考资料:
【1】《机器学习》周志华
【2】《机器学习视频》吴恩达
【3】Neural network models (supervised)
【4】 神经网络中的激活函数的作用和选择
【5】交叉熵代价函数(cross-entropy cost function)
【6】理解交叉熵损失(Cross-Entropy)
【7】交叉熵代价函数(作用及公式推导)

上一篇 下一篇

猜你喜欢

热点阅读