基于汉语短文本对话的立场检测系统理论与实践

阅读大概需要5分钟 跟随小博主,每天进步一丢丢

导读

汉语短文本对话立场检测的主要任务就是通过以对话的一个人的立场为主要立场,而判断另一个人针对该人的回话的立场。立场包括支持,反对,中立三种立场。基于对话的立场检测应用方向很广,比如人机对话系统,机器需要判断对方说话的立场是什么来决定自己回话的立场;比如情感挖掘,和一个支持者的立场进行对话,就能判断出该对话者的情感倾向。

本文采用深度学习的方法,使用双向 LSTM 神经网络进行对给定答案的微博对话语料进行训练。每一份训练样例包含博主的话和回话者的对话,博主和回话者的话都有各自对话的立场表示标注,回话者还有针对博主的立场方向标注自己的立场。本文用的新颖方法是将博主和回话者的话分别经过双向 LSTM,将结果拼接到一起,在经过池化层和线性层得到分类结果。通过该结果和标准的答案比较,通过交叉熵得出损失值,反向传播梯度,更新参数,达到训练模型的效果。最后通过调参得出在测试集上效果最佳的模型为最终的模型。并使用该模型,对人工输入的对话进行真实的立场预测。

文本数据

这里的是很多组人工处理过的数据文本。

随便拿出来一个原始数据进行举例:

[PO]为post,博主说的话,[RE]为response,回答博主的话。而立场检测就是为了站在博主的立场,来检测其他回复的立场。所以需要标记post的立场,这里是积极的,positive,简称p,这里用[PO]STANCE#p表示;而回复response是不支持post的观点的,就是消极的,negative,简称n;但是有时候也是中立的,不支持也不反对post的话,那就是中立的,middle,简称m。

那么这里对数据进行处理:

又因为句子本身的情感极性也会对立场判断有影响,所以再标上每个句子的极性:[PO]是个积极的句子,这里用[PO]POLAR#p来表示,即post 极性为positive;[RE]极性为消极的,即这里用[RE]POLAR#n来表示:

文本数据处理

1.中文余料的清洗

一段文本,中英文都会有,所以对中英文都得分别处理。对中文的处理:

(1)繁体转简体

(2)全角英文字母转半角

(3)全角中文标点转半角

举个例子

代码为:

2.对英文语料的清洗

因为中文中掺杂着英文是很正常的事,所以还是有必要处理下英文的文本。

对英文的处理分为以下几个方面:

为了好区分,下面用下划线(_)表示空格

(1)将缩写词用空格分开:

比如Tom’s改为Tom_’s;I’ve改为I_ ‘ve;don’t改为do_n‘t;we’re改为we_’re;you’d改为you_’d;we’ll改为we_’ll

(2)一些标点符号需要和词分开:

比如 , 改为 _,_ ;!改为_!_;(改为_(_;)改为_)_;?改为_?_

(3)连续两个以上的空格修改为一个空格

(4)全都改为小写

代码为:

训练集和测试集

训练集是作为训练模型用的数据集,测试集是测试训练集训练出来的模型的效果。这里用的训练集和测试集数据对数分别为:7321对和2090对。(如果再完美些,可以加上开发集。这里数据有限,省略了开发集。)

建立字典

通过训练集对每一个词建立字典的目的是为了接下来的数据操作,因为计算机无法对文本进行处理,只能对数字进行识别并处理。但是测试集里的词一般都不会都在训练集里出现,所以也需要设置未出现的词unknown,简写unk;之后写批处理batch的时候也需要进行空白填补padding,简称pad。这两个也都需要放进字典里。

建立字典的主要代码

其中想了解HyperParams类代码,在下面的链接里我会附上github源代码。

序列数字化

这个和字典是对应的,字典是将词对应上数字,这里是通过字典将句子或label变成数字。

代码为

算法实现流程

(1)文本数字化:因为每个文本里有很多组数据,每组数据里都有一个post,一个response,一个label(金标,用来表示)。训练的时候都是选取几组数据来训练的,这个几组数据也就是之后调参用的批处理大小batch_size,这里默认为1,也就是一组数据。因为之前这些数据都是分好词的,所以这里直接通过word字典直接数字化即可。然后将数据放进网络开始进行一系列处理。

(2)embedding层:因为要给这两个post,response分别对每个数字一个编码,也就是将它向量化,这里叫做embedding layer。最终的到两个embedding向量。将每个数字放到维度为embed_size的向量空间。这样每个词都有自己的唯一表示。这里初始化embedding有两种:一种是通过当前文本进行训练学习,也即是刚开始可以随机初始化或者都初始化为0,随机初始化过程中可以用概率分布来控制数据分布,比如正态分布;另一种是通过外部预训练好的词向量,将它拷贝到当前网络的embedding layer的权重里。这俩种初始化方法可以作为之后调参的方法。

