注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】
自然语言处理系列六十二
神经网络算法》MLP多层感知机算法
神经网络,尤其是深度神经网络(Deep Neural Networks,DNN)在过去的数年已经在图像分类、语音识别、自然语言处理中取得了突破性的进展。在实践中的应用已经证明了它可以作为对于一种十分有效的技术手段应用在大数据相关领域中。深度神经网络通过众多的简单线性变换层次性的进行非线性变换对于数据中的复杂关系能够很好的进行拟合,即对数据特征进行的深层次的挖掘。因此作为一种技术手段,深度神经网络对于任何领域都是适用的。神经网络的算法也有好多种,从最早的MLP多层感知机算法,到之后的CNN卷积神经网络、RNN循环神经网络、LSTM长短期记忆神经网络,以及在此基础神经网络算法之上衍生的混合神经网络Seq2Seq端到端神经网络、GAN生成对抗网络、深度强化学习DQN等,可以做很多有趣的应用。下面我们就分别讲一下各个算法。
18.3.1 MLP多层感知机算法
MLP我们在上一章讲Spark的时候已经介绍过,原理都是一样,这次我们拿TensorFlow来实现MLP,解决分类的应用场景。
1. TensorFlow多层感知机实现原理
说道分类问题,我们可以用Softmax回归来做。Softmax回归可以算是多分类问题logistic回归,它和神经网络的最大区别是没有隐含层。理论上只要隐含节点足够多,即时只有一个隐含层的神经网络也可以拟合任意函数,同时隐含层越多,越容易拟合复杂结构。为了拟合复杂函数需要的隐含节点的数目,基本上随着隐含层的数量增多呈指数下降的趋势,也就是说层数越多,神经网络所需要的隐含节点可以越少。层数越深,概念越抽象,需要背诵的知识点就越少。在实际应用中,深层神经网络会遇到许多困难,如过拟合、参数调试、梯度弥散等。
过拟合是机器学习中的一个常见问题,是指模型预测准确率在训练集上升高,但是在测试集上的准确率反而下降,这通常意味着模型的泛化能力不好,过度拟合了训练集。针对这个问题,Hinton教授团队提出了Dropout的解决办法,在使用CNN训练图像数据时效果尤其有效,其大体思路是在训练时将神经网络某一层的输出节点数据随机丢失一部分。这种做法实质上等于创造出了许多新的随机样本,通过增大样本量、减少特征数量来防止过拟合。
参数调试问题尤其是SGD(StochasticGradient Descent)的参数,对SGD设置不同的学习率learning rate,最后得到的结果可能差异巨大。神经网络的优化通常不是一个简单的凸优化问题,它处处充满了局部最优。有理论表示,神经网络可能有很多个局部最优解都可以达到比较好的分类效果,而全局最优很可能造成过拟合。对SGD,我们希望一开始学习率大一些,加速收敛,在训练的后期又希望学习率小一些,这样可以低速进入一个局部最优解。不同的机器学习问题的学习率设置也需要针对性的调试,像Adagrad、Adam、Adadelta等自适应的方法可以减轻调试参数的负担。对于这些优化算法,通常我们使用其默认的参数设置就可以得到比较好的效果。
梯度弥散(Gradient Vanishment)是另一个影响深层神经网络训练的问题,在ReLU激活函数出现之前,神经网络训练是使用Sigmoid作为激活函数。非线性的Sigmoid函数在信号的特征空间映射上,对中央区的信号增益较大,对两侧区的信号增益小。当神经网络层数较多时,Sigmoid函数在反向传播中梯度值会逐渐减小,到达前面几层的梯度值就变得非常小了,在神经网络训练的时候,前面几层的神经网络参数几乎得不到训练更新。指导ReLU,y = max(0, x),的出现才比较完美的解决了梯度弥散的问题。信号在超过某个阈值时,神经元才会进入兴奋和激活的状态,否则会处于抑制状态。ReLU可以很好的反向传递梯度,经过多层的梯度反向传播,梯度依旧不会大幅减小,因此非常适合深层神经网络的训练。ReLU对比于Sigmoid的主要特点有以下几点:单侧抑制、相对宽阔的兴奋边界、稀疏激活性。目前,ReLU及其变种EIU、PReLU、RReLU已经成为最主流的激活函数。实践中大部分情况下(包括MLP、CNN、RNN)将隐含层的激活函数从Sigmoid替换为ReLU都可以带来训练速度和模型准确率的提升。当然神经网络的输出层一般都是Sigmoid函数,因为它最接近概率输出分布。
作为最典型的神经网络,多层感知机(MLP)结构简单且规则,并且在隐层设计的足够完善时,可以拟合任意连续函数,利用TensorFlow来实现MLP更加形象,使得使用者对要搭建的神经网络的结构有一个更加清醒的认识,本文就将对TensorFlow搭建MLP模型的方法进行一个简单的介绍,并实现MNIST数据集的分类任务。
2. TensorFlow手写数字识别分类任务MNIST分类
作为数据挖掘工作中处理的最多的任务,分类任务占据了机器学习的大半江山,而一个网络结构设计良好(即隐层层数和每个隐层神经元个数选择恰当)的多层感知机在分类任务上也有着非常优越的性能,依然以MNIST手写数字数据集作为演示,上一篇中我们利用一层输入层+softmax搭建的分类器在MNIST数据集的测试集上达到93%的精度,下面我们使用加上一层隐层的网络,以及一些tricks来看看能够提升多少精度;
网络结构:
这里我们搭建的多层前馈网络由784个输入层神经元——200个隐层神经元——10个输出层神经元组成,而为了减少梯度弥散现象,我们设置relu(非线性映射函数)为隐层的激活函数,如图7.2所示。
图7.2 激活函数
这种激活函数更接近生物神经元的工作机制,即在达到阈值之前持续抑制,在超越阈值之后开始兴奋;而对输出层,因为对数据做了one_hot处理,所以依然使用softmax进行处理;
Dropout:
过拟合是机器学习尤其是神经网络任务中经常发生的问题,即我们的学习器将训练集的独特性质当作全部数据集的普遍性质,使得学习器在训练集上的精度非常高,但在测试集上的精度却非常低(这里假设训练集与测试集数据分布一致),而除了随机梯度下降的一系列方法外(如上一篇中我们提到的在每轮训练中使用全体训练集中一个小尺寸的训练批来进行本轮的参数调整),我们可以使用类似的思想,将神经网络某一层的输出节点数据随机丢弃一部分,即令这部分被随机选中的节点输出值令为0,这样做等价于创造出很多新样本,通过增大样本量,减少特征数量来防止过拟合,dropout也算是一种bagging方法,可以将每次丢弃节点输出视为对特征的一次采样,相当于我们训练了一个ensemble的神经网络模型,对每个样本都做特征采样,并构成一个融合的神经网络
学习效率:
因为神经网络的训练通常不是一个凸优化问题,它充满了很多局部最优,因此我们通常不会采用标准的梯度下降算法,而是采用一些有更大可能跳出局部最优的算法,常用的如SGD,而SGD本身也不稳定,其结果也会在最优解附近波动,且设置不同的学习效率可能会导致我们的网络落入截然不同的局部最优之中,对于SGD,我们希望开始训练时学习率大一些,以加速收敛的过程,而后期学习率低一些,以更稳定地落入局部最优解,因此常使用Adagrad、Adam等自适应的优化方法,可以在其默认参数上取得较好的效果;
下面。就结合上述策略,利用Tensorflow搭建我们的多层感知机来对MNIST手写数字数据集进行训练:
先使用朴素的风格来搭建网络,首先还是照例从Tensorflow自带的数据集中提取出mnist数据集,代码如下所示。
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
'''导入MNIST手写数据'''
mnist = input_data.read_data_sets('MNIST_data/', one_hot = True)
接着使用交互环境下会话的方式,将生成的第一个会话作为默认会话:
'''注册默认的session,之后的运算都会在这个session中进行'''
sess = tf.InteractiveSession()
接着初始化输入层与隐层间的784x300个权值、隐层神经元的300个bias、隐层与输出层之间的300x10个权值、输出层的10个bias,其中为了避免隐层的relu激活时陷入0梯度的情况,对输入层和隐层间的权值初始化为均值为0,标准差为0.2的正态分布随机数,对其他参数初始化为0,代码如下所示。
'''定义输入层神经元个数'''
in_units = 784
'''定义隐层神经元个数'''
h1_units = 300
'''为输入层与隐层神经元之间的连接权重初始化持久的正态分布随机数,这里权重为784乘300,300是隐层的尺寸'''
W1 = tf.Variable(tf.truncated_normal([in_units, h1_units],mean=0,stddev=0.2))
'''为隐层初始化bias,尺寸为300'''
b1 = tf.Variable(tf.zeros([h1_units]))
'''初始化隐层与输出层间的权重,尺寸为300X10'''
W2 = tf.Variable(tf.zeros([h1_units, 10]))
'''初始化输出层的bias'''
b2 = tf.Variable(tf.zeros([10]))
接着我们定义自变量、隐层神经元dropout中的保留比例keep_prob的输入部件:
'''定义自变量的输入部件,尺寸为任意行X784列'''
x = tf.placeholder(tf.float32, [None, in_units])
'''为dropout中的保留比例设置输入部件'''
keep_prob = tf.placeholder(tf.float32)
接着定义隐层relu激活部分的计算部件、隐层dropout部分的操作部件、输出层softmax的计算部件,代码如下所示。
'''定义隐层求解部件'''
hidden1 = tf.nn.relu(tf.matmul(x, W1) + b1)
'''定义隐层dropout操作部件'''
hidden1_drop = tf.nn.dropout(hidden1, keep_prob)
'''定义输出层softmax计算部件'''
y = tf.nn.softmax(tf.matmul(hidden1_drop, W2) + b2)
还有样本真实分类标签的输入部件以及loss_function部分的计算组件:
'''定义训练label的输入部件'''
y_ = tf.placeholder(tf.float32, [None, 10])
'''定义均方误差计算部件,这里注意要压成1维'''
loss_function = tf.reduce_mean(tf.reduce_sum((y_ - y)**2, reduction_indices=[1]))
这样我们的网络结构和计算部分全部搭建完成,接下来至关重要的一步就是定义优化器的组件,它会完成自动求导调整参数的工作,这里我们选择自适应的随机梯度下降算法Adagrad作为优化器,学习率尽量设置小一些,否则可能会导致网络的测试精度维持在一个很低的水平不变即在最优解附近来回震荡却难以接近最优解,代码如下所示。
'''定义优化器组件,这里采用AdagradOptimizer作为优化算法,这是种变种的随机梯度下降算法'''
train_step = tf.train.AdagradOptimizer(0.18).minimize(loss_function)
接下来就到了正式的训练过程了,我们激活当前会话中所有计算部件,并定义训练步数为15000步,每一轮迭代选择一个批量为100的训练批来进行训练,dropout的keep_prob设置为0.76,并在每50轮训练完成后将测试集输入到当前的网络中计算预测精度,注意在正式预测时dropout的keep_prob应设置为1.0,即不进行特征的丢弃,代码如下所示。
'''激活当前session中的全部部件'''
tf.global_variables_initializer().run()
'''开始迭代训练过程,最大迭代次数为3001次'''
for i in range(15000):
'''为每一轮训练选择一个尺寸为100的随机训练批'''
batch_xs, batch_ys = mnist.train.next_batch(100)
'''将当前轮迭代选择的训练批作为输入数据输入train_step中进行训练'''
train_step.run({x: batch_xs, y_: batch_ys, keep_prob:0.76})
'''每500轮打印一次当前网络在测试集上的训练结果'''
if i % 50 == 0:
print('第',i,'轮迭代后:')
'''构造bool型变量用于判断所有测试样本与其真是类别的匹配情况'''
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
'''将bool型变量转换为float型并计算均值'''
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
'''激活accuracy计算组件并传入mnist的测试集自变量、标签及dropout保留比率,这里因为是预测所以设置为全部保留'''
print(accuracy.eval({x: mnist.test.images,
y_: mnist.test.labels,
keep_prob: 1.0}))
经过全部迭代后,我们的多层感知机在测试集上达到了0.9802的精度。事实上在训练到10000轮左右的时候我们的多层感知机就已经到达这个精度了,说明此时的网络已经稳定在当前的最优解中,后面的训练过程只是在这个最优解附近微弱的震荡而已,所以实际上可以设置更小的迭代轮数。
MLP属于性对浅层的神经网络,下面我们讲一下深层的CNN卷积神经网络。
CNN卷积神经网络算法
CNN卷积神经网络算法可参见
《自然语言处理原理与实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】书籍。
更多的技术交流和探讨也欢迎加我个人微信chenjinglei66。
总结
此文章有对应的配套新书教材和视频:
【配套新书教材】
《自然语言处理原理与实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】
新书特色:本书从自然语言处理基础开始,逐步深入各种NLP热点前沿技术,使用了Java和Python两门语言精心编排了大量代码实例,契合公司实际工作场景技能,侧重实战。
全书共分为19章,详细讲解中文分词、词性标注、命名实体识别、依存句法分析、语义角色标注、文本相似度算法、语义相似度计算、词频-逆文档频率(TF-IDF)、条件随机场、新词发现与短语提取、搜索引擎Solr Cloud和Elasticsearch、Word2vec词向量模型、文本分类、文本聚类、关键词提取和文本摘要、自然语言模型(Language Model)、分布式深度学习实战等内容,同时配套完整实战项目,例如对话机器人实战、搜索引擎项目实战、推荐算法系统实战。
本书理论联系实践,深入浅出,知识点全面,通过阅读本书,读者不仅可以理解自然语言处理的知识,还能通过实战项目案例更好地将理论融入实际工作中。
《分布式机器学习实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】
新书特色:深入浅出,逐步讲解分布式机器学习的框架及应用配套个性化推荐算法系统、人脸识别、对话机器人等实战项目。
【配套视频】
推荐系统/智能问答/人脸识别实战 视频教程【陈敬雷】
视频特色:把目前互联网热门、前沿的项目实战汇聚一堂,通过真实的项目实战课程,让你快速成为算法总监、架构师、技术负责人!包含了推荐系统、智能问答、人脸识别等前沿的精品课程,下面分别介绍各个实战项目:
1、推荐算法系统实战
听完此课,可以实现一个完整的推荐系统!下面我们就从推荐系统的整体架构以及各个子系统的实现给大家深度解密来自一线大型互联网公司重量级的实战产品项目!
2、智能问答/对话机器人实战
由浅入深的给大家详细讲解对话机器人项目的原理以及代码实现、并在公司服务器上演示如何实际操作和部署的全过程!
3、人脸识别实战
从人脸识别原理、人脸识别应用场景、人脸检测与对齐、人脸识别比对、人脸年龄识别、人脸性别识别几个方向,从理论到源码实战、再到服务器操作给大家深度讲解!
自然语言处理NLP原理与实战 视频教程【陈敬雷】
视频特色:《自然语言处理NLP原理与实战》包含了互联网公司前沿的热门算法的核心原理,以及源码级别的应用操作实战,直接讲解自然语言处理的核心精髓部分,自然语言处理从业者或者转行自然语言处理者必听视频!
人工智能《分布式机器学习实战》 视频教程【陈敬雷】
视频特色:视频核心内容有互联网公司大数据和人工智能、大数据算法系统架构、大数据基础、Python编程、Java编程、Scala编程、Docker容器、Mahout分布式机器学习平台、Spark分布式机器学习平台、分布式深度学习框架和神经网络算法、自然语言处理算法、工业级完整系统实战(推荐算法系统实战、人脸识别实战、对话机器人实战)。
上一篇:自然语言处理系列六十一》分布式深度学习实战》TensorFlow深度学习框架
下一篇:自然语言处理系列六十三》神经网络算法》LSTM长短期记忆神经网络