Transformer架构详解(2):位置编码深入理解

一、位置编码的作用

自然语言是一种高度依赖顺序的符号系统。同样的几个词,仅仅调换顺序就可能产生截然不同的含义。

Transformer 的自注意力机制是排列不变的:它仅基于 token 之间的内容相似度计算权重,完全不依赖 token 在序列中的位置。这意味着,如果不加位置信息,模型会将输入序列视为一个无序集合,比如:

  • 序列 A我 打 你
  • 序列 B你 打 我

这两个序列的词表相同(均为 “我”、“打”、“你”),只是顺序不同。如果不加位置编码,自注意力在计算时:

  1. 每个 token 的 Query/Key/Value 只由其词嵌入决定
  2. 注意力权重只取决于词与词之间的相似度
  3. 由于两个序列中的词完全相同,QKV 也完全相同
  4. 因此,自注意力输出的向量序列也完全相同——交换顺序不会改变任何中间表示

最终模型无法知道“谁打了谁”,翻译或理解任务必然出错。

下面通过 Self-Attention 的计算来进一步说明位置编码的重要性:

Q=XWQK=XWKV=XWVAttention(Q,K,V)=softmax(QKTdk)VQ=XW_Q,K=XW_K,V=XW_V \\ Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt{d_k}})V

现在考虑对输入做一个任意的排列操作。设 P\Rho 是一个 N×N 的排列矩阵(每行每列恰好有一个 1,其余为 0),PX\Rho X 表示把 X 的行打乱重排。排列后的 Q、K、V 为:

