为什么需要位置编码?
问题:Transformer 天生"分不清顺序"
Transformer 的核心是自注意力机制。自注意力计算的是:
Attention(Q,K,V)=softmax(QKTdk)V Attention(Q,K,V) = softmax\left(\frac{QK^T}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dk QKT)V
它本质上是在做"两两相似度加权求和"。这个过程有一个关键特性:排列不变性(Permutation Invariance)。
什么意思?假设输入序列是:
text
"我 爱 你"
对应的词向量是 [v_我, v_爱, v_你]。自注意力会对这三个向量做加权组合。如果把输入顺序换成:
text
"你 爱 我"
对应的词向量是 [v_你, v_爱, v_我]。自注意力计算时,它只是把这三个向量重新排列了一下,计算出的注意力权重和输出结果,本质上还是同一组向量的组合,只是顺序变了。
换句话说,如果没有位置信息,Transformer 看到的输入是一个集合(set) ,而不是一个序列(sequence)。它会认为"我爱你"和"你爱我"是同一个东西。
解决方案:把位置信息"加"进词向量
位置编码的思路很直接:
为序列中每个位置
pos生成一个固定向量 PE(pos),然后把它和该位置的词向量相加。
输入=词嵌入(token)+位置编码(pos) 输入 = 词嵌入(token) + 位置编码(pos) 输入=词嵌入(token)+位置编码(pos)
这样,同一个词出现在不同位置时,输入向量就不同了。模型就能区分"第 0 个位置的我"和"第 2 个位置的我"。
正余弦位置编码的公式
对于位置 pos(从 0 开始)和维度索引 d(从 0 到 dmodel−1d_{model} - 1dmodel−1)
PE(pos,2i)=sin(pos100002i/dmodel) PE(pos, 2i) = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right) PE(pos,2i)=sin(100002i/dmodelpos)
PE(pos,2i+1)=cos(pos100002i/dmodel) PE(pos, 2i+1) = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right) PE(pos,2i+1)=cos(100002i/dmodelpos)
其中:
- i=0,1,...,dmodel2−1i = 0, 1, \dots, \frac{d_{model}}{2} - 1i=0,1,...,2dmodel−1 是维度配对索引;
- 偶数维 2i2i2i 用 sin\sinsin,奇数维 2i+12i+12i+1 用 cos\coscos;
- 同一对 (2i,2i+1)(2i, 2i+1)(2i,2i+1) 共享同一个频率基数 100002i/dmodel10000^{2i/d_{model}}100002i/dmodel。
频率的直观理解
令:
ωi=1100002i/dmodel \omega_i = \frac{1}{10000^{2i/d_{model}}} ωi=100002i/dmodel1
则:
PE(pos,2i)=sin(pos⋅ωi),PE(pos,2i+1)=cos(pos⋅ωi) PE(pos, 2i) = \sin(pos \cdot \omega_i), \quad PE(pos, 2i+1) = \cos(pos \cdot \omega_i) PE(pos,2i)=sin(pos⋅ωi),PE(pos,2i+1)=cos(pos⋅ωi)
- iii 小 →ωi\to \omega_i→ωi 大 →\to→ 频率高 →\to→ 波长短 →\to→ 能精细区分相邻位置;
- iii 大 →ωi\to \omega_i→ωi 小 →\to→ 频率低 →\to→ 波长长 →\to→ 能覆盖远距离。
这就像用不同频率的波来"编码"位置,形成一个多尺度的表示体系。
例子
为了能手动计算,我们取一个很小的模型:
text
d_model = 4
序列长度 N = 3,位置 pos = 0, 1, 2
计算频率
i=0,1i = 0, 1i=0,1
i = 0:
100002×0/4=100000=110000^{2 \times 0 / 4} = 10000^0 = 1100002×0/4=100000=1
ω0=11=1\omega_0 = \frac{1}{1} = 1ω0=11=1
i = 1:
100002×1/4=100000.5=10010000^{2 \times 1 / 4} = 10000^{0.5} = 100100002×1/4=100000.5=100
ω1=1100=0.01\omega_1 = \frac{1}{100} = 0.01ω1=1001=0.01
计算每个位置的编码向量
位置 pos = 0
| 维度 d | 公式 | 计算 | 值 |
|---|---|---|---|
| 0 (2i, i=0) | sin(0×1)\sin(0 \times 1)sin(0×1) | sin(0)\sin(0)sin(0) | 0 |
| 1 (2i+1, i=0) | cos(0×1)\cos(0 \times 1)cos(0×1) | cos(0)\cos(0)cos(0) | 1 |
| 2 (2i, i=1) | sin(0×0.01)\sin(0 \times 0.01)sin(0×0.01) | sin(0)\sin(0)sin(0) | 0 |
| 3 (2i+1, i=1) | cos(0×0.01)\cos(0 \times 0.01)cos(0×0.01) | cos(0)\cos(0)cos(0) | 1 |
PE(0) = 0, 1, 0, 1
位置 pos = 1
| 维度 d | 公式 | 计算 | 值 |
|---|---|---|---|
| 0 | sin(1×1)\sin(1 \times 1)sin(1×1) | sin(1)\sin(1)sin(1) | 0.8415 |
| 1 | cos(1×1)\cos(1 \times 1)cos(1×1) | cos(1)\cos(1)cos(1) | 0.5403 |
| 2 | sin(1×0.01)\sin(1 \times 0.01)sin(1×0.01) | sin(0.01)\sin(0.01)sin(0.01) | 0.0100 |
| 3 | cos(1×0.01)\cos(1 \times 0.01)cos(1×0.01) | cos(0.01)\cos(0.01)cos(0.01) | 0.9999 |
PE(1) = 0.8415, 0.5403, 0.0100, 0.9999
位置 pos = 2
| 维度 d | 公式 | 计算 | 值 |
|---|---|---|---|
| 0 | sin(2×1)\sin(2 \times 1)sin(2×1) | sin(2)\sin(2)sin(2) | 0.9093 |
| 1 | cos(2×1)\cos(2 \times 1)cos(2×1) | cos(2)\cos(2)cos(2) | -0.4161 |
| 2 | sin(2×0.01)\sin(2 \times 0.01)sin(2×0.01) | sin(0.02)\sin(0.02)sin(0.02) | 0.0200 |
| 3 | cos(2×0.01)\cos(2 \times 0.01)cos(2×0.01) | cos(0.02)\cos(0.02)cos(0.02) | 0.9998 |
PE(2) = 0.9093, -0.4161, 0.0200, 0.9998
三个位置的编码对比
text
pos=0: [0.0000, 1.0000, 0.0000, 1.0000]
pos=1: [0.8415, 0.5403, 0.0100, 0.9999]
pos=2: [0.9093, -0.4161, 0.0200, 0.9998]
观察:
- 每个位置的编码向量都独一无二,模型可以据此区分位置;
- 低维(d=0,1)变化剧烈,高维(d=2,3)变化缓慢;
- 所有值都在 −1,1 之间,数值稳定
与词向量相加
假设"我"的词嵌入是 [0.2, 0.5, -0.1, 0.3],它出现在位置 0:
text
输入(我, 0) = [0.2, 0.5, -0.1, 0.3] + [0, 1, 0, 1]
= [0.2, 1.5, -0.1, 1.3]
同一个词"我"如果出现在位置 1:
text
输入(我, 1) = [0.2, 0.5, -0.1, 0.3] + [0.8415, 0.5403, 0.0100, 0.9999]
= [1.0415, 1.0403, -0.0900, 1.2999]
虽然词相同,但加上位置编码后,输入向量完全不同。模型就能区分"位置 0 的我"和"位置 1 的我"。
正余弦编码如何提供位置信息?
唯一性:每个位置有独特模式
从上面的例子可以看到,PE(0)、PE(1)、PE(2) 各不相同。
对于任意两个不同位置 pos1≠pos2,它们的编码向量一定不同。模型通过观察这些模式,就能判断当前 token 在序列中的位置。
相对位置:核心优势
正余弦编码最优雅的性质是:对于固定的偏移量 k,PE(pos+k) 可以表示为 PE(pos) 的线性变换,且这个变换只依赖于 k,与 pos 无关。
用三角恒等式证明:
sin((pos+k)ωi)=sin(posωi)cos(kωi)+cos(posωi)sin(kωi) \sin((pos + k)\omega_i) = \sin(pos\omega_i)\cos(k\omega_i) + \cos(pos\omega_i)\sin(k\omega_i) sin((pos+k)ωi)=sin(posωi)cos(kωi)+cos(posωi)sin(kωi)
cos((pos+k)ωi)=cos(posωi)cos(kωi)−sin(posωi)sin(kωi) \cos((pos + k)\omega_i) = \cos(pos\omega_i)\cos(k\omega_i) - \sin(pos\omega_i)\sin(k\omega_i) cos((pos+k)ωi)=cos(posωi)cos(kωi)−sin(posωi)sin(kωi)
写成矩阵形式:
PE(pos+k,2i)PE(pos+k,2i+1)\]=\[cos(kωi)sin(kωi)−sin(kωi)cos(kωi)\]\[PE(pos,2i)PE(pos,2i+1)\] \\begin{bmatrix} PE(pos+k, 2i) \\\\ PE(pos+k, 2i+1) \\end{bmatrix} = \\begin{bmatrix} \\cos(k\\omega_i) \& \\sin(k\\omega_i) \\\\ -\\sin(k\\omega_i) \& \\cos(k\\omega_i) \\end{bmatrix} \\begin{bmatrix} PE(pos, 2i) \\\\ PE(pos, 2i+1) \\end{bmatrix} \[PE(pos+k,2i)PE(pos+k,2i+1)\]=\[cos(kωi)−sin(kωi)sin(kωi)cos(kωi)\]\[PE(pos,2i)PE(pos,2i+1)
这是一个旋转矩阵,旋转角度 kωikωikωi 只取决于偏移 kkk 和频率ωiωiωi。
数值验证
取 i=0,ω0=1,k=1:i = 0, \omega_0 = 1, k = 1:i=0,ω0=1,k=1:
旋转矩阵:
cos(1)sin(1)−sin(1)cos(1)\]=\[0.54030.8415−0.84150.5403\] \\begin{bmatrix} \\cos(1) \& \\sin(1) \\\\ -\\sin(1) \& \\cos(1) \\end{bmatrix} = \\begin{bmatrix} 0.5403 \& 0.8415 \\\\ -0.8415 \& 0.5403 \\end{bmatrix} \[cos(1)−sin(1)sin(1)cos(1)\]=\[0.5403−0.84150.84150.5403
用位置 0 的 (PE(0,0),PE(0,1))=(0,1) 验证:
0.54030.8415−0.84150.5403\]\[01\]=\[0.84150.5403\] \\begin{bmatrix} 0.5403 \& 0.8415 \\\\ -0.8415 \& 0.5403 \\end{bmatrix} \\begin{bmatrix} 0 \\\\ 1 \\end{bmatrix} = \\begin{bmatrix} 0.8415 \\\\ 0.5403 \\end{bmatrix} \[0.5403−0.84150.84150.5403\]\[01\]=\[0.84150.5403
这正好是 PE(1,0),PE(1,1)!
再用位置 1 验证 k=1 得到位置 2:
0.54030.8415−0.84150.5403\]\[0.84150.5403\]=\[0.5403×0.8415+0.8415×0.5403−0.8415×0.8415+0.5403×0.5403\]=\[0.9093−0.4161\] \\begin{bmatrix} 0.5403 \& 0.8415 \\\\ -0.8415 \& 0.5403 \\end{bmatrix} \\begin{bmatrix} 0.8415 \\\\ 0.5403 \\end{bmatrix} = \\begin{bmatrix} 0.5403 \\times 0.8415 + 0.8415 \\times 0.5403 \\\\ -0.8415 \\times 0.8415 + 0.5403 \\times 0.5403 \\end{bmatrix} = \\begin{bmatrix} 0.9093 \\\\ -0.4161 \\end{bmatrix} \[0.5403−0.84150.84150.5403\]\[0.84150.5403\]=\[0.5403×0.8415+0.8415×0.5403−0.8415×0.8415+0.5403×0.5403\]=\[0.9093−0.4161
这正是 PE(2,0),PE(2,1)!
这意味着什么?
自注意力机制在计算 QKTQK^{T}QKT 时,如果它能学到这个旋转关系,就能轻松地判断:
"当前位置的编码,和另一个位置的编码之间,相差了 k 步。"
这使得模型不仅能感知绝对位置,还能感知相对距离。
对于语言建模来说,相对距离往往比绝对位置更重要。比如"动词在主语后面 2 个词"这种模式,无论主语出现在句子开头还是中间,相对距离都是 2。
为什么正余弦编码有这些优点?
| 优点 | 原因 |
|---|---|
| 无需训练 | 公式固定,不增加可学习参数 |
| 数值稳定 | sin/cos 值域 −1,1-1, 1−1,1,与词向量相加不会爆炸 |
| 唯一性 | 不同位置编码不同,可区分位置 |
| 相对位置感知 | 三角恒等式保证 PE(pos+k)PE(pos+k)PE(pos+k) 是 PE(pos)PE(pos)PE(pos) 的线性变换 |
| 可外推 | 公式对任意 pospospos 都有定义,理论上可处理比训练时更长的序列 |
| 多尺度 | 不同维度频率不同,低维管局部,高维管全局 |
和可学习位置嵌入的对比
| 对比项 | 正余弦编码 | 可学习位置嵌入 |
|---|---|---|
| 参数 | 无 | 有,每个位置一个向量 |
| 训练 | 不需要 | 需要 |
| 外推 | 理论上可外推到更长序列 | 只能处理训练时见过的位置 |
| 相对位置 | 天然支持 | 需要模型自己学 |
| 典型应用 | 原始 Transformer | BERT、GPT 等 |
总结
正余弦位置编码是什么?
用不同频率的 sin/cos 函数,为序列中每个位置生成一个固定向量,加到词嵌入上,让 Transformer 感知顺序。
为什么需要它?
Transformer 的自注意力是排列不变的,没有位置信息时,"我爱你"和"你爱我"无法区分。
它如何提供位置信息?
每个位置的编码向量独一无二;且通过三角恒等式,PE(pos+k) 是 PE(pos)的线性变换,变换只依赖于 kk,因此模型能同时感知绝对位置和相对距离。
数值例子核心结论:
text
pos=0: [0.0000, 1.0000, 0.0000, 1.0000]
pos=1: [0.8415, 0.5403, 0.0100, 0.9999]
pos=2: [0.9093, -0.4161, 0.0200, 0.9998]
三个位置编码各不相同;用旋转矩阵可以从 PE(0) 推出 PE(1),从 PE(1) 推出 PE(2),旋转角只取决于偏移量 1。
这就是正余弦编码既能区分位置、又能建模相对位置的根本原因。