Transformer 为什么需要位置编码?

位置编码:Transformer 的"顺序感知"补丁
上篇我们聊完 Multi-Head Attention,知道了它是怎么让每个词"看到"其他所有词的。
但有个致命漏洞:它根本分不清谁先谁后。
"我爱你"和"你爱我",在它眼里可能是一回事。
这可不是 bug。Attention 的数学公式天生就长这样。要解决这个"词序盲区",得靠一个看似简单、实则精妙的机制——位置编码。
1. 位置编码到底是啥?
简单说,它就是一个和词向量维度完全一样的数字串,专门负责告诉模型:"这个词在第几个位置。"
具体怎么用?两个字:相加。
词向量本身表达的是词的语义——"爱"和"喜欢"的意思相近,它们的向量也相近。位置编码加进来之后,词向量就多了一层信息:顺序。
"我"在第1位,"你"在第2位,"爱"在第3位——每个位置都有一个专属的数字标签。
这个标签不是拼接上去的,而是直接和词向量加在一起。所以最终输入 Transformer 的,其实是一个"语义+位置"的混合向量。
模型拿到这个向量,既知道你在说什么,也知道你说在第几个字说的。

2. Attention 的词序盲区
回到 Attention 的核心公式。Query、Key、Value 三者相乘,本质是在算"谁和谁更像"。
问题来了:这个计算过程跟顺序无关。
不管你先输入"我爱你"还是先输入"你爱我",Attention 看到的都是三个词的无序集合。它只关心"爱"应该关注谁,不关心"爱"在第几个字。
举个例子:
"我爱你" → 模型觉得"爱"最该关注"我"和"你"
"你爱我" → 模型还是觉得"爱"最该关注"我"和"你"
但这两句话的意思,明明完全相反啊!
所以没有位置信息,Attention 就变成了一个"只看内容、不管顺序"的大冤种。中文这种语序敏感的语言尤其吃亏,换个顺序意思天差地别。

3. 三角函数位置编码
Transformer 原始论文用的是 Sinusoidal 编码,中文叫三角函数位置编码。
公式长这样:
PE(pos, 2i) = sin(pos / 10000^(2i/d))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d))
看不懂没关系,我来拆解它的精髓。
核心思路:用不同频率的正弦余弦函数,在不同维度上生成周期信号。
打个比方:想象一支交响乐队。低音乐器振动慢、周期长;高音乐器振动快、周期短。不同频率叠加,才能表达完整的音乐。
位置编码同理。
- 低维度(i=0,1):频率高、变化快,负责区分短距离位置
- 高维度(i=100,101):频率低、变化慢,负责区分长距离位置
每个位置都会得到一个独一无二的向量。两个位置越近,向量越相似;位置越远,向量差异越大。
更有意思的是,PE(pos+k) 可以用 PE(pos) 加上一个固定的线性变换得到。这意味着模型不需要显式知道绝对位置,通过 Attention 的计算就能学出词与词之间的距离关系。
你说巧不巧?

4. 为什么偏偏选 10000 这个底数?
公式里有个神秘的 10000。
10000^(2i/d) 是频率的分母。分母越大,频率越低,周期越长。
底数太小会怎样?假设底数是 100,对于序列长度 512 的文本,高维度的波长可能只有几十。这意味着相隔 100 个词的位置,编码就已经开始重复了。模型根本分不清第 10 个词和第 110 个词的位置差别。
底数太大呢?假设底数是 1000000,相邻位置的编码向量会非常接近,模型很难从中学到有用的位置信息。放大放大再放大,相邻的还是分不清。
10000 是个经验值。这个数字不是推导出来的,是实验试出来的。后续很多模型也会调整这个参数,比如 Llama 用的是 500000。但 10000 作为初代设定,在大多数任务上效果稳定,是经过验证的工程选择。
说白了就是:不大不小,刚刚好。

5. 固定编码 vs 可学习编码
原始 Transformer 用的是固定编码,位置向量是人工设计好的函数,训练时不变。
后来 BERT 改成了可学习编码,位置向量变成一个可以训练的参数矩阵,模型自己学出最优的位置表示。
哪个更好?原始论文做过实验,结论是:差不多。
固定编码能直接表达相对位置关系,有数学上的先天优势;可学习编码更灵活,能从数据中发现真正有用的位置模式。
但这不代表可以随便选。
固定编码的好处是不增加参数量。一个 512 词上限的模型,固定编码只需要存一套公式;可学习编码需要额外存储一个 512 × d 的矩阵。
固定编码还有个隐含优势:外推能力强。训练时见过 512 长度的句子,用的编码函数照样能算第 513 个位置。可学习编码碰到没见过的位置,只能靠模型自己泛化。
不过可学习编码也有杀手锏:下游任务适配。BERT 在预训练时就把位置信息学进去了,具体任务微调时不需要再操心编码设计。
说到底,这是"人工经验"和"数据驱动"两条路的取舍。不同场景,各有最优解。

6. 相对位置感知
位置编码解决了"能不能感知位置"的问题,但带来了新问题:绝对位置真的重要吗?
"爱"在第 3 位,"你"在第 2 位——模型知道这个信息。但它真的需要知道"第 3 位"这个绝对坐标吗?
其实对语言理解来说,更关键的是相对关系。
"爱"和"你"之间隔了 1 个词,这个信息比"爱在第 3 位"有用得多。因为语言本质上是由关系构成的:我爱你、你爱我,区别不在于谁在第几位,而在于谁在谁的前面。
三角函数编码恰好满足这个数学性质——相邻位置的编码差值,可以表达它们之间的距离。
但这毕竟是"恰好",不是专门设计。后来出现了更精细的方案:
- RoPE(旋转位置编码):直接在 Attention 计算中注入相对位置信息,连绝对位置都不需要了
- ALiBi(线性偏置注意力):用 Attention 分数的偏置项表达位置距离,不需要显式的位置向量
这些新设计的共同目标是让模型更自然地学习相对位置关系。绝对位置像是个人的身份证号,知道就行;相对位置才是真正的社交关系——谁是谁的邻居,谁听谁的。

还有个小尾巴
位置编码让 Transformer 获得了顺序感知能力。但不同架构的位置编码处理方式完全不一样——Encoder-only 的 BERT 只需要理解不需要生成;Decoder-only 的 GPT 需要遮遮掩掩、只看你不该看的;Encoder-Decoder 的原始 Transformer 则是两边都要兼顾。
这些架构之间到底有什么区别?各自擅长什么场景?下篇来聊。
