GhostNet网络详解

2023-11-07

1. GhostNet网络

一张图片经过神经网络进行特征提取后,能够得到很多特征图。
在这里插入图片描述
在特征图中会有一些相似性很高,这就是神经网络中存在的特征图冗杂的情况(如图中扳手相连的两幅特征图)。

作者认为可以对其中的一个特征图进行(Cheap Operations)简单的线性运算从而生成更多相似特征图,从而可以使用更少的参数生成更多的特征图,将相似的特征图认为是彼此的Ghost。

2. Ghost Module

在这里插入图片描述
作者用Ghost Module代替传统卷积,首先采用普通的1x1卷积对输入图片进行通道数的压缩,然后再进行深度可分离卷积(逐层卷积)得到更多的特征图,然后将不同的特征图concat到一起,组合成新的output。

Ghost Module构建代码

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__()
        #ratio一般会指定成2,保证输出特征层的通道数等于exp
        self.oup = oup
        init_channels = math.ceil(oup / ratio)
        new_channels = init_channels*(ratio-1)

        #利用1x1卷积对输入进来的特征图进行通道的浓缩,获得特征通缩
        #跨通道的特征提取
        self.primary_conv = nn.Sequential(
            nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size//2, bias=False),  #1x1卷积的输入通道数为GhostModule的输出通道数oup/2
            nn.BatchNorm2d(init_channels),                       #1x1卷积后进行标准化
            nn.ReLU(inplace=True) if relu else nn.Sequential(),  #ReLU激活函数
        )

        #在获得特征浓缩后,使用逐层卷积,获得额外的特征图
        #跨特征点的特征提取    一般会设定大于1的卷积核大小
        self.cheap_operation = nn.Sequential(
            nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size//2, groups=init_channels, bias=False),  #groups参数的功能就是将普通卷积转换成逐层卷据
            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)
        #将1x1卷积后的结果和逐层卷积后的结果进行堆叠
        out = torch.cat([x1,x2], dim=1)
        return out[:,:self.oup,:,:]

1x1卷积的输入通道数为GhostModule的输出通道数oup/2,因为最终输出特征层由1x1卷积后的结果和逐层卷积后的结果进行堆叠组成的,进行逐层卷积时特征层的通道数不变,如果要保证最终输出特征层的通道数是一个固定值,那么就要使得1x1卷积后的结果的通道数为最终输出特征层的1/2。

3. Ghost BottleNeck原理

Ghost BottleNeck是由Ghost Module组成的瓶颈结构

Ghost BottleNeck整体架构和Residual Block非常相似,也可以直接认为是将Residual Block中的普通卷积操作替换成Ghost Module得到。
在这里插入图片描述
左图中主干部分用用两个Ghost Module(GM)串联组成,其中第一个GM扩大通道数,第二个GM将通道数降低到与输入通道数一致;残差边部分与ResNet一样。由于S=1,因此不会对输入特征层的高和宽进行压缩,其功能为加深网络的深度。

右图中主干部分的两个GM之间加入了一个stride=2的Deepwise卷积,可以将特征图高和宽进行压缩,使其大小降为输入的1/2;在残差边部分,也会添加一个步长为2x2的深度可分离卷积和1x1的普通卷积,以保证Add操作可以对齐。由于S=2,因此会对输入特征层的高和宽进行压缩,其功能为改变输入特征层的形状。

实际应用中,为了进一步提高效率,GhostModule中的所有常规卷积都用pointwise卷积代替。

Ghost BottleNeck构建代码

class GhostBottleneck(nn.Module):
    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
        #首先利用一个ghost模块进行特征提取
        #此时指定的通道数会比较大,可以看作是逆残差结构,进行通道数上升
        self.ghost1 = GhostModule(in_chs, mid_chs, relu=True)

        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)

        if has_se:    #判断是否使用注意力机制模块
            self.se = SqueezeExcite(mid_chs, se_ratio=se_ratio)
        else:
            self.se = None

        #再次利用一个ghost模块进行特征提取
        self.ghost2 = GhostModule(mid_chs, out_chs, relu=False)
        #判断步长是否等1、输入通道和输出通道是否一样
        if (in_chs == out_chs and self.stride == 1):
            self.shortcut = nn.Sequential()
        else:     #如果不一样则利用深度可分离卷积和1x1卷积调整通道数,保证主干部分和残差边部分能够进行相加
            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

        x = self.ghost1(x)

        if self.stride > 1:
            x = self.conv_dw(x)
            x = self.bn_dw(x)

        if self.se is not None:
            x = self.se(x)

        x = self.ghost2(x)
        
        x += self.shortcut(residual)
        return x

