Transformer的正弦/余弦位置编码

📅 2026/7/8 19:02:50 👁️ 阅读次数 📝 编程学习
Transformer的正弦/余弦位置编码

正弦/余弦位置编码 = 用不同频率的波浪给每个位置打独特的"时间戳",让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)

参数含义

符号含义示例
postoken的位置(第几个字)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)
00.001.000.001.00
10.840.540.011.00
20.91-0.420.021.00
30.14-0.990.031.00
4-0.76-0.650.041.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个位置的小"!