为什么 Transformer 需要位置信息,以及绝对位置编码的方案与局限

配套代码


Transformer 的排列不变性

我们先来看一个有趣的事实:标准的 Self-Attention 完全不关心输入的顺序

回顾 Attention 的计算过程:

其中 𝑄

𝑋 𝑊 𝑄 Q=XW Q ,𝐾

𝑋 𝑊 𝐾 K=XW K ,𝑉

𝑋 𝑊 𝑉 V=XW V 。如果我们打乱输入 𝑋 X 中 token 的顺序,每个 token 的 𝑄 Q、𝐾 K、𝑉 V 向量本身不会改变,只是它们在序列中的位置变了。而 Attention 计算的是所有 token 之间的两两点积,这个操作对顺序是不敏感的。

换句话说,“我 喜欢 猫” 和 “猫 喜欢 我” 对 Attention 来说是一样的。这显然不行,语言的意义高度依赖词序。

这就是为什么我们需要位置编码:在输入中注入位置信息,让模型知道每个 token 在序列中的位置。

绝对位置编码

最直接的思路是:给每个位置分配一个固定的向量,加到 token 的 embedding 上。

Sinusoidal 位置编码

原始 Transformer 论文(Vaswani et al., 2017)使用了基于正弦和余弦函数的位置编码:

其中 𝑝 𝑜 𝑠 pos 是位置索引,𝑖 i 是维度索引,𝑑 d 是 embedding 维度。

import torch
def sinusoidal_pe(max_len, d_model):
    """Sinusoidal 位置编码"""
    pe = torch.zeros(max_len, d_model)
    position = torch.arange(0, max_len).unsqueeze(1).float()
    # 频率项:10000^(-2i/d)
    div_term = 10000.0 ** (torch.arange(0, d_model, 2).float() / d_model)
    pe[:, 0::2] = torch.sin(position / div_term)  # 偶数维度
    pe[:, 1::2] = torch.cos(position / div_term)  # 奇数维度
    return pe  # Shape: (max_len, d_model)

这个设计有几个巧妙之处: