资讯动态

旋转的魔力:RoPE复数形式如何重塑注意力分数与长距离衰减

发布时间:2026/9/18 19:49:58 来源:尧图企业网站定制
如果只能选一个词来回答“RoPE旋转位置编码为什么管用”我会毫不犹豫地说是旋转。很多讲位置编码的文章都止步于“RoPE把位置信息通过旋转矩阵加进去”这句话但这远远不够。你真正需要理解的是当位置编码以复数形式参与运算时它对内积——也就是注意力分数——到底施加了怎样的几何作用这背后不是玄学而是一套非常漂亮的数学。这篇文章我会从复数形式的推导出发把 RoPE 对注意力分数的几何影响一层层剥开顺便聊聊那些论文里没直接写、但实际推导和调参时一定会遇到的细节。这一篇对应的是系列问答里的 Q38。坦白讲第38次被问到“RoPE 的复数形式到底对注意力分数做了什么”我觉得是时候系统写一篇推导笔记了。它适合三类读者正在啃 Transformer 源码、想知道为什么位置编码要写成q * e^{imθ}的人做长文本外推、被“注意力漂移”问题折磨的人以及纯粹想把内积几何吃透的数学型选手。如果你是第一次接触 RoPE建议先熟悉基本的欧拉公式剩下的推导我会尽量做到每一步都有出处。1. 为什么偏偏是“旋转”RoPE 要解决的位置编码痛点1.1 绝对位置编码的尴尬编码进去了但用不上在 RoPE 出现之前最主流的做法是绝对位置编码——把位置 $p$ 直接加进 embedding或者加进每一层的输入。Transformer 原论文用的是正弦函数后来 BERT 用可学习的绝对位置向量。这类方法的问题不在于“能不能区分不同位置”而在于区分的方式和注意力机制本身的计算逻辑是脱节的。注意力分数的核心是内积$q \cdot k$。内积天然关注的是两个向量之间的“相对关系”——夹角小、方向接近分数就高。但绝对位置编码是把位置信息以加法形式揉进向量里的位置 $m$ 和位置 $n$ 的关系并不是显式地以 $(m-n)$ 的形式出现。模型要隐式地从绝对位置里学出相对关系这需要额外的参数和训练量。更麻烦的是一旦序列长度超过训练时的最大长度绝对位置编码直接“没见过的位置”就出现了外推能力几乎为零。所以研究者们一直在找一种方案能不能让位置编码的效果直接体现在内积结果里并且让这个内积结果只和相对位置 $(m-n)$ 有关这就是相对位置编码的动机RoPE 是其中做得最彻底、数学形式也最优雅的一个。1.2 为什么“加法不行乘法才行”如果我们把位置信息通过乘法注入向量例如 $q_m q \cdot r_m$、$k_n k \cdot r_n$那么内积就会变成$$ \langle q_m, k_n \rangle \langle q \cdot r_m, k \cdot r_n \rangle $$如果 $r_m$ 和 $r_n$ 是某种“好”的运算使得上式能化简为仅依赖 $(m-n)$ 的函数那我们就实现了相对位置编码的目标。什么样的运算是“好”的答案藏在复数乘法里。复数乘法有一个美妙的几何特性乘以一个模长为 1 的复数就是旋转。而旋转有一个关键性质——两个向量各自旋转之后再求内积等于其中一个向量先旋转“相对角度”再做内积。具体来说对于两个复数 $q e^{im\theta}$ 和 $k e^{in\theta}$它们的内积复共轭后相乘是$$ \langle q e^{im\theta}, k e^{in\theta} \rangle q e^{im\theta} \cdot \overline{k e^{in\theta}} q \bar{k} e^{i(m-n)\theta} $$看到了吗位置 $m$ 和 $n$ 消失了剩下的只有它们的差 $(m-n)$。这就是“旋转”的魔力加法做不到的事情乘法旋转做到了。这也是在整个 RoPE 推导中我要你始终记住的一句话——位置信息以旋转的方式参与最终以相位差的形式出现在内积里。2. 从欧拉到矩阵RoPE 复数形式的完整推导2.1 复数形式下旋转位置编码的“定义式”设词向量对应的 query 和 key 的二维形式为 $q [q_0, q_1]$、$k [k_0, k_1]$把它们看成复数 $q q_0 i q_1$、$k k_0 i k_1$。RoPE 的核心操作是对 $q$ 和 $k$ 各施加一个与位置相关的旋转$$ f_q(q, m) q \cdot e^{im\theta}, \quad f_k(k, n) k \cdot e^{in\theta} $$其中 $\theta$ 是预先设定的旋转角度基频。这时注意力分数内积的实部变成$$ \text{score}(m, n) \text{Re}\left[ q \bar{k} e^{i(m-n)\theta} \right] $$这个式子就是整个 RoPE 复数形式的“源点”。它揭示了三件事第一注意力分数只依赖于位置差 $(m-n)$绝对位置被完全消掉第二分数本质上是在原始内积 $q\bar{k}$ 的基础上做了一个角度为 $(m-n)\theta$ 的旋转投影第三$e^{i(m-n)\theta}$ 的实部 $\cos((m-n)\theta)$ 和虚部 $\sin((m-n)\theta)$ 会分别调制 $q\bar{k}$ 的实部与虚部。2.2 拆开实部和虚部从复数到旋转矩阵如果你不习惯复数可以把上面的式子拆回实数形式。把 $q\bar{k}$ 记为 $a bi$其中 $a \text{Re}(q\bar{k})$、$b \text{Im}(q\bar{k})$。那么$$ q\bar{k} e^{i(m-n)\theta} (a bi)(\cos\Delta\theta i\sin\Delta\theta) $$其中 $\Delta m-n$。展开并取实部$$ \text{score}(m,n) a\cos(\Delta\theta) - b\sin(\Delta\theta) $$这个形式和向量点乘 $\begin{bmatrix} a \ b \end{bmatrix} \cdot \begin{bmatrix} \cos(\Delta\theta) \ -\sin(\Delta\theta) \end{bmatrix}$ 是完全一致的。也就是说旋转位置编码对一对维度施加的效果等价于先把 key 对应的向量反向旋转 $\Delta\theta$再和 query 做标准内积。这跟我们前面说的“两个向量都旋转等于一个向量旋转相对角度”是同一件事。在实际实现中我们并不是真的在复数域做乘法而是用一个旋转矩阵来操作$$ R(\Delta\theta) \begin{bmatrix} \cos(\Delta\theta) -\sin(\Delta\theta) \ \sin(\Delta\theta) \cos(\Delta\theta) \end{bmatrix} $$也就是$$ \text{score}(m,n) q^T R(\Delta\theta) k $$这是 RoPE 最常见的向量形式也是源码里rotate_half函数的数学依据。2.3 高维扩展逐对维度旋转与频率分配上面只讨论了二维的情况。实际 Transformer 的 head 维度通常是 64、128 这样的偶数。RoPE 的处理方式是把 $d$ 维向量分成 $d/2$ 对每一对用一个独立的旋转角 $\theta_i$其中$$ \theta_i 10000^{-2i/d}, \quad i 0, 1, \dots, \frac{d}{2} - 1 $$于是完整的内积变成 $d/2$ 个二维内积的累加$$ \text{score}(m, n) \sum_{i0}^{d/2-1} \text{Re}\left[ q_i \bar{k}_i e^{i(m-n)\theta_i} \right] $$每一对维度都在自己的二维子空间里旋转但这些旋转的频率不同——低维度$i$ 小对应高频旋转高维度$i$ 大对应低频旋转。这个“不同频率”的设计是 RoPE 能够像正弦编码一样表达多尺度位置关系的核心。这里我要补一个源码里才会看到的细节rotate_half的实现并不是按“相邻两维一组”来旋转的。代码中是把向量分成前一半和后一半然后交错恢复成 $[q_0, q_1, \dots]$ 和 $[-q_{d/2}, \dots, -q_{d-1}]$ 的形式。数学上这两者等价但如果你自己手推用相邻配对的方式更容易理解几何含义用代码的切分方式更容易对照源码。3. 旋转如何改写注意力分数几何层面的逐项拆解3.1 内积的几何意义模长、方向、夹角在进入 RoPE 的几何影响之前先回到一个最基础的事实两个向量的内积等于“模长乘积 × 夹角余弦”$$ q \cdot k |q| |k| \cos\phi $$其中 $\phi$ 是两个向量的夹角。注意力分数的大小由两个因素决定一个是两个向量各自的长度模长另一个是它们的夹角。模长反映的是“这个 token 本身的强弱”夹角反映的是“两个 token 在语义空间中的方向一致性”。RoPE 的旋转操作不改变模长——旋转是等距变换$|R(\Delta\theta)q| |q|$。它改变的只有夹角。这就引出一个重要的几何结论RoPE 对注意力分数的影响是纯“方向性”的它不会因为位置编码而放大或缩小 token 本身的能量。3.2 相对距离如何调制注意力从矢量投影看“旋转相位差”有了模长不变的结论注意力分数在 RoPE 下的几何意义就变得非常清晰。我们把 $q$ 旋转到角度 $\phi_q m\theta$ 的方向把 $k$ 旋转到角度 $\phi_k n\theta$ 的方向那么两者夹角变成$$ \phi (\phi_q m\theta) - (\phi_k n\theta) (\phi_q - \phi_k) (m-n)\theta $$注意力分数为$$ \text{score}(m,n) |q||k|\cos\left[(\phi_q - \phi_k) (m-n)\theta\right] $$这个式子透露出非常直观的几何行为当 $m n$ 时位置贡献为 0分数退化为原始语义内积 $|q||k|\cos(\phi_q - \phi_k)$当 $|m-n|$ 逐渐增大时余弦函数的自变量会以 $(m-n)\theta$ 的步长线性偏移当 $(m-n)\theta$ 超过 $\pi$ 时余弦值可能从正变负也就是说原本语义相近的两个 token 会因为距离过远而被抑制。一个具体例子设 $\theta 0.1$两个 token 的语义夹角 $\phi_q - \phi_k 0$完全同向那么距离为 8 时$\cos(0.8) \approx 0.696$分数还行但距离为 16 时$\cos(1.6) \approx -0.029$注意力几乎消失距离为 32 时$\cos(3.2) \approx -0.998$分数变成强烈的负值。这就是“相对距离调制注意力”的几何本质。3.3 二维配对与语义维度混合每个子空间都在独立旋转上面说的是单个二维配对上的行为。整个 head 的注意力分数是 $d/2$ 个配对子空间分数之和。因为 $\theta_i$ 是逐对不同的所以不同维度对的相位偏移速度不一样。说个更直白的几何图像如果 $d8$那么有 4 对子空间每对都是一个小的复平面。query 在这个复平面里的投影长度可能很短在另一个复平面里可能很长。距离 $m-n$ 增大时第一对子空间旋转得快对应 $\theta_0 1$第二对旋转得慢对应 $\theta_1 0.01$等等。最终的注意力分数是所有“旋转后投影”之和。这意味着每个 token pair 的注意力分数是由多个频率的余弦波的加权叠加决定的。低位维度负责精细的短距离位置感知高位维度负责粗粒度的长距离位置感知。不同频率叠加之后整体会出现振荡衰减的现象——这为下一节的长距离衰减埋下了伏笔。4. 长距离衰减不是偶然振荡求和与狄利克雷核4.1 从单维度到完整注意力分数求和带来的“抵消效应”如果只有一对维度那么注意力分数就是一个单纯的余弦函数距离越大越振荡没有衰减。但因为 RoPE 有 $d/2$ 对维度且频率从 $\theta_0$ 到 $\theta_{d/2-1}$ 指数递减完整分数是一个多频率余弦的叠加$$ S(\Delta) \sum_{i0}^{d/2-1} \text{Re}\left[ q_i \bar{k}_i e^{i\Delta\theta_i} \right] $$假设 $|q_i| |k_i| 1$ 且 $q_i\bar{k}_i 1$所有维度同相位那么$$ S(\Delta) \sum_{i0}^{d/2-1} \cos(\Delta\theta_i) $$问题变成不同频率的余弦波叠加后振幅如何随 $\Delta$ 变化直观上低频项$\theta_i$ 小变化慢高频项$\theta_i$ 大变化快。当 $\Delta$ 增大时高频项开始快速振荡它们的正负号频繁交替互相抵消低频项虽然仍在贡献正值但数量少。整体结果是$S(\Delta)$ 的峰值出现在 $\Delta$ 较小时随着 $\Delta$ 增大净分数被限制在一个围绕 0 的带内振荡。这个效应在数学上有一个很强的类比——狄利克雷核。它是一系列同频余弦的求和形式会出现主瓣集中在原点附近、旁瓣衰减振荡的特征。RoPE 的注意力分数正是这种“准狄利克雷核”的行为。4.2 平均场近似的解析边界$|\sum e^{i\Delta\theta_i}|$ 的上界为了更严谨地说明“为什么不会随距离无限累积”考虑把所有 $q_i\bar{k}_i$ 都设为 1 的简化情况。注意力分数去掉 Re 之前是$$ \sum_{i0}^{d/2-1} e^{i\Delta\theta_i} $$当 $\theta_i 10000^{-2i/d}$ 时这是一个关于 $i$ 的等比级数。记 $r e^{i\Delta \cdot 10000^{-2/d}}$则$$ \sum_{i0}^{d/2-1} e^{i\Delta\theta_i} \frac{1 - r^{d/2}}{1 - r} $$其模长上界为$$ \left|\sum_{i0}^{d/2-1} e^{i\Delta\theta_i}\right| \le \frac{2}{|1 - e^{i\Delta \cdot 10000^{-2/d}}|} \frac{1}{|\sin(\Delta \cdot 10000^{-2/d} / 2)|} $$注意这个上界与 $d$ 无关在等比级数视角下只与 $\Delta$ 和底数 10000 有关。当 $d$ 增大、$\theta_0$ 更接近 1 时高频部分的抵消会更充分但整体仍被 $1/\sin$ 形的包络约束。这就是为什么 RoPE 的长距离注意力分数会落在一个有界区域里振荡而不是线性增长或发散。4.3 “注意力漂移”的几何解释长距离外推时相位混沌开始出现热词里有“rope 导致注意力漂移吗”我直接给结论RoPE 本身不会导致注意力“漂移”但它会在长距离外推时引发一种特殊的相位混沌。什么叫相位混沌训练时模型见过的距离 $\Delta$ 大多在 0 到训练长度 $L$ 之间。对于高频维度$\theta_i$ 接近 1$\Delta\theta_i$ 可以快速跨越多个 $2\pi$ 周期。模型在训练中学习到的注意力模式是这些多频率分量在“已见距离”上的稳定叠加。但当推理时的 $\Delta$ 远超 $L$高频分量的相位已经进入完全不可预测的周期状态——你不知道 $\cos(\Delta\theta_i)$ 落在哪个区间。而低频率项的贡献太弱无法压住高频率项的随机振荡。结果就是长距离外推时注意力分数不再遵循训练时学到的平滑衰减规律而是变成一个类似噪声的振荡函数。这不是“漂移”而是“相位失稳”。解决思路也因此非常清晰要么调整频率分配如 YaRN 的 NTK 缩放要么直接改变旋转角的基础频率让高频分量在目标长度内不要转满太多圈。5. 工程视角下的 RoPE维度配对、θ_i 取值的隐藏学问5.1 为什么底数是 10000频率分布怎么影响感受野$\theta_i 10000^{-2i/d}$ 里面那个 10000和 Transformer 原论文正弦位置编码里的 10000 如出一辙。这个数的选择不是拍脑袋它决定了各维度旋转频率的跨度。拿 $d64$ 举例$i0$: $\theta_0 1$对应周期 $2\pi \approx 6.28$最高频$i31$: $\theta_{31} 10000^{-62/64} \approx 0.00135$对应周期约 4654最低频。也就是说最低频维度在距离约 4654 时才会转完一整圈最高频维度在距离 6.28 时已经转完一圈。这样的跨频率设计让 RoPE 同时具备“短距离高分辨率”和“长距离不饱和”的能力。如果你希望模型支持更长的序列一个直接的思路是增大底数比如从 10000 调到 500000这会降低所有频率让高频维度在给定距离内转的圈数变少从而减小相位混沌风险。这也是 NTK 类缩放方法的核心思想之一。5.2 不同 head、不同层的旋转角一个常被忽略的细节你在看源码时可能会发现很多实现里所有 head 共用同一组 $\theta_i$。但严格来说这并非必须。有些改进工作会对不同 head 采用不同的底数或偏移让不同的 head 有不同的位置感受野——有的 head 专注短距离依赖有的 head 专注长距离建模。一个实用的建议是在做长文本微调时不要只调底数还要观察每个 head 的平均注意力距离。如果某个 head 在短距离任务上表现很好但长距离总是学不动可以考虑对它的 $\theta_i$ 做分组缩放而不是一刀切。5.3 完整的 RoPE 实现从 NumPy 到推理优化的细节最后给一个快速可跑的参考实现用 NumPy 实现 RoPE 前向传播的核心部分。这只是教学用让你看清维度的组织方式import numpy as np def precompute_rope_angles(d_model, seq_len, base10000.0): # d_model 必须是偶数 assert d_model % 2 0 # 每对维度一个频率, shape (d_model // 2,) freqs 1.0 / (base ** (np.arange(0, d_model, 2) / d_model)) # 位置索引 shape (seq_len, 1) positions np.arange(seq_len)[:, None] # angles shape (seq_len, d_model // 2) angles positions * freqs[None, :] return angles def apply_rope(q, k, angles): # q, k shape: (batch, seq_len, num_heads, head_dim) # 这里仅处理单条序列单头的情况 seq_len q.shape[1] cos np.cos(angles)[None, :, :] # (1, seq_len, head_dim//2) sin np.sin(angles)[None, :, :] # (1, seq_len, head_dim//2) def rotate(x): # 把 x 转成两半, 再拼回去 x1 x[..., : x.shape[-1] // 2] x2 x[..., x.shape[-1] // 2 :] # 旋转操作: (x1*cos - x2*sin, x1*sin x2*cos) rotated np.concatenate( [x1 * cos - x2 * sin, x1 * sin x2 * cos], axis-1 ) return rotated q_rot rotate(q) k_rot rotate(k) return q_rot, k_rot一个容易踩的坑rotate里 x1 取前一半、x2 取后一半这是源码常见的做法。它和数学上“第 0 维和第 1 维配对”并不完全一致但结果等价因为这种切分方式等价于把配对顺序重新排列了。你只要保证训练和推理用的切分方式一致即可。5.4 实测经验RoPE 在 FP16 下的数值稳定性RoPE 涉及大量的正弦余弦计算。在 FP16 混合精度训练时余弦值在接近 $\pm 1$ 时精度尚可但在角度接近 $\pi/2$、余弦值接近 0 时相对误差会变大。这个问题在和旋转矩阵的乘法叠加后可能导致梯度噪声。我的建议是旋转角度的计算最好在 FP32 下完成再 cast 到 FP16/FP32 参与后续矩阵乘。如果你用的是 PyTorch 之类的框架它会自动处理大部分情况但如果你在写自定义 CUDA kernel这个问题一定要留意。我们曾经在长序列训练时遇到过 loss 抖动排查到最后就是 RoPE 角度在 FP16 下的累积误差导致的把角度计算改成 FP32 之后就稳定了。6. 关于“注意力漂移”的讨论与实测经验6.1 哪些情况下你会真正感知到 RoPE 的几何行为异常前面讲了相位混沌的理论背景实际操作中它通常表现为三种可观测现象注意力分数的距离-分数曲线出现反常尖峰某个远距离位置对当前 token 的注意力异常高不是语义原因而是相位叠加导致的偶发现象长文本困惑度perplexity在某个长度处突然恶化这通常是高频维度在这个距离上“转满”了 $2\pi$ 的整数倍导致位置区分度丢失外推长度增加时模型出现重复生成位置信息对某些 head 失效后模型只能靠语义信息“猜”当前位置容易陷入循环。6.2 调参经验如何处理 4K 到 32K 的长度外推做长文本外推时我试过三种方案简单分享效果方案操作效果注意点线性内插把位置索引除以缩放因子 $\lambda$短距离性能略降但 4K→8K 可稳定需要几万步微调提升空间有限NTK 感知缩放YaRN 风格修改底数 $base base \times \lambda^{d/(d-2)}$长距离保持较好短距离几乎不掉点高频和低频的平衡需要按 head 调试随机截断 位置丢包训练时随机丢掉部分位置编码稳定常用方案但效果提升中等综合性不如前两者适合作为辅助我最常用的组合是NTK 风格底数修改 少量步数的位置插值微调。实测在 4K 训练、32K 推理的任务上perplexity 能控制在可接受范围。底数修改时如果 $d$ 是 64$\lambda8$那么新的 base 约等于 $10000 \times 8^{64/62} \approx 10000 \times 8.1 \approx 81000$。不要凭感觉乱设直接按公式来。6.3 站在几何视角重新审视 RoPE它到底“学会”了什么最后说一点我个人的理解。RoPE 的复数形式之所以优雅是因为它把位置编码从“附加特征”变成了“算子”。它不是往向量里加信息而是对向量的方向做了旋转操作。注意力分数因此获得了显式的相对位置调制这个调制是刚性的——不依赖训练数据的分布只要你给定 $(m-n)$相位差就确定了。但刚性也带来脆弱性在训练长度之外刚性的旋转会变成无约束的混沌。所以 RoPE 从来不是一个“安装完就不管”的组件。它需要你理解它的频率分配、理解它的相位行为、理解它和长度的关系。你越懂它的几何越知道在长文本外推、微调、混合精度、kernel 优化这些环节里该往哪个方向使劲。如果你接下来要做 RoPE 的源码复现或者长文本优化我建议你先把 $d4$ 或 $d8$ 的完整前向用 NumPy 手写一遍把每一对维度的余弦值随位置的变化画出来。画完那几张图你对 RoPE 的直觉会比读十篇论文都管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价