技术博客

RoPE 原理:为什么用旋转矩阵编码位置

第 73 / 147 篇 · 2026-04-27 · ai · RoPE位置编码Transformer

RoPE(Rotary Position Embedding)现在是 LLaMA、Qwen、DeepSeek 等主流模型的标配。它的实现看起来古怪——把 query/key 向量两两分组,每组按不同角速度转一个与位置成正比的角度。这篇文章回答的问题是:为什么偏偏是旋转?答案藏在一个函数方程里,旋转不是某个聪明人的灵感,而是"相对位置编码"这个需求的近乎必然推论。

需求:让内积只依赖相对位置

自注意力的打分是内积 $\langle q_m, k_n\rangle$,其中 $m,n$ 是 token 位置。我们希望位置信息以"相对"的方式进入打分——第 5 个和第 8 个 token 的关系,应该和第 105 个与第 108 个的一致,因为语言里重要的是相对距离而不是绝对坐标。形式化地,设 $\tilde q_m = f(q, m)$ 是注入位置后的 query,$\tilde k_n = f(k, n)$ 同理,我们要求存在函数 $g$ 使得

$$ \langle f(q, m),\, f(k, n) \rangle = g(q, k, m-n). $$

即内积只通过 $m-n$ 依赖位置。这是 RoPE 论文(Su et al., 2021)的出发点,也是全部推导的锚点。

二维情形的求解:旋转被函数方程逼出来

先看最简单的二维情形。把向量写成复数会舒服得多:$f(q,m)$ 看成复数,内积对应 $\mathrm{Re}[f(q,m)\,\overline{f(k,n)}]$。设

$$ f(q, m) = R(q)\, e^{i\Theta(q,m)}, \qquad f(k,n) = R(k)\, e^{i\Theta(k,n)}, $$

其中 $R$ 是模长、$\Theta$ 是辐角。代入内积条件:

$$ \mathrm{Re}\big[R(q)R(k)\, e^{i(\Theta(q,m)-\Theta(k,n))}\big] = R(q)R(k)\cos\big(\Theta(q,m)-\Theta(k,n)\big), $$

要求右边只依赖 $m-n$。对模长没有约束(令 $R$ 与位置无关即可),关键在辐角:需要 $\Theta(q,m)-\Theta(k,n)$ 只是 $m-n$ 的函数。取 $n=m$ 时内积应为 $\|q\|^2$ 一类的与内容有关的量,这逼着辐角差只含位置差。满足"差的函数"性质的线性选择是

$$ \Theta(q, m) = \theta(q) + m\,\vartheta, $$

其中 $\vartheta$ 是一个常数角速度。代回复数形式:

$$ f(q, m) = \underbrace{R(q)e^{i\theta(q)}}_{\text{内容部分}}\cdot\, e^{im\vartheta} = q\, e^{im\vartheta}. $$

复数乘 $e^{im\vartheta}$ 在二维实数空间里就是旋转矩阵

$$ R_m = \begin{pmatrix} \cos m\vartheta & -\sin m\vartheta \\ \sin m\vartheta & \cos m\vartheta \end{pmatrix}. $$

于是位置编码的操作是 $\tilde q_m = R_m q$、$\tilde k_n = R_n k$。验证一下内积:

$$ \langle R_m q, R_n k\rangle = q^\top R_m^\top R_n k = q^\top R_{n-m} k, $$

用到旋转矩阵群性质 $R_m^\top R_n = R_{n-m}$——旋转群对"相对位置"的封闭性正是我们要的。为什么用旋转矩阵?因为"内积只依赖位置差"这个要求,在保模长的线性变换里,只有旋转群能保证 $R_m^\top R_n$ 仍是 $m-n$ 的函数。 缩放会破坏保距,剪切不满足群封闭,旋转是留下来的那个。

群表示视角:唯一性的另一种说法

二维情形解出了旋转,但"高维为什么是两两平面的块对角旋转"还有一个更彻底的回答。把位置编码看成一个映射 $T:m\mapsto T(m)$,要求 $\langle T(m)q,\,T(n)k\rangle$ 只依赖 $n-m$ 且对所有 $q,k$ 成立。归一化 $T(0)=I$ 后,这个条件逼着 $T(m)^\top T(m)=I$(每步都是保内积的正交变换),进而给出乘积性质

$$ T(m+n) = T(m)\,T(n). $$

这是说 $T$ 是位置加法群到正交群的同态——一个一参数子群。这类同态有标准形:$T(m)=e^{m\Theta}$,生成元 $\Theta$ 必须是反对称矩阵(对 $T^\top T=I$ 求导即得 $\Theta^\top+\Theta=0$);而实反对称矩阵必可经正交相似变换成 $2\times2$ 块的对角拼合,每块形如 $\begin{pmatrix}0&-\vartheta_i\\ \vartheta_i&0\end{pmatrix}$,指数化之后正是

$$ T(m) = \bigoplus_i R_{m\vartheta_i}. $$

链条收拢了:相对性 ⇒ 正交 ⇒ 群同态 ⇒ 块对角旋转。RoPE 的结构不是谁设计出来的,它是"相对位置 + 内积形式"这两个要求解函数方程的通解(自由度只剩基的选择,即维度怎么配对,以及各平面的角速度取值)。底数 10000、频率几何分布这些反而是自由参数——方程钉死了"旋转"这个形式,没钉死角速度。

高维推广:两两分组与多尺度频率

$d$ 维向量没法整体旋转,RoPE 的做法是切成 $d/2$ 个二维平面,第 $i$ 个平面用角速度

