正余弦位置编码(Sinusoidal Positional Encoding)

为什么需要位置编码?

问题: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。

这就是正余弦编码既能区分位置、又能建模相对位置的根本原因。

相关推荐
H.莓飛1 小时前
【数据结构】队列
linux·数据结构·centos
Ivanqhz1 小时前
层归一化、残差、前馈网络与激活函数简述
服务器·数据库·人工智能·深度学习·算法
BD_Marathon1 小时前
消息对象中字段的说明
java·前端·python
Java后端的Ai之路1 小时前
Python进阶探索23 - Python中的Time与Datetime模块
开发语言·数据库·python·datetime·time
小此方1 小时前
Linux网络(十九):TCP流量控制与滑动窗口详解,超时重传和快重传到底有什么区别
linux·运维·服务器·网络·网络协议·tcp/ip
zlpzlpzyd2 小时前
nacos的openapi地址
java
码上有光2 小时前
Linux进程通信——共享内存、消息队列和信号量
android·linux·运维·共享内存·通信
沫璃染墨2 小时前
《从零入门Linux系统篇(五十四):线程篇·七——互斥锁底层原理:从原子交换到线程竞争与锁实现》
linux·运维·服务器·开发语言·c++·驱动开发·系统架构
Java后端的Ai之路2 小时前
Python进阶探索24_应用案例_linux系统的监控
linux·开发语言·python·应用·探索