1 注意力机制中的Q、K、V
我们通过一个简化的例子来说明注意力机制中q(查询)、k(键)和v(值)的作用。
假设我们有一个简单的语言模型,它正在处理一个英文句子,并试图预测下一个单词。句子是 "The cat sat on the mat",我们想要预测下一个单词。
步骤 1: 定义嵌入向量
首先,我们假设每个单词都有一个对应的嵌入向量(embedding vector),这些向量是从数据中学习得到的。我们将每个单词的嵌入向量表示为e1, e2, ..., e6,对应于句子中的每个单词。
步骤 2: 计算查询、键和值
我们使用可学习的权重矩阵W_q、W_k和W_v来计算查询、键和值:
q = W_q * e3(假设我们正在处理单词 "sat",它位于位置3)k1 = W_k * e1,k2 = W_k * e2, ...,k6 = W_k * e6(计算每个单词的键向量)v1 = W_v * e1,v2 = W_v * e2, ...,v6 = W_v * e6(计算每个单词的值向量)
步骤 3: 计算注意力得分
接下来,我们计算查询q与每个键k的点积,这会给出每个单词对于当前查询的重要性得分:
score1 = q * k1^Tscore2 = q * k2^T- ...
score6 = q * k6^T
步骤 4: 应用softmax函数
我们将这些得分通过softmax函数转换为概率(注意力权重):
weight1 = softmax(score1)weight2 = softmax(score2)- ...
weight6 = softmax(score6)
softmax确保所有权重加起来等于1。
步骤 5: 加权求和值
最后,我们使用这些权重对值进行加权求和,以生成最终的输出(上下文向量):
output = weight1 * v1 + weight2 * v2 + ... + weight6 * v6
这个输出向量output将包含来自整个句子的信息,但是根据每个单词与当前查询("sat")的相关性进行了加权。例如,如果模型认为单词 "cat" 和 "mat" 对于预测下一个单词非常重要,那么它们对应的权重将会较高,从而在最终的输出中占据较大的比重。- 通过这种方式,注意力机制允许模型在处理每个单词时,动态地关注整个句子中最相关的部分,而不是将每个单词视为独立的实体。这有助于捕捉长距离依赖关系,并显著提高了序列模型的效果。
2 多种注意力机制
这里主要介绍两种注意力和一种扩展注意力。前面两种注意力主要区别就是输入的Key、Value的不同。
这里以Transformer的解码器子模块的两个多头注意力为例,假设解码器输入为x、编码器输出为enc_output。详细可以去看看原文。
# 自注意力
X2 = self.attention1(X, key_values, key_values, dec_valid_lens)
Y = self.addnorm1(X, X2)
# 编码器-解码器注意力。
# enc_outputs的开头:(batch_size,num_steps,num_hiddens)
Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens)
Z = self.addnorm2(Y, Y2)
return self.addnorm3(Z, self.ffn(Z)), state
2.1 自注意力 (Self-Attention)
自注意力机制允许模型在处理一个序列时关注该序列内部的不同位置的信息。在自注意力中,查询(Query)、键(Key)和值(Value)都来自同一来源,即解码器的输入X。具体来说,自注意力机制会对X进行线性变换以生成Q、K、V,然后计算Q和K的相似度,通过softmax函数获得注意力权重,最后用这些权重对V进行加权求和。
在训练阶段,为了防止模型关注到未来信息,通常会采用掩码(masking)机制。这意味着在计算注意力权重时,会将未来位置的权重设置为0,确保解码器在生成每个词时只能依赖于之前的输出。
2.2 编码器-解码器注意力 (Encoder-Decoder Attention)
编码器-解码器注意力机制用于在解码器中引入编码器的输出信息。在这种机制中,查询(Query)来自解码器的当前状态(即Y),而键(Key)和值(Value)来自编码器的输出enc_outputs。这样,解码器在生成每个词时,不仅考虑了之前解码器的输出,还考虑了整个输入序列的编码信息。
这种注意力机制有助于模型在生成输出时关注输入序列中的相关部分,从而提高生成文本的准确性和相关性。
2.3 多头注意力 (Multi-Head Attention)
多头注意力机制是自注意力和编码器-解码器注意力的扩展,它允许模型同时从多个表示子空间中学习信息。在多头注意力中,输入的Q、K、V会被分成多个头(head),每个头都会独立地进行自注意力或编码器-解码器注意力的计算。然后将所有头的输出拼接起来,通过一个线性变换生成最终的注意力输出。
多头注意力机制增加了模型的表示能力,因为它可以同时关注输入序列的不同方面。每个头可以学习到不同的信息,例如一些头可能关注位置信息,而另一些头可能关注语义信息。
在代码实现中,多头注意力通常涉及到以下步骤:
- 将输入的Q、K、V分别通过线性变换分成多个头。
- 对每个头分别计算注意力权重和加权求和。
- 将所有头的输出拼接起来。
- 通过一个线性变换将拼接后的输出转换为所需的维度。
这样,多头注意力机制可以在不同的表示子空间中捕捉到更丰富的信息,从而提高模型的性能。