Mask 在NLP中是一个很常规的操作,也有多种应用的场景和形式,下面尝试从以下几个方面去全(用了夸张的修辞手法)解Mask,并尽可能地辅以图片说明和代码解释:
- Mask的作用:
- 处理非定长序列
- RNN中的Mask
- Attention中Mask
- 防止标签泄露
- Transformer中的Mask
- BERT中的Mask
- XLNet中的Mask
Mask的作用
对于NLP中mask的作用,先上结论:
1、padding mask:处理非定长序列,区分padding和非padding部分,如在RNN等模型和Attention机制中的应用等
2、sequence mask:防止标签泄露,如:Transformer decoder中的mask矩阵,BERT中的[Mask]位,XLNet中的mask矩阵等
PS:padding mask 和 sequence mask非官方命名
处理非定长序列
在NLP中,文本一般是不定长的,所以在进行 batch训练之前,要先进行长度的统一,过长的句子可以通过truncating 截断到固定的长度,过短的句子可以通过 padding 增加到固定的长度,但是 padding 对应的字符只是为了统一长度,并没有实际的价值,因此希望在之后的计算中屏蔽它们,这时候就需要 Mask。
上图为中文场景下,一个 batch=5 的,以字为单位的输入矩阵(也可以在分词后以词为单位)和 mask 矩阵,左图已经将文本 padding 到统一长度了,右图中的1表示有效字,0代表无效字。
RNN中的Mask
对于RNN等模型,本身是可以直接处理不定长数据的,因此它不需要提前告知 sequence length,如下是pytorch下的LSTM定义:
nn.LSTM(input_size, hidden_size, *args, **kwargs)
但是在实践中,为了 batch 训练,一般会把不定长的序列 padding 到相同长度,再用 mask 去区分非 padding 部分和 padding 部分。
区分的目的是使得RNN只作用到它实际长度的句子,而不会处理无用的 padding 部分,这样RNN的输出和隐状态都会是对应句子实际的最后一位。另外,对于token级别的任务,也可以通过mask去忽略 padding 部分对应的loss。
不过,在 pytorch 中,对 mask 的具体实现形式不是mask矩阵,而是通过一个句子长度列表来实现的,但本质一样。实现如下,sentence_lens 表示的是这个batch中每一个句子的实际长度。参考
embed_input_x_packed = pack_padded_sequence(embed_input_x, sentence_lens, batch_first=True)
encoder_outputs_packed, (h_last, c_last) = self.lstm(embed_input_x_packed)
encoder_outputs, _ = pad_packed_sequence(encoder_outputs_packed, batch_first=True)
btw,在 pytorch 的 Embedding 和 Loss 中也有对 padding 值的设置:
# padding_idx (int, optional): If given, pads the output with the embedding vector at
# `padding_idx` (initialized to zeros) whenever it encounters the index.
embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
# ignore_index (int, optional): Specifies a target value that is ignored
# and does not contribute to the input gradient.
criterion = nn.CrossEntropyLoss(ignore_index=0)
Attention中Mask
在 Attention 机制中,同样需要忽略 padding 部分的影响,这里以transformer encoder中的self-attention为例:
self-attention中,