$$ \vartheta_i = \text{base}^{-2i/d}, \qquad \text{base}=10000. $$

于是位置 $m$ 在第 $i$ 个平面旋转角度 $m\vartheta_i$。相邻维度的角速度按几何级数分布,从接近 1(每步转近 1 弧度)到约 $10^{-4}$(每步转万分之一弧度)。直觉上这是一组从快到慢的时钟:高频平面转得飞快,负责区分邻近位置;低频平面转得极慢,负责编码长程距离。这和正弦位置编码的频率设计一脉相承,区别只是正弦编码用加法注入位置,RoPE 用旋转(乘法)注入,乘法形式天然保住了相对性。

旋转角的取值范围给出"波长"概念:第 $i$ 个平面的周期是 $2\pi/\vartheta_i$,最低频维度周期约 $2\pi\times 10^4$ 个位置。训练长度远小于这个周期的维度,在训练时根本转不完一圈——这是后面外推问题的伏笔。

多尺度时钟:同一位置 m=100,不同频率的二维平面转过不同角度 mθᵢ mθᵢ mθᵢ 高频平面:θᵢ≈1,周期≈6 token 中间维度:θᵢ=10⁻²,周期≈630 最低频:θᵢ=10⁻⁴,周期≈6.3 万 已转约 15.9 圈 转过 0.16 圈 0.0016 圈,几乎没动

与正弦位置编码的关系

看过原始 Transformer 的正弦编码后会有疑问:两者频率设计几乎一样,差别在哪?正弦编码是加性的——位置向量 $\mathrm{PE}_m$ 直接加到 token 嵌入上,位置信息进入后续所有线性层,内积里位置与内容交叉耦合,相对性要靠网络自己去学。RoPE 是乘性的——位置只作用在 $q,k$ 的旋转上,由旋转群性质自动保证 $\langle \tilde q_m, \tilde k_n\rangle$ 只依赖 $m-n$,相对性是结构自带而非学出来的。这解释了实证上的一个稳定现象:RoPE 收敛略快、外推略好。加性编码把位置当内容的一部分,乘性编码把位置当内容之间的几何关系——后者更贴近"位置"这个概念的本义。

还有一点值得提:RoPE 的相对性是"内积层面"的,不是逐分量的。注意力打分时才有相对性,MLP 层看到的 $\tilde q_m$ 仍然带着绝对旋转角。这不算缺陷(位置信息本就该在注意力里消费),但分析 RoPE 模型行为时别把"相对"理解成处处成立。

实现技巧与稀疏性

直接构造 $d\times d$ 旋转矩阵是浪费:$R_m$ 是块对角的,每块 $2\times 2$。实现上等价于

$$ \tilde q_{2i} = q_{2i}\cos m\vartheta_i - q_{2i+1}\sin m\vartheta_i,\quad \tilde q_{2i+1} = q_{2i}\sin m\vartheta_i + q_{2i+1}\cos m\vartheta_i, $$

两次乘加搞定一个平面,$\cos m\vartheta_i$、$\sin m\vartheta_i$ 还可以按位置预计算缓存。RoPE 的应用代价几乎可以忽略,这也是它打败一堆相对位置编码变体的工程原因。

看主流实现时有两个细节容易困惑。一是 HuggingFace 代码里的 inv_freq 就是 $\vartheta_i = \text{base}^{-2i/d}$,预计算成缓存表后按位置索引;二是"两两分组"存在两种约定——相邻配对 $(q_{2i}, q_{2i+1})$(GPT-NeoX 风格,转置连续维度)和对半配对 $(q_i, q_{i+d/2})$(原始论文风格),两者数学等价但权重布局不同,加载不同来源的权重时搞混这个约定会让输出完全错乱。排查 RoPE 相关的移植 bug,先查这一点。

外推问题的入口

RoPE 在训练长度内表现优异,但直接外推到 4 倍长度会崩。把失效机制按分量拆开看更清楚:每个二维平面给注意力打分提供一对基函数 $(\cos m\vartheta_i,\ \sin m\vartheta_i)$,而训练只在 $m\in[0,L)$ 这个区间上拟合过它们。外推时各维度处境完全不同——高频维度(周期几十个 token)在训练期已转过成百上千圈,外推产生的新角度落在同样密集的相位分布里,退化温和;真正的重灾区是中低频维度,训练期只走过 $[0,\,L\vartheta_i)$ 这一段弧,外推意味着输入角度进入网络从未见过的区间,而对 $\sin/\cos$ 这类基函数做区间外推没有任何泛化保证。位置插值(PI)把所有位置除以 $s$,等价于所有维度角速度同除 $s$:角度压回训练区间,代价是邻近位置的相位差也被压缩 $s$ 倍,短程分辨率变糊。NTK 缩放、位置插值、YaRN 这一系列外推方法,本质都是在处理"不同频率维度该用不同缩放"这个问题——那是下一篇的主题。这里只需记住:理解 RoPE 的一切后续工作,都建立在"多尺度时钟"这个图像上。

最后补一个常被误传的点:RoPE 论文提到内积有"远程衰减"性质,即 $q^\top R_{n-m}k$ 的期望幅度随距离增大而衰减。这个性质对随机向量大致成立(各频率分量随距离去相位,叠加后抵消),但对训练好的模型并不严格——网络完全可以学会把关键信息放进低频维度以保留远程依赖。远程衰减是初始化时的先验,不是训练后的约束。把它当成"RoPE 天生适合短程"是误读;实际情况是先验偏向短程平滑,而长程依赖靠低频通道学出来,两者配合得相当好。