问答系统(QA)是非常热门也是很有前景的一个领域,学术界有很多人在研究,工业界也在积极寻求落地,因此本文对问答系统的发展现状和做法做了一个综述整理。这里有许多模型,笔记中把这些方法的特点进行归类。

一、基于知识图谱的QA

以知识图谱构建事实性问答系统,也称之为KB-QA,在业界是一种比较靠谱的做法,从知识图谱中寻找答案。对事实性问答任务而言,这种做法依赖于知识图谱,准确率比较高,同时也要求我们的知识图谱是比较大规模的,因为KB-QA无法给出在知识图谱之外的答案。KB-QA又可以分成两类:基于符号表示的KB-QA,基于向量表示的KB-QA。

基于符号表示的KB-QA

这种做法主要是利用语义解析的方法对问题进行解析,把问题转换成逻辑表达式,再加上一些规则,得到一个结构化的SQL查询语句,用来查询知识库得到答案。
语义解析的传统做法是:问题->短语检测->资源映射->语义组合->逻辑表达式
短语检测:词性标注、实体识别
资源映射: 实体链接、实体消岐、关系抽取
语义组合:将映射得到的资源进行组合,得到逻辑形式。
训练分类器:计算每一种语义解析结果的概率,再对于问答对计算损失函数。

现在的做法一般是:
首先是建图:包含知识库实体(圆角矩形,比如family guy),聚合函数(棱形,比如argmin),中间变量 y 和答案变量 x。

问答系统(QA) - 图1

对问题进行信息抽取:提取问题特征(问题词,问题焦点,问题主题词和问题中心动词),识别命名实体,进行词性标注来删除限定词和介词。
确定核心推导链:将自然语言问题,映射为一个谓语序列。
增加约束和聚合:增加约束和聚合函数相当于扩展查询图,缩小答案范围。
构建查询图特征:主题词链接特征,核心推导链特征,约束聚合特征,总体特征
分类器:对查询图做二分类,只有正确的查询图才是正样本。

基于向量表示的QA

知识表示是近几年很火的方向,就像词向量刚出来那会也发了很多论文,不过现在热度转向知识推理了。基于向量表示的KB-QA主要是对问题和答案学习到一个向量表示,然后进行向量匹配,归根到底就是个匹配问题。这种方法就是我当时差点要入的坑,后来因为项目比较繁琐 时间也不够,选择了基于文本的QA。
我们先把问题和候选答案都映射成向量。
如何学习问题向量:把问题用LSTM进行建模(因为问题本来就是自然语言)
如何学习答案向量:答案不能简单映射成词向量,一般是利用到答案实体,答案类型,答案路径(从问题主题词到答案的知识库路径),答案关系(从主题词到答案之间的知识库关系),答案上下文信息(与答案在一定范围内有连接的知识库实体),然后把这些特征分别映射成不同的向量,作为答案的其中一个向量(而不是直接拼接起来),最后用这些特征向量依次和问题做匹配,把score加起来作为总的score。

接下来我们要对问题和答案进行向量匹配,计算问题-答案score,常见的有,
最简单的方法:直接进行向量点乘,可以用CNN对这种方式做改进;
Attention匹配法:计算答案对问题每一步的Attention。

问答系统(QA) - 图2

我们的训练目标是:一般用Margin Loss,极大化问题对正确答案的score,同时极小化问题对错误答案的score。
当模型训练完成后,通过score进行筛选,取最高分的作为最终答案
另外,有一些论文加入了Multi-Task Learning,同时使用TranE去训练知识库。
也可以使用记忆网络来做,首先通过Input模块来处理问题,加入知识库信息,将三元组通过输入模块变换为一条一条的记忆向量,再通过匹配主语获得候选记忆,进行cos匹配来获取最终记忆,将最终记忆中的宾语输出作为答案。

现在来比较一下基于符号和向量的方法:
1)基于符号的方法,缺点是需要大量的人工规则,构建难度相对较大。优点是通过规则可以回答更加复杂的问题,有较强的可解释性。
2)基于向量的方法,缺点是目前只能回答简单问题,可解释性差。优点是不需要人工规则,构建难度相对较小。

