《Squeeze-and-Excitation Networks》

Squeeze-and-Excitation Networks

1. 摘要

  • 卷积操作可以使网络通过在每层的局部感受野内融合空间和通道级信息以构建信息特征;
  • 空间信息尝试通过加强CNN整个特征层次的空间编码质量以加强CNN的表示能力;
  • 本文主要聚焦于通道级信息,并提出SE块,该模块可以通过明确建模通道之间的相互依赖关系自适应调整通道级特征
  • 可通过堆栈式叠加SE块,构建SE网络架构;
  • 代码地址:https://github.com/hujie-frank/SENet.
  • 原文链接:Squeeze-and-Excitation Networks

2. 介绍

  • CNN的每层中的滤波器集合表示沿着输入通道的领域空间连接模式;
  • CNN网络可以产生图像表示、捕捉层次模式、获取理论的全局感受野;
  • 作者从通道间的关系角度进行网络设计:1)提出SE块:通过对卷积特征通道间的相互依赖关系进行明确建模,以提高网络表示能力;2)提出一个可让网络进行特征重新校正的机制,该机制可以学习使用全局信息有选择地强调信息特征,并抑制不太有用的信息;
    在这里插入图片描述
  • 特征U(H*W*C)首先经过压缩操作Fsq,通过聚合空间维度的特征映射产生一个通道描述符(1*1*C);
  • 通道描述符的函数用于产生一个通道级特征的全局分布的嵌入,允许来自网络的全局感受野的信息可以被所有网络层使用;
  • 通道描述符后接激励操作,该操作采用一个自门控的机制。以通道描述符的输出(全局分布的嵌入)作为输入,产生一个每通道调制权重的集合(1*1*C);
  • 激励操作的输出权重结合特征U得到SE块的最终输出特征;
  • SE块可用于堆叠构建SE网络,也可直接替换某现有网络中的模块(插入在非线性层之后);
    在这里插入图片描述
  • SE块在网络不同深度处作用不同,浅层处以不知类的方式激励特征加强低级特征表示,深层处会以特定于类的方式处理不同的输入特征;

3. 网络设计

  • Ftr可使用卷积操作表示,每个卷积核的参数中混合了局部空间相关性和通道间依赖关系,其中通道间依赖关系是隐式学习的;
    在这里插入图片描述

  • 本文研究旨在显式学习通道间依赖关系,增强卷积特征的学习;

  • 所谓的特征校正机制,我理解就是为原始卷积特征进行压缩、激励操作学习一个各通道对应的权重,然后对原始特征进行一个校正(通道级乘法);

  • 压缩操作:卷积结果中的每个点代表上层特征的一个局部表示,无法利用全局信息,该操作(利用全局平均池化操作实现)将全局信息压缩为一个通道表示器;实质就是将U中每个通道的所有像素点进行平均,得到各通道的一个平均全局表示;
    在这里插入图片描述

  • 激励操作:用于利用前面压缩操作聚合的信息,完全捕捉通道级依赖;
    建立激励操作需要遵循两个标准:1)该函数必须是灵活的,必须可以学习通道间的非线性相互作用;2)该函数一定可以学习一个非互斥关系,因为通道间是具有相互关系的,通道注意力要做的是强调某些通道信息或抑制某些通道信息;
    使用具有Sigmoid激活的简单门控机制实现激励操作:
    在这里插入图片描述
    实际通过两个FC层(全卷积层)形成的瓶颈结构实现,一层用于降维,一层用于恢复维度(FC+ReLU+FC+Sigmoid)。

  • SE块的输出为原始特征U与激励操作的输出S进行通道级乘法对应结果:
    在这里插入图片描述

4. 代码实现

# SE注意力机制
class SE_Block(nn.Module):
    def __init__(self,r,feature_h,feature_w,feature_c):
        super(SE_Block, self).__init__()
        self.avg_pool=nn.AvgPool2d(kernel_size=(feature_h, feature_w), stride=1)
        self.block=nn.Sequential(
            nn.Linear(in_features=feature_c, out_features=int(feature_c / r)),  # 输入特征形状为:N,in_features
            nn.ReLU(inplace=False),
            nn.Linear(in_features=int(feature_c / r), out_features=feature_c),
            nn.Sigmoid()
        )

    def forward(self, x):
        n=x.shape[0]
        c=x.shape[1]
        avg_x = self.avg_pool(x)
        # print(avg_x.shape)
        y=self.block(avg_x.view(n,c)).view(n,c,1,1)
        return y*x


if __name__=='__main__':
    # 模拟tensor
    # data:1,,5,5,16
    # r=2
    device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
    data=np.array(np.random.uniform(0,255,(1,16,5,5)))
    img=torch.tensor(data,dtype=torch.float32)
    img=img.to(device)
    print("输入形状",img.shape)
    r=2
    se_block=SE_Block(r,img.shape[2],img.shape[3],img.shape[1])
    print(se_block)

    se_block.to(device)
    out=se_block(img)
    print("输出形状",out.shape)

猜你喜欢

转载自blog.csdn.net/qq_43665602/article/details/130098682
今日推荐