【卷积神经网络系列】十四、SqueezeNet


参考资料:

论文:

  SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size

博客:

  SqueezeNet 详解与复现

  SqueezeNet详解

  SqueezeNet详细解读


一、前言

(1)模型轻量化优势

 近年来,深度卷积神经网络(CNNs)的研究主要集中在 提高准确性 方面 ;对于 同等精度级别 ,通常存在着多种 CNN 网络结构 ;但是在同样的精度下,更小的 CNN 至少有三方面的 优势

 ①模型训练:更小的 CNN 在分布式训练中需要更少的跨服务器通信 ;
 ②模型导出:更小的 CNN 需要更少的带宽来将新模型从云导出到自动驾驶汽车 ;
 ③模型部署:较小的 CNN 更适合部署在FPGA等内存有限的硬件上 ;

(2)模型压缩方法

 ①奇异值分解(singular value decomposition (SVD));
 ②网络剪枝(Network Pruning);
 ③深度压缩(Deep Compression);

 从LeNet5到DenseNet,反应卷积网络的一个发展方向:提高精度。这里我们开始另外一个方向的介绍:在不大幅降低模型精度的前提下,最大程度的提高运算速度

 提高运算所读有两个可以调整的方向:

  • 1.减少可学习参数的数量;
  • 2.减少整个网络的计算量。

 这个方向带来的效果是非常明显的:

  • 1.减少模型训练和测试时候的计算量,单个step的速度更快;
  • 2.减小模型文件的大小,更利于模型的保存和传输;
  • 3.可学习参数更少,网络占用的显存更小。

 SqueezeNet正是诞生在这个环境下的一个精度的网络,它能够在ImageNet数据集上达到AlexNet近似的效果,但是参数比AlexNet少50倍,结合他们的模型压缩技术Deep Compression,模型文件可比AlexNet小510倍。


二、网络结构详解

2.1 压缩策略

 SqueezeNet的模型压缩使用了3个策略:

  • (1)将 3×3 卷积替换成 1×1 卷积:通过这一步,一个卷积操作的参数数量减少了9倍;

  • (2)减少 3×3 卷积的通道数:一个 3×3 卷积的计算量是 3×3×M×N (其中 M , N 分别是输入Feature Map和输出Feature Map的通道数),作者认为这样一个计算量过于庞大,因此希望将 M , N 减小以减少参数数量,利用 squeeze layers 实现 ;

  • (3)将降采样后置:作者认为较大的Feature Map含有更多的信息,因此将降采样往分类层移动。注意这样的操作虽然会提升网络的精度,但是它有一个非常严重的缺点:即会增加网络的计算量。

 策略(1)(2)用于减少CNN参数量,策略(3)用于在有限的参数量下最大化CNN准确率;


2.2 Fire Module

Fire Module 组成: 主要包括 挤压层(squeeze) 和 拓展层(expand) ;

  • squeeze :只有 1 × 1 1 \times 1 1×1 卷积滤波器 ;
  • expand :混合有 1 × 1 1 \times 1 1×1 3 × 3 3 \times 3 3×3 卷积滤波器 ;

在这里插入图片描述

 并引入了三个调节维度的 超参数

  • s 1 × 1 s_{1 \times 1} s1×1squeeze 中 1 x 1 卷积滤波器个数 ;
  • e 1 × 1 e_{1 \times 1} e1×1expand 中 1 x 1 卷积滤波器个数 ;
  • e 3 × 3 e_{3 \times 3} e3×3expand 中 3 x 3 卷积滤波器个数 ;

 使用Fire module的过程中,令s1x1s1x1 < e1x1e1x1 + e3x3e3x3,这样squeeze layer可以限制输入通道数量,即以上提到的策略(2)


2.3 SqueezeNet的网络架构

 下图是SqueezeNet的几个实现,左侧是不加short-cut的SqueezeNet,中间是加了short-cut的,右侧是short-cut跨有不同Feature Map个数的卷积的。

在这里插入图片描述

在这里插入图片描述

其他细节:

  • (1)为使 1 x 1 和 3 x 3 卷积滤波器的输出激活具有相同的高度和宽度,在 3 x 3 卷积滤波器操作中添加padding=1
  • (2)使用 ReLU 函数作为挤压层(squeeze) 和 拓展层(expand)的激活函数 ;
  • (3)在 Fire9 模块后应用了 50%的 Dropout
  • (4)在 SqueezeNet 中没有 全连接层 ,设计灵感来源于 NiN 网络 ;
  • (5)训练 SqueezeNet 时,学习率设置为 0.04 开始,整个训练过程使用线性降低学习率 ;

2.4 SqueezeNet 结构探索

(1)微观结构探索

微观结构探索( 每个模块层的维度和配置 ):

 使用了以下超参数进行实验分析:

  • Squeeze Ratio(挤压比,SR) s 1 × 1 = S R ∗ ( e 1 × 1 + e 3 × 3 ) s_{1 \times 1} = SR * ( e_{1 \times 1} + e_{3 \times 3} ) s1×1=SR(e1×1+e3×3)

  • Percentage of 3x3 filters :在expand layer有1x1和3x3两种卷积,这里定义的参数是3x3卷积个占卷积总个数的比例;

在这里插入图片描述

(2)宏观结构探索

