张芷铭的个人博客

位置编码为 Transformer 自注意力机制注入位置信息,解决其置换不变性问题。

核心问题

自注意力机制的置换不变性使其无法区分序列顺序。位置编码通过添加位置向量解决:

$$\text{InputEmbedding}_{\text{final}} = \text{TokenEmbedding} + \text{PositionalEncoding}$$

设计原则

原则说明
唯一性每个位置有独特的编码
可解释性相对距离编码稳定
外推性处理超出训练长度的序列
效率计算不应成为瓶颈

经典正弦位置编码

$$PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)$$

$$PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)$$

核心性质

  1. 唯一性:不同频率的正弦/余弦组合形成唯一信号
  2. 可外推:理论上支持任意长度序列
  3. 相对位置:$PE_{pos+k}$ 可由 $PE_{pos}$ 线性变换得到

位置编码演进

类型代表模型优点缺点
可学习绝对编码BERT、GPT-2简单有效无法外推
固定绝对编码Transformer可外推、无参数长序列效果下降
相对位置编码T5、DeBERTa更直观实现复杂
RoPELLaMA优雅融合相对位置需修改注意力结构

PyTorch 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class PositionalEncoding(nn.Module):
    def __init__(self, d_model: int, dropout: float = 0.1, max_len: int = 5000):
        super().__init__()
        self.dropout = nn.Dropout(p=dropout)

        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        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)
        pe = pe.unsqueeze(0)

        self.register_buffer('pe', pe)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        x = x + self.pe[:, :x.size(1)]
        return self.dropout(x)

学习资源

Comments