跳转至

相对位置编码

相对位置编码的核心思想是什么?与绝对位置编码的本质区别在哪?

核心思想:相对位置编码不再告诉模型每个 token 的绝对位置(第1个、第2个...),而是告诉模型任意两个 token 之间的相对距离。在自注意力计算中,模型只需知道“我与你相距多远”,而无需知道“我在第几排”。这使得注意力权重的计算不再依赖全局位置索引,而是依赖于 token 之间的相对偏移。其本质是将位置信息融入注意力分数的计算中,而非输入表示中。

本质区别:

  • 绝对位置编码:为每个绝对位置分配一个唯一的向量,加在输入嵌入上。模型在每一层都要从这些被污染过的向量中分离出位置和语义。它能区分绝对位置,但难以泛化到训练时未见过的长度。

  • 相对位置编码:不在输入中注入位置信息,而是在每一层的注意力分数矩阵中,根据两个 token 的相对距离添加偏置项或直接调整注意力计算方式。它天然捕捉相对关系,并具有更好的长度外推能力,因为相对距离的范围可定义、可泛化。

请描述 Transformer-XL 中相对位置编码的设计,它如何在注意力分数中融入相对位置?

Transformer-XL 提出了一种基于相对位置的注意力机制,旨在解决长文本建模和片段间上下文复用问题。其注意力分数计算公式为:

image.png

设计巧妙之处:

  • 分离内容和位置:将注意力分数分解为四项,分别代表内容之间的交互、内容与相对位置的交互、以及两个全局偏置。这使得模型明确知道每个 token 对之间的位置关系,而不依赖于绝对位置。

  • 用正弦编码表示相对位置:直接复用正弦位置编码函数,输入变为相对距离 i−j,网络可以学习到相对距离与相关性之间的关系。

  • 循环机制结合:配合片段级别的循环(缓存前一片段的隐藏状态),相对位置编码使得跨片段 token 的注意力也能有效计算。

Transformer-XL 的这种相对位置编码设计,是后续许多相对位置编码(包括 T5、ALiBi 的前身思路)的重要基础。

ALiBi(Attention with Linear Biases)是如何实现相对位置偏置的?它的公式是什么?

ALiBi 的做法非常简单直接:在 softmax 之前,给注意力分数加上一个与距离成线性关系的负偏置项。具体公式为:

image.png

image.png

特点:

  • 无额外参数:不需要学习位置嵌入,不需要修改网络结构,只是在注意力矩阵上加一个静态的、基于距离的惩罚。

  • 线性偏置:距离越远,惩罚越大(分数减去越多),导致注意力倾向于关注邻近 token。

  • 高效:计算简单,内存占用几乎为零。

ALiBi 为什么能实现长度外推?其线性偏置的背后直觉是什么?

长度外推:ALiBi 的偏置仅仅依赖于相对距离 ∣i−j∣,与绝对位置索引无关。即使在推理时序列变得极长,相对距离的偏置模式仍然适用,模型已经学会了“距离每增加1,注意力分数应降低 mm”这一规则。因此,模型在长序列上可以平滑外推,不会因为遇到未见过的绝对位置而失效。

背后直觉:

  • 局部性偏好:自然语言具有强局部依赖——相邻的词往往最相关。ALiBi 通过线性递减的偏置直接编码了这种局部性先验,让模型无需从数据中学习就能获得“离得近就更重要”的归纳偏置。

  • 避免注意力分散:在非常长的序列中,softmax 的归一化会使得大量无关 token 也分走少量注意力权重,导致“注意力稀释”。线性偏置将远距离 token 的分数压低,从而集中注意力在邻近区域,保护了局部建模能力。

  • 距离与内容解耦:偏置不参与内容交互,使得模型在学习文本内容依赖时不会与位置纠缠,更容易泛化。

因此,ALiBi 通过在注意力分数上施加一个随距离衰减的“重力”,使得模型天然具有处理长文的能力。

RoPE(旋转位置编码)的数学原理是什么?如何通过旋转矩阵将位置信息融入 Q 和 K?

RoPE 的核心思想是:通过旋转矩阵对查询和键向量进行位置相关的旋转变换,使得它们的内积天然携带着相对位置信息。

数学原理: 对于维度为 dd 的向量,将其两两分组,每组维度2,看作一个复数或二维向量。位置 mm 的旋转操作定义为对角块矩阵 Rm,每个 2x2 块为旋转矩阵:

image.png

实际施加时,对于查询向量 q 和键向量 k,先将其转换为多个二维子向量的形式,然后分别按位置 m 旋转:

image.png

即内积的结果仅依赖于相对位置 n−mnm,而不依赖于各自的绝对位置 m,nm,n。RoPE 巧妙地将绝对位置嵌入到旋转操作中,却实现了相对位置编码的效果。

