R-Net:问答系统机器阅读理解
R-Net:问答系统机器阅读理解摘要:问答系统在当前学术界和工业界都非常具有研究和应用价值的任务,本文分享一篇2017年的端到端的问答系统经典之作——R-Net。该工作在当时的SQuAD1.1的测试集上达到最优结果。一、SQuAD SQuAD是斯坦福NLP开放的一个机器阅读理解(文档问答系统)的评测数据集,最初是SQuAD1.1版本,现如今已经根据学术界的意见更新到SQuAD2.0版本。该数据集
R-Net:问答系统机器阅读理解
摘要:问答系统在当前学术界和工业界都非常具有研究和应用价值的任务,本文分享一篇2017年的端到端的问答系统经典之作——R-Net。该工作在当时的SQuAD1.1的测试集上达到最优结果。
一、SQuAD
SQuAD是斯坦福NLP开放的一个机器阅读理解(文档问答系统)的评测数据集,最初是SQuAD1.1版本,现如今已经根据学术界的意见更新到SQuAD2.0版本。该数据集开源的官网是SQuAD2.0,只提供训练集和验证集,提交模型后会在测试集上进行验证,可以参与刷榜排名。本文分享的R-Net模型只是在SQuAD1.1上进行,因此我们主要借鉴R-Net的思路。
二、问题定义
基于文档的问答系统,在一定程度上也属于阅读理解。即先给定一个上下文passage,再给定一个问句,训练一个模型从上下文中寻找正确答案。SQuAD1.1版本的数据集保证正确答案完全出现在上下文中,所以本文R-Net的任务是预测正确答案的起始范围,即answer span。
设问句表示为 Q = { w t Q } t = 1 m Q=\{w_t^Q\}_{t=1}^{m} Q={wtQ}t=1m,上下文表示为 P = { w t P } t = 1 n P=\{w_t^{P}\}_{t=1}^{n} P={wtP}t=1n ,对应使用词向量 e e e 表示分别为 Q = { e t Q } t = 1 m Q=\{e_t^Q\}_{t=1}^{m} Q={etQ}t=1m, P = { e t P } t = 1 n P=\{e_t^{P}\}_{t=1}^{n} P={etP}t=1n 。
二、R-Net
R-Net主要分为四个主要部分,分别是:
- 循环神经网络编码器(Recurrent Network Encoder):主要对问句(question)和文档(passage)进行编码;
- 门控匹配层(Gated Matching Layer):将答案与上下文进行匹配;
- 自匹配层(Self-matching Layer):对整个上下文进行信息聚集;
- 指针网络(pointer network):使用指针网络预测正确答案的起始位置。
模型的整体结构如下图所示:

2.1 循环神经网络编码器(Recurrent Network Encoder)
在该层,主要任务是对问句和上下文进行表征,分别使用两个参数不共享的GRU神经网络进行编码:
u t Q = B i G R U Q ( u t − 1 Q , [ e t Q , c t Q ] ) u_t^Q=\operatorname{BiGRU_Q}(u_{t-1}^Q, [e_t^Q, c_t^Q]) utQ=BiGRUQ(ut−1Q,[etQ,ctQ])
u t P = B i G R U P ( u t − 1 P , [ e t P , c t P ] ) u_t^P=\operatorname{BiGRU_P}(u_{t-1}^P, [e_t^P, c_t^P]) utP=BiGRUP(ut−1P,[etP,ctP])
其中 u t Q u_t^Q utQ 和 u t P u_t^P utP 则分别表示问句和上下文的编码(一个矩阵)。
2.2 门控匹配层(Gated Matching Layer)
为了模拟人类,在做阅读理解时都是带着问题去读文章,因此需要对文章Passage的各个token进行编码时,都要带着问题,即question-aware passage representation。因此主要编码过程如下所示:
v t P = BiGRU ( v t − 1 P , [ u t P , c t ] ) v_t^P = \operatorname{BiGRU}(v_{t-1}^P, [u_t^P, c_t]) vtP=BiGRU(vt−1P,[utP,ct])
s j t = v T tanh ( W u Q u j Q + W u P u t P + W v P v t − 1 P ) s_{j}^{t}=\mathrm{v}^{\mathrm{T}} \tanh \left(W_{u}^{Q} u_{j}^{Q}+W_{u}^{P} u_{t}^{P}+W_{v}^{P} v_{t-1}^{P}\right) sjt=vTtanh(WuQujQ+WuPutP+WvPvt−1P)
a i t = exp ( s i t ) / Σ j = 1 m exp ( s j t ) a_{i}^{t}=\exp \left(s_{i}^{t}\right) / \Sigma_{j=1}^{m} \exp \left(s_{j}^{t}\right) ait=exp(sit)/Σj=1mexp(sjt)
c t = Σ i = 1 m a i t u i Q c_{t}=\Sigma_{i=1}^{m} a_{i}^{t} u_{i}^{Q} ct=Σi=1maituiQ
g t = sigmoid ( W g [ u t P , c t ] ) g_{t}=\operatorname{sigmoid}\left(W_{g}\left[u_{t}^{P}, c_{t}\right]\right) gt=sigmoid(Wg[utP,ct])
[ u t P , c t ] ∗ = g t ⊙ [ u t P , c t ] \left[u_{t}^{P}, c_{t}\right]^{*}=g_{t} \odot\left[u_{t}^{P}, c_{t}\right] [utP,ct]∗=gt⊙[utP,ct]
其中 v t v_t vt 则表示带有问题去读文章的意思,使用GRU对文章再次编码,但每次编码时不是单纯将passage的token喂入,而是使用门控注意力机制来完成,即在passage的第 t t t 时刻,都与所有问句 Q Q Q 的每个token计算一次权重,并对 Q Q Q 进行加权求和得到 c t c_t ct。为了充分保留更多的信息,可以将 [ u t P , c t ] [u_t^P, c_t] [utP,ct] 一同代入GRU中计算。
该段公式对应的代码编写时需要注意,例如使用pytorch时,则需要使用 torch.nn.GRUCell(),其可以灵活的为每个时间步计算。
2.3 自匹配层(Self-matching Layer)
在2.2 部分得到的 v t P v_t^P vtP 只是全局的表征信息,就好比人在带着问题阅读时,只是单纯的将整个文章读完了,但此时的记忆是比较杂乱的,需要对看过的信息进行整合,因此自匹配层的目标就是将重要的信息聚集起来,该部分非常类似于 Transformer中的self-attention。
h t P = BiGRU ( h t − 1 P , [ v t P , c t ] ) h_{t}^{P}=\operatorname{BiGRU}\left(h_{t-1}^{P},\left[v_{t}^{P}, c_{t}\right]\right) htP=BiGRU(ht−1P,[vtP,ct])
s j t = v T tanh ( W v P v j P + W v P ~ v t P ) s_{j}^{t}=\mathrm{v}^{\mathrm{T}} \tanh \left(W_{v}^{P} v_{j}^{P}+W_{v}^{\tilde{P}} v_{t}^{P}\right) sjt=vTtanh(WvPvjP+WvP~vtP)
a i t = exp ( s i t ) / Σ j = 1 n exp ( s j t ) a_{i}^{t}=\exp \left(s_{i}^{t}\right) / \Sigma_{j=1}^{n} \exp \left(s_{j}^{t}\right) ait=exp(sit)/Σj=1nexp(sjt)
c t = Σ i = 1 n a i t v i P c_{t}=\Sigma_{i=1}^{n} a_{i}^{t} v_{i}^{P} ct=Σi=1naitviP
在聚集时,聚集的信息只与passage有关。此时使用一层双向GRU进行编码,每到一个时刻 t t t 时,便于整个passage的所有token进行权重计算和加权求和,得到 c t c_t ct,再与 v t P v_t^P vtP 结合起来进入下一时刻。
2.4 指针网络(pointer network)
模型的目标是预测正确答案所在passage中的起始位置区间,因此可以使用指针网络。具体的来讲,指针网络主要用于sequence2sequence任务,因此本文可以将指针网络视为由passage表征后得到的 h t P h_t^P htP 作为输入转换为只有2个序列长度的输出,一个是起始start位置,一个是终止end位置。
第(1)步:指针网络是一个单向的GRU,因此首先对于隐层要有一个初始化,相当于decoder部分初始化。本文初始化使用的是 r Q r_Q rQ, 表示为
s j = v T tanh ( W u Q u j Q + W v Q V r Q ) s_{j}=\mathrm{v}^{\mathrm{T}} \tanh \left(W_{u}^{Q} u_{j}^{Q}+W_{\mathrm{v}}^{Q} V_{r}^{Q}\right) sj=vTtanh(WuQujQ+WvQVrQ)
a i = exp ( s i ) / Σ j = 1 m exp ( s j ) a_{i}=\exp \left(s_{i}\right) / \Sigma_{j=1}^{m} \exp \left(s_{j}\right) ai=exp(si)/Σj=1mexp(sj)
r Q = Σ i = 1 m a i u i Q r^{Q}=\Sigma_{i=1}^{m} a_{i} u_{i}^{Q} rQ=Σi=1maiuiQ
即问句在表征层得到的向量进行加权求和;
第(2)步:根据初始化的隐向量 h t − 1 a = r Q h_{t-1}^a=r^Q ht−1a=rQ,可以得到在passage中start位置的概率分布
s j t = v T tanh ( W h P h j P + W h a h t − 1 a ) s_{j}^{t}=\mathrm{v}^{\mathrm{T}} \tanh \left(W_{h}^{P} h_{j}^{P}+W_{\mathrm{h}}^{a} h_{t-1}^{a}\right) sjt=vTtanh(WhPhjP+Whaht−1a)
a i t = exp ( s i t ) / Σ j = 1 n exp ( s j t ) a_{i}^{t}=\exp \left(s_{i}^{t}\right) / \Sigma_{j=1}^{n} \exp \left(s_{j}^{t}\right) ait=exp(sit)/Σj=1nexp(sjt)
p t = argmax ( a 1 t , … , a n t ) p^{t}=\operatorname{argmax}\left(a_{1}^{t}, \ldots, a_{n}^{t}\right) pt=argmax(a1t,…,ant)
最终 p t ( t = 1 ) p^t (t=1) pt(t=1) 就是预测的start位置 p 1 p^1 p1;
第(3)步:预测完第一个位置之后,保存第一个位置的权重分布 a i t a_i^t ait,并使用单向GRU计算一次,得到第二个位置的隐状态:
c t = Σ i = 1 n a i t h i P c_{t}=\Sigma_{i=1}^{n} a_{i}^{t} h_{i}^{P} ct=Σi=1naithiP
h t a = RNN ( h t − 1 a , c t ) h_{t}^{a}=\operatorname{RNN}\left(h_{t-1}^{a}, c_{t}\right) hta=RNN(ht−1a,ct)
计算结束后,再次回到第(2)步,此时 t = 2 t=2 t=2,可以得到end位置 p 2 p^2 p2。.
最后模型在训练时,在预测每个位置时,passage中所有token都对应一个one-hot向量,其中1表示该位置是正确的start/end位置,其他位置都是0,可以用概率分布 a i t a_i^t ait 来计算交叉信息熵损失。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)