【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
标题
246、【AI】【模型部署】基座模型研究:RoPE 的维度与频率——32 对、多频率与 rope_theta
背景
上篇 blog
【AI】【模型部署】基座模型研究:RoPE 的数学——为什么旋转就能编码相对位置
把 RoPE 的核心恒等式推了出来:把一对维度看成平面上的点,位置m mm的查询q m = R ( m θ ) q q_m=R(m\theta)qqm=R(mθ)q、位置n nn的键k n = R ( n θ ) k k_n=R(n\theta)kkn=R(nθ)k,利用旋转矩阵的正交性(R ( θ ) ⊤ = R ( − θ ) R(\theta)^\top=R(-\theta)R(θ)⊤=R(−θ))与角度可加(R ( a ) R ( b ) = R ( a + b ) R(a)R(b)=R(a+b)R(a)R(b)=R(a+b)),得到
q m ⋅ k n = q ⊤ R ( ( n − m ) θ ) k q_m\cdot k_n=q^\top R\big((n-m)\theta\big)kqm⋅kn=q⊤R((n−m)θ)k
绝对位置被消掉,只剩相对差n − m n-mn−m。还提到真实head_dim是 64、被拆成 32 对分别旋转,各对自己的角度;只转q , k q,kq,k不转v vv;位置 0 时R ( 0 ) = I R(0)=IR(0)=I(等于没转)。
上篇的推导对任意一组频率都成立,但频率到底怎么取、为什么正好是 32 个、为什么低维转得快高维转得慢,还没有交代。本篇回答这几个问题:角度从哪来、inv_freq怎么算、多频率在做什么、以及rope_theta为什么取到 100 万。
模型部署
先把一个位置的"旋转角"说清楚。RoPE 给第i ii对维度、位置pos分配的旋转角是
angle i ( pos ) = pos × inv_freq [ i ] \text{angle}_i(\text{pos})=\text{pos}\times \text{inv\_freq}[i]anglei(pos)=pos×inv_freq[i]
角度与位置成正比,比例系数inv_freq [ i ] \text{inv\_freq}[i]inv_freq[i]就是这一对的"角速度"。32 对维度就是 32 个不同的角速度——这决定了它们的旋转节奏,也决定了模型能分辨多远的距离。本篇沿着"角速度从哪来 → 为什么有 32 个 → 为什么这样分布 → theta 起什么作用"这条线展开。
🧩先把"角度"记清楚
第i ii对维度在位置pos的旋转角,等于位置乘以这一对的角速度:angle i ( pos ) = pos × inv_freq [ i ] \text{angle}_i(\text{pos})=\text{pos}\times\text{inv\_freq}[i]anglei(pos)=pos×inv_freq[i]。把它换算成"转了几圈"更直观:圈数= pos × inv_freq [ i ] / ( 2 π ) =\text{pos}\times\text{inv\_freq}[i]/(2\pi)=pos×inv_freq[i]/(2π)。
- 圈数多⇒ 角速度大 ⇒ 位置稍动角度就差很多 ⇒ 擅长分辨近处;
- 圈数少⇒ 角速度小 ⇒ 很长范围内角度都接近 ⇒ 擅长标记远处。
于是问题归到inv_freq [ i ] \text{inv\_freq}[i]inv_freq[i]怎么定义——这正是下一节的内容。
🧩逆频率:inv_freq从哪来
inv_freq的计算式在227篇里见过(modeling_qwen2.py的compute_default_rope_parameters):
inv_freq [ i ] = 1 θ 2 i / d , i = 0 , 1 , … , d 2 − 1 \text{inv\_freq}[i]=\frac{1}{\theta^{\,2i/d}},\qquad i=0,1,\dots,\frac{d}{2}-1inv_freq[i]=θ2i/d1,i=0,1,…,2d−1
其中d dd是head_dim、θ \thetaθ是rope_theta。指数里出现2 i / d 2i/d2i/d,让相邻维度对按几何级数拉开:每往前一对,角速度就乘以一个固定比例θ − 2 / d \theta^{-2/d}θ−2/d。
以 Qwen2 的θ = 10 6 \theta=10^6θ=106、d = 64 d=64d=64为例,θ − 2 / 64 = ( 10 6 ) − 1 / 32 ≈ 0.6494 \theta^{-2/64}=(10^6)^{-1/32}\approx0.6494θ−2/64=(106)−1/32≈0.6494,于是前五个inv_freq是
[ 1.0000 , 0.6494 , 0.4217 , 0.2738 , 0.1778 ] [1.0000,\;0.6494,\;0.4217,\;0.2738,\;0.1778][1.0000,0.6494,0.4217,0.2738,0.1778]
每一对都是上一对的0.6494 0.64940.6494倍——正是227篇实测的那组数。长度是d / 2 = 32 d/2=32d/2=32,所以共有32 个频率。抽几对列出来,量程差别非常直观:
| 维度对i ii | inv_freq [ i ] \text{inv\_freq}[i]inv_freq[i] | 波长2 π / inv_freq 2\pi/\text{inv\_freq}2π/inv_freq(位置数) |
|---|---|---|
| 0 | 1.000 1.0001.000 | 6.3 6.36.3 |
| 1 | 0.649 0.6490.649 | 9.7 9.79.7 |
| 3 | 0.274 0.2740.274 | 23 2323 |
| 7 | 0.049 0.0490.049 | 129 129129 |
| 15 | 0.0015 0.00150.0015 | 4.1 × 10 3 4.1\times10^{3}4.1×103 |
| 31 | 1.6 × 10 − 6 1.6\times10^{-6}1.6×10−6 | 4.1 × 10 6 4.1\times10^{6}4.1×106 |
从第 1 对的"几个位置一个周期"到第 32 对的"几百万个位置一个周期",跨度跨越了六个数量级,却只用了 32 个维度。
🧩32 个频率:低维快、高维慢
把 32 个频率排在一起,节奏差别一眼可见:
图 1 画出三对维度的cos ( pos ⋅ inv_freq ) \cos(\text{pos}\cdot\text{inv\_freq})cos(pos⋅inv_freq)随位置的变化:第 1 对的角速度是1.0 1.01.0,转得最快(几乎每几个位置就一个周期);第 3 对是0.422 0.4220.422,慢一些;第 7 对只有0.075 0.0750.075,在一个很宽的位置范围内几乎是常数。低维转得快、高维转得慢,就像钟表的秒针、分针、时针。
换个角度看同一件事——逆频率与波长:
图 2 左格是inv_freq,随i ii几何级数衰减(很快趋近 0);右格是波长2 π / inv_freq 2\pi/\text{inv\_freq}2π/inv_freq,随i ii指数级增长。第 1 对的波长约2 π ≈ 6.28 2\pi\approx6.282π≈6.28个位置,而第 32 对的波长可以达到数百万个位置。一组频率 = 一把有粗细不同刻度的尺子。
🧩为什么是几何级数,而不是等差
指数里特意写成2 i / d 2i/d2i/d,为的是让相邻维度对按固定比例拉开(比例是θ − 2 / d \theta^{-2/d}θ−2/d,比如× 0.649 \times0.649×0.649)。这样做有两个好处:
- 用很少的维度覆盖很大的范围。从个位数到百万级,等差分布要么集中在一小段尺度、要么需要成百上千个维度;几何级数(也就是"对数刻度")用 32 个就把六个数量级铺满。
- 和感知距离的方式一致。近处需要精细分辨、远处只需粗略标记,刻度天然应该"近密远疏"。几何级数正好如此。
换句话说,几何级数不是巧合,而是"用有限维度尽可能广地覆盖尺度"的自然选择。
🧩为什么需要多把"尺子"
图 3 用多根快慢不同的指针来类比多频率:
- 快指针(低维):角速度大,位置刚变化一点点,角度就差很多——擅长分辨近距离,但转几圈就"回到原点",无法区分很远的距离;
- 慢指针(高维):角速度小,在很长范围内角度都接近——擅长标记远距离,但分辨不了相邻位置。
单靠一根指针都不行:快的会周期性混淆,慢的无法区分邻近。把它们合起来,位置的"角度组合"就同时具备细粒度和宽量程——这正是多频率做的事。前面(245)证明了各对维度独立旋转、点积按块相加,所以多频率不会互相干扰,只会让"相对距离"在多个尺度上都被编码。
🧩波长与"能分辨多远"
“波长"这个词还有个直接用途:一个频率能唯一确定位置的范围,只有它的波长那么长。一旦位置超过一个波长,角度就转回原点附近、发生周期性的"混淆”(aliasing)——就像钟表的分针转过一整圈后,再也分不清是几点几分。
由此可以读出一件事:整个模型能分辨多长的位置,取决于最慢的那一对维度。只要最慢的波长大于目标长度,所有位置就能在整个范围内保持唯一。以 Qwen2 为例,第 32 对的波长约4 × 10 6 4\times10^64×106个位置,远大于max_position_embeddings=32768——所以在这个上下文长度内,位置都能被干净地区分。这也预告了后面外推的话题:当序列长到接近甚至超过最慢波长时,位置就会开始混淆,需要专门处理。
🧩rope_theta:为什么是 100 万
θ \thetaθ出现在1 / θ 2 i / d 1/\theta^{2i/d}1/θ2i/d里,它决定频率衰减的快慢,从而缩放所有波长:
- θ \thetaθ越大,θ − 2 / d \theta^{-2/d}θ−2/d越接近 1、衰减越慢,高维的波长被拉得更长;
- θ \thetaθ越小,波长整体偏短,能分辨的位置范围更有限。
Qwen2 取θ = 10 6 \theta=10^6θ=106,而不少小模型取10 4 10^4104。差别在于量程:1 11万对应的最长波长量级远小于10 6 10^6106,能稳定区分的位置范围也就短得多。配合max_position_embeddings=32768,10 6 10^6106让高维那些"慢指针"在 3 万多个位置上都不会转完一圈,从而把这些位置区分开。把 theta 调大,是长上下文模型最常用的手段之一(更系统的外推技巧留到#249/#250两篇)。
🧩一个补充:partial rotary
并非所有模型都把整个head_dim转一遍。有些实现只旋转前一部分维度(partial rotary,如设置一个旋转比例),剩下的维度不做旋转、只承担内容。Qwen2 这里是把整个head_dim(32 对)都转。
两种做法各有取舍:全转时所有维度都携带位置信息、相对位置信号更强;部分转则给内容保留一些"与位置无关"的维度,也能省一点计算。理解它的前提仍是本篇这条主线——旋转的维度决定位置信号覆盖多少维,频率的分布决定能分辨多远。
需要强调的是,partial rotary 改变的是"哪些维度转",不改变"每对怎么转":被旋转的那些对,仍然按本篇的inv_freq公式各自取角。所以即使模型只转一半维度,本篇关于频率与波长的结论对它照样成立。
🧩四个常见疑问
- “为什么低频(慢)的维度反而对应高维?”:这是
inv_freq公式1 / θ 2 i / d 1/\theta^{2i/d}1/θ2i/d定的——i ii越大、指数越大、频率越小、波长越长。约定俗成把它放在靠后的维度,本身没有对错,换一种排列模型照样能学。 - “32 个频率够用吗,为什么不更多?”:频率个数受
head_dim限制——每两个维度才能组成一对,64 维最多 32 对。想增加尺度,只能加宽head_dim,或者用 partial rotary 之外的频率缩放技巧。 - “波长和 theta 有什么关系?”:波长是2 π θ 2 i / d 2\pi\,\theta^{2i/d}2πθ2i/d,哪个频率的波长都随θ \thetaθ一起放大。调大
rope_theta相当于把整把尺子等比拉长,让所有"指针"都转得更慢、量程更宽。 - “位置 0 要不要特殊处理?”:不用。位置 0 时所有对的角度都是0 00,R ( 0 ) = I R(0)=IR(0)=I,等于没转——这也解释了
227篇实测的"位置 0 的 cos 全是 1"。
📌一句话记忆
RoPE 给第i ii对维度的角速度是inv_freq [ i ] = 1 / θ 2 i / d \text{inv\_freq}[i]=1/\theta^{2i/d}inv_freq[i]=1/θ2i/d,i ii从 0 到d / 2 − 1 d/2-1d/2−1,所以
head_dim=64有 32 个频率;它们按几何级数排开,低维转得快(分辨近处)、高维转得慢(覆盖远处),合起来像一把刻度粗细不同的尺子;rope_theta越大、波长越长、可区分的位置越多,Qwen2 取10 6 10^6106正是为了长上下文。下一篇从数学转到工程:旋转到底发生在前向还是反向,训练和推理各是什么样,以及它和 KV cache 怎么配合。
OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog
【AI】【模型部署】基座模型研究:RoPE 用在前向还是反向——训练、推理与 KV cache