RoPE 如何自然地体现相对位置?请用复数形式推导其相对位置衰减特性。

image.png

image.png

并且有一个根据频率 θθ 振荡的衰减因子。对于高频维度(大 θθ),相对位置变化会引起剧烈震荡,适宜捕捉局部精细位置;低频维度变化缓慢,适合长程关系。这种特性使得 RoPE 能够以连续、平滑的方式融入相对位置信息,同时保留了通过训练学习得到的各维度重要性。

因此,RoPE 通过复数旋转变换,优雅地将绝对位置转化为相对位置表示,在数学上非常自然。

为什么 RoPE 成为当前主流大模型(LLaMA, Qwen)的首选?相比 ALiBi 有哪些优势?

RoPE 的优势:

  • 灵活性与表达能力:RoPE 可学习(虽然旋转矩阵本身固定,但查询和键向量是可训练的,模型可以适应旋转操作),在不同维度上具有不同频率,形成多尺度的位置感知,既捕捉局部细节也捕捉长程依赖。ALiBi 是硬编码的线性偏置,没有可调参数,对复杂位置模式的建模能力较弱。

  • 长文本外推性能:通过调整基频(如从 10000 提高到 500000 或 1000000),RoPE 可以显著扩展支持的长度,而不需要重新训练。ALiBi 虽然也能外推,但其线性偏置在超长距离下可能导致注意力过度集中于极近 token,忽视中等距离的有效信息。

  • 兼容性:RoPE 完美融入标准自注意力计算,只需在 Q、K 上施加旋转,计算开销增加极小。与 FlashAttention 等高效注意力实现兼容(需将旋转变换融入 kernel)。

  • 相对位置与绝对位置兼顾:RoPE 通过旋转隐式实现相对位置,同时绝对位置也可以从旋转后的向量中被部分保留(若需要),提供了更丰富的信号。

  • 经验效果:在多个基准上,RoPE 表现出优于 ALiBi 的泛化能力和下游任务性能,已成为大模型的事实标准。

RoPE 中每个维度的旋转频率是如何设定的?通常高频和低频维度的作用分别是什么?

频率设定与原始正弦位置编码相同:对于维度索引 ii(从 0 到 d/2−1d/2−1),旋转频率为 θi=10000−2i/dθi=10000−2i/d。因此,低索引组(前几个二维子空间)频率高,高索引组频率低。

  • 高频组(小 i):旋转角度变化快,对位置变化极其敏感,两个位置的向量内积随距离剧烈振荡。它们负责编码局部精确位置,有助于模型关注相邻 token 的精细关系,例如词法、短语搭配。

  • 低频组(大 i):旋转角度变化极慢,在较长距离上向量内积保持稳定。它们负责编码长程位置趋势,对于篇章级连贯性、远距离指代消解很重要。

这种多频率设计形成了一个类似“位置尺度的谱”,允许模型根据自己的需要学习利用不同频率的位置信息。

在 RoPE 中,通常只对 Q 和 K 施加旋转,不对 V 施加,这是为什么?

  • 数学目的:RoPE 的设计目标是让注意力分数 QKTQKT 体现相对位置。因此只旋转 QQ 和 KK 就足够了,因为只有它们的点积决定注意力权重。

  • 保持内容完整性:值 VV 承载的是实际信息内容,在加权求和后传递给下一层。如果对 VV 也旋转,可能会污染内容表示,引入位置相关的扭曲,不利于序列的逐层抽象。

  • 实际验证:实验表明,只旋转 QQ 和 KK 效果最好,旋转 VV 反而可能降低性能。

因此,RoPE 专注在注意力分数中注入位置,保护了值流的信息纯度。

如果 RoPE 只作用在部分维度(如仅前半部分),会有什么影响?有没有这种做法?

如果只对部分维度施加 RoPE,比如前 50% 维度旋转,后 50% 维度保持原样:

  • 部分相对位置感知:旋转部分能够捕捉相对位置,未旋转部分则完全不携带位置信息,仅依赖内容。这相当于给模型提供了两种信息通道:位置敏感通道和位置不变通道。

  • 可能提升泛化:一些研究表明,这种部分旋转可以在保持长文本外推能力的同时,让模型在短文本上避免过拟合位置信息。它类似于混合了绝对位置(未旋转部分可视为位置无关)和相对位置。

  • 实际做法:LLaMA 等模型通常对所有维度施加 RoPE,但社区有尝试部分 RoPE(如只对高频组旋转)以优化外推性能。例如,通过仅旋转高频维度,让低频维度作为绝对位置参考,有助于超长文本的稳定性。

比较 ALiBi 和 RoPE 在长文本外推时的表现和局限性。

