概述

最近对卷积神经网络中的“自适应调整感受野”这样的操作很感兴趣,从字面的意思可以理解:如果可以自适应的调整感受野的大小,其背后说得应该就是自适应的选择卷积核的大小。看上去不难理解,但是仔细想想,大多的网络模型,他的BankBone和Neck和Prediction三个网络组成了神经网络,作为特征提取,卷积核是关键因素,卷积核都是固定设置的,那么如何做到对于输入的特征图,做到自适应的选择卷积核。所以就有了很多的疑惑。并且这项工作在前几年就有人提出,比如我最近看了一篇SKAttention注意力,其中就用到了自适应的选择卷积核去调整感受野。比如动态卷积,同样是抛弃传统的固定卷积核,选择动态的卷积核来做特征提取。
——————————————————————————————————————————————————————————
论文链接:https://arxiv.org/pdf/1903.06586.pdf
在这里插入图片描述
Selective Kernel(SK) Attention — 该文章最大的亮点感觉就在于动态调节其自身的感受野,可以直观的认为,调整感受野也就是自适应的调整卷积核的大小。文章中也提到对于大于1的卷积核会自适应的选择。可能你会想到如果自适应选择很大的卷积核怎么办?岂不是在参数量上面大大提升了,如模型结构图中的U^\widehat{U}U,是由XXX经过Kernel=5∗5Kernel =5*5Kernel=55得到的,但是实际的运算过程中是用dilation=2dilation=2dilation=2kernel=3∗3kernel =3*3kernel=33的空洞卷积实现的。——而SKAttention的自适应选择感受野的原理如下:
过程:具体来说,通过三个步骤实现 SK 卷积——拆分、融合和选择,如模型结构图 所示,其中显示了两个分支的情况。

  • (一)Split拆分:对于任何给定的特征图X∈RH×W×CX ∈ R^{H×W×C}XRH×W×C,经过两次变换 U~\widetilde{U}U :F~\widetilde{F}F =X → 3x3和U^\widehat{U}U : F^\widehat{F}F=X → 5x5。要注意的是,F~\widetilde{F}FF^\widehat{F}F 均由分组或者深度卷积组成,归一化和 激活函数依次运行。为了进一步提高效率,传统的卷积5∗55*555被空洞卷积替换扩张为2,卷积核大小为3∗33*333 。这一步可以认为是在初步的设定下,不同分支不同的卷积核处理输入X特征,得到多分支的处理后的特征图。
  • (二)Fuse融合:基本思想是使用门来控制来自多个分支的信息流携带​​不同尺度的信息进入下一层的神经元。 为了达到这个目标,门需要整合信息从所有分支。我们首先通过元素求和融合来自多个(下图中的两个)分支的结果:U=U~+U^U = \widetilde{U} + \widehat{U}U=U+U,然后UUU经过FgpF_{gp}Fgp全局平均池化
    Fgp(uc)=1H×W∑i=1H∑j=1Wuc(i,j) F_{gp}(u_c) =\frac{1}{H \times W} \sum_{i=1}^{H} \sum_{j=1}^{W} u_c(i,j) Fgp(uc)=H×W1i=1Hj=1Wuc(i,j)
    不得不说,经过全局平均池化特征由H×W×CH\times W\times CH×W×C转换成1×1×C1\times 1\times C1×1×C。此时的C个通道的就是UUU的全局信息。在经过FfcF_{fc}Ffc全连接层,Ffc(s)=δ(B(Ws))F_{fc}(s)= \delta(B(W_s))Ffc(s)=δ(B(Ws)),其中δ\deltaδReLuReLuReLu激活函数,BBB为标准化,WWW可以认为是一个要学习的参数W∈Rd×CW ∈R^{d\times C}WRd×C,为了研究ddd对模型效率的影响,d=max(C/r,L)d = max(C/r,L)d=max(C/r,L),其中LLL表示ddd的最小值,一般取32,实验中设置。
  • (三)Select选择: 在上一步骤中,我们通过全连接层经过softmaxsoftmaxsoftmax函数,获得了两个分支融合后的注意力分数。其实到这块我是有一些疑惑的,因为此时的UUU是经过两个分支融合后的,不再是单独的两个特征,所以通过UUU获得的softmaxsoftmaxsoftmax分数是融合UUU的一个分数。而他却得到了关于a和b的两个分数,有点没明白,可能这个就是和上一步中的d有一定关系。这个疑惑暂且放一放,说说这个a和b(在图中就可以找到a和b),a表示为ac=eAczeAcz+eBcza_c=\frac{e^{A_cz}}{e^{A_cz}+e^{B_cz}}ac=eAcz+eBczeAcz,b表示为bc=eBczeAcz+eBczb_c=\frac{e^{B_cz}}{e^{A_cz}+e^{B_cz}}bc=eAcz+eBczeBcz,原文中提到ac+bc=1a_c+b_c=1ac+bc=1从这里我们就可以知道,比如在c=3的这个通道,如果a为0.6,b为0.4,那么最终特征应该选择的a而不选择b,选择a的意思就是a的这个分数乘以分支过来的特征,最终的特征V(c=3)这个通道选择a。如果c=5这个通道,a=0.2,b=0.8,那么最终的特征V(c=5)这个通道的特征就是b乘以他的分支过来的特征而得到的。最后的V特征,里面既有3∗33*333卷积核得到的注意力特征,也有5∗55*555卷积核得到的注意力特征,这也就是为什么这一步骤叫做slectslectslect,这个设计还是很巧妙的。(这是我对自适应特征选择的理解)
    下面是论文中的一小部分专业术语:
    其中A,B∈RC×dA,B∈R^{C\times d}A,BRC×d,a、b表示U~\widetilde{U}UU^\widehat{U}U的soft attention(softmax得分),Ac∈R1×dA_c∈R^{1\times d}AcR1×d是A的第c行,aca_cac是a的第c个元素。在两个分支的情况下,矩阵B是冗余的,因为ac+bc=1a_c + b_c = 1ac+bc=1。跨通道的软注意力用于自适应选择不同空间尺度的信息,在两个分支的情况下,矩阵B是冗余的,因为ac+bc=1a_c + b_c = 1ac+bc=1。最终的特征图V是通过各种内核上的注意力权重获得:Vc=ac⋅U~c+bc⋅U^c,ac+bc=1V_c = a_c · \widetilde{U}_c + b_c · \widehat{U}_c, a_c + b_c = 1Vc=acUc+bcUc,ac+bc=1其中V=[V1,V2,...,Vc],Vc∈RHXWV = [V_1, V_2, ..., V_c ], V_c∈ R^{HXW}V=[V1,V2,...,Vc],VcRHXW
模型结构

在这里插入图片描述

代码实现
import torch
from torch import nn
from torch.nn import init

class SKAttention(nn.Module):
    def __init__(self, channel=512,kernels=[1,3,5,7],reduction=16,group=1,L=32):
        super().__init__()
        self.d=max(L,channel//reduction)
        self.convs=nn.ModuleList([])
        for k in kernels:
            self.convs.append(
                nn.Sequential(OrderedDict([
                    ('conv',nn.Conv2d(channel,channel,kernel_size=k,padding=k//2,groups=group)),
                    ('bn',nn.BatchNorm2d(channel)),
                    ('relu',nn.ReLU())
                ]))
            )
        self.fc=nn.Linear(channel,self.d)
        self.fcs=nn.ModuleList([])
        for i in range(len(kernels)):
            self.fcs.append(nn.Linear(self.d,channel))
        self.softmax=nn.Softmax(dim=0)

    def forward(self, x):
        bs, c, _, _ = x.size()
        conv_outs=[]
        ### split
        for conv in self.convs:
            conv_outs.append(conv(x))
        feats=torch.stack(conv_outs,0)#k,bs,channel,h,w
        ### fuse
        U=sum(conv_outs) #bs,c,h,w
        ### reduction channel
        S=U.mean(-1).mean(-1) #bs,c
        Z=self.fc(S) #bs,d
        ### calculate attention weight
        weights=[]
        for fc in self.fcs:
            weight=fc(Z)
            weights.append(weight.view(bs,c,1,1)) #bs,channel
        attention_weughts=torch.stack(weights,0)#k,bs,channel,1,1
        attention_weughts=self.softmax(attention_weughts)#k,bs,channel,1,1
        ### fuse
        V=(attention_weughts*feats).sum(0)
        return V
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