一句话先看懂:Transformer本身分不清“猫咬狗”和“狗咬猫”。位置编码就是给每个词发一个“座位号”,让模型知道谁在前、谁在后。
一、先看两个句子
句子A:猫咬狗
句子B:狗咬猫
词一模一样,就是“猫”和“狗”换了个位置。意思完全不同——一个猫吃亏,一个狗倒霉。
二、Transformer的“盲点”
Transformer最核心的组件叫自注意力机制(Self-Attention) 。它做的事情是:计算句子中每个词和其他所有词之间的“关联强度”。
但这个计算有个特点——它不看位置。
| 特性 | 说明 |
|---|---|
| 自注意力关注什么 | 词与词之间的语义关联 |
| 自注意力不关注什么 | 词在句子中的先后顺序 |
| 没有位置编码时 | “猫咬狗”和“狗咬猫”在模型眼里完全一样 |
数学上这叫置换不变性(Permutation Invariance) ——把输入顺序打乱,计算结果不变。
这就麻烦了。语言的核心就是顺序决定意义。“猫咬狗”和“狗咬猫”,主语和宾语一换,意思就反了。如果模型分不清,那翻译、问答、摘要全都做不对。
三、解决方案:发“座位号”
既然模型自己分不清顺序,那就人为地把位置信息加进去。
方法很简单:每个词原本有一个词嵌入向量(表示这个词的意思),再给它加上一个位置编码向量(表示这个词在第几位)。两者相加,得到一个既包含语义又包含位置的新向量。
四、手把手算一遍:假设词向量只有2个维度
为了看清楚,我们假设每个词的词向量只有 2个维度(实际模型通常是512或768维)。
假设“猫”、“咬”、“狗”这三个词的词嵌入向量(纯语义)都是 [0.5, 0.5] (仅为了演示计算)。
Transformer原始论文《Attention Is All You Need》里用的位置编码公式是:
第pos个位置,第 2i 维(偶数维):sin(pos / 10000^(2i/d))
第pos个位置,第 2i+1 维(奇数维):cos(pos / 10000^(2i/d))
其中d是词向量维度。我们的d=2,所以只有两个维度:第0维(偶数,i=0)用正弦,第1维(奇数,i=0)用余弦。
当i=0时,2i=0,分母变成了 10000^(0/2) = 10000^0 = 1。
所以公式简化为:
第0维 = sin(pos)
第1维 = cos(pos)
我们来算“猫”(位置1)、“咬”(位置2)、“狗”(位置3)的位置编码:
| 位置 | 对应词 | 第0维(sin) | 第1维(cos) |
|---|---|---|---|
| 1 | 猫 | sin(1) ≈ 0.8415 | cos(1) ≈ 0.5403 |
| 2 | 咬 | sin(2) ≈ 0.9093 | cos(2) ≈ -0.4161 |
| 3 | 狗 | sin(3) ≈ 0.1411 | cos(3) ≈ -0.9900 |
算出来了。接着,把位置编码加到原来的词向量 [0.5, 0.5] 上:
| 词 | 原始词向量 | 位置编码 | 最终输入向量(词向量+位置编码) |
|---|---|---|---|
| 猫(位置1) | [0.5, 0.5] | [0.8415, 0.5403] | [1.3415, 1.0403] |
| 咬(位置2) | [0.5, 0.5] | [0.9093, -0.4161] | [1.4093, 0.0839] |
| 狗(位置3) | [0.5, 0.5] | [0.1411, -0.9900] | [0.6411, -0.4900] |
现在看看结果:虽然“猫”和“狗”的词向量原本一模一样,但加完位置编码后,“猫”的最终向量是[1.3415, 1.0403],“狗”的最终向量是[0.6411, -0.4900],完全不同了。Transformer再处理时,就能清楚地知道谁是主语、谁是宾语。
五、这种设计的精妙之处在哪?
原始论文用正弦和余弦,绝不是随便选的,里面藏着三处巧思:
1. 能轻松算出“相对距离”
三角恒等式告诉我们:sin(pos + k) 可以拆解成 sin(pos) 和 cos(pos) 的线性组合。这意味着,无论“咬”在“猫”后面隔了几个词,模型只需要通过简单的数学变换,就能从“猫”的位置向量推导出“咬”的相对位置偏移量。模型学到的不仅是“我在哪”,更是“你在我前面多远”。对于理解“猫咬狗”这种强依赖邻近顺序的句子,这个性质至关重要。
2. 低维度管近邻,高维度管全局
低维度(比如我们例子里的第0、1维):正弦和余弦的波长很短(2π),位置变一点,数值就剧烈变化。这就像秒针,精准区分“猫”和“咬”这种紧挨着的词。
高维度(实际模型中靠后的维度):分母里的10000^(2i/d)非常大,导致波长极长(10000×2π)。位置变动一点,数值几乎不变。这就像时针,只管这个句子的大致长度和全局顺序。
高低维度搭配使用,既能抓住局部的“猫咬”关系,又能把握整个句子的结构。
3. 不增加一个参数,且能无限延伸
这套编码是用数学公式直接算出来的,不需要模型花时间去学习,也不占用任何模型参数。更厉害的是,训练时模型只见过最长512个词的句子,但推理时突然来了一句1000个词的,这套公式依然能准确无误地算出第1000个位置的正余弦值,天生支持超长序列。
六、总结
Transformer的自注意力机制本身不分顺序——“猫咬狗”和“狗咬猫”在它眼里没区别。
位置编码就是给每个词一个独一无二的“座位号” 。为了算这个座位号,Transformer用了正弦和余弦函数。
我们用2维词向量的例子亲手算了一遍——“猫”得到[1.3415, 1.0403],“狗”得到[0.6411, -0.4900] ,一模一样的词因为位置不同,向量天差地别。
而正余弦编码的高明之处,在于它既能让模型轻松感知相对距离,又能用不同频率区分远近,还不增加任何参数且能无限扩展。
有了位置编码,模型才能真正理解“猫咬狗”和“狗咬猫”是两件完全不同的事。
夜雨聆风