Bonky Zhu
If someone is able to show me that what I think or do is not right, I will happily change, for I seek the truth, by which no one was ever truly harmed. It is the person who continues in his self-deception and ignorance who is harmed.

为什么RNN需要mask

原文链接:https://blog.csdn.net/zwqjoy/article/details/95050794

Padding

文本数据在处理的时候,由于各样本的长度并不一样,有的句子长有的句子短。抛开动态图、静态图模型的差异,由于需要进行矩阵运算,句长需要是等长的才可以,这就需要padding操作。padding一般是用最长的句子长度为最大长度,然后其他样本补0到最大长度,这样样本就是等长的了。

但是注意padding后的样本如果不作处理只用普通的循环神经网络来做的话其实是有影响的,因为即使输入的是0,做了embedding后也不是0,而且还有上一时刻隐藏层,所以输出不会是0。但是在实际使用中,padding的这种操作如果不做特殊处理,模型也是可以学到它是无用的padding

20190708133229781

但是这会有一个问题,什么问题呢?比如上图,句子“Yes”只有一个单词,但是padding了5的pad符号,这样会导致LSTM对它的表示通过了非常多无用的字符,这样得到的句子表示就会有误差,更直观的如下图:

20190708133216502

结论:直接填充0,在数据运算上没有问题,但是从序列的整个含义来说,这是不合理的,所以一般情况下不能这么做。

RNN的Mask

在使用RNN based model处理序列的应用中,如果使用并行运算batch sample,我们几乎一定会遇到变长序列的问题。

通常解决变长的方法主要是将过长的序列截断,将过短序列用0补齐到一个固定长度(例如max_length)。

最后由n个sample组成的dataset能形成一个shape == (n, max_length)的矩阵。然后可以将这个矩阵传递到后续的模型中使用。

然而我们可以很明显,如果用0或者其他整数补齐,势必会影响到模型自身(莫名其妙被输入很多个0,显然是有问题的)。有什么方法能够做到“能够使用一个二维矩阵作为输入数据集,从而达到并行化的同时,还能让RNN模型自行决定真正输入其中的序列的长度。

Mask主要用于解决RNN中输入有多种长度的问题。要输入RNN中的是尺寸固定的张量,即 批尺寸(batch size) * 序列长度(sequence length) * 嵌入大小(embedding size) 。因为RNN在计算状态向量时不仅考虑当前,也考虑前一次的状态向量,如果为了维持真实长度,采用补0的方式,在进行状态向量计算的时候也会包含进用0补上的位置,而且这种方式无法进行彻底的屏蔽。由于喂到模型中是固定大小的tensorbatch size * sequence length * embedding size), RNN需要用maskmaintain序列真实长度,从而在计算loss的时候去除掉padding的部分。

相比于补0,Mask会得到不同的状态向量。对于每一个用0初始化的的样本,我们建立一个Mask,并使其长度与数据集中最长的序列相同。然后样本中所有有数值的地方,我们用1把Mask中对应的位置填充起来。

举个例子,数据集中最长的序列长度为10,所以所有的Mask将以如下的方式初始化:

mask = [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]


同时,我们还有一个如下的样本:

a = [ 2., 0. ,5. ,6. ]


现在我们用1将Mask中所有有数值的地方填充起来,因而得到以下的Mask

mask_a = [ 1., 1., 1., 1., 0., 0., 0., 0., 0., 0.]


在这之后,我们将样本与mask输入RNN中,RNN将会把所有没有值的的地方加上0,所以a变成了:

a_hood = [ 2., 0. ,5. ,6., 0., 0., 0., 0., 0., 0.]


但是如果我们任由RNN用这种补0的方式,RNN会认为所有的序列长度都为10,并且在计算时用上所有的补上的0。

mask_a = [ 1., 1., 1., 1., 0., 0., 0., 0., 0., 0.]


而此时mask_a的作用就是让RNN跳过所有Mask为0的输入,复制cell中前一次的隐藏状态;对于Mask为1的输入RNN将按常规处理。

CNN

对于CNN来说,首先它的输入已经是固定尺寸,不需要Mask,其次就算用上Mask,结果和补0一样,所以采用padding补0这种方便的方法,而CNN是卷积操作,补0的位置对卷积结果没有影响,即补0和Mask两种方式的结果是一样的,因此大家为了省事起见,就普遍在CNN使用补0的方法了。CNN的的输入本身就是fixed-size,所以不需要mask

Share

发表评论