4. GhostNet的构建

在这里插入图片描述
整个Ghostnet都是由Ghost Bottlenecks进行组成的。

当一张图片输入到Ghostnet当中时,首先进行一个16通道的普通1x1卷积块(卷积+标准化+激活函数)

之后就开始Ghost Bottlenecks的堆叠了,利用Ghost Bottlenecks,最终获得了一个7x7x160的特征层(当输入是224x224x3的时候)

然后利用一个1x1的卷积块进行通道数的调整,此时可以获得一个7x7x960的特征层

之后进行一次全局平均池化,然后再利用一个1x1的卷积块进行通道数的调整,获得一个1x1x1280的特征层

最后平铺后进行全连接就可以进行分类了。

class GhostNet(nn.Module):
    def __init__(self, cfgs, num_classes=1000, width=1.0, dropout=0.2):
        super(GhostNet, self).__init__()
        # setting of inverted residual blocks
        self.cfgs = cfgs
        self.dropout = dropout

        # building first layer   对输入图片进行第一个卷积标准化+激活函数
        output_channel = _make_divisible(16 * width, 4)
        #以应用到yolov4为例   416,416,3 ->  208,208,16   压缩高和宽,通道数扩张
        self.conv_stem = nn.Conv2d(3, output_channel, 3, 2, 1, bias=False)   #s=2,output_channel=16
        self.bn1 = nn.BatchNorm2d(output_channel)
        self.act1 = nn.ReLU(inplace=True)
        input_channel = output_channel

        # building inverted residual blocks   构建瓶颈结构
        stages = []
        block = GhostBottleneck
        for cfg in self.cfgs:    #对配置列表进行循环
            layers = []
            for k, exp_size, c, se_ratio, s in cfg:
                output_channel = _make_divisible(c * width, 4)
                hidden_channel = _make_divisible(exp_size * width, 4)
                #根据cfg里面的内容构建瓶颈结构
                layers.append(block(input_channel, hidden_channel, output_channel, k, s,
                              se_ratio=se_ratio))
                #计算下一个瓶颈结构的输入
                input_channel = output_channel
            stages.append(nn.Sequential(*layers))
        #卷积标准化+激活函数
        output_channel = _make_divisible(exp_size * width, 4)
        stages.append(nn.Sequential(ConvBnAct(input_channel, output_channel, 1)))
        input_channel = output_channel
        #根据构建好的block序列模型
        self.blocks = nn.Sequential(*stages)        
        #构建分类才能够
        # building last several layers
        output_channel = 1280
        self.global_pool = nn.AdaptiveAvgPool2d((1, 1))
        self.conv_head = nn.Conv2d(input_channel, output_channel, 1, 1, 0, bias=True)
        self.act2 = nn.ReLU(inplace=True)
        self.classifier = nn.Linear(output_channel, num_classes)

    def forward(self, x):
        #第一个卷积标注化+激活函数
        x = self.conv_stem(x)
        x = self.bn1(x)
        x = self.act1(x)
        #瓶颈结构特征提取
        x = self.blocks(x)
        #构建分类岑那个
        x = self.global_pool(x)

        x = self.conv_head(x)
        x = self.act2(x)
        x = x.view(x.size(0), -1)
        if self.dropout > 0.:
            x = F.dropout(x, p=self.dropout, training=self.training)
        x = self.classifier(x)
        return x


