BERT在MRC任务上已经达到了很高的效果,但是缺点在于BERT的输入最多只能512个单词。而对于MRC任务来说,有的数据集的文章特别长。因此想要用BERT处理这类数据集,就必须将文章切分开。每一篇文章与问题独立的构成一个example,也就是一个样本作为BERT的输入。
拿BERT的源码举例:

import collections
length_of_doc_tokens=700#文章的长度为700
max_seq_length=512
length_of_query_tokens=9#问题长度为9
max_tokens_for_doc=max_seq_length-length_of_query_tokens-3#减去问题长度,[CLS],[SEP],[SEP]后才是文章的最大长度
_DocSpan=collections.namedtuple("DocSpan",["start","length"])
doc_spans=[]
start_offset=0
doc_stride=128#doc_stride指的是在切分文章时的滑动步长
while start_offset<length_of_doc_tokens:length=length_of_doc_tokens-start_offsetif length>max_tokens_for_doc:#剩余的文章长度大于文章可以传进BERT的最大长度length=max_tokens_for_docdoc_spans.append(_DocSpan(start=start_offset,length=length))if start_offset+length==length_of_doc_tokens:break#当偏移位置加上切分的文章长度等于该文章的长度时就可以退出了start_offset+=min(doc_stride,length)#从滑动步长和剩余文章长度中选出最小的#通常来说剩余文章的长度都是要小于doc_stride的
print(max_tokens_for_doc)#500

我们来一步一步的计算切分过程:

  • 首先start_offset=0,length=700>max_tokens_for_doc,所以第一次的切分就是[0-500],由于length=500>doc_stride=128,所以start_offset=128
  • 第二次start_offset=128,length=700-128=572>max_tokens_for_doc,所以第二次的切分就是[128-628],由于length=500>doc_stride=128,所以start_offset这时等于256
  • 第三次start_offset=256,length=700-256=444<max_tokens_for_doc,所以第三次的切分就是[256-700]
    因此第一次切分的文章长度为500,第二次切分的文章长度为500,第三次剩下的文章长度为444


看来是没有错的。doc_stride设置为321,那么此时的切分结果如下:

显然第一次是从0–500,第二次是从321–700

切分后的每一篇segment与问题构成一个样本作为BERT的输入,比如在文章长度为700,doc_stride为128的情况下,会切分出来3个segment,于是也就和同一个问题组成了三个example,需要注意的是如果某个segment不包含有答案,那么这个segment是不用的。

之前的做法有两个弊端:

  • 以固定长度分割文章,那么就可能导致某些分割后的文章所包含的答案的上下文不充分,也就是指答案在分割的文章中的位置比较靠近边缘。

论文的图1鲜明的指出当答案的中心词位置与文章的中心词位置之间的距离影响着模型预测答案位置的准确率。

如下图所示,假如红色的单词是答案,绿色的单词是,那么当前的文章就包含有充足的要预测答案的上下文

下图这种情况下答案的上下文就没有上一个的充足

  • 第二个弊端就是当分割完成之后,每一篇分割后的文章独立的与问题构成独立的样本送进BERT,假如一篇文章被分割成两份,那么显然模型在阅读了第一个分割片段后的语义信息,有助于模型在第二个片段提取答案。而之前的做法忽略了这一点。

这篇论文的两个创新点正是基于上述两个弊端提出的。

模型:

首先BERT的输入形式如下:

我们重点关注CLS,BERT原文中对CLS的描述为:

The final hidden state corresponding to this token is used as the aggregate sequence representation for classification tasks.

也就是说CLS这个token的最终的hidden state聚集了整个sequence的表示,因此这篇论文的模型的循环机制的原理就是:

将CLS的hidden state通过LSTM来达到循环的目的,具体的就是将前一个segment的LSTM的输出作为当前LSTM的hidden stateht−1h_{t-1}ht−1​,当前segment的CLS的表示作为LSTM的输入xtx_txt​,然后当前的LSTM的输出$h_{t}作为下一时刻的segment的hidden state

看论文的源码:

