RoPE (Rotary Position Embedding) 不是把“位置向量”加到输入上,而是把位置 p 映射成一个旋转矩阵,作用到每个位置的 Q 和 K 上;这样注意力分数
(RoPE(qm,m))⊤(RoPE(kn,n))
会自然只依赖于相对位置差 n−m,从而把相对位置信息直接注入到 self-attention 中。
1. 先回顾 Transformer 注意力公式
单头自注意力里,给定输入 X∈RL×dmodel:
Q=XWQ,K=XWK,V=XWV
其中 Q,K,V∈RL×dh(单头时)。
第 m 个位置和第 n 个位置的注意力打分是:
sm,n=dhqm⊤kn
softmax 后得到权重:
am,n=∑j=1Lexp(sm,j)exp(sm,n)
输出:
om=n=1∑Lam,nvn
问题在于:如果不加位置编码,qm⊤kn 只反映“内容相似性”,不反映“位置关系”。
2. RoPE 的基本想法:对 Q 和 K 做“位置相关旋转”
2.1 从二维向量的旋转开始
对二维向量 x=(x1,x2)⊤,旋转角 θ 的矩阵是:
R(θ)=[cosθsinθ−sinθcosθ]
旋转后:
R(θ)x=[x1cosθ−x2sinθx1sinθ+x2cosθ]
RoPE 的做法是:
对于位置 p,不用“加一个位置向量”,而是对向量做一个与 p 相关的旋转。
3. 关键公式:为什么它能得到“相对位置”?
设第 m 个位置的 query 是 qm,第 n 个位置的 key 是 kn。
对它们分别做位置旋转:
q~m=R(mθ)qm,k~n=R(nθ)kn
则它们的内积为:
q~m⊤k~n=(R(mθ)qm)⊤(R(nθ)kn)
利用矩阵性质 (Ax)⊤y=x⊤A⊤y,有:
q~m⊤k~n=qm⊤R(mθ)⊤R(nθ)kn
又因为旋转矩阵满足:
R(α)⊤=R(−α),R(α)⊤R(β)=R(β−α)
所以:
q~m⊤k~n=qm⊤R((n−m)θ)kn
这一步是 RoPE 的核心。
它说明:注意力分数不再只看内容 qm,kn,还会显式依赖于相对位移 n−m,而不是绝对位置 m,n 分别是多少。
4. 推广到高维:把向量按两维一组分块旋转
Transformer 的每个 head 维度一般是 dh,通常为偶数。
RoPE 将 dh 维向量分成 dh/2 个二维子空间:
x=[x0,x1,x2,x3,…,xdh−2,xdh−1]
分成:
(x0,x1), (x2,x3), …, (xdh−2,xdh−1)
对于第 i 个二维对子,使用一个不同的频率 ωi。
在位置 p 上,该对子旋转角度为:
θp,i=p⋅ωi
所以第 i 对的旋转矩阵是:
R(θp,i)=[cos(pωi)sin(pωi)−sin(pωi)cos(pωi)]
5. RoPE 的完整分量公式
设某个向量 x∈Rdh,位置为 p。
RoPE 作用后的结果记为 RoPE(x,p)。
对每个 i=0,1,…,2dh−1,有:
x2i′x2i+1′=x2icos(pωi)−x2i+1sin(pωi),=x2isin(pωi)+x2i+1cos(pωi).
于是:
RoPE(x,p)=[x0′,x1′,…,xdh−1′]
频率 ωi 的常见定义
通常设:
ωi=θi=10000−dh2i
所以旋转角度是:
pθi=p⋅10000−dh2i
这和经典 sinusoidal PE 的频率设计是一致的:
低维对子频率高,高维对子频率低,形成多尺度位置表示。
6. 用块对角矩阵写得更紧凑
把整个 dh 维的 RoPE 写成一个块对角矩阵:
Rp=diag(R(pω0),R(pω1),…,R(pω2dh−1))
则:
RoPE(x,p)=Rpx
对于 query/key:
q~m=Rmqm,k~n=Rnkn
注意力打分变成:
sm,n=dhq~m⊤k~n=dhqm⊤Rm⊤Rnkn
由于每个二维块都满足旋转矩阵的差角性质,因此:
Rm⊤Rn=Rn−m
于是:
sm,n=dhqm⊤Rn−mkn
这说明整个高维版本同样只依赖于相对位置差 n−m。
7. 用复数表示会更优雅
把每一对维度 (x2i,x2i+1) 看作一个复数:
zi=x2i+jx2i+1
旋转就等价于乘上一个单位复数:
zi′=zi⋅ejpωi
因为:
ejpωi=cos(pωi)+jsin(pωi)
这和二维旋转完全等价。
对于 query 和 key:
z~m,i(q)=zm,i(q)ejmωi,z~n,i(k)=zn,i(k)ejnωi
在复数内积的意义下,会出现因子:
ej(n−m)ωi
所以相对位置差自然出来了。
8. RoPE 如何与 Transformer 融合?
8.1 插入位置
RoPE 不是加在输入 embedding 上,而是插在 线性投影后的 Q、K 上:
- 输入隐状态 X
- 线性映射:
Q=XWQ,K=XWK,V=XWV
- 对每个位置 p:
Q~p=RoPE(Qp,p),K~p=RoPE(Kp,p)
- 用 Q~,K~,V 做注意力:
Attention(Q,K,V)=softmax(dhQ~K~⊤)V
注意:通常只对 Q,K 做 RoPE,不对 V 做。
因为位置信息主要需要进入“匹配分数” QK⊤。
9. 多头注意力里的公式
多头注意力中,每个 head 有自己的 Q(h),K(h),V(h),维度为 dh。
RoPE 对每个 head 独立应用:
Q~p(h)=RpQp(h),K~p(h)=RpKp(h)
然后第 h 个头的注意力:
head(h)=softmax(dhQ~(h)(K~(h))⊤)V(h)
最后拼接各头结果并线性映射:
MHA(X)=Concat(head(1),…,head(H))WO
10. 为什么它比“直接加位置向量”更自然?
对比两种方式:
10.1 绝对位置编码(加法)
把位置向量 pm 加到输入上:
xm′=xm+pm
然后经过线性层变成 qm,km。
这里位置信息是“混入内容表示里”的。
10.2 RoPE(乘法/旋转)
直接在 Q,K 上做:
q~m=Rmqm,k~n=Rnkn
此时内积变成:
q~m⊤k~n=qm⊤Rn−mkn
可以看到,相对位置差直接进入了 attention kernel,这是 RoPE 更“贴近注意力结构”的地方。
11. 一个小的二维展开示例
假设只有一个二维对子,query 和 key 分别是:
q=[q1q2],k=[k1k2]
位置分别为 m,n,频率为 ω。
则:
q~=R(mω)q,k~=R(nω)k
内积:
q~⊤k~=q⊤R((n−m)ω)k
将 R((n−m)ω) 展开:
R(Δω)=[cos(Δω)sin(Δω)−sin(Δω)cos(Δω)],Δ=n−m
于是:
q~⊤k~=q1(k1cosΔω−k2sinΔω)+q2(k1sinΔω+k2cosΔω)
整理得:
q~⊤k~=(q1k1+q2k2)cos(Δω)+(q2k1−q1k2)sin(Δω)
这个公式很有意思,它表明注意力分数中会出现:
- 一个乘 cos(Δω) 的项
- 一个乘 sin(Δω) 的项
所以分数会随着相对位置 Δ=n−m 发生规律变化。
12. 实际实现公式:常见的“rotate half”写法
在工程实现里,通常不显式构造大矩阵 Rp,而是直接按偶/奇位做操作。
设:
xeven=[x0,x2,…],xodd=[x1,x3,…]
定义一个“旋转半维”的操作:
rotate(x)=[−x1,x0,−x3,x2,…]
那么 RoPE 可以简写为:
RoPE(x,p)=x⊙cosp+rotate(x)⊙sinp
其中 cosp,sinp 是按维度广播后的余弦/正弦表。
更严格地写,对每个二维对子 (x2i,x2i+1):
[x2i′x2i+1′]=[x2ix2i+1]cos(pωi)+[−x2i+1x2i]sin(pωi)
这个形式和前面完全等价。
13. 训练与推理时的使用
训练时
对整段序列每个位置 p=0,1,…,L−1 预先算好:
cos(pωi),sin(pωi)
然后对所有 token 的 Q,K 施加 RoPE。
自回归推理 + KV Cache
假设当前解码到位置 t:
- 历史位置 0,…,t−1 的 K,V 已缓存
- 新 token 只需生成自己的 qt,kt,vt
然后应用:
q~t=Rtqt,k~t=Rtkt
缓存 k~t,vt,并让 q~t 和所有历史 K~ 做注意力即可。
因为每个位置的旋转是独立确定的,所以与 KV cache 天然兼容。
14. RoPE 为什么有一定长度外推能力?
因为它的位置函数不是查表,而是连续函数:
cos(pωi),sin(pωi)
理论上 p 可以取任意大,不需要新增参数。
但注意:可计算 ≠ 一定泛化得好。
当 p 很大时,某些高频分量会快速振荡,可能导致模型在超长上下文下性能下降。因此后续有很多 RoPE scaling、NTK-aware scaling、YaRN、LongRoPE 等改进。
15. 最后给一个“最标准”的总公式
如果你想在脑子里只记一个公式,可以记这个:
定义位置旋转矩阵
Rp=diag(R(pω0),R(pω1),…,R(pωdh/2−1))
作用到 Query / Key
q~m=Rmqm,k~n=Rnkn
注意力分数
sm,n=dhq~m⊤k~n=dhqm⊤Rn−mkn
这个最后等式就是 RoPE 的核心价值:
绝对位置旋转,得到相对位置感知的注意力。