src_mask和src_key_padding_mask的区别

27

来自官方PyTorch论坛的内容:src_mask只是一个正方形矩阵,用于过滤注意力权重。... src_key_padding_mask更像是一个填充标记,在src序列中掩盖特定的令牌(也就是注意力矩阵的整个列/行设置为“-inf”)。 https://discuss.pytorch.org/t/nn-transformer-explaination/53175/5 - Charlie Parker
另一篇来自官方PyTorch论坛的帖子:https://discuss.pytorch.org/t/transformer-difference-between-src-mask-and-src-key-padding-mask/84024。 - Charlie Parker
又一个:https://www.reddit.com/r/pytorch/comments/okfh2k/transformer_difference_between_src_mask_and_src/ - Charlie Parker
相关问题:https://dev59.com/urvoa4cB1Zd3GeqP7aLe - Charlie Parker
也许阅读MHA文档是最好的选择...?https://pytorch.org/docs/master/generated/torch.nn.MultiheadAttention.html#torch.nn.MultiheadAttention - Charlie Parker
3个回答

33

src_mask和src_key_padding_mask的区别

需要注意的是,在transformer中使用张量_mask_key_padding_mask的区别。 当进行attention操作时,我们通常会得到一个由所有比较组成的平方中间张量,大小为[Tx, Tx](对于编码器的输入),[Ty, Ty](对于移位输出 - 解码器的输入之一)和[Ty, Tx](用于存储掩码 - 编码器/存储器输出与解码器/移位输出之间的attention)。