论文的run_RCM_triva.py以及其它的两个py文件类似,区别在于数据处理的方式要依据数据集的差异制定。我们重点探讨模型,因此就以run_RCM_triva.py为例:

在train_model中有for t in range(args.max_read_times)
根据这行代码我们了解到模型是固定了分割次数的,也就是说模型会在一篇document上分割max_read_times次数。

在175行可以看到模型需要的参数其中就有prev_hidden_states,而整个调用过程就是prev_hidden_states=model(prev_hidden_states)

所以达到了循环机制的目的。

这里有一点需要注意,我们打开modeling_RCM.py,定位在158行
outputs其实是包含有两个tensor的,第一个是sequence_output,第二个是pooler_output,
sequence_output的第一个单词就是CLS的表示,pooler_output也是CLS的表示,不同的是pooler_output是CLS的最后一层的hidden state通过一个带有tanh激活函数的全连接层。具体的请详细阅读transformers的源码.

难点在于利用强化学习来解决分割document的问题

这里说一下基于策略的强化学习(policy-based RL)

RL的思想是:环境environment输入给智能体agent一个状态s,agent根据策略πθ\pi_\thetaπθ​做出一个行为a,然后得到环境给它的奖励r的同时环境转移到了下一个状态。θ\thetaθ为模型的参数。如下图所示

其中环境与agent交互,环境给agent的是状态和奖励,agent做出的行为会改变环境的状态。

在深度学习中应用强化学习时,策略policy就是神经网络NN。

好的接下来我们定义目标函数:
我们从agent与环境交互所产生的轨迹的角度来定义目标函数:

参照上图,分别解释每一行公式:
首先,给定一个初始状态s1s_1s1​,agent会在s1s_1s1​下与环境进行交互,产生一条轨迹τ\tauτ
我们希望的是在这条轨迹τ\tauτ上的所有奖励rtτr_t^\taurtτ​的累计值最大。
那么目标函数自然就是期望所有的轨迹上的累计奖励最大。

也就是说我们希望在最优的参数θ∗\theta^*θ∗下,根据策略πθ∗\pi_{\theta^{*}}πθ∗​,我们任意得到的轨迹τ\tauτ上的累计奖励值都是最大的

所以接下来的问题就是如何优化策略的参数θ\thetaθ。

对目标函数求导
首先我们重新写出来目标函数:

第一行我们已经知道,它的含义就是对策略πθ\pi_\thetaπθ​下的任意一条轨迹上的累计奖励值的期望。

第二行公式就是将期望用公式符号表达出来,也就是在策略πθ\pi_\thetaπθ​下产生的轨迹τ\tauτ的概率乘以这条轨迹τ\tauτ的累计奖励值。

但是轨迹的个数是不确定的,因为随便拿一个初始状态,都会产生很多条轨迹。因此用采样近似的方式。取出来N条轨迹,我们的目标是这N条轨迹上的累计奖励值求和后的值最大。

而给定一个初始状态,在参数θ\thetaθ下产生一条轨迹的概率为:

第二行是对概率取log,第三行对log概率求导。
这里的第一行公式就是指一条轨迹的产生的概率为:

初始状态s1s_1s1​的概率乘以在s1s_1s1​状态下根据πθ\pi_\thetaπθ​采取行为a1a_1a1​的概率乘以在s1s_1s1​状态下采取了行为a1a_1a1​后得到的奖励r1r_1r1​以及环境转移为状态s2s_2s2​的概率,以此类推。
注意到P(s1τ)P(s_1^\tau)P(s1τ​)是初始状态的概率,P(st+1τ,rtτ∣atτ,stτ)P(s_{t+1}^\tau,r_t^\tau|a_t^\tau,s_t^\tau)P(st+1τ​,rtτ​∣atτ​,stτ​)是由环境给出的,因此这两项与参数θ\thetaθ无关,所以对参数的导数为0

我们重新整理一下:

在已知上面的公式后,我们对目标函数求导:

第一行公式就是对目标函数求导数,奖励是环境给的,与策略无关,因此只需要计算P(τ∣θ)P(\tau|\theta)P(τ∣θ)的导数。

第二行的目的是为了构造出对log概率求导,因为dlog⁡f(x)=df(x)f(x)d\log f(x)=\frac{df(x)}{f(x)}dlogf(x)=f(x)df(x)​,于是也就有了第三行公式

最后我们按照前面所说的,将∑τP(τ∣θ)\sum_{\tau}P(\tau|\theta)∑τ​P(τ∣θ)采样近似,并且我们已经推导出:
∇log⁡P(τ∣θ)=∑t∇log⁡P(atτ∣stτ;θ)\nabla\log P(\tau|\theta)=\sum_{t}\nabla\log P(a_t^\tau|s_t^\tau;\theta)∇logP(τ∣θ)=∑t​∇logP(atτ​∣stτ​;θ)

因此我们得到最后一行公式

最后一行公式有两个求和符号,他们的意义是,对N条轨迹的每一条轨迹上的累计奖励求和取平均,而我们知道轨迹上的奖励是在某个状态s下采取行为a获得的。因此上述公式也就等价于对数据集中的所有可能的(状态,行为)对,所获得的奖励求和取平均。

我们的的目标就是在参数θ∗\theta^*θ∗下,数据集中所有可能的(状态,行为)对,所获得的奖励累计是最大的。

最终我们将J(θ)J(\theta)J(θ)改写为:

这也正是论文的公式(14)。上述的推导过程基于策略梯度理论。

就算没有懂上述的推导过程,也没关系,我们只要知道P(a∣s;θ)P(a|s;\theta)P(a∣s;θ)和R(s,a)是什么就可以训练模型了。

这里的P(a∣s;θ)P(a|s;\theta)P(a∣s;θ)就是神经网络的输出,这个输出是指在行为空间上的概率分布。

所以我们要清楚状态、行为、奖励是什么:

  • 状态是模型的输入,在本文中当然就是切割后的某一段文本片段segment
  • 行为是模型的输出,本文的目的是让模型学会自己分割文章,那么输出的自然是下一次切分的滑动步长,行为空间定义为{-16,16,32,64,128},显然输出层的维度是5。
  • 奖励是要预先定义好的,见论文的公式11

我们对照着论文的公式11和公式12:

不需要看文字,只要看公式就行了:

qcq_cqc​是模型预测当前segment包含答案的概率,它是由前一个segment的循环状态和当前segemnt的CLS的表示通过LSTM得到的。

rcr_crc​是模型从当前的segment提取出答案位置的概率,R(s′,a′)R(s',a')R(s′,a′)是未来的累计奖励,关于R(s′,a′)R(s',a')R(s′,a′)我们后面参照论文源码来解释。

这里需要知道的是当当前的segment包含有答案时,如果模型预测的当前segment包含答案的概率较高,也就是qcq_cqc​值比较高,那么当前状态下采取行为a的奖励主要是由qc∗rcq_c*r_cqc​∗rc​决定的。

而如果当前的segment没有包含答案,那么rcr_crc​=0,而如果模型预测当前segment包含答案的概率较小,也就是qcq_cqc​值比较小,那么1−qc1-q_c1−qc​比较大,所以当前状态采取行为a的奖励主要由未来的累计奖励决定。若模型预测当前segment包含答案的概率较大,那么1−qc1-q_c1−qc​比较小,显然此时模型获得的奖励就比较少。

通俗的说就是,若当前的segment包含答案,模型预测当前的segment确实包含答案,而且还能正确的提取出答案位置,那么此时给模型的奖励主要是qc∗rcq_c*r_cqc​∗rc​,希望模型在下次见到同样的segment时,增加P(a∣s;θ)P(a|s;\theta)P(a∣s;θ)的概率。其他的情况下是类似的。

训练模型

想要训练模型我们需要知道模型的loss

模型的loss有三个,第一个是答案提取器(Answer Extractor)的loss,它的值是模型预测的答案位置与真实位置的交叉熵:

其中有两个求和符号,第一个求和符号是对整个序列上每一个位置的单词的预测概率,第二个求和符号是对所有的分割次数求和。源码中定义了分割次数固定为3

