
在自然语言处理中,情感分析一般是指判断一段文本所表达的情绪状态 。其中,一段文本可以是一个句子,一个段落或一个文档 。情绪状态可以是两类,如(正面,负面),(高兴,悲伤);也可以是三类,如(积极,消极,中性)等等 。
情感分析的应用场景十分广泛,如把用户在购物网站(亚马逊、天猫、淘宝等)、旅游网站、电影评论网站上发表的评论分成正面评论和负面评论;或为了分析用户对于某一产品的整体使用感受,抓取产品的用户评论并进行情感分析等等 。
今天是5月20日,PaddlePaddle教你用情感分析算法体会女神心意 。
在下文中 , 我们将以情感分析为例,介绍使用深度学习的方法进行端对端的短文本分类,并使用PaddlePaddle完成全部相关实验 。
项目地址:
https://github.com/PaddlePaddle/book/blob/develop/06.understand_sentiment/README.cn.md
应用背景
在自然语言处理中 , 情感分析属于典型的文本分类问题,即把需要进行情感分析的文本划分为其所属类别 。文本分类涉及文本表示和分类方法两个问题 。
在深度学习的方法出现之前,主流的文本表示方法为词袋模型BOW(bag of words),话题模型等等;分类方法有SVM(support vector machine), LR(logistic regression)等等 。
对于一段文本,BOW表示会忽略其词顺序、语法和句法 , 将这段文本仅仅看做是一个词集合,因此BOW方法并不能充分表示文本的语义信息 。
例如 , 句子这部电影糟糕透了和一个乏味 , 空洞,没有内涵的作品在情感分析中具有很高的语义相似度,但是它们的BOW表示的相似度为0 。又如,句子一个空洞,没有内涵的作品和一个不空洞而且有内涵的作品的BOW相似度很高,但实际上它们的意思很不一样 。
在本教程中 , 我们所要介绍的深度学习模型克服了BOW表示的上述缺陷,它在考虑词顺序的基础上把文本映射到低维度的语义空间,并且以端对端(end to end)的方式进行文本表示及分类,其性能相对于传统方法有显著的提升[1] 。
模型概览
本教程所使用的文本表示模型为卷积神经网络(Convolutional Neural Networks)和循环神经网络(Recurrent Neural Networks)及其扩展 。下面依次介绍这几个模型 。
文本卷积神经网络简介(CNN)
对卷积神经网络来说 , 首先使用卷积处理输入的词向量序列,产生一个特征图(feature map),对特征图采用时间维度上的最大池化(max pooling over time)操作得到此卷积核对应的整句话的特征,最后,将所有卷积核得到的特征拼接起来即为文本的定长向量表示,对于文本分类问题,将其连接至softmax即构建出完整的模型 。
在实际应用中,我们会使用多个卷积核来处理句子 , 窗口大小相同的卷积核堆叠起来形成一个矩阵 , 这样可以更高效的完成运算 。另外,我们也可使用窗口大小不同的卷积核来处理句子 , 图1表示卷积神经网络文本分类模型 , 不同颜色表示不同大小的卷积核操作 。
对于一般的短文本分类问题,上文所述的简单的文本卷积网络即可达到很高的正确率[1] 。若想得到更抽象更高级的文本特征表示,可以构建深层文本卷积神经网络[2,3] 。
循环神经网络(RNN)
循环神经网络是一种能对序列数据进行精确建模的有力工具 。实际上,循环神经网络的理论计算能力是图灵完备的[4] 。自然语言是一种典型的序列数据(词序列),近年来,循环神经网络及其变体(如long short term memory[5]等)在自然语言处理的多个领域,如语言模型、句法解析、语义角色标注(或一般的序列标注)、语义表示、图文生成、对话、机器翻译等任务上均表现优异甚至成为目前效果最好的方法 。
循环神经网络按时间展开后如图2所示:在第t时刻 , 网络读入第t个输入 。
其中,在处理自然语言时,一般会先将词(one-hot表示)映射为其词向量表示 , 然后再作为循环神经网络每一时刻的输入 。
长短期记忆网络(LSTM)
对于较长的序列数据,循环神经网络的训练过程中容易出现梯度消失或爆炸现象[6] 。LSTM能够解决这一问题 。相比于简单的循环神经网络,LSTM增加了记忆单元c、输入门i、遗忘门f及输出门o 。这些门及记忆单元组合起来大大提升了循环神经网络处理长序列数据的能力 。若将基于LSTM的循环神经网络表示的函数记为F,则其公式为:
F由下列公式组合而成[7]:
其中,
LSTM通过给简单的循环神经网络增加记忆及控制门的方式 , 增强了其处理远距离依赖问题的能力 。类似原理的改进还有Gated Recurrent Unit (GRU)[8],其设计更为简洁一些 。这些改进虽然各有不同,但是它们的宏观描述却与简单的循环神经网络一样(如图2所示),即隐状态依据当前输入及前一时刻的隐状态来改变 , 不断地循环这一过程直至输入处理完毕:
其中,Recrurent可以表示简单的循环神经网络、GRU或LSTM 。
栈式双向LSTM(Stacked Bidirectional LSTM)
对于正常顺序的循环神经网络,
如图4所示(以三层为例),奇数层LSTM正向,偶数层LSTM反向,高一层的LSTM使用低一层LSTM及之前所有层的信息作为输入,对最高层LSTM序列使用时间维度上的最大池化即可得到文本的定长向量表示(这一表示充分融合了文本的上下文信息,并且对文本进行了深层次抽象) , 最后我们将文本表示连接至softmax构建分类模型 。
基于PaddlePaddle的实战PaddlePaddle简介
PaddlePaddle(paddlepaddle.org)是百度研发的深度学习框架 。除了核心框架之外,PaddlePaddle还提供了丰富的工具组件 。官方开源了多个工业级应用模型 , 涵盖自然语言处理、计算机视觉、推荐引擎等多个领域,并开放了多个领先的预训练中文模型 。4月23日深度学习开发者峰会上,PaddlePaddle发布了一系列新特性和应用案例 。
数据集介绍
我们以IMDB情感分析数据集为例进行介绍 。IMDB数据集的训练集和测试集分别包含25000个已标注过的电影评论 。其中,负面评论的得分小于等于4,正面评论的得分大于等于7,满分10分 。
aclImdb|- test|-- neg|-- pos|- train|-- neg|-- pos
PaddlePaddle在 dataset/imdb.py 中实现了imdb数据集的自动下载和读?。?并提供了读取字典、训练数据、测试数据等API 。
配置模型
在该示例中,我们实现了两种文本分类算法,文本卷积神经网络,和栈式双向LSTM 。我们首先引入要用到的库和定义全局变量:
from __future__ import print_functionimport paddleimport paddle.fluid as fluidimport numpy as npimport sysimport mathCLASS_DIM = 2情感分类的类别数EMB_DIM = 128词向量的维度HID_DIM = 512隐藏层的维度STACKED_NUM = 3LSTM双向栈的层数BATCH_SIZE = 128batch的大小
文本卷积神经网络
我们构建神经网络 convolution_net , 示例代码如下 。需要注意的是:fluid.nets.sequence_conv_pool 包含卷积和池化层两个操作 。
文本卷积神经网络def convolution_net(data, input_dim, class_dim, emb_dim, hid_dim):emb = fluid.layers.embedding(input=data, size=[input_dim, emb_dim], is_sparse=True)conv_3 = fluid.nets.sequence_conv_pool(input=emb,num_filters=hid_dim,filter_size=3,act="tanh",pool_type="sqrt")conv_4 = fluid.nets.sequence_conv_pool(input=emb,num_filters=hid_dim,filter_size=4,act="tanh",pool_type="sqrt")prediction = fluid.layers.fc(input=[conv_3, conv_4], size=class_dim, act="softmax")return prediction
网络的输入 input_dim 表示的是词典的大小 , class_dim 表示类别数 。这里,我们使用 sequence_conv_pool API实现了卷积和池化操作 。
栈式双向LSTM
栈式双向神经网络stacked_lstm_net的代码片段如下:
栈式双向LSTMdef stacked_lstm_net(data, input_dim, class_dim, emb_dim, hid_dim, stacked_num):assert stacked_num % 2 == 1计算词向量emb = fluid.layers.embedding(input=data, size=[input_dim, emb_dim], is_sparse=True)第一层栈全连接层fc1 = fluid.layers.fc(input=emb, size=hid_dim)lstm层lstm1, cell1 = fluid.layers.dynamic_lstm(input=fc1, size=hid_dim)inputs = [fc1, lstm1]其余的所有栈结构for i in range(2, stacked_num1):fc = fluid.layers.fc(input=inputs, size=hid_dim)lstm, cell = fluid.layers.dynamic_lstm(input=fc, size=hid_dim, is_reverse=(i % 2) == 0)inputs = [fc, lstm]池化层fc_last = fluid.layers.sequence_pool(input=inputs[0], pool_type=max)lstm_last = fluid.layers.sequence_pool(input=inputs[1], pool_type=max)全连接层,softmax预测prediction = fluid.layers.fc(input=[fc_last, lstm_last], size=class_dim, act=softmax)return prediction
以上的栈式双向LSTM抽象出了高级特征并把其映射到和分类类别数同样大小的向量上 。最后一个全连接层的’softmax’激活函数用来计算分类属于某个类别的概率 。
重申一下,此处我们可以调用 convolution_net 或 stacked_lstm_net 的任何一个网络结构进行训练学习 。我们以 convolution_net 为例 。
接下来我们定义预测程序(inference_program) 。预测程序使用convolution_net 来对 fluid.layer.data 的输入进行预测 。
def inference_program(word_dict):data = https://www.45baike.com/post/fluid.layers.data(name="words", shape=[1], dtype="int64", lod_level=1)dict_dim = len(word_dict)net = convolution_net(data, dict_dim, CLASS_DIM, EMB_DIM, HID_DIM)net = stacked_lstm_net(data, dict_dim, CLASS_DIM, EMB_DIM, HID_DIM, STACKED_NUM)return net
我们这里定义了 training_program 。它使用了从 inference_program 返回的结果来计算误差 。我们同时定义了优化函数 optimizer_func。
因为是有监督的学习,训练集的标签也在fluid.layers.data中定义了 。在训练过程中,交叉熵用来在fluid.layer.cross_entropy中作为损失函数 。
在测试过程中,分类器会计算各个输出的概率 。第一个返回的数值规定为cost 。
def train_program(prediction):
label = fluid.layers.data(name="label", shape=[1], dtype="int64")cost = fluid.layers.cross_entropy(input=prediction, label=label)avg_cost = fluid.layers.mean(cost)accuracy = fluid.layers.accuracy(input=prediction, label=label)return [avg_cost, accuracy]返回平均cost和准确率acc优化函数def optimizer_func():return fluid.optimizer.Adagrad(learning_rate=0.002)
训练模型定义训练环境
无钥匙进入离开车会自动锁车吗?无钥匙进入怎么锁车我们知道,没有无钥匙进入系统的车辆在你离开车辆后需要按一下遥控钥匙的锁车按键来进行锁车 。而如果你的车辆配有无钥匙进入系统 。
定义你的训练是在CPU上还是在GPU上:
use_cuda = False在cpu上进行训练place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
定义数据提供器
下一步是为训练和测试定义数据提供器 。提供器读入一个大小为 BATCH_SIZE的数据 。paddle.dataset.imdb.word_dict 每次会在乱序化后提供一个大小为BATCH_SIZE的数据,乱序化的大小为缓存大小buf_size 。
注意:读取IMDB的数据可能会花费几分钟的时间,请耐心等待 。
print("Loading IMDB word dict....")word_dict = paddle.dataset.imdb.word_dict()print ("Reading training data....")train_reader = paddle.batch(paddle.reader.shuffle(paddle.dataset.imdb.train(word_dict), buf_size=25000),batch_size=BATCH_SIZE)print("Reading testing data....")test_reader = paddle.batch(paddle.dataset.imdb.test(word_dict), batch_size=BATCH_SIZE)feed_order = [words, label]pass_num = 1
word_dict 是一个字典序列,是词和label的对应关系,运行下一行可以看到具体内容:
word_dict
每行是如(’limited’: 1726)的对应关系 , 该行表示单词limited所对应的label是1726 。
构造训练器
训练器需要一个训练程序和一个训练优化函数 。
main_program = fluid.default_main_program()star_program = fluid.default_startup_program()prediction = inference_program(word_dict)train_func_outputs = train_program(prediction)avg_cost = train_func_outputs[0]test_program = main_program.clone(for_test=True)sgd_optimizer = optimizer_func()sgd_optimizer.minimize(avg_cost)exe = fluid.Executor(place)
该函数用来计算训练中模型在test数据集上的结果
def train_test(program, reader):count = 0feed_var_list = [program.global_block().var(var_name) for var_name in feed_order]feeder_test = fluid.DataFeeder(feed_list=feed_var_list, place=place)test_exe = fluid.Executor(place)accumulated = len([avg_cost, accuracy]) * [0]for test_data in reader():avg_cost_np = test_exe.run(program=program,feed=feeder_test.feed(test_data),fetch_list=[avg_cost, accuracy])accumulated = [x[0]x[1][0] for x in zip(accumulated, avg_cost_np)]count= 1return [x / count for x in accumulated]
提供数据并构建主训练循环
feed_order 用来定义每条产生的数据和 fluid.layers.data 之间的映射关系 。比如,imdb.train 产生的第一列的数据对应的是words这个特征 。
Specify the directory path to save the parametersparams_dirname = "understand_sentiment_conv.inference.model"feed_order = [words, label]pass_num = 1训练循环的轮数程序主循环部分def train_loop():启动上文构建的训练器feed_var_list_loop = [main_program.global_block().var(var_name) for var_name in feed_order]feeder = fluid.DataFeeder(feed_list=feed_var_list_loop,place=place)exe.run(star_program)训练循环for epoch_id in range(pass_num):for step_id, data in enumerate(train_reader()):运行训练器metrics = exe.run(main_program,feed=feeder.feed(data),fetch_list=[var.name for var in train_func_outputs])测试结果print("step: {0}, Metrics {1}".format(step_id, list(map(np.array, metrics))))if (step_id1) % 10 == 0:avg_cost_test, acc_test = train_test(test_program,test_reader)print(Step {0}, Test Loss {1:0.2}, Acc {2:0.2}.format(step_id, avg_cost_test, acc_test))print("Step {0}, Epoch {1} Metrics {2}".format(step_id, epoch_id, list(map(np.array, metrics))))if math.isnan(float(metrics[0])):sys.exit("got NaN loss, training failed.")if params_dirname is not None:fluid.io.save_inference_model(params_dirname, ["words"],prediction, exe) 保存模型train_loop()
训练过程处理
我们在训练主循环里打印了每一步输出,可以观察训练情况 。
开始训练
最后,我们启动训练主循环来开始训练 。训练时间较长,如果为了更快的返回结果 , 可以通过调整损耗值范围或者训练步数,以减少准确率的代价来缩短训练时间 。
train_loop(fluid.default_main_program())
应用模型构建预测器
和训练过程一样,我们需要创建一个预测过程,并使用训练得到的模型和参数来进行预测,params_dirname 用来存放训练过程中的各个参数 。
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()exe = fluid.Executor(place)inference_scope = fluid.core.Scope()
生成测试用输入数据
为了进行预测 , 我们任意选取3个评论 。请随意选取您看好的3个 。我们把评论中的每个词对应到word_dict中的id 。如果词典中没有这个词,则设为unknown 。然后我们用create_lod_tensor来创建细节层次的张量
reviews_str = [read the book forget the movie, this is a great movie, this is very bad]reviews = [c.split() for c in reviews_str]UNK = word_dict[]lod = []for c in reviews:lod.append([word_dict.get(words, UNK) for words in c])base_shape = [[len(c) for c in lod]]tensor_words = fluid.create_lod_tensor(lod, base_shape, place)
应用模型并进行预测
现在我们可以对每一条评论进行正面或者负面的预测啦 。
with fluid.scope_guard(inference_scope):[inferencer, feed_target_names,fetch_targets] = fluid.io.load_inference_model(params_dirname, exe)reviews_str = [read the book forget the moive’,’this is a great moive,this is very bad]reviews = [c.split() for c in reviews_str]UNK = word_dict[]lod = []for c in reviews:lod.append([np.int64(word_dict.get(words, UNK)) for words in c])base_shape = [[len(c) for c in lod]]tensor_words = fluid.create_lod_tensor(lod, base_shape,place)assert feed_target_names[0] == "words"results = exe.run(inferencer,feed={feed_target_names[0]: tensor_words},fetch_list=fetch_targets,return_numpy=False)np_data = https://www.45baike.com/post/np.array(results[0])for i, r in enumerate(np_data):print("Predict probability of ", r[0], " to be positive and ", r[1]," to be negative for review \\", reviews_str[i], "\\")
感兴趣的小伙伴可以在PaddlePaddle官网上阅读其他相关文档内容:http://www.paddlepaddle.org/
参考文献:
Kim Y. Convolutional neural networks for sentence classification[J]. arXiv preprint arXiv:1408.5882, 2014.
Kalchbrenner N, Grefenstette E, Blunsom P. A convolutional neural network for modelling sentences[J]. arXiv preprint arXiv:1404.2188, 2014.
Yann N. Dauphin, et al. Language Modeling with Gated Convolutional Networks[J] arXiv preprint arXiv:1612.08083, 2016.
这个程序只有99行 , 利用UNIX系统的缺点,用finger命令查联机用户名单,然后破译用户口令,用MAIL系统复制、传播本身的源程序,再编译生成代码 。最初的网络蠕虫设计目的是当网络空闲时,程序就在计算机间“游荡”而不带来任何损害 。
Siegelmann H T, Sontag E D. On the computational power of neural nets[C]//Proceedings of the fifth annual workshop on Computational learning theory. ACM, 1992: 440-449.
Hochreiter S, Schmidhuber J. Long short-term memory[J]. Neural computation, 1997, 9(8): 1735-1780.
Bengio Y, Simard P, Frasconi P. Learning long-term dependencies with gradient descent is difficult[J]. IEEE transactions on neural networks, 1994, 5(2): 157-166.
Graves A. Generating sequences with recurrent neural networks[J]. arXiv preprint arXiv:1308.0850, 2013.
Cho K, Van Merrinboer B, Gulcehre C, et al. Learning phrase representations using RNN encoder-decoder for statistical machine translation[J]. arXiv preprint arXiv:1406.1078, 2014.
Zhou J, Xu W. End-to-end learning of semantic role labeling using recurrent neural networks[C]//Proceedings of the Annual Meeting of the Association for Computational Linguistics. 2015.
————
编辑 ∑Gemini
来源:新浪专栏·创事记
更多精彩:
泰勒定理的奇闻轶事
丘成桐:漫谈微分几何
Leibniz 如何想出微积分?(一)
线性相关和秩的物理意义
数学史上你认为最丑陋的公式是什么?
陶哲轩谈什么是好的数学
田渊栋:数学的用处(下篇)
你绝对没想过原来数学家这么流氓 , 一言不合就进行暴力证明
世界上最牛的五篇博士论文
数学中有哪些巧合让人眼前一亮?
算法立功!清华毕业教授美国被抢车,警察无能为力自己用贪心算法找回
学术史上的奇文:怎样用数学抓狮子
台大教授的反思:最难的一课 我们却没教给学生
麻省理工学院(MIT)研究生学习指导—— 怎样做研究生
分享 数学,常识和运气 ——投资大师詹姆斯·西蒙斯2010年在MIT的讲座
稿件涉及数学、物理、算法、计算机、编程等相关领域,经采用我们将奉上稿酬 。
【情感的五种分类 情感分类实战】池塘里的地仙尸:竹林屋村前有一口大池塘 , 水很深,传说这口池塘淹不死人 。有一天来了个的古董贩子胡胖子,偶然听说了这件怪事,于是怀疑池塘里有宝物 。他花钱买通了村长,雇了四五个人,划着小船在池塘里撒网捕捞 。可整整忙活了三天 , 屁也没捞到 。
猜你喜欢
- 黄疸分为哪三种类型 情感分为哪三种类型
- 翡翠种水带和色带 什么叫翡翠的色带
- 男生戴什么样的翡翠 男人戴什么颜色翡翠好看
- 汉朝的强大在哪里?只是军事实力吗?
- 漠北之战的经过如何?汉武帝为何要发动这场战役呢?
- 历史上宋忠与朱棣争夺居庸关,最后他是怎么失败的?
- 上官婉儿的身世如何?他的下场如何呢?
- 胡雪岩的家境并不好,他是如何成为清朝首富的?
- 东吴作为三国时期的一部分 东吴是怎么灭亡的
- 离婚后孩子的探视权时间法律有哪些规定? 离婚小孩探视权时间规定是什么
