音乐
暂未播放
RoPE 完全拆解:旋转位置编码的原理、实现与长度外推
背景:为什么 Transformer 必须显式编码位置#
自注意力(self-attention)有一个经常被忽略的性质:它是排列不变的(permutation invariant)。把输入序列 x1,x2,…,xn 打乱顺序重新排列,输出中每个位置的向量只是跟着重新排列,计算过程本身完全不感知顺序——因为注意力做的就是”每个 token 跟其他所有 token 做内积再加权求和”,而求和与顺序无关。
这对语言模型是致命的。句子”我不爱你”和”你不爱我”的词完全相同,只有顺序不同,语义却相反。如果模型看不见顺序,这两句话对它毫无区别。因此 Transformer 必须显式把位置信息注入到表示中,这就是位置编码(positional encoding)存在的意义。
围绕”怎么注入位置”这个问题,2021 年之前的主流方案可以分成两类:
绝对位置编码(absolute positional encoding):把位置当作一个附加的”标签”加到 token 上。
- 可学习嵌入(learned embedding,BERT 使用):训练一个位置向量表,位置 m 对应一个可学习向量 pm,加到 token embedding 上。简单直接,但有一个硬伤——训练时最多见过 512 个位置(BERT 的 max length),推理时序列一旦超过 512 就无向量可用。
- 三角位置编码(sinusoidal,原始 Transformer 使用):用固定频率的正弦、余弦函数生成位置向量,理论上可以推广到任意长度。但它把位置信息”加”进 embedding 后,经过多层注意力、MLP 的叠加,位置信号会被逐层稀释、与内容信号纠缠,模型很难提取出干净的”相对距离”信息。
相对位置编码(relative positional encoding):不编码”第 m 个位置”,而是编码”两个 token 相隔多远”。
- T5 relative bias:在注意力得分上加一个只依赖 m−n 的可学习偏置。
- Transformer-XL / Shaw et al.:在注意力公式里显式加入相对位置项。
- ALiBi:在注意力得分上线性减去 ∣m−n∣ 倍的斜率。
相对位置编码信息更精准,但代价是:要么修改注意力公式的结构(增加实现复杂度、破坏与 FlashAttention 等优化 kernel 的兼容性),要么额外引入可学习参数。
RoPE(Rotary Position Embedding,旋转位置编码)于 2021 年由苏剑林团队提出(RoFormer 论文,NeurIPS 2021),它给出了一条不同的路线:用旋转矩阵把绝对位置”乘”进 query 和 key 的向量里,让内积结果天然只依赖相对位置。形式上它像绝对位置编码(实现简单,不碰注意力公式),性质上却是相对位置编码(内积只依赖位置差)。这个”绝对的形式、相对的本质”让它成为 LLaMA、Mistral、Qwen、DeepSeek 等几乎所有主流开源模型的标准配置——你在前面读过的 GQA、MLA、DeepSeek-V4 混合注意力 里,RoPE 都是地基。
这篇文章从数学原理、工程实现到长度外推,把 RoPE 拆到最底层。
核心思想:把”位置差”变成”旋转角差”#
目标函数:内积只依赖相对位置#
先放下具体形式,想想我们希望位置编码满足什么条件。设 fq(xm,m) 是把位置 m 编码进 query 向量的函数(xm 是第 m 个 token 的向量),fk(xn,n) 同理。注意力得分的核心是内积 ⟨fq(xm,m),fk(xn,n)⟩。我们希望这个内积只依赖相对位置 m−n,而不依赖 m、n 各自是多少,即存在某个函数 g 使得:
⟨fq(xm,m),fk(xn,n)⟩=g(xm,xn,m−n)翻译成人话:无论第 5 个 token 和第 9 个 token,还是第 1005 个和第 1009 个 token,只要相对距离都是 4,注意力得分就应该一样(平移不变性)。这是 RoPE 全部设计的出发点。
二维情形:完整的求解过程#
论文先考虑最简单的 d=2 情形,用复数把二维向量看作平面上的点。这一步值得完整走一遍,因为它揭示了”为什么答案必须是旋转”。
把二维向量 (a,b) 写成复数 a+bi。两个二维向量的内积等于复数乘积的实部:
⟨q,k⟩=Re[q⋅k∗]其中 k∗ 是 k 的共轭复数(实部不变、虚部取负)。再假设 fq(xm,m) 可以写成极坐标形式 Rq(xm,m)eiΘq(xm,m),其中 Rq 是模长、Θq 是幅角,fk 同理。把它们代进目标等式,注意到位置 m=n 时两个 token 位置相同,应有 g(xm,xm,0)=∥q∥∥k∥(此时退化成普通内积),可以得到两条关键结论:
第一,模长与位置无关。 对比两边模长:
Rq(xm,m)⋅Rk(xn,n)=Rg(xm,xn,m−n)当 m=n 时右边是 Rg(xm,xn,0)=∥q∥∥k∥,与 m 无关;要让两边恒等,只能让 Rq、Rk 与位置无关,即模长不变。直观上也说得通:位置编码不该改变”这个 token 内容有多大”,只该改变”它的朝向”。
第二,幅角按等差增长。 对比两边幅角,令 m=n 得幅角差与内容无关、只与位置有关。记公共的幅角函数为 Θf(x,m),则对任意 token x 有
Θf(x,m)=ϕ(m)+θx其中 θx 是 token 自身方向(m=0 时的幅角)。再把 n=m+1 代入,可以得到 ϕ(m+1)−ϕ(m) 是与 m 无关的常数——换句话说,每前进一个位置,幅角转过的角度恒定,所以 ϕ(m) 必须是等差序列:
ϕ(m)=mθ+γ取 γ=0,并记 q=fq(xm,0)=Wqxm、k=fk(xn,0)=Wkxn(Wq,Wk 是 query/key 投影矩阵,位置 0 时函数退化为普通投影),就得到最终的答案:
fq(xm,m)fk(xn,n)=(Wqxm)eimθ=(Wkxn)einθ其中 θ∈R 是预设的非零常数。写成矩阵形式,复数乘法 eimθ 就是二维平面上的旋转矩阵:
Rm=(cosmθsinmθ−sinmθcosmθ)“旋转”这个名字的由来就在这里:给向量乘上 eimθ,就是把它在平面上逆时针旋转 mθ 弧度。位置 m 越大,转过的角度越多。
论文里的图 1 直观展示了这个过程——左边是原始 query/key 向量 (x1,x2),右边是旋转 mθ1 之后的位置编码向量:

