如何理解注意机制中经常提到的键、查询和值 ?

Attention 机制很厉害,但是他是怎么想出来的,少有人讨论。stackexchange 上有人讨论了一些,可作为参考:

neural networks - What exactly are keys, queries, and values in attention mechanisms? - Cross Validated

置顶的回答:

键/值/查询的概念类似于检索系统。例如,当您在Youtube上搜索视频时,搜索引擎会将您的查询(搜索栏中的文本)映射到数据库中与候选视频相关的一组键(视频标题、描述等),然后向您显示最佳匹配的视频(值)。

注意操作也可以被认为是一个检索过程。

接下来这个回答解释了注意力机制的好处:

在seq2seq模型中,我们将输入序列编码为上下文向量,然后将该上下文向量提供给解码器以产生预期的良好输出。

但是,如果输入序列很长,只依赖一个上下文向量就不那么有效了。我们需要输入序列(编码器)中隐藏状态的所有信息来进行更好的解码(注意机制)。

一种利用输入隐藏状态的方法如下所示:

换句话说,在这种注意机制中,上下文向量是作为值的加权和计算的,其中分配给每个值的权重是通过与相应键的查询的兼容函数计算的(这是来自 [Attention Is All You Need]的一个稍微修改过的句子)

这里,查询来自解码器的隐藏状态,键和值来自编码器的隐藏状态(键和值在此图中是相同的)。分数是查询和键之间的相似性,它可以是查询和键之间的点积(或其他形式的相似性)。然后,这些分数通过softmax函数生成一组权重,其总和等于1。每个权值乘以它对应的值,得到上下文向量,它利用所有的输入隐藏状态。

请注意,如果我们手动将最后一个输入的权重设置为1,其所有优先级设置为0,那么我们将注意机制减少到原始的seq2seq上下文向量机制。也就是说,不需要注意先前的输入编码器状态。

现在,让我们考虑如下图所示的自我注意机制:

与上图的不同之处在于,查询、键和值是对应输入状态向量的转换。其他的保持不变。

注意,我们仍然可以使用原始的编码器状态向量作为查询、键和值。那么,我们为什么需要这种转变呢?这个变换是一个简单的矩阵乘法,像这样:

Query = I x W(Q)

Key = I x W(K)

Value = I x W(V)

其中I是输入(编码器)状态向量,W(Q)、W(K)和W(V)是将I向量转换为Query、Key、Value向量的相应矩阵。

这个矩阵乘法(向量变换)的好处是什么?

显而易见的原因是,如果我们不转换输入向量,用于计算每个输入值的权值的点积将总是为单个输入token本身产生最大的权值得分。这可能不是我们想要的情况,例如,对于代词标记,我们需要它注意它的所指物。

另一个不太明显但重要的原因是,转换可能会为Query、Key和Value产生更好的表示。回想一下奇异值分解(SVD)的效果,如下图所示:

通过将一个输入向量与一个矩阵V(来自SVD)相乘,如果这两个向量在主题空间中类似,如图中示例所示,我们可以获得计算两个向量之间兼容性的更好表示。

这些用于变换的矩阵可以在神经网络中学习!

简而言之,将输入向量与矩阵相乘,得到:

1、增加每个输入token处理输入序列中其他tokens的可能性,而不是单个token本身。

2、可能是输入向量更好的(潜)表示;

3、将输入向量转换为一个具有期望维数的空间,例如,从维数5到维数2,或从维数n到m,等等(这实际上很有用);

注意转换矩阵是可学习的(不需要手动设置)。

我希望这能帮助您理解深度神经网络(自我)注意机制中的查询、键和值。

我的思考:

从之前 seq2seq 的注意力机制可以看出一些 transformer 中注意力机智的雏形。transformer的作者可能在考虑语言理解问题的时候重点考虑了 it 这类词的指代问题。因为 word2vec 得到的词向量,类似词语的距离已经是接近的了。所以不需要很特别的机制,就可以计算一句话里那些词可能是关联的。对于 it 指代这个问题,它和YouTube检索太像了,作者可能就产生了联想。这样将一个词分为 Q、K、V  三个向量就多了一个存储关系的空间。下图是一个答主的示意,也可以做参考:

 文末顺带给有道翻译点赞!本文都是用“有道翻译”翻译的,翻译文本基本可以直接使用。论文这种专业文本都翻译这么好,很厉害。

 

 

 