第二个是LCSL_{CS}LCS​,它代表的是切分段落打分器(Chunking Scorer)的损失值,也就是模型在每一次切分的segment下,预测该segment包含有答案的概率与实际包含答案的交叉熵,仍然是对所有的切分次数求和。

前两个的损失函数都是交叉熵损失函数,属于监督学习范畴,有明确的标签,但是第三个损失值:
LCPL_{CP}LCP​是强化学习的损失函数,没有标签,只有奖励。

不过根据前面基于策略梯度理论,我们已经推导出该损失函数的导数为:

所以现在我们只需要计算出log⁡P(a∣s;θ)\log P(a|s;\theta)logP(a∣s;θ)和R(s,a)R(s,a)R(s,a)就可以了

下面我们仿照论文的源码一步一步的看:

以rum_RCM_trivia.py为例:

chunk_stop_rewards=[]#用来记录每一次分割下得到的r_c
contarin_answer_probs=[]#用来记录每一次分割下的q_c
#我们并不能每一个segment下立即计算出奖励,由公式11可以看出来每一步的奖励是要依赖于后面时间步的
#因此我们要保存每一步的r_c和q_c,然后才能计算最终的奖励
#源码用chunk_stop_rewards来命名,含义就是说r_c这个值其实对应着模型能从中正确提取出答案的奖励
#rewards_for_stop[i] =  chunk_start_probs[i][start_position] * chunk_end_probs[i][end_position]#强制模型切分一篇document max_read_times次,我们假设max_read_times=4,
#######第一次切分 ############################################################chunk_stop_rewards=[]#用来记录每一次分割下得到的r_c
contarin_answer_probs=[]#用来记录每一次分割下的q_c
#我们并不能每一个segment下立即计算出奖励,由公式11可以看出来每一步的奖励是要依赖于后面时间步的
#因此我们要保存每一步的r_c和q_c,然后才能计算最终的奖励
#源码用chunk_stop_rewards来命名,含义就是说r_c这个值其实对应着模型能从中正确提取出答案的奖励
#rewards_for_stop[i] =  chunk_start_probs[i][start_position] * chunk_end_probs[i][end_position]stride_log_probs=[]#用来记录每一次的logP(a|s;\theta)#强制模型切分一篇document max_read_times次,我们假设max_read_times=4,
#######第一次切分 ############################################################
cur_global_pointer=0#我们不考虑batch_size这个维度,仅仅只考虑一篇文章document
stop_flag=1#代表第一个segment是包含有答案的
prev_hidden_states=None#第一次切割下是没有之前的prev_hidden_states的
#把stop_flag输入到模型中用来计算Chunking Scorer这个loss,也就是模型预测第一篇document包含有答案的损失值
(sequence_output,pool_output)=bert(input_ids)#我们把第一篇segment的input_ids输入到bert_model中
cls_representation=sequence_output.narrow(1,0,1)#这就是cls的表示,也就是论文的v_cstart_logits,end_logits=unstack(Linear(in_features=768,out_features=2)(sequence_output),axis=2)
start_loss=cross_entropy(start_logits,start_positions)
end_loss=cross_entropy(end_logits,end_positions)#计算Answer Extractor的lossstart_probs,end_probs=softmax(start_logits),softmax(end_logits)
if stop_flag==1:#表明当前segment包含答案r_c=start_probs*end_probs
else:r_c=0#对应论文的公式12
v_c_pie=LSTM(cls_representation,prev_hidden_states)#第一次的prev_hidden_states为None,那么默认初始化为全0向量
stride_prob=Softmax(Linear(in_features=768,out_features=5)(cls_representation))#输出的张量为行为空间{-16,16,32,64,128}的概率分布
stride_index=sample(stride_prob)#从概率分布中随机采样,概率大的会优先采样出来,注意这不是argmax,也就是说
#某些概率不是最大的行为也会被采样出来,一种解释是:即使是同样的segment,问题不同的情况下,答案当然不同,因此同样的segment在面对
#不同的问题时,要采取不同的分割策略,而不是看到一篇segment,就以某种固定的分割方式切分
stride_log_prob=log(stride_prob)#这个值就是log(a|s,\theta),接下来我们计算奖励
q_c=sigmoid(Linear(in_features=768,out_features=2)v_c_pie)
contain_answer_probs.append(q_c)
chunk_stop_rewards.append(r_c)