(图源:RoFormer 论文 Figure 1。图中 θ1 是第一个频率,mθ1 是位置 m 对应的旋转角,位置 1、2、5 等分别转过不同角度。)
为什么内积自动变成相对位置#
现在把 fq、fk 代入内积,关键的一步来了——两个旋转矩阵相乘,角度直接相减:
⟨fq(xm,m),fk(xn,n)⟩=(RmWqxm)⊤(RnWkxn)=(Wqxm)⊤Rm⊤Rn(Wkxn)=(Wqxm)⊤Rn−m(Wkxn)最后一步用到了旋转矩阵的两个性质:Rm⊤=R−m(转置等于逆,因为旋转矩阵是正交矩阵),以及 R−mRn=Rn−m(旋转可以叠加)。于是位置信息在注意力得分里只剩下 n−m 一个量,两个 token 相隔 5 步还是相隔 500 步,取决于旋转角之差,而不是各自的位置。这正是上一节目标等式要求的形式。
这个推导值得停下来体会一下:整个过程没有”设计”编码公式,而是先写下”内积只依赖相对位置”这个需求,然后通过复数运算一步步逼出了唯一合理的解——旋转。苏剑林在博客里总结为”通过绝对位置编码的方式实现相对位置编码”,一句话点破了它的双重身份。
高维推广:分块旋转矩阵与频率序列#
二维情形的解很漂亮,但实际的 hidden size 通常是 128、256、4096 这样的偶数维。把 d 维空间拆成 d/2 个互相独立的二维子空间,每个子空间里做旋转,就得到分块对角矩阵形式的旋转矩阵:
RΘ,md=cosmθ1sinmθ1−sinmθ1cosmθ1cosmθ2sinmθ2−sinmθ2cosmθ2⋱cosmθd/2sinmθd/2−sinmθd/2cosmθd/2每个二维块负责一对维度,使用自己的角频率 θi。频率序列的取值是:
θi=10000−2i/d,i=0,1,…,d/2−1这是一个几何级数:从 θ0=1(最”慢”,转一圈要 2π 个位置)到 θd/2−1=10000−(d−2)/d(最”快”)。为什么要用几何级数而不是统一一个频率?有两个原因:
- 呼应原始 Transformer 的 sinusoidal 编码:Vaswani 等人 2017 年的三角位置编码用的正是 10000−2i/d 这个几何级数频率,实验证明它比均匀频率效果好。RoPE 沿用了这个被验证过的设计。
- 频率分工:慢频率(小 θ)的旋转周期长,能区分”很远”的位置,负责长距离依赖;快频率(大 θ)的旋转周期短,对相邻位置敏感,负责局部信息。多尺度频率让不同维度各司其职——这个特性在后面讲长度外推时会反复出现。
旋转矩阵还有两个对工程至关重要的性质:
- 正交性:RΘ,md 是正交矩阵(R⊤R=I),转置就是逆,所以 Rn−md=(RΘ,md)⊤RΘ,nd 成立,数值上非常稳定,不会像普通矩阵连乘那样积累误差。
- 保范性:旋转不改变向量长度 ∥Rx∥=∥x∥。这意味着位置编码不会扰动注意力 softmax 的分母——q⊤k/d 的尺度完全由内容决定,位置只改变方向。论文还利用这一点把 RoPE 推广到了线性注意力上(旋转可以放进 kernel 里而不破坏归一化,这是加法型位置编码做不到的)。
另外回答一个常见疑问:为什么只旋转 Q 和 K,不旋转 V? 因为位置信息需要进入的是注意力得分 q⊤k(决定”谁和谁有关”),而 V 只负责”提供内容”。旋转是等距变换,把 V 全部旋转一遍不改变加权和的结果,只会白费算力,所以 V 保持原样。KV cache 里也只需要对 K 施加位置、V 直接存原值。
远程衰减:为什么远处的 token 相关性天然变弱#
自然语言里有个经验规律:距离越远的词,语义关联通常越弱。位置编码如果能”内置”这个偏置,模型学起来会轻松很多。RoPE 恰好做到了这一点——论文证明了在 θi=10000−2i/d 的设置下,注意力得分的大小随相对距离增大而衰减,这个性质叫 long-term decay(远程衰减)。
先看怎么把 q⊤k 写成复数求和。把 q=Wqxm、k=Wkxn 按维度两两配对,每个内积项就是一对复数的乘积:
(RΘ,mq)⊤(RΘ,nk)=Rei=0∑d/2−1q[2i:2i+1]k[2i:2i+1]∗ei(m−n)θi其中 q[2i:2i+1] 表示 q 的第 2i 和第 2i+1 维组成的复数。整个求和就是 d/2 个”旋转后的复数”相加,每个复数带着自己的相位 (m−n)θi。
问题是:为什么一堆相位不同的复数相加,和的模长会随 m−n 衰减? 论文用 Abel 变换(离散分部求和)给出了上界。记 hi=q[2i:2i+1]k[2i:2i+1]∗,Sj=∑i=0j−1ei(m−n)θi,则
i=0∑d/2−1hiei(m−n)θi=−i=0∑d/2−1Si+1(hi+1−hi)取模长并用三角不等式放缩:
i=0∑d/2−1hiei(m−n)θi≤imax∣hi+1−hi∣⋅i=0∑d/2−1∣Si+1∣hi 是相邻维度对的乘积,它的一阶差分 maxi∣hi+1−hi∣ 是有界的;而关键的量是右边那个几何级数和 ∑i∣Si∣。论文证明,当 θi=10000−2i/d 时,Si 的均值 d/21∑i=1d/2∣Si∣ 随着 ∣m−n∣ 增大而单调衰减,衰减曲线的形状如论文图 2 所示:

(图源:RoFormer 论文 Figure 2。横轴是相对距离 ∣m−n∣,纵轴是上界 d/21∑i=1d/2∣Si∣ 的相对值,曲线随距离增大而下降。)
这个衰减的直觉其实很朴素:几何级数的频率 θi 让”旋转角”按维度从慢到快排开。当 ∣m−n∣ 很大时,大部分高频项的相位 ei(m−n)θi 已经转了好几圈,在复平面上指向四面八方、互相抵消;只有少数低频项还保持大致同向。抵消得越多,总和的模就越小。
但要注意两点:
- 远程衰减是”软”约束。它只说明内积上界随距离收缩,模型完全可以在训练中通过调节 hi(即学出来的 q、k 分量)来放大某个特定距离的响应。它内置的是”先验倾向”,不是硬性限制。
- 衰减并非均匀。不同头、不同维度对距离的敏感度不同,模型可以学到”某些头专注局部、某些头专注远程”的分工,这正是多头注意力的灵活性所在。
工程实现:布局约定、PyTorch 代码与推理侧细节#
数学上”稀疏矩阵乘法”,工程上”逐元素乘加”#
直接拿分块对角矩阵 RΘ,md 去乘向量是浪费的——矩阵里几乎全是零。论文给出的高效实现把旋转写成逐元素运算。对第 i 对维度 (x2i,x2i+1)(这里用 0 起始下标):
(x2i′x2i+1′)=(cosmθisinmθi−sinmθicosmθi)(x2ix2i+1)整个向量上就是:
RΘ,mdx=x⊗(cosmθi 每维重复)+rotate_half(x)⊗(sinmθi 每维重复)其中 rotate_half 交换配对维度并给其中一个取负号。全部计算只有乘法和加法,没有矩阵乘法——两个长度为 d 的向量逐元素乘,总共 O(d) 次运算。
两种布局约定:interleaved 与 split-half#
“哪两个维度配成一对”有两种约定,这是阅读各种开源实现时最容易踩的坑:
| 约定 | 配对方式 | 代表实现 |
|---|---|---|
| interleaved(交错) | 相邻维度配对 (x2i,x2i+1) | GPT-J、HuggingFace 的 RoFormer 实现 |
| split-half(半切) | 前半与后半配对 (xi,xi+d/2) | GPT-NeoX、LLaMA、Mistral |
两种约定数学上完全等价(都是同样的旋转,只是坐标顺序不同),但频率 θi 的对应关系不同。split-half 的实现里,rotate_half 操作是”把后半部分取负后接到前半部分后面”:
1def rotate_half(x):2 """split-half 约定下的配对旋转:交换两半并给后半取负。"""3 x1 = x[..., : x.shape[-1] // 2] # 前半4 x2 = x[..., x.shape[-1] // 2 :] # 后半5 return torch.cat([-x2, x1], dim=-1)6
7def apply_rotary_pos_emb(x, cos, sin):8 return x * cos + rotate_half(x) * sin而 interleaved 约定下最自然的写法是直接把相邻对当复数乘:
1def apply_rotary_pos_emb_interleaved(x, cos, sin):2 # x: [..., d] 视为 [..., d/2, 2],用 view_as_complex 转成复数3 x_complex = torch.view_as_complex(x.reshape(*x.shape[:-1], -1, 2))4 # freqs_cis[m] = cos(mθ) + i·sin(mθ),预计算好的一张复数表5 out = x_complex * freqs_cis6 return torch.view_as_real(out).flatten(-2)实际推理框架里几乎都预计算好 cos、sin 表:对每个位置 m 和每个频率 θi,预先算出 cosmθi 和 sinmθi,存成形状为 [max_seq_len, d/2] 的常量张量(复数版就是 freqs_cis),推理时只做查表 + 乘加。生成过程中序列长度逐步增长,但每个 token 的旋转角在它生成那一刻就固定了,之前算过的位置无需重算。
推理侧:decode 时的位置、KV cache 与融合 kernel#
在自回归解码中,RoPE 的具体行为值得单独理一遍:
- Prefill 阶段:对整段 prompt 的每个 token 施加各自的位置旋转,然后算注意力。此时位置 0 到 L−1 的 cos/sin 表都要用到。
- Decode 阶段:每次只生成 1 个新 token,它位于位置 m= 当前序列长度。只需要对这一个 token 查一次表、做一次旋转;已有的 KV 全部不用动(它们的旋转角在写入时就固定了)。
- KV cache:K 旋转之后再写入缓存(因为注意力需要的是旋转后的 K),V 原样写入。KV cache 里存的是”已带位置信息”的 K——这也是为什么 KV cache 无法在请求间复用长度不同的前缀,位置信息已经烧进 K 里了(关于这一点,跨模型 KV 转移里有更深入的讨论)。
- 融合 kernel:现代推理框架不会单独跑一次 RoPE 再算注意力。vLLM 的 PagedAttention 系列 kernel 和 FlashAttention 的
rotary embedding接口都把旋转融合进注意力 kernel 内部:加载 K 块时顺带完成旋转,省掉一次额外的显存读写。对带宽敏感的解码阶段,省这一次全量 K 的读写就是几个百分点的吞吐。 - 矩阵吸收:在 MLA 这类低秩注意力里,RoPE 因为打破矩阵乘法的结合律(旋转依赖位置、不能吸收进 Wq),必须”解耦”出来单独作用,代价是增加一点 KV cache 占用——MLA 完全拆解里讲过这个设计权衡。
长度外推:RoPE 的阿克琉斯之踵#
RoPE 把位置信息做得这么优雅,是不是就能无限外推了?不能。以固定长度训练(比如 4k token)的模型,在更长的序列上推理时,困惑度会灾难性上升——这不是 RoPE 特有的毛病,但 RoPE 模型的失效方式很有代表性。
为什么”相对位置编码”也会外推失败?问题出在旋转角度的分布上。模型训练时见过的是 0≤∣m−n∣<4096 范围内的位置差;推理时序列长到 32k,∣m−n∣ 第一次超出训练范围。此时高频维度(旋转最快的那些)的相位 ei(m−n)θi 进入了模型从未见过的角度区间,注意力模式完全乱掉。
更精确的失效分析来自 YaRN 论文(Peng et al., 2023)提出的波长视角。每个维度 i 的旋转周期是:
λi=θi2π=2π⋅100002i/d维度 i 越小,波长越短。LLaMA 模型(d=128,base 10000)里,维度 0 的波长只有 2π≈6.3 个 token(每 6 个 token 就转一整圈),而维度 63 的波长是 2π×10000≈62832 个 token。不同维度对”距离”的分辨率差了四个数量级:

(图源:YaRN 论文 Figure 1。横轴是维度编号,纵轴是波长,对数刻度下是一条直线,直观展示了几何级数频率造成的巨大差异。)
波长分析揭示了两件相反的事:
- 短波长的维度(高频):它们对相邻 token 的分辨率至关重要——两个相邻位置在这些维度上转过的角度差很大,模型靠它们区分”紧挨着的词”。但正因为转得快,序列一超过训练长度,它们的相位就迅速偏离训练分布。
- 长波长的维度(低频):它们转得慢,训练时在 4k 序列里根本没转过一圈,相位几乎没变过——模型实际上根本没学会使用它们。这些维度在超长序列上恰恰是最需要的。
所以”直接外推”(把位置索引 0..32767 原样喂进去)两头不讨好:高频维度偏离分布、低频维度从未被训练利用。要让 4k 模型支持 128k,必须改造位置编码本身。这催生了下面几个经典的扩展方法。
四大长度扩展方法:PI、NTK、YaRN 与生产实践#
Position Interpolation:把尺子压扁#
PI(Position Interpolation,Chen et al., Meta AI, 2023)的思路最直白:训练时模型只见过 4k 个位置,那就把 32k 的序列”压缩”回 4k 的范围内。做法是把位置索引除以缩放因子 s=L′/L:
f′(xm,m)=f(xm,m/s)以 4k → 32k 为例,s=8,原本位置 0, 1, 2, …, 32767 变成 0, 0.125, 0.25, …, 4095.875。等价的说法是:把原始频率全部除以 8,让”尺子的刻度变密”,用原来的角度范围覆盖更长的距离。
PI 有扎实的理论支撑:论文证明了插值后的注意力得分上界比直接外推小约 600 倍(外推时高频维度的内积可以无界增长,插值时所有角度都被限制在训练范围内),因此微调更稳定。实验上,LLaMA 7B–65B 只需约 1000 步微调就能扩展到 32k,且 32k 内保持质量。
但 PI 的代价也很明显:局部分辨率损失。缩放 s=8 后,相邻两个 token 的位置差从 1 变成 0.125,高频维度上”相邻 token 旋转角差”被压缩,模型区分紧邻 token 的能力下降——这正是 YaRN 论文指出的”PI 丢失高频信息”问题,缩放倍数越大越严重。
NTK-aware:改 base,而不是改位置#
NTK-aware(bloc97, 2023,名字借自神经正切核理论)换了个角度:不改位置索引,改频率序列的 base。把 base 从 b 提高到
b′=b⋅sd/(d−2)几何级数频率 b−2i/d 被整体”拉伸”。拉伸的效果对每个维度不是均匀的:高频维度(i 小,指数接近 0)几乎不动,保留了对相邻位置的精细分辨;低频维度(i 接近 d/2,指数接近 −1)被显著压低,旋转周期变长、能覆盖更远的距离。
比起 PI,NTK-aware 最大的优点是零微调即可用:直接把 base 换成新的值,模型在 4k 内的行为几乎不变,同时能把序列推远好几倍。它的局限在于:用单一标量 s 缩放所有维度,缺少针对”哪些维度该插值、哪些该外推”的精细控制;而且最优 base 要靠经验试出来,训练/推理的 scale 必须固定。
YaRN:按波长分工 + 温度补偿#
YaRN(Peng et al., 2023,Nous Research + EleutherAI)把上面的分析推到极致,核心判断是:不同波长的维度应该区别对待——波长远小于训练上下文长度的维度(高频,负责局部)应该保持原样、继续外推;波长大于或接近上下文长度的维度(低频,负责远程)应该插值、压回训练范围;中间地带平滑过渡。
定义每个维度的”旋转圈数”比值 r(d)=L/λd(L 是训练上下文长度,λd 是该维度波长),即”在这个维度上,训练长度的序列一共转过多少圈”。引入两个阈值 α、β(LLaMA 家族的经验值是 α=1、β=32),用斜坡函数 γ(r) 决定每个维度插值多少:
γ(r)=⎩⎨⎧01β−αr−αr<αr>β其他然后逐维度混合”插值后的频率”和”原始频率”:
θi′=(1−γ(r(i)))sθi+γ(r(i))θir<α 的维度(转了超过 β 圈……这里注意方向:r 大说明波长短、转的圈数多)完全外推(γ=1,用原始 θi),r>β 的维度完全插值(γ=0,用 θi/s)。这就是 “NTK-by-parts”(按部分处理)的含义。
YaRN 的第二招是注意力温度缩放。直觉是:序列变长后,平均来看每个 query 面对的”近距离 token 密度”下降,softmax 的分布熵变大,模型学到的最佳注意力锐度不再匹配。补偿方式是给注意力得分乘一个温度 t:
softmax(tdqm⊤kn)LLaMA/Llama 2 上拟合出的经验公式是 1/t=0.1ln(s)+1(s 为缩放倍数)。工程上的巧妙之处在于:温度可以乘进预计算的复数旋转嵌入里(“长度缩放”技巧),不需要改任何注意力代码,推理和训练零额外开销。
实验上,YaRN 只需 400 步、约 1 亿 token(预训练数据的 0.1%) 微调,就能把 Llama 2 7B/13B 从 4k 扩展到 64k,再外推到 128k——训练量比 PI 少约 10 倍,步数少 2.5 倍。下图是 YaRN 论文中固定 s=8 时各缩放方法在 896 篇 16k token 文档上的困惑度对比:

(图源:YaRN 论文 Figure 4。横轴是评估用的上下文长度,纵轴是困惑度;YaRN 在短程和长程都保持更低、更平坦的困惑度曲线,而 PI 在超长距离上明显退化。)
论文还给出了扩展到 128k 后、10 篇 128k 长文档上的滑窗困惑度(窗口 256)数据:
| 模型 | 缩放 | 微调步数 | 8192 | 16384 | 32768 | 65536 | 131072 |
|---|---|---|---|---|---|---|---|
| Llama 2 7B | 4k × 16 | 400 | 3.51 | 2.99 | 2.65 | 2.42 | >101 |
| Llama 2 7B | 4k × 32 | 400+200 | 3.56 | 3.04 | 2.70 | 2.45 | 2.37 |
| Llama 2 13B | 4k × 16 | 400 | 3.25 | 2.79 | 2.50 | 2.29 | >101 |
| Llama 2 13B | 4k × 32 | 400+200 | 3.29 | 2.83 | 2.53 | 2.31 | 2.24 |
(数据来源:YaRN 论文 Table 1。注意 s=16 的模型只用 64k 上下文训练,在 128k 上困惑度直接崩到 >101;s=32 的模型虽然也只训练了 64k 数据,却成功外推到了 128k——这正是”低频频段外推 + 高频频段插值”设计的直接收益。)
Dynamic NTK 与动态缩放#
上面所有方法都有一个隐含假设:缩放因子 s 在整个推理过程中固定。但自回归解码中序列长度逐 token 增长,固定 s 意味着”短的序列也被按最终长度缩放”,浪费了短序列上本可保留的原始分辨率。
Dynamic NTK(emozilla, 2023,后来也被引入 YaRN)的做法是每个 forward pass 按当前序列长度动态重算缩放因子:s=max(1,l′/L),其中 l′ 是当前序列长度。序列从 1 长到 128k,缩放因子从 1 平滑涨到 32,每个瞬间模型看到的都是”恰好匹配当前长度”的频率配置。这是纯推理侧、零微调的实用方案——HuggingFace transformers 的 rope_scaling: {"type": "dynamic"}、llama.cpp 的 --rope-scaling 选项都内置支持,代价是序列长度跨过训练长度时需重算一次频率表(摊销后开销可忽略,因为频率表是预计算的)。
下图对比了不缩放(RoPE 直接外推)、Dynamic-PI、Dynamic-YaRN 在 Llama 2 长文档上的表现——不缩放的模型随距离增长迅速崩溃,动态方案则维持稳定:

(图源:YaRN 论文 Figure 8。横轴是文档中的 token 距离,纵轴是滑窗困惑度或相对困惑度变化;直接外推的 RoPE 在超出训练范围后困惑度急剧上升,两种动态方法保持平稳。)
生产模型里 RoPE 的配置一览#
把上面这些方法放到真实模型里对照,能清楚地看到各家怎么选:
- LLaMA 1 / LLaMA 2 / Mistral:base 10000,训练 4k;LLaMA 2 后续用 PI 微调出 32k 版本,Mistral 靠 4k 训练 + 滑窗注意力凑出 32k 的”有效窗口”。
- LLaMA 3:直接把 base 提高到 500000(官方实现的
precompute_freqs_cis),预训练 8k,再经长上下文微调支持 128k(Llama 3 Herd of Models)。base 500000 意味着低频维度波长长达 2π×5×105≈314 万 token——模型从一开始就”准备好”了远程分辨能力,这是新一代模型原生长上下文的秘诀之一。 - Qwen 系列:动态 NTK(zero-shot 外推,配合少量长文本微调)。
- DeepSeek-V3:4k 预训练长度 + YaRN 缩放到 128k(技术报告里明确采用的方案)。
在推理框架里,这些配置通过 rope_scaling 参数暴露:HuggingFace transformers 支持 {"type": "linear", "factor": 16.0}(PI)、{"type": "dynamic"}(动态 NTK)与 {"type": "yarn", "factor": ...};vLLM、SGLang、llama.cpp 都能直接读取这份配置并在 kernel 里生效。对推理而言,缩放因子只影响预计算的 cos/sin 表,不增加任何每 token 的额外开销——这也是 RoPE 系方案在工程上最大的优势:外推能力可以免费叠加在已有模型上。
小结#
把 RoPE 拆完,值得记住的四件事:
- 它是一次”求解”而非”设计”:写下”内积只依赖相对位置”的需求,复数运算自然逼出旋转矩阵这个答案。绝对位置的形式、相对位置的本质,是 LLaMA 系架构最优雅的细节之一。
- 几何级数频率负责一切:正交性保证数值稳定,保范性保证不扰动注意力尺度,多尺度频率天然给出远程衰减,也天然决定了”哪些维度能外推、哪些必须插值”。
- 工程上它几乎免费:O(d) 的逐元素乘加、预计算查表、融合进注意力 kernel,decode 阶段每个新 token 只多几次乘加。
- 长度外推是它的软肋,也是它的舞台:PI、NTK、YaRN、动态缩放构成了一套”从零微调到零微调”的完整谱系,直接决定了今天 128k、1M 上下文模型怎么来的。
RoPE 也是理解后续注意力优化的支点:GQA 里的 KV 头共享、MLA 里的低秩压缩与 RoPE 解耦、DeepSeek-V4 混合注意力 里的多模态 M-RoPE,全都建立在今天这套旋转机制之上。理解了旋转,你就拿到了整个现代 LLM 注意力体系的钥匙。
参考资料#
- RoFormer: Enhanced Transformer with Rotary Position Embedding(NeurIPS 2021 原始论文)
- 苏剑林:Transformer 升级之路 2——博采众长的旋转式位置编码(RoPE 原始推导博客)
- Extending Context Window of Large Language Models via Positional Interpolation(PI 论文)
- YaRN: Efficient Context Window Extension of Large Language Models(YaRN 论文)
- The Llama 3 Herd of Models(LLaMA 3 长上下文与 base 频率说明)
- meta-llama/llama3 官方实现(precompute_freqs_cis 与 theta=500000)
- HuggingFace Transformers: RoFormer 模型文档
- NTK-aware Scaled RoPE(bloc97 提出 NTK-aware 的原始讨论帖)
- Dynamically Scaled RoPE further increases performance of long context LLaMA with zero fine-tuning(emozilla 提出的动态 NTK 缩放方法)
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分内容可能已过时
评论区
分享你的想法,与大家交流讨论
音乐
暂未播放



