常见轻量级模型

SqueezeNet

论文

https://arxiv.org/pdf/1602.07360

核心是fire module
结构如下:

特征进入squeeze层,squeeze的1×1卷积核数量比特征少一个,也就变成降维就是所谓的压缩
再进入expand层,会同时1×1和3×3的两个核再将维度升回原始特征维度,也即是所谓的扩展
再将这两个卷积核输出concat就构成完整的fire层

squeezenet的结构:

与VGG思想相同,一直堆叠卷积层

GhostNet

论文 https://openaccess.thecvf.com/content_CVPR_2020/papers/Han_GhostNet_More_Features_From_Cheap_Operations_CVPR_2020_paper.pdf

本质通过线性变换减少滤波器数量降低计算量

作者发现残差模块后输出特征图有一些存在高度近似情况,说明在同一个卷积层生成的许多特征图谱之间存在显著的相似性或冗余会提升模型鲁棒性和对输入的理解:

  1. 更全面的特征表示: 冗余的特征图谱可以从略微不同的角度捕捉输入数据的相似特征。即使某些特征图谱因为噪声、遮挡或其他干扰而失效,其他的冗余特征图谱仍然可以保留相关信息,从而提高网络的鲁棒性。
  2. 增强特征的可靠性: 如果多个特征图谱都对某个特定的输入模式做出响应,那么网络对该模式的识别就更加可靠。这种冗余降低了网络对单个“脆弱”特征的依赖。
  3. 潜在的隐式正则化: 一些研究认为,特征图谱的冗余性可能起到一种隐式的正则化作用,有助于防止模型过拟合到训练数据中的噪声。

Ghost模块

ghost的核心思想是设计更高效的网络实现这种冗余性,于是提出了ghost模块改进CNN计算:

输入经过1×1降维度(和resnet一样),然后分成连个分支,一个分支做深度卷积(depthwise)和其他的线性变换,再将这个分支和之前的分支concat

Ghost bottleneck(G-neck)

用ghost module组合的模块,有两种:

步长为1用于扩张维度,步长为2时增加一个深度卷积减少尺寸以和shortcut相加

ShuffleNet

论文

https://arxiv.org/pdf/1707.01083v2
https://arxiv.org/pdf/1807.11164v1

shufflev1

通过分组卷积核通道扰乱(shuffle)改进resnet,解决了分组卷积间通道信息不流动的问题

shuffle模块代码中先将已经concat的两个分组卷积的第二个维度num_channels拆分成groups, channels_per_group两个维度,使用transpose转置将groups和channels_per_group互换,然后再用view展开到新维度,也就是将concat后的分组卷积第二个维度互换了

    def forward(self, x):
    # 分组卷积,shufflenet都是是2个组
        if self.stride == 1:
            x1, x2 = x.chunk(2, dim=1)
            out = torch.cat((x1, self.branch2(x2)), dim=1)
        else:
            out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)

    # 两个组concat后进入shuffle
        out = channel_shuffle(out, 2)

    def channel_shuffle(x, groups):
        # type: (torch.Tensor, int) -> torch.Tensor
        batchsize, num_channels, height, width = x.data.size()
        channels_per_group = num_channels // groups
        # reshape
    # 将num_channels分为groups, channels_per_group
        x = x.view(batchsize, groups, channels_per_group, height, width)
        x = torch.transpose(x, 1, 2).contiguous()
        # flatten
        x = x.view(batchsize, -1, height, width)

        return x

shufflev2

总结FLOPs以外v1对模型推理效率影响的因素:

  1. 输入通道和输出通道保持相同可降低内存访问成本
  2. 分组卷积使用过多的分组导致内存访问成本增加
  3. 网络结构复杂(分支和基本单元种类多)会降低网络并行运算
  4. 逐元素相加操作(element-wise)(包括relu、tensor、偏置这些操作都是每个元素独立进行,硬件会并行执行这些任务但是有线程启动调度开销)
    v1违反了1和2

重新设计了v2基础单元:

新单元中先将通道split取代原本在右分支内的分组卷积满足上述2,左分支没有pool满足3,使用concat直接拼接左右分支取代原本的elements-wise add操作满足4

MobileNet

论文及参考

https://arxiv.org/pdf/1704.04861
https://arxiv.org/pdf/1801.04381v4
https://arxiv.org/pdf/1905.02244
https://blog.csdn.net/weixin_45277161/article/details/130525282

v1

核心为深度可分离卷积,将卷积拆分为depthwise和pointwise两个层:

计算量相当于传统卷积模块的1/9

mobilenet的成功使得2017年开始深度可分离卷积成为移动端边缘设备主流

v2

核心是倒残差模块、替换线性激活函数

resnet的残差是1×1降维、3×3卷积,再1×1升维度
而倒残差就是1×1升维、3x3depthwise、再1×1降维度,而升维即所谓扩张层

很直观升维参数量增大导致特征更丰富,而depthwise保证运算量不大
并且原始残差中检测卷积可以改变维度升维,但depthwise无法升维只能用1×1先升

v1 vs v2:

原本v1的激活函数是relu,v2除了前两个relu6,relu6的输出区间在 [0, 6] 比原本 [0, 无穷] 收敛更快,其余都是线性激活函数,避免信息损失;而所谓线性激活函数就是BN后没有激活函数,输出就是线性

# _*_coding:utf-8_*_
import torch
import torch.nn as nn


class InvertedResidualsBlock(nn.Module):
    def __init__(self, in_channels, out_channels, expansion, stride):
        super(InvertedResidualsBlock, self).__init__()
        channels = expansion * in_channels
        self.stride = stride

        self.basic_block = nn.Sequential(
            nn.Conv2d(in_channels, channels, kernel_size=1, stride=1, bias=False),
            nn.BatchNorm2d(channels),
            nn.ReLU6(inplace=True),
            nn.Conv2d(channels, channels, kernel_size=3, stride=stride, padding=1, groups=channels, bias=False),
            nn.BatchNorm2d(channels),
            nn.ReLU6(inplace=True),
            nn.Conv2d(channels, out_channels, kernel_size=1, stride=1, bias=False),
            nn.BatchNorm2d(out_channels)
        )
        # The shortcut operation does not affect the number of channels
        self.shortcut = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, bias=False),
            nn.BatchNorm2d(out_channels)
        )

    def forward(self, x):
        out = self.basic_block(x)
        if self.stride == 1:
            print("With shortcut!")
            out = out + self.shortcut(x)
        else:
            print("No shortcut!")
        print(out.size())

        return out


if __name__ == "__main__":
    x = torch.randn(16, 3, 32, 32)
    # no shortcut
    net1 = InvertedResidualsBlock(3, 6, 6, 2)
    # with shortcut
    net2 = InvertedResidualsBlock(3, 6, 6, 1)
    y1, y2 = net1(x), net2(x)

v3

核心是又引入了SE注意力机制,h-swish替换了swish,NAS搜索
backbone如下:

其中下面是是v2的倒残差线性结构

下面是v1的深度可分离卷积

轻量级注意力

轻量级注意力机制,即对下一层上层输入有多重要,同过加权决定:

SE的squeeze:
对特征图全局平均池化,得到1x1xc输出,c的每个通道都相当于一部分特征
SE的excitation:
给squeeze得到的每个通道特征评分,通过两个全连接层实现,接着通过sigmoid得到[0, 1]区间输出作为权重
SE可以用在很多网络上融合提升通道维度间的注意力

hard-swish

原版swish:

hard-swish用relu实现对swish近似:

NAS搜索

v3的结构是google automl搜索出的

Github:
https://github.com/MirusUmbra
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