原文地址可以查看更多信息
本文主要参考于:Multilayer Perceptron
python源代码(github下载 CSDN免费下载)
本文主要介绍含有单隐层的MLP的建模及实现。建议在阅读本博文之前,先看一下LR的实现。因为LR是简化版的MLP。LR不含有单隐层,则其输入层直接连接到输出层。从何处可以看出LR是输入层直接连接输出层?借用上一博文的公式: P(Y=i|x,W,b)=softmaxi(Wx+b) 。其中, x 是输入层, softmax 是激活函数, P 就是输出层了。我们将其化简并转换为一般神经网络表达式: f(x)=g(Wx+b) ,其中 g 就是激活函数, f(x) 就是输出层了。可见输入层经过激活函数得到的结果就是输出了。很简单吧。
那么MLP的模型公式和LR又有什么不同呢?下面来看一下MLP的模型建立。
一、模型
f(x)=G(W(2)(S(W(1)x+b(1)))+b(2))
其中, b(1),b(2) 分别是三层之间(输入层与隐层、隐层与输出层之间)的偏置向量; W(1),W(2) 分别是三层之间的权值矩阵;而 S,G 是分别是三层之间的激活函数。
对于连接单隐层的的表达式 h(x)=Φ(x)=S(W(1)x+b(1)) ,其激活函数 S 常用的有 tanh(a)=(ea−e−a)(ea+e−a) 和 sigmoid(a)=1(1+e−a) 函数。在这里,我们将使用 tanh 作为激活函数,因为它通常能更快的达到训练目标。
对于连接输出层的表达式 o(x)=G(W(2)h(x)+b(2)) ,我们应该不是很陌生,就是在篇头或上一篇博文中讲到的LR的模型表达式,在那里面其激活函数用的是 softmax ,它可以用来多分类,那么在这里, G 也是采用 softmax 多分类器来分类。
接下来就是训练模型获取最佳参数,我们这里仍然采用MSGD(批量随机梯度下降法)方法。这里我们需要学习的参数为 θ={W(2),b(2),W(1),b(1)} 。同样,对于用链式法则求梯度 ∂ℓ/∂θ ,我们就用theano已经实现的T.grad()方法。
讲到这里小伙伴们或许有疑问了:没有代价函数怎么求梯度啊?也就是 ∂ℓ/∂θ 中的 ℓ 是什么啊?很简单,就是和LR中的代价函数基本是一样的(在后边还会讲到cost代价函数为什么是基本一样而不是一样),因为结构都是一样的,我们同样要处理的是对于手写数字MNIST的识别。最小化方法也是采用负对数似然函数,所以这里的 ℓ 一清二楚了吧,还不清楚的,可以翻看LR的(二、定义代价函数)。
二、MLP代码实现
我们主要关注实现含单隐层的MLP(只要单隐层的实现了,多隐层的就是多实例化几个隐层而已)。MLP的实现包括3部分:输入层、隐层、输出层。
第一层:输入层就是我们直接的输入数据 x ,将其直接输入到第二层;
第二层:隐层的功能是用 tanh 函数处理第一层的输入数据,并将结果作为第三层的输入数据。因此需要我们用代码实现,即根据LR实现一个隐层类;
第三层:输出层的功能是将第二层的输入数据经过 softmax 函数,然后进行分类输出。因此和LR是一样的,所以这里直接调用上一篇定义的LR类-LogisticRegression类。
先提前讲一下隐层权值的初始化问题。在LR类中,权值矩阵初始化为0。然而在隐层中就不能再初始化为0了,而是依据激活函数从symmetric interval(对称间隔)中均匀采样。这样初始化是为了确保在训练早期,每一个神经元都可以向后传播(upward)激活信息,向前传播(backward )梯度数据。也就是说在这一层用到了梯度反向传播,那么权值矩阵就不能初始化为0,不知道解释的如何?原文:(This initialization ensures that, early in training, each neuron operates in a regime of its activation function where information can easily be propagated both upward (activations flowing from inputs to outputs) and backward (gradients flowing from outputs to inputs))。
那么具体的均匀采样范围是什么呢?
对于函数 tanh 来说,interval为: [−6fanin+fanout−−−−−−−−−√,6fanin+fanout−−−−−−−−−√] ,其中 fanin 是第 (i−1) 层的单元个数; fanout 是第 i 层的单元个数。如果对应到本实验,第 (i−1) 层就是输入层,输入层输入数据是样本数据,因此其单元个数是样本数据的长度;第 i 层就是隐层,其单元个数是需要实例化时传入的。具体的看下方代码。
对于函数 sigmoid 来说,interval为: [−46fanin+fanout−−−−−−−−−√,46fanin+fanout−−−−−−−−−√] 。
可以从文献Xavier10中获取以上范围的来因。是一个大牛写的论文,如果能看懂也很牛。
部分代码:
根据HiddenLayer类计算完结果后,我们相当于计算了公式 h(x)=Φ(x)=S(W(1)x+b(1)) ,下面我们需要计算公式 o(x)=G(W(2)h(x)+b(2)) ,然后根据前文的分析,第二个公式就是我们之前实现的LR。因此,我们只需要将隐层的结果作为LR的输入,即可实现MLP的功能。下面,我们来写一下MLP的代码:
现在就来解释一下篇头中提到的代价函数是基本一样的原因。
首先来说一下规则化。当我们训练模型试图让它在面对新的输入样本时产生更好的结果(就是有更好的泛化能力),我们往往会采用梯度下降方法。而本实验中用到的MSGD方法却没有考虑到一个问题,那就是过拟合现象。一旦出现过拟合,那么在面对新的样本时很难有好的结果。为了控制过拟合,一个比较有效的方法就是规格化。就是给每个参数 θ (对应于 W 中的每一个元素)增加一个惩罚项,如果某个参数的系数非常大那么 θ 就会接近于0了。那么什么是L1和L2规格化呢?下面看一个公式,假设我们的代价函数是:
原则上,当代价函数中增添了规则化项之后会使得网络拟合函数时更加平滑。所以,理论上,最小化 NLL 与 R(θ) 的和等价于在拟合训练数据和找到一般性的解(分类超平面)之间找到最佳的权衡。根据Occam’s razor规则,最小化加入规则化项的公式会使得我们更容易找到拟合训练数据的解(分类超平面)。
实际上一个模型有一个简单的超平面解并不意味着它的泛化能力很好。实验证明,含有这种规格化项的神经网络的泛化能力更好,尤其是在比较小的数据集上。
下面来看一下带有规则化项的代价函数代码如何写:
最后就是参数求梯度以及参数更新,基本和LR的区别不大。训练模型函数、测试模型函数、验证模型函数和LR相同。至此,代码实现基本告一段落,可以下载全部代码运行测试。其他的代码注释不多,因为应该比较简单。只有一个mlp.py文件。下载来,直接python mlp.py即可运行。如果有不懂的地方可以留言,也可以翻看上一篇LR的实现。本文所介绍的mlp去对mnist分类官方给出的一个结果是:
我也正在跑代码,因为还是比较耗时的。这个网址里边记录了目前为止所做过的实验以及结果。有兴趣的可以看一下。最好的误差能达到0.23%了,天呢,接近100%的识别率了。
参考目录:
1. 官方教程
2. Stanford机器学习—第三讲. 逻辑回归和过拟合问题的解决 logistic Regression & Regularization
3. 深度学习(DL)与卷积神经网络(CNN)学习笔记随笔-03-基于Python的LeNet5之LR