### 多头自注意力机制与MHA 在Transformer架构中,多头自注意力机制(Multi-Head Self-Attention, MHSA)是一个核心组件。该机制允许模型在同一序列的不同位置关注不同部分的信息,从而增强表达能力。 #### 单头自注意力机制概述 单头自注意力机制通过查询矩阵\(Q\)、矩阵\(K\)矩阵\(V\)来计算注意力权重。具体过程如下: 1. \(Q\), \(K\), \(V\) 是输入数据经过线性变换后的三个向量表示; 2. 计算相似度得分:\[ A = softmax(\frac{QK^T}{\sqrt{d_k}}) \][^1]; 3. 得到加权求的结果作为最终输出:\[ Z = AV \][^1]; 其中,\( d_k \) 表示维度大小,用于缩放点积以稳定梯度传播。 #### 多头自注意力机制详解 为了捕捉更丰富的特征关系,在单一头部的基础上引入了多个平行的注意层——即所谓的“多头”。每个独立的头都执行相同的自注意力操作,但是参数彼此之间互不共享。这样做的好处是可以让网络学会从不同的表征空间提取信息。 对于一个多头结构来说,其整体流程可描述为: 1. 将原始输入分别映射至若干组新的\( Q_i,K_i,V_i \)(对应第 i 个head); 2. 对每一对\( (Q_i,K_i,V_i)\),按照上述提到的方式单独做一次标准的自注意力运算; 3. 把所有这些结果拼接起来形成一个更大的张量,并再次投影回期望尺寸的空间内; 公式化表述就是: \[ MultiHead(Q,K,V)=Concat(head_1,...,head_h)V'W_O \] 这里 h 指的是总的heads数目; W_O 则是用来调整最后组合后维度的一个全连接层权重矩阵. 因此,“MHSA”实际上指的就是这种带有多个并行工作的自注意力单元的整体设计模式。“MHA”通常是指实现了这个概念的具体模块或函数名称,二者本质上是一致的概念但在称呼上有所区别. ```python import torch.nn as nn class MultiHeadSelfAttention(nn.Module): def __init__(self, embed_size, num_heads): super(MultiHeadSelfAttention, self).__init__() self.embed_size = embed_size self.num_heads = num_heads assert embed_size % num_heads == 0, "Embed size must be divisible by heads" self.depth = embed_size // num_heads self.wq = nn.Linear(embed_size, embed_size) self.wk = nn.Linear(embed_size, embed_size) self.wv = nn.Linear(embed_size, embed_size) self.dense = nn.Linear(embed_size, embed_size) def split_heads(self, x, batch_size): """Split the last dimension into (num_heads, depth). Transpose the result such that the shape is (batch_size, num_heads, seq_len, depth) """ x = x.view(batch_size, -1, self.num_heads, self.depth).transpose(1, 2) return x def forward(self, q, k, v, mask=None): batch_size = q.size(0) q = self.wq(q) k = self.wk(k) v = self.wv(v) q = self.split_heads(q, batch_size) k = self.split_heads(k, batch_size) v = self.split_heads(v, batch_size) scaled_attention, attention_weights = scaled_dot_product_attention( q, k, v, mask) concat_attention = scaled_attention.transpose(1, 2)\ .contiguous()\ .view(batch_size, -1, self.embed_size) output = self.dense(concat_attention) return output, attention_weights def scaled_dot_product_attention(q, k, v, mask): matmul_qk = torch.matmul(q, k.transpose(-2,-1)) dk = torch.tensor([k.shape[-1]], dtype=torch.float32).sqrt() logits = matmul_qk / dk if mask is not None: logits += (mask * -1e9) attention_weights = F.softmax(logits, dim=-1) output = torch.matmul(attention_weights, v) return output, attention_weights ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值