难度
进阶
从“模型为什么需要顺序感”讲到绝对位置、相对位置与 RoPE,建立长上下文位置建模的统一直觉。
难度
进阶
阅读时长
约 110 分钟
更新日期
2026/03/24
主题
位置编码 / RoPE / 长上下文 / Transformer
这篇教程希望帮你真正搞清下面几件事:
如果你已经看过 Attention Is All You Need 但对位置编码仍然“看过公式、没成直觉”,这篇会比较适合接着读。
注意力机制的核心是:
Attention(Q, K, V) = softmax(QK^T / sqrt(d_k))V
这个公式只关心 token 之间的相似度和交互强度,但它本身并没有告诉模型:
如果我们把一个句子的 token 顺序完全打乱,只保留同样的一组 token 向量,那么纯注意力机制并不会自动意识到顺序变化。这对语言理解是灾难性的,因为:
所以位置编码不是可选增强,而是 Transformer 进入自然语言任务的必要条件。
最早的 Transformer 论文采用的是绝对位置编码。它的思路很简单:
模型看到的输入就变成:
token_embedding + position_embedding
这样一来,即使两个 token 内容相同,只要位置不同,最终输入向量也会不同。
《Attention Is All You Need》没有直接用可学习位置向量,而是用了固定的正弦/余弦函数:
PE(pos, 2i) = sin(pos / 10000^(2i / d_model))
PE(pos, 2i + 1) = cos(pos / 10000^(2i / d_model))
可以不用死背公式,更值得理解的是它的设计动机:
于是,每个位置会得到一个由多种频率叠加形成的独特“相位签名”。
正余弦位置编码之所以经典,有三个原因:
第三点尤其重要。虽然它表面上是绝对位置编码,但由于正余弦的组合性质,模型有机会从中恢复某些相对位置信息。
这也是为什么它会成为早期 Transformer 的默认方案。
绝对位置编码很好理解,但它有几个长期问题:
于是社区很快开始转向相对位置编码。
相对位置编码的核心思想是:
对于注意力来说,很多时候更重要的是 token A 和 token B 之间相差几步,而不是它们各自的绝对编号。
例如:
因此,相对位置编码通常会把“位置差”直接注入注意力分数或注意力表示中。这样模型可以更自然地学习:
绝对位置的一大问题,是不同长度序列会导致“编号语义”发生变化。相对位置则更稳定:
这使得相对位置建模在长上下文和长度外推问题上更自然。很多现代长序列方法,都会在某种程度上引入相对位置思想。
RoPE(Rotary Positional Embedding)之所以流行,是因为它把位置建模写成了一个非常优雅的几何形式:
换句话说,位置不再是“贴一张标签”,而是“让向量随位置改变相位”。
这件事的妙处在于,相对位置关系会自然地体现在 query 和 key 的内积里。也就是说,RoPE 不是额外给注意力塞一个位置偏置,而是把位置关系融进了注意力计算本身。
假设我们把向量按两维一组拆开。每一组都可以看成二维平面上的一个点。RoPE 做的事情就是:
于是,同一个 token 表示在不同位置上,会对应不同相位。两个位置之间的相对距离,最终会反映在旋转角度差里。
这就是为什么很多人说 RoPE 有很强的几何美感:它把“位置关系”翻译成了“相位关系”。
RoPE 流行的核心原因,不只是它优雅,而是它在现代 LLM 中特别实用:
这也是为什么 LLaMA、许多开源大模型以及大量长上下文工作都选择或继承了 RoPE。
很多入门介绍会给人一种印象:用了 RoPE,长上下文问题就差不多解决了。其实并没有这么简单。RoPE 仍然可能遇到:
这也是为什么后来出现了:
所以正确理解是:RoPE 是现代长上下文建模的重要基础,但不是终点。
你不一定要完整推导 RoPE,但可以记住它的核心效果:
因此,RoPE 的“魔法”并不是多了一个额外输入,而是让原本注意力的匹配函数本身具备了位置敏感性。
位置编码不是只影响论文 benchmark,它也直接影响真实系统:
这也是为什么 Lost in the Middle 这类论文虽然主要做现象评测,背后却总会牵扯到位置编码与注意力设计。
下面这段代码不是完整 RoPE 实现,但足以帮你建立“位置 = 旋转”的直觉:
import math
def rotate(x1, x2, theta):
y1 = x1 * math.cos(theta) - x2 * math.sin(theta)
y2 = x1 * math.sin(theta) + x2 * math.cos(theta)
return y1, y2
vector = (1.0, 0.0)
for pos in [0, 1, 2, 3]:
theta = pos * 0.5
print(pos, rotate(vector[0], vector[1], theta))
如果你把不同位置看成不同旋转角度,就能更容易理解为什么“相对角度差”会变成模型可利用的位置信号。
真正关键的是:这个序号如何进入注意力计算,以及它是否能表达相对关系。
很多现代方法其实处在两者之间,或者把相对思想嵌进绝对表示形式里。
RoPE 更适合长序列,但长度外推依然需要额外设计和验证。
位置编码不仅影响论文分数,也影响 RAG 命中利用率、代码补全距离感知和多轮对话稳定性。
先看原理,再到模拟器里调参验证,学习效果更稳定。
从相近主题继续深入,建立连续学习链路。