☰ inferserver 的数学基础

8. RoPE:旋转位置编码为什么是相对的

8.1 二维旋转矩阵从哪来

把平面向量 \((a,b)\) 逆时针旋转 \(\theta\),希望变换是线性的、保持长度、保持夹角。设矩阵

\[ R(\theta)=\begin{pmatrix}c&-s\\ s&c\end{pmatrix},\qquad c=\cos\theta,\ s=\sin\theta . \]

它把基向量 \((1,0)\) 送到 \((\cos\theta,\sin\theta)\),把 \((0,1)\) 送到 \((-\sin\theta,\cos\theta)\),正好是旋转后的两个基。验证保长度:

\[ R^\top R=\begin{pmatrix}c&s\\-s&c\end{pmatrix}\begin{pmatrix}c&-s\\s&c\end{pmatrix} =\begin{pmatrix}c^2+s^2&0\\0&c^2+s^2\end{pmatrix}=I . \]

所以 \(\|R x\|^2=x^\top R^\top R x=x^\top x=\|x\|^2\)。用复数写更短:\((a+ib)e^{i\theta}=(a\cos\theta-b\sin\theta)+i(a\sin\theta+b\cos\theta)\),实部虚部正好是上面两行。

8.2 位置变成旋转角度

对第 \(m\) 个位置、第 \(i\) 对维度,定义角频率 \(\omega_i\),旋转角为 \(m\omega_i\):

\[ q'_m=R(m\omega_i)q,\qquad k'_n=R(n\omega_i)k . \]

关键性质:内积只依赖相对位置 \(n-m\)。证明只用 \(R(a)^\top=R(-a)\) 和 \(R(a)R(b)=R(a+b)\):

\[ \langle q'_m,k'_n\rangle =q^\top R(m\omega)^\top R(n\omega)k =q^\top R(-m\omega)R(n\omega)k =q^\top R((n-m)\omega)k . \]

右边只出现 \(n-m\)。绝对位置 \(m,n\) 被消掉了,剩下的是“这两个 token 隔多远”。对多头、多频率,点积是若干对这样的项之和,每一项都只依赖 \(n-m\),所以整体也只依赖相对位置。

8.3 频率怎么选:几何级数

head_dim \(d=128\) 有 64 对维度。第 \(i\) 对(\(i=0,\dots,63\))的频率取

\[ \omega_i=\theta_{\text{base}}^{-2i/d},\qquad \theta_{\text{base}}=10^6 . \]

低维(\(i\) 小)频率高,转得快;高维频率低,转得慢。对应的波长 \(\lambda_i=2\pi/\omega_i=2\pi\,\theta_{\text{base}}^{2i/d}\):

\(i\)\(\omega_i\)波长 \(\lambda_i\)(token)直观
016.28每 6 个位置转一圈
16\(10^{-1.5}\approx0.0316\)199百级
32\(10^{-3}=0.001\)6283千级
63\(10^{-5.906}\approx1.24\times10^{-6}\)\(5.06\times10^6\)在 40960 上下文里几乎不转

高低频搭配:低频维度负责“远距离的粗定位”,高频维度负责“近距离的细分辨”。\(\theta_{\text{base}}=10^6\) 让最慢的维度在常见上下文长度内几乎不变,相当于给模型留了长距离的“全局坐标”。

8.4 HF 的 rotate_half 就是上面的旋转

代码里不是两两配对 \((2i,2i+1)\),而是把 head_dim 从中间劈开,配对 \((i,i+d/2)\)。HF 的写法:

\[ \operatorname{rotate\_half}(x)=\begin{pmatrix}-x_{\text{hi}}\\ x_{\text{lo}}\end{pmatrix}, \qquad \text{out}=x\cos+\operatorname{rotate\_half}(x)\sin . \]

把 \(x=(x_{\text{lo}},x_{\text{hi}})\) 代进去,逐分量:

\[ \text{out}_{\text{lo}}=x_{\text{lo}}\cos-x_{\text{hi}}\sin,\qquad \text{out}_{\text{hi}}=x_{\text{hi}}\cos+x_{\text{lo}}\sin . \]

这正是 \(R(\theta)\) 作用在 \((x_{\text{lo}},x_{\text{hi}})\) 上的结果。所以“劈半配对”和“相邻配对”只是索引组织不同,数学上是同一组二维旋转;但必须和权重训练时的约定一致,否则数值全错。

另一个等价性:HF 把频率拼成 \(\operatorname{cat}(\text{freqs},\text{freqs})\),所以 \(\cos[i]=\cos[i+d/2]\),和上面的写法一致。

8.5 应用位置

RoPE 只作用在 Q 和 K 上,不作用在 V 上:分数来自 Q·K,位置信息通过旋转进入“匹配”;V 是内容,旋转它只会把内容也搅乱,没有好处。并且增量解码时只旋转新 token,历史 K 已经在 cache 里是旋转过的,重复旋转会叠加角度。

最隐蔽的坑

增量解码传的必须是绝对位置(全局第几个 token),不是“本次数组的下标”。传错后模型仍然能生成看似通顺的文字,只是数值和参考实现对不上,极难排查。

对应代码:model/rope.go 的 ApplyRoPE;model/attn.go 里 Q/K 各调用一次,V 不调用。