经过第一次切分之后,假设有下列值存在

chunk_stop_rewards=[0.07]
contain_answer_probs=[0.75]
prev_hidden_states#此时不是空值了,而是LSTM的输出值
stride_log_probs=[-0.68]
stride_index=2#行为空间:{-16,16,32,64,128},也就代表这一次应该向右移动32个单词

经过max_read_times次数后我们会得到下列值:

  • chunk_stop_rewards=[0.07,0,0.15,0.05],chunk_stop_rewards存储的是max_read_times次数下的每一次模型获得的rcr_crc​,
    rcr_crc​也就是正确的答案可以从那一次的segment中提取出来的概率,rcr_crc​也可以认为是当前的segment,也就是在当前状态下,环境给模型预测答案的奖励,如果答案在当前的segment下,那么奖励就是pcstart∗pcendp_c^{start}*p_c^{end}pcstart​∗pcend​。否则奖励为0,也就是说不希望模型在这种状态下还要预测答案。
  • contain_answer_probs=[0.75,0.15,0.9,0.6], contain_answer_probs存储的是max_read_times次数下的每一次模型预测该次的segment包含答案的概率,它是将v~c\tilde{v}_cv~c​通过一个激活函数为sigmoid的全连接层输出的概率,而v~c\tilde{v}_cv~c​是vcv_cvc​和v~c−1\tilde{v}_{c-1}v~c−1​通过LSTM得到的,其中vcv_cvc​是CLS的向量表示,v~c−1\tilde{v}_{c-1}v~c−1​是上一次的segment的LSTM的输出
  • stride_log_probs=[-2.12,-0.85,-0.56,-0.28], stride_log_probs存储的是max_read_times次数下的每一次的segment,模型在该segment下做出的行为a的log概率,对应着log⁡P(a∣s)\log P(a|s)logP(a∣s)。而P(a∣s)P(a|s)P(a∣s)是由v~c\tilde{v}_cv~c​通过一个输出维度为action_space_nums的全连接层然后softmax得到的,其中action_space_nums指的是行为数目,本文中是5,比如{-16,16,32,64,128}

有了上面的数据后我们就可以计算模型中切分策略(Chunking Policy)的损失函数了,也就是∑(s,a)∈Dlog⁡P(a∣s)R(s,a)\sum_{(s,a)\in \mathcal{D}}\log P(a|s)R(s,a)∑(s,a)∈D​logP(a∣s)R(s,a)

损失函数有两部分,第一个log⁡P(a∣s)\log P(a|s)logP(a∣s)我们已经获得了,主要来看奖励是如何计算的。

这里的R(s′,a′)R(s',a')R(s′,a′)是未来的奖励

#到了这里我们就可以计算强化学习部分的loss了,我们自己构造下数据
#假设batch_size=2,max_read_times=5,
#stop_probs代表的是这2个document在这5次的切分当中,每一次模型预测segment包含answer的概率,
stop_probs=[[0.15,0.5],[0.2,0.7],[0.25,0.35],[0.40,0.2],[0.15,0.30]]#每一个值对应的是q_c
#stop_rewards代表的是这2个document在这5次的切分当中,每一次模型从segment中提取出answer的概率
stop_rewards=[[0.08,0.15],[0.0,0.36],[0.5,0.0],[0.15,0.25],[0.0,0.4]]#每一个值对应的是r_c,0.0代表这个segment不包含answer
stop_probs=np.transpose(stop_probs)
stop_rewards=np.transpose(stop_rewards)
print(stop_probs)
print(stop_rewards)

整个计算过程如下:
stop_probs=[0.15 0.2 0.25 0.4 0.15]

stop_rewards=[0.08 0. 0.5 0.15 0. ]