(3)dropout层:然后经过dropout层,因为一般情况下,随着数据量增大的时候,网络学习的精度也就越高,但是这样的效果其实不是我们想要的,会达到过拟合的效果。所以就需要减少数据,在神经网络中,为了减少数据,只能从节点入手,去掉节点即可,这样实现就是讲想要去掉的节点的权重设为0即可,dropout就是这样的功能。Dropout层有一个参数,float型的,表示网络中每个隐藏层中需要留下可用节点的比率,比如0.6,就表示保留60%,使每层40%的节点失活。经过dropout层不会改变维度大小,也就是说原本什么维度,输出还是什么维度。

(4)biLSTM层:将这两个post,response的数据经过biLSTM层,biLSTM层里有两个设置参数的地方,为hidden_size和hidden_num,hidden_size为隐层中的节点(也可以说是参数)个数,hidden_num表示有多少层隐层。这两个设置也是之后调参用的数据。输出为两个值,outputs(这里包含预测的信息)和C(状态数据,用不上,舍去)。

(5)拼接:之后将post和response两个outputs合并到一起,维度对上即可。比如此时post维度为(1,5,128),response维度为(1,10,128),那么合并之后为(1,15,128)。其中维度具体为(批处理大小,句子长度,隐层参数大小*2),注释:乘2是因为单向的LSTM该位置为隐层参数大小,双向LSTM就为两个隐层拼接到一起的,所以这里为两个隐层大小,故乘2。

(6)池化:上述数据经过max_pooling层最大池化层(当然也有avg_pooling平均池化层,min_pooling最小池化层,常用的为max_pooling)。选取指定维度的最大值,最终维度变为(2*hidden_size,label_size)。

(7)线性层:再经过一个Linear Layer线性层,就是讲一个维度映射到另一个维度上。这里将维度变为(hidden_size/2,label_size)。因为2*hidden_size比较大一般,直接降到1维会丢到很多信息。所以需要两个线性层。

(8)线性层:再经过一个Linear Layer线性层,维度变为(1,label_size)。(9)Softmax化:之后经过Softmax层,概率化,也叫归一化。

(10)选最大值下标:选出最大值的下标,即为idx_pred,这个就是预测的结果的数字,如果想看到具体分类,需要通过label字典转换。

(11)求损失并更新参数:最后将这个预测结果idx_pred和真实的label作为参数给交叉熵损失函数,损失函数反向传播梯度,更新参数。

上述流程图为

biLSTM神经网络搭建

简单的来说,该神经网络总结为post和response先分别经过Embedding层;然后进行dropout;之后将其结果放入biLSTM里,获得输出;然后将该输出concat到一起;再经过pooling层;最后经过线性层映射到分类上即可。

注:其中最后过线性层之前的数据有些庞大,大概有百个数值,直接映射3分类上,会使得数据损失的信息较大。所以这里采用两次经过线性层,中间用了一个激活函数进行非线性变换。

网络代码初始化

网络数据流动

训练的整体流程

一些因素对F1值的影响

如果还没了解F1值的话,这里有我之前写的通俗易懂的文章

详谈P(查准率),R(查全率),F1值

通过控制变量法,对以下因素进行测试:

1.词向量维度,embed_size即Word Embedding size,词被映射到的向量空间的大小

2.因为神经网路要学习到训练文本的每一个细节,这样会导致在文本数量很多的时候,出现过拟合现象。为了避免这样的现象,本文调参采用了dropout方法。Dropout使网络的部分连接部分节点的权重为0,导致该节点失活,从而达到防止过拟合的效果。设置每层神经网络不失活节点比例为x,则dropout=x。将dropout层加入到Embedding层和biLSTM层,防止两者过拟合。

3.隐层里参数的个数即hidden_size。hidden_size越大,训练的精度越高,但是也就会越容易过拟合。所以这些也需要通过调参来选择最好的参数。

4.批处理的大小batch_size,这里表示一批性处理多少训练语料。不同数据量的batch大小不同,batch越大GPU训练速度越快,但是精度可能会降低,所以需要通过调参来决定具体选择的大小为多少。

5.隐藏层的数量hidden_num,数量越大,训练速度越慢,一般很有可能出现过拟合现象。在简单的网络中,一般隐藏层数为1即可,但是也要看看层数为2的效果,不好的话,就用1层隐藏层。所以,这里需要通过调参获得。

6.权重衰退weight_decay,为了防止过拟合,在原本损失函数的基础上,加上L2正则化,而weight_decay就是这个正则化的lambda参数,一般设置为1e-8,所以调参的时候调整是否使用权重衰退即可。

7.修剪梯度clip_grad,为了防止梯度爆炸(gradient explosion)。原理为:损失函数反向传播的时候,使得每个参数都有了梯度gradient,如果所有的梯度平方和sum_sq_gradient大于clip_grad,那么求出缩放因子:

scale_factor = clip_grad / sum_sq _gradient

