利用Theano理解深度学习——Multilayer Perceptron
一、多层感知机MLP
1、MLP概述
对于含有单个隐含层的多层感知机(single-hidden-layer Multi-Layer Perceptron, MLP),可以将其看成是一个特殊的Logistic回归分类器,这个特殊的Logistic回归分类器首先通过一个非线性变换Φ(non-linear transformation)对样本的输入进行非线性变换,然后将变换后的值作为Logistic回归的输入。非线性变换的目的是将输入的样本映射到一个空间,在该空间中,这些样本是线性可分的。这个中间层我们称之为隐含层(a hidden layer)。
A single hidden layer is sufficient to make MLPs a universal approximator.
2、MLP模型
对于含有单隐层的MLP模型(也可以称为人工神经网络,ANN)可以由下图表示:
在上图中,对于含有一个隐含层的MLP可以表示为:
其中,D指的是输入向量x的大小,L表示的是输出向量f(x)的大小,输出向量f(x)可以表示为:
其中,b(1)和b(2)是偏置,W(1)和W(2)是权重矩阵,s和G是激活函数。
隐含层的输出为h(x)=Φ(x)=s(b(1)+W(1)x)。W(1)∈RD×Dh是输入层到隐含层的权重向量,W(1)中的每一列代表从输入单元到第i个隐含单元的权重。对于激活函数s,可以使用tanh或者sigmoid函数,其中,tanh函数为:
sigmoid函数为:
选择激活函数tanh有时具有更快的训练速度和训练精度
输出向量为o(x)=G(b(2)+W(2)h(x))。(在下面的程序中G选择的是sigmoid函数,类似于Logistic Regression的多类别分类)。
3、MLP模型的训练
为了训练处MLP模型中的所有参数,可以使用带mini-batch的随机梯度下降法。需要学习的参数为:
在这里,要获得梯度∂l∂θ可以使用backpropagation algorithm,这是一个链式求导的法则。
二、Tips和Tricks
在代码中存在着很多的超参数,有些参数的选择是不能通过梯度下降法得到的。严格来讲,这些超参数的最优解是不可解的。首先,我们不能单独的优化每一个超参数。其次,我们不能直接使用梯度下降法,因为有些超参数是离散的,有些超参数是连续的。最后,这是非凸优化问题,找到一个局部最优解需要花费很大的功夫。
在过去的25年中,研究者们已经设计出大量的经验法则用于在一个神经网络中选择超参数。
1、非线性变换
两个最常见的非线性函数是sigmoid函数和tanh函数。其中,tanh函数的形式为:
sigmoid函数的形式为:
对于非线性变换的选择,通常是选择关于原点对称的非线性变换。这是因为具有这样特性的变换能够为下一层产生零均值的输入。[Nonlinearities that are symmetric around the origin are preferred because they tend to produce zero-mean inputs to the next layer (which is a desirable property)].
从经验上看,tanh函数具有更好的收敛性。
2、权重向量的初始化
在初始化阶段,我们希望权重在原点的周围,而且尽可能的小,这样,激活函数对其进行操作就像是线性函数,此处的梯度也是最大的。
At initialization we want the weights to be small enough around the origin so that the activation function operates in its linear regime, where gradients are the largest. Other desirable properties, especially for deep networks, are to conserve variance of the activation as well as variance of back-propagated gradients from layer to layer.
对于tanh激活函数,权重的激活方法为在区间:
上以均匀分布的方式产生随机数。而对于sigmoid激活函数,则是在区间:
上以均匀分布的方式产生随机数。其中,fanin是i−1层节点的个数,而fanout是i层节点的个数。
3、学习率
有很多文章在讲如何选择一个好的学习率。最简单的办法是选择一个固定的学习率,即常数。经验法则:尝试一些对数空间的数,如10−2,10−3,⋯和通过网格搜索(grid search)不断缩小对数值以获得最小的验证误差。
随着代数不断减小学习率是一个很好的想法,一个简单的做法是:
其中,u0是初始的学习率(可以通过上述的网格搜索的办法取得),d称为减少常数(decrease constant),控制着学习率的下降速度,通常是一个很小的正数,如10−3或者更小。t是epoch/stage。
4、隐含层节点的个数
这个超参数在很大程度上是取决于数据集的。笼统的说,对于越复杂的数据分布,神经网络需要越强的能力去对这批数据建模,因此,需要越多的隐含层节点个数。
除非我们使用一些正则化的策略,例如early stopping或者L1/L2惩罚,隐含层节点个数与泛化能力在图上表现为U字形的。
5、正则化参数
对于L1或者L2正则的参数λ有一些经验值,如10−2,10−3,⋯。
三、基于Theano的MLP实现解析
在利用Theano实现单隐层的MLP的过程中,主要分为如下几个步骤:
- 导入数据集
- 建立模型
- 训练模型
- 利用模型进行预测
接下来,对每个部分的代码进行解析。
1、导入数据集
导入数据集的代码部分与利用Theano理解深度学习——Logistic Regression中一致,就不再细说。
2、建立模型
在实现的过程中,可以将单隐层的MLP想像成LR模型中增加了一个隐含层,故在实现的过程中使用到了LR中的LogisticRegression
类。此外,还增加了一个MLP
类和HiddenLayer
类。其中,MLP
类是整个MLP算法的模型,具体的代码如下:
class MLP(object):
"""含单隐层的多层感知机类
多层感知机是一个前馈人工神经网络模型,该模型有一个或者多个隐含层单元和非线性的激活函数。
隐层层单元在“HiddenLayer”类中定义
顶层是一个softmax层,在“LogisticRegression”类中定义
"""
def __init__(self, rng, input, n_in, n_hidden, n_out):
"""初始化参数
:type rng: numpy.random.RandomState
:param rng: 随机数生成器,用于随机生成权重
:type input: theano.tensor.TensorType
:param input: 符号,用于表示输入
:type n_in: int
:param n_in: 输入单元的个数
:type n_hidden: int
:param n_hidden: 隐含层单元的个数
:type n_out: int
:param n_out: 输出层单元的个数
"""
# 输入层到单个隐含层的初始化
self.hiddenLayer = HiddenLayer(
rng=rng,#随机数生成器
input=input,#输入
n_in=n_in,#输入层的节点个数
n_out=n_hidden,#输出层的节点个数
activation=T.tanh#激活函数
)
# 隐含层到输出层的初始化
self.logRegressionLayer = LogisticRegression(
input=self.hiddenLayer.output,
n_in=n_hidden,
n_out=n_out
)
# L1正则
self.L1 = (
abs(self.hiddenLayer.W).sum()
+ abs(self.logRegressionLayer.W).sum()
)
# L2正则
self.L2_sqr = (
(self.hiddenLayer.W ** 2).sum()
+ (self.logRegressionLayer.W ** 2).sum()
)
# 损失函数的定义
self.negative_log_likelihood = (
self.logRegressionLayer.negative_log_likelihood
)
# 用于计算validation和testing的错误率
self.errors = self.logRegressionLayer.errors
# 声明所有的参数
self.params = self.hiddenLayer.params + self.logRegressionLayer.params
#声明输入
self.input = input
MLP
类中主要的部分包括声明输入层到隐含层以及隐含层到输出层的结构,正则化的方法以及损失函数的定义和模型中的主要参数。在MLP
类中使用到了HiddenLayer
类和LogisticRegression
类。其中HiddenLayer
类用于定义隐含层的结构以及基本操作,LogisticRegression
类用于定义输出层的结构以及基本操作,LogisticRegression
类在利用Theano理解深度学习——Logistic Regression已经解析过,下面是HiddenLayer
类的代码:
class HiddenLayer(object):
def __init__(self, rng, input, n_in, n_out, W=None, b=None, activation=T.tanh):
"""在MLP中,典型的隐含层中的节点是全连接的,使用的是sigmoid激活函数,权重矩阵W的大小为(n_in, n_out),
偏置向量b的大小为(n_out,)。在这里激活函数使用的是tanh
:type rng: numpy.random.RandomState
:param rng: 用于生成权重的随机数生成器
:type input: theano.tensor.dmatrix
:param input: 符号,输入
:type n_in: int
:param n_in: 输入层的节点个数
:type n_out: int
:param n_out: 输出层的节点个数
:type activation: theano.Op or function
:param activation: 激活函数的类型
"""
self.input = input
if W is None:
'''对于权重矩阵W中的元素的初始化,若使用的激活函数是tanh,则使用均匀分布在区间[sqrt(-6./(n_in+n_hidden)),sqrt(6./(n_in+n_hidden))]上生成。
权重矩阵的初始化与选择的激活函数是相关联的。若使用sigmoid激活函数,则生成的数是tanh激活函数的4倍。
'''
W_values = numpy.asarray(
rng.uniform(low=-numpy.sqrt(6. / (n_in + n_out)), high=numpy.sqrt(6. / (n_in + n_out)), size=(n_in, n_out)),
dtype=theano.config.floatX
)
if activation == theano.tensor.nnet.sigmoid:
W_values *= 4
W = theano.shared(value=W_values, name='W', borrow=True)
if b is None:
'''对于偏置向量b的初始化,使用的是全部初始化为0
'''
b_values = numpy.zeros((n_out,), dtype=theano.config.floatX)
b = theano.shared(value=b_values, name='b', borrow=True)
self.W = W
self.b = b
lin_output = T.dot(input, self.W) + self.b#线性的输出
#判断是选择线性的激活函数还是非线性的激活函数
self.output = (
lin_output if activation is None
else activation(lin_output)
)
# 声明模型中的参数
self.params = [self.W, self.b]
在定义好上述的结构之后就可以建立模型,详细的代码如下:
#2、建立模型
print '... building the model'
# 分配全局的符号
index = T.lscalar() # 用于指示batch
x = T.matrix('x')
y = T.ivector('y')
rng = numpy.random.RandomState(1234)#随机数生成器
# 构造函数
classifier = MLP(
rng=rng,#随机数生成器
input=x,#输入
n_in=28 * 28,#输入的节点个数
n_hidden=n_hidden,#隐含层节点个数
n_out=10#输出的节点个数
)
# 在训练的时候,所有的损失是损失函数+L1正则+L2正则
cost = (
classifier.negative_log_likelihood(y)
+ L1_reg * classifier.L1
+ L2_reg * classifier.L2_sqr
)
#构造验证模型的函数
validate_model = theano.function(
inputs=[index],#输入为batch的索引
outputs=classifier.errors(y),#输出为每个batch上的错误率
givens={#x:样本,y:标签
x: valid_set_x[index * batch_size:(index + 1) * batch_size],
y: valid_set_y[index * batch_size:(index + 1) * batch_size]
}
)
# 对每一个参数求偏导数
gparams = [T.grad(cost, param) for param in classifier.params]
# 更新规则
updates = [
(param, param - learning_rate * gparam)
for param, gparam in zip(classifier.params, gparams)
]
# 训练模型的函数
train_model = theano.function(
inputs=[index],#输入为batch的索引
outputs=cost,#输出为所有的损失
updates=updates,#更新参数的规则
givens={
x: train_set_x[index * batch_size: (index + 1) * batch_size],
y: train_set_y[index * batch_size: (index + 1) * batch_size]
}
)
1、正则化方法
正则化方法是防止模型过拟合(overfitting)的重要的方法,模型过拟合是指训练出来的模型在训练集上表现的很好,但是在未知的数据集上表现较差。在前面的LR的模型训练中,我们没有考虑到正则项,只是使用到了early-stopping策略。在这里我们考虑L1和L2正则。
L1和L2正则是指在损失函数的基础上增加一个额外的正则项,这个正则项的目的是为了对模型中的参数进行惩罚。若损失函数如下所示:
则加入正则项的损失函数就变成:
其中,R(θ)=∥θ∥pp
这里
2、权重W的初始化
在权重的初始化过程中,我们希望权重的初始值在原点的附近。一些经验的做法是对于激活函数为tanh的情况下,权重的激活方法为在区间:
上以均匀分布的方式产生随机数。而对于sigmoid激活函数,则是在区间:
上以均匀分布的方式产生随机数。其中,fanin是i−1层节点的个数,而fanout是i层节点的个数。
3、激活函数的选择
在本程序中,主要牵涉到的激活函数有两个,一个是tanh函数,另一个是sigmoid函数。其中,tanh函数的形式为:
sigmoid函数的形式为:
3、训练模型
训练模型的具体代码如下:
#3、训练模型
print '... training'
# early-stopping参数
patience = 10000
patience_increase = 2
improvement_threshold = 0.995
validation_frequency = min(n_train_batches, patience / 2)
best_validation_loss = numpy.inf
best_iter = 0
start_time = timeit.default_timer()
epoch = 0
done_looping = False
while (epoch < n_epochs) and (not done_looping):
epoch = epoch + 1
for minibatch_index in xrange(n_train_batches):
minibatch_avg_cost = train_model(minibatch_index)
# iteration number
iter = (epoch - 1) * n_train_batches + minibatch_index
if (iter + 1) % validation_frequency == 0:
# compute zero-one loss on validation set
validation_losses = [validate_model(i) for i
in xrange(n_valid_batches)]
this_validation_loss = numpy.mean(validation_losses)
print(
'epoch %i, minibatch %i/%i, validation error %f %%' %
(
epoch,
minibatch_index + 1,
n_train_batches,
this_validation_loss * 100.
)
)
if this_validation_loss < best_validation_loss:
if (
this_validation_loss < best_validation_loss *
improvement_threshold
):
patience = max(patience, iter * patience_increase)
best_validation_loss = this_validation_loss
best_iter = iter
if patience <= iter:
done_looping = True
break
end_time = timeit.default_timer()
print(('Optimization complete. Best validation score of %f %% '
'obtained at iteration %i') %
(best_validation_loss * 100., best_iter + 1))
print >> sys.stderr, ('The code for file ' +
os.path.split(__file__)[1] +
' ran for %.2fm' % ((end_time - start_time) / 60.))
4、利用模型进行预测
这部分的代码主要是将训练好的模型应用在新的测试数据集上,具体的代码如下:
'''增加了一段预测的代码
'''
# 4、利用模型进行预测
predict_model = theano.function(
inputs=[classifier.input],
outputs=classifier.logRegressionLayer.y_pred)
test_set_x = test_set_x.get_value()
predicted_values = predict_model(test_set_x[:10])#进行预测
print ("Predicted values for the first 10 examples in test set:")
print predicted_values
参考文献
利用Theano理解深度学习——Multilayer Perceptron的更多相关文章
- 从极大似然估计的角度理解深度学习中loss函数
从极大似然估计的角度理解深度学习中loss函数 为了理解这一概念,首先回顾下最大似然估计的概念: 最大似然估计常用于利用已知的样本结果,反推最有可能导致这一结果产生的参数值,往往模型结果已经确定,用于 ...
- (转) 基于Theano的深度学习(Deep Learning)框架Keras学习随笔-01-FAQ
特别棒的一篇文章,仍不住转一下,留着以后需要时阅读 基于Theano的深度学习(Deep Learning)框架Keras学习随笔-01-FAQ
- 从Image Caption Generation理解深度学习
0. 前面的话 建丁让我写一篇深度学习相关小文章,目标读者是国内的开发者.刚接到这个任务时我是颇为忐忑的,写文章要讲究厚积薄发,如果“水之积也不厚”,“则其负大舟也无力”.因为我自知水平很有限,又不是 ...
- 基于Theano的深度学习框架keras及配合SVM训练模型
https://blog.csdn.net/a819825294/article/details/51334397 1.介绍 Keras是基于Theano的一个深度学习框架,它的设计参考了Torch, ...
- 如何正确理解深度学习(Deep Learning)的概念
现在深度学习在机器学习领域是一个很热的概念,不过经过各种媒体的转载播报,这个概念也逐渐变得有些神话的感觉:例如,人们可能认为,深度学习是一种能够模拟出人脑的神经结构的机器学习方式,从而能够让计算机具有 ...
- win7上安装theano keras深度学习框架
近期在学习深度学习,需要在本机上安装keras框架,好上手.上网查了一些资料,弄了几天今天终于完全搞好了.本次是使用GPU进行加速,使用cpu处理的请查看之前的随笔keras在win7下环境搭建 本机 ...
- 【深度学习】perceptron(感知机)
目录 1.感知机的描述 2.感知机解决简单逻辑电路,与门的问题. 2.多层感应机,解决异或门 个人学习笔记,有兴趣的朋友可参考. 1.感知机的描述 感知机(perceptron)由美国学者Frank ...
- 【论文笔记】如何理解深度学习中的End to End
End to end:指的是输入原始数据,输出的是最后结果,应用在特征学习融入算法,无需单独处理. end-to-end(端对端)的方法,一端输入我的原始数据,一端输出我想得到的结果.只关心输入和输出 ...
- 基于Windows,Python,Theano的深度学习框架Keras的配置
1.安装Anaconda 面向科学计算的Python IDE--Anaconda 2.打开Anaconda Prompt 3.安装gcc环境 (1)conda update conda (2)cond ...
随机推荐
- 洛谷P3396 哈希冲突(分块)
传送门 题解在此,讲的蛮清楚的->这里 我就贴个代码 //minamoto #include<iostream> #include<cstdio> #include< ...
- 浅析localstorage、sessionstorage
原文链接:http://caibaojian.com/localstorage-sessionstorage.html 简介 html5 中的 web Storage 包括了两种存储方式:sessio ...
- 阿里、腾讯热门面试题:聊聊Unix与Java的IO模型?(含详细解析)
众所周知 如果去百度.腾讯等一线大厂面试,一定会深入考候选人的基础技术功底,其中尤为关键和重视的就是IO相关的技术和知识. 而要搞明白IO相关的概念,首先就得弄清楚同步与异步,阻塞与非阻塞到底是什么意 ...
- java中pojo对象首字母大写导致无法赋值问题
命名规范(文末附有java命名规范)中指出,属性变量命名应采用驼峰命名的方式,即首字母小写,其他单词首字母大写: 但有时候我们对接三方的接口时,想要封装实体类来接受,但是发现接收到的参数的变量首字母是 ...
- IdentityServer4 学习笔记[1]-客户端授权
前言 本文内容来自IdentityServer4官网,官网有详细的介绍,并且有源码Demo 官网源码例子传送门 建立授权服务端 我们暂时不配置Https,选择空模板建立项目,项目建立后, 为了查看de ...
- Could not find iPhone X simulator
Could not find iPhone X simulator Error: Could not find iPhone X simulator at resolve (/Users/zhouen ...
- 关于php命名空间的理解
以phpmailer这个类库为例,composer自动加载好该类库,有用的文件都放在src这个目录下 这些文件的命名空间都是这个:namespace PHPMailer\PHPMailer; 如果我们 ...
- 【ACM】一种排序
一种排序 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 现在有很多长方形,每一个长方形都有一个编号,这个编号可以重复:还知道这个长方形的宽和长,编号.长.宽都是整数 ...
- Codeforces Round #172 (Div. 2) D. Maximum Xor Secondary 单调栈应用
http://codeforces.com/contest/281/problem/D 要求找出一个区间,使得区间内第一大的数和第二大的数异或值最大. 首先维护一个单调递减的栈,对于每个新元素a[i] ...
- Java文件与io——复制文件和转换流
字节流与字符流的区别 在所有的流操作里,字节永远是最基础的.任何基于字节的操作都是正确的.无论是文本文件还是二进制的文件. 如果确认流里面只有可打印的字符,包括英文的和各种国家的文字,也包括中文,那么 ...