下面我们按照公式R(s,a)=qcrc+(1−qc)R(s′,a′)R(s,a)=q_cr_c+(1-q_c)R(s',a')R(s,a)=qc​rc​+(1−qc​)R(s′,a′)来手动计算第一个样本所应该获得的奖励

首先我们需要的是从后向前计算,max_read_times=5

  • 第五次由于没有下一次了,所以第五次的奖励设置为第五次的rcr_crc​,也就是0.
  • 第四次的奖励为0.4*0.15+(1-0.4)*0.=0.06
  • 第三次的奖励为0.25*0.5+(1-0.25)*0.06=0.17
  • 第二次的奖励为0.2*0. +(1-0.2)*0.17=0.136
  • 第一次的奖励为0.15*0.08+(1-0.15)*0.136=0.1276

所以对于第一个样本从第一次到倒数第二次的奖励应该为[0.1276,0.136,0.17,0.06]

但是按照论文的github代码运行出来的结果:

我觉得我的理解没有问题,感觉好像是论文的代码错了

所以我认为rl_reward.py中的代码应该是下面这样

q_vals = []
# calc from the end to the beginning time
next_q_vals = stop_rewards[:,-1] #np.zeros(len(stop_rewards))
for t in reversed(range(0, stop_rewards.shape[1]-1)):t_rewards = stop_rewards[:, t]t_probs = stop_probs[:, t]cur_q_vals = np.multiply(t_rewards, t_probs) + np.multiply(next_q_vals, 1-t_probs)q_vals.append(list(cur_q_vals)[:])next_q_vals = cur_q_vals
# q_vals: (bsz, max_read_times-1)
q_vals=list(reversed(q_vals))
q_vals = np.transpose(q_vals)

运行结果:

假如我的理解是对的,那么我们已经获得了每一个时刻的奖励R(s,a)R(s,a)R(s,a),而且之前也已经把各个时刻的stride_log_probs记录下来,

那么现在log⁡pact(a∣s)\log p^{act}(a|s)logpact(a∣s)和R(s,a)R(s,a)R(s,a)都已经得到了,

接下来自然就是:

reinforce_loss=torch.mean(torch.sum(-q_vals*stride_log_probs,dim=1))

最终的loss形式为:

loss = (stop_loss + answer_loss) / args.max_read_times + reinforce_loss

需要注意的是,因为最后一个时间步的行为不与环境进行交互,因此对最后一个时间步没有奖赏。

模型的loss=(stop_loss+answer_loss)/max_read_times+reinforce_loss

根据这个式子我们可以看出,每一个时间步我们都会计算Answer Extractor部分提取答案的loss,以及Chunking Scorer部分预测segment包含答案的loss,并且每一个时间步的值是累加的。
而reinforce_loss是整个轨迹执行完之后,再计算loss

测试阶段

测试阶段不需要那么复杂,因为不必计算loss了,我们只需要根据模型在当前state下
采取的action a 得到的概率值P(a∣s)P(a|s)P(a∣s),这是在行为空间上的概率分布,在训练阶段我们是采样的方式,也就是每一个行为都有可能被采样出来,只是概率大的更多次的会被采样出来。而测试阶段采取贪心策略,只选取概率最大的那个行为作为输出

定义:

作为当前的segment下预测答案位置的分数

然后根据当前状态下所作出的行为a∗a^*a∗,我们会得到一个新的状态st+1s_{t+1}st+1​,比如之前的a∗=32a^*=32a∗=32,那么当前的状态就是上一次的segment向右移动32个单词长度得到的。

然后这个新的状态下,仍然得到:

总计会有max_read_times个,我们取出来其中值最大的作为模型在切分了一篇文章max_read_times后所作出的综合预测。

实验部分

根据此表我们可以看出来,当最大长度max_seq_len限制为512的时候,其实这种循环切分的机制并没有比BERT-large有什么明显的效果,主要原因是,CoQA和QuAC数据集的文章大部分不是特别的长

