在Transformer的自注意力机制中,Masked-Attention(掩码自注意力) 是生成式大模型(如GPT系列)的核心模块。它通过“屏蔽未来Token”的方式,强制模型在生成文本时只能依赖已生成的内容,从而实现自回归式的序列生成。
1. 为什么需要Masked-Attention?
在标准的Self-Attention中,每个Token都能看到序列中的所有其他Token,包括它“后面”的Token。这对于文本理解任务(如BERT)是有效的,但对于生成式任务(如GPT) 来说,这是一个致命的问题:
- • 如果模型在生成第 个Token时,就能“看到”第 个及之后的Token,它就可以直接“抄答案”,而不是真正地学习如何预测下一个词。
- • 这会导致模型无法学习到合理的语言模式,生成的文本会变得毫无逻辑。
因此,我们需要一种机制,强制模型在生成时只能看到当前位置及之前的Token,而看不到未来的Token。这就是Masked-Attention的核心作用。
2. Masked-Attention的核心原理:下三角掩码
2.1 直观理解
Masked-Attention通过一个下三角矩阵(Lower Triangular Matrix) 来实现对未来Token的屏蔽:
- • 下三角(保留部分):矩阵中对角线及以下的位置()被保留,代表当前Token 可以关注它自己及之前所有的Token。
- • 上三角(屏蔽部分):矩阵中对角线以上的位置()被屏蔽(赋值为 ),代表当前Token 无法看到它之后的任何Token。
这种结构确保了模型在生成序列时,只能“向后看”,而不能“向前看”,从而保证了生成过程的自回归性。
2.2 数学原理
在Scaled Dot-Product Attention的基础上,我们引入一个下三角掩码矩阵 :
将这个掩码矩阵加到注意力分数矩阵 上,再进行SoftMax归一化:
这样,被屏蔽的上三角位置的注意力分数会变成 ,经过SoftMax后,这些位置的权重就会变为0,从而不会对最终的输出产生任何影响。
3. 两种关键的Mask类型
在实际应用中,除了用于生成的下三角掩码(Look-ahead Mask),我们还会遇到另一种用于处理不等长序列的填充掩码(Padding Mask)。
3.1 Look-ahead Mask(前瞻掩码)
- • 作用:专门用于生成式模型(如GPT),强制模型在生成第 个Token时,只能看到第 到第 个Token,看不到第 及之后的Token。
- • 实现:生成一个下三角矩阵,对角线以上的位置全部设为1(表示需要屏蔽),对角线及以下设为0。
3.2 Padding Mask(填充掩码)
- • 作用:在处理长度不一的序列时,我们会用特殊的
<pad> Token将短序列填充到统一长度。这些填充的Token不包含任何有效语义,因此需要在注意力计算中被屏蔽掉。 - • 实现:生成一个掩码矩阵,所有对应
<pad> Token的位置设为1,其他位置设为0。
在实际的Transformer实现中,这两种掩码通常会被合并使用,以同时处理序列长度和生成顺序的问题。
4. Special Tokens(特殊Token)的角色
如你提供的示意图所示,Transformer中定义了一系列特殊Token,它们在注意力计算中扮演着重要角色:
| | | |
<bos> | | | |
<eos> | | | |
<pad> | | | |
<sep> | | 用于区分两个或更多不同的序列(如翻译任务中的源语言和目标语言) | |
<cls> | | | |
<unk> | | | |
5. 代码实现(PyTorch版)
下面是一个完整的Masked Self-Attention实现,它严格遵循了上述原理:
import torchimport torch.nn as nnimport torch.nn.functional as Fdefcreate_mask(seq_len):""" 创建下三角掩码(Look-ahead Mask) """ mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1)return mask.bool()defscaled_dot_product_attention(q, k, v, mask=None):""" 实现带掩码的缩放点积注意力 """ d_k = q.size(-1) scores = torch.matmul(q, k.transpose(-2, -1)) / torch.sqrt(torch.tensor(d_k, dtype=torch.float32))if mask isnotNone: scores = scores.masked_fill(mask == 1, -1e9) attn_weights = F.softmax(scores, dim=-1) output = torch.matmul(attn_weights, v)return output, attn_weightsclassMaskedMultiHeadAttention(nn.Module):def__init__(self, d_model, num_heads):super().__init__()self.d_model = d_modelself.num_heads = num_headsself.d_k = d_model // num_headsself.w_q = nn.Linear(d_model, d_model)self.w_k = nn.Linear(d_model, d_model)self.w_v = nn.Linear(d_model, d_model)self.w_o = nn.Linear(d_model, d_model)defsplit_heads(self, x): batch_size, seq_len, _ = x.shapereturn x.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)defforward(self, x, mask=None): batch_size, seq_len, _ = x.shape# 线性投影 q = self.w_q(x) k = self.w_k(x) v = self.w_v(x)# 拆分多头 q = self.split_heads(q) k = self.split_heads(k) v = self.split_heads(v)# 合并掩码(如果有Padding Mask,需要在这里合并)if mask isnotNone: mask = mask.unsqueeze(1) # [batch_size, 1, seq_len, seq_len]# 带掩码的自注意力计算 attn_output, _ = scaled_dot_product_attention(q, k, v, mask)# 拼接多头 attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)# 最终投影 output = self.w_o(attn_output)return output# 测试代码if __name__ == "__main__":# 初始化模型 d_model = 512 num_heads = 8 masked_attn = MaskedMultiHeadAttention(d_model, num_heads)# 模拟输入 batch_size = 2 seq_len = 5 x = torch.randn(batch_size, seq_len, d_model)# 创建下三角掩码 look_ahead_mask = create_mask(seq_len) look_ahead_mask = look_ahead_mask.unsqueeze(0).repeat(batch_size, 1, 1) # [2, 5, 5]# 执行带掩码的自注意力 output = masked_attn(x, look_ahead_mask)print(f"输入形状: {x.shape}")print(f"输出形状: {output.shape}")
6. 多模态场景下的Masked-Attention拓展
在多模态大模型(如GPT-4V、Qwen-VL)中,Masked-Attention的逻辑得到了进一步拓展:
- 1. 图文混合序列掩码:当输入是图文混合序列时,我们需要确保文本生成时,只能看到已生成的文本和已输入的图像,而不能看到未来的文本。
- 2. 图像块掩码:在处理图像时,有时也需要对图像块进行掩码(如MAE预训练),但这与生成式任务的下三角掩码逻辑不同。
- 3. 跨模态注意力掩码:在Encoder-Decoder架构中,Decoder的注意力不仅需要对自身的未来Token进行掩码,还需要对Encoder的输出进行对齐,这就需要更复杂的掩码策略。
7. 总结
Masked-Attention是Transformer生成式能力的核心基石,它通过下三角掩码强制模型遵守自回归生成的规则,确保了生成文本的逻辑性和连贯性。
- • 核心思想:通过下三角矩阵屏蔽未来Token,强制模型“向后看”。
- • 拓展方向:在多模态场景下,掩码逻辑需要适配图文混合序列的生成需求。