1. 算法
多层感知机(MLP)和后向传播算法(BP)是神经网络的入门算法,是复杂的神经网络的基本构架,也就是神经层搭建与反向传播计算系数这两步。很多人不把MLP当神经网络,也有很多人把BP误当做一种神经网络,事实上BP只是一种求解神经网络的算法,而不是一种单独的神经网络类别。
1.1 感知机与激活函数
其实在介绍支持向量机和逻辑回归的时候,我们就已经接触过感知机的概念了。
感知机接收多个输入信号,输出一个信号,如下面两图所示,输入信号是,输出信号是。
这个式子我们很熟悉,不就是上一节中逻辑回归的判别式吗?
因此,一个逻辑回归的函数可以作为我们神经网络的一个基础单元,我们将其称为激活函数,这是很重要的一个概念,事实上,神经网络的感知机其实就是一个线性方程再套上一个激活(励)函数,是组成神经网络的基本单元。(这句话很重要)
如果不用激励函数(其实相当于激励函数是f(x) = x),在这种情况下你每一层节点的输入都是上层输出的线性函数,很容易验证,无论你神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果相当,这种情况就是最原始的感知机(Perceptron)了,那么网络的逼近能力就相当有限。正因为上面的原因,我们决定引入非线性函数作为激励函数,这样深层神经网络表达能力就更加强大(不再是输入的线性组合,而是几乎可以逼近任意函数)。
常用的激活函数有:
1.Sigmoid函数
其中,, Sigmoid的几何图像如下:
特点:
它能够把输入的连续实值变换为0和1之间的输出,特别的,如果是非常大的负数,那么输出就是0;如果是非常大的正数,输出就是1.
缺点:
sigmoid函数曾经被使用的很多,不过近年来,用它的人越来越少了。主要是因为它固有的一些 缺点。
缺点1:在深度神经网络中梯度反向传递时导致梯度爆炸和梯度消失,其中梯度爆炸发生的概率非常小,而梯度消失发生的概率比较大。如果我们初始化神经网络的权值为 [ 0 , 1 ] [0,1][0,1] 之间的随机值,由反向传播算法的数学推导可知,梯度从后向前传播时,每传递一层梯度值都会减小为原来的0.25倍,如果神经网络隐层特别多,那么梯度在穿过多层后将变得非常小接近于0,即出现梯度消失现象;当网络权值初始化为 ( 1 , + ∞ ) (1,+∞)(1,+∞) 区间内的值,则会出现梯度爆炸情况。
缺点2:Sigmoid 的 output 不是0均值(即zero-centered)。这是不可取的,因为这会导致后一层的神经元将得到上一层输出的非0均值的信号作为输入。 产生的一个结果就是:如,那么对w求局部梯度则都为正,这样在反向传播的过程中w要么都往正方向更新,要么都往负方向更新,导致有一种捆绑的效果,使得收敛缓慢。 当然了,如果按batch去训练,那么那个batch可能得到不同的信号,所以这个问题还是可以缓解一下的。因此,非0均值这个问题虽然会产生一些不好的影响,不过跟上面提到的梯度消失问题相比还是要好很多的。
缺点3:其解析式中含有幂运算,计算机求解时相对来讲比较耗时。对于规模比较大的深度网络,这会较大地增加训练时间。
2.tanh函数
tanh读作Hyperbolic Tangent,它解决了Sigmoid函数的不是zero-centered输出问题,然而,梯度消失(gradient vanishing)的问题和幂运算的问题仍然存在。
3. Relu函数
ReLU函数其实就是一个取最大值函数,注意这并不是全区间可导的,但是我们可以取sub-gradient,如上图所示。ReLU虽然简单,但却是近几年的重要成果,有以下几大优点:
1) 解决了gradient vanishing问题 (在正区间)
2)计算速度非常快,只需要判断输入是否大于0
3)收敛速度远快于sigmoid和tanh
ReLU也有几个需要特别注意的问题:
1)ReLU的输出不是zero-centered
2)Dead ReLU Problem,指的是某些神经元可能永远不会被激活,导致相应的参数永远不能被更新。有两个主要原因可能导致这种情况产生: (1) 非常不幸的参数初始化,这种情况比较少见 (2) learning rate太高导致在训练过程中参数更新太大,不幸使网络进入这种状态。解决方法是可以采用Xavier初始化方法,以及避免将learning rate设置太大或使用adagrad等自动调节learning rate的算法。
尽管存在这两个问题,ReLU目前仍是最常用的activation function,在搭建人工神经网络的时候推荐优先尝试!
4.Leaky ReLU函数(PReLU)
可由方向传播算法学出来。理论上来讲,Leaky ReLU有ReLU的所有优点,外加不会有Dead ReLU问题,但是在实际操作当中,并没有完全证明Leaky ReLU总是好于ReLU。
5. ELU (Exponential Linear Units) 函数
ELU也是为解决ReLU存在的问题而提出,显然,ELU有ReLU的基本所有优点,以及:
不会有Dead ReLU问题
输出的均值接近0,zero-centered
它的一个小问题在于计算量稍大。类似于Leaky ReLU,理论上虽然好于ReLU,但在实际使用中目前并没有好的证据ELU总是优于ReLU。
6. MaxOut函数
这个函数可以参考论文《maxout networks》,Maxout是深度学习网络中的一层网络,就像池化层、卷积层一样等,我们可以把maxout 看成是网络的激活函数层,我们假设网络某一层的输入特征向量为:X=(x1,x2,……xd),也就是我们输入是d个神经元。Maxout隐藏层每个神经元的计算公式如下:
上面的公式就是maxout隐藏层神经元i的计算公式。其中,k就是maxout层所需要的参数了,由我们人为设定大小。就像dropout一样,也有自己的参数p(每个神经元dropout概率),maxout的参数是k。公式中Z的计算公式为:
权重w是一个大小为(d,m,k)三维矩阵,b是一个大小为(m,k)的二维矩阵,这两个就是我们需要学习的参数。如果我们设定参数k=1,那么这个时候,网络就类似于以前我们所学普通的MLP网络。
我们可以这么理解,本来传统的MLP算法在第i层到第i+1层,参数只有一组,然而现在我们不这么干了,我们在这一层同时训练n组的w、b参数,然后选择激活值Z最大的作为下一层神经元的激活值,这个max(z)函数即充当了激活函数。
应用中如何选择合适的激活函数?
这个问题目前没有确定的方法,凭一些经验吧。
1)深度学习往往需要大量时间来处理大量数据,模型的收敛速度是尤为重要的。所以,总体上来讲,训练深度学习网络尽量使用zero-centered数据 (可以经过数据预处理实现) 和zero-centered输出。所以要尽量选择输出具有zero-centered特点的激活函数以加快模型的收敛速度。
2)如果使用 ReLU,那么一定要小心设置 learning rate,而且要注意不要让网络出现很多 “dead” 神经元,如果这个问题不好解决,那么可以试试 Leaky ReLU、PReLU 或者 Maxout.
3)最好不要用 sigmoid,你可以试试 tanh,不过可以预期它的效果会比不上 ReLU 和 Maxout.
1.2 多层感知机
一个感知机可以解决一个线性可分的二分类问题,但实际二分类问题中可能会出现如下线性不可分的情况,这种情况下是没有办法用一条线将两类分割开来的:
我们能否将感知机组合起来,使其解决如上的问题呢?
答案当然是可以的,请仔细体会下面这个例子。
我们构建了如下的一个网络,第0层输入了,第1层有两个感知机构成,第一个感知机我们将他设置为并门,第二个感知机我们设置为或门+非门。并、或、非这三个都是线性操作。
并门s1,输入为x1,x2,输出为w1:
或门+非门s2,输入为x1,x2,输出为w2:
第2层只有一个感知机,我们将其设置为或门,输入为上一层的w1,w2,输出为y:
用图像来表示,图中的蓝色代表y=1, 黄色代表y=0.可以看出在第二层的两个感知机都只能分对一部分,有错分的情况,但在最后一层或门输出的时候得到了分类正确的情况。:
纯的感知机当然拟合能力很弱,但是当多个神经元组合起来组成神经网络就会变得非常得强大,可以深入学习到数据当中的非线性关系。非线性关系的学习一直是机器学习时代很令人头疼的问题,通过神经网络这一点得到了很好的解决。神经网络之所以厉害比普通的机器学习模型效果更好,一方面除了它的参数更多拟合能力更强之外,很重要的一点就是它能够很好地学习数据当中的非线性关系。
TensorFlow官网提供了一个叫做playground的网页应用,可以让我们自己直观地感受神经网络的组成以及它的训练过程。比如刚才的异或问题,我们就可以自己来设置神经网络的层数以及每一层包含的神经元的数量。并且还可以设置神经元的激活函数,可以非常直观地体会神经网络的训练过程以及各种参数的作用。链接在这:TensorFlow
正如上面的例子所演示的那样,一般的神经网络结构包含:输入层(最左边),隐藏层(中间两层),和输出层(最右边)
光搭建出一个神经网络的结构是远远不够的,前文说过,神经网络的感知机其实就是一个线性方程再套上一个激活函数,因此我们需要求解出每一个线性方程的系数,求解过程分为前向传播和后向传播两个过程。
1.3 前向传播
前向传播其实很好理解,就是从输入层输入训练样本,经过神经网络的一层层的计算得到最后的输出结果。但问题是,显然神经网络各个层的感知机的权重系数是未知值,因此我们先将这些系数设定一个初始值,然后使用前向传播计算所得的结果去不断的更新各个神经网络层的权重系数。这个更新的过程就是后向传播。
接下来我们拿一个简单的三层神经网络举例,来解释前向传播的过程。
由前文所叙述的那样,神经网络的感知机其实就是一个线性方程再套上一个激活(励)函数,是组成神经网络的基本单元。每个神经元由两部分组成,第一部分(e)是输入值和权重系数乘积的和,第二部分(f(e))是一个激活函数(非线性函数)的输出, y=f(e)即为某个神经元的输出,如下,请仔细体会这幅图的含义:
下面是前向传播过程,只要拿着笔纸跟着图示仔细算一遍就能很好的理解这个过程了:
到这里为止,神经网络的前向传播已经完成,最后输出的y就是本次前向传播神经网络计算出来的结果(预测结果),但这个预测结果不一定是正确的,要和真实的标签(z)相比较,计算预测结果和真实标签的误差(δ),如下:
1.4 链式求导法则与后向传播
先简单介绍一下链式求导法则,这是后向传播更新权重的依据,理解起来并不复杂,但是计算比较麻烦。
实际上你可以把整个神经网络看做是一个嵌套的复合函数,你可以尝试写出这个复合函数的表达式。我们接下来使用梯度下降的方法来对这个函数的各个参数进行求解,梯度下降法是我们的老朋友,使用梯度下降方法,首先就要对函数进行求导,复合函数的求导法则相信大家高中就已经掌握了。反向传播的过程在传播什么?传播的是误差,需要从输出层往输出层一层一层将误差进行传播,求解出每一层的误差,更新参数值。
接下来我们还是使用上面的例子为大家演示误差反向传播的过程,请拿出纸笔跟着算一下:
下面开始利用反向传播的误差,计算各个神经元(权重)的导数,开始反向传播修改权重:
到这里,一个完整的前向传播与后向传播的过程就完成了。
神经网络的计算理解过程只是比较麻烦,它所用到的数学知识甚至还没有SVM晦涩难懂,这里还举出了第二个例子供大家加深理解,请拿出纸笔跟着算一遍:
下图的神经网络有一个输入层,二维的特征空间,一个隐含层,有两个神经元,一个输出层,两个神经元。与上面的神经网络略有差别的是每一层都加入了一个常数项,但计算方法没有改变。
首先看前向传播的过程:
接下来是后向传播。我们先来求最简单的,求误差E对w5的导数。首先明确这是一个“链式求导”过程,要求误差E对w5的导数,需要先求误差E对out o1的导数,再求out o1对net o1的导数,最后再求net o1对w5的导数,经过这个链式法则,我们就可以求出误差E对w5的导数(偏导),如下图所示:
导数(梯度)已经计算出来了,下面就是反向传播与参数更新过程:
如果要想求误差E对w1的导数,误差E对w1的求导路径不止一条,这会稍微复杂一点,但换汤不换药,计算过程如下所示:
通过以上的两个具体的计算例子,希望你已经能掌握多层感知机与后向传播算法的计算了。