一、前言
最近因为做对话机器人的原因,看了一下seq2seq。不禁感慨,自由对话机器人的水好深呀。
查阅了一些市面上能看到资料,工业上的做法,普遍是 基础模板(例如 aiml)+IR闲聊库(例如 小黄鸡语料QA)+爬虫(百度、搜狗)+知识图谱(wiki百科)+对话生成模型。
aiml模板就不说了,网上有很多的资料,效果上来说,比较智障。人工编写模板的工作量也大,也不能覆盖很多的回答。
IR闲聊库的方法,理论上来说,面对单轮闲聊的话,能产生流畅的回复,通俗的说就是能说人话,毕竟是从正常的对话记录中去检索的。实现上的话,方式就很多了,词面上的编辑距离,tfidf,bm25等等。模型上就是Response Selection in Retrieval-Based Chatbots这类的。不过后面出现了多轮的Retrieval-Based Chatbots,诸如:SMN(Sequential Matching Network)、DAM(Deep Attention Matching Network)、IMN(Interactive Matching Network)和MRFN(Multi-Representation Fusion Network)这些多轮检索式对话。因为,不是本文所要讨论的话题,所以,具体的大家去看论文吧。
参考:https://zhuanlan.zhihu.com/p/65062025
https://zhuanlan.zhihu.com/p/46366940
https://www.jianshu.com/p/01a0ec0370c4
爬虫类的,就不说了,具体的大家可以看一下:QA-Snake(https://github.com/SnakeHacker/QA-Snake)
图谱的话,网上资料很多,我这里简单说一下我知道的一般的实现方案:
1)NER,获取需要的实体词,具体可以分为非监督的实现(模板,词典),监督的实现(lstm+crf,crf)。
2)将得到的实体词,送入图谱检索到可能三元组集合。
3)将三元组集合的属性和问句进行打分(其实就是做一个2分类),选出合适的三元组。
二、seq2seq
接下来,就是本文主要说的内容了seq2seq。seq2seq也就是encoder2decoder。
Seq2Seq(Sequence to Sequence), 就是一种能够根据给定的序列,通过特定的方法生成另一个序列的方法。它被提出于2014年,最早由两篇文章独立地阐述了它主要思想,分别是Google Brain团队的《Sequence to Sequence Learning with Neural Networks》(https://arxiv.org/pdf/1409.3215.pdf)和Yoshua Bengio团队的《Learning Phrase Representation using RNN Encoder-Decoder for Statistical Machine Translation》(https://arxiv.org/pdf/1406.1078.pdf)。
同年9月,随着Bahdanau的《Neural Machine Translation by Jointly Learning to Align and Translate》(https://arxiv.org/pdf/1409.0473.pdf)横空出世,也就是大名鼎鼎的NMT模型,随着attention被引入到seq2seq模型,从而解决原来rnn长序列的long-term问题,模型准确度随之得到提升。
下面,我们根据Pytorch的官方文档https://pytorch.org/tutorials/beginner/chatbot_tutorial.html,通过GRU+Attention的样例来熟悉一下seq2seq的模型吧。
seq2seq模型的输入是一个变长的序列,而输出也是一个变长的序列。而且这两个序列的长度并不相同。一般我们使用RNN来处理变长的序列,一般来说两个RNN可以解决这类问题。
Encoder是一个RNN,它会遍历输入的每一个Token(词),每个时刻的输入是上一个时刻的隐状态和输入,然后会有一个输出和新的隐状态。这个新的隐状态会作为下一个时刻的输入隐状态。每个时刻都有一个输出,对于seq2seq模型来说,我们通常只保留最后一个时刻的隐状态,认为它编码了整个句子的语义。Encoder处理结束后会把最后一个时刻的隐状态作为Decoder的初始隐状态。
但是后面我们会用到Attention机制,它还会用到Encoder每个时刻的输出。
官方文档中采用的是双向Gated Recurrent Unit(GRU)作为Encoder。
这里需要说一下一般都会说的小知识点:
1)因为RNN机制实际中存在长程梯度消失的问题,seq2seq引入attention
2)使用拥有gate的lstm避免RNN的梯度消失问题
3)GRU因为使用更少的门,模型参数更小,因为比lstm效率好
Decoder也是一个RNN,它每个时刻输出一个词。每个时刻的输入是上一个时刻的隐状态和上一个时刻的输出。一开始的隐状态是Encoder最后时刻的隐状态,输入是特殊的。然后使用RNN计算新的隐状态和输出第一个词,接着用新的隐状态和第一个词计算第二个词,...,直到遇到EOS,结束输出。
三、attention
因为上面小知识点第一个所说的问题,是当输入句子很长的时候,encoder的效果会很差,利用隐状态(context向量)来编码输入句子的语义实际上是很困难的。为了解决这个问题,Bahdanau等人提出了注意力机制(attenti