智能客服问题相似度算法设计——第三届魔镜杯大赛第12名解决方案
目录:
一、比赛介绍
二、数据介绍
三、解决方案
(一)问题分析
(二)数据探索
(三)模型
(四)调参
(五)特征工程
(六)模型集成
(七)后处理
四、比赛总结
(一)比赛成绩
(二)代码分享
(三)经验总结
(四)心得体会
(五)展望未来
五、参考文献
一、比赛介绍
第三届“魔镜杯”由拍拍贷智慧金融研究院主办,在2018年6月10日至7月24日举行,总奖池高达10万美金。拍拍贷提供智能客服聊天机器人真实数据,以自然语言处理和文本挖掘技术为主要探索对象,希望比赛选手能利用这些资源开发一种算法,提高智能客服的识别能力和服务质量。
智能客服聊天机器人场景中,待客户提出问题后,往往需要先计算客户提出问题与知识库问题的相似度,进而定位最相似问题,再对问题给出答案。本次比赛的题目便是问题相似度算法设计。
问题匹配二、数据介绍
本次比赛使用了脱敏数据,所有原始文本信息都被编码成单字ID序列和词语ID序列,并提供由google word2vec训练的300维的word_embedding和char_embedding。
训练数据包含3列,label, q1, q2,其中q1和q2表示要判断的两个问题,label=1表示是相同的问题,label=0表示不同的问题。比赛要求参赛选手预测测试数据中的每一对问题是否是同一个意思。
具体数据请查看第三届魔镜杯大赛数据介绍
三、解决方案
(一)问题分析
预测问题对(或者说句子对)是否表达一个意思,这是一个自然语言处理方面的分类问题。跟传统的分类问题不同,这里有三个充满自然语言处理特色的难题:
1. 如何把文本转化为机器可以学习的数据?
2. 句子长度不是固定的,有长句子,也有短句子,也就是说我们的输入是变长的,如何处理变长输入?
3. 句子的相似我们是指语义上的相似,怎么抽取句子的语义信息,然后计算它们的距离呢?
对于问题1,由于本次比赛数据提供了word embedding,我们可以跳过文本预处理、分词和词向量训练了。
对于问题2,有两种思路,一种是把句子处理成定长的数据,比如使用tfidf编码,或者对word embedding做平均,不过这样效果并不是很好;另一种就是使用可以处理序列数据的模型,比如循环神经网络RNN,卷积神经网络CNN,隐藏马尔科夫模型HMM等。
对于问题3,也是最困难的问题,举个例子,“彩虹年化多少”这个问题,人一看就知道是指彩虹产品的收益率,但是机器是很难理解的,它最多也就是知道说的是彩虹这个产品,但是“年化”这个充满了口语特色的词 ,如果机器从未学习过这方面的知识,它是无法理解的。而且很有可能的是,年化这个词在分词中可能并没有分好,那在后续处理中机器就更加get不到“年化”的意思了。虽然机器暂时无法达到人的理解水平,但是在大规模数据的帮助下,它还是可以学习到很多东西的。我们的任务其实就是利用我们的洞察,设计算法让机器能更好的理解问题的意思。对于这个问题,我们主要采取了端对端的学习策略,利用孪生神经网络、ESIM,从句子对输入到语义相似性判断输出,一步搞定。
(二)数据探索
数据探索的思路和方向有很多,以下3点是我主要做的:
1、训练数据的label分布:训练数据共254385个样本,其中相似问题对132054个,不相似问题对122332个,类别平衡。
2、从相似问题的词编码来看,两个相似的问题中词语相同的比例并不多,很多句子甚至没有相同的词语;字符编码的话相同字符稍微比较多,这也很容易理解(毕竟有很多虚词助词出现的频次是很高的)
3、对训练数据相似的问题的图关系进行探索,发现有48691个独立的问题topic,topic size呈现长尾分布,有些topic的规模很大(最大1328),说明很多人问;还有些问题很少出现,说明只有极少数的人来问。
数据探索是很重要的,帮助我们建立对数据的一些洞察,这些洞察在后面做特征工程时帮助很大。
(三)模型
我们主要使用了Siamese结构的LSTM、CNN和ESIM三个深度模型,分别对句子的word embedding和 char embedding进行训练。
1、Siamese LSTM 模型
长短期记忆网络LSTM,是循环神经网络RNN的一个变种,引入forget gate, update gate,output gate三个门,克服了RNN梯度消失的造成长期记忆遗失的问题,使得网络同时具有短期和长期的记忆。
Siamese LSTM模型[1], 端对端学习模型,主要由两个部分构成:
(1)问题的语义表示向量抽取: 通过LSTM完成,在问题1和问题2的对称网络中,这部分LSTM共享权重。
(2)语义向量相似性计算:计算语义表示向量的平方距离和角度,再喂给多层感知机MLP进行分类。
Siamese LSTM模型效果:10折CV之后线上logloss为0.19左右,在模型中增加手工特征,可进一步提升模型效果。
2、Siamese CNN 模型
CNN卷积神经网络模型,是我队友Moka负责的,模型的思想是使用了不同大小卷积窗口的filter来抽取句子中各种长度元组的信息,然后再计算相似度。是个非常直观,效果也很好的模型。
(这里插一句题外话,为什么我的电脑跑不了CNN?!!电脑配置Win10 64位,Anoconda 3, Tensorflow(GPU), Keras,batch size设为1都不行,跑一次CNN的前向传播都不行,一跑就kernel died。。。谁能给我解答一下?)
3、ESIM (Enhanced LSTM)
ESIM模型[2],也是Moka负责的,模型是一个自然语言推理网络,由输入编码,局部推理模型,和推断合成三部分构成。这个模型可以说是很强大了,是我们表现最好的模型(线上logloss0.165),对电脑的要求也很高。模型使用了双向LSTM,并引入attention机制。这里面跟前面两个模型不同的地方在于,没有直接计算两个句子表示向量的距离,而是让两个句子表示进行了匹配。这个我还没研究透彻,我得仔细看看论文,论文链接在下面,请各位客官享用。
(四)调参
以下是我在本次比赛中LSTM调参中的一些经验,有什么不对的地方或者更好的建议,欢迎各位指出或分享。
1、学习率
网上很多都在说学习率是最重要的参数,我并没有这个感觉。使用自适应的优化算法,如adam,只要结果没有崩掉(不收敛 、学习过慢等),调这个参数的带来的收益很小,后期我都直接使用Keras默认参数。
2、优化算法
推荐自适应的算法,adam, adadelta, adagrad, rmsprop, 带动量的sgd。使用这些算法的好处是可以使模型跳出鞍点和局部最小值,加速学习,收敛到更好的结果。
3、Batch Size
Batch size这个主要影响的是模型的学习效率,batch size的设置一般取2^n,同时也要跟电脑的内存相匹配,大了可能跑不动,小了会很慢,没有充分利用电脑的计算能力。
4、参数初始化
参数初始化非常非常重要,使用tensorflow感受太明显了,一个不合适的初始化会使得模型结果非常糟糕,推荐使用Xavier初始化,LSTM推荐使用正交初始化,forget bias初始化取大于等于1。
这个问题在Keras倒还好,Keras的默认初始化都是很好的初始化方法,因此使用Keras基本不需要调这个参数。
5、隐藏节点个数
这个试了好多,收益不大。
6、网络层数
调参最大的收益就是来自于这里了,两层LSTM比一层LSTM效果提升很多啊!!!
7、Dropout随机失活
为了防止模型过拟合,使用了dropout,网络神经元随机失活,使得权重能够分散到所有的神经元。LSTM的dropout一般竖向进行,效果会更好,如果没有丰富的经验,还是不要在循环层使用dropout,不好搞。
RNN Dropout8、BatchNormalization
BatchNormalization可以加速学习,并带有轻微正则化效果。关于LSTM如何做BN,可以参考这篇文章:CNN和RNN中如何引入BatchNorm
(五)特征工程
特征工程我们主要参考了Kaggle Quora Question Pairs第4名的方案[3],抽取了统计特征和图特征。我们在模型中用的特征不多,这里介绍一些比较有用和有趣的特征。
1、统计特征
(1)adjusted_common_word_ratio:两个句子中相同词的个数 / 两个问题文本长度较大的那个,引入tfidf权重系数进行调整。
(2)edit_distance:扩展的编辑距离(Damerau-Levenshtein Distance) / 两个问题文本长度较大的那个。编辑距离衡量的是两句话在图像上看上去是否相似。
(3)powerful_words: 指对判断两个句子是否相似影响较大的词(类似于关键词的概念)。
* pword_dside_rate:对于train中双侧出现的词,样本为1的数量占总样本的比例,反映一个双侧词对两句话是不是一个意思的影响,值越大,则label越可能是1。
* pword_oside_rate:对于train中单侧出现的词,样本为0的数量占总样本的比例,反映一个单侧词对两句话是不是一个意思的影响,值越大,则label越可能是0。
2、图特征
(1)Co-occurance based graph features
Co-occurance based graph根据这个共现图,统计节点的degree,得到了两个比较强的特征:coo_q1_q2_degree_diff(问题1和问题2的degree的差异)、coo_max_degree(问题对最大的degree,进一步离散化做1-hot又得到3个特征)。
(2)common_neighbors_count,common_neighbors_ratio:问题对公共邻居的数量/比例。
(六)模型集成
模型集成我们一开始采用了平均集成的方式,获得了很大的提升,最后我们的集成方案使用了加权平均,对于表现好的模型给与更多的权重,线上的logloss有提升,但没有达到我们的预期。由于每天只能提交一次,我们无法获得反馈,到底模型效果提升了多少,集成效果提升了多少。
我们的方案二是使用Xgboost做stacking,线下test logloss为0.137,但由于对它的泛化能力存在怀疑,我们最终没有提交这个方案,有点可惜。
强烈建议比赛举办方在比赛结束后开放一天提交(不计入比赛成绩),让我们测试一下模型效果,获得更多反馈。
(七)后处理
我们使用了图特征进行后处理。流程如下:
1、基于训练数据的问题相似关系建立无向图,把每个问题看出一个节点,如果两个问题相似就连一条边。
2、计算训练数据问题之间的距离,进行统计发现:
* label = 1 的 graph_distance = 1
* label = 0 的 graph_distance = 1000(表示不连通)
这说明不相似的问题不可能在一个连通图里(否则它们的距离会小于1000),由此推断:q1与q2不相似,则q1与q2的连通图CC(q2)的所有节点都不相似,q2与q1的连通图CC(q1)的所有节点都不相似。另有一个不太充分的结论: 相似问题具有传递性,而且可以传递很远。
3、基于以上发现总结出两条规律:
(1)如果q1和q2相似,q2与q3相似,则q1与q3相似;
(2)如果q1和q2不相似,q2和q3相似,则q1与q3不相似。
4、基于上面的规律,对test数据的预测进行了后处理,其中(1)处理了12568个样本,(2)处理了5129个样本,获得了很大提升,由于这个图特征发现得很早,在初赛时,我的logloss最高提升过0.04,随着模型的预测能力和集成效果的提升,图特征会有衰减,但依然给我们带来可观的提升。
关于这个图特征,是我这次比赛觉得最开心的事情之一,能够发现数据之间的联系,能够从正面和反面进行思考和推理,我觉得自己好像也有了一点洞察力了。
四、比赛总结
(一)比赛成绩
我们是moka_tree团队,在本次比赛中成绩如下:
* 初赛成绩第16名,logloss 0.1632,Top 5%;
* 复赛成绩第12名,logloss 0.1517。
初赛成绩,16/359 复赛成绩,12/95(二)代码分享
代码已上传至Github:https://github.com/LittletreeZou/Question-Pairs-Matching,欢迎查阅、提建议。
(三)经验总结
1、模型效果才是潜在收益最大的,从平时开始积累,关注最新研究,多逛社区(kaggle,github还有很多专业的公众号等),扩宽视野,不断尝试新的模型。
2、尽早进行stacking,并对效果进行测试,在初赛时要为复赛做好准备。
3、端对端模型前期表现很好,但到后期,提升就很困难了,特征工程要果断做。
4、Tensorflow具有清晰的流程,可以帮助你更好的理解深度学习的完整过程;Keras的易用性在模型网络调参时大大节约了写代码的时间,可快速验证很多想法。
5、图算法在本次比赛中有应用很多,对于图算法,在跑之前,一定要测试算法的时间复杂度,因为很有可能算法要跑好几天,而你傻傻地坐在电脑前等结果。
(四)心得体会
1、认识了我的队友moka,团队合作真的很重要,1+1 > 2;(单方面)认识了很多大佬,听了很多大佬的江湖传说,特别佩服。赏金猎人真的是太酷了惹!
2、动手做了很多事情,撸paper用github,虚心求教,把之前在Coursera学到的东西应用到了比赛中。纸上学来终觉浅,绝知此事要躬行。
(五)展望未来
这次比赛也发现了很多需要学习的东西,大佬们提到的模型、自然语言处理领域知识、编程等等。前路漫漫,步履不停。
我先抛转引玉了,希望大佬们能开源一波。
五、参考文献
[1] Semantic Question Matching with Deep Learning