因此,我们可以得到transformer中每个掩码的用途(请注意,来自pytorch文档的符号表示如下:其中Tx=S是源序列长度(例如,输入批次的最大值), Ty=T是目标序列长度(例如,目标长度的最大值), B=N是批次大小D=E是特征数量):

  1. src_mask [Tx, Tx] = [S, S] – 在处理源序列时添加的掩码(可选)。在进行 atten_src + src_mask 操作时使用。我不确定有哪些输入示例,但可以查看 tgt_mask 以获取示例。通常的用途是添加 -inf,以便根据需要屏蔽 src_attention。如果提供了 ByteTensor,则不允许非零位置参与注意力计算,而零位置将保持不变。如果提供了 BoolTensor,则 True 值的位置不允许参与注意力计算,而 False 值将保持不变。如果提供了 FloatTensor,则会将其加到注意力权重中。

  2. tgt_mask [Ty, Ty] = [T, T] – 在处理目标序列时添加的掩码(可选)。在进行 atten_tgt + tgt_mask 操作时使用。一个示例用途是避免解码器作弊。因此,tgt 向右移动,第一个令牌是起始序列令牌嵌入 SOS/BOS,因此第一个条目为零,而剩余的则不同。请参见附录中的具体示例。如果提供了 ByteTensor,则不允许非零位置参与注意力计算,而零位置将保持不变。如果提供了 BoolTensor,则 True 值的位置不允许参与注意力计算,而 False 值将保持不变。如果提供了 FloatTensor,则会将其加到注意力权重中。

  3. memory_mask [Ty, Tx] = [T, S]– 在处理编码器输出时添加的掩码(可选)。在进行 atten_memory + memory_mask 操作时使用。我不确定有哪些示例用途,但与之前一样,添加 -inf 可以将某些注意力权重设置为零。如果提供了 ByteTensor,则不允许非零位置参与注意力计算,而零位置将保持不变。如果提供了 BoolTensor,则 True 值的位置不允许参与注意力计算,而 False 值将保持不变。如果提供了 FloatTensor,则会将其加到注意力权重中。

  4. src_key_padding_mask [B, Tx] = [N, S] – 用于每个批次的 src 键的 ByteTensor 掩码(可选)。由于您的 src 通常具有不同长度的序列,因此常见的做法是删除您在末尾追加的填充向量。为此,您需要指定每个示例中序列的长度。请参见附录中的具体示例。如果提供了 ByteTensor,则不允许非零位置参与注意力计算,而零位置将保持不变。如果提供了 BoolTensor,则 True 值的位置不允许参与注意力计算,而 False 值将保持不变。如果提供了 FloatTensor,则会将其加到注意力权重中。

  5. tgt_key_padding_mask [B, Ty] = [N, t] – 用于每个批次的 tgt 键的 ByteTensor 掩码(可选)。与之前相同。请参见附录中的具体示例。如果提供了 ByteTensor,则不允许非零位置参与注意力计算,而零位置将保持不变。如果提供了 BoolTensor,则 True 值的位置不允许参与注意力计算,而 False 值将保持不变。如果提供了 FloatTensor,则会将其加到注意力权重中。

  6. memory_key_padding_mask 附录

    来自PyTorch教程的示例 (https://pytorch.org/tutorials/beginner/translation_transformer.html):

    1 src_mask 示例

        src_mask = torch.zeros((src_seq_len, src_seq_len), device=DEVICE).type(torch.bool)
    

    返回一个大小为[Tx, Tx]的布尔张量:

    tensor([[False, False, False,  ..., False, False, False],
             ...,
            [False, False, False,  ..., False, False, False]])
    

    2 tgt_mask示例

        mask = (torch.triu(torch.ones((sz, sz), device=DEVICE)) == 1)
        mask = mask.transpose(0, 1).float()
        mask = mask.masked_fill(mask == 0, float('-inf'))
        mask = mask.masked_fill(mask == 1, float(0.0))
    

    生成右移输出的对角线,该对角线是解码器的输入。
    tensor([[0., -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf,
             -inf, -inf, -inf],
            [0., 0., -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf,
             -inf, -inf, -inf],
            [0., 0., 0., -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf,
             -inf, -inf, -inf],
             ...,
            [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
             0., 0., -inf],
            [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
             0., 0., 0.]])
    

    通常右移的输出在开头有BOS/SOS,教程通过在前面添加BOS/SOS并使用tgt_input = tgt[:-1, :]来进行右移。

    3 _padding

    填充只是为了遮盖结尾处的填充。 源填充通常与内存填充相同。 tgt有自己的序列,因此有自己的填充。 例子:

        src_padding_mask = (src == PAD_IDX).transpose(0, 1)
        tgt_padding_mask = (tgt == PAD_IDX).transpose(0, 1)
        memory_padding_mask = src_padding_mask
    

    输出:

    tensor([[False, False, False,  ...,  True,  True,  True],
            ...,
            [False, False, False,  ...,  True,  True,  True]])
    

    请注意,False表示没有填充令牌(因此在转换器前向传递中使用该值),而True表示存在填充令牌(因此将其屏蔽,以便转换器前向传递不受影响)。
    答案有点分散,但我发现只有这三个参考资料有用(单独的层文档/内容并不是很有用):

1
对于情况3,如果它们相同,我们应该提供哪个掩码?内存和src填充掩码之间有什么区别? - Evan Zamir
我基本上正在构建一个类似于word2vec的模型,但只使用Transformer编码器模块。因此,每个样本最多可以有512个标记,但某些样本可能要少得多,然后显然需要填充标记。与BERT一样,我将15%的标记替换为掩码标记,然后尝试预测缺失的标记。所以现在我想知道用哪种掩码标记来处理填充标记。看起来像是src_key_padding_mask?我只需用1和0填充它吗? - Evan Zamir
1
@EvanZamir 在我的回答中使用的 _key_padding_mask 表示如果有 True,则告诉 PyTorch Transformer 层那里有一个填充标记,因此它会忽略它。我认为填充标记的内容实际上并不重要,只要你正确地输入这个掩码即可。任何带有 True 的都将被忽略,而任何带有 False 的都不会被忽略。我在我的答案中添加了一些注释(建议您阅读)。简而言之,专注于制作正确的掩码,您的序列批处理将被正确处理。 - Charlie Parker
嗨@CharlieParker,感谢您的详细评论。我只有一个问题:如果 src_mask 将只包含 False 值,那么它有什么用处呢?因为您写道 while False values will be unchanged - maq
@maq 我认为这是因为您正在向期望整个张量的API提供整个张量。这是因为PyTorch API接收张量以加速GPU计算,因此当GPU计算完成时,它知道在GPU代码中要更改什么和不更改什么。这样PyTorch才能够正常运行并与CUDA兼容(这是我的猜测)。 - Charlie Parker
显示剩余2条评论

7
我必须说,PyTorch 的实现有些令人困惑,因为它包含了太多的掩码参数。但我可以阐述一下你所指的两个掩码参数。在 MultiheadAttention 机制中,使用了 src_masksrc_key_padding_mask。根据 MultiheadAttention 的文档:

key_padding_mask - 如果提供,那么将忽略键中指定的填充元素。

attn_mask - 防止对某些位置进行注意力计算的 2D 或 3D 掩码。

正如你从论文 Attention is all you need 中所知道的,MultiheadAttention 在编码器和解码器中都被使用。然而,在解码器中,有两种类型的 MultiheadAttention。一种叫做 Masked MultiheadAttention,另一种是普通的 MultiheadAttention。为了适应这两种技术,PyTorch 在他们的 MultiheadAttention 实现中使用了上述两个参数。

所以,长话短说-

  • attn_maskkey_padding_mask 在编码器的 MultiheadAttention 和解码器的 Masked MultiheadAttention 中使用。
  • memory_mask 在解码器的 MultiheadAttention 机制中被使用,如在 这里 指出。

查看 MultiheadAttention 的实现可能会对你有所帮助。

正如从 这里这里 可以看到的,首先使用 src_mask 阻止了对特定位置的注意力,然后使用 key_padding_mask 阻止了对填充标记的关注。

注意: 根据@michael-jungo的评论更新答案。


6
长话短说下方的两个点是不正确的。首先,在自注意力(enc-enc和dec-dec)以及编码器-解码器注意力(enc-dec)中使用attn_maskkey_padding_mask。其次,PyTorch在解码器中不使用src_mask,而是使用memory_mask (它们通常相同,但在API中是分开的)。src_masksrc_key_padding_mask属于编码器的自我关注。最后一句话很好地概括了这个问题。 - Michael Jungo
@wasi,你能否回应Michael的评论并在答案有误时进行更正? - Charlie Parker

6

举个简单的例子,如果我想要构建一个序列推荐器,也就是在给定用户在时间“t”之前购买的物品后,预测在“t + 1”时下一个可能会被购买的物品。

u1 - [i1, i2, i7]
u2 - [i2, i5]
u3 - [i6, i7, i1, i2]

对于这个任务,我可以使用一个Transformer模型,通过在左侧填充0来使序列长度相等。

u1 - [0,  i1, i2, i7]
u2 - [0,  0,  i2, i5]
u3 - [i6, i7, i1, i2]

我将使用key_padding_mask告诉PyTorch忽略0.现在考虑用户u3,给定[i6],我想要预测[i7],然后给定[i6, i7],我想要预测[i1],也就是说,我想要因果关注,以便关注不会窥视未来元素。为此,我将使用attn_mask。因此对于用户u3,attn_mask将如下:

[[True, False, False, False],
 [True, True , False, False],
 [True, True , True , False]
 [True, True , True , True ]]

那么你会使用 src_key_padding_mask 来进行填充吗? - Evan Zamir
是的,如果我需要填充,我会使用一个名为src_key_padding_mask的2D布尔矩阵。 - Sanjay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接