赞
踩
本文简单介绍了问答任务相关类型和解法。
问答(Question Answering,QA)任务中首先会有一个问题(question),有些问题可能是没有什么争议,看一篇文章就能回答,比如“世界第一高峰是什么”;而有些问题就没有那么容易,需要从多处找资料,然后还需要进行整理,比如“世界第一高峰比世界第二高峰高多少米?”,此时需要找到世界第一高峰的高度和世界第二高峰的高度,然后还要做一个减法;还有些问题没有标准答案,比如“你妈和你对象一起掉进水里,你先救谁”。
问题答案的来源叫知识源(Knowledge source),可以是网络,可以使文本文件,甚至可以是语言或视频。
然后可以把知识源丢到某个模型里面,如果是文本的话,可以丢到BERT里面,得到每个token的嵌入向量,然后把问题也丢到BERT里面,得到问题每个token的嵌入向量。在计算嵌入向量时,知识源和问题可以互相计算Attention,然后把得到的这两个向量丢给另一个模型,得到问题的答案。
答案可以有多种形态,可以是一个单词;一个片段(span);选择题中的一个选项;甚至是分散在文章的段落之间。
我们就基于答案的形态来介绍QA任务。
最早的一个QA的基准语料库叫bAbI1,它有20种不同的问题。
对于这种问题,可以简单的看成是一个分类问题,所有可能的答案就是一个类别,从中选择概率最大的类别即可。
这里除了需要有模型处理source和问题外,还需要一个模型处理选项,得到选项中每个token的向量。然后这三个模型间可以互相做Attention。再把每个模型得到的向量都丢到计算答案的模型里面,输出是否为答案,这样需要为每个选项计算一次(可以是输出一个数值,选择数值最大的作为答案)。
还可以是把它也当成分类问题,预测时直接把所有选择丢到模型里面,然后输出概率最大的选项作为答案。
但是每次只输入一个选项,作为二分类问题的可伸缩性更好。因为不同的问题,可能的选项是不同的。
对于答案是一个片段(span)的问题,也叫基于抽取(Extraction-based)的问题。其中英文数据集最有代表性的是SQuAD2,解决这个xtraction-based QA问题的方法是,首先上面的Source是输入的文章,你的模型要计算输入文章中每个token作为答案起始位置的分数,选择分数最高的token,比如是 w 3 w_3 w3;然后还要作为结束位置的分数,这里最高的假设是 w 5 w_5 w5,然后答案片段就是 w 3 , w 4 , w 5 w_3,w_4,w_5 w3,w4,w5。
最常见的做法如上,拿两个模型,一个计算知识源中每个token的向量,另一个计算问题中每个token的向量。在计算过程中,这两个模型互相做Attention。接下来,拿到知识源每个token的嵌入向量,喂给一个解答的模型,计算每个向量作为答案起始位置的概率,和作为结束位置的概率。
在BERT之前,解答模型是由多层LSTM组成的,在BERT之后,解答模型起始就只有两个向量,分别与知识源每个token的向量计算点积,然后得到两排点积结果,分别选取每排结果中点积最大的token作为起始token和结束token。
还一种常见的情况是,答案是没有限制的,并且可能在文章里面,也可能不在文章里面。具有代表性的数据集,英文有MS MARCO3,中文有DuReader4。
上图是MS MARCO的例子,其中比较常见的情况是,答案的词汇在文章里面都找得到,但是这些词汇没有连在一起。所以上面介绍的Extraction-based方法也无法解决;还有一种情况是,答案的文字除了出现在文章里面,还出现在问题里面;也有可能是部分答案词汇没有出现在文章或问题里面;甚至还有的答案既没有出现在文章里面,也没有出现在问题里面(不过常见的是YES/NO问题)。
针对MS MARCO问题,直觉的想法是用seq2seq模型来产生答案。
其中最有代表性的是S-net5,它有针对问题的Question Encoder,还有针对文章的Passage Encoder。然后把问题和文章的向量喂给Decoder,就会输出答案。
我们已经介绍了好几种解决问答问题的模型,但是还存在一种情况就是,没有答案的问题。
基于这种情况,有个基准数据集,叫SQuAD2.06,就是为了测验机器处理无答案问题的能力。
该数据集中,有很多无答案问题。那碰到这种情况如何解决呢?
我们可以在文章的末尾加入一个特别的token,叫Null
,如果输出的答案是这个token,就代表无答案。其实在最原始的BERT7里面,就用[CLS]
token在这里来代表Null
。
其实还有很多其他做法,一个简单的做法就是,另外再训练一个模型来决定问题能不能被回答。
看了文章和问题后,来决定能否回答。还有另外一种做法是,基于上面的模型进行改进,在判断能否回答问题前,继续看模型的回答再做决定。
训练一个答案验证模型,把文章、问题和模型的答案当成输入,如果模型的答案非常荒谬(和问题不匹配),那么就输出不能回答。
我们本节来看一下知识源,最常见的知识源就是文章,但是常常碰到的情况是,我们也不知道答案在哪篇文章,所以比较常见的常见是,从网络中去获取答案。
此时整个英特网就是我们的知识源,上图是以维基百科为例,首先经过检索系统查询出一定数量相关的文章,然后再交给我们的模型去回答。
但是不是所有的文章都有答案,所以你的模型需要额外判断一篇文章是否有答案。
举例来说,V-Net8就尝试解决这个问题。它的做法是,输入一个问题,和多篇文章,每篇文章都给出一个答案,然后评估每个答案的确信度,同时结合投票机制,假设有10篇文章,有8篇都给出一样的答案,那么就选择该答案作为问题的答案。 哪怕有一篇文章给的答案确信度最高,但不在这8篇里面,我们也不能选。 这也很合理,因为总有些人错的离谱,但是十分肯定自己是对的。
现在我们把目光看向问题。
由上到下难度越来越高。最简单的是匹配和抽取问题,其次是推理问题,最难的是对话QA问题。
比如在SQuAD中比较常见的问题。匹配就是找到出现问题中相关词汇的句子,然后从中抽取答案。
用神经网络实现的经典做法是使用问题到文章内容的注意力(query-to-context Attention)。
首先有一个知识源,通常是一篇文章,然后把这篇文章丢到对应的模型里面,然后输出文章中每个token的嵌入向量(上图蓝色的向量)。 接着对于问题的部分也是如此,把问题丢到问题的模型里面,但是输出一个向量代表整个问题(BERT中的[CLS]
)。
然后用问题的向量与文章的每个向量做Attention,得到注意力权重。接着用这些权重对文章中的向量做加权和,得到一个新的向量。再把这个新的向量丢到解答模型(Answer Module)。也可以不对蓝色向量做加权和,可以让Source Module生成另外一批针对每个token得到的绿色向量,对它们进行加权和。
第一个使用这种方法的模型是端到端的记忆网络9,输入是问题和文章,当时是用文章的句子而不是单词当成单位来生成嵌入向量,然后有两个变换,一个用于制造计算匹配的向量,另一个用于制造计算加权和的向量。前者与问题计算注意力权重,然后与问题计算加权和,得到一个新向量。接着把新向量和问题向量加起来,丢到解答模型中,得到答案。
如果今天问题得到的是它每个token的嵌入向量,那如何计算Attention呢,其实也很简单,把每个token都计算一遍Attention。假设问题有3个token,那么对于每个token得到的注意力权重,取其中最大的一个即可。后面的步骤是一样的了。
我们上面看到的是用query对context计算Attention,也可以反过来做。
context里面每一个token都会用一个词嵌入向量来表示,然后用这个向量来对问题中的每个嵌入做Attention。得到对应问题中每个token的注意力权重,然后计算加权和,得到一个新的黄色向量。然后把这个黄色向量,与上面拿来计算Attention的蓝色向量做某种程度的连接操作(可以是直接相加,也可以是元素级相乘,也可以直接做Concat等)。
然后每个蓝色向量都会去做这件事,最终可以得到一排绿色向量。总之,这种做法就是把问题的信息,加到context里面来。接下来把绿色这排向量丢到解答模型,得到答案。
context-to-query的Attention是BERT之前非常主流的解法。
比较经典是是R-Net,感兴趣的可以自己去搜索下。
那到底是query-to-context好呢,还是context-to-query好呢。其实不需要纠结。可以同时使用。
BiDAF10同时使用了两种Attention。 h 1 ⋯ h T h_1\cdots h_T h1⋯hT代表context, u 1 ⋯ u J u_1 \cdots u_J u1⋯uJ代表query。左边用context的每个token对query去做Attention,右边反之。
对于每一个词汇,都会抽取词嵌入和字符嵌入向量。把字符向量通过一个CNN+最大化池,得到的向量和词嵌入向量合并起来,当成整个模型的输入。
然后把结果丢到LSTM里面去,然后LSTM输出起始位置和结束位置。
下面我们来看下BERT,它也是综合了query-to-context和context-to-query。
把知识源,比如文章和问题中间加入一个分隔符,丢到BERT里面,然后文章和问题都会得到一排嵌入编码。接下来解答模型会再文章的嵌入里面找答案的开始位置和结束位置。
为什么BERT的效果这么好呢,BERT里面有很多自注意模块,它可以做到context-to-query注意力和query-to-context注意力。还做了Self-matching(Self-Attention)。
BERT里面几乎过去所有QA模型有的机制。
更复杂一点的问题就是推理。
我们先来看下这类问题的著名的语料库。
其中有一个叫Qangaroo。比如问题是孟买的这个花园在哪个国家?
如果是传统的基于匹配抽取的方法是做不了的,因为没有一篇文章直接说这个花园是哪个国家的。
文章一说这个花园在孟买;文章二说孟买是印度的一个城市。
这个语料库的答案是选择题,从提供的选项里面选得正确答案。
还有另外一个语料库叫Hoppot QA,里面的问题是从文章里面抽出来的知识点。解答这些问题也需要看多个段落,不能通过匹配抽取的方法来解。
最后再介绍一个语料库,叫DROP。里面的很多问题像是数学的应用题。比如第一题。需要做一个减法。
而第四题需要在时间上做加法。注意这个语料库中的问题选择的都是BiDAF模型答错的问题。
这种需要推理的问题过去也是有的。比如在bAbI问题里面,问Greg的颜色是什么,但是只有句子说Greg是青蛙,Multiple-hop的解法是,抽出frog之后,继续改变我们的目标,改成去匹配出现frog的句子;然后第一个句子,中抽出Brian,再改变我们的目标,变成要匹配有Brian的句子;最终从第3个句子中抽出正确答案。
Memory Network里面就有Multi-hop机制,给定一篇文章,然后生成两种嵌入向量,一种用来做匹配,另一种用来做抽取。问题来了之后,先做匹配,再做抽取,把抽取出来的信息和原来的问题相加,得到一个新的对象。我们现在要匹配的变成了这个新的对象。但是要做几次这种改变(hop)呢?过去是一个预先设定好的值。
,
但是有一个网络叫ReasoNet试图让机器自己决定hop的次数。
近年来往往使用图神经网络来解这种Multiple-hop的问题。
在图神经网络中,首先抽取实体,然后把这些实体建立一个图结构。如果这些实体出现在同一篇文章里面,就连起来。出现在不同段落中的实体也连起来。然后假设问题里面出现了实体“Tom Clancy”,就从文章中找这个实体联系最多的其他实体,可能就是答案。
对话QA希望机器能回答一连串的问题。它有两个知名的基准语料库。一个是CoQA11。
其中主要是,先让机器读一篇文章,然后问机器一个问题,然后机器会给一个回答。接下来问第二个问题,可能是“Where”,这时需要机器能联系第一个问题去作答。
另一知名的语料库是QuAC12。这个语料库中特别的是,问的问题可能和文章无关。
这种问题最直觉的解法是,把每个问题看成是独立的。但是这种解法不会得到太好的结果。
有一种解法是,假设用的是BERT,把文档 D D D和问题 Q 1 Q_1 Q1丢到网络中,在第 l l l层产生一个嵌入向量, l + 1 l+1 l+1层又可以产生一个不同的嵌入。在解第二个问题 Q 2 Q_2 Q2的时候,通过RNN接入上一个问题的信息。
比如在第 l l l层上,不仅要看上一层的信息,还要看当前层上一个问题的信息。
还有一种做法是,采用注意力机制,在产生后面问题答案的时候,注意到同一层前面问题的信息。
Towards AI-Complete Question Answering: A Set of Prerequisite Toy Tasks ↩︎
SQuAD: 100,000+ Questions for Machine Comprehension of Text ↩︎
MS MARCO: A Human Generated MAchine Reading COmprehension Dataset ↩︎
DuReader: a Chinese Machine Reading Comprehension Dataset from Real-world Applications ↩︎
S-Net: From Answer Extraction to Answer Generation for Machine Reading Comprehension ↩︎
Know What You Don’t Know: Unanswerable Questions for SQuAD ↩︎
BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding ↩︎
Multi-Passage Machine Reading Comprehension with Cross-Passage Answer Verification ↩︎
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。