位置编码的困境

Transformer的自注意力机制本身是排列不变的——它不知道"猫追老鼠"和"老鼠追猫"的区别。位置编码是赋予模型位置感知能力的关键组件。

从绝对位置编码(正弦编码、可学习编码)到相对位置编码(T5 Bias、ALiBi),位置编码经历了多代演进。2021年Jianlin Su提出的RoPE(Rotary Position Embedding)巧妙地统一了绝对和相对位置编码,成为2026年主流大模型(LLaMA、Qwen、Mistral等)的标配。

RoPE的核心数学

基本思想

RoPE的核心思想是:通过对Query和Key施加旋转操作,使得它们的内积自然地编码相对位置信息

给定位置 m 的Query向量 q 和位置 n 的Key向量 k,RoPE希望满足:

<ROPE(q, m), ROPE(k, n)> = g(q, k, m-n)

即旋转后的内积只依赖于相对位置 m-n。

二维情况

在二维空间中,旋转操作有明确的几何意义。对向量 (q₀, q₁) 施加角度为 mθ 的旋转:

┌ q₀' ┐   ┌ cos(mθ)  -sin(mθ) ┐ ┌ q₀ ┐
│      │ = │                     │ │    │
└ q₁' ┘   └ sin(mθ)   cos(mθ) ┘ └ q₁ ┘

旋转后 Query 和 Key 的内积自然只依赖于角度差 (m-n)θ,即相对位置。

高维推广

将 d 维向量分成 d/2 组二维子空间,每组施加不同频率的旋转:

def apply_rope(x, positions, freqs):
    """
    x: [batch, seq_len, n_heads, d_head]
    positions: [seq_len]
    freqs: [d_head // 2]
    """
    angles = positions[:, None] * freqs[None, :]  # [seq_len, d_head//2]
    cos = torch.cos(angles).float()
    sin = torch.sin(angles).float()
    
    # 扩展到完整维度
    cos = torch.cat([cos, cos], dim=-1)  # [seq_len, d_head]
    sin = torch.cat([sin, sin], dim=-1)
    
    # 将x的偶数位和奇数位视为二维子空间
    x1 = x[..., 0::2]  # 偶数位
    x2 = x[..., 1::2]  # 奇数位
    
    rotated = torch.stack([
        x1 * cos[..., 0::2] - x2 * sin[..., 0::2],
        x1 * sin[..., 1::2] + x2 * cos[..., 1::2]
    ], dim=-1).flatten(-2)
    
    return rotated

更高效的实现通常使用复数运算:

def apply_rope_complex(x, positions, freqs):
    """使用复数乘法实现RoPE"""
    x_complex = torch.view_as_complex(x.reshape(*x.shape[:-1], -1, 2))
    angles = positions[:, None] * freqs[None, :]
    rotation = torch.polar(torch.ones_like(angles), angles)
    return torch.view_as_real(x_complex * rotation).flatten(-2)

频率的选择

RoPE的频率通常使用几何级数:

θ_i = 10000^{-2i/d}, i = 0, 1, ..., d/2-1

高频分量(小i)捕捉局部位置关系,低频分量(大i)捕捉长距离关系。这类似于多尺度分析的思想。

长度外推:RoPE的最大挑战

原始RoPE在训练长度之外的性能急剧下降。如果模型在4K长度上训练,在8K上推理时注意力分数会出现严重偏差。这被称为长度外推问题

Position Interpolation (PI)

Meta在2023年提出的简单方法:将推理位置线性缩放到训练范围内:

m' = m * (L_train / L_infer)

例如,在8K位置推理时,将位置压缩到4K范围。这种方法简单但会损失局部位置的精度。

NTK-aware Scaling

NTK(Neural Tangent Kernel)感知缩放不均匀地缩放频率:

θ_i' = θ_i * (base_scale)^{-2i/(d-2)}

高频分量几乎不缩放(保持局部精度),低频分量大幅缩放(扩展长距离范围)。这种方法比PI在长度外推上表现更好。

YaRN(Yet another RoPE extensioN)

YaRN结合了NTK-aware缩放和注意力温度因子调整,是目前最先进的RoPE外推方法之一:

def yarn_rope(positions, base_freq=10000, scale_factor=4.0, 
              original_len=4096, target_len=16384):
    # NTK-aware频率缩放
    freqs = 1.0 / (base_freq ** (torch.arange(0, dim, 2).float() / dim))
    
    # 根据波长决定缩放策略
    wavelengths = 2 * math.pi / freqs
    scale = torch.where(
        wavelengths < original_len,
        1.0,  # 短波长不缩放
        scale_factor  # 长波长线性缩放
    )
    freqs = freqs / scale
    return positions[:, None] * freqs[None, :]

2026年的RoPE改进

Dynamic NTK

在推理时根据当前序列长度动态调整缩放因子,而非使用固定的目标长度。这使得模型可以在任意长度上平滑过渡:

def dynamic_ntk_scaling(seq_len, max_train_len, base=10000):
    if seq_len <= max_train_len:
        return base
    scale = (seq_len / max_train_len) - 1
    return base * (scale ** (dim / (dim - 2)))

LogN-Scaling

微软提出的方法,根据注意力距离动态调整RoPE的缩放,使得远距离的注意力分数不会过度衰减。在长文档理解任务上表现优异。

3D-RoPE

将RoPE扩展到三维,分别编码序列位置、层级位置和类别位置。这在多轮对话和文档理解场景中特别有用——对话轮次和段落结构都能被位置编码捕捉。

与其他位置编码的对比

特性SinusoidalLearned PET5 Relative BiasALiBiRoPE
绝对/相对绝对绝对相对相对统一
可外推部分需改进
参数量0O(Ld)O(Lh)O(h)0
长上下文好(改进后)

RoPE的优势在于零额外参数、统一的绝对/相对编码、以及丰富的外推改进方案。

工程实现注意事项

  1. 精度问题:RoPE计算中的三角函数在FP16下可能损失精度,建议在FP32下计算旋转
  2. 缓存友好性:旋转操作改变了K的值,因此KV Cache中存储的是旋转后的K
  3. 与Flash Attention的兼容:现代Flash Attention实现已经原生支持RoPE融合
  4. 分布式训练:不同GPU上的位置需要全局协调,确保频率参数一致

结语

RoPE以其优雅的数学形式和出色的工程性能,成为大模型位置编码的事实标准。长度外推问题虽未完全解决,但PI、NTK、YaRN等方法已使模型能够处理数十万token的上下文。随着AGI对长上下文需求的持续增长,RoPE的演进仍在继续。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。