深度学习在自然语言处理中的应用之情感分析

730 阅读7分钟
原文链接: mp.weixin.qq.com

1. 引言

随着深度学习理论研究的逐渐深入,自然语言处理(Natural Language Processing, NLP)领域作为人工智能研究的重要方向之一也出现了很多深度学习模型。相对于传统的机器学习方法,深度学习的优点主要在于训练效果好,以及不需要复杂的特征提取过程。同时,在深度学习框架如TensorFlow和PyTorch的帮助下,搭建和部署深度学习模型的难度也相对较小。因此,一些重要的深度学习结构如卷积神经网络(Convolution Neural Network, CNN)和循环神经网络(Recurrent Neural Network, RNN)在近年来被广泛应用于自然语言处理的研究中并且取得了很好的效果。这里通过用TensorFlow(API r1.2)来实现一个简单的文本情感分析(积极/消极二分类),作为一个深度学习在自然语言处理过程中的应用实例。

2. 数据来源与预处理

首先我们来介绍一下数据集,这里使用的是Pang 和Lee的情感极性数据集(Sentiment polarity datasets v 2.0),总共由2000条电影的评论组成,正负各一半。首先我们要做的事情是给其中的词汇进行编号。为了能够捕捉到对文本情感分类至关重要的高频词汇的信息,我们先对词汇进行一个词频统计。首先我们需要把句子分割成单词(Tokenize),然后把词汇按照词频从高到低排序,然后对高频词进行编号。如下表所示:

表1 词汇频率统计表

单词 频率排名 出现次数
the 1 76536
a 2 38095
and 3 35581
of 4 34126
... ... ...
also 71 1967
get 72 1945
other 73 1931
character 74 1928
... ... ...

在这个表里面排在比较前面的单词作为英语中经常出现的单词(比如说表1中的1~4),对于我们分析句子情感并没有很大的影响,被称作停用词(Stop Words),我们可以忽略掉这些单词(可以利用Python的自然语言处理包NLTK中的nltk.corpus.stopwords的英文停用词表来过滤这些单词),而有意义的一些词(比如表1中的71~74),可以保留下来并且编号为1,2,3...来作为我们的词汇表。这里我们截取前20000条高频词作为我们的词汇表。有了这样一个词汇表,我们的句子就可以被转换成一个数字代表的列表了。比如说(hello world)这个句子用刚才的词汇表就可以编号成(10057,145)。接下来我们需要对文本进行展宽。因为TensorFlow采用的是静态计算图,所以每个批次的训练数据需要有相同的长度,因此我们需要把句子展宽到一定的长度。这里取句子的平均长度作为展宽的长度,长的句子会被截断,短的句子在后边补上0(这个就是特殊用途的0),这样我们整个数据集的长度就完成了。

3. 模型的建立

接下来就是深度学习模型的构建了。首先我们来介绍一下这里使用的深度学习神经网络模型:循环神经网络模型。这个模型的主要思想来源于人类阅读的过程:人们一般是按照顺序一个个单词顺序读写一句话的,最后领会整个句子的意思。而循环网络每个单元接受上次的隐含层输出和外界的输入作为当前步的总输入,并且根据这些输入形成新的输出和新的隐含层输出。整个过程如下图(图1)所示,因为整个隐含层是循环的,所以称之为循环神经网络。

图1 神经网络单元模型 

下一步就是整个深度学习网络的结构了。这个网络的结构如图2所示。这里我们采用双向的循环神经网络结构,其中蓝色的是正向的层,红色的是反向的层,词向量按照顺序和逆序分别输入到这两个层当中,最后用输出的两个状态向量来预测句子的情感。绿色的层是词嵌入层(Word Embedding Layer),其作用是把一个高维的词向量压缩到一个比较低的维度上面,以我们的模型为例是把单词从20000维的独热编码(One-hot Encoding,也就是用一个二进制的向量来代表单词)压缩到一个128维的32位浮点数组成的向量上。在循环网络中,每个网络的单元采用的是长短时记忆单元(Long Short-Term Memory, LSTM)构成。最后的输出采用两个状态向量拼接在一起做一个线性映射,然后采用交叉熵(Cross-Entropy)作为损失函数。

 图2 情感分析深度学习模型 

3. 具体的Tensorflow代码实现

具体的代码如下图所示,其中词嵌入层由embedding这个变量来进行存储,并且使用tf.nn.embedding_lookup函数来查询一个单词对应的词向量。有tf.nn.rnn_cell.LSTMCell来创建长短时记忆单元(一种特殊的循环神经网络单元),每个单元的输入长度和状态向量长度均等于embedding后词向量的长度。最后使用tf.nn.bidirectional_dynamic_rnn函数来组成循环神经网络,这个函数可以传入一个参数,代表的是句子的长度,这样就能避免前面在文本展宽中加入太多的0对于训练结果造成的影响。

下面的代码是嵌入层的实现过程:

with tf.variable_scope("embedding"):    embedding = tf.get_variable("embedding", \    shape=(self.vocab_size, self.embedding_size), \    type=tf.float32, initializer=tf.truncated_normal_initializer(stddev=1e-3) \    )    self.embedding = embeddingsents = tf.nn.embedding_lookup(embedding, self.input)vector_in = sents

下面的代码是双向循环神经网络的实现过程:

with tf.variable_scope("embedding"):    embedding = tf.get_variable("embedding", \    shape=(self.vocab_size, self.embedding_size), \    type=tf.float32, initializer=tf.truncated_normal_initializer(stddev=1e-3) \    )    self.embedding = embeddingsents = tf.nn.embedding_lookup(embedding, self.input)vector_in = sents

4. 训练结果和预测结果

这里采用ADAM算法作为损失函数的优化方法。学习率在0.001左右。整个训练过程在GTX980Ti显卡上完成,需要大约5分钟的时间。在经过10个周期的训练之后,损失函数下降到0.1左右,最后可以达到大约80%的预测准确率。作为最后的结尾,让我们来测试一些句子吧(大于0代表积极情感,小于0代表消极情感)。

python sentiment.py it was the best of times it was the worst of timesFinal predict: -0.129  python sentiment.py light of my life fire of my loinsFinal predict: 0.956

5. 总结

在这里,我们通过一个简单的模型对句子的情感作了一个分类,使用的模型很粗糙,原理也很简单。虽然这个模型比较简单,但是相对来说准确率并不低。这充分说明了深度学习的威力。如果要进一步优化模型的话,有一下几个思路:可以增加循环神经网络的层数,增加训练样本的数目,引入卷积神经网络的结构等等。大家可以试着来搭建一个自己的神经网络,尝试不同模型对于预测准确率的影响。

技术沙龙推荐

点击下方图片即可阅读

技术沙龙报名| Web 前端工程化架构实践

基于 Google Facets 的数据洞察

翻译 | 使用A-Frame打造WebVR版《我的世界》

沪江Web前端技术团队打造【小程序实战文章+视频教程】登场啦!