GhostNet(CVPR 2020) 原理与代码解析

论文 https://arxiv.org/abs/1911.11907v2

目录

Ghost Module

Ghost Bottleneck

GhostNet


作者发现特征图中存在冗余的情况

如图所示,同色的两个特征图之间存在很强的线性关系,可以通过一些成本较低的操作得到。

Ghost Module

作者提出了Ghost module,具体做法是,通过常规卷积生成部分特征图,论文中将这部分特征图称为intrinsic feature maps。然后对这些intrinsic特征图进行简单的线性变换生成ghost feature maps,然后将两者进行concat保持最终的输出通道数不变。

Ghostmodule的代码如下

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(),
        )

        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)
        out = torch.cat([x1, x2], dim=1)
        return out[:, :self.oup, :, :]

其中,ratio是intrinsic特征图和ghost特征图通道数的比例,一般设为2,即各占总输出通道数的1/2。primary_conv是常规卷积用来生成intrinsic特征图,cheap_operation是深度卷积用来生成ghost特征图,然后将两者进行concat得到最终输出。

常规卷积转换成ghost module后计算量的比值如下

其中,h'×w'×c是输入特征图的尺寸,k是常规卷积大小,n是输出特征图通道数。s是ghost module中常规卷积的占比即上面代码中的ratio,d是线性变换即深度卷积的大小。另外,d≈k,一般为3或5。s\llc,一般s=2,c可能为512、1024甚至更大。因此最终计算量的压缩比大约就是s,即减少了1/2。

Ghost Bottleneck

作者以ghost module为基础,构建了Ghost bottleneck

代码如下 

class GhostBottleneck(nn.Module):
    """ Ghost bottleneck w/ optional SE"""

    def __init__(self, in_chs, mid_chs, out_chs, dw_kernel_size=3,
                 stride=1, act_layer=nn.ReLU, se_ratio=0.):
        super(GhostBottleneck, self).__init__()
        has_se = se_ratio is not None and se_ratio > 0.
        self.stride = stride

        # Point-wise expansion
        self.ghost1 = GhostModule(in_chs, mid_chs, relu=True)

        # Depth-wise convolution
        if self.stride > 1:
            self.conv_dw = nn.Conv2d(mid_chs, mid_chs, dw_kernel_size, stride=stride,
                                     padding=(dw_kernel_size - 1) // 2,
                                     groups=mid_chs, bias=False)
            self.bn_dw = nn.BatchNorm2d(mid_chs)

        # Squeeze-and-excitation
        if has_se:
            self.se = SqueezeExcite(mid_chs, se_ratio=se_ratio)
        else:
            self.se = None

        # Point-wise linear projection
        self.ghost2 = GhostModule(mid_chs, out_chs, relu=False)

        # shortcut
        if (in_chs == out_chs and self.stride == 1):
            self.shortcut = nn.Sequential()
        else:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_chs, in_chs, dw_kernel_size, stride=stride,
                          padding=(dw_kernel_size - 1) // 2, groups=in_chs, bias=False),
                nn.BatchNorm2d(in_chs),
                nn.Conv2d(in_chs, out_chs, 1, stride=1, padding=0, bias=False),
                nn.BatchNorm2d(out_chs),
            )

    def forward(self, x):
        residual = x

        # 1st ghost bottleneck
        x = self.ghost1(x)

        # Depth-wise convolution
        if self.stride > 1:
            x = self.conv_dw(x)
            x = self.bn_dw(x)

        # Squeeze-and-excitation
        if self.se is not None:
            x = self.se(x)

        # 2nd ghost bottleneck
        x = self.ghost2(x)

        x += self.shortcut(residual)
        return x

其中一些具体实现的细节如下,只有第一个Ghost module后有ReLU函数;使用se时,se是在第一个Ghost module或者stride=2的深度卷积后,se中降维比例se_ratio=0.25;stride=2时,shortcut依次经过stride=2的深度卷积、BN、1×1卷积、BN,然后再进行Add。

GhostNet

最后通过堆叠Ghost bottleneck,作者提出了GhostNet,其结构如下

需要注意到是,具体实现中卷积核的大小

cfgs = [
    # k, t, c, SE, s
    [3, 16, 16, 0, 1],
    [3, 48, 24, 0, 2],
    [3, 72, 24, 0, 1],
    [5, 72, 40, 1, 2],
    [5, 120, 40, 1, 1],
    [3, 240, 80, 0, 2],
    [3, 200, 80, 0, 1],
    [3, 184, 80, 0, 1],
    [3, 184, 80, 0, 1],
    [3, 480, 112, 1, 1],
    [3, 672, 112, 1, 1],
    [5, 672, 160, 1, 2],
    [5, 960, 160, 0, 1],
    [5, 960, 160, 1, 1],
    [5, 960, 160, 0, 1],
    [5, 960, 160, 1, 1]
]

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
GhostBottleNeck模块的代码实现如下: ```python import torch.nn as nn class GhostBottleNeck(nn.Module): def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1, ratio=2, dw_size=3): super(GhostBottleNeck, self).__init__() self.conv = nn.Sequential( nn.Conv2d(in_channels, out_channels // ratio, kernel_size=1), nn.BatchNorm2d(out_channels // ratio), nn.ReLU(inplace=True), nn.Conv2d(out_channels // ratio, out_channels // ratio, kernel_size=dw_size, stride=stride, padding=padding, groups=out_channels // ratio), nn.BatchNorm2d(out_channels // ratio), nn.ReLU(inplace=True), nn.Conv2d(out_channels // ratio, out_channels, kernel_size=1), nn.BatchNorm2d(out_channels) ) self.shortcut = nn.Sequential() if stride != 1 or in_channels != out_channels: self.shortcut = nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride), nn.BatchNorm2d(out_channels) ) def forward(self, x): out = self.conv(x) out += self.shortcut(x) out = nn.ReLU(inplace=True)(out) return out ``` GhostBottleNeck模块是GhostNet中的核心模块之一,它包括深度可分离卷积和GhostModule两部分。首先通过一个$1\times1$卷积将输入通道数降维,然后使用深度可分离卷积进行特征提取,最后使用一个$1\times1$卷积将通道数升维。同时,为了解决模型剪枝后精度下降的问题,GhostBottleNeck模块还使用了GhostModule。GhostModule首先将输入特征张量分为两个部分,其中一部分数量为原始通道数的$\frac{1}{r}$,另一部分数量为原始通道数减去第一部分的数量,然后使用第一部分的特征从原始通道中随机选择一些通道进行线性组合,得到GhostModule的输出。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

00000cj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值