自注意力机制中的Q(查询)、K(键)、V(值)是通过对输入张量进行线性映射得到的:
1. 首先,通过对输入张量进行线性映射,生成三个张量Q、K和V。
- 对于Q(查询),将输入张量乘以一个权重矩阵W_q。
- 对于K(键),将输入张量乘以一个权重矩阵W_k。
- 对于V(值),将输入张量乘以一个权重矩阵W_v。
2. 接下来,对Q、K、V进行形状调整,使其适应自注意力机制的计算。
- 对于Q和K,通常会将它们的形状调整为(B, N, d_k),其中B是批次大小,N是序列长度,d_k是每个查询/键的维度。
- 对于V,通常会将其形状调整为(B, N, d_v),其中B是批次大小,N是序列长度,d_v是每个值的维度。
通过上述步骤,我们生成了经过线性映射的查询Q、键K和值V,然后可以用它们来计算自注意力权重并应用于输入张量。这样,我们可以根据输入张量中各个位置的信息来计算每个位置的注意力,实现自注意力机制。
下面是不同方式的Q,K,V的生成。
第一种方式:
self.proj_linear = nn.Linear(model_dim, 3*model_dim)
proj_output = self.proj_linear(input) # [batch_size, seq_len, 3*model_dim]
q, k, v = proj_output.chunk(3, dim=-1) # 拆分tensor, 按照最后一维 [batch_size, seq_len, model_dim]
q = q.reshape(batch_size, seq_len, num_head, head_dim).transpose(1, 2)
q = q.reshape(batch_size*num_head, seq_len, head_dim)
k = k.reshape(batch_size, seq_len, num_head, head_dim).transpose(1, 2)
k = k.reshape(batch_size*num_head, seq_len, head_dim)
v = v.reshape(batch_size, seq_len, num_head, head_dim).transpose(1, 2)
v = v.reshape(batch_size*num_head, seq_len, head_dim)
第二种方式:
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) # 三个 Q、K、V 分别对应着输出张量的前 dim,中间 dim 和后 dim 部分
qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) # 获取q,k,v的值
# reshape()输出张量重新整形为一个五维张量
q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)