从易到难,针对复杂问题的无监督式问题分解方法
从易到难,针对复杂问题的无监督式问题分解方法
本文介绍了一种无监督的问题分解方法,用于解决复杂问答任务。该方法首先通过伪分解构造生成问题对,然后学习问题分解映射,最后将分解结果应用于复杂问题的解答。实验结果表明,该方法在HotPotQA数据集上取得了显著效果,接近当前最佳的强监督模型。
问答中的复杂问题
针对问答系统的研究已经有了多年历史,在近些年,各类数据集的出现对问答系统的要求越来越高。如数据集HotPotQA就要求系统能够在多段不连续的文本中寻找线索,并且按照逻辑顺序把这些线索组织起来形成答案。这种需要多次答案“跳跃”的问答形式就称为“多跳问答”(Multi-Hop QA),或者简单地称为复杂问题。
模型直接回答这些问题往往不现实,但一个简单的想法是,可以把这个复杂问题分解成若干模型能够回答的简单的子问题,然后再把这些子问题及其答案作为上下文提供给模型,再去回答复杂问题,这样就可以大大降低直接回答复杂问题的难度。
比如下图是一个例子,要回答复杂问题,我们先从中拆分出两个简单问题(或“单跳问答”),回答之后再回答复杂问题本身,就容易得多。
无监督问题分解
问答任务是,给定问题 (q) 和若干段文本 (c),模型 (M) 要去找到答案 (a),即最大化概率 (P(a|q,c))。
而问题分解就是,找到和 (q) 相关的 (k) 个子问题 (q_1, q_2, ..., q_k),并得到它们的答案 (a_1, a_2, ..., a_k),模型现在要基于以上所有信息去最大化答案的概率 (P(a|q, c, q_1, a_1, ..., q_k, a_k))。
所谓问题分解,指的就是复杂问题到子问题集的一个映射:(f: q \rightarrow (q_1, q_2, ..., q_k)),其中 (q_i) 是从简单问题数据集中得到的一个问题。我们首先需要构造这样的映射对,然后学习这个映射,最后应用它。
Step1: 伪分解构造
假设我们现在有两个数据集:复杂问题数据集 (Q_c)(本文使用HotPotQA中的所有问题作为 (Q_c))和简单问题数据集 (Q_s)(使用SQuAD2的所有问题作为初始的 (Q_s))。
为了进一步扩大这两个数据集的大小,我们从Common Crawl中挖掘更多的问题补充 (Q_c) 和 (Q_s),最终得到约2.4M句复杂问题,10.1M句简单问题。
另一方面,因为我们没有标注好的 (q \rightarrow q_i) 对,所以我们想要先构造出这样的问题对 (q \rightarrow q_i),然后在此之上学习映射 (f)。这种构造我们称为“伪分解”构造。
具体来说,我们对每个复杂问题 (q),从 (Q_s) 中选取 (k) 个简单问题作为伪分解,这些选取的简单问题需要尽量满足两个标准:和 (q) 相似,相互不同。这两个标注可以统一为下述公式:
[ \text{score}(q, q_i) = \frac{\cos(q, q_i)}{\sum_{j \neq i} \cos(q_i, q_j)} ]
下面给出两种抽取子问题的策略(本文设 (k=3)):
相似度抽取:使用余弦值改写上述公式为(其中 (q, q_i) 为单位句向量,用fastText得到)
[ \text{score}(q, q_i) = \frac{\cos(q, q_i)}{\sum_{j \neq i} \cos(q_i, q_j)} ]随机抽取:随机中 (Q_s) 中抽取 (k) 个简单问题作为 (q_i)。
在抽取了若干子问题后,由于其中包含了很多噪声,所以我们还把其中没在 (Q_c) 中出现的实体都随机替换为 (Q_s) 中同类型的一个实体(如果存在)。如此一来,我们就得到了很多 (q \rightarrow q_i) 伪分解对。
Step2: 问题分解映射学习
在上述得到伪分解对 (q \rightarrow q_i) 之后,我们就要去学习分解映射 (f),我们有以下学习策略:
- 不学习(No Learning),直接使用抽取得到的 (q_i) 作为复杂问题 (q) 的子问题;
- 序列到序列(Seq2Seq),把所有的 (q_i) 拼接起来作为目标文本 (t),学习一个 (q \rightarrow t) 的序列到序列模型 (M_f);
- 无监督序列到序列(USeq2Seq),不直接从 (q \rightarrow q_i) 中学习,而是用各种无监督学习手段去学习。
对Seq2Seq和USeq2Seq,我们首先在 (Q_s) 上用MLM的方法预训练一个XLM去初始化,然后用上述伪分解对训练。
对Seq2Seq,直接用 (q \rightarrow q_i) 对训练;对USeq2Seq,用去噪和反译的方法训练。对去噪,把 (q_i) 中字符随机掩码、交换、丢弃,得到 (q'_i),然后训练 (M_f(q) \rightarrow q'_i)。对反译,分别用 (M_f) 生成一个 (q_i) 和用 (M_f^{-1}) 生成 (q),然后去优化 (M_f(M_f^{-1}(q)) \rightarrow q)。
Step3: 如何使用
在学习了映射 (f) 之后,我们就可以针对每个复杂问题 (q),得到它的分解后的子问题 (q_1, q_2, ..., q_k),然后用一个简单的QA模型找到每个子问题的答案,再把这些问题和对应的答案拼接起来,和原来的复杂问题、给定的上下文一道,送入最后的QA模型,得到复杂问题的答案即可。
具体地,我们使用RoBERTa(Large)和BERT(Base)作为简单问答模型,并且也使用了模型集成。类似地,对回答复杂问题我们也用同样的模型。
实验
我们在数据集HotPotQA上实验,测评指标有F1和EM,其他实验设置详见原文。
下表是实验结果,第一列是分解学习方法,第二列是分解构造方法。可以看到,和基线模型比较,在原始版本(Orig)上得到3F1的提升,在多跳(MultiHop)和领域外(OOD)版本上,得到10F1的提升,并且还能实现强监督模型DecompRC的效果。
在测试集上,本方法能接近当前最佳的强监督(额外的监督信号,知道哪些句子能回答问题)模型SAE和HGN。在后面的实验中,我们使用Useq2seq+fastText的方法。
下面我们想要知道提供怎样的上下文信息可以使模型更好。我们有几种选择:(1)简单的答案段(span);(2)答案所在的一整句话(sentence);(3)随机实体。
下表是实验结果,可以看到,相比提供子问题,子答案的选择更为关键。其中,提供一整句话而非仅仅是答案本身可以大幅提高效果,这说明,充足的额外的上下文对模型至关重要。
接下来我们探究子问题和子答案对最终结果的影响。如下图所示,左图是使用beam search生成的子问题的置信度对最终结果的影响,右图是子答案置信度对最终结果的影响。可以看到,置信度越大的子问题或子答案能最大化提升最终的结果。
最后来看看具体分解的子问题及其得到的答案的例子,如下表所示。可以看到,尽管有些子问题语法不通,但这并不妨碍简单模型找到相关答案,并为复杂模型提供有效的信息。
小结
本文提出了一种无监督式的“问题分解-分解学习-分解应用”的将复杂问题分解为简单问题从而提供丰富上下文信息的问答范式,在HotPotQA上能够接近当前最佳的强监督模型,并通过一系列分析实验验证了此方法的有效性。
这种方法的本质是为模型提供更多的上下文信息,非常类似我们推出的一系列工作——将各种任务归纳到阅读理解框架下。实际上这种想法也是很直观的:人类也无法只从一句话中推出很多信息,总是需要各种各样的背景信息作为支撑。从这个观点看,未来拓展模型能够处理的文本长度也许会是一种趋势。