宏观结构探索( 模块与其他层之间的高层次端到端结构 ):

 作者通过前面所说到的三个结构对 SqueezeNet 的宏观结构进行了实验探索 ;

 ( SqueezeNet - 带简单旁路的 SqueezeNet - 带复杂旁路的 SqueezeNet )

 作者使用了三种宏观架构对 SqueezeNet 进行了训练,并对它们的准确率和模型大小进行了对比 ;

有趣的是,简单旁路比复杂旁路具有更高的精度提高 ;

在这里插入图片描述


三、总结

 SqueezeNet的压缩策略是依靠将 3×3 卷积替换成 1×1 卷积来达到的,其参数数量是等性能的AlexNet的2.14%。从参数数量上来看,SqueezeNet的目的达到了。SqueezeNet的最大贡献在于其开拓了模型压缩这一方向,之后的一系列文章也就此打开。

 这里我们着重说一下SqueezeNet的缺点:

  • SqueezeNet的侧重的应用方向是嵌入式环境,目前嵌入式环境主要问题是实时性。SqueezeNet的通过更深的深度置换更少的参数数量虽然能减少网络的参数,但是其丧失了网络的并行能力,测试时间反而会更长,这与目前的主要挑战是背道而驰的

为什么会丧失网络的并行性?

 SqueezeNet的Fire模块的两个分支的计算方式不同,在GPU并行计算两个分支时,运算量较小的分支会等待运算量较大的分支,于是丧失了网络的并行性,因为小分支的计算量小的优点无法体现出来。

  • 论文的题目非常标题党,虽然纸面上是减少了50倍的参数,但是问题的主要症结在于AlexNet本身全连接节点过于庞大,50倍参数的减少和SqueezeNet的设计并没有关系,考虑去掉全连接之后3倍参数的减少更为合适。
  • SqueezeNet得到的模型是5MB左右,0.5MB的模型还要得益于Deep Compression。虽然Deep Compression也是这个团队的文章,但是将0.5这个数列在文章的题目中显然不是很合适。

四、论文复现

参考

  SqueezeNet 详解与复现


(1)Fire模块:

在这里插入图片描述

class Fire(nn.Module):
    def __init__(self, in_channels, squeeze_channels, expand1x1_channels, expand3x3_channels):
        super(Fire, self).__init__()
        self.in_channels = in_channels
        
        # squeeze layers
        self.squeeze = nn.Conv2d(in_channels, squeeze_channels, kernel_size=1)
        self.squeeze_activation = nn.ReLU(inplace=True)
        
        # expand layers - 1x1
        self.expand1x1 = nn.Conv2d(squeeze_channels, expand1x1_channels,
                                   kernel_size=1)
        self.expand1x1_activation = nn.ReLU(inplace=True)
        
        # expand layers -3x3
        self.expand3x3 = nn.Conv2d(squeeze_channels, expand3x3_channels,
                                   kernel_size=3, padding=1)
        self.expand3x3_activation = nn.ReLU(inplace=True)

    def forward(self, x):
        x = self.squeeze_activation(self.squeeze(x))
        e1 = self.expand1x1_activation(self.expand1x1(x))
        e2 = self.expand3x3_activation(self.expand3x3(x))
        out = torch.cat([e1, e2], 1)
        return out

(2)主体网络结构:

在这里插入图片描述
在这里插入图片描述

class SqueezeNet(nn.Module):
    def __init__(self, version='1_0', num_classes=1000):
        super(SqueezeNet, self).__init__()
        self.num_classes = num_classes
        if version == '1_0':
            self.features = nn.Sequential(
                nn.Conv2d(3, 96, kernel_size=7, stride=2),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(96, 16, 64, 64),
                Fire(128, 16, 64, 64),
                Fire(128, 32, 128, 128),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(256, 32, 128, 128),
                Fire(256, 48, 192, 192),
                Fire(384, 48, 192, 192),
                Fire(384, 64, 256, 256),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(512, 64, 256, 256),
            )

        elif version == '1_1':
            self.features = nn.Sequential(
                nn.Conv2d(3, 64, kernel_size=3, stride=2),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(64, 16, 64, 64),
                Fire(128, 16, 64, 64),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(128, 32, 128, 128),
                Fire(256, 32, 128, 128),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(256, 48, 192, 192),
                Fire(384, 48, 192, 192),
                Fire(384, 64, 256, 256),
                Fire(512, 64, 256, 256),
            )

        final_conv = nn.Conv2d(512, self.num_classes, kernel_size=1)
        
        self.classifier = nn.Sequential(
            nn.Dropout(p=0.5),
            final_conv,
            nn.ReLU(inplace=True),
            nn.AdaptiveAvgPool2d((1, 1))
        )

    def forward(self, x):
        x = self.features(x)
        x = self.classifier(x)
        out = torch.flatten(x, 1)
        return out
def _squeezenet(version, **kwargs):
    model = SqueezeNet(version, **kwargs)
    return model


def squeezenet1_0(**kwargs):
    return _squeezenet('1_0', **kwargs)


def squeezenet1_1(**kwargs):
    return _squeezenet('1_1', **kwargs)


def test():
    net = squeezenet1_0()
    #创建模型,部署gpu
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    net.to(device)
    summary(net, (3, 224, 224))


if __name__ == '__main__':
    test()
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

travellerss

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

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

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

打赏作者

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

抵扣说明:

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

余额充值