趋近智
大师班
虽然绝对位置编码,如正弦或学习得到的嵌入,为Transformer模型提供了必要的序列顺序信息,但它们独立处理每个位置。位置 i 的编码,表示为 Pi,在生成时没有直接参考任何其他位置 j。这种方法存在局限。例如,如果一个模型在长度为512的序列上训练,在推理时遇到长度为1024的序列,这些绝对编码的泛化能力如何,并不立即明了。此外,注意力机制天然地计算标记对之间的关系(查询 i 注意到键 j)。如果注意力计算中包含的位置信息能明确反映位置 i 和 j 之间的相对距离或联系,而非仅仅它们的绝对位置,那可能会更自然。
这一观察促成了相对位置编码方案的提出。其核心思想是修改注意力机制或其输入,以便相对位置差(通常是 i−j)影响标记 i 和标记 j 之间的注意力得分。相对方案的目标是,不再仅仅将 Pi 添加到标记 i 的输入嵌入中,而是将查询和键位置之间的偏移信息直接注入到它们的交互计算里。
考虑标准的缩放点积注意力:
注意力(Q,K,V)=softmax(dkQKT)V这里,查询 i(Q 的第 i 行)和键 j(K 的第 j 行)之间的交互由点积 qi⋅kj 捕获。绝对位置编码通常在初始嵌入被投影到 Q 和 K 之前添加。相对位置编码旨在根据相对位置 i−j 来修改这种交互。
这种相对信息通常可以通过两种主要方式来纳入:
让我们将差异可视化:
绝对编码定义了相对于固定原点的位置,而相对编码则侧重于位置对之间的有向关系。
为何要采用这种方法?相对编码具有多项潜在优势:
考虑修改注意力得分的一种简化方式是,计算每个查询位置 i 和位置 j 之间的相对偏移。然后,此偏移量可用于查找表示该特定相对距离的学习嵌入。
import torch
import torch.nn as nn
# --- 示例代码段 ---
# 假设 B=批大小, L=序列长度, H=头数, D=头维度
# 假设查询向量的形状为 [B, H, L, D]
seq_len = 10
max_relative_distance = 4 # 只明确建模附近的相对位置
# 查询位置:0, 1, ..., 9
query_pos = torch.arange(seq_len, dtype=torch.long)
# 位置:0, 1, ...
key_pos = torch.arange(seq_len, dtype=torch.long)
# 计算相对位置 (矩阵[i, j] = i - j)
# 形状:[L, L]
relative_pos = query_pos.unsqueeze(1) - key_pos.unsqueeze(0)
print(f"L=5时的原始相对位置 (i-j):\n{relative_pos[:5, :5]}")
# 将相对位置裁剪到一定范围
# [-最大相对距离, 最大相对距离]
# 将这些裁剪后的值映射到索引 [0, 2*最大相对距离]
clipped_relative_pos = torch.clamp(
relative_pos,
-max_relative_distance,
max_relative_distance
)
relative_pos_indices = clipped_relative_pos + max_relative_distance
print(f"\nL=5时裁剪后的相对索引:\n{relative_pos_indices[:5, :5]}")
# 假设我们有一个用于相对位置的嵌入表
num_relative_embeddings = 2 * max_relative_distance + 1
# 例如,对于 -4 到 +4 -> 9 个嵌入
# 嵌入维度:通常只有 1 (每个头的标量偏置)
# 或 D (每个头的向量偏置)
# 让我们假设每个头使用标量偏置,嵌入维度 = 头数 (H)
num_heads = 8
relative_embedding = nn.Embedding(num_relative_embeddings, num_heads) # 例如 H=8
# 根据相对位置索引查找偏置
# 形状:[L, L, H]
relative_position_bias = relative_embedding(relative_pos_indices)
# 重塑/置换偏置以匹配注意力得分形状
# [B, H, L, L]
# 在softmax之前将此偏置添加到 QK^T 点积中
# 示例(为简化起见忽略批维度 B):
# 注意力得分形状:[H, L, L]
# 相对位置偏置形状:[L, L, H] -> 置换为 [H, L, L]
bias_for_scores = relative_position_bias.permute(2, 0, 1) # 现在是 [H, L, L]
# 注意力计算修改:
# 查询形状:[H, L, D],键形状:[H, L, D]
# attn_scores = torch.matmul(query, key.transpose(-2, -1))
# / (D**0.5) # [H, L, L]
# attn_scores = attn_scores + bias_for_scores # 添加相对偏置
# attn_probs = torch.softmax(attn_scores, dim=-1)
print(f"\n查找索引的形状: {relative_pos_indices.shape}")
print(f"相对嵌入表输出的形状: {relative_position_bias.shape}")
print(f"用于注意力得分的置换偏置的形状: {bias_for_scores.shape}")
此代码段说明了核心流程:计算相对索引,裁剪它们以管理嵌入表大小,查找对应的嵌入,并准备将其添加到注意力得分中。
当然,这是一种简化视角。高效且实际的实现需要仔细处理嵌入查找、层间嵌入的潜在共享,以及将其自然地整合到Transformer块中。接下来的部分将审视具体、成熟的方法,如Shaw等人的方法、Transformer-XL的相对编码和旋转位置嵌入(RoPE),这些方法在实践中应用了这些思想。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造