Pytorch实现SqueezeNet

参考链接:https://blog.csdn.net/csdnldp/article/details/78648543
https://blog.csdn.net/u011995719/article/details/78908755
原论文:https://arxiv.org/pdf/1602.07360.pdf
SqueezeNet 于2016年11月发布,是一个人工设计的轻量化网络,它在ImageNet上实现了和AlexNet相同水平的正确率,但是只使用了1/50的参数。更进一步,使用模型压缩技术,可以将SqueezeNet压缩到0.5MB,这是AlexNet的1/510。
引入了两个术语CNN微结构(microarchitecture)CNN宏结构(macroarchitecture)

  • CNN微结构: 由层或几个卷积层组成的小模块,如inception模块。
  • CNN微结构: 由层或模块组成的完整的网络结构,此时深度是一个重要的参数。

SqueezeNet结构设计

网络结构的设计策略
(1)代替3x3的滤波器为1x1,这样会减少9倍的参数。
(2)减少输入到3x3滤波器的输入通道,这样可以进一步减少参数,本文使用squeeze层来实现。
(3)降采样操作延后,可以给卷积层更大的激活特征图,意味着保留的信息更多,可以提升准确率。
策略(1)(2)是减少参数的方案,(3)是在限制参数预算的情况下最大化准确率。
Fire模块
作者引入了Fire模块来构造CNN,此模块成功地应用了上述的3个策略。
在这里插入图片描述
模块由squeeze层和expand层组成,squeeze层由1x1的卷积层组成,,可以减少输入expand层的特征图的输入通道。expand层由1x1和3x3的卷积混合而成,且 s 1 x 1 &lt; e 1 x 1 + e 3 x 3 s_{1x1}&lt;e_{1x1}+e_{3x3} s1x1<e1x1+e3x3,称为扩展了特征图。

full结构
以普通的卷积层(conv1)开始,接着连接8个Fire(2-9)模块,最后以卷积层(conv10)结束。每个Fire模块的filter数量逐渐增加,并且在conv1,Fire4,Fire8和conv10后使用步长为2的max-pooling,这种相对延迟的pooling符合了策略(3)。如下作者对比了添加跳跃层的squeezenet:
在这里插入图片描述
其他的实验细节:

  • 在3x3的filter之前特征图用0填充1像素边缘,使3x3与1x1卷积的输出具有相同的高度和宽度。
  • 在squeeze层和expand层之间使用ReLU函数激励
  • Fire9模块之后使用Dropout层,比例为50%。
  • 最后不使用全连接层的思想来源于Network in network。

网络结构维度信息和压缩情况
论文使用Han Song提出的Deep Compression对网络进行进一步压缩。
在这里插入图片描述
CNN microarchitecture(Fire模块hyper-parameter分析)
对于一个Fire模块,要确定各超参数:squeeze层的filter数量 s 1 x 1 s_{1x1} s1x1和expand层的filter数量 s 1 x 1 , s 3 x 3 s_{1x1},s_{3x3} s1x1,s3x3。在搭建CNN时,会进行Fire模块的堆叠,此时从上到下的Fire模块被标记为 i = 1 , 2 , . . . i=1,2,... i=1,2,...。每增长 f r e q freq freq个模块,expand层的filter增加 i n c r e incr_{e} incre。因此,对于第 i i i个Fire模块,其filter数量 e i = s 1 x 1 + s 3 x 3 = b a s e e + ( i n c r e ∗ ⌊ i f r e q ⌋ ) e_{i}=s_{1x1}+s_{3x3}=base_{e}+ (incr_{e} * \left \lfloor\frac{i}{freq}\right \rfloor) ei=s1x1+s3x3=basee+(increfreqi)
定义3x3卷积在expand层的比例为 p c t 3 x 3 pct_{3x3} pct3x3,定义压缩比 S R SR SR,有 s i , 1 x 1 = S R ∗ e i s_{i,1x1}=SR*e_{i} si,1x1=SRei在squeezenet中,作者定义 b a s e e = 128 , i n c r e = 128 , p c t 3 x 3 = 0.5 , f r e q = 2 , S R = 0.125 base_{e}=128,incr_{e}=128,pct_{3x3}=0.5,freq=2,SR=0.125 basee=128,incre=128,pct3x3=0.5,freq=2,SR=0.125.
作者分析了不同的 p c t 3 x 3 pct_{3x3} pct3x3 S R SR SR对性能的影响。
在这里插入图片描述
对于 S R = 0.75 SR=0.75 SR=0.75,此时为与准确率的plateau,再继续增加为 S R = 1.0 SR=1.0 SR=1.0不能提升准确率但是却增加了模型的复杂度。对于 p c t 3 x 3 pct3x3 pct3x3,继续增加3x3卷积的比例无法提升准确率但是却增加了模型的复杂度。
CNN macroarchitecture(添加bypass连接)
作者对原生的,和添加simple bypass(图2(2),没有增加参数的跳连),以及complex bypass(添加1x1卷积的跳连)的squeezenet进行对比。
在这里插入图片描述
发现加入simple bypass 的结果要比其他两者都高。

用pytorch实现squeezenet

来自Github torch/vision
首先实现Fire模块:

class Fire(nn.Module):

    def __init__(self, inplanes, squeeze_planes,
                 expand1x1_planes, expand3x3_planes):
        super(Fire, self).__init__()
        self.inplanes = inplanes
        self.squeeze = nn.Conv2d(inplanes, squeeze_planes, kernel_size=1)
        self.squeeze_activation = nn.ReLU(inplace=True)
        self.expand1x1 = nn.Conv2d(squeeze_planes, expand1x1_planes,
                                   kernel_size=1)
        self.expand1x1_activation = nn.ReLU(inplace=True)
        self.expand3x3 = nn.Conv2d(squeeze_planes, expand3x3_planes,
                                   kernel_size=3, padding=1)
        self.expand3x3_activation = nn.ReLU(inplace=True)

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

实现squeezenet网络1.0和1.1版本,论文中所述的是1.0版本,1.1版本对1.0做了改进,首先是将第一个普通的conv由7x7改为了3x3。Fire模块的参数维度除in_planes以外保持不变,将max_pool层提前了,减少了计算量。1.1版相比
1.0版具有相同水平的准确率,且计算量减少了2.4x倍,参数量也有轻微减少。

class SqueezeNet(nn.Module):

    def __init__(self, version=1.0, num_classes=1000):
        super(SqueezeNet, self).__init__()
        if version not in [1.0, 1.1]:
            raise ValueError("Unsupported SqueezeNet version {version}:"
                             "1.0 or 1.1 expected".format(version=version))
        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),
            )
        else:
            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 convolution is initialized differently form the rest
        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))
        )

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                if m is final_conv:
                    init.normal_(m.weight, mean=0.0, std=0.01)
                else:
                    init.kaiming_uniform_(m.weight)
                if m.bias is not None:
                    init.constant_(m.bias, 0)

    def forward(self, x):
        x = self.features(x)
        x = self.classifier(x)
        return x.view(x.size(0), self.num_classes)
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值