
对于一般的短文本分类问题,上文所述的简单的文本卷积网络即可达到很高的正确率[1] 。若想得到更抽象更高级的文本特征表示,可以构建深层文本卷积神经网络[2,3] 。
循环神经网络(RNN)
循环神经网络是一种能对序列数据进行精确建模的有力工具 。实际上,循环神经网络的理论计算能力是图灵完备的[4] 。自然语言是一种典型的序列数据(词序列),近年来,循环神经网络及其变体(如long short term memory[5]等)在自然语言处理的多个领域 , 如语言模型、句法解析、语义角色标注(或一般的序列标注)、语义表示、图文生成、对话、机器翻译等任务上均表现优异甚至成为目前效果最好的方法 。
循环神经网络按时间展开后如图2所示:在第t时刻,网络读入第t个输入Xt(向量表示)及前一时刻隐层的状态值 ht-1(向量表示,h0一般初始化为0向量) , 计算得出本时刻隐层的状态值ht,重复这一步骤直至读完所有输入 。如果将循环神经网络所表示的函数记为f,则其公式可表示为:
其中Wxh是输入到隐层的矩阵参数,Whh是隐层到隐层的矩阵参数,bh为隐层的偏置向量(bias)参数 , σ为sigmoid函数 。
在处理自然语言时 , 一般会先将词(one-hot表示)映射为其词向量表示,然后再作为循环神经网络每一时刻的输入Xt 。此外,可以根据实际需要的不同在循环神经网络的隐层上连接其它层 。如,可以把一个循环神经网络的隐层输出连接至下一个循环神经网络的输入构建深层(deep or stacked)循环神经网络,或者提取最后一个时刻的隐层状态作为句子表示进而使用分类模型等等 。
长短期记忆网络(LSTM)
对于较长的序列数据 , 循环神经网络的训练过程中容易出现梯度消失或爆炸现象[6] 。LSTM能够解决这一问题 。相比于简单的循环神经网络,LSTM增加了记忆单元c、输入门i、遗忘门f及输出门o 。这些门及记忆单元组合起来大大提升了循环神经网络处理长序列数据的能力 。若将基于LSTM的循环神经网络表示的函数记为F,则其公式为:
F由下列公式组合而成[7]:
其中,it , ft,ct,ot,分别表示输入门 , 遗忘门,记忆单元及输出门的向量值,带角标的W及b为模型参数,tanh为双曲正切函数,⊙表示逐元素(elementwise)的乘法操作 。输入门控制着新输入进入记忆单元c的强度,遗忘门控制着记忆单元维持上一时刻值的强度,输出门控制着输出记忆单元的强度 。三种门的计算方式类似,但有着完全不同的参数 , 它们各自以不同的方式控制着记忆单元c , 如图3所示:
LSTM通过给简单的循环神经网络增加记忆及控制门的方式,增强了其处理远距离依赖问题的能力 。类似原理的改进还有Gated Recurrent Unit (GRU)[8],其设计更为简洁一些 。这些改进虽然各有不同,但是它们的宏观描述却与简单的循环神经网络一样(如图2所示),即隐状态依据当前输入及前一时刻的隐状态来改变,不断地循环这一过程直至输入处理完毕:
其中,Recrurent可以表示简单的循环神经网络、GRU或LSTM 。
栈式双向LSTM(Stacked Bidirectional LSTM)
对于正常顺序的循环神经网络,ht包含了t时刻之前的输入信息,也就是上文信息 。同样,为了得到下文信息 , 我们可以使用反方向(将输入逆序处理)的循环神经网络 。结合构建深层循环神经网络的方法(深层神经网络往往能得到更抽象和高级的特征表示) , 我们可以通过构建更加强有力的基于LSTM的栈式双向循环神经网络[9],来对时序数据进行建模 。
如图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 |-- posPaddlePaddle在 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 = 3 LSTM双向栈的层数BATCH_SIZE = 128 batch的大小文本卷积神经网络
车上security一闪一闪是什么意思?security灯亮怎么解除security灯代表的是安全提示灯 , 符号一般有人形、气囊、安全带形状的 。security灯一直闪,一般情况是代表驾驶人员或副驾驶人员没有系好安 。
我们构建神经网络 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 表示的是词典的大?。琧lass_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 = 1word_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 = 0 feed_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= 1 return [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()生成测试用输入数据
【百度情感分析API原理 百度情感分析api】为了进行预测,我们任意选取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.
- 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 Merri?nboer 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.
诚挚招聘
量子位正在招募编辑/采访人员,工作地点在北京中关村 。期待有才气、有热情的同学加入我们!相关细节 , 请在量子位公众号(QbitAI)对话界面,回复招聘两个字 。
量子位 QbitAI · 头条号签约作者
?? ? 追踪AI技术和产品新动态
心想梦成:一个叫杰克的美国人,在中国内地的风景区建了个度假村 。由于生意忙,很久没 能回家和家人团聚,他十分思念他的故乡,一个靠近海边的小镇 。一天 , 他突发奇想,在职工大会上宣布:谁要是能让他做一个梦,梦见自己坐着 船乘风破浪地回到大洋彼岸的海...
猜你喜欢
- 一只贾小蝶百度百科 贾浅浅百度百科
- 爱情是一种高级情感包括什么 情感包括什么
- 情感和感情一样吗 情感包括什么
- 情感语录简介 情感简介
- 人类情感大全
- 百度ai 情感分析
- 情感和爱的起源和进化最能表现人情感的
- 婚姻情感网
- 百度词条官网入口 百度词条
- 百度词条是谁写的 百度词条
