正弦/余弦位置编码 = 用不同频率的波浪给每个位置打独特的"时间戳",让Transformer既能知道绝对位置,又能通过数学运算推导出相对距离。
一句话理解
位置编码 = 给每个座位(位置)发一个独特的"波形密码",让模型知道谁坐在第几排。
为什么需要位置编码?
Transformer没有循环结构(不像RNN逐个处理),它是一次性看完全部token。所以它天生是"瞎子"——不知道顺序!
| 输入 | 对Transformer来说 |
|---|---|
| “小猫在打滚” | 一堆向量,不知道谁在前谁在后 |
| “滚打在小猫” | 也是一堆向量,完全一样对待 |
位置编码就是给每个位置贴一个"座位号",让模型能区分顺序。
正弦/余弦是什么?(直觉理解)
想象一个弹簧或波浪:
位置0: 🌊 波浪起点(sin=0, cos=1) 位置1: 🌊 波浪往前一点(sin=0.84, cos=0.54) 位置2: 🌊 再往前(sin=0.91, cos=-0.42) 位置3: 🌊 更前面(sin=0.14, cos=-0.99)每个位置对应波浪上的一个不同点,所以每个位置的编码独一无二。
具体公式拆解
PE(pos,2i)=sin(pos100002i/d)PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d}}\right)PE(pos,2i)=sin(100002i/dpos)
PE(pos,2i+1)=cos(pos100002i/d)PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d}}\right)PE(pos,2i+1)=cos(100002i/dpos)
参数含义
| 符号 | 含义 | 示例 |
|---|---|---|
pos | token的位置(第几个字) | 0, 1, 2, 3, 4 |
i | 向量的维度索引 | 0, 1, 2, 3 |
d | 总维度(这里=4) | 4 |
2i | 偶数维度(0, 2) | 用sin |
2i+1 | 奇数维度(1, 3) | 用cos |
以"小猫在打滚"为例,手算位置编码
假设d=4(4维向量),计算每个位置的编码:
位置0(“小”)
维度0 (i=0): sin(0 / 10000^(0/4)) = sin(0) = 0 维度1 (i=0): cos(0 / 10000^(0/4)) = cos(0) = 1 维度2 (i=1): sin(0 / 10000^(2/4)) = sin(0) = 0 维度3 (i=1): cos(0 / 10000^(2/4)) = cos(0) = 1位置0编码 = [0, 1, 0, 1]
位置1(“猫”)
维度0: sin(1 / 10000^0) = sin(1) = 0.84 维度1: cos(1 / 10000^0) = cos(1) = 0.54 维度2: sin(1 / 10000^0.5) = sin(1/100) = sin(0.01) = 0.01 维度3: cos(1 / 10000^0.5) = cos(0.01) = 1.00位置1编码 = [0.84, 0.54, 0.01, 1.00]
位置2(“在”)
维度0: sin(2) = 0.91 维度1: cos(2) = -0.42 维度2: sin(0.02) = 0.02 维度3: cos(0.02) = 1.00位置2编码 = [0.91, -0.42, 0.02, 1.00]
完整位置编码表
| 位置 | token | 维度0 (sin) | 维度1 (cos) | 维度2 (sin) | 维度3 (cos) |
|---|---|---|---|---|---|
| 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 |
| 4 | 滚 | -0.76 | -0.65 | 0.04 | 1.00 |
关键特性
1. 每个位置编码独一无二
位置0: [0, 1, 0, 1] 位置1: [0.84, 0.54, 0.01, 1] 位置2: [0.91, -0.42, 0.02, 1]没有两个位置是一样的!
2. 相对位置可以计算
神奇之处:模型可以通过编码相减,知道两个token相距多远!
位置1 - 位置0 的某种组合 → 距离是1 位置3 - 位置1 的某种组合 → 距离是2这是因为正弦/余弦的周期性和线性组合性质。
3. 值域固定 [-1, 1]
不管句子多长,位置编码的值永远在-1到1之间,不会爆炸。
直观比喻
想象5个人站成一排,每个人手里拿一个不同颜色的灯:
位置0: 🔴 红灯(最左边) 位置1: 🟠 橙灯 位置2: 🟡 黄灯 位置3: 🟢 绿灯 位置4: 🔵 蓝灯(最右边)Transformer通过"灯的颜色"就知道谁站在哪里。正弦/余弦就是生成这些"颜色"的数学方法。
为什么不用简单的 0,1,2,3,4?
| 方法 | 问题 |
|---|---|
直接用位置数字[0,1,2,3,4] | 句子长了数字太大,模型不稳定 |
用one-hot[1,0,0,0,0] | 维度爆炸,5000个位置要5000维 |
| 正弦/余弦 | ✅ 维度固定,值域稳定,能表达相对位置 |
最终操作:和词嵌入相加
词嵌入("小") = [0.2, -0.5, 0.8, 0.3] 位置编码(0) = [0, 1, 0, 1 ] ───────────────────────────────────── 最终输入 = [0.2, 0.5, 0.8, 1.3]模型看到的不是单纯的"小",而是"坐在第0个位置的小"!