🎯 课程主题
填充掩码(Padding Mask)注意力机制——处理批次中不等长序列的并行计算方案。
📝 核心知识点
1. 填充掩码的必要性
- 概念说明:Transformer 要求同一批次所有句子长度一致,以便 GPU 并行计算。
- 关键细节:
- 批次(Batch)中句子长度不一(如 5 词、3 词、2 词)
- 找到批次中最长句子长度,对短句填充 0(Padding)
- 填充后的序列参与矩阵运算,但填充部分无实际语义
- 需通过 Mask 机制屏蔽填充部分的影响
2. 填充过程
- 概念说明:将短句填充至批次最大长度。
- 关键细节:
- 假设批次最长句子长度为 3
- 长度 2 的句子 $[A_1, A_2]$ → 填充为 $[A_1, A_2, A_3]$,其中 $A_3$ 全为 0
- 填充后批次内所有句子长度一致,可并行计算
3. 填充 Mask 的作用机制
- 概念说明:通过将填充位置的注意力得分设为负无穷,经 Softmax 后变为 0。
- 关键细节:
- 注意力得分矩阵中,填充部分对应位置为黑色(无用信息)
- 填充 Mask:原始位置为 0,填充位置为 $-\infty$
- 注意力得分 + Mask:原始位置不变,填充位置变为 $-\infty$
- 经 Softmax:$-\infty \to 0$,即填充位置的注意力得分为 0
- 注意力得分矩阵 × V:填充位置权重为 0,不影响输出
4. 填充 Mask 的效果
- 概念说明:填充部分在注意力计算中被完全忽略。
- 关键细节:
- 输入 $[A_1, A_2, A_3(\text{padding})]$ → 输出 $[B_1, B_2, B_3(\text{padding})]$
- $B_3$ 全为 0(无用信息),仅为方便并行计算
- 不影响 $B_1, B_2$ 的计算结果
5. 批次间序列长度规则
- 概念说明:同批次必须等长,不同批次可不等长。
- 关键细节:
- 同批次:序列长度必须一致(通过填充实现)
- 不同批次:序列长度可以不同(如批次1长度3,批次2长度5)
- 训练时建议将长度相近的句子放在同一批次,减少填充量
🧮 核心公式与推导
填充 Mask 的数学表达:
$$\text{Mask}_{ij} = \begin{cases} 0 & \text{如果 } j \text{ 是有效位置} \ -\infty & \text{如果 } j \text{ 是填充位置} \end{cases}$$
带 Mask 的注意力计算:
$$\text{Attention}(Q, K, V) = \text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}} + \text{Mask}\right) V$$
Softmax 对 $-\infty$ 的处理:
$$\text{Softmax}(-\infty) = \frac{e^{-\infty}}{\sum e^{x_k}} = \frac{0}{\sum e^{x_k}} = 0$$
物理意义:填充位置的注意力权重为 0,不参与信息融合,保证只有有效 token 的信息被提取。
🏗️ 模型架构与数据流向
💻 代码实战
import torch
import torch.nn.functional as F
import math
def attention_with_padding_mask(Q, K, V, padding_mask):
"""
带填充掩码的注意力计算
Q: [batch, L, d_k]
K: [batch, L, d_k]
V: [batch, L, d_v]
padding_mask: [batch, L] 1表示有效, 0表示填充
"""
d_k = Q.size(-1)
# 1. 计算注意力得分
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
# scores: [batch, L, L]
# 2. 构建填充mask: [batch, L] → [batch, 1, L] → 广播到 [batch, L, L]
mask = padding_mask.unsqueeze(1) # [batch, 1, L]
scores = scores.masked_fill(mask == 0, float('-inf'))
# 3. Softmax归一化 (填充位置变为0)
attn_weights = F.softmax(scores, dim=-1)
# 4. 加权融合
output = torch.matmul(attn_weights, V)
return output, attn_weights
# 示例: batch=2, 一个长度3, 一个长度2(填充为3)
Q = torch.randn(2, 3, 64)
K = torch.randn(2, 3, 64)
V = torch.randn(2, 3, 64)
# 填充mask: 第1个样本全部有效, 第2个样本最后1位是填充
padding_mask = torch.tensor([
[1, 1, 1], # 样本1: 3个有效token
[1, 1, 0] # 样本2: 2个有效token, 第3个是填充
])
output, attn = attention_with_padding_mask(Q, K, V, padding_mask)
print(output.shape) # torch.Size([2, 3, 64])
# 第2个样本的第3行(填充位)注意力权重全为0
print(attn[1, 2, :]) # 接近0
⚠️ 常见问题与避坑指南
- 填充 Mask 的值是 $-\infty$(不是 0),经 Softmax 后才变为 0
- 同批次必须等长,不同批次可不等长
- 训练时建议按长度排序分批,减少不必要的填充
- 填充位置的输出全为 0,不携带有效信息
💡 个人总结与延伸
填充掩码是 Transformer 处理变长序列并行计算的关键技术,通过将填充位置的注意力得分置为 $-\infty$,使 Softmax 后权重为 0,从而屏蔽无效信息。这一机制在训练和批量推理中广泛使用,是现代大模型高效训练的基础。