因此目前可以改进的地方有:
1)复杂问句,目前End2End的模型只能解决简单问答。
2)多源异构知识库问答,对于开放域问答,单一的知识库不能完全回答所有问题。
3)训练语料,知识库中有实体和关系,除此之外还可能有描述实体的文本信息,或许可以结合结构化知识和非结构化文本,也就是第二部分要介绍的方法。

二、基于阅读理解的QA

对非结构化文章进行阅读理解得到答案,又可以分成匹配式QA,抽取式QA和生成式QA,目前绝大部分是抽取式QA。但是我有一段时间没有follow了,不知道能不能跟上时代的步伐,因为阅读理解花样很多,但是基本框架应该差不多的。

匹配式QA

给定文章,问题,和一个候选答案集(一般是实体或者单词),从候选答案中选一个score最高的作为答案。这种形式比较像选择题型,已经基本上没人做了。
具体的问题可以定义为多分类问题:

问答系统(QA) - 图3

其中d是document,q是query,a是answer,求概率最大的候选答案。注意:词汇表V 可以定义为 document 和 query 中的所有词,也可以定义为所有的 entity,或者定义为这篇document里面的词,而有的会直接提供包括正确答案在内的 N个候选答案。

重点在于求解g(d, q),g是对document和question建模得到的向量,把这个向量变化到词表空间再进行归一化可以得到所有候选score。求解g的方式比如:
1)LSTM Reader:直接把query跟document拼接起来,输入到双向LSTM中,最终得到一个向量g,这种做法非常粗糙,只是提供一个简单的baseline。
2)Attentive Reader:先对query用LSTM进行建模得到问题向量u,然后也对document建模,接下来用u 给document分配attention,去算文章向量r,再结合u和r得到g。相当于每读完一个问题,然后带着这个问题去读文档。
3)Impatient Reader:这里考虑到了query的每一个token,每个token都去算一个r。相当于每读问题的一个字,都要读一遍文档。(增加计算量)
4)Gated-Attention Reader:相当于带着一个问题,反复读文档,读k遍。

抽取式QA

目前认同度最高的应该是斯坦福的SQuAD数据集,数据形式是给定一篇文章,围绕这个文章提出一些问题,直接从文章中扣答案出来。这个数据集是基于维基百科爬取的真实文章,目前难度比较高的应该是TriviaQA数据集,在语义各方面难度都要超过squad。

常见的模型框架基本上是这样:

问答系统(QA) - 图4

Embedder:对词进行embedding
Encoder:分别对问题和文章用LSTM进行建模。
Interaction Layer:各种Attention机制花式结合问题和文章,对问题和文章进行交互,在原文上得到query-aware的文章表示。(差别主要在这一部分)
Answer Layer:用query-aware的文章表示来预测答案,一般是用两个网络分别预测答案起止位置,或者直接对文章进行答案标注。

这里简单介绍一下,
最简单的就是Match-LSTM:Interaction 层用的是中规中矩的Attention,用到两种预测答案的模式,其中Boundary Model 比较简单好用。

BiDAF 也比较出名:Interaction 层中引入了双向注意力机制,使用Query2Context 和 Context2Query 两种注意力机制,去计算 query-aware 的原文表示。另外在Embedder 层用到词级 embedding 和字符级 embedding,这倒不是什么新奇的做法,我记得很早就看过这种embedding了,比较适合英文。

FastQAExt 主打轻量级:在Interaction 层用了两种轻量级的信息 fusion 策略;Embedder 层加入了一些额外特征(这个确实有用);确定答案范围的时候用到了Beam Search(这个一般用在机器翻译的Decode测试阶段)

R-NET 也是曾经占领过leaderboard第一的模型:双 Interaction 层架构,分别捕捉原文和问题之间的关系(类似于match-lstm),原文内部各词之间的关系(self match);先预测答案开始概率,更新RNN状态,再预测答案截止位置。

生成式QA

