上一篇聊到,Transformer 本身并不天然感知顺序,所以需要额外注入位置信息。绝对位置编码直接告诉模型“当前 token 在第几位”,相对位置编码则更关注“两个 token 相隔多远”。
这两种思路各有问题。绝对位置编码简单,但它把位置直接加到 token embedding 上,位置和内容混在一起;相对位置编码更符合 attention 的结构,但通常要额外改 attention score,形式上没那么干净。
RoPE(Rotary Position Embedding)提供了一个很漂亮的折中:它用绝对位置来定义旋转角度,但在 query 和 key 做内积之后,真正起作用的是相对位置。
从二维旋转说起
先看一个二维向量:
x=[x1x2]
如果把它旋转角度 θ,可以写成:
Rθx=[cosθsinθ−sinθcosθ][x1x2]
也就是:
Rθx=[x1cosθ−x2sinθx1sinθ+x2cosθ]
RoPE 的基本想法很直接:不同位置的 token,用不同角度去旋转它的 query 和 key。
假设第 m 个 token 的 query 是 qm,key 是 km,那么 RoPE 会做:
qm′=Rmqm
km′=Rmkm
其中 Rm 是由位置 m 决定的旋转矩阵。
这里要注意一点:RoPE 不是把一个位置向量加到 token embedding 上,而是在 attention 里面,对 query 和 key 动手。这也是它和普通绝对位置编码很不一样的地方。
高维向量怎么旋转
真实模型里的 query 和 key 当然不是二维的,而是高维向量。RoPE 的处理方式是:把向量的维度两两分组,每两个维度当成一个二维平面,然后在每个二维平面里做旋转。
比如:
x=[x1,x2,x3,x4,…]
可以分成:
(x1,x2),(x3,x4),…
每一组都旋转,但旋转速度不同。
对第 i 组维度,位置 m 对应的角度通常写成:
mθi
其中:
θi=10000−2i/d
所以位置越靠后,旋转角度越大;不同维度组的 θi 不同,相当于有不同频率的“指针”。
如此,对不同维度使用不同频率进行旋转,RoPE将位置信息注入了向量。
RoPE 中的相对位置计算
RoPE 最关键的地方在 query 和 key 的点积。
位置 m 的 query 旋转后是:
qm′=Rmqm
位置 n 的 key 旋转后是:
kn′=Rnkn
attention 里计算的是:
(qm′)⊤kn′
代入进去:
(qm′)⊤kn′=(Rmqm)⊤(Rnkn)
由于旋转矩阵满足:
Rm⊤Rn=Rn−m
所以:
(Rmqm)⊤(Rnkn)=qm⊤Rn−mkn
一开始我们明明用了两个绝对位置 m 和 n,但点积之后,位置项变成了 n−m。也就是说,attention score 里真正出现的是两个 token 的相对距离。这就是 RoPE 最巧妙的地方:用绝对位置做旋转,最后在 attention 里留下相对位置。
正如开篇所说, RoPE 是绝对位置和相对位置之间的折中。但更准确地说,它不是简单把二者拼起来,而是通过旋转矩阵把绝对位置转化成了相对位置信息。
从复数角度理解
RoPE 还有一个更简洁的理解方式:把每两个维度看成一个复数。
比如二维向量:
(x1,x2)
可以看成:
z=x1+ix2
二维旋转角度 θ,等价于乘上:
eiθ
那么位置 m 上的旋转就是:
zm′=zmeimθ
query 在位置 m,key 在位置 n,它们分别带上相位:
eimθ,einθ
做相似度计算时,两个相位相减,留下:
ei(n−m)θ
也还是相对位置 n−m。
这个视角下,RoPE 像是在给每个 token 的 query 和 key 挂上一个“相位”。位置越靠后,相位转得越多。不同维度转速不同,于是模型就获得了一组多尺度的位置参考。
RoPE 的优与劣
自回归模型预测下一个 token 时,光知道"我现在是第几个"基本没用,真正有用的是当前 token 和前面某个 token 的关系——上一个词是什么、那个左括号在哪里、这个变量之前在哪定义过、这句话呼应的是前文哪一段。而RoPE 把这种相对关系直接塞进了 attention 里。
除此之外它还有几个好处。一是省掉了一张要学的位置 embedding 表,比 learned absolute positional embedding 更容易往训练长度之外推。二是实现简单,对 query 和 key 做个旋转就完事,不用给每个相对距离另外存参数。三是它和 causal attention 配合得很顺——反正自回归只看左边,RoPE 表达"当前位置离历史位置多远"刚好对口。
所以现在很多大模型里都用它。
但 RoPE 的底子是三角函数,三角函数有周期:
sin(θ+2π)=sinθ
cos(θ+2π)=cosθ
转得够久,指针会绕回原地。
之前说 RoPE 像一组指针。沿着这个比喻,时间足够长,指针总要回来。
短序列里这不是问题,因为 RoPE 不是单一频率,而是多个频率叠在一起转。一个维度绕回来了,别的维度未必同步绕回来,整体上还能区分。
长上下文里就麻烦了。
训练时模型可能只见过 4K、8K 的长度,推理时你扔给它 32K、128K,位置 m 变得很大,旋转角度 mθi 跑到了训练时根本没出现过的区域。两件事会同时发生:模型没见过这些相位的组合,attention 分布开始飘;远处的位置在某些频率上又恰好和近处对上了,发生位置混叠。
所以 RoPE 能给任意长度"算出"一个位置编码,不代表模型真的能在那个长度上正常工作。
算得出来是一回事,泛化得过去是另一回事。
小结
RoPE 不是“完美的位置编码”,而是一种很优雅、很实用、但也有边界的方法。它将绝对位置和相对距离连接了起来,同时依旧面对着长上下文的难题。
下一篇可以顺着这个问题继续往下看。文本里,RoPE 的长距离问题可能表现为上下文利用变差;到了视频生成里,这个问题会更直观——模型可能不是简单忘掉前文,而是在生成很长时间后突然“回到开头”。