我很难理解transformers。一切都在逐渐变得清晰,但有一件事让我困惑的是,在编码器层和解码器层的前向函数中传递的src_mask和src_key_padding_mask之间有什么区别。
https://pytorch.org/docs/master/_modules/torch/nn/modules/transformer.html#Transformer
我很难理解transformers。一切都在逐渐变得清晰,但有一件事让我困惑的是,在编码器层和解码器层的前向函数中传递的src_mask和src_key_padding_mask之间有什么区别。
https://pytorch.org/docs/master/_modules/torch/nn/modules/transformer.html#Transformer
需要注意的是,在transformer中使用张量_mask
和_key_padding_mask
的区别。
当进行attention操作时,我们通常会得到一个由所有比较组成的平方中间张量,大小为[Tx, Tx]
(对于编码器的输入),[Ty, Ty]
(对于移位输出 - 解码器的输入之一)和[Ty, Tx]
(用于存储掩码 - 编码器/存储器输出与解码器/移位输出之间的attention)。
因此,我们可以得到transformer中每个掩码的用途(请注意,来自pytorch文档的符号表示如下:其中Tx=S是源序列长度
(例如,输入批次的最大值),
Ty=T是目标序列长度
(例如,目标长度的最大值),
B=N是批次大小
,
D=E是特征数量
):
src_mask [Tx, Tx] = [S, S]
– 在处理源序列时添加的掩码(可选)。在进行 atten_src + src_mask
操作时使用。我不确定有哪些输入示例,但可以查看 tgt_mask 以获取示例。通常的用途是添加 -inf
,以便根据需要屏蔽 src_attention。如果提供了 ByteTensor,则不允许非零位置参与注意力计算,而零位置将保持不变。如果提供了 BoolTensor,则 True 值的位置不允许参与注意力计算,而 False 值将保持不变。如果提供了 FloatTensor,则会将其加到注意力权重中。
tgt_mask [Ty, Ty] = [T, T]
– 在处理目标序列时添加的掩码(可选)。在进行 atten_tgt + tgt_mask
操作时使用。一个示例用途是避免解码器作弊。因此,tgt 向右移动,第一个令牌是起始序列令牌嵌入 SOS/BOS,因此第一个条目为零,而剩余的则不同。请参见附录中的具体示例。如果提供了 ByteTensor,则不允许非零位置参与注意力计算,而零位置将保持不变。如果提供了 BoolTensor,则 True 值的位置不允许参与注意力计算,而 False 值将保持不变。如果提供了 FloatTensor,则会将其加到注意力权重中。
memory_mask [Ty, Tx] = [T, S]
– 在处理编码器输出时添加的掩码(可选)。在进行 atten_memory + memory_mask
操作时使用。我不确定有哪些示例用途,但与之前一样,添加 -inf
可以将某些注意力权重设置为零。如果提供了 ByteTensor,则不允许非零位置参与注意力计算,而零位置将保持不变。如果提供了 BoolTensor,则 True 值的位置不允许参与注意力计算,而 False 值将保持不变。如果提供了 FloatTensor,则会将其加到注意力权重中。
src_key_padding_mask [B, Tx] = [N, S]
– 用于每个批次的 src 键的 ByteTensor 掩码(可选)。由于您的 src 通常具有不同长度的序列,因此常见的做法是删除您在末尾追加的填充向量。为此,您需要指定每个示例中序列的长度。请参见附录中的具体示例。如果提供了 ByteTensor,则不允许非零位置参与注意力计算,而零位置将保持不变。如果提供了 BoolTensor,则 True 值的位置不允许参与注意力计算,而 False 值将保持不变。如果提供了 FloatTensor,则会将其加到注意力权重中。
tgt_key_padding_mask [B, Ty] = [N, t]
– 用于每个批次的 tgt 键的 ByteTensor 掩码(可选)。与之前相同。请参见附录中的具体示例。如果提供了 ByteTensor,则不允许非零位置参与注意力计算,而零位置将保持不变。如果提供了 BoolTensor,则 True 值的位置不允许参与注意力计算,而 False 值将保持不变。如果提供了 FloatTensor,则会将其加到注意力权重中。
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
表示存在填充令牌(因此将其屏蔽,以便转换器前向传递不受影响)。
答案有点分散,但我发现只有这三个参考资料有用(单独的层文档/内容并不是很有用):
src_key_padding_mask
?我只需用1和0填充它吗? - Evan Zamir_key_padding_mask
表示如果有 True
,则告诉 PyTorch Transformer 层那里有一个填充标记,因此它会忽略它。我认为填充标记的内容实际上并不重要,只要你正确地输入这个掩码即可。任何带有 True
的都将被忽略,而任何带有 False
的都不会被忽略。我在我的答案中添加了一些注释(建议您阅读)。简而言之,专注于制作正确的掩码,您的序列批处理将被正确处理。 - Charlie Parkersrc_mask
将只包含 False
值,那么它有什么用处呢?因为您写道 while False values will be unchanged
。 - maqMultiheadAttention
机制中,使用了 src_mask
和 src_key_padding_mask
。根据 MultiheadAttention 的文档:
key_padding_mask - 如果提供,那么将忽略键中指定的填充元素。
attn_mask - 防止对某些位置进行注意力计算的 2D 或 3D 掩码。
正如你从论文 Attention is all you need 中所知道的,MultiheadAttention 在编码器和解码器中都被使用。然而,在解码器中,有两种类型的 MultiheadAttention。一种叫做 Masked MultiheadAttention
,另一种是普通的 MultiheadAttention
。为了适应这两种技术,PyTorch 在他们的 MultiheadAttention 实现中使用了上述两个参数。
所以,长话短说-
attn_mask
和 key_padding_mask
在编码器的 MultiheadAttention
和解码器的 Masked MultiheadAttention
中使用。memory_mask
在解码器的 MultiheadAttention
机制中被使用,如在 这里 指出。查看 MultiheadAttention 的实现可能会对你有所帮助。
正如从 这里 和 这里 可以看到的,首先使用 src_mask
阻止了对特定位置的注意力,然后使用 key_padding_mask
阻止了对填充标记的关注。
注意: 根据@michael-jungo的评论更新答案。
attn_mask
和key_padding_mask
。其次,PyTorch在解码器中不使用src_mask
,而是使用memory_mask
(它们通常相同,但在API中是分开的)。src_mask
和src_key_padding_mask
属于编码器的自我关注。最后一句话很好地概括了这个问题。 - Michael Jungo举个简单的例子,如果我想要构建一个序列推荐器,也就是在给定用户在时间“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_mask只是一个正方形矩阵,用于过滤注意力权重。... src_key_padding_mask更像是一个填充标记,在src序列中掩盖特定的令牌(也就是注意力矩阵的整个列/行设置为“-inf”)。
https://discuss.pytorch.org/t/nn-transformer-explaination/53175/5 - Charlie Parker