NLP中的Mask全解
©PaperWeekly 原创 · 作者|海晨威
学校|同济大学硕士生
研究方向|自然语言处理
Mask 在 NLP 中是一个很常规的操作,也有多种应用的场景和形式,下面尝试从以下几个方面去全解(用了夸张的修辞手法)Mask,并尽可能地辅以图片说明和代码解释:
1. Mask 的作用
处理非定长序列
RNN 中的 Mask
Attention 中 Mask
2. 防止标签泄露
Transformer 中的 Mask
BERT 中的 Mask
XLNet 中的 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。
上图(图片参考 [1])为中文场景下,一个 batch=5 的,以字为单位的输入矩阵(也可以在分词后以词为单位)和 mask 矩阵,左图已经将文本 padding 到统一长度了,右图中的 1 表示有效字,0 代表无效字。
RNN中的Mask
对于 RNN 等模型,本身是可以直接处理不定长数据的,因此它不需要提前告知 sequence length,如下是 PyTorch 下的 LSTM 定义:
nn.LSTM(input_size, hidden_size, *args, **kwargs)
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)
# 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)
def attention(query, key, value, mask=None, dropout=None):
"Compute 'Scaled Dot Product Attention'"
d_k = query.size(-1)
scores = torch.matmul(query, key.transpose(-2, -1)) \
/ math.sqrt(d_k)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9) # mask步骤,用 -1e9 代表负无穷
p_attn = F.softmax(scores, dim = -1)
if dropout is not None:
p_attn = dropout(p_attn)
return torch.matmul(p_attn, value), p_attn
▲ 代码参考 [4]
PS:上述两个参考都是非常好的 transformer 介绍文章,参考 1,图片与文字相得益彰,参考 2,代码与讲解相辅相成。
在语言模型中,常常需要从上一个词预测下一个词,但如果要在 LM 中应用 self attention 或者是同时使用上下文的信息,要想不泄露要预测的标签信息,就需要 mask 来“遮盖”它。不同的 mask 方式,也对应了一篇篇的 paper,这里选取典型的几个。
Transformer中的Mask
Transformer 是包括 Encoder和 Decoder的,Encoder中 self-attention 的 padding mask 如上,而 Decoder 还需要防止标签泄露,即在 t 时刻不能看到 t 时刻之后的信息,因此在上述 padding mask 的基础上,还要加上 sequence mask。
sequence mask 一般是通过生成一个上三角矩阵来实现的,上三角区域对应要 mask 的部分。
在 Transformer 的 Decoder 中,先不考虑 padding mask,一个包括四个词的句子 [A,B,C,D] 在计算了相似度 scores 之后,得到下面第一幅图,将 scores 的上三角区域 mask 掉,即替换为负无穷,再做 softmax 得到第三幅图。这样,比如输入 B 在 self-attention 之后,也只和 A,B 有关,而与后序信息无关。
因为在 softmax 之后的加权平均中: B‘ = 0.48*A+0.52*B,而 C,D 对 B' 不做贡献。
▲ 图片参考 [5]
import torch
def padding_mask(seq, pad_idx):
return (seq != pad_idx).unsqueeze(-2) # [B, 1, L]
def sequence_mask(seq):
batch_size, seq_len = seq.size()
mask = 1- torch.triu(torch.ones((seq_len, seq_len), dtype=torch.uint8),diagonal=1)
mask = mask.unsqueeze(0).expand(batch_size, -1, -1) # [B, L, L]
return mask
def test():
# 以最简化的形式测试Transformer的两种mask
seq = torch.LongTensor([[1,2,0]]) # batch_size=1, seq_len=3,padding_idx=0
embedding = torch.nn.Embedding(num_embeddings=3, embedding_dim=10, padding_idx=0)
query, key = embedding(seq), embedding(seq)
scores = torch.matmul(query, key.transpose(-2, -1))
mask_p = padding_mask(seq, 0)
mask_s = sequence_mask(seq)
mask_decoder = mask_p & mask_s # 结合 padding mask 和 sequence mask
scores_encoder = scores.masked_fill(mask_p==0, -1e9) # 对于scores,在mask==0的位置填充
scores_decoder = scores.masked_fill(mask_decoder==0, -1e9)
test()
# mask_p
[[[1 1 0]]]
# mask_s
[[[1 0 0]
[1 1 0]
[1 1 1]]]
# mask_decoder
[[[1 0 0]
[1 1 0]
[1 1 0]]]
可以看到 mask_decoder 的第三列为 0 ,对应 padding mask,上三角为 0,对应 sequence mask。
BERT中的Mask
BERT 实际上是 Transformer 的 Encoder,为了在语言模型的训练中,使用上下文信息又不泄露标签信息,采用了 Masked LM,简单来说就是随机的选择序列的部分 token 用 [Mask] 标记代替。这波 Mask 操作,思想很直接,实现很简单,效果很惊人。
BERT 之后,也有不少在 Mask 的范围和形式上做文章的,比如:ERNIE,但大同小异,不多赘述。
而 XLNet 的 Mask 操作非常的巧(nan)妙(dong),如下。
XLNet中的Mask
XLNet 通过 Permutation Language Modeling 实现了不在输入中加 [Mask],同样可以利用上下文信息,关键的操作就是下面所示的 Attention Mask 机制。
但并不是那么好理解,要理解 XLNet 中的 Mask,一定要先看张俊林老师的:XLNet:运行机制及和 Bert 的异同比较 [6],再来看下面的内容。上图也是引自该文,这里再引用一句我认为非常关键的一段话:
在 Transformer 内部,通过 Attention 掩码,从 X 的输入单词里面,也就是 Ti 的上文和下文单词中,随机选择 i-1 个,放到 Ti 的上文位置中,把其它单词的输入通过 Attention 掩码隐藏掉,于是就能够达成我们期望的目标(当然这个所谓放到 Ti 的上文位置,只是一种形象的说法,其实在内部,就是通过 Attention Mask,把其它没有被选到的单词 Mask 掉,不让它们在预测单词 Ti 的时候发生作用,如此而已。看着就类似于把这些被选中的单词放到了上文 Context_before 的位置了)。
对于排列序列:3->2->4->1,通过 Attention Mask,在 self-attention 的加权平均计算中,以上图中的 为例:
self-attention 计算之后 Content stream 中的 ,其中 表示第 2 个词对应的向量,其他同理。这样在 中就看到了它的下文 ,就好像是把 放到了它的上文位置一样,但实际顺序并没有改变。
对序列进行排列的目的是为了生成这个 Attention Mask,再加上双流注意力去解决预测歧义的问题,可以说就是 Permutation Language Modeling 的全部了。
到这里,就是本文全部的 Mask,但这肯定不是 NLP 中 Mask 的全部,但希望能帮助你去更好地理解 Mask。
参考文献
点击以下标题查看更多往期内容:
#投 稿 通 道#
让你的论文被更多人看到
如何才能让更多的优质内容以更短路径到达读者群体,缩短读者寻找优质内容的成本呢?答案就是:你不认识的人。
总有一些你不认识的人,知道你想知道的东西。PaperWeekly 或许可以成为一座桥梁,促使不同背景、不同方向的学者和学术灵感相互碰撞,迸发出更多的可能性。
PaperWeekly 鼓励高校实验室或个人,在我们的平台上分享各类优质内容,可以是最新论文解读,也可以是学习心得或技术干货。我们的目的只有一个,让知识真正流动起来。
📝 来稿标准:
• 稿件确系个人原创作品,来稿需注明作者个人信息(姓名+学校/工作单位+学历/职位+研究方向)
• 如果文章并非首发,请在投稿时提醒并附上所有已发布链接
• PaperWeekly 默认每篇文章都是首发,均会添加“原创”标志
📬 投稿邮箱:
• 投稿邮箱:hr@paperweekly.site
• 所有文章配图,请单独在附件中发送
• 请留下即时联系方式(微信或手机),以便我们在编辑发布时和作者沟通
🔍
现在,在「知乎」也能找到我们了
进入知乎首页搜索「PaperWeekly」
点击「关注」订阅我们的专栏吧
关于PaperWeekly
PaperWeekly 是一个推荐、解读、讨论、报道人工智能前沿论文成果的学术平台。如果你研究或从事 AI 领域,欢迎在公众号后台点击「交流群」,小助手将把你带入 PaperWeekly 的交流群里。