模型怎么知道词的顺序?位置编码的难题
“猫追狗”和”狗追猫”意思完全不同。但对于 self-attention 来说,这两个句子一模一样。位置编码就是为了解决这个荒谬的问题。
一个你可能没注意到的缺陷
上一篇我们兴高采烈地介绍了 self-attention:每个词和所有其他词算关注度,汇总信息。并行、高效、能捕捉长距离关系。
但等等——self-attention 的计算过程中,有考虑词的位置吗?
答案是:没有。
回想 attention 的打分方式:Query 和 Key 做内积。Query 来自”这个词”,Key 来自”那个词”。内积只关心两个向量的内容——和它们分别在句子的第几个位置完全无关。
这意味着如果你把句子打乱——”追猫狗”——self-attention 算出的结果和”猫追狗”一模一样(假设词向量相同)。
这显然是不行的。语言高度依赖顺序:”张三打了李四”和”李四打了张三”意思截然不同。
第一个解法:正弦波 (2017)
原始 Transformer 的解决方案相当巧妙:给每个位置生成一个独特的”位置信号”,直接加到词向量上。
想象一下:每个词本来有一个代表”我是什么词”的向量。现在给每个位置额外加一个”我在第几位”的向量。模型拿到的是两者之和——既知道这个位置是什么词,也知道它在哪里。
怎么生成这个位置向量?用正弦和余弦函数。不同频率的正弦波组合,可以为每个位置生成唯一的”指纹”。就像给每个座位号编了一个独特的条形码。
这个方法有几个好处:
- 不需要学习——数学公式固定的
- 理论上可以编码任意长度的序列
- 不同位置之间的关系可以通过线性变换表达
但也有问题:实际上模型很难从这种加法编码中精确地”解码”出位置信息。而且当序列长度超过训练时见过的长度时,效果就崩了。
一个更自然的思路:相对位置
人类理解语言时,我们很少在意”这个词在第 47 个位置”。我们关心的是词与词之间的距离:”这个形容词紧挨着那个名词”,”这个代词距离它的指代对象有 3 个词”。
既然如此,为什么不直接让模型学习”相对位置”——两个词之间的距离——而非绝对位置呢?
这就是 2018-2019 年一系列工作的方向(Relative Position Encoding、T5 的 relative bias 等)。但它们实现起来比较笨重,打破了 self-attention 优雅的矩阵乘法形式,导致速度变慢。
RoPE:用旋转编码位置 (2021)
2021 年,苏剑林提出了一个极其优雅的方案——旋转位置编码(Rotary Position Embedding, RoPE)。
核心直觉是这样的:
想象一个时钟的指针。12 点方向、3 点方向、6 点方向——不同位置对应不同的角度。现在,如果我把一个向量”旋转”一个与位置成正比的角度,那两个向量做内积时,结果就自然取决于它们角度差——也就是位置差。
具体来说:位置 $m$ 的向量旋转 $m\theta$ 度,位置 $n$ 的向量旋转 $n\theta$ 度。它们的内积只取决于 $(m-n)\theta$——只和相对位置有关!
这个方法的美妙之处在于:
- 它自然编码相对位置——不需要额外的参数或复杂操作
- 它和标准 attention 的矩阵乘法形式完全兼容——不降速
- 位置信息是”嵌入”在向量的方向中的,而非加上去的
RoPE 迅速成为事实标准。LLaMA、Mistral、Qwen、DeepSeek——几乎所有 2023 年后的主流开源模型都用它。
新问题:训练 4K,推理 100K?
RoPE 解决了”怎么编码位置”的问题,但引入了一个新问题:模型只在训练时见过特定长度的文本。比如训练时最长 4096 个 token。那推理时遇到 10 万 token 的输入呢?
模型会碰到它从未见过的”旋转角度”——就像一个只见过 12 小时制时钟的人,突然要它读 24 小时制。
2022-2023 年,研究者们想出了几种巧妙的解法:
ALiBi:最简单的方案
ALiBi(Attention with Linear Biases)干脆换了一种思路:别折腾复杂的位置编码了。直接在 attention 分数上减去一个与距离成正比的惩罚值。
意思是:离我越远的词,我天然就不太关注。这是一个固定的偏置——不需要训练,也不需要学习。
就像人说话时自然会更关注前后几个词,而不太关心 1000 个词之前说了什么。ALiBi 把这个先验直接编码进去了。
它的好处是:训练时用短序列(比如 1024),推理时直接用长序列(比如 8192)——因为偏置是按距离定义的,天然泛化到任意长度。不需要额外训练。
YaRN:修复 RoPE 的外推
YaRN(2023)选择保留 RoPE,但修复它的外推失败。
核心观察是:RoPE 中不同频率的分量扮演着不同角色。高频分量负责区分相邻位置(”第 5 个词还是第 6 个词”),低频分量负责编码大范围位置关系(”大概在句子前半段还是后半段”)。
当序列变长时,低频分量需要被”拉伸”来覆盖更大的位置范围,但高频分量不应该被动——它们负责的局部精度不应该改变。
YaRN 的做法:对不同频率分量采用不同的处理策略。高频保持不变,低频做插值拉伸,中间的按比例过渡。再加上一个注意力温度调节。
效果:只需原始训练数据 1/10 的额外训练,就能把 4K 上下文可靠地扩展到 64K 甚至 128K。
更激进的想法:不要位置编码?
2024 年,有研究开始探索一个更大胆的方向:如果模型足够聪明,它能不能自己从数据中学会位置概念,不需要我们显式告诉它?
初步结果表明,大规模模型在某些情况下确实能做到。但目前还不如 RoPE 稳定可靠——这个方向更多是学术探索。
flowchart LR
A["Sinusoidal 2017<br/>固定·绝对位置"] --> B["Relative PE 2018<br/>建模词间距离"]
B --> C["RoPE 2021<br/>旋转矩阵·相对位置"]
C --> D["ALiBi 2022<br/>线性偏置·免训练外推"]
D --> E["YaRN 2023<br/>频率插值·长上下文"]
位置编码的演进告诉了我们什么
从正弦波 → 相对位置 → RoPE → ALiBi → YaRN → 无位置编码的探索,有一条清晰的趋势线:
越来越少的人工设计,越来越多地让模型自己处理。
早期:手工设计正弦函数,加到向量上。 中期:用旋转这样的数学性质来隐式编码。 现在:要么用最简单的线性偏置(ALiBi),要么探索不要编码。
同时,上下文长度从 512 → 2048 → 4096 → 32K → 128K → 1M+ 的爆发式增长,让位置编码从一个”次要工程问题”变成了决定模型能力上限的关键因素。
一个有趣的类比:人类其实也不太精确地跟踪”第 347 个词”。我们对位置的感知是模糊的、相对的、基于上下文的。某种程度上,RoPE 和 ALiBi 正在朝着这个方向靠近。
下一篇我们要面对一个更实际的问题:当序列长度从几千增长到几十万时,$O(n^2)$ 的 attention 计算怎么办?一场工程和算法的效率战争。