要理解Transformer中的位置编码(Positional Encoding),核心是先搞懂「为什么需要它」,再用「直观比喻+关键特性」拆解它的设计逻辑,全程避开复杂公式,聚焦「它到底在做什么」。
一、先解决核心问题:为什么需要位置编码?
Transformer的核心是自注意力机制,但自注意力有个“天生缺陷”——它会把整个序列的token(比如单词、子词)“一次性并行处理”,完全没考虑token的「顺序」。
举个例子:
“我吃苹果”和“苹果吃我”,这两个句子的token完全一样,但顺序不同导致语义天差地别。如果没有位置编码,Transformer会把这两个句子当成“相同的token集合”,根本无法区分语义。
而RNN/LSTM是“逐词处理”(先算“我”,再算“吃”,最后算“苹果”),天然能捕捉顺序;卷积虽然并行,但也能通过“局部窗口”间接感知位置。
Transformer没有这些结构,所以必须手动给每个token注入“位置信息”——这就是位置编码的核心作用:给token贴“位置标签”,让模型知道“谁在谁前面/后面”。
二、位置编码的直观设计:给每个位置一个“独一无二的指纹”
位置编码的本质是:为序列中每个「位置(pos)」生成一个和「词嵌入(Embedding)维度相同」的向量(论文中是512维),然后把这个“位置向量”和“词嵌入向量”直接相加,这样每个token就同时包含了“语义信息(词嵌入)”和“位置信息(位置编码)”。
关键是:怎么设计这个“位置向量”,才能让模型轻松区分不同位置,还能理解「相对位置」(比如“我”和“苹果”隔了1个词,“苹果”和“吃”相邻)?
1. 论文的选择:用「正弦/余弦函数」生成位置向量
论文没有用“学习型位置编码”(比如随机初始化后让模型学),而是用了固定的正弦和余弦函数,形式很简单(不用记公式,看规律):
- 对于位置
pos
(比如第1个词pos=0,第2个pos=1,…,第n个pos=n-1) - 对于位置向量的第
i
个维度(比如512维中的第0维、第1维、…、第511维)- 如果
i
是偶数(比如0、2、4…):用正弦函数sin(pos / 10000^(2i/d_model))
- 如果
i
是奇数(比如1、3、5…):用余弦函数cos(pos / 10000^(2i/d_model))
- 如果
这个设计的直观好处,用一个比喻就能懂:
把每个“位置向量”当成一个「收音机」——不同维度对应不同的“频率频道”,位置pos
不同,每个频道的“信号强度”(函数值)也不同。
- 低维度(比如前几个维度):频率很低,不同位置的信号差异小(适合捕捉“长距离相对位置”,比如pos=0和pos=100的差异);
- 高维度(比如后几个维度):频率很高,不同位置的信号差异大(适合捕捉“短距离相对位置”,比如pos=0和pos=1的差异);
这样一来,每个位置都有一个独一无二的“信号组合”(位置向量),就像每个人的指纹一样,模型能轻松区分“谁在哪个位置”。
2. 最关键的优势:天然支持「相对位置」
语言中,“相对位置”比“绝对位置”更重要(比如“猫追狗”中,“猫”在“追”前面,“追”在“狗”前面——这个相对关系决定语义,而它们是第1、2、3个词的绝对位置没那么重要)。
而正弦/余弦函数的数学特性刚好满足这一点:对于任意固定的距离k
(比如两个词隔了k个位置),位置pos+k
的编码向量,都能通过位置pos
的编码向量“线性组合”得到。
举个简单例子:假设k=2,那么pos=3的编码向量,能由pos=1的编码向量算出来。这就意味着,模型学完pos=1和pos=3的关系后,遇到pos=5和pos=7(同样隔2个位置),能自动理解它们的相对关系——不用重新学习所有位置的组合,泛化能力更强。
3. 为什么不用“学习型位置编码”?
论文也试过“让模型自己学位置向量”(随机初始化,训练中更新),发现效果和正弦余弦几乎一样,但正弦余弦有个致命优势:能外推到“比训练时更长的序列”。
比如训练时序列最长是512个词,测试时遇到1000个词的长句子:
- 正弦余弦编码:直接代入公式计算pos=512、513…1000的向量,完全没问题;
- 学习型编码:模型没学过pos≥512的位置,无法生成有效向量,直接“懵了”。
三、一句话总结位置编码
位置编码就是给Transformer中的每个token“贴一张带位置信息的身份证”:
- 身份证的“格式”(维度)和词嵌入完全一致,方便直接结合;
- 身份证的“内容”(向量值)由正弦余弦函数生成,确保每个位置独一无二,还能让模型看懂“谁和谁隔多远”;
- 最终目的:解决Transformer“并行处理时丢了顺序”的bug,让模型能正确理解“语序决定语义”的语言规律。