根据上图我们了解到,CoQA和QuAC的训练集合的文章长度平均为352和516个单词,显然很多文章并不需要切分。因此Recurrent chunking机制发挥不了什么作用,但是随着最大长度的减小,比如,当最大长度限制为192的时候,也就是说当一篇文章会被切分成很多长度不超过192的segment的时候,循环切分的方式明显好于baseline,因为baseline的做法是将切分的各个segment独立的预测答案,而且也不考虑answer在segment中的位置,前面已经分析了,那些answer位置在segment位置中心的答案容易预测出来,而处于segment边缘的答案由于缺少足够的上下文,是不容易准确的预测出来的

下表是在TriviaQA数据集上的实验结果,
因为TriviaQA数据集比较长,max_seq_len固定为512的情况下,显然循环切分机制效果要好于baseline的,说明循环切分机制适用于处理长文本。

上图指的是BERT-large这个基线模型和RCM这个循环切分机制在切分文本时,切分的文本包含完整答案在切分的所有文本中的比例,也叫Hit rate,命中率。显然循环切分机制切分的文章中有大部分都是包含完整答案的。而baseline这种按照固定滑动步数切分文章的做法,有相当一部分的文章是不包含有答案的。

这里需要注意的是,在baseline中,也就是BERT模型。它是按照固定的doc_stride切分一篇文章,如果切分的某个segment不包含答案,那么这个segment是不算作样本训练模型的,因为没有答案需要预测。具体的可以查看BERT源码:


从代码中我们可以看到,doc_start指的就是当前的segment的第一个单词在原始的document中的位置,显然若是该document的答案的起始位置和终止位置小于segment的起始位置或者都大于segment的末尾位置,表明当前所分割的segment成功的避开了答案,那么就continue,也就是不要这个segment了

而在循环切分机制当中,对于不包含有答案的segment,仍然会保留下来作为样本参与训练,因为整个切分过程是在一篇document上连续切分的,中间断了显然是不行的,而且我们需要让模型学会避免切分出这种不包含answer的segment.具体的做法是,对于不包含答案的segment,和question构成一个example输入到BERT当中,BERT的CLS的表示和上一时刻的segment的LSTM的输出一起输入到LSTM当中,得到v~c\tilde{v}_cv~c​,然后v~c\tilde{v}_cv~c​输入到激活函数为sigmiod,输出维度是2的全连接层得到qcq_cqc​,这个qcq_cqc​代表的就是模型预测当前segment包含答案的概率,然后在这个不包含answer的segment下,给模型的奖励是R(s,a)=(1−qc)∗R(s′,a′)R(s,a)=(1-q_c)*R(s',a')R(s,a)=(1−qc​)∗R(s′,a′),显然模型预测的qcq_cqc​越小,获得的奖励越多


上图是论文给出的一个例子,红色字体代表的是answer,第一次切分时,固定到max_seq_length为止,也就是图片中的黄色部分,第二次切分时,向右滑动128个单词,也就是蓝色部分chunk 2,第三次切分下模型向左移动了16个单词,目的是使得第三个chunk包含答案更多的上下文,也就是图片中的橘色部分,显然这种切分方式比固定切分要好,而且固定的切分方式是不会向左移动的。