Q=PXWQK=PXWKV=PXWVQ^{'}=\Rho XW_Q,K^{'}=\Rho XW_K,V^{'}=\Rho XW_V

那么排列后的 Attention 输出为:

\begin{align} softmax(\frac{Q^{'}K^{'T}}{\sqrt{d_k}})V^{'}&=softmax(\frac{\Rho QK^T \Rho ^{T}}{\sqrt{d_k}})\Rho V \\ &=\Rho \cdot softmax(\frac{QK}{\sqrt{d_k}}) \cdot \Rho ^T \cdot \Rho V \\ &=\Rho \cdot softmax(\frac{QK}{\sqrt{d_k}}) V \end{align}

这就是排列等变性(permutation equivariance)的含义:输入打乱后,输出只是做了同样的打乱,但每个 token 聚合到的信息内容完全不变。换句话说,原始 Attention 把输入当作一个集合而非序列处理 — 位置 0 的 token 和位置 99 的 token 在它眼中没有任何区别。

这就是为什么我们必须额外引入位置编码:Attention 本身是”位置盲”的,需要外部注入位置信号才能区分不同词序。

二、绝对位置编码 & 相对位置编码

在介绍具体方案之前,先区分一下两大设计思路。

2.1 绝对位置编码

绝对位置编码为序列中的每个位置分配一个固定的编码向量 pip_i​,然后将其加到(或拼接到)对应 token 的词嵌入上:

xi=xi+pix^{'}_i=x_i+p_i

这种方案的思路很直接:第 0 个位置有一个”身份证号” p0p_0,第 1 个位置有另一个”身份证号” p1p_1,以此类推。模型通过学习这些身份证号之间的关系来间接感知相对位置。

代表方案:Sinusoidal 编码(固定的)、BERT/GPT-2 的可学习编码(学出来的)。

2.2 相对位置编码

相对位置编码不给每个位置一个固定标签,而是在计算 Attention 分数时直接注入”两个 token 之间距离多远”的信息。

打个比方:绝对位置编码像给每个人发一个门牌号(“我住 301,你住 305”),然后靠门牌号之间的差值来推断距离;相对位置编码则直接在两个人交谈时告诉他们”你们之间隔了 4 个房间”。

代表方案:RoPE(旋转位置编码)、ALiBi(线性偏置)。

三、Sinusoidal 位置编码

3.1 公式

“Attention Is All You Need” 论文提出了一种不需要任何可学习参数的位置编码方案。对于位置 posp**os 和维度索引 ii,编码值定义为:

PosEnc(pos,2i)=sin(pos100002idmodel)PosEnc(pos,2i+1)=cos(pos100002idmodel)PosEnc_{(pos, 2i)} = \sin(\frac{pos}{10000^{\frac{2i}{d_{model}}}}) \\ PosEnc_{(pos, 2i+1)} = \cos(\frac{pos}{10000^{\frac{2i}{d_{model}}}})

  • pos:token 在序列中的位置(0,1,2,…)
  • dmodeld_{model}:PE 的维度,和嵌入向量一样
  • 2i2i+12i、2i+1:当位置编码为偶数时使用 sin函数,当位置编码为奇数时使用 cos函数,取值范围:$ 0 \leq i \leq d_model/2 $

每一对相邻维度 (2i,2i+1)(2i,2i+1) 共享同一个频率 $\omega _i = \frac{1} {10000^{\frac{2i}{d}}} $,波长 λi=2π100002id\lambda_i=2\pi \cdot 10000^{\frac{2i}{d}} 分别用正弦和余弦函数编码。随着 i 从 0 增大到 d21\frac{d}{2}−1,频率从 1(i=0i=0 时分母为 100000=110000^0=1)指数衰减到 1/100001/10000i=d21i=\frac{d}{2}−1 时)。因此,低维度的频率低、波长长,编码变化缓慢;高维度的频率高、波长短,编码变化剧烈。整个编码覆盖了从 2π2\pi100002π10000 \cdot 2\pi 的多个尺度。

维度 变化特点
低维度 变化快,适合区分近距离位置
高维度 变化慢,适合表达远距离位置

也就是说,不同维度使用了不同频率的波形。多个频率组合在一起,就可以为每个位置生成一个独特的位置“指纹”。

下面看一个例子:

假设 embedding 维度是:d=8,i=0,1,2,3

位置编码可以展开为:

1
PE(pos)=[sin(pos),cos(pos),sin(pos/10),cos(pos/10),sin(pos/100),cos(pos/100),sin(pos/1000),cos(pos/1000)]

对于位置 pos=1:

1
PE(1)=[sin(1),cos(1),sin(0.1),cos(0.1),sin(0.01),cos(0.01),sin(0.001),cos(0.001)]

对于位置 pos=2:

1
PE(2)=[sin(2),cos(2),sin(0.2),cos(0.2),sin(0.02),cos(0.02),sin(0.002),cos(0.002)]

可以看到,不同位置会得到不同的位置编码向量。

3.2 数学原理

单看公式,Sinusoidal 编码似乎只是给每个绝对位置分配一个固定向量。但它的巧妙之处在于:相对位置偏移可以通过一个线性变换作用在绝对位置编码上得到

Sin(A+B)=Sin(A)Cos(B)+Cos(A)Sin(B)Cos(A+B)=Cos(A)Cos(B)Sin(A)Sin(B)Sin(A+B) = Sin(A)Cos(B) + Cos(A)Sin(B) \\ Cos(A+B) = Cos(A)Cos(B) - Sin(A)Sin(B)

对于任意位置偏移 kk,有:

\begin{align} PE_{(pos+k,2i)}&=sin(pos \cdot f_i + k \cdot f_i) \\ &= sin(pos \cdot f_i)cos(k \cdot f_i) + cos(pos \cdot f_i)sin(k \cdot f_i) \\ \end{align}

\begin{align} PE_{(pos+k,2i+1)}&=cos(pos \cdot f_i + k \cdot f_i) \\ &= cos(pos \cdot f_i)cos(k \cdot f_i) - sin(pos \cdot f_i)sin(k \cdot f_i) \\ \end{align} \\

写成矩阵形式:

\left[ \begin{array} &PE_{(pos+k,2i)}\\ PE_{(pos+k,2i+1)} \end{array} \right] = \left[ \begin{array} &cos(k \cdot f_i)&sin(k \cdot f_i)\\ -sin(k \cdot f_i)&cos(k \cdot f_i) \end{array} \right] \cdot \left[ \begin{array} &PE_{(pos+k,2i)}\\ PE_{(pos+k,2i+1)} \end{array} \right]

中间的矩阵恰好是旋转矩阵,它与位置 无关,仅由偏移量 决定。因此:

PE(pos+k)=T(k)PE(pos)PE_{(pos+k)}=T(k) \cdot PE_{(pos)}

其中 T(k)T(k) 是一个由若干块对角旋转矩阵构成的线性变换。

这意味着,虽然 Sinusoidal 编码是一种”绝对位置编码”,但它内含的三角函数结构使得模型有可能通过学习线性变换来提取相对位置信息。不过这种能力是间接的 — 模型需要自己从数据中学会利用这一数学性质,而非显式地被注入相对位置信号。

3.3 局限性

尽管设计精巧,Sinusoidal 编码在大模型时代逐渐被取代,主要原因包括:

  1. 信息稀释:位置编码在输入层以加法的形式注入到词嵌入中。经过数十层网络的非线性变换后,这个加性信号可能被逐渐”淹没”,导致深层网络难以有效利用位置信息
  2. 外推困难:虽然理论上可以为任意长度的位置生成编码,但模型在训练时只见过有限长度的序列。对于超出训练长度的位置,虽然编码值本身是有定义的,但模型从未在这些位置上训练过,Attention 模式的泛化效果往往很差
  3. 相对位置的间接性:如 3.4 节所述,模型需要自行学会通过线性变换来提取相对位置,这增加了学习负担,不如直接在 Attention 计算中显式注入相对位置来得高效

四、可学习位置编码

4.1 基本思路

既然固定的三角函数编码有局限性,一个自然的想法是:让模型自己学习每个位置的编码向量。 这就是可学习位置编码(Learned Positional Embedding)的方案。

具体做法是维护一个形状为 (Lmax,d)(L_{max},d) 的可学习嵌入表,其中 LmaxL_{max} 是支持的最大序列长度,d 是模型维度。位置 pos 的编码就是从这个嵌入表中查找第 pos 行的向量,然后加到对应 token 的词嵌入上:

xi=xi+Epos[i]x^{'}_i = x_i + E_{pos}[i]

这与词嵌入的机制完全一致 — 词嵌入是用 token ID 查表,位置嵌入是用位置 ID 查表。

4.2 代表模型

  • BERT:使用可学习位置编码,最大序列长度 512。位置嵌入矩阵的参数量为 512 x 768 = 393,216,相比 BERT-base 1.1 亿的总参数量微乎其微。

  • GPT-2:同样使用可学习位置编码,最大序列长度 1024。位置嵌入矩阵为 1024 x 768(GPT-2 Small)或 1024 x 1600(GPT-2 XL)。

4.3 优势与不足

优势

  • 实现极其简单,就是一次嵌入表查找加法
  • 模型可以自由学习任意位置模式,不受预设函数形式的约束
  • 在训练长度范围内,表现通常不逊于甚至略优于 Sinusoidal 编码

不足

  • 硬性长度限制:最大序列长度在模型定义时就确定了。如果要支持更长的序列,需要重新训练或做复杂的插值处理
  • 无法外推:对于超出嵌入表范围的位置,根本没有对应的编码向量
  • 与绝对位置编码共享的缺陷:信息在深层同样可能被稀释,相对位置同样需要间接学习

正是这些局限性,推动了研究者去寻找能在 Attention 计算中直接编码相对位置的方案 — RoPE 就是其中最成功的答案。

五、RoPE:旋转位置编码

RoPE(Rotary Position Embedding)是苏剑林在 2021 年提出的位置编码方案,目前被 LLaMA、Mistral、Qwen、DeepSeek 等几乎所有主流大模型采用。它的核心思想可以用一句话概括:通过旋转 Q 和 K 向量来编码位置,使得 Attention 分数天然包含相对位置信息。

RoPE具有很强的表达能力 ,主要应用于图像处理和三维数据处理中,尤其是在处理具有对称性或周期性的任务时,能够更加自然地捕捉序列中的位置信息。

5.1 公式

想象你手中有一个指南针,指针指向某个方向,用一个二维向量 (x,y) 来表示。现在你把指南针旋转一个角度 θθ,指针就指向了新的方向 (x′,y′)。旋转操作不改变指针的长度(模长不变),只改变方向。

二维旋转的数学表达是:

\left[ \begin{array} &x^{'}\\ y^{'} \end{array} \right] = \left[ \begin{array} &cos\theta&-sin\theta \\ sin\theta&cos\theta \end{array} \right] \left[ \begin{array} &x \\ y \end{array} \right]

RoPE 的核心创意在于:用 token 的位置来决定旋转角度。 位置 0 的 token 不旋转,位置 1 的 token 旋转 θ\theta 度,位置 2 的 token 旋转 2θ2\theta度,以此类推。位置 m 的 token 旋转 mθm\theta 度。

现在考虑两个 token:位置 m 的 Q 向量被旋转了 mθm\theta 度,位置 n 的 K 向量被旋转了 nθn\theta 度。当我们计算它们的内积(Attention 分数)时,由于旋转是刚性变换,两个向量的内积只取决于它们之间的角度差 (mn)θ(m-n)\theta,非各自的绝对旋转角度。

这就是 RoPE 能编码相对位置的几何本质:两个向量各自旋转后的内积,只取决于旋转角度之差。

5.2 数学原理

现在严格推导。考虑二维情形,设 Q 和 K 分别是位置 m 和 n 的查询和键向量。RoPE 对它们施加位置相关的旋转:

\bar q = R(m\theta) \cdot q = \left[ \begin{array} &cos(m\theta)&-sin(m\theta) \\ sin(m\theta)&cos(m\theta) \end{array} \right] \left[ \begin{array} &q_0 \\ q_1 \end{array} \right] \\ \bar k = R(n\theta) \cdot k = \left[ \begin{array} &cos(n\theta)&-sin(n\theta) \\ sin(n\theta)&cos(n\theta) \end{array} \right] \left[ \begin{array} &k_0 \\ k_1 \end{array} \right]

计算旋转后 Q 和 K 的内积:

qˉTkˉ=qTR(mθ)R(nθ)k\bar q^T \bar k = q^TR(m\theta)R(n\theta)k

由于旋转矩阵的性质 R(α)T=R(α)R(\alpha)^T = R(-\alpha),因此:

R(mθ)TR(nθ)=R(mθ)R(nθ)=R((nm)θ)R(m\theta)^TR(n\theta)=R(-m\theta)R(n\theta)=R((n-m)\theta)

最终:

qˉTkˉ=qTR((nm)θ)k\bar q^T \bar k = q^TR((n-m)\theta)k

结果只依赖 (n−m),即两个 token 的相对位置差,与绝对位置 m、n 的具体值无关。这正是我们想要的性质。

5.3 推广到高维

实际模型的头维度 dkd_k 远大于 2(通常为 64 或 128)。RoPE 的推广方式非常自然:把 dkd_k 维向量拆分成 dk/2d_k/2 个二维子空间,在每个子空间内独立做旋转,但使用不同的频率。

对于第 i 个二维子空间(0idk/20 \leq i \leq d_k/2),旋转角度为 mθm \cdot \theta,其中频率参数为:θi=i100002i/kk\theta_i=\frac{i}{10000^{2i/k_k}}(注意这个频率参数与 Sinusoidal 编码中的完全一致。)。

将所有子空间的旋转矩阵拼在一起,形成一个分块对角矩阵:

R_m= \left[ \begin{array} &R(m\theta_0)&&&\\ &R(m\theta_1) \\ &&\ddots \\ &&&R(m\theta_{d_k/2-1}) \end{array} \right]

其中每个 R(mθi)R(m\theta_i) 是一个 2×2 的旋转矩阵。

RoPE 的完整操作就是:

qˉm=Rmqmkˉm=Rnkn\bar q_m = R_m \cdot q_m \\ \bar k_m = R_n \cdot k_n

5.4 代码实现

在实际工程中,我们不会真的构造一个 dk×dkd_k×d_k 的分块对角矩阵再做矩阵乘法。二维旋转可以用逐元素乘法和加法高效实现:

qˉ2i=q2icos(mθi)q2i+1sin(mθi)qˉ2i+1=q2isin(mθi)+q2i+1cos(mθi)\bar q_{2i} = q_{2i}cos(m\theta_i)-q_{2i+1}sin(m\theta_i) \\ \bar q_{2i+1} = q_{2i}sin(m\theta_i)+q_{2i+1}cos(m\theta_i)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import numpy as np

def precompute_freqs(dim, max_seq_len, base=10000.0):
"""
预计算 RoPE 所需的频率参数和三角函数值。

参数:
dim: 每个注意力头的维度 (d_k)
max_seq_len: 最大序列长度
base: 频率基数,默认 10000

返回:
cos_cached: (max_seq_len, dim/2) 的余弦值
sin_cached: (max_seq_len, dim/2) 的正弦值
"""
# 频率参数: theta_i = 1 / base^(2i / dim), i = 0, 1, ..., dim/2 - 1
freq_indices = np.arange(0, dim, 2, dtype=np.float32) # [0, 2, 4, ..., dim-2]
freqs = 1.0 / (base ** (freq_indices / dim)) # (dim/2,)

# 位置序列
positions = np.arange(max_seq_len, dtype=np.float32) # [0, 1, 2, ..., max_seq_len-1]

# 外积得到每个 (position, freq_pair) 的角度
angles = np.outer(positions, freqs) # (max_seq_len, dim/2)

cos_cached = np.cos(angles)
sin_cached = np.sin(angles)

return cos_cached, sin_cached

def apply_rope(q, cos, sin):
"""
对查询或键向量施加 RoPE 旋转。

参数:
q: (seq_len, dim) 的向量,dim 必须是偶数
cos: (seq_len, dim/2) 的预计算余弦值
sin: (seq_len, dim/2) 的预计算正弦值

返回:
q_rotated: 旋转后的向量,形状与输入相同
"""
dim = q.shape[-1]
assert dim % 2 == 0, "维度必须是偶数"

# 将向量拆分为偶数和奇数维度
q_even = q[:, 0::2] # (seq_len, dim/2) -- 取 q_0, q_2, q_4, ...
q_odd = q[:, 1::2] # (seq_len, dim/2) -- 取 q_1, q_3, q_5, ...

# 对每个二维子空间施加旋转
q_even_rotated = q_even * cos - q_odd * sin
q_odd_rotated = q_even * sin + q_odd * cos

# 重新交错合并
q_rotated = np.zeros_like(q)
q_rotated[:, 0::2] = q_even_rotated
q_rotated[:, 1::2] = q_odd_rotated

return q_rotated

# 使用示例
dim = 128 # 头维度
seq_len = 64 # 序列长度

# 预计算三角函数值(只需在初始化时做一次)
cos_cached, sin_cached = precompute_freqs(dim, seq_len)

# 模拟 Q 和 K 向量
q = np.random.randn(seq_len, dim).astype(np.float32)
k = np.random.randn(seq_len, dim).astype(np.float32)

# 施加 RoPE
q_rotated = apply_rope(q, cos_cached, sin_cached)
k_rotated = apply_rope(k, cos_cached, sin_cached)

# 验证:旋转后的内积只依赖相对位置
# 取位置 10 的 Q 和位置 15 的 K
dot1 = np.dot(q_rotated[10], k_rotated[15])

# 取位置 20 的 Q 和位置 25 的 K(相对位置差同样是 5)
# 注意:这里 Q 和 K 的原始值不同,所以内积值不同
# 但如果给定相同的 q, k 向量,放在不同绝对位置上,
# 只要相对位置差相同,内积就相同
print(f"dot(q_rotated[10], k_rotated[15]) = {dot1:.4f}")

5.5 RoPE VS Sinusoidal

维度 Sinusoidal RoPE
注入方式 加到词嵌入上 旋转 Q 和 K 向量
注入位置 输入层(只注入一次) 每层 Attention(每层都注入)
编码类型 绝对位置编码 相对位置编码
对 V 的影响 间接影响(V 的输入包含了位置信息) 不影响(只旋转 Q 和 K)
深层保持性 位置信号可能在深层网络中衰减 每层重新注入,不会衰减
外推能力 较弱 较强(配合插值方法)

一个关键差异值得强调:RoPE 不对 V 向量做任何变换。这意味着位置信息只影响”谁该关注谁”(Attention 权重的计算),而不影响”关注后传递什么信息”(V 向量的内容)。这种设计更加干净 — 位置影响的是信息流动的路由,而非信息本身。

六、ALiBi - 带线性偏置的注意力

ALiBi(Attention with Linear Biases)是 Press et al. 在 2022 年提出的另一种位置编码方案,思路与 RoPE 截然不同。

6.1 核心思想

ALiBi 完全不修改 Q 和 K 向量,而是在计算出 Attention 分数后,直接给分数加上一个与距离成正比的负偏置:

Attention(Q,K,V)=softmax(QKT(dk)+mBias)VAttention(Q,K,V)=softmax(\frac{QK^T}{\sqrt(d_k)}+m \cdot Bias)V

  • Bias 矩阵的元素为:Bias[i][j]=ijBias[i][j]=-\vert i - j \vert
  • m 是一个头相关的标量斜率,不同头使用不同的 m 值(通常按几何级数设置,如 1/2,1/4,1/8,…)

简单来说,ALiBi 给 Attention 分数施加了一个”距离惩罚” —— 两个 token 距离越远,Attention 分数被减去的值越大,从而倾向于关注近处的 token。不同的头有不同的衰减速率:有些头几乎只关注紧邻的 token(大斜率),有些头仍然能关注远处的 token(小斜率)。

6.2 优势和局限

优势

  • 实现极其简单,只需要在 Attention 分数上做一次加法
  • 不引入可学习参数
  • 外推能力强:论文证明在 1024 长度上训练的模型可以在推理时外推到 2048 甚至更长
  • 计算几乎零开销

局限

  • 位置信息的表达能力较为有限(只有线性距离衰减这一种模式)
  • 在非常长的上下文中,线性惩罚可能过度压制远距离 token 的注意力
  • 在实际的大模型竞赛中,ALiBi 的采用率远低于 RoPE

ALiBi 目前主要被 BLOOM 等少数模型采用。在大模型的位置编码方案中,RoPE 以其优秀的表达能力和外推扩展性成为了事实上的标准。


Transformer架构详解(2):位置编码深入理解
http://example.com/2026/06/28/Transformer架构详解-2-:位置编码深入理解/
作者
Yu xin
发布于
2026年6月28日
许可协议