接着改变每个gradient,使每个gradient都乘scale_factor,达到缩放的效果,使每个梯度的sum_sq_gradient都被限制在clip_grad里,来达到防止梯度爆炸的效果。通常设置为10,那么调参的内容为是否需要clip_grad机制。

8.学习率衰退lr_decay,一般设置为1e-8,公式为:

lr = lr/(1+step*lr_decay)#lr为学习率,step为当前迭代次数

因为一般情况下循环迭代次数越多的时候,学习率的步伐就应该越来越小,这样才能慢慢接近函数的极值点,。但是有时候也不一定会有效,所以这里需要通过调参来查看是否需要开启lr_decay。

9.外部词向量,即提前提前训练好的词向量,这里指word2vec。因为以前自然语言处理用的是one-hot方法进行对每个词进行编码向量化的,维度为1*字典大小,就一位是1其余位都为0,但是这样在数据量大的情况下会让计算机达到难以计算困难的情况,而且每个词都是独立存在的,之间没有计算相似度的可能性。所以Word2vec在2012年被Google提出来,目的是将文本生成词向量模型,其中包括两个模型,分别是CBOW(continous bag of words)和Skip-Gram。这两个模型分别从两个不同的角度建立词向量模型。其中CBOW是通过一个或多个单词的上下文来对这个词进行预测,而这里用的正是CBOW方法训练的词向量。

所以最终选择的参数为

实例测试

测试流程

(1) 从键盘获取post,response的文本。

(2) 然后用jieba分词器进行分词。

(3) 通过word字典将文本转换成数字序列。

(4) 使用上述最佳的模型model。

(5) 将数字化的post,response输入到model里。

(6) 得到每个种类的得分。

(7) 然后找出最大的得分的位置。

(8) 通过label字典,得到对应的立场文字描述即可。

伪代码为

测试样例

总结

本文有一些不足:

  1. 在使用外部词向量的时候,人工加入的两种标签在外部向量中是没有的,这里只能用unk表示。因为数据量太小,不能用自己的这个数据来训练词向量。
  2. 本文的精确度很低,感觉可以用attention机制等方法来提高F1值。

源代码,请点击https://github.com/zenRRan/Short_Conversation_based_Stance_Detection获取

原文发布于微信公众号 - 深度学习自然语言处理(zenRRan)

原文发表时间:2018-06-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Coding迪斯尼

详解神经网络中的神经元和激活函数

13230
来自专栏AI科技大本营的专栏

重磅 | 周志华最新论文:首个基于决策树集成的自动编码器,表现优于DNN

向AI转型的程序员都关注了这个号☝☝☝ ? 翻译 | AI科技大本营(rgznai100) 参与 | 周翔、reason_W成龙,Shawn 今年 2 月,南京...

52140
来自专栏PPV课数据科学社区

TensorFlow 广度和深度学习的教程

? 在这篇文章中,我们将会介绍如何使用 TF.Learn API 同时训练一个广度线性模型和一个深度前馈神经网络。这种方法结合了记忆和泛化的优势。它在一般的大...

38950
来自专栏老秦求学

[Deep-Learning-with-Python] Keras高级概念

目前为止,介绍的神经网络模型都是通过Sequential模型来实现的。Sequential模型假设神经网络模型只有一个输入一个输出,而且模型的网络层是线性堆叠在...

19010
来自专栏量子位

Attention!神经网络中的注意机制到底是什么?

原作:Adam Kosiorek 安妮 编译自 GitHub 量子位 出品 | 公众号 QbitAI 神经网络的注意机制(Attention Mechanism...

49750

如何在Python中规范化和标准化时间序列数据

如果您的时间序列数据具有连续的尺度或分布,则在某些机器学习算法将获得更好的性能。

62590
来自专栏新智元

TensorFlow 自动句子语义编码,谷歌开源机器学习模型 Skip-Thoughts

【新智元导读】谷歌今天开源一个句子编码器模型 Skip-Thoughts,在 TensorFlow 上实现,学习将输入的句子编码成固定维度的向量表示,可以用于语...

43330
来自专栏专知

【干货】Batch Normalization: 如何更快地训练深度神经网络

【导读】本文是谷歌机器学习工程师 Chris Rawles 撰写的一篇技术博文,探讨了如何在 TensorFlow 和 tf.keras 上利用 Batch N...

5.1K80
来自专栏机器之心

学界 | 谷歌论文新突破:通过辅助损失提升RNN学习长期依赖关系的能力

选自arXiv 机器之心编译 参与:李诗萌、黄小天 本文提出了一种简单的方法,通过在原始函数中加入辅助损失改善 RNN 捕捉长期依赖关系的能力,并在各种设置下评...

38350
来自专栏雷博生的专栏

一个小白的 TensorFlow 学习笔记(一)

本系列文章以《深度学习原理与TensorFlow实践》一书的内容为基础,结合网络上其他材料,提取并梳理了一些感觉比较有意义的点,也记录了一个菜鸟的心路历程。好东...

1.3K20

扫码关注云+社区

领取腾讯云代金券