RoPE 旋转位置编码:用相对位置旋转注入位置信息
什么是位置编码
Transformer 模型的自注意力机制本身不具备序列顺序的感知能力。为了让模型理解 token 的先后顺序,必须在输入向量中加入位置信息。一种常见做法是向词嵌入向量中叠加一个位置编码向量。早期的绝对位置编码(如正弦位置编码、可学习位置编码)将每个位置的编码独立定义,但它们在处理相对位置关系时比较生硬,且难以外推到训练时未见过的序列长度。
旋转位置编码(Rotary Position Embedding,RoPE)采用了一种完全不同的思路:通过旋转来注入位置信息。它不增加额外的可学习参数,而是利用复数或二维向量的旋转变换,让注意力计算自然地具备相对位置感知能力。这使得 RoPE 兼具高效性、理论上的优雅性,已广泛应用于 LLaMA、GLM、ChatGLM 等主流大语言模型。
核心思想:用旋紧相对位置的信息
RoPE 的精髓可以凝练成一句话:通过绝对位置编码的方式,实现相对位置编码的效果。
具体来说,对于每个 token 的查询向量 ( \mathbf{q} ) 和键向量 ( \mathbf{k} ),RoPE 会根据它们各自的位置 ( m ) 和 ( n ),分别施加一个旋转变换。经过旋转后的向量进行内积时,内积结果仅依赖于位置差 ( m - n ),而与具体的绝对位置无关。这样,注意力分数就天然包含了相对位置关系,而模型架构本身无需改动,计算过程依然高效。
数学原理:从二维旋转到高维扩展
RoPE 的实现可以分解为以下几个关键步骤。
基础:二维向量的旋转
在二维空间中,将一个向量 ( \mathbf{x} = (x_1, x_2) ) 逆时针旋转 ( \theta ) 角度,得到的新向量为:
[ \begin{pmatrix} x_1' \ x_2' \end{pmatrix} = \begin{pmatrix} \cos\theta & -\sin\theta \ \sin\theta & \cos\theta \end{pmatrix} \begin{pmatrix} x_1 \ x_2 \end{pmatrix} ]
如果我们把位置信息融入旋转角度,比如让位置 ( m ) 对应的旋转角度为 ( m\theta ),那么旋转矩阵就变为:
[ R_m = \begin{pmatrix} \cos(m\theta) & -\sin(m\theta) \ \sin(m\theta) & \cos(m\theta) \end{pmatrix} ]
对于另一个位置 ( n ) 上的向量,用 ( R_n ) 旋转。当计算旋转后的两个向量的内积时,根据三角恒等式可得:
[ (R_m \mathbf{u})^\top (R_n \mathbf{v}) = \mathbf{u}^\top R_{m-n} \mathbf{v} ]
与位置相关的部分只由差值 ( m-n ) 决定。这正是 RoPE 赋予注意力机制相对位置感知的数学基础。
扩展到高维:分块处理
实际使用的隐藏维度远大于 2,RoPE 会将 ( d )- 维的向量两两分组,形成 ( d/2 ) 个二维子空间。每个子空间使用不同的旋转频率。具体来说,对于维度索引 ( i \in {0, 1, \dots, d/2 - 1} ),定义对应的旋转角速度:
[ \theta_i = 10000^{-2i/d} ]
这与 Transformer 原始正弦位置编码的频率设定一致,确保了不同维度有不同的变化周期。
给定位置 ( m ),对向量 ( \mathbf{x} \in \mathbb{R}^d ) 应用 RoPE 的公式为:
[ \text{RoPE}(\mathbf{x}, m) = \begin{pmatrix} x_0 \cos(m\theta_0) - x_1 \sin(m\theta_0) \ x_1 \cos(m\theta_0) + x_0 \sin(m\theta_0) \ x_2 \cos(m\theta_1) - x_3 \sin(m\theta_1) \ x_3 \cos(m\theta_1) + x_2 \sin(m\theta_1) \ \vdots \ x_{d-2} \cos(m\theta_{d/2-1}) - x_{d-1} \sin(m\theta_{d/2-1}) \ x_{d-1} \cos(m\theta_{d/2-1}) + x_{d-2} \sin(m\theta_{d/2-1}) \end{pmatrix} ]
实际操作中,我们会预先计算好每个位置对应的 ( \cos ) 和 ( \sin ) 值,然后将向量分段相乘、相加即可。整个运算是对元素成对施加的,极其高效。
融入注意力计算
在 Transformer 的每一层,对计算出的查询 ( Q ) 和键 ( K ) 分别按各自位置施加 RoPE。值向量 ( V ) 通常不施加位置编码(仅需相对位置体现在注意力权重中)。注意力得分计算如下:
[ \text{Attention}(Q, K, V) = \text{softmax}\left( \frac{ \text{RoPE}(Q) \cdot \text{RoPE}(K)^\top }{\sqrt{d_k}} \right) V ]
因为 RoPE 保持了内积的相对位置性质,注意力矩阵天然包含距离信息。具体地,位置 ( m ) 对位置 ( n ) 的注意力得分会隐式地依赖 ( m-n ),模型能够轻易学到“邻近 token 更相关”等局部性先验。
为什么 RoPE 优于传统方案
与最常用的正弦位置编码和可学习位置编码相比,RoPE 展现出多项优势。
天然捕捉相对位置
绝对位置编码是将位置向量直接加到词向量上,这样两个位置的相互作用通过训练才能模糊捕获相对距离,且效果不够稳定。RoPE 将相对位置直接写入内积计算中,使模型对序列中 token 之间的先后顺序、距离远近有了更结构化的感知。
长度外推能力
可学习位置编码受限于最大训练长度,超出后必须插值或重新训练。RoPE 由于频率设定与正弦编码类似,天然具有向更长度差泛化的潜力。通过调整频率基值或使用位置插值(如 NTK-aware 缩放),可以将上下文窗口轻松扩展数倍,而无需完全重新训练。
计算效率与实现简洁
RoPE 不需要引入额外参数,也不改变矩阵乘法的核心形状。大部分框架(如 PyTorch、JAX)仅需约 10 行核心代码即可实现。因为它在注意力计算前直接修改 ( Q ) 和 ( K ),可以无缝接入已有的 Transformer 代码库。
代码实现示例(PyTorch)
下面给出一个简洁且功能齐全的 RoPE 实现。
import torch
import torch.nn as nn
class RotaryPositionalEmbedding(nn.Module):
def __init__(self, dim, max_seq_len=2048, base=10000.0):
super().__init__()
self.dim = dim
self.base = base
# 预先计算频率 theta_i
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
self.register_buffer("inv_freq", inv_freq)
# 可选:预缓存 cos, sin 表以加速
self.max_seq_len = max_seq_len
self._build_cache(max_seq_len)
def _build_cache(self, max_len):
seq = torch.arange(max_len, device=self.inv_freq.device)
freqs = torch.einsum("i,j->ij", seq, self.inv_freq) # (max_len, dim/2)
emb = torch.cat((freqs, freqs), dim=-1) # (max_len, dim)
self.register_buffer("cos_cached", emb.cos())
self.register_buffer("sin_cached", emb.sin())
def forward(self, x, seq_len=None):
# x: tensor of shape (batch, n_heads, seq_len, head_dim)
seq_len = x.shape[2] if seq_len is None else seq_len
cos = self.cos_cached[:seq_len, :].unsqueeze(0).unsqueeze(0) # (1,1,seq_len,dim)
sin = self.sin_cached[:seq_len, :].unsqueeze(0).unsqueeze(0)
# 将 x 分成两半以施加旋转
x_rot = x[..., : self.dim // 2]
x_pass = x[..., self.dim // 2 :]
# 传统 RoPE 是成对旋转,这里采用“交替”模式简化:
x1, x2 = x[..., 0::2], x[..., 1::2]
rotated = torch.cat((-x2, x1), dim=-1)
# 最终输出
return (x * cos) + (rotated * sin)
注意:实际高性能实现多采用直接两两分组运算,以上代码为思路示意。更高效的写法可以参见 HuggingFace Transformers 或 FlashAttention 中的融合算子。
常见变体与技巧
随着大模型规模的扩大,RoPE 也在实践中衍生出一些改进策略。
- 位置插值(Position Interpolation):简单地将位置索引缩放,例如将原长度 ( L ) 的模型扩展到 ( 2L ),只需将位置索引除以 2。这几乎不损失性能,即可实现上下文窗口倍增。
- NTK-aware 插值:调整 RoPE 的基频
base,使得高频细节更少被压缩,保留下游任务的长距离依赖。 - YaRN(Yet another RoPE extensioN):结合温度缩放和频率域调整,进一步改善外推时的困惑度。
- 线性注意力中的 RoPE:对于线性注意力机制,RoPE 也可被改造为无 softmax 的形式,保持相对位置编码的特性。
总结
RoPE 旋转位置编码以简洁的数学变换,实现了高性能的相对位置编码。它的核心在于“用绝对位置施加旋转,注意力内积自然依赖相对位置”。由于其出色的性能、外推潜力和实现便利性,RoPE 已经成为当代大语言模型位置编码的事实标准。理解并掌握 RoPE,是深入 Transformer 模型设计与训练的重要一步。