转载:真正的即插即用!盘点11种CNN网络设计中精巧通用的“小”插件

注意力机制(SE、Coordinate Attention、CBAM、ECA,SimAM)、即插即用的模块整理 总结曾经使用过的一些即插即用的模块以及一些注意力机制 注意力模块:SE 代码源自这位大佬的仓库:https://github.com/moskomule/senet.pytorch class SELayer(nn.Module): def __init__(self, channel, reduction=16): super(SELayer, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) 阅读详情

原文链接:https://mp.weixin.qq.com/s/kR5ZpZ7PcpLMA4PTtDIoLA

导读

本文盘点一些CNN网络中设计比较精巧而又实用的“插件”。所谓“插件”,就是不改变网络主体结构, 可以很容易嵌入到主流网络当中,提高网络提取特征的能力,能够做到plug-and-play。网络也有很多类似盘点工作,都宣称所谓的即插即用、无痛涨点。不过根据笔者经验和收集,发现很多插件都是不实用、不通用、甚至不work的,于是有了这一篇。

首先,我的认识是:既然是“插件”,就要是锦上添花的,又容易植入,容易落地的,真正的即插即用。本文盘点的“插件”,在很多SOTA网络中会看到它们的影子。是值得推广的良心“插件”,真正能做到plug-and-play。总之一句话,就是能够work的“插件”。很多“插件”都为提升CNN能力而推出的,例如平移、旋转、scale等变性能力,多尺度特征提取能力,感受野等能力,感知空间位置能力等等。

入围名单:STN、ASPP、Non-local、SE、CBAM、DCNv1&v2、CoordConv、Ghost、BlurPool、RFB、ASFF

1 STN

出自论文:Spatial Transformer Networks

论文链接:https://arxiv.org/pdf/1506.02025.pdf

核心解析:

在OCR等任务中,你会经常看到它的身影。对于CNN网络,我们希望其具有对物体的姿态、位置等有一定的不变性。即在测试集上可以适应一定的姿态、位置的变化。不变性或等变性可以有效提高模型泛化能力。虽然CNN使用sliding-window卷积操作,在一定程度上具有平移不变性。但很多研究发现,下采样会破坏网络的平移不变性。所以可以认为网络的不变性能力非常弱,更不用说旋转、尺度、光照等不变性。一般我们利用数据增强来实现网络的“不变性”。

本文提出STN模块,显式将空间变换植入到网络当中,进而提高网络的旋转、平移、尺度等不变性。可以理解为“对齐”操作。STN的结构如上图所示,每一个STN模块由Localisation net,Grid generator和Sampler三部分组成。Localisation net用于学习获取空间变换的参数,就是上式中的六个参数。Grid generator用于坐标映射。Sampler用于像素的采集,是利用双线性插值的方式进行。

STN的意义是能够把原始的图像纠正成为网络想要的理想图像,并且该过程为无监督的方式进行,也就是变换参数是自发学习获取的,不需要标注信息。该模块是一个独立模块,可以在CNN的任何位置插入。符合本次“插件”的盘点要求。

核心代码:


class SpatialTransformer(nn.Module):
    def __init__(self, spatial_dims):
        super(SpatialTransformer, self).__init__()
        self._h, self._w = spatial_dims 
        self.fc1 = nn.Linear(32*4*4, 1024) # 可根据自己的网络参数具体设置
        self.fc2 = nn.Linear(1024, 6)

    def forward(self, x): 
        batch_images = x #保存一份原始数据
        x = x.view(-1, 32*4*4)
        # 利用FC结构学习到6个参数
        x = self.fc1(x)
        x = self.fc2(x) 
        x = x.view(-1, 2,3) # 2x3
        # 利用affine_grid生成采样点
        affine_grid_points = F.affine_grid(x, torch.Size((x.size(0), self._in_ch, self._h, self._w)))
        # 将采样点作用到原始数据上
        rois = F.grid_sample(batch_images, affine_grid_points)
        return rois, affine_grid_points
 

2 ASPP

插件全称:atrous spatial pyramid pooling

出自论文:DeepLab: Semantic Image Segmentation with Deep Convolutional Nets, Atrous Conv

论文链接:https://arxiv.org/pdf/1606.00915.pdf

核心解析:

本插件是带有空洞卷积的空间金字塔池化模块,主要是为了提高网络的感受野,并引入多尺度信息而提出的。我们知道,对于语义分割网络,通常面临是分辨率较大的图片,这就要求我们的网络有足够的感受野来覆盖到目标物体。对于CNN网络基本是靠卷积层的堆叠加上下采样操作来获取感受野的。本文的该模块可以在不改变特征图大小的同时控制感受野,这有利于提取多尺度信息。其中rate控制着感受野的大小,r越大感受野越大。

ASPP主要包含以下几个部分:1. 一个全局平均池化层得到image-level特征,并进行1X1卷积,并双线性插值到原始大小;2. 一个1X1卷积层,以及三个3X3的空洞卷积;3. 将5个不同尺度的特征在channel维度concat在一起,然后送入1X1的卷积进行融合输出。

核心代码:

class ASPP(nn.Module):
    def __init__(self, in_channel=512, depth=256):
        super(ASPP,self).__init__()
        self.mean = nn.AdaptiveAvgPool2d((1, 1))
        self.conv = nn.Conv2d(in_channel, depth, 1, 1)
        self.atrous_block1 = nn.Conv2d(in_channel, depth, 1, 1)
        # 不同空洞率的卷积
        self.atrous_block6 = nn.Conv2d(in_channel, depth, 3, 1, padding=6, dilation=6)
        self.atrous_block12 = nn.Conv2d(in_channel, depth, 3, 1, padding=12, dilation=12)
        self.atrous_block18 = nn.Conv2d(in_channel, depth, 3, 1, padding=18, dilation=18)
        self.conv_1x1_output = nn.Conv2d(depth * 5, depth, 1, 1)

    def forward(self, x):
        size = x.shape[2:]
     # 池化分支
        image_features = self.mean(x)
        image_features = self.conv(image_features)
        image_features = F.upsample(image_features, size=size, mode='bilinear')
     # 不同空洞率的卷积
        atrous_block1 = self.atrous_block1(x)
        atrous_block6 = self.atrous_block6(x)
        atrous_block12 = self.atrous_block12(x)
        atrous_block18 = self.atrous_block18(x)
        # 汇合所有尺度的特征
     x = torch.cat([image_features, atrous_block1, atrous_block6,atrous_block12, atrous_block18], dim=1)
        # 利用1X1卷积融合特征输出
        x = self.conv_1x1_output(x)
        return net

3 Non-local

出自论文:Non-local Neural Networks

论文链接:https://arxiv.org/abs/1711.07971

核心解析:

Non-Local是一种attention机制,也是一个易于植入和集成的模块。Local主要是针对感受野(receptive field)来说的,以CNN中的卷积操作和池化操作为例,它的感受野大小就是卷积核大小,而我们常用3X3的卷积层进行堆叠,它只考虑局部区域,都是local的运算。不同的是,non-local操作感受野可以很大,可以是全局区域,而不是一个局部区域。捕获长距离依赖(long-range dependencies),即如何建立图像上两个有一定距离的像素之间的联系,是一种注意力机制。所谓注意力机制就是利用网络生成saliency map,注意力对应的是显著性区域,是需要网络重点关注的区域。

  • 首先分别对输入的特征图进行 1X1的卷积来压缩通道数,得到,特征。

  • 通过reshape操作,转化三个特征的维度,然后对进行矩阵乘操作,得到类似协方差矩阵, 这一步为了计算出特征中的自相关性,即得到每帧中每个像素对其他所有帧所有像素的关系。

  • 然后对自相关特征进行 Softmax 操作,得到0~1的weights,这里就是我们需要的 Self-attention系数。

  • 最后将 attention系数,对应乘回特征矩阵g上,与原输入 feature map X 残差相加输出即可。

这里我们结合一个简单例子理解一下,假设g为(我们暂时不考虑batch和channel维度):

g = torch.tensor([[1, 2],                           [3, 4]).view(-1, 1).float()

\Theta为:

theta = torch.tensor([2, 4, 6, 8]).view(-1, 1)

\Phi为:

phi = torch.tensor([7, 5, 3, 1]).view(1, -1)

那么,和矩阵相乘如下:

tensor([[14., 10.,  6.,  2.],                [28., 20., 12.,  4.],                [42., 30., 18.,  6.],                [56., 40., 24.,  8.]])

进过softmax(dim=-1)后如下,每一行代表着g里面的元素的重要程度,每一行前面的值比较大,因此希望多“注意”到g前面的元素,也就是1比较重要一点。或者这样理解:注意力矩阵代表着g中每个元素和其他元素的依赖程度。

tensor([[9.8168e-01, 1.7980e-02, 3.2932e-04, 6.0317e-06],                [9.9966e-01, 3.3535e-04, 1.1250e-07, 3.7739e-11],                [9.9999e-01, 6.1442e-06, 3.7751e-11, 2.3195e-16],                [1.0000e+00, 1.1254e-07, 1.2664e-14, 1.4252e-21]])

注意力作用上之后,整体值向原始g中的值都向1靠拢:

tensor([[1.0187, 1.0003],                [1.0000, 1.0000]])

核心代码:

class NonLocal(nn.Module):
    def __init__(self, channel):
        super(NonLocalBlock, self).__init__()
        self.inter_channel = channel // 2
        self.conv_phi = nn.Conv2d(channel, self.inter_channel, 1, 1,0, False)
        self.conv_theta = nn.Conv2d(channel, self.inter_channel, 1, 1,0, False)
        self.conv_g = nn.Conv2d(channel, self.inter_channel, 1, 1, 0, False)
        self.softmax = nn.Softmax(dim=1)
        self.conv_mask = nn.Conv2d(self.inter_channel, channel, 1, 1, 0, False)

    def forward(self, x):
        # [N, C, H , W]
        b, c, h, w = x.size()
        # 获取phi特征,维度为[N, C/2, H * W],注意是要保留batch和通道维度的,是在HW上进行的
        x_phi = self.conv_phi(x).view(b, c, -1)
        # 获取theta特征,维度为[N, H * W, C/2]
        x_theta = self.conv_theta(x).view(b, c, -1).permute(0, 2, 1).contiguous()
        # 获取g特征,维度为[N, H * W, C/2]
        x_g = self.conv_g(x).view(b, c, -1).permute(0, 2, 1).contiguous()
        # 对phi和theta进行矩阵乘,[N, H * W, H * W]
        mul_theta_phi = torch.matmul(x_theta, x_phi)
        # softmax拉到0~1之间
        mul_theta_phi = self.softmax(mul_theta_phi)
        # 与g特征进行矩阵乘运算,[N, H * W, C/2]
        mul_theta_phi_g = torch.matmul(mul_theta_phi, x_g)
        # [N, C/2, H, W]
        mul_theta_phi_g = mul_theta_phi_g.permute(0, 2, 1).contiguous().view(b, self.inter_channel, h, w)
        # 1X1卷积扩充通道数
        mask = self.conv_mask(mul_theta_phi_g)
        out = mask + x # 残差连接
        return out

4 SE

出自论文:Squeeze-and-Excitation Networks

论文链接:https://arxiv.org/pdf/1709.01507.pdf

核心解析:

本文是ImageNet最后一届比赛的冠军作品,你会在很多经典网络结构中看到它的身影,例如Mobilenet v3。其实是一种通道注意力机制。由于特征压缩和FC的存在,其捕获的通道注意力特征是具有全局信息的。本文提出了一种新的结构单元——“Squeeze-and Excitation(SE)”模块,可以自适应的调整各通道的特征响应值,对通道间的内部依赖关系进行建模。有以下几个步骤:

  • Squeeze: 沿着空间维度进行特征压缩,将每个二维的特征通道变成一个数,是具有全局的感受野。

  • Excitation: 每个特征通道生成一个权重,用来代表该特征通道的重要程度。

  • Reweight:将Excitation输出的权重看做每个特征通道的重要性,通过相乘的方式作用于每一个通道上。

核心代码:

class SE_Block(nn.Module):
    def __init__(self, ch_in, reduction=16):
        super(SE_Block, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)  # 全局自适应池化
        self.fc = nn.Sequential(
            nn.Linear(ch_in, ch_in // reduction, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(ch_in // reduction, ch_in, bias=False),
            nn.Sigmoid()
        )

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x).view(b, c) # squeeze操作
        y = self.fc(y).view(b, c, 1, 1) # FC获取通道注意力权重,是具有全局信息的
        return x * y.expand_as(x) # 注意力作用每一个通道上

5 CBAM

出自论文:CBAM: Convolutional Block Attention Module

论文链接:https://openaccess.thecvf.com/content_ECCV_2018/papers/Sanghyun_Woo_Convolutional_Block_Attention_ECCV_2018_paper.pdf

SENet在feature map的通道上进行attention权重获取,然后与原来的feature map相乘。这篇文章指出,该种attention方法法只关注了通道层面上哪些层会具有更强的反馈能力,但是在空间维度上并不能体现出attention。CBAM作为本文的亮点,将attention同时运用在channel和spatial两个维度上, CBAM与SE Module一样,可以嵌入在大部分的主流网络中,在不显著增加计算量和参数量的前提下能提升模型的特征提取能力。

通道注意力: 如上图所示,输入是一个 H×W×C 的特征F,我们先分别进两个空间的全局平均池化和最大池化得到 两个 1×1×C 的通道描述。再将它们分别送进一个两层的神经网络,第一层神经元个数为 C/r,激活函数为 Relu,第二层神经元个数为 C。注意,这个两层的神经网络是共享的。然后,再将得到的两个特征相加后经过一个 Sigmoid 激活函数得到权重系数 Mc。最后,拿权重系数和 原来的特征 F 相乘即可得到缩放后的新特征。伪代码:

def forward(self, x):    # 利用FC获取全局信息,和Non-local的矩阵相乘本质上式一样的    avg_out = self.fc2(self.relu1(self.fc1(self.avg_pool(x))))    max_out = self.fc2(self.relu1(self.fc1(self.max_pool(x))))    out = avg_out + max_out    return self.sigmoid(out)

空间注意力: 与通道注意力相似,给定一个 H×W×C 的特征 F‘,我们先分别进行一个通道维度的平均池化和最大池化得到两个 H×W×1 的通道描述,并将这两个描述按照通道拼接在一起。然后,经过一个 7×7 的卷积层, 激活函数为 Sigmoid,得到权重系数 Ms。最后,拿权重系数和特征 F’ 相乘即可得到缩放后的新特征。伪代码:

def forward(self, x):    # 这里利用池化获取全局信息    avg_out = torch.mean(x, dim=1, keepdim=True)    max_out, _ = torch.max(x, dim=1, keepdim=True)    x = torch.cat([avg_out, max_out], dim=1)    x = self.conv1(x)    return self.sigmoid(x)

6 DCN v1&v2

插件全称:Deformable Convolutional

出自论文:

v1: [Deformable Convolutional Networks]

https://arxiv.org/pdf/1703.06211.pdf

v2: [Deformable ConvNets v2: More Deformable, Better Results]

https://arxiv.org/pdf/1811.11168.pdf

核心解析:

变形卷积可以看作变形+卷积两个部分,因此可以当作插件使用。在各大主流检测网络中,变形卷积真是涨点神器,网上解读也非常之多。和传统的固定窗口的卷积相比,变形卷积可以有效地对几何图形,因为它的“局部感受野”是可学习的,面向全图的。这篇论文同时提出了deformable ROI pooling,这两个方法都是增加额外偏移量的空间采样位置,不需要额外的监督,是自监督的过程。

如上图所示,a为不同的卷积,b为变形卷积,深色的点为卷积核实际采样的位置,和“标准的”位置有一定的偏移。c和d为变形卷积的特殊形式,其中c为我们常见到的空洞卷积,d为具有学习旋转特性的卷积,也具备提升感受野的能力。

变形卷积和STN过程非常类似,STN是利用网络学习出空间变换的6个参数,对特征图进行整体变换,旨在增加网络对形变的提取能力。DCN是利用网络学习数整图offset,比STN的变形更“全面一点”。STN是仿射变换,DCN是任意变换。公式不贴了,可以直接看代码实现过程。

变形卷积具有V1和V2两个版本,其中V2是在V2的基础上进行改进,除了采样offset,还增加了采样权重。V2认为3X3采样点也应该具有不同的重要程度,因此该处理方法更具有灵活性和拟合能力。

核心代码:

def forward(self, x):
    # 学习出offset,包括x和y两个方向,注意是每一个channel中的每一个像素都有一个x和y的offset
    offset = self.p_conv(x)
    if self.v2: # V2的时候还会额外学习一个权重系数,经过sigmoid拉到0和1之间
        m = torch.sigmoid(self.m_conv(x))
    # 利用offset对x进行插值,获取偏移后的x_offset
    x_offset = self.interpolate(x,offset)
    if self.v2: # V2的时候,将权重系数作用到特征图上
        m = m.contiguous().permute(0, 2, 3, 1)
        m = m.unsqueeze(dim=1)
        m = torch.cat([m for _ in range(x_offset.size(1))], dim=1)
        x_offset *= m
    out = self.conv(x_offset) # offset作用后,在进行标准的卷积过程
    return out

7 CoordConv

出自论文:An intriguing failing of convolutional neural networks and the CoordConv solution

论文链接:https://arxiv.org/pdf/1807.03247.pdf

图片

核心解析:在Solo语义分割算法和Yolov5中你可以看到它的身影。本文从几个小实验为出发点,探究了卷积网络在坐标变换上的能力。就是它无法将空间表示转换成笛卡尔空间中的坐标。如下图所示,我们向一个网络中输入(i, j)坐标,要求它输出一个64×64的图像,并在坐标处画一个正方形或者一个像素,然而网络在测试集上却无法完成。虽然这项任务是我们人类认为极其简单的工作。分析原因是卷积作为一种局部的、共享权重的过滤器应用到输入上时,它是不知道每个过滤器在哪,无法捕捉位置信息的。因此我们可以帮助卷积,让它知道过滤器的位置。仅仅需要在输入上添加两个通道,一个是i坐标,另一个是j坐标。具体做法如上图所示,送入滤波器之前增加两个通道。这样,网络就具备了空间位置信息的能力,是不是很神奇?你可以随机在分类、分割、检测等任务中使用这种挂件。

如上面第一组图片,传统的CNN在根据坐标数值生成图像的任务中,训练集很好,测试集一团糟。第二组图片增加了 CoordConv 之后可以轻松完成该任务,可见其增加了CNN空间感知的能力。

核心代码:

ins_feat = x # 当前实例特征tensor
# 生成从-1到1的线性值
x_range = torch.linspace(-1, 1, ins_feat.shape[-1], device=ins_feat.device)
y_range = torch.linspace(-1, 1, ins_feat.shape[-2], device=ins_feat.device)
y, x = torch.meshgrid(y_range, x_range) # 生成二维坐标网格
y = y.expand([ins_feat.shape[0], 1, -1, -1]) # 扩充到和ins_feat相同维度
x = x.expand([ins_feat.shape[0], 1, -1, -1])
coord_feat = torch.cat([x, y], 1) # 位置特征
ins_feat = torch.cat([ins_feat, coord_feat], 1) # concatnate一起作为下一个卷积的输入

8 Ghost

插件全称:Ghost module

出自论文:GhostNet: More Features from Cheap Operations

论文链接:https://arxiv.org/pdf/1911.11907.pdf

核心解析:

在ImageNet的分类任务上,GhostNet在相似计算量情况下Top-1正确率达75.7%,高于MobileNetV3的75.2%。其主要创新点就是提出了Ghost 模块。在CNN模型中,特征图是存在大量的冗余,当然这也是非常重要和有必要的。如下图所示,其中标“小扳手”的特征图都存在冗余的特征图。那么能否降低卷积的通道数,然后利用某种变换生成冗余的特征图?事实上这就是GhostNet的思路。

而本文就从特征图冗余问题出发,提出一个仅通过少量计算(论文称为cheap operations)就能生成大量特征图的结构——Ghost Module。而cheap operations就是线性变换,论文中采用卷积操作实现。具体过程如下:

  • 使用比原始更少量卷积运算,比如正常用64个卷积核,这里就用32个,减少一半的计算量。

  • 利用深度分离卷积,从上面生成的特征图中变换出冗余的特征。

  • 上面两步获取的特征图concat起来输出,送入后续的环节。

核心代码:

class GhostModule(nn.Module):
    def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True):
        super(GhostModule, self).__init__()
        self.oup = oup
        init_channels = math.ceil(oup / ratio)
        new_channels = init_channels*(ratio-1)

        self.primary_conv = nn.Sequential(
            nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size//2, bias=False),
            nn.BatchNorm2d(init_channels),
            nn.ReLU(inplace=True) if relu else nn.Sequential(), )
  # cheap操作,注意利用了分组卷积进行通道分离
        self.cheap_operation = nn.Sequential(
            nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size//2, groups=init_channels, bias=False),
            nn.BatchNorm2d(new_channels),
            nn.ReLU(inplace=True) if relu else nn.Sequential(),)

    def forward(self, x):
        x1 = self.primary_conv(x)  #主要的卷积操作
        x2 = self.cheap_operation(x1) # cheap变换操作
        out = torch.cat([x1,x2], dim=1) # 二者cat到一起
        return out[:,:self.oup,:,:]

9 BlurPool

出自论文:Making Convolutional Networks Shift-Invariant Again

论文链接:https://arxiv.org/abs/1904.11486

核心解析:

我们都知道,基于滑动窗口的卷积操作是具有平移不变性的,因此也默认为CNN网络具有平移不变性或等变性,事实上真的如此吗?实践发现,CNN网络真的非常敏感,只要输入图片稍微改一个像素,或者平移一个像素,CNN的输出就会发生巨大的变化,甚至预测错误。这可是非常不具有鲁棒性的。一般情况下我们利用数据增强获取所谓的不变性。本文研究发现,不变性的退化根本原因就在于下采样,无论是Max Pool还是Average Pool,抑或是stride>1的卷积操作,只要是涉及步长大于1的下采样,均会导致平移不变性的丢失。具体示例如下图所示,仅仅平移一个像素,Max pool的结果就差距很大。

为了保持平移不变性,可以在下采样之前进行低通滤波。传统的max pool可以分解为两部分,分别是stride = 1的max + 下采样 。因此作者提出的MaxBlurPool = max + blur + 下采样来替代原始的max pool。实验发现,该操作虽然不能彻底解决平移不变性的丢失,但是可以很大程度上缓解。

核心代码:

class BlurPool(nn.Module):
    def __init__(self, channels, pad_type='reflect', filt_size=4, stride=2, pad_off=0):
        super(BlurPool, self).__init__()
        self.filt_size = filt_size
        self.pad_off = pad_off
        self.pad_sizes = [int(1.*(filt_size-1)/2), int(np.ceil(1.*(filt_size-1)/2)), int(1.*(filt_size-1)/2), int(np.ceil(1.*(filt_size-1)/2))]
        self.pad_sizes = [pad_size+pad_off for pad_size in self.pad_sizes]
        self.stride = stride
        self.off = int((self.stride-1)/2.)
        self.channels = channels
    # 定义一系列的高斯核
        if(self.filt_size==1):
            a = np.array([1.,])
        elif(self.filt_size==2):
            a = np.array([1., 1.])
        elif(self.filt_size==3):
            a = np.array([1., 2., 1.])
        elif(self.filt_size==4):    
            a = np.array([1., 3., 3., 1.])
        elif(self.filt_size==5):    
            a = np.array([1., 4., 6., 4., 1.])
        elif(self.filt_size==6):    
            a = np.array([1., 5., 10., 10., 5., 1.])
        elif(self.filt_size==7):    
            a = np.array([1., 6., 15., 20., 15., 6., 1.])

        filt = torch.Tensor(a[:,None]*a[None,:])
        filt = filt/torch.sum(filt) # 归一化操作,保证特征经过blur后信息总量不变
        # 非grad操作的参数利用buffer存储
        self.register_buffer('filt', filt[None,None,:,:].repeat((self.channels,1,1,1)))
        self.pad = get_pad_layer(pad_type)(self.pad_sizes)

    def forward(self, inp):
        if(self.filt_size==1):
            if(self.pad_off==0):
                return inp[:,:,::self.stride,::self.stride]    
            else:
                return self.pad(inp)[:,:,::self.stride,::self.stride]
        else:
            # 利用固定参数的conv2d+stride实现blurpool
            return F.conv2d(self.pad(inp), self.filt, stride=self.stride, groups=inp.shape[1])

10 RFB

插件全称:Receptive Field Block

出自论文:Receptive Field Block Net for Accurate and Fast Object Detection

论文链接:https://arxiv.org/abs/1711.07767

图片

论文发现目标区域要尽量靠近感受野中心,这会有助于提升模型对小尺度空间位移的鲁棒性。因此受人类视觉RF结构的启发,本文提出了感受野模块(RFB),加强了CNN模型学到的深层特征的能力,使检测模型更加准确。RFB可以作为一种通用模块嵌入到绝大多数网路当中。下图可以看出其和inception、ASPP、DCN的区别,可以看作是inception+ASPP的结合。

具体实现如下图,其实和ASPP类似,不过是使用了不同大小的卷积核作为空洞卷积的前置操作。

核心代码:

class RFB(nn.Module):
    def __init__(self, in_planes, out_planes, stride=1, scale = 0.1, visual = 1):
        super(RFB, self).__init__()
        self.scale = scale
        self.out_channels = out_planes
        inter_planes = in_planes // 8
        # 分支0:1X1卷积+3X3卷积
        self.branch0 = nn.Sequential(conv_bn_relu(in_planes, 2*inter_planes, 1, stride),
                conv_bn_relu(2*inter_planes, 2*inter_planes, 3, 1, visual, visual, False))
        # 分支1:1X1卷积+3X3卷积+空洞卷积
        self.branch1 = nn.Sequential(conv_bn_relu(in_planes, inter_planes, 1, 1),
                conv_bn_relu(inter_planes, 2*inter_planes, (3,3), stride, (1,1)),
                conv_bn_relu(2*inter_planes, 2*inter_planes, 3, 1, visual+1,visual+1,False))
        # 分支2:1X1卷积+3X3卷积*3代替5X5卷积+空洞卷积
        self.branch2 = nn.Sequential(conv_bn_relu(in_planes, inter_planes, 1, 1),
                conv_bn_relu(inter_planes, (inter_planes//2)*3, 3, 1, 1),
                conv_bn_relu((inter_planes//2)*3, 2*inter_planes, 3, stride, 1),
                conv_bn_relu(2*inter_planes, 2*inter_planes, 3, 1, 2*visual+1, 2*visual+1,False)  )
        self.ConvLinear = conv_bn_relu(6*inter_planes, out_planes, 1, 1, False)
        self.shortcut = conv_bn_relu(in_planes, out_planes, 1, stride, relu=False)
        self.relu = nn.ReLU(inplace=False)
    def forward(self,x):
        x0 = self.branch0(x)
        x1 = self.branch1(x)
        x2 = self.branch2(x)
    # 尺度融合
        out = torch.cat((x0,x1,x2),1)
        # 1X1卷积
        out = self.ConvLinear(out)
        short = self.shortcut(x)
        out = out*self.scale + short
        out = self.relu(out)
        return out

11 ASFF

插件全称:Adaptively Spatial Feature Fusion出自论文:Adaptively Spatial Feature Fusion Learning Spatial Fusion for Single-Shot Object Detection

论文链接:https://arxiv.org/abs/1911.09516v1

图片

核心解析:

为了更加充分的利用高层语义特征和底层细粒度特征,很多网络都会采用FPN的方式输出多层特征,但是它们都多用concat或者element-wise这种融合方式,本论文认为这样不能充分利用不同尺度的特征,所以提出了Adaptively Spatial Feature Fusion,即自适应特征融合方式。FPN输出的特征图经过下面两部分的处理:

Feature Resizing:特征图的尺度不同无法进行element-wise融合,因此需要进行resize。对于上采样:首先利用1X1卷积进行通道压缩,然后利用插值的方法上采样特征图。对于1/2的下采样:利用stride=2的3X3卷积同时进行通道压缩和特征图缩小。对于1/4的下采样:在stride=2的3X3的卷积之前插入tride=2的maxpooling。

Adaptive Fusion:特征图自适应融合,公式如下

其中x n→l表示在(i,j)位置的特征向量,来自n特征图,经过上述resize到l尺度。Alpha。Beta,gamma为空间注意力权重,经过softmax处理,如下:

代码解析:


class ASFF(nn.Module):
    def __init__(self, level, rfb=False):
        super(ASFF, self).__init__()
        self.level = level
        # 输入的三个特征层的channels, 根据实际修改
        self.dim = [512, 256, 256]
        self.inter_dim = self.dim[self.level]
        # 每个层级三者输出通道数需要一致
        if level==0:
            self.stride_level_1 = conv_bn_relu(self.dim[1], self.inter_dim, 3, 2)
            self.stride_level_2 = conv_bn_relu(self.dim[2], self.inter_dim, 3, 2)
            self.expand = conv_bn_relu(self.inter_dim, 1024, 3, 1)
        elif level==1:
            self.compress_level_0 = conv_bn_relu(self.dim[0], self.inter_dim, 1, 1)
            self.stride_level_2 = conv_bn_relu(self.dim[2], self.inter_dim, 3, 2)
            self.expand = conv_bn_relu(self.inter_dim, 512, 3, 1)
        elif level==2:
            self.compress_level_0 = conv_bn_relu(self.dim[0], self.inter_dim, 1, 1)
            if self.dim[1] != self.dim[2]:
                self.compress_level_1 = conv_bn_relu(self.dim[1], self.inter_dim, 1, 1)
            self.expand = add_conv(self.inter_dim, 256, 3, 1)
        compress_c = 8 if rfb else 16  
        self.weight_level_0 = conv_bn_relu(self.inter_dim, compress_c, 1, 1)
        self.weight_level_1 = conv_bn_relu(self.inter_dim, compress_c, 1, 1)
        self.weight_level_2 = conv_bn_relu(self.inter_dim, compress_c, 1, 1)

        self.weight_levels = nn.Conv2d(compress_c*3, 3, 1, 1, 0)

  # 尺度大小 level_0 < level_1 < level_2
    def forward(self, x_level_0, x_level_1, x_level_2):
        # Feature Resizing过程
        if self.level==0:
            level_0_resized = x_level_0
            level_1_resized = self.stride_level_1(x_level_1)
            level_2_downsampled_inter =F.max_pool2d(x_level_2, 3, stride=2, padding=1)
            level_2_resized = self.stride_level_2(level_2_downsampled_inter)
        elif self.level==1:
            level_0_compressed = self.compress_level_0(x_level_0)
            level_0_resized =F.interpolate(level_0_compressed, 2, mode='nearest')
            level_1_resized =x_level_1
            level_2_resized =self.stride_level_2(x_level_2)
        elif self.level==2:
            level_0_compressed = self.compress_level_0(x_level_0)
            level_0_resized =F.interpolate(level_0_compressed, 4, mode='nearest')
            if self.dim[1] != self.dim[2]:
                level_1_compressed = self.compress_level_1(x_level_1)
                level_1_resized = F.interpolate(level_1_compressed, 2, mode='nearest')
            else:
                level_1_resized =F.interpolate(x_level_1, 2, mode='nearest')
            level_2_resized =x_level_2
    # 融合权重也是来自于网络学习
        level_0_weight_v = self.weight_level_0(level_0_resized)
        level_1_weight_v = self.weight_level_1(level_1_resized)
        level_2_weight_v = self.weight_level_2(level_2_resized)
        levels_weight_v = torch.cat((level_0_weight_v, level_1_weight_v,
                                     level_2_weight_v),1)
        levels_weight = self.weight_levels(levels_weight_v)
        levels_weight = F.softmax(levels_weight, dim=1)   # alpha产生
    # 自适应融合
        fused_out_reduced = level_0_resized * levels_weight[:,0:1,:,:]+\
                            level_1_resized * levels_weight[:,1:2,:,:]+\
                            level_2_resized * levels_weight[:,2:,:,:]

        out = self.expand(fused_out_reduced)
        return out

结语

本文盘点了近年来比较精巧而又实用的CNN插件,希望大家活学活用,用在自己的实际项目中。

2027年新疆职业院校技能大赛“网络系统管理”A模块网络构建真题 2027年新疆职业院校技能大赛“网络系统管理”A模块——网络构建真题,仅供学习使用。 阅读详情

相关推荐

Intel SGX Explained

计算机中最主要的资源是CPU和内存,由操作系统和虚拟管理系统等系统软件来管理这些资源。

hanqdi_的博客 3456

即插即用 | YOLOv5可以这样升级小目标检测

作者|小书童 编辑| 集智书童点击下方卡片,关注“自动驾驶之心”公众号ADAS巨卷干货,即可获取点击进入→自动驾驶之心【目标检测】技术交流群后台回复【2D检测综述】获取鱼眼检测、实时检测、通用2D检测等近5年内所有综述!小目标检测是计算机视觉和目标检测领域的一个重大挑战。小目标检测器的性能往往因缺乏像素和不太重要的特征而受到影响。这个问题源于特征尺度的变化和特征处理过程中的信息丢失引起的信息...

CV_Autobot的博客 5374

【驭风计划 机器学习】实验6 基于集成学习的 Amazon 用户评论质量预测

¶随着电商平台的兴起,以及疫情的持续影响,线上购物在我们的日常生活中扮演着越来越重要的角色。在进行线上商品挑选时,评论往往是我们十分关注的一个方面。然而目前电商网站的评论质量参差不齐,甚至有水军刷好评或者恶意差评的情况出现,严重影响了顾客的购物体验。因此,对于评论质量的预测成为电商平台越来越关注的话题,如果能自动对评论质量进行评估,就能根据预测结果避免展现低质量的评论。本案例中我们将基于集成学习的方法对 Amazon 现实场景中的评论质量进行预测。

Mege1的博客 473

顶刊TIP 2023 | CFP:即插即用的多尺度融合模块,助力检测分割任务有效涨点!

本文介绍了一种基于全局显式集中特征规范的对象检测方法CFP。该方法首先提出了一种空间显式视觉中心方案,其中使用轻量级MLP来捕捉全局长程依赖,并使用并行可学习的视觉中心来捕捉输入图像的局部角区域。基于所提出的EVC,该文进一步提出了一种自上而下的特征金字塔的全局集中特征规范方法。与现有方法相比,CFP不仅具有捕捉全局长程依赖的能力,还可以高效地获得全方位的、具有判别力的特征表示。实验结果表明,CFP在MS-COCO数据集上具有优异的表现。

专注计算机视觉全栈知识分享 3720

地震勘探原理---地震预处理

3.1 为什么说要对数据进行振幅补偿?具体来说,地震波在传播过程中会遇到各种各样的地质条件,例如不同的岩层、土壤、水层等,这些介质的密度、速度等特性不同,因而对地震波的能量消耗也有所不同。在传播距离较远的情况下,能量损失可能会导致接收到的地震信号振幅变小。相反,在传播距离较近的情况下,能量损失可能较小,导致接收到的地震信号振幅变大。如果不对数据进行振幅补偿,这种振幅不均衡会导致地震数据分析和解释的误差和不确定性增加,可能产生错误的结论。

DARRENANJIAN的博客 3274

ITIL-IT运维管理-概述

ITIL-IT运维管理-概述 概述 IT服务管理(ITSM)是一套帮助企业对IT系统的规划、研发、实施和运营进行有效管理的方法,是一套方法论。ITSM起源于ITIL(IT Infrastructure Library,IT基础架构标准库),ITIL是CCTA(英国国家电脑局)于1980年开发的一套IT服务管理标准库。它把英国在IT管理方面的方法归纳起来,变成规范,为企业的IT部门提供一套从计划、研发、实施到运维的标准方法。 中文名 IT服务管理 外文名 ITService Management 专家

syjhct的博客 4万+

【深度学习】真正的即插即用!盘点11种CNN网络设计中精巧通用的“小”插件...

作者丨皮特潘编辑丨极市平台导读所谓“插件”,就是要能锦上添花,又容易植入、落地,即真正的即插即用。本文盘点的“插件”能够提升CNN平移、旋转、scale等变性能力或多尺度特征提取,...

fengdu78的博客 3756

让 AI 自己“画施工图”——基于扩散模型的智能 CAD 管线生成

AI驱动CAD管线设计革命:Layout2CAD实现30秒自动化出图 针对设计院70%时间耗费在管线绘制的问题,研究团队基于Stable Diffusion开发了Layout2CAD系统,实现建筑草图到DWG施工图的智能转换。该系统采用三阶段流程:语义分割建筑底图→扩散生成多通道Heatmap(含管径/标高信息)→矢量化输出DWG实体,结合129M参数的多模块模型(Diffusion UNet+GraphRefiner)及规范判别器,确保1mm精度并自动满足GB50015-2019标准。

qq_74383080的博客 2014

盘点13种即插即用的涨点模块,含注意力机制、卷积变体、Transformer变体

各种注意力机制被研究用以提高不同计算机视觉任务的性能。然而,之前的方法忽略了保留通道和空间两个方面的信息以增强跨维度交互的重要性。因此,论文提出一种全局注意力机制,通过通道和空间双注意力减少信息损失,增强全局特征交互,从而提升视觉任务的性能。

学姐带你玩AI的博客 4966

CANOE DIVA 工程怎样在具体服务前增加前置条件

然后会在CANoe的Test Module视窗中发现一个跟系统条件相关的Test Module模块,在该模块中有生成跟系统条件相关的CAPL 脚本。这里需要根据你的实际项目进行更改,diva工程加载到canoe的工程的时候,会同步加入你创建的cdd文件,这里示例的ABS_ESP 就是你的cdd的ECU命名,后面就是具体的前置条件服务。4、配置完成后,测试用例就会如下图Trace窗口所示,在每一次10 02请求之前都会调用CAPL Function的PreconditionCheck函数中定义的脚本。

weixin_45940007的博客 1967

把 LangGraph 当“工作流引擎”用:与传统 BPM 的差异与融合策略

首先,我们来回顾一下有限状态机(FSM)的基本定义——一个 FSM 可以用一个五元组MQΣδq0FMQΣδq0​FQQQ有限状态集合,包含 FSM 所有可能的状态;Σ\SigmaΣ有限输入字母表,包含 FSM 所有可能的输入;δ\deltaδ状态转移函数,定义了 FSM 从当前状态q∈Qq \in Qq∈Q接收输入σ∈Σσ∈Σ后,转移到的下一个状态q′∈Qq' \in Qq′∈Q,即δQ×Σ→QδQ×Σ→Q。

AI 原生应用开发的博客 227

【每天一篇深度学习论文】(IEEE TIP)即插即用多尺度特征提取模块MSB

Mix Structure Block contains multi-scale parallel large convolution kernel module and enhanced parallel attention module链接: https://arxiv.org/abs/2305.17654这篇文章介绍了一个名为MixDehazeNet的图像去雾网络,其主要创新点包括:多尺度并行大卷积核模块(MSPLCK):该模块结合了多尺度特性和大的感受野,能够同时捕获大雾区域和恢复纹理细节。与单一大

Magnolia_He的博客 2933

Autosar PNC网络管理配置(1)-基于ETAS软件

Autosar PNC网络管理配置(1)-基于ETAS软件

赞的博客 2731

11种CNN网络设计中精巧通用的“小”插件

导言 本文转载于:(附代码)真正的即插即用!盘点11种CNN网络设计中精巧通用的“小”插件 所谓“插件”,就是要能锦上添花,又容易植入、落地,即真正的即插即用。本文盘点的“插件”能够提升CNN平移、旋转、scale等变性能力或多尺度特征提取,感受野等能力,在很多SOTA网络中都会看到它们的影子。 前沿 本文盘点一些CNN网络中设计比较精巧而又实用的“插件”。所谓“插件”,就是不改变网络主体结构, 可以很容易嵌入到主流网络当中,提高网络提取特征的能力,能够做到plug-and-play。网络.

m0_61899108的博客 809

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

四路 CAN 转 4G 网关

SG‑CAN‑4G‑410 为**四路隔离 CAN 转 4G 工业网关**,把 CAN2.0A/B 报文封装成 TCP/UDP 报文,通过 4G 无线网络实现 CAN 总线远程监控、远程控制。四路 CAN 相互独立;以太网仅用于本地配置,不做业务数据传输;支持移动 / 联通 / 电信 4G 网络。

S2003120416的博客 167

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

【计算机网络】断点续传是怎么做到的:从 Range 请求到分片上传的完整机制

断点续传在下载侧靠的是 HTTP 现成的 Range 协议,Content-Range 保证偏移对得上,If-Range 防止文件变更导致的错误拼接;上传侧没有标准,需要自己设计 init/part/complete 三段式交互,核心是让服务端记住“哪些块已经收下”,从而把失败粒度从整个文件降到单个分片。 这套机制可以迁移到任何长耗时操作的设计上。判断方法很简单:先问失败粒度是多少。如果一次操作失败就得从头再来,那它迟早会在生产环境里出问题——传输、批量导入、数据迁移、长事务,都是同一个道理。把大操作切成

2503_94545876的博客 542

Linux 服务器性能排查手册(2026):CPU、内存、磁盘IO、网络四大瓶颈定位

iptables 四表五链模型、firewalld zone 信任级别、云安全组与主机防火墙的关系与优先级,避坑改了防火墙把自己挡在服务器外面的翻车事故。

2502_91473995的博客 333

【Linux网络】TCP 服务器并发处理多客户端:从 fork 分发到双重 fork 彻底消灭僵尸进程

拆解 TCP 服务器并发演进链:fork 分发连接、SIGCHLD 循环回收僵尸、双重 fork 交孙进程给 init 收养,并给出模型选型与线上排错对照。

2301_78967866的博客 431

AI 芯片片上网络 NoC 深度解析:flit、虚通道与拥塞控制如何决定 GPU/TPU/LPU 的算力利用率

AI芯片算力瓶颈不在FLOPS,而在片上网络(NoC)——数据搬运的能耗与延迟决定MFU上限。物理限制下,长线延迟不随工艺缩小,迫使NoC将全局布线拆为可流水的短链路;能量账显示,一跳传输耗能可达数十次乘加操作。拓扑选择直接影响性能:二维mesh虽布线可行,但均匀流量下每节点可用带宽按1/√N衰减,实测仅达标称值30%。虚通道、双网设计、静态调度(如Groq)等技术旨在突破此瓶颈。从总线到crossbar再到多跳NoC,本质是用延迟换可行性。未来趋势:专用通路(脉动阵列)、晶圆级集成(Cerebras)、跨

我的博客 106

台湾行情数据API接入教程

想用程序获取台股行情,很多人第一步会搜“台股资料 API 推荐”,结果发现选择不少:证交所有免费接口,Yahoo Finance 也能查,券商还有自己的 API。真正写代码时才发现,有的数据只到收盘,有的延迟 20 分钟,有的必须先开户。本文先梳理台湾行情数据的几种获取渠道,再用 Python 从零搭一个可以运行的台股数据脚本:查代码清单、取实时报价和盘口、拉历史K线、做复权、用 WebSocket 接收实时推送,最后结合台积电 ADR 和新台币汇率计算跨市场溢价。

qq_34102871的博客 311

【基于 WebSocket 的多用户网页五子棋】—— 数据管理模块

但行好事,莫问前程

qq_42996461的博客 238

Qt网络编程实战指南:从底层Socket到现代异步架构的选型与避坑

本文梳理Qt网络编程的核心模块与演进路线,对比QTcpSocket与QNetworkAccessManager的使用边界,结合HTTP请求与TCP长连接的完整示例,说明信号槽、内存管理与事件循环的配合方式。重点记录连接池设计、证书校验、线程亲和性等实际开发中的关键决策点,帮助读者在C/S或跨平台项目中建立稳定的网络层基座。

2402_83344867的博客 300

从母钟到子钟:NTP 网络时钟系统架构与软件接口

本文从工程角度简要介绍公共场所的时间同步需求、NTP协议的定位,以及典型LED同步时钟的系统架构与硬件设计,以讯鹏科技4寸与5寸同步时钟为例。

SPZHCS的博客 169

DPVS 报文黑匣子与 Crash 自动拉黑:给转发面装上“飞行记录仪“

谁打挂了我(crash dump)、他之前发了什么(慢路径环)、他还会再来(入口拉黑)。设计上我们始终坚持两条原则:记录与决策分离(黑匣子永远只说不做),以及误伤成本优先于漏放成本(三道闸门全部通过才拉黑,expire 到期自动解封)。性能上,全量快路径摘要 + 入口查表的综合开销低于 1.3%,在 FIP 网关的流量水位下不可感知。飞行记录仪的价值不在于飞机正常飞行的时候,而在于它让每一次事故都变成一次改进。对转发面来说,同样如此。

zyun360的博客 275

全球实时竞价 RTB 中 IP 定位数据应用:链路完整性与工程实践

全球实时竞价(RTB)中IP定位数据的应用,核心判断是:IP数据是RTB竞价决策的底层输入之一,但它的价值高度依赖于从IP捕获到OpenRTB序列化整条链路的完整性。大量定位误差并非源于IP库本身不准,而是发生在CDN边缘节点捕获了错误的IP、供应链各环节使用了不同版本的库、或者OpenRTB字段缺失导致买方无法判断数据可信度。对于DSP和SSP的工程团队而言,理解IP定位在RTB中的实际作用边界,比选一个更准的库更重要。

ITxiaobing2023的博客 259

HTTP、SSE、WebSocket、Socket.IO:实时通信方案深度对比与选型指南

HTTP 是基础,是整个 Web 的底座;SSE 是单向推送的轻量最优解;WebSocket 是高性能双向实时通信的底层标准;Socket.IO 是一套降低开发门槛、抹平环境差异的上层框架。技术选型永远不是“哪个技术更酷”,而是匹配业务的实时性、交互方向、网络环境、团队维护成本。简单需求优先轻方案,复杂双向实时再上 WebSocket;如果追求快速落地、兼容各种奇葩网络,Socket.IO 是很好的选择。

changshuaihua001的博客 352
上一篇: 图解 72 个机器学习基础知识点
下一篇: 转载:CVPR2023 | SCConv:即插即用的高效卷积模块,减少冗余计算并提升特征学习
wuling129
博客等级 码龄23年 486粉丝 263原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值