ALiBi:

  • 外推能力强,因为线性偏置与绝对位置无关,长度增加时只要相对距离仍在偏置的作用范围内(偏置随距离线性增长),模型可以自然适应。但线性偏置严格强调局部,可能导致在需要长程依赖时注意力无法有效聚焦远距离 token,即“近视”。

  • 局限性:单一的线性衰减难以建模复杂的非单调位置关系;没有可学习的参数来根据任务调整位置偏好。

RoPE:

  • 通过调整基频(如增大 10000→500000),可以拉伸频率尺度,使低频维度在更长距离下仍保持单调变化,从而支持长度外推。RoPE 的多频率特性能同时保留局部和全局位置信息。

  • 局限性:直接外推到极长(如 32k→128k)时,如果不微调,仍可能因训练时未见过的旋转模式而出现性能下降。虽然可通过修改基频缓解,但极端情况下需要少量长文本微调。

总结:ALiBi 实现极简,但灵活性受限;RoPE 更强大、灵活,成为主流。

相对位置编码是否能完全替代绝对位置编码?实践中通常如何结合?

在一些架构中,相对位置编码完全取代了绝对位置编码(如 Transformer-XL、T5、ALiBi、RoPE 的典型用法)。但绝对位置在某些任务中仍有帮助,如需要知道句子起始、终结符位置等。因此,实践中可以有多种结合方式:

  • 同时使用:在输入嵌入中保留可学习的绝对位置嵌入,同时在注意力中使用相对位置偏置。这提供了最丰富的位置信息,但增加了参数。

  • 仅保留必要绝对位置:只对特殊标记(如 [CLS], [SEP])使用绝对位置,其余使用相对位置。

  • RoPE 的变体:RoPE 本身虽然基于旋转,但可以通过不旋转某些维度或使用可学习的绝对位置偏置来兼得。

现代大模型趋势是仅使用相对位置编码(尤其是 RoPE),因为其极佳的长度外推和灵活性,绝对位置编码已非必需。

在多头注意力中应用相对位置编码,每个头是否可以有不同的相对位置偏置?有什么好处?

可以。在 ALiBi 中,不同头就有不同的斜率。在 RoPE 中,虽然旋转频率统一,但每个头的 Q、K 投影矩阵不同,使得同一相对距离在不同头中可能产生不同的内积效果,隐式实现了头特异的位置模式。显式地为每个头设置不同的相对位置偏置(可学习或静态)能让不同头关注不同距离范围的依赖。例如,某些头专注于极近距离(局部语法),某些头关注中等距离(短语结构),某些头捕捉远距离(篇章主题)。这种分工提升了模型的整体表示能力。

T5 中使用的是什么样的位置偏置?它是如何简化的相对位置编码?

T5 使用简化的相对位置偏置。它不是为每个距离对学习一个偏置,而是将相对距离分桶(bucketing),将距离值映射到有限的几个桶中,每个桶对应一个可学习的标量偏置。计算注意力时,根据 query 和 key 的相对距离查表得到标量,加到注意力 logits 上。具体地,相对距离 ∣i−j∣∣ij∣ 通过一个对数分桶函数分配桶索引。这使得偏置参数量仅与桶数有关(如 32),极大减少了参数量,同时保留了捕捉不同距离范围重要性的能力。它相比 Transformer-XL 的位置编码更轻量。

为什么相对位置编码比绝对位置编码更难做 KV 缓存优化?推理时会遇到什么问题?

在推理(自回归生成)时,KV 缓存用于存储历史 token 的 Key 和 Value,避免重复计算。相对位置编码需要为每个新 token 计算与历史 token 的相对位置,这通常需要动态修改注意力分数。如果简单地使用 KV 缓存,会引入问题:

  • 位置漂移:绝对位置编码下,KV 向量一旦缓存就固定,因为绝对位置不变。相对位置编码下,历史 token 与新 token 的相对位置随生成动态变化,这意味着每个生成步骤都需要重新计算相对位置偏置或调整 Key 表示。直接缓存 Key 并重用,可能无法正确捕捉动态的相对距离。

  • 额外计算:某些实现需要在每一步重新计算部分位置偏置,或重新注入位置信息到 KV 中,这会部分抵消 KV 缓存带来的加速。

以 RoPE 为例,由于 Key 向量在生成时也需要根据其绝对位置旋转,如果仅缓存旋转后的 Key,随着序列增长,绝对位置编码会不断向后移动,但实际上每个 token 的 Key 旋转是基于它生成时的绝对位置,之后不变。标准做法是:在生成每个 token 时,对当前 token 的 Q 用当前绝对位置旋转,对历史 Key 使用它们在生成时的绝对位置旋转(已经存储在缓存中)。这样,相对位置自然通过内积体现,无需修改缓存。因此,RoPE 与 KV 缓存是兼容的,FlashAttention 也能高效实现,但需要正确地处理旋转操作,对于某些自制推理框架可能带来开发复杂度。

RoPE 在使用 FlashAttention 时是否兼容?FlashAttention 如何支持 RoPE?

兼容。FlashAttention 是一种高效精确注意力算法,通过分块计算避免大矩阵写入。要支持 RoPE,需要在每个分块内对 Q 和 K 的块执行旋转操作。通常的做法是:在加载 Q、K 块到 SRAM 后,直接在线应用旋转(根据位置生成旋转矩阵或复数乘法),然后再计算点积。由于旋转是逐元素、可并行的操作,完全可以融入 FlashAttention 的分块流水线中,几乎不增加额外显存访问。当前许多推理框架(如 FlashAttention-2、xformers)已原生支持 RoPE。

请手写一个简化版的 RoPE 实现,输入 Q, K 和位置,返回旋转后的 Q, K。

假设 PyTorch 实现,Q 和 K 形状为 [batch, seq_len, num_heads, head_dim],位置 pos 形状 [seq_len]

import torch

def rotate_half(x):
    """将最后维度的后半部分取负并与前半部分拼接,实现复数乘法中的虚部操作"""
    x1 = x[..., : x.shape[-1] // 2]
    x2 = x[..., x.shape[-1] // 2 :]
    return torch.cat((-x2, x1), dim=-1)

def apply_rotary_pos_emb(q, k, pos, dim=128):
    """
    q, k: [batch, seq_len, heads, dim]
    pos: [seq_len]
    """
    # 生成频率
    freqs = 1.0 / (10000 ** (torch.arange(0, dim, 2).float() / dim))  # [dim/2]
    freqs = pos.unsqueeze(-1) * freqs.unsqueeze(0)  # [seq_len, dim/2]
    emb = torch.cat((freqs, freqs), dim=-1)  # [seq_len, dim]
    cos_cached = emb.cos().unsqueeze(0).unsqueeze(2)  # [1, seq_len, 1, dim]
    sin_cached = emb.sin().unsqueeze(0).unsqueeze(2)

    q_out = (q * cos_cached) + (rotate_half(q) * sin_cached)
    k_out = (k * cos_cached) + (rotate_half(k) * sin_cached)
    return q_out, k_out

这段代码实现了将频率嵌入与查询/键向量按元素相乘后与旋转后的另一半相加,本质上完成了复数乘法。

在 Decoder-only 模型中,RoPE 是如何处理生成时的位置递增的?推理时如何进行位置编码的更新?

在自回归解码时,每次生成一个新 token,序列长度 +1。RoPE 需要为每个 token 应用对应的绝对位置旋转。推理时的 KV 缓存处理:

  • 当生成第一个 token(位置 0)时,将其 Q、K 用位置 0 的旋转矩阵旋转,然后缓存 K、V。

  • 当生成位置 t 的新 token 时,用当前 token 的 Q 用位置 t 旋转,同时用当前位置 t 旋转新 token 的 K。然后将这个新 K、V 追加到 KV 缓存中。

  • 由于缓存中的历史 K 保留了各自生成时的绝对位置旋转信息,它们与新 Q 的内积会自动实现相对位置效应,无需任何修改。因此,推理时只需正常执行旋转并追加缓存,无需额外更新历史缓存。

这样,RoPE 完美适配增量解码。

相对位置编码中“相对距离”是否需要截断?截断窗口如何选择?

可以截断,也可以不截断。截断的好处是减少参数数量(对于可学习的相对位置偏置)或限制注意力范围。常见的做法是设置一个最大相对距离 k,超出该距离的位置偏置统一设为截断值。窗口大小选择一般依据训练数据中的典型依赖长度和模型容量,如 T5 将距离分桶时设置对数桶,远距离归入同一桶。对于 RoPE 这类连续编码,通常不截断。

分析一下相对位置编码带来的额外计算开销,与绝对位置编码相比如何?

  • 绝对位置编码:开销极小,仅需将向量加在输入嵌入上,一次即可,后续每一层无需额外计算。

  • 相对位置编码:每一层注意力计算时都需要引入位置偏置或调整 Q、K,带来额外计算:

  • RoPE:需对 Q、K 进行逐元素旋转,复杂度 O(L⋅d),相比注意力 O(L2d)可忽略。
  • 可学习偏置:T5 式查表偏置,每对 token 需要一次查表,也轻量。
  • Transformer-XL 式:需要额外的矩阵乘法和偏置项,开销较大但仍可控。 总体而言,相对位置编码的额外计算开销非常小,现代模型(LLaMA等)的大量实验已证明其性价比极高,是绝对位置编码的优秀替代。