解读ACL2020的一篇机器阅读理解方向的论文(Recurrent Chunking Mechanisms for Long-text machine reading comprehension)相关推荐

  1. 清华 NLP 团队推荐:必读的77篇机器阅读理解论文

    https://mp.weixin.qq.com/s/2VhgEieBwXymAv2qxO3MPw [导读]机器阅读理解(Machine Reading Comprehension)是指让机器阅读文本 ...

  2. 神经机器阅读理解最新综述:方法和趋势

    作者丨刘姗姗 学校丨国防科技大学 研究方向丨自然语言处理 近年来,基于深度学习方法的机器阅读理解受到了广泛的关注.近日,来自国防科技大学的团队在arXiv上发布了预印版综述文章 Neural Mach ...

  3. 机器阅读理解MRC论文整理

    机器阅读理解MRC论文整理 最近发现一篇机器阅读理解整理的博客机器阅读理解整理整理于2020年 论文代码查找网站: https://dblp.uni-trier.de/db/conf/acl/acl2 ...

  4. 机器阅读理解 / 知识库 / 深度学习 / 对话系统 / 神经机器翻译 | 本周值得读

    在碎片化阅读充斥眼球的时代,越来越少的人会去关注每篇论文背后的探索和思考. 在这个栏目里,你会快速 get 每篇精选论文的亮点和痛点,时刻紧跟 AI 前沿成果. 点击本文底部的「阅读原文」即刻加入社区 ...

  5. 2021秋招-机器阅读理解整理

    机器阅读理解整理 经典模型整理 笔记 后Bert时代机器阅读理解 后续 自己论文整理 已经分类整 大的实验室: UCL MRC_Group: AI2: 微软: THU: PKU: 数据集文章 ROPE ...

  6. 【NLP】面向对话的机器阅读理解任务(Dialogue MRC)相关论文整理

    来自 | 知乎 作者 | 李家琦 链接|https://zhuanlan.zhihu.com/p/410984053 本文已获作者授权,未经许可禁止二次转载 Dialogue-based Machin ...

  7. 【阅读笔记】机器阅读理解(中)——架构篇

    文章目录 一.MRC模型架构 总体架构 编码层 词表向量 字符编码 上下文编码 交互层 互注意力 自注意力 上下文编码 输出层 多项式选择答案生成 区间式答案 自由式答案生成 注意力机制的应用 拷贝生 ...

  8. 【论文解读】NER任务中的MRC(机器阅读理解)

    论文:https://arxiv.org/pdf/1910.11476v6.pdf 前沿: 在之前的NER任务中常常分为两种:nested NER和 flat NER.从直观的角度来看,nested N ...

  9. 科大讯飞刷新纪录,机器阅读理解如何超越人类平均水平? | 技术头条

    点击上方↑↑↑蓝字关注我们~ 「2019 Python开发者日」明日开启,扫码咨询 ↑↑↑ 记者 | 琥珀 出品 | AI科技大本营(公众号ID:rgznai100) 对于日常从事模型训练的研究人员来 ...

最新文章

  1. C# 调用ArcGIS server admin api
  2. sublime3配置pythonIDE
  3. caffe-builder相关资料
  4. 一文看懂神经网络初始化!吴恩达Deeplearning.ai最新干货
  5. solaris 中挂载usb移动硬盘
  6. 利用bootstrap框架做了一个采摘节节日活动网页
  7. WINDOWS的SHELLCODE编写高级技巧
  8. HyperLink的绑定用法(做笔记)
  9. Sqli-labs less 53
  10. atoi 原来将字符串02002xzm100转换为int以后是2002
  11. iOS中创建动态库及调用方法
  12. 安卓手机的APP图标尺寸规范和图标命名规范
  13. 在计算机中常见的硬盘接口类型有,硬盘接口类型主要有哪几种?
  14. 高德智慧交通地图空间可视化SDK设计与实现
  15. 计算机硬盘对计算机速度的影响,固态硬盘会不会影响整个电脑的运行速度
  16. 图像超分算法小合集二:FSRCNN、DRCN、RDN、EDSR
  17. c++编程拼图小游戏
  18. 儿时经典电影回顾,你看过几部?
  19. n个评委给m个选手打分python_n个评委为m个选手打分(n个评委打分总次数mn)。请问如何评判m个选手的成绩?...
  20. 阿里飞绪: poll 性能回归分析

热门文章

  1. 基于kinect + EmguCV 的监控小应用(视频录制保存)
  2. Python——库docx(四)12.25
  3. 使用jdbc:nested exception is java.sql.SQLException: No value specified for parameter或bad SQL grammar
  4. 《开天辟地》之《网上冲浪篇》将带你进入一个精彩的互联网世界
  5. 微信小程序 | 证件照制作小程序源码下载
  6. Oracle域完整性约束
  7. 大学医学院有计算机专业吗,上大学时辛苦一点,将来工作轻松一点,这些专业可以做到...
  8. 拨开零售电商数字化转型迷雾,电商RPA应用揭秘
  9. 【数据库】数据库入门(二): 关系型数据库
  10. mysql报错(Not unique table/alias)