2021 CVPR | EPSANet一种金字塔拆分注意力机制(keras实现)

EPSANet: An Efficient Pyramid Split Attention Block on Convolutional Neural Network 

paper:https://arxiv.org/pdf/2105.14447v1.pdf

code:https://github.com/murufeng/EPSANet

摘要

近年来,研究表明,通过加入注意力机制可以有效地提高深度卷积神经网络的性能。本文提出了一种新的轻量级和有效的注意方法——金字塔拆分注意(PSA)模块。通过用ResNet瓶颈块中的PSA模块代替3x3卷积,得到了一种名为有效金字塔拆分注意(EPSA)的新表示块。EPSA模块可以很容易地作为一个即插即用组件添加到一个完善的主干网络中,并可以实现模型性能的显著改进。因此,通过堆叠这些ResNet-resnet风格的EPSA块,开发了一个名为EPSANet的简单而高效的主干体系结构。相应地,EPSANet可以为各种计算机视觉任务提供更强的多尺度表示能力,包括但不限于图像分类、目标检测、实例分割等能力。如果没有其他一些优化措施,所提议的EPSANet的性能优于大多数最先进的通道注意方法。与SENet-50相比,ImageNet数据集上的Top 1精度提高了1.93%,目标检测的box 获得+2.7AP,以及在MS-COCO数据集上比Mask-RCNN的分割精度提高了+1.7AP。

论文背景

注意机制广泛应用于图像分类、目标检测、实例分割、语义分割、场景分析和动作定位等许多计算机视觉领域。具体来说,注意方法有两种类型,分别是通道注意和空间注意。最近,研究证明,通过使用通道注意、空间注意或两者中的,可以实现显著的性能改进。最常用的通道注意方法是SENet,它可以以相当低的成本显著提高性能。SENet的缺点是它忽略了空间信息的重要性。因此,提出BAM模块和CBAM模块,通过有效地结合空间和通道注意,来丰富注意力图。然而,仍存在两个重要而具有挑战性的问题需要解决。第一个是如何有效地捕获和利用不同尺度的特征地图的空间信息来丰富特征空间。第二种情况是,信道或空间注意力只能有效地捕获局部信息,但无法建立远程信道依赖关系。

论文主要思想

 PSA模块: 

通过使用不同大小的核卷积将输入特征图拆分成多尺度特征图,并且在不同尺度上分别使用通道注意力SE模块,让网络关注不同尺度下的特征。然后,将不同尺度上的特征合并,使用SoftMax重新校准通道方面的注意力向量,得到多尺度通道的权重。最后,将多尺度的权重作用于相应的特征图,得到一个更丰富的多尺度特征信息的细化特征图作为输出。

keras实现

以下是根据论文和pytorch源码实现的keras版本(支持Tensorflow1.x)。


def _PSA(inputs, filters, name, conv_kernels=[3, 5, 7, 9], conv_groups=[1, 4, 8, 16]):
    assert len(conv_kernels) == len(conv_groups)
    split_num = len(conv_kernels)
    channel_axis = 1 if K.image_data_format() == 'channels_first' else -1
    in_dim = K.int_shape(inputs) # 计算输入特征图的通道数
    split_channel = filters // len(conv_kernels)
    mult_scals = []
    feature_se = []
    for i,  kernel_group in enumerate(zip(conv_kernels, conv_groups)):
        kernel, group = kernel_group
        feature = _group_conv(inputs, split_channel, kernel, 1, group) # group convolution
        mult_scals.append(feature)
        se_name = name + '_' + str(i)
        feature_se.append(_SE(feature, se_name))
    feature_attention = Concatenate(axis=channel_axis)(feature_se)
    features = Concatenate(axis=channel_axis)(mult_scals)
    if channel_axis == -1:
        attention_vectors = Reshape((1, 1, split_channel, split_num))(feature_attention)
        features = Reshape((in_dim[1], in_dim[2], split_channel, split_num))(features)
        attention_vectors = Activation('softmax')(attention_vectors)
        feats_weight = Multiply()([attention_vectors, features])
        feats_weight = Reshape((in_dim[1], in_dim[2], filters))(feats_weight)
    else:
        attention_vectors = Reshape((split_num, split_channel, 1, 1))(feature_attention)
        features = Reshape((split_num, split_channel, in_dim[3], in_dim[2]))(features)
        attention_vectors = Activation('softmax')(attention_vectors)
        feats_weight = Multiply()([attention_vectors, features])
        feats_weight = Reshape((filters, in_dim[3], in_dim[2]))(feats_weight)
    return feats_weight
def _SE(inputs, name, reduction=16):
    channel_axis = 1 if K.image_data_format() == 'channels_first' else -1
    out_dim = K.int_shape(inputs)[channel_axis]  # 计算输入特征图的通道数
    temp_dim = max(out_dim // reduction, reduction)

    squeeze = GlobalAvgPool2D(name=name+'_GlobalAvgPool2D')(inputs)
    if channel_axis == -1:
        excitation = Reshape((1, 1, out_dim), name=name + '_Reshape')(squeeze)
    else:
        excitation = Reshape((out_dim, 1, 1), name=name + '_Reshape')(squeeze)
    excitation = Conv2D(temp_dim, 1, 1, activation='relu', name=name + '_Conv2D_1')(excitation)
    excitation = Conv2D(out_dim, 1, 1, activation='sigmoid', name=name + '_Conv2D_2')(excitation)
    return excitation

def _group_conv(x, filters, kernel, stride, groups, padding='same'):
    channel_axis = 1 if K.image_data_format() == 'channels_first' else -1
    in_channels = K.int_shape(x)[channel_axis]  # 计算输入特征图的通道数
    nb_ig = in_channels // groups  # 对输入特征图通道进行分组
    nb_og = filters // groups  # 对输出特征图通道进行分组
    assert in_channels % groups == 0
    assert filters % groups == 0
    assert filters > groups
    gc_list = []
    for i in range(groups):
        if channel_axis == -1:
            x_group = Lambda(lambda z: z[:, :, :, i * nb_ig: (i + 1) * nb_ig])(x)
        else:
            x_group = Lambda(lambda z: z[:, i * nb_ig: (i + 1) * nb_ig, :, :])(x)
        gc_list.append(Conv2D(filters=nb_og, kernel_size=kernel, strides=stride,
                              padding=padding, use_bias=False)(x_group))
    return Concatenate(axis=channel_axis)(gc_list) if groups != 1 else gc_list[0]

声明:本内容来源网络,版权属于原作者,图片来源原论文。如有侵权,联系删除。

创作不易,欢迎大家点赞评论收藏关注!(想看更多最新的注意力机制文献欢迎关注浏览我的博客)

  • 10
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值