论文:https://arxiv.org/abs/2105.02358
大部分内容来自论文作者 国孟昊 的B站视频,讲解的非常细致,强烈推荐
典型的 self-attention 计算方法如下图所示。 \(Q\) 和 \(K\) 相乘,使用 softmax 归一化,然一和\(V\)相乘,然后得到输出的特征,这个大家应该都相当熟悉了。
第一类是子空间方法。 如下图所示,研究人员发现,当 query 落在车上时, 得到的结果中车的响应比较强(第1行);当query落在人上时,行人的响应比较强(第2行);落在道路上时,道路上的响应就比较强(第3行)。因此,可以把像素点聚为一些代表性的点,在代表性的点上计算相似性。典型的方法是 EMANet (ICCV 2019),OCRNet(ECCV2020)。 还有一类方法是进行矩阵分解来降低计算复杂度, 典型的方法是 Performer。
在这个工作中,作者提出使用两个记忆模块计算 attention,具体为建立一个共享空间\(M\),把输入特征\(F\) 映射到 \(M\),再恢复,如下图所示:
实现时是通过两个线性层来替代的,具体如下:
算法的伪代码如下图所示。其实中的关注一步就是双归一化,先使用 Softmax 归一化,然后再使用 l1_norm 归一化。作者也说,这个双归一化是相当关键的,相对于只使用 Softmax 归一化,能够显著提升性能。
在PASCAL VOC 数据集上的语义分割结果如下表所示,可以看出,使用双归一化可以显著提升性能。 作者也表示,external attention 非常依赖这种双归一化方式,但作者也指出,归一化的方法和安排次序,仍然是相当值得关注的问题。
通过查看作者的源代码,可以看出,两个归一化所处理的维度不一样,softmax 是作用在 n 上,l1_norm是作用在 k 上。其实我不是特别清楚这么设计的原因,如果有网友比较清楚可以随时交流。
x = x.view(b, c, h*w) # b, c, n
attn = self.linear0(x) # b, k, n
attn = F.softmax(attn, dim=-1) # b, k, n
attn = attn / (1e-9 + attn.sum(dim=1, keepdim=True)) # b, k, n
x = self.linear1(attn) # b, c, n
x = x.view(b, c, h, w)