def ghostnet(**kwargs):
    """
    Constructs a GhostNet model
    """
    cfgs = [
        # k, t, c, SE, s
        # k代表卷积核大小,表示跨特征点的特征提取能力
        # t代表第一个ghost模块的通道数大小,它的值一般比较大一点
        # c代表瓶颈结构最终的输出通道数
        # SE代表是否使用注意力机制,如果不为0就是用注意力机制
        # s代表步长,如果s=2就会对输入特征层进行高和宽的压缩
        # stage1
        # 208,208,16 -> 208,208,16
        [[3,  16,  16, 0, 1]],
        # stage2
        # 208,208,16 -> 104,104,24
        [[3,  48,  24, 0, 2]],
        [[3,  72,  24, 0, 1]],
        # stage3
        # 104,104,24 -> 52,52,40
        [[5,  72,  40, 0.25, 2]],
        [[5, 120,  40, 0.25, 1]],
        # stage4
        # 52,52,40 -> 26,26,80 -> 26,26,112
        [[3, 240,  80, 0, 2]],
        [[3, 200,  80, 0, 1],
         [3, 184,  80, 0, 1],
         [3, 184,  80, 0, 1],
         [3, 480, 112, 0.25, 1],
         [3, 672, 112, 0.25, 1]
        ],
        # stage5
        # 26,26,112 -> 13,13,160
        [[5, 672, 160, 0.25, 2]],
        [[5, 960, 160, 0, 1],
         [5, 960, 160, 0.25, 1],
         [5, 960, 160, 0, 1],
         [5, 960, 160, 0.25, 1]
        ]
    ]
    return GhostNet(cfgs, **kwargs)

5. 将GhostNet应用到Yolov4上

在这里插入图片描述需要利用主干特征提取网络获得的三个有效特征进行加强特征金字塔的构建

取出stage3、stage4、stage5的输出:

class GhostNet(nn.Module):
    def __init__(self, pretrained=True):
        super(GhostNet, self).__init__()
        model = ghostnet()
        if pretrained:
            state_dict = torch.load("model_data/ghostnet_weights.pth")
            model.load_state_dict(state_dict)
        del model.global_pool
        del model.conv_head
        del model.act2
        del model.classifier
        del model.blocks[9]
        self.model = model

    def forward(self, x):
        x = self.model.conv_stem(x)
        x = self.model.bn1(x)
        x = self.model.act1(x)
        feature_maps = []

        for idx, block in enumerate(self.model.blocks):
            x = block(x)
            if idx in [2,4,6,8]:
                feature_maps.append(x)
        return feature_maps[1:]

