解释 RoPE 如何通过逐对旋转把 token 顺序注入注意力,而无需可学习的位置嵌入。
自注意力是置换不变的——没有位置信号,模型无法区分 'cat sat on mat' 和 'mat sat on cat'。
术语表 · 6 个术语
- RoPE
- 旋转位置嵌入(Rotary positional embedding)。在点积之前,把 Q 和 K 的每个 (x_2i, x_2i+1) 维度对旋转 m * theta_i 的角度。
- pair frequency (theta_i)
- 每个维度对的旋转速率:theta_i = base^(-2i / rope_dims)。i 小转得快,i 大转得慢。
- rope_theta (base)
- 频率指数的底数。Qwen3.5 用 1e7,远高于最初的 1e4——把频谱拉长以支持长上下文。
- partial rotary factor
- RoPE 旋转每个头维度的比例。Qwen3.5 用 0.25,所以 256 个头维度中只有 64 个被旋转;其余原样通过。
- relative-position property
- RoPE(q,m) 与 RoPE(k,n) 的点积只取决于 m-n,所以模型只需要学习相对偏移。
- unitary rotation
- 旋转保持向量范数不变——RoPE 改变向量指向的方向,从不改变它们的大小。
位置编码:模型如何知道 token 的顺序
自注意力有一个奇怪的性质:它是置换不变(permutation-invariant)的。如果把输入 token 打乱,每个 token 的注意力输出也会跟着一起被打乱,但注意力层计算出的关系本身不会变。没有外力帮助,Transformer 真的分不清 "the cat sat on the mat" 和 "mat the on sat cat the"。必须有什么东西把顺序重新注入进来。
先亲眼看看。下面是一个由三个 token 组成的玩具集合,没有附带任何位置信息——随意重新排列它们,注意力权重纹丝不动:
朴素的注意力只看见哪些 token 在场,看不见它们的顺序——换个顺序,每个权重都保持不变。这种对顺序的失明,正是 RoPE 通过按位置旋转 Q 和 K 所填补的空缺。
仅作示意——三个固定 token,使用手挑的 2 维内容向量,没有任何位置信号。一个 token 对另一个 token 的权重,是二者内容向量点积的 softmax,只取决于这两个 token 本身,与槽位无关——所以在任何排序下都相同。真实模型有更多维度、可学习的 Q/K 投影,以及恰好打破这种对称性的 RoPE 旋转。
两个更早的想法
- 可学习的位置嵌入。早期的 GPT、BERT 等模型给 0..N-1 的每个位置都配一个可学习的向量,并把它加到 token 嵌入上。简单,但模型无法外推到训练中从未见过的位置,而且必须事先定死最大长度。
- 正弦位置嵌入。2017 年最初的 Transformer 用不同频率的固定正弦波,同样加到 token 嵌入上。理论上可以外推,但实践中超过训练窗口后质量会退化。
RoPE 的想法:旋转,而不是相加
旋转位置嵌入(Rotary Positional Embedding,RoPE)不去动嵌入本身,而是把位置直接烘焙进注意力的数学里。诀窍是在点积计算之前,把 query 和 key 向量按一个取决于 token 位置的角度旋转。
具体来说,RoPE 把相邻的特征维度配成对——(x_0, x_1)、(x_2, x_3)、… ——并把每一对当作 2D 平面上的一个点。对维度对序号 i 和旋转维度 d(RoPE 实际旋转的特征数——不一定是完整的头维度;下文马上细说),频率是:
用文字读这个式子:随着维度对序号 i 增大,指数 −2i/d 越来越负,所以 越来越小。低序号的对转得快(追踪精细的局部位置);高序号的对转得慢(追踪粗粒度的长程位置)。
在 token 位置 m,这一对被旋转 的角度:
旋转矩阵做的事就是把一个 2-D 点——想象一根钟表指针——旋转角度 ;下面的四个三角函数项只是计算旋转后落点的配方。再往下的钟面表盘展示的正是这种旋转。这里的角度用弧度(radian)度量——在这个单位里一整圈是 ,所以“1 rad/token” 的速率大约是每步六分之一圈。
亲手把这个矩阵算一遍,就用 pair 0——它的频率恰好是 θ₀ = base⁰ = 1 弧度/token,所以 pair 0 每步转 1 rad。取位置 m = 2 处的 (1, 0):它旋转 2 rad,落在 (cos 2, sin 2) ≈ (−0.42, 0.91)。长度不变,方向变了——而这正是你将在下方表盘 0(高频钟面)上看到的旋转。
这里为了直观把维度画成相邻配对(x_0 配 x_1);Qwen3.5 实际如何排列它们是一个实现细节,第一遍阅读可以跳过(见下方进阶)。
进阶:维度对实际是怎么排列的(rotate-half) · 可选,给好奇的读者
这里为了直观把维度画成相邻配对(x_0 配 x_1),但 Qwen3.5——和大多数 Llama/HF 风格的模型一样——用的是数学上等价的 rotate-half 布局:维度 i 与旋转块后一半中的对应维度 i + d/2 配对。同样的角度、同样的频率、同样的相对位置性质;不同的只是哪两个维度共享一次旋转。
旋转是酉变换(unitary)——它保持向量范数不变——所以 RoPE 不改变 Q 和 K 的大小,只改变它们指向的方向。位置信息骑在角度上,而不是大小上。
宽广的频率范围
低序号的维度对(i 小)拿到最高的频率、转得最快——几个 token 就转完一整圈。高序号的维度对频率小到几乎为零,即使跨越数万个 token 也几乎不动。直觉是:高频对编码精细的局部位置(“我离邻居是不是 3 个 token?”),低频对编码粗粒度的全局位置(“我在文档的前半部分还是后半部分?”)。
Qwen3.5 的头维度是 256,部分旋转因子(partial rotary factor)是 0.25(所以每个头只有前 64 个特征被旋转——其余原样通过),RoPE 底数则大得不寻常:1e7。大底数把频谱拉长,使最低频的维度对在模型 262,144 token 的上下文窗口内几乎不旋转——这是长上下文外推的关键配料之一。
为什么只旋转头的四分之一?因为未被旋转的那 192 个维度完全不携带位置——它们做纯粹的内容匹配(“这个 key 的含义是不是我的 query 正在找的?”)。位置只需要骑在向量的一部分上;其余部分可以只关心语义本身。
还有一个 Qwen3.5 混合架构特有的细节:这个 RoPE 旋转只施加在 6 个全量注意力层上(如上所述,这些层旋转 256 个头维度中的前 64 个)。其余 18 层是线性(GatedDeltaNet)层,通过循环再加一段短因果卷积来隐式地编码位置——完全不用 RoPE。
相对位置性质
这是让 RoPE 真正“通”的部分。取 RoPE(q, m) 与 RoPE(k, n) 的点积,结果只取决于差值 m - n,与 m 和 n 各自的取值无关。模型永远不需要学习“位置 1,427”是什么意思——它只需要学习注意力在相对偏移上应该如何表现,而这天然地泛化到它从未见过的长度。
右侧第三个面板把这一点变得具体。把 query 位置固定在 m = 50、变动 key 位置 n,旋转后的点积在 n - m = 0 处出现尖锐峰值,并随着距离拉远向两侧(带着涟漪地)衰减。这种衰减正是让注意力天然“偏爱”近处 token 胜过远处 token 的归纳偏置——而且完全不需要逐位置的参数。
你可以直接感受这种只依赖偏移量的性质。把两个位置一起平移,分数纹丝不动:
两支箭头都从同一个基向量出发;RoPE 把 query 旋转 m·ω,把 key 旋转 n·ω,所以 query 的相对旋转恰好是 Δ·ω = (m − n)·ω,且 q·k = cos((m − n)·ω)。让 m 和 n 一起增大,整幅图都会旋转,但夹角——以及分数——冻结不动。只动其中一个,夹角就会张开或闭合:让它们对齐(Δ → 0),q·k → 1。
仅作示意——这里只用了一个代表性频率(ω = 0.5 rad/token),并把内容向量简化为共享的基向量,以便单独观察位置部分。随着位置增大,相对旋转 Δ·ω 可能超过一整圈,但余弦每圈重复一次,所以分数仍然只取决于偏移 Δ。真实模型会把它与真正的 query/key 内容结合,在许多速度各异的频率对上同时进行(见下方的频率谱)。
本章直接可视化 RoPE 的数学——不需要任何模型推理。想看看它在真实注意力分数上的效果?打开自注意力(第 4 章),观察分数图如何偏向对角线。
- RoPE 把位置编码成旋转而不是加法——向量大小保持恒定,只有角度携带位置信号。
- 低序号的维度对是高频的(精细的局部位置);高序号的维度对是低频的(粗粒度的全局位置)。
- RoPE 之后的点积只取决于 m - n,这正是带 RoPE 的模型能外推到训练中从未见过的长度的原因。
把 'Token position m' 滑块从 0 拖到 100。对比高频旋转面板(pair 0)和低频面板(pair 28)。哪一个先转完一整圈?当 m=100 时,另一个转了多少?