qkv就是Query,Key和Value,这个概念很早就有,随着transformer的爆火,在注意力领域该结构可以说是最典型的、最重要的。我接触深度学习是从视觉领域入手的,调研了很多注意力结构,比如SENet等等。注意力领域也有诸多细分,比如通道注意力、空间注意力等。我总结为注意力结构就是生成一个权重,附加到内容上,效果就是只关注某部分内容,忽略其他内容。如果权重由内容本身生成,就是自注意力机制。
transformer论文地址:https://arxiv.org/abs/1706.03762
qk就是我理解的权重,v就是内容。但我总也理解不了Query和Key的区别,尤其是从代码的角度来看这个问题,特征x过一个全联接层得到Query,过另一个全连接层得到Key,让q和k的概念互换一下似乎也没啥问题吧。代码地址:https://github.com/LiamMaclean216/Pytorch-Transfomer/blob/master/utils.py
class Key(torch.nn.Module):
def __init__(self, dim_input, dim_attn):
super(Key, self).__init__()
self.dim_attn = dim_attn
self.fc1 = nn.Linear(dim_input, dim_attn, bias = False)
#self.fc2 = nn.Linear(5, dim_attn)
def forward(self, x):
x = self.fc1(x)
#x = self.fc2(x)
return x
class Query(torch.nn.Module):
def __init__(self, dim_input, dim_attn):
super(Query, self).__init__()
self.dim_attn = dim_attn
self.fc1 = nn.Linear(dim_input, dim_attn, bias = False)
#self.fc2 = nn.Linear(5, dim_attn)
def forward(self, x):
x = self.fc1(x)
#print(x.shape)
#x = self.fc2(x)
return x
然后我们来看看论文,Q和K有怎么计算呢,仅仅就是Q乘以K的转置,然后再softmax,然后除以维度开根,做个简单的归一化。这种级别的计算是一种简单的结构,但是我认为还是很弯弯绕绕的,没有SENet那么简单好理解,我尤其不能理解Q和K,在我看来就算只有一项也能行吧。
transformer最早应用于NLP领域,很多博客解释这个问题从NLP角度出发,我总结理解不了。图像领域也有很多qkv的结构,ViT、Twins等在CV上把QKV用的得心应手。我仍不能理解对于一个图,QKV对应着什么级别的概念。经过最近的调研,我把自己的理解写下来,欢迎大家讨论、纠错。我把某些具体的含义带入到了推理过程,方便描述QKV的抽象概念。
我的理解
我们通过人类区分猫狗时候的QKV,联想模型区分猫狗时候的QKV。
从人类角度区分猫狗的话,我们可以从以下角度做对比:耳朵的弯曲程度、胡子长度与脸长度的比例、嘴巴长度与脸长度的比例、花纹、眼睛、爪子、尾巴。经过我这几年的观察,我发现了以下规律。耳朵上,弯耳大概率是狗(小部分狗是直耳),直耳大概率是猫;胡子上,胡子长度接近脸长的是大概率猫,胡子短的是大概率狗;嘴长上,嘴长接近脸长的大概率是狗,嘴长明显小于脸长的大概率是猫。
上述是我通过观察总结的经验,现在有一张我没见过的图片,它的特点如下:
我通过查找图片中的对比项,我可以轻松地得出这个动物大概率是猫,即使胡子的长度不是很清晰,不绝对符合猫的特征,能看到的胡子长度是我们之前总结胡子长度的中间值。但是其他两项还是足够支撑我们作出预测。
总结一下上述过程,“经过我这几年的观察”就是模型的训练过程,新图片预测大概率是猫是一次推理过程。QKV隐藏在推理过程中,我们接下来再细致地区分一下加了注意力的推理过程。
推理时,我先看到这个图片,然后我看到直耳、短嘴、胡子长度好像是脸长的四分之三、黄色花纹。那么对于这个图,胡子没啥区分度就不关注了,花纹也不管了。这个图我们只需要关注耳朵和嘴巴位置就行了,得到下面的注意图,黑色部分不关注,只关注白色部分:
Query就是直耳、短嘴、胡子长度这些特征,Key就是耳朵、嘴长、胡子长度这些项。Query与Key运算得到的就是这些特征对于区分猫狗重不重要,重要的就关注,不重要的就不关注。
Key很难理解,我们具体到向量,key向量可以理解为:此图中不同项的不同值的重要性;此时Query向量就是此图中某些项的特征值。Query向量作为索引,key向量就是字典,查到的值就是重要性。
为什么key向量一定是通过全图特征计算得到?我们本来就知道耳朵、嘴巴和胡子是重要的,而花纹是不重要的,只需要一个常量就行了?因为并不是所有的图都有清晰的胡子,这个图的胡子并没有拍清楚,只能看到隐约的长度,那么胡子长度就不是关键的特征,就不需要关注了。
在transformer中,这一部分输出为:
对应到transformer结构图就是红色箭头的位置:
Value是整个图片的特征图,这里我们借用原图展示。整个注意力结构的输入输出如下:
ViT的推理过程实际上要复杂些,首先将图片做patch embedding,还做了位置编码,使用的是multi-head attention,并堆叠多个block。