为什么需要位置编码

Transformer 并行处理整个序列,不像 RNN 那样逐步读取。这种并行性带来了效率,但也丢失了序列的顺序信息。位置编码正是为了解决这个问题。

Transformer 架构概览

Sinusoidal Position Encoding

原始 Transformer 使用正弦和余弦函数:

PE(pos,2i)=sin(pos/100002i/dmodel)PE_{(pos, 2i)} = \sin(pos / 10000^{2i/d_{model}}) PE(pos,2i+1)=cos(pos/100002i/dmodel)PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i/d_{model}})

优点

  • 可以外推到训练时未见过的序列长度
  • 不需要额外学习参数
  • 相对位置关系可通过三角函数推导

代码实现

def sinusoidal_encoding(max_len, d_model):
    pe = torch.zeros(max_len, d_model)
    position = torch.arange(0, max_len).unsqueeze(1).float()
    div_term = torch.exp(torch.arange(0, d_model, 2).float() * 
                        -(math.log(10000.0) / d_model))
    pe[:, 0::2] = torch.sin(position * div_term)
    pe[:, 1::2] = torch.cos(position * div_term)
    return pe

Learned Position Embedding

GPT 系列使用的是可学习的位置嵌入。直接将位置索引映射到一个 Embedding 层,让模型自行学习最佳的位置表示。

选择 Sinusoidal 还是 Learned?如果需要在推理时使用比训练更长的序列,Sinusoidal 是更安全的选择。

更多背景可参考 Transformer 架构详解注意力机制深入理解