目前只有MSRA的MS MARCO数据集,答案形式是这样的:
1)答案完全在某篇原文
2)答案分别出现在多篇文章中
3)答案一部分出现在原文,一部分出现在问题中
4)答案的一部分出现在原文,另一部分是生成的新词
5)答案完全不在原文出现(Yes / No 类型)

MSRA发布这个数据集后,也发布了S-Net,在R-Net基础上使用Multi task Learning,先抽取出答案后,利用这个特征再对文章生成答案。

问答系统(QA) - 图5
抽取部分:

问答系统(QA) - 图6
这个模型同时做了两个任务,预测答案ID,还有对文档进行排序。左下角是对问题建模,右下角是对文章建模,左上角是预测答案ID,右上角是对文章进行排序,对问题跟答案去算一个score,去做排序。但是我总觉得右下角画得不够严谨,这里其实是用到所有passages去预测ID,再对单个passage计算score,图中应该是只画了一个passage的多个词。

生成部分:

问答系统(QA) - 图7

前面花这么大力气标注出答案后,其实…..这里只是作为一个特征信息叠加到文章向量中,对这段文章和问题,重新通过Encoder建模(其实我觉得这里问题所用的LSTM,可以和前面抽取模型中的问题LSTM共享,不需要重新搞一下问题LSTM),得到一个综合的语义向量,再输入到Decoder中生成答案。就是一个简单的seq2seq模型。

这个论文我本来打算follow的,但是数据集处理实在麻烦,而且感觉模型也各种复杂(并不是说他难,而是说他模型庞大,我感觉我可能跑不动)。然后我刚刚查了一下这个论文的应用,现在渐渐有人去follow这个工作了,比如加上强化学习,同时修正问题和生成答案。OK,这个坑,就留给别人填吧。然而……有点尴尬的是,自家的数据集,被百度和猿题库刷到前面了,可能MSRA最近也在憋大招,毕竟是长期占领SQuAD排行榜第一的人。

三、基于多轮对话的QA

由于在对话问题上,需要想办法去规避或者缓解生成式QA带来的评估以及控制的问题。所以,这就是工业界偏爱检索式对话系统的原因了。

检索式QA

检索式对话系统的一般框架如图所示。

问答系统(QA) - 图8
检索式对话可以用于解决闲聊型对话或FAQ问答型对话问题。对于FAQ问题,大部分情况下单轮对话就能解决问题,而闲聊型对话就要依赖上下文了,否则容易出现前后话语矛盾的情况(虽然现在聊天一致性问题依然是一个未解难题)。
FAQ问题一般是限定域,相当于容易解决。但是,闲聊型问题一般是不限定对话范围的,也就是说是开放域的,那么这时候存放query-response pairs(上图中的message-response pairs)的语料库必须要是非常大非常丰富的,否则难以应对用户千奇百怪的问题和回复,这时候语料库往往要从一些开放的社交网站上爬取,如Twitter、微博、贴吧等。
显然我们不可能让一个深度神经网络在每次寻找回复时遍历整个语料库(可能9102年的计算机可以做到),因此在用神经网络深度匹配合适的回复之前,一般要先经过一个“粗筛”的模块召回若干相关的回复,减少q-r匹配的工作量。这个模块一般将用户当前轮的query与语料库里query进行快速匹配(当然你也可以加更多feature提高合适回复的召回率),得到几十上百个候选回复,完成第一轮的匹配。
显然这就要求q-q粗召模块非常轻量级且匹配的相关度可接受,因此一些常用的信息检索模型如BM25以及一些轻量级文本特征表示模型如bow就派上用场了。小夕这里推荐一个专业的开源文本匹配工具AnyQ[6],不仅将上面的常用模型集成进来,方便“一键调用”,而且部署阶段做了非常多的优化,所以想自己搭检索式对话/问答系统的小伙伴就不用在该模块上重复造轮子啦。
有了若干相关的候选回复后,我们假设其中一定至少存在一个合适的回复,那么就需要一个非常精良的q-r精排模块来将这个合适的回复挑出来。这部分也是学术界研究主要focus的点。在多轮对话匹配这一方面百度的对话团队做了大量的工作,属于长期霸榜的状态,比如Multi-view、DAM以及DGU。
如果说生成式对话是在茫茫实数语义空间中寻找那几个合适的回复,那么检索式对话就是给你有限数量的选项,让你选出里面最合适作为回复的选项(有没有联想到word2vec与语言模型的关系)。显然做选择题要比大海捞针容易多了。而且生成式系统中,合适的回复是位于一个抽象、不可解释的实数语义空间,我们当然很难直接干预捞针范围,而在检索式系统中,合适的回复是在一堆干扰回复中,我们要滤掉那些干扰回复或者缩小搜索范围非常容易——给q-q粗召模块添油加醋甚至直接对语料库开刀就好啦╮(╯▽╰)╭
然而,很多时候我们的对话是带有目的性的。虽然检索式对话与生成式对话系统可以为每个query找到合适的回复,但是这些模型是在纯文本语料上训练的,无力建模对话过程中的意图或者发出一个结构化的动作,比如

“小夕你帮我看看现在几点啦”小夕:“好呀好呀”

然后没了。。。显然用户期待的并不是“好呀好呀”就结束了,而是当前时刻具体的时间!而要想知道当前的时间,就必须要调用一些服务/API(不然你觉得对话语料库中会写着当前时刻么)去查询一下,显然这些隐式的操作不可能出现在检索式对话或生成式对话模型的训练集中。
更不用说,如果对话开始之前你的目的就很明确——比如找到一家适合今晚约约约的餐厅并预定上座位,那么整个对话的过程一定都是全程高能的(需要频繁的查询数据库,记录和更新前面对话的关键信息),任何一个检索式和生成式对话系统都会崩溃的。

路人:小夕,帮我推荐一家餐厅吧~小夕:好呀好呀,几个人呢~路人:2个人哦(小夕赶紧拿出本本记下)小夕:好的哦,今天中午吗?路人:不是呢,今天晚上~差不多6点左右到(小夕赶紧记下)小夕:好滴,想吃中餐还是西餐呢?路人:想吃牛排!算了算了都快200斤了,而且单位附近貌似没有,那就。。吃椰子鸡吧!(小夕努力理解了一下,然后打开百度地图APP开始寻找附近的椰子鸡)小夕:找到一家!距离我们2.4km!

怎么解决这类问题呢?显然这类对话问题已经远远超出了文本匹配和文本生成问题的范畴,于是就有了“任务完成型对话”这个重要问题。

任务完成式QA

与闲聊为目的的对话有很大的不同,任务完成型的对话的最终目标是完成任务,就相当于打通关一个游戏一样,需要在每一轮对话都采取合适的决策,保证自己不死掉(比如被用户骂死),然后慢慢的打怪升级直到打败最终boss,用户送出好评。
显然这就是一个多步决策求取reward(对话目标完成情况)最大化的问题了,也就是一个RL问题。这时候完全使用有监督学习的话显然建模起来是有缺陷的,难以从整个对话session(一次完整的对话过程)的层次上去优化每轮对话的决策。
啰嗦一下举个栗子,比如这里有个邮件助手,它的action空间中有很多操作,比如add邮件、delete邮件和edit邮件等,那么显然误决策到edit的代价要远远小于误决策到delete的代价,毕竟错误的选择编辑邮件不会直接导致对话终止,而是用户会给你纠错的机会,但是误决策把邮件删了,用户就肯定骂街了。因此通过RL学习到的任务完成型对话系统可以aware到“谨慎”决策,会关注长远的对话目标的实现;而使用有监督学习的话,这些actions是同等权重的,网络只会满足于当下模式匹配。

当然啦,RL并不只是作用于任务完成型对话问题上,在其他对话问题如问答型( 信息获取)[36,41]和闲聊型对话[20]上依然是适用的。总之做对话的话,是很难完全离开RL的,不管有没有全局目标,毕竟多轮对话和用户交互问题就放在这里呢╮( ̄▽ ̄””)╭