<aside> <img src="/icons/condense_yellow.svg" alt="/icons/condense_yellow.svg" width="40px" /> Python | 浏览器 | 语言模型 | 推理 | 神经网络 | 数学 | 交互式 | 可视化 | 透明度 | 歌词生成 | 不确定性 | 鲁棒性 | 深度学习 | 估计基准 | 文本 | 前馈 | 记忆组合 | 注意力 | 视觉解释器 | 知识 | 信息
</aside>
🎯浏览器语言推理识别神经网络 | 🎯不同语言秽语训练识别数据集 | 🎯交互式语言处理解释 Transformer 语言模型 | 🎯可视化Transformer 语言模型 | 🎯语言模型生成优质歌词 | 🎯模型不确定性和鲁棒性深度学习估计基准 | 🎯文本生成神经网络诗歌生成 | 🎯模型透明度 | 🎯验证揭示前馈Transformer 语言模型记忆组合 | 🎯可视化语言模型注意力 | 🎯Transformer语言模型文本解释器和视觉解释器 | 🎯分布式训练和推理模型 | 🎯知识获取模型 | 🎯信息提取模型 | 🎯文本生成模型 | 🎯语音图像视频模型
注意力机制描述了神经网络中最近出现的一组新层,在过去几年中引起了广泛关注,尤其是在序列任务中。文献中对“注意力”有很多不同的定义,但我们在这里使用的定义如下:注意力机制描述了(序列)元素的加权平均值,其权重根据输入查询和元素的键动态计算。那么这到底是什么意思呢?目标是对多个元素的特征取平均值。但是,我们不希望对每个元素赋予相同的权重,而是希望根据它们的实际值赋予它们权重。换句话说,我们希望动态地决定我们更希望“关注”哪些输入。
💦缩放点积注意力
自注意力背后的核心概念是缩放点积注意力。我们的目标是建立一种注意力机制,序列中的任何元素都可以关注任何其他元素,同时仍能高效计算。点积注意力将一组查询 $Q \in R ^{T \times d_k}$、键 $K \in R ^{T \times d_k}$ 和值 $V \in R ^{T \times d_v}$ 作为输入,其中 T 是序列长度,$d_k$ 和 $d_v$ 分别是查询/键和值的隐藏维度。为了简单起见,我们现在忽略批量维度。从元素i到j的注意力值基于查询$Q_i$和键$K_j$的相似度,使用点积作为相似度度量。在数学中,我们计算点积注意力如下:
$$ \text { 注意力 }(Q, K, V)=\operatorname{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
其中 $Q、K、V$ 是查询、键和值向量的串联。
矩阵乘法 $Q K^T$ 对每个可能的查询和键对执行点积,产生形状为 $T \times T$ 的矩阵。每行代表特定元素 i 相对于序列中所有其他元素的注意力 logits。对此,我们应用 softmax 并与值向量相乘以获得加权平均值(权重由注意力决定)。这种注意力机制的另一个视角提供了如下所示的计算图。
我们尚未讨论的一方面是缩放因子 $1 / \sqrt{d_k}$。这个比例因子对于在初始化后保持注意力值的适当方差至关重要。请记住,我们初始化层的目的是使整个模型具有相等的方差,因此 Q 和 K 的方差也可能接近 1 。然而,对方差为 \sigma^2 的两个向量执行点积会产生方差为 d_k 倍的标量:
$$ q_i \sim N \left(0, \sigma^2\right), k_i \sim N \left(0, \sigma^2\right) \rightarrow \operatorname{Var}\left(\sum_{i=1}^{d_k} q_i \cdot k_i\right)=\sigma^4 \cdot d_k $$
如果我们不将方差缩小到 $\sim \sigma^2$,则 logits 上的 softmax 对于一个随机元素将饱和为 1,对于所有其他元素则饱和为 0。通过 softmax 的梯度将接近于零,因此我们无法正确地学习参数。请注意,$\sigma^2$ 的额外因子,即用 $\sigma^4$ 而不是 $\sigma^2$,通常不是问题,因为我们保持原始方差 $\sigma^2$ 接近无论如何,到1。
上图中的块 Mask (opt.)
表示对注意力矩阵中的特定条目进行可选屏蔽。例如,如果我们将具有不同长度的多个序列堆叠成一个批次,就会使用此功能。为了仍然受益于 PyTorch 中的并行化,我们将句子填充到相同的长度,并在计算注意力值时屏蔽填充标记。这通常是通过将相应的注意力逻辑设置为非常低的值来实现的。
在讨论了缩放点积注意力块的细节之后,我们可以在下面编写一个函数,在给定查询、键和值三元组的情况下计算输出特征:
def scaled_dot_product(q, k, v, mask=None):
d_k = q.size()[-1]
attn_logits = torch.matmul(q, k.transpose(-2, -1))
attn_logits = attn_logits / math.sqrt(d_k)
if mask is not None:
attn_logits = attn_logits.masked_fill(mask == 0, -9e15)
attention = F.softmax(attn_logits, dim=-1)
values = torch.matmul(attention, v)
return values, attention
请注意,上面的代码支持序列长度前面的任何附加维度,因此我们也可以将其用于批处理。但是,为了更好地理解,让我们生成一些随机查询、键和值向量,并计算注意力输出:
seq_len, d_k = 3, 2
pl.seed_everything(42)
q = torch.randn(seq_len, d_k)
k = torch.randn(seq_len, d_k)
v = torch.randn(seq_len, d_k)
values, attention = scaled_dot_product(q, k, v)
print("Q\\n", q)
print("K\\n", k)
print("V\\n", v)
print("Values\\n", values)
print("Attention\\n", attention)
Q
tensor([[ 0.3367, 0.1288],
[ 0.2345, 0.2303],
[-1.1229, -0.1863]])
K
tensor([[ 2.2082, -0.6380],
[ 0.4617, 0.2674],
[ 0.5349, 0.8094]])
V
tensor([[ 1.1103, -1.6898],
[-0.9890, 0.9580],
[ 1.3221, 0.8172]])
Values
tensor([[ 0.5698, -0.1520],
[ 0.5379, -0.0265],
[ 0.2246, 0.5556]])
Attention
tensor([[0.4028, 0.2886, 0.3086],
[0.3538, 0.3069, 0.3393],
[0.1303, 0.4630, 0.4067]])