参考文献:
原作者解读
Ghostnet网络介绍与构建

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GhostNet网络详解 的相关文章

  • 使用请求验证 SSL 证书

    我正在尝试验证 SSL 但它不起作用 我在浏览器上访问了我想要访问的机密网站 在 Chrome 上 我单击了储物柜 gt 证书 gt 详细信息 gt 复制到文件 gt base64 gt cert cer 我的代码是 test reques
  • 翠儿。让流永远运行

    我对 tweepy python 库比较陌生 我想确保我的流 python 脚本始终在远程服务器上运行 因此 如果有人能够分享如何实现这一目标的最佳实践 那就太好了 现在我正在这样做 if name main while True try
  • 将tensorflow 2.0 BatchDataset转换为numpy数组

    我有这个代码 train images test images tf keras datasets mnist load data train dataset tf data Dataset from tensor slices train
  • 一次将Python dict的内容分配给多个变量?

    我想做这样的事情 def f return a 1 b 2 c 3 a b f or a b f IE 这样 a 被分配为 1 b 被分配为 2 并且 c 是未定义的 这与此类似 def f return 1 2 a b f 依赖于变量名称
  • Tensorflow 可变图像输入大小(自动编码器、放大......)

    Edit WARNING不建议使用不同图像大小的图像 因为张量需要具有相同的大小才能实现并行化 我一直在寻找解决方案 了解如何使用不同大小的图像作为神经网络的输入 Numpy 第一个想法是使用numpy 然而 由于每个图像的大小不同 我无法
  • python - 是否可以扩展 xml-rpc 可以序列化的事物集?

    我看到几个问题询问如何发送numpy ndarray通过 xml rpc 调用 这不能开箱即用 因为正如 xml rpc 中所述docs https docs python org 2 library xmlrpclib html 有一组固
  • 从字符串到类型的词法转换

    最近 我尝试用Python存储和读取文件中的信息 遇到了一个小问题 我想从文本文件中读取类型信息 从 string 到 int 或 float 的类型转换非常有效 但从 string 到 type 的类型转换似乎是另一个问题 当然 我尝试了
  • 如何从 PyCharm 项目中获取我的“exe”[重复]

    这个问题在这里已经有答案了 通过 PyCharm 在 Python 上编写一些项目 我想从中获取一个exe文件 我尝试过 另存为 gt XXX exe 但是 当我尝试执行它时出现错误 此类操作系统不支持该文件 附注 我有win7 x64 它
  • 如何使用Python将WebP图像转换为Gif?

    我已经尝试过这个 from PIL import Image im Image open this webp im save that gif gif save all True 这给了我这个错误 类型错误 不支持的操作数类型 tuple
  • Python BeautifulSoup XML 解析

    我编写了一个简单的脚本来使用 BeautifulSoup 模块解析 XML 聊天日志 标准 soup prettify 工作正常 只是聊天日志中有很多绒毛 您可以在下面看到我正在使用的脚本代码和一些 XML 输入文件 Code import
  • 可以在 TensorFlow 中使用排名相关作为成本函数吗?

    我正在处理偶尔充满异常值的极其嘈杂的数据 因此我主要依靠相关性来衡量我的神经网络的准确性 是否可以明确使用诸如等级相关性 斯皮尔曼相关系数 之类的东西作为我的成本函数 到目前为止 我主要依赖 MSE 作为相关性的代理 我现在面临三个主要障碍
  • 无法打开 Python。错误 0xc000007b

    我最近一直在学习 Python 3 我在我的上网本 32 位 Windows 7 上创建简单的小程序没有任何问题 当我将它安装在我的上网本上时 我没有遇到任何问题 但现在我已经开始使用它了 我想将它安装在我的台式机上 并且我有一个 我的桌面
  • 创建一个类似于 Tkinter 的表

    我希望创建类似于 Tkinter 中的表格的东西 但它不一定是这样的 例如 我想创建标题 Name1 Name2 Value 并在每个标题下面有几个空白行 然后 我希望稍后用我计算的值或名称的字符串值填充这些行 因此是标签 对于 Name2
  • matplotlib matshow 标签

    我一个月前开始使用 matplotlib 所以我仍在学习 我正在尝试用 matshow 制作热图 我的代码如下 data numpy array a reshape 4 4 cax ax matshow data interpolation
  • 更改 pandas 中多个日期时间列的时区信息

    有没有一种简单的方法可以将数据帧中的所有时间戳列转换为本地 任何时区 不是逐列进行吗 您可以有选择地将转换应用于所有日期时间列 首先 选择它们select dtypes https pandas pydata org pandas docs
  • Python 中的 @staticmethod 与 @classmethod

    方法和方法有什么区别装饰的 https peps python org pep 0318 with staticmethod http docs python org library functions html staticmethod和
  • Django 在选择列表更改时创建毫无意义的迁移

    我正在尝试使用可调用创建一个带有选择字段的模型 以便 Django 在选择列表更改时不会创建迁移 如中所述this https stackoverflow com questions 31788450 stop django from cr
  • 如何有效地从 loadmat 函数生成的嵌套 numpy 数组中提取值?

    python中是否有更有效的方法从嵌套的python列表中提取数据 例如A array array 12000000 dtype object 我一直在使用A 0 0 0 0 当你有很多像 A 这样的数据时 这似乎不是一个有效的方法 我也用
  • Scipy 稀疏 Cumsum

    假设我有一个scipy sparse csr matrix代表下面的值 0 0 1 2 0 3 0 4 1 0 0 2 0 3 4 0 我想就地计算非零值的累积和 这会将数组更改为 0 0 1 3 0 6 0 10 1 0 0 3 0 6
  • 来自 django 教程 was_published_recently.admin_order_field = 'pub_date'

    From Django 教程 https www jetbrains com help pycharm 2017 1 creating and running your first django project html d28041e21

随机推荐

  • 还没用熟 TypeScript 社区已经开始抛弃了

    前端Q 我是winty 专注分享前端知识和各类前端资源 乐于分享各种有趣的事 关注我 一起做个有趣的人 公众号 点击上方 前端Q 关注公众号 回复加群 加入前端Q技术交流群 根据 rich harris talks sveltekit an
  • C# 加密解码各种方法

    目录 一 加密解密介绍 二 MD5 三 SHA x系列 四 DES 3DES 五 RC2 六 AES 七 Base64 八 Rsa 九 参考文献 一 加密解密介绍 不可逆加密 MD5 SHA x系列对称式加密 Des 3DES RC2 AE
  • “加密系统”的巨坑

    在来公司之前 我压根就不知道这世界上原来还有 加密系统 这种软件产品存在 学名叫数据防泄漏 也怪我孤陋寡闻了 因为之前在厦门从来没听说过哪家公司有在用加密系统 当然 每家公司都有自己独特的管理需求 也许公司这边也确实重要信息比较多 核心机密
  • java自定义排序

    java中sort的自定义排序 一 Arrays sort nums 的一般用法 二 最大数 力扣179 三 合并区间 力扣59 四 总结 一 Arrays sort nums 的一般用法 整个数组按照升序排序 若需要降序排序 将数组转置即
  • ng-model数据绑定实例

    ng mode的作用是数据绑定 placeholder是默认显示值 在input里面输入任何字符都会对应显示到你下面ng model对应的name中 name
  • Python 3基础教程19-模块导入语法

    本文开始介绍模块导入的一些基本语法 我们现在还在Python自带的IDLE编辑器里写Python代码 如果你要需要一个功能 例如build in的模块 那么你就需要先导入这个模块 然后才能使用这个模块相关方法 不像在Pycharm编辑器了
  • 剑指 Offer 18. 删除链表的节点

    题目链接 18 删除链表的节点 Definition for singly linked list struct ListNode int val ListNode next ListNode int x val x next NULL c
  • 良品铺子年报:将冲击百亿营收门槛 斥资1.8亿现金分红

    雷递网 雷建平 3月23日报道 良品铺子 603719 SH 日前公布2021年度业绩 财报显示 良品铺子2021年公司营收93 24亿元 较2020年的78 94亿元增长18 11 近五年营收翻番 这意味着 良品铺子2022年将冲击百亿门
  • local_irq_save和 local_irq_disable

    如果你要禁止所有的中断该怎么办 在2 6内核中 可以通过下面两个函数中的其中任何一个关闭当前处理器上的所有中断处理 这两个函数定义在
  • constraints java_java-来自javax.validation.constraints的注释不起作用

    java 来自javax validation constraints的注释不起作用 使用id 如name NotNull等 中的注释需要什么配置 这是我的代码 import javax validation constraints Not
  • 深入理解Solidity——创建合约

    Solidity的合约类似于面向对象语言中的类 它们包含存放持久化数据的状态变量和可修改这些变量的函数 调用不同的合约实例上的函数将执行EVM函数调用 从而切换上下文 使得状态变量不可访问 创建合约 Creating Contracts 合
  • 支付宝商户转账给支付宝个人

    首先引入支付宝的jar
  • TorchServe部署pytorch模型

    文件准备 1 model file model py 参考 https github com pytorch serve blob master examples object detector maskrcnn model py 2 se
  • GODIVA论文阅读

    论文链接 GODIVA Generating Open DomaIn Videos from nAtural Descriptions 文章目录 摘要 引言 相关工作 Video to video generation Text to im
  • Hadoop3.2.*安装

    CentOS8系统安装Hadoop 3 2 1伪分布式配置 Hadoop3 2 1版本的环境搭建 Java提升营 博客园 TOC 实验目的 在 Linux VM15pro CentOS8 环境下完成Hadoop 3 2 1伪分布式环境的搭建
  • 格密码学习,抽代基础学习(二)

    今天找到了一个比较新手友好的slides 结合lec1和学姐的笔记一起看 full rank lattice 满格 R n mathbb R n Rn的概念 n维度实数集 每个元素是n维向量 向量中的每个分量是实数 Z
  • 数据分析时,进行数据建模该如何筛选关键特征?

    1 为什么要做关键特征筛选 在数据量与日俱增的时代 我们收集到的数据越来越多 能运用到数据分析挖掘的数据也逐渐丰富起来 但同时 我们也面临着如何从庞大的数据中筛选出与我们业务息息相关的数据 大背景 从数据中挖掘潜在的规律 辅助我们在实际业务
  • 2.block_device_operations结构体

    在块设备驱动中 有一个类似于字符设备驱动中file operations结构体的block device operations结构体 他是对块设备操作的集合 1 block device operations结构体 2 打开和释放 int
  • Linux上配置http上网代理

    有些局域网环境上网需要使用代理上网 图形界面的很好解决就设置一下浏览器的代理就好了 但是linux纯命令行的界面就需要手动配置了 如果要全局用户使用应用于所有的Shell 就需要修改 etc profile 文件 vi etc profil
  • GhostNet网络详解

    1 GhostNet网络 一张图片经过神经网络进行特征提取后 能够得到很多特征图 在特征图中会有一些相似性很高 这就是神经网络中存在的特征图冗杂的情况 如图中扳手相连的两幅特征图 作者认为可以对其中的一个特征图进行 Cheap Operat