1. 为什么大模型更愿意用 RoPE,而不是原生正余弦位置编码
原生 sinusoidal 正余弦位置编码公式是:
$$ \begin{cases} PE_{pos,2i} = \sin\left(\frac{pos}{10000^{2i/d}}\right) \ PE_{pos,2i+1} = \cos\left(\frac{pos}{10000^{2i/d}}\right) \end{cases} $$
它不是完全没用,外推、相对距离表达、远程衰减这些能力都能沾上一点边。但问题也很明显:一到长文本,分布就开始飘,模型很难稳定地把这些性质用起来。RoPE之所以成了主流,靠的不是'看起来更高级',而是它把位置注入这件事做得更干净。
先看最核心的差别:
- sinusoidal PE 依赖位置向量之间的内积,位置关系是间接带出来的,能表达相对距离,但表达得比较弱,也不够直接;
- RoPE 直接对 $Q/K$ 做旋转,注意力分数里天然就带着相对位置 $m-n$,推导上更清楚,实际训练也更稳;
- sinusoidal PE 是把位置向量和词向量直接相加,语义和位置混在一起;
- RoPE 是正交旋转,不改模长,至少从几何上看,语义部分不会被硬塞进去的位置信息搅乱。
外推这块差得更明显。sinusoidal PE 在训练长度外常常不是'慢慢变差',而是分布直接跑偏;RoPE 的旋转规则在训练内外是一致的,至少从形式上,它是更像真外推,而不是靠运气蒙过去。
工程上也简单。没有额外可学习参数,计算量低,和标准自注意力完全兼容。大模型最后几乎都往这边靠,不是偶然。
2. RoPE 的 base 到底在控制什么
RoPE 的频率定义一般写成:
$$ \theta_i = base^{2i/d} $$
对应的旋转角是:
$$ \phi_{pos,i} = \frac{pos}{\theta_i} = \frac{pos}{base^{2i/d}} $$
默认 base=10000,它是 RoPE 里最关键的全局超参数。直白点说,它在管三件事:频率、周期和可区分长度。
base越大,角频率越低,同样的位置增长带来的旋转越慢;base越小,旋转越快,更容易在短范围里拉开差异,但也更容易相位缠绕;- 旋转周期变长,能区分的位置范围也会跟着变长。
这里的取舍其实很现实。大 base 对长文本更友好,但近邻位置的细粒度区分会弱一点;小 base 的局部辨识更强,却更早进入循环,长文就容易出问题。它不是'调大一定更好',而是在长程和局部之间换一个更合适的平衡点。
3. 为什么 RoPE 能从 2 维自然扩展到 n 维
RoPE 的扩展不是靠近似拼出来的,而是把高维空间拆成一组互相正交的 2 维平面,然后在每个平面里做同样的旋转。
任意偶数维空间都可以写成:
$$ \mathbb{R}^d = \mathbb{R}^2_{(0,1)} \oplus \mathbb{R}^2_{(2,3)} \oplus \dots \oplus \mathbb{R}^2_{(d-2,d-1)} $$
每一组两维互不干扰,分别旋转就行。整体上就是一个分块对角的正交矩阵:
$$ R_d = \begin{pmatrix} R_{\theta_0} & & & \ & R_{\theta_1} & & \ & & \ddots & \ & & & R_{\theta_{d/2-1}} \end{pmatrix} $$
所以它不是'从 2 维硬凑成 n 维',而是把 n 维拆开后,每块都按 2 维旋转来处理。这样做的好处很直接:模长保住了,正交性保住了,注意力里的几何关系也更干净。
4. Qwen 里的 GPT-J 和 GPT-NeoX 两种 RoPE 实现,等价吗
等价。这个地方很多人第一次看源码会觉得写法差很多,但本质上是同一件事。
理论上的 2 维旋转写法是:
$$ \begin{cases} x'{2i} = x{2i}\cos\phi - x_{2i+1}\sin\phi \ x'{2i+1} = x{2i}\sin\phi + x_{2i+1}\cos\phi \end{cases} $$
GPT-J 的实现更像是按矩阵去算,显式拿出 cos 和 sin,逐块变换;GPT-NeoX 更像复数乘法,把每个 2 维块看成一个复数,再乘上单位复指数。
写法不同,结果一样。只要角度和分组方式一致,输出元素就是逐项对齐的。实际差异更多来自工程实现:向量化怎么做、显存怎么省、CUDA kernel 怎么写。不是理论偏了。


