位置编码
位置编码是Transformer模型的关键组件,用于为模型提供序列中token的位置信息,因为自注意力机制本身不包含位置信息。
为什么需要位置编码?
Transformer中的自注意力机制是位置无关的,即:
如果我们打乱输入序列中token的顺序,自注意力层的计算结果仍然相同。然而,语言是有序的,单词或token的顺序对语义至关重要。
如果没有位置信息,"猫追狗"和"狗追猫"在自注意力中将被视为等价的,这显然是不正确的。
位置编码的主要方法
1. 绝对位置编码 (Absolute Positional Encoding)
学习型位置嵌入
最简单的方法是学习每个位置的嵌入向量:
class LearnedPositionalEmbedding(nn.Module):
def __init__(self, max_len, d_model):
super().__init__()
self.positional_embeddings = nn.Parameter(torch.zeros(max_len, d_model))
nn.init.normal_(self.positional_embeddings, mean=0, std=0.02)
def forward(self, x):
seq_len = x.size(1)
return x + self.positional_embeddings[:seq_len, :]
正弦位置编码 (Sinusoidal)
原始Transformer论文中使用的正弦位置编码:
实现代码:
def sinusoidal_positional_encoding(max_len, d_model):
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)
return pe
正弦位置编码的优势:
- 可以扩展到未见过的序列长度
- 提供不同频率的位置信号
- 固定编码,不需要学习参数

2. 相对位置编码 (Relative Positional Encoding)
相对位置编码关注的是token之间的相对距离,而不是它们在序列中的绝对位置。
Shaw等人的方法 (Transformer-XL)
在注意力计算中加入相对位置信息:
其中R是相对位置编码矩阵。
class RelativePositionalEncoding(nn.Module):
def __init__(self, d_model, max_rel_distance=32):
super().__init__()
self.max_rel_distance = max_rel_distance
self.rel_embeddings = nn.Parameter(torch.zeros(2 * max_rel_distance + 1, d_model))
nn.init.normal_(self.rel_embeddings, mean=0, std=0.02)
def forward(self, q, k):
pass
3. 旋转位置编码 (RoPE - Rotary Position Embedding)
最近在现代大语言模型中流行的位置编码方法,将旋转矩阵应用于查询和键向量: