🧠 图解记忆:内容向量告诉模型是什么,位置编码告诉模型在哪里;点击图片可查看原图。
💡 答案要点
位置编码 = 给每个token注入位置信息
为什么需要?
问题:
Self-Attention 是置换不变的(permutation-invariant)
例子:
"我 爱 你" 和 "你 爱 我"
如果没有位置编码,Attention 输出可能相同
→ 无法区分顺序
解决:
加入位置信息,让模型知道每个token的位置Transformer 的位置编码(Sinusoidal):
公式:
python
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
其中:
pos: 位置(0, 1, 2, ...)
i: 维度索引(0 到 d_model/2)
d_model: 模型维度(如512)实现:
python
def positional_encoding(max_len, d_model):
# 创建位置矩阵
position = torch.arange(max_len).unsqueeze(1) # (max_len, 1)
# 创建维度矩阵
div_term = torch.exp(
torch.arange(0, d_model, 2) *
-(math.log(10000.0) / d_model)
) # (d_model/2,)
# 初始化位置编码
pe = torch.zeros(max_len, d_model)
# 偶数维度用sin
pe[:, 0::2] = torch.sin(position * div_term)
# 奇数维度用cos
pe[:, 1::2] = torch.cos(position * div_term)
return pe
# 使用
pe = positional_encoding(max_len=100, d_model=512)
# pe.shape: (100, 512)
# 加到输入上
x = embedding(input_ids) + pe[: input_ids.size(1)]为什么用sin/cos?
优势:
- 确定性:位置固定,不需要学习
- 可生成任意位置的编码:公式能计算训练长度之外的位置,但模型在更长序列上的有效能力仍需实测
- 周期性:不同位置间有规律的相对关系
相对位置关系:
python
# PE(pos + k) 可以表示为 PE(pos) 的线性组合
# 这让模型能学习相对位置
例如:
PE(5) 和 PE(10) 的距离
= PE(1) 和 PE(6) 的距离
(相对距离都是5)可视化(前10个位置,前64维):
位置 维度0 维度1 维度2 维度3 ...
0 0.00 1.00 0.00 1.00
1 0.84 0.54 0.01 1.00
2 0.91 -0.42 0.02 1.00
3 0.14 -0.99 0.03 1.00
...其他位置编码方案:
| 方案 | 优点 | 缺点 | 应用 |
|---|---|---|---|
| Sinusoidal | 确定性,可计算更长位置 | 训练长度外效果无保证 | Transformer |
| Learned | 可学习,灵活 | 超出已训练位置通常需要扩展并继续训练 | BERT |
| RoPE | 相对位置,旋转 | 实现复杂 | LLaMA |
| ALiBi | 注意力偏置 | 需要重训练 | BLOOM |
现代改进:RoPE(Rotary Position Embedding):
python
# LLaMA/GPT-NeoX 使用
# 核心思想:在注意力计算时旋转 Q 和 K
def apply_rotary_emb(q, k, cos, sin):
# 旋转 Q 和 K
q_rot = rotate_half(q)
k_rot = rotate_half(k)
q = q * cos + q_rot * sin
k = k * cos + k_rot * sin
return q, k
# 优势:
# - 相对位置编码
# - 便于表达相对位置信息
# - 长度扩展仍需要缩放策略并通过目标任务验证面试话术:
"Transformer 需要额外注入位置信息。Sin/Cos 编码无训练参数且能计算任意位置;RoPE 通过旋转 Q、K 让注意力分数自然携带相对位置信息。两者在训练长度外都不能只凭公式宣称有效,必须做长上下文评测。"
