深度学习笔记-自适应选择感受野

概述

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

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

模型结构

深度学习笔记-自适应选择感受野_第2张图片

代码实现

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

你可能感兴趣的:(深度学习专栏,深度学习,神经网络)