炼数成金 门户 商业智能 自然语言处理 查看内容

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

2018-6-29 17:39| 发布者: 炼数成金_小数| 查看: 18606| 评论: 0|原作者: zenRRan|来自: 黑龙江大学自然语言处理实验室

摘要: 汉语短文本对话立场检测的主要任务就是通过以对话的一个人的立场为主要立场,而判断另一个人针对该人的回话的立场。立场包括支持,反对,中立三种立场。基于对话的立场检测应用方向很广,比如人机对话系统,机器需要 ...

网络 工具 模型 测试 神经网络

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

本文采用深度学习的方法,使用双向 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字典,得到对应的立场文字描述即可。

伪代码为

测试样例


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

声明:文章收集于网络,如有侵权,请联系小编及时处理,谢谢!

欢迎加入本站公开兴趣群
商业智能与数据分析群
兴趣范围包括各种让数据产生价值的办法,实际应用案例分享与讨论,分析工具,ETL工具,数据仓库,数据挖掘工具,报表系统等全方位知识
QQ群:81035754

鲜花

握手

雷人

路过

鸡蛋

相关阅读

最新评论

热门频道

  • 大数据
  • 商业智能
  • 量化投资
  • 科学探索
  • 创业

即将开课

 

GMT+8, 2018-12-13 12:37 , Processed in 0.185942 second(s), 24 queries .