20161019 工作很忙,更新极其缓慢
引言
本系列是本人第一次在简书写东西,想将手头上正在学的神经网络归纳整理,尽量详细地介绍神经网络的结构、计算公式与C语言实现。文中内容基本参考消化了计算机的潜意识的博文,文中图片基本来自他的博文和Ng老师的课件,所用的符号和上下标与Ng老师的一致,在此感谢。
神经网络结构
说到神经网络,对神经网络有概念的人第一个冒出来的肯定是下面这幅图,这是最基本的3层神经网络,图中圆圈标识神经元,有输入层、1层隐藏层、输出层。输入层有3个输入单元,隐藏层有4个单元,输出层有2个单元,中间交错的是传递路线。输入层一般表示每个样本(sample)的特征(features),特征是根据不同的样本我们自己提取的(例如一段声音波形,我提取了最大值、总能量等作为特征,根据这些特征,我们可以把这段声音归类,而归类的依据就是输出层的输出结果)。
神经网络中有一些设计规则需要记住:
- 设计一个神经网络时,输入层与输出层的节点数往往是固定的,中间层则可以自由指定;
- 神经网络结构图中的拓扑与箭头代表着预测过程时数据的流向,跟训练时的数据流有一定的区别;
- 结构图里的关键不是圆圈(代表“神经元”),而是连接线(代表“神经元”之间的连接)。每个连接线对应一个不同的权重(其值称为权值),这是需要训练得到的。
神经元模型
神经元模型包括输入、输出和计算模块,如下图所示是经典的神经元模型,神经元首先对输入信号进行加权求和,这里的权值在神经网络计算中是很重要,一个神经网络算法就是不断地调整每一层的权值矩阵来使得整个网络的预测结果达到最佳。求和之后经过一个非线性函数后输出,这个非线性函数在很多地方被称为激活函数。
我们用Ng老师的符号来表示上图,得到下面图所示,输出z是在输入和权值的线性加权和叠加了一个函数g的值。在MP模型里,函数g是sgn函数,也就是取符号函数。这个函数当输入大于0时,输出1,否则输出0。很多地方为了简化,会把sum和函数g放在同一个圈内作为一个节点,当看到一个节点时,一定要知道节点内部是有这些计算过程的。
1943年发布的MP模型,虽然简单,但已经建立了神经网络大厦的地基。但是,MP模型中,权重的值都是预先设置的,因此不能学习。
1949年心理学家Hebb提出了Hebb学习率,认为人脑神经细胞的突触(也就是连接)上的强度上可以变化的。于是计算科学家们开始考虑用调整权值的方法来让机器学习。这为后面的学习算法奠定了基础。
单层神经网络
1958年,计算科学家Rosenblatt提出了由两层神经元组成的神经网络。他给它起了一个名字--“感知器”(Perceptron)。 在“感知器”中,有两个层次。分别是输入层和输出层。输入层里的“输入单元”只负责传输数据,不做计算。输出层里的“输出单元”则需要对前面一层的输入进行计算。
我们把需要计算的层次称之为“计算层”,并把拥有一个计算层的网络称之为“单层神经网络”。有一些文献会按照网络拥有的层数来命名,例如把“感知器”称为两层神经网络。但在本文里,我们根据计算层的数量来命名。
如下图是一个两个输出的单层神经网络,这里我们的预测目标不再是一个值,而是两个值,一般用向量表示。那么单层网络模型的计算公式如下图所示。(注意权值w的下标,如W1,2表示这一层第1个神经元对前一层第二个神经元的权重)
我们仔细看输出的计算公式,会发现这两个公式就是线性代数方程组。因此可以用矩阵乘法来表达这两个公式。例如,输入的变量是[a1,a2,a3]T,用向量a来表示。方程的左边是[z1,z2]T,用向量z来表示,权重则是矩阵W(2行3列的矩阵),于是,输出公式可以改写成:
g(W a) = z
这个公式就是神经网络中从前一层计算后一层的矩阵运算。
两层神经网络
介绍
Minsky说过单层神经网络无法解决异或问题。但是当增加一个计算层以后,两层神经网络不仅可以解决异或问题,而且具有非常好的非线性分类效果。不过两层神经网络的计算是一个问题,没有一个较好的解法。
1986年,Rumelhar和Hinton等人提出了反向传播(Backpropagation,BP)算法,解决了两层神经网络所需要的复杂计算量问题,从而带动了业界使用两层神经网络研究的热潮。目前,大量的教授神经网络的教材,都是重点介绍两层(带一个隐藏层)神经网络的内容。
结构
两层神经网络除了包含一个输入层,一个输出层以外,还增加了一个中间层(即开头我们给出的模型)。此时,中间层和输出层都是计算层。现在,我们的权值矩阵增加到了两个,Ng老师用上标来区分不同层次之间的变量。
例如ax(i)代表第i层的第x个节点。a1(2),a2(2)的计算公式如下图所示:
计算最终输出z的方式是利用了中间层的a1(2),a2(2)和第二个权值矩阵W(2)计算得到的,如下图。
假设我们的预测目标是一个向量,那么与前面类似,只需要在“输出层”再增加节点即可。我们使用向量和矩阵来表示层次中的变量。a(1),a(2),z是网络中传输的向量数据。W(1)和W(2)是网络的矩阵参数。
计算公式为:
g(W(1) a(1)) = a(2)
g(W(2) a(2)) = z
至今为止,我们对神经网络的结构图的讨论中都没有提到偏置节点(bias unit)。事实上,这些节点是默认存在的。它本质上是一个只含有存储功能,且存储值永远为1的单元。在神经网络的每个层次中,除了输出层以外,都会含有这样一个偏置单元。正如线性回归模型与逻辑回归模型中的一样。偏置单元与后一层的所有节点都有连接,我们设这些参数值为向量b,称之为偏置。如下图。
有些神经网络的结构图中会把偏置节点明显画出来,有些不会。一般情况下,我们都不会明确画出偏置节点。 在考虑了偏置以后的一个神经网络的矩阵运算如下:
g(W(1) a(1) + b(1)) = a(2)
g(W(2) a(2) + b(2)) = z
需要说明的是,在两层神经网络中,我们不再使用sgn函数作为函数g,而是使用平滑函数sigmoid作为函数g。我们把函数g也称作激活函数(active function)。
事实上,神经网络的本质就是通过参数与激活函数来拟合特征与目标之间的真实函数关系。初学者可能认为画神经网络的结构图是为了在程序中实现这些圆圈与线,但在一个神经网络的程序中,既没有“线”这个对象,也没有“单元”这个对象。实现一个神经网络最需要的是线性代数库。
分类效果
与单层神经网络不同。理论证明,两层神经网络可以无限逼近任意连续函数。这是什么意思呢?也就是说,面对复杂的非线性分类任务,两层(带一个隐藏层)神经网络可以分类的很好。下面就是一个例子(此两图来自colah的博客),红色的线与蓝色的线代表数据。而红色区域和蓝色区域代表由神经网络划开的区域,两者的分界线就是决策分界。
可以看到,这个两层神经网络的决策分界是非常平滑的曲线,而且分类的很好。有趣的是,前面已经学到过,单层网络只能做线性分类任务。而两层神经网络中的后一层也是线性分类层,应该只能做线性分类任务。为什么两个线性分类任务结合就可以做非线性分类任务?我们可以把输出层的决策分界单独拿出来看一下。就是下图。
可以看到,输出层的决策分界仍然是直线。关键就是,从输入层到隐藏层时,数据发生了空间变换。也就是说,两层神经网络中,隐藏层对原始的数据进行了一个空间变换,使其可以被线性分类,然后输出层的决策分界划出了一个线性分类分界线,对其进行分类。
这样就导出了两层神经网络可以做非线性分类的关键--隐藏层。联想到我们一开始推导出的矩阵公式,我们知道,矩阵和向量相乘,本质上就是对向量的坐标空间进行一个变换。因此,隐藏层的参数矩阵的作用就是使得数据的原始坐标空间从线性不可分,转换成了线性可分。
两层神经网络通过两层的线性模型模拟了数据内真实的非线性函数。因此,多层的神经网络的本质就是复杂函数拟合。
神经网络设计与训练
在设计一个神经网络时,输入层的节点数需要与特征的维度匹配,输出层的节点数要与目标的维度匹配。而中间层的节点数,却是由设计者指定的。因此,“自由”把握在设计者的手中。但是,节点数设置的多少,却会影响到整个模型的效果。如何决定这个自由层的节点数呢?目前业界没有完善的理论来指导这个决策。一般是根据经验来设置。较好的方法就是预先设定几个可选值,通过切换这几个值来看整个模型的预测效果,选择效果最好的值作为最终选择。这种方法又叫做Grid Search(明明就是瞎猜法)。
机器学习的训练,机器学习模型训练的目的,就是使得参数尽可能的与真实的模型逼近。具体做法是这样的。首先给所有参数赋上随机值。我们使用这些随机生成的参数值,来预测训练数据中的样本。样本的预测目标为h(x)
,真实目标为y。那么,定义一个值cost,计算公式如下:
cost = (h(x) - y)2
我们的目标就是使对所有训练数据的损失和(cost)尽可能的小。如果将先前的神经网络预测的矩阵公式带入到h(x)中(z=h(x)),那么我们可以把cost写为关于参数(parameter)的函数,这个函数称之为损失函数(cost function)。下面的问题就是求:如何优化参数,能够让损失函数的值最小。cost function一般用![](http://chart.googleapis.com/chart?cht=tx&chl= J(\theta))来表示。
在线性回归中,cost function可以表示为
但是在logistic回归中,cost function 就不一样了。
此时这个问题就被转化为一个优化问题,一个常用的方法就是高等数学中的求导,但是这里的问题是由于参数不止一个,求导后计算导数等于0的运算量很大,所以一般来说解决这个优化问题使用的是梯度下降算法。梯度下降算法每次计算参数(权重矩阵w中的每一个值)在当前的梯度(偏导数),然后让参数向着梯度的反方向前进一段距离,不断重复,直到梯度接近0。一般这个时候,所有的参数恰好达到使损失函数达到最低值的状态。还有一种在多元回归中用的标准公式法。都能够计算出使cost function达到最小值的参数矩阵。
在神经网络模型中,由于结构复杂,每次计算梯度的代价很大。因此还需要使用反向传播算法。反向传播算法是利用了神经网络的结构进行的计算。不一次计算所有参数的梯度,而是从后往前。首先计算输出层的梯度,然后是第二个参数矩阵的梯度,接着是中间层的梯度,再然后是第一个参数矩阵的梯度,最后是输入层的梯度。计算结束以后,所要的两个参数矩阵的梯度就都有了。