跳转至

位置编码:从绝对编码到 RoPE 到 NoPE,外推与内插

更新日期:2026-04-14

本文目标:覆盖所有主流位置编码方案,理解每种方案的数学原理、外推能力、工程实现,能根据需求选型和配置。

七、NoPE(No Positional Encoding)

7.1 核心观察

Causal Attention 的 mask 本身就隐含了位置信息:

  • 位置 0 只能看到 [0]
  • 位置 1 能看到 [0, 1]
  • 位置 2 能看到 [0, 1, 2]
  • ……

每个位置的"可见集合"不同 → 模型可以隐式推断位置。

7.2 NoPE 的优缺点

7.3 RoPE + NoPE 混合(2025 新方向)

论文 "RoPE to NoPE and Back Again" 提出:不同层用不同编码

class HybridAttentionModel:
    def __init__(self, n_layers):
        self.layers = []
        for i in range(n_layers):
            if i % 4 == 0:
                # 每 4 层放一个 NoPE 层 → 提供长距离外推能力
                self.layers.append(AttentionLayer(use_rope=False))
            else:
                # 其余层用 RoPE → 精确的位置感知
                self.layers.append(AttentionLayer(use_rope=True))
    # 效果: 兼得 RoPE 的精度和 NoPE 的外推能力

八、2D-RoPE(多模态位置编码)

8.1 用于 Vision Transformer

图像的 patch 排列在 2D 网格上,需要二维位置编码。

def apply_2d_rope(x, height, width, theta=10000.0):
    d = x.shape[-1]
    half_d = d // 2

    # 前半维度: 编码行位置 (y)
    freqs_y = 1.0 / (theta  (arange(0, half_d, 2) / half_d))
    # 后半维度: 编码列位置 (x)
    freqs_x = 1.0 / (theta  (arange(0, half_d, 2) / half_d))

    # 每个 patch 的 (row, col) 坐标
    rows = arange(height).unsqueeze(1).expand(height, width).flatten()
    cols = arange(width).unsqueeze(0).expand(height, width).flatten()

    # 分别编码行和列
    angles_y = rows.unsqueeze(-1) * freqs_y
    angles_x = cols.unsqueeze(-1) * freqs_x

    # 拼接: 前半维度旋转 y, 后半维度旋转 x
    x[:, :half_d] = rotate(x[:, :half_d], angles_y)
    x[:, half_d:] = rotate(x[:, half_d:], angles_x)
    return x
    # Qwen2-VL 用这种方式 → 自然支持任意分辨率图像

九、CoPE(Contextual Position Encoding)

Meta 2024 提出,位置不是固定的,而是依赖内容计算的

def cope_attention(Q, K, V):
    # 传统: 位置是固定的整数 0, 1, 2, ...
    # CoPE: 位置由内容决定
    # 思路: 用 gate 网络计算每个 token 的"有效距离"

    gates = sigmoid(gate_proj(K))  # [B, S, 1] ∈ (0, 1)
    # gates ≈ 0: 这个 token "不占位"(如标点、停用词)
    # gates ≈ 1: 这个 token "占一个位"(如实词、关键信息)

    # 累积位置 = gate 的前缀和
    positions = cumsum(gates, dim=1)  # [B, S, 1]
    # 不再是均匀的 0,1,2,3,...
    # 而是类似 0, 0.1, 1.1, 1.2, 2.2, ...

    # 用连续位置做 RoPE 或其他编码
    Q = apply_rope(Q, positions)
    K = apply_rope(K, positions)
    ...

优势:位置由语义决定,而非固定间隔。对代码(大量空格/缩进)和多语言(不同语言的 token 密度不同)更合理。


十、DoPE(Denoising RoPE,2025)

观察到 RoPE 在长序列中引入"位置噪声"→ DoPE 通过去噪改善。


十一、位置编码对 LLM 的影响

11.1 对训练的影响

11.2 对推理的影响

11.3 对 MLA 的特殊影响

MLA 中 RoPE 不能走压缩路径

为什么? 
设压缩操作为 C = W_down @ x (线性)
设旋转操作为 R(pos) (依赖位置)

如果先压缩后旋转: R(pos) @ W_down @ x 
如果先旋转后压缩: W_down @ R(pos) @ x

这两者不相等! 因为 R(pos) 和 W_down 不交换。
所以 RoPE 部分必须走单独的通道,绕过压缩。
这就是 MLA 中有 d_rope 额外维度的原因。

十二、追问延伸

问题 方向
训练时 theta 怎么选? 经验:基础模型用 10000,需要长上下文训练加大到 500K-1M
2D-RoPE 怎么处理视频(时间维度)? 加第三个维度做 3D-RoPE,或 temporal + spatial 分开
不同层用不同 theta 可以吗? 可以,DeepSeek 研究过,但收益有限
RoPE 和 Flash Attention 的兼容性? 完全兼容:在 FlashAttention kernel 内部应用 RoPE

参考链接


上级 · A4. 位置编码:RoPE 原理、扩展方法、2D-RoPE