轻量化的网络模型:SqueezeNet 详解与复现(已解决)
论文地址:SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size
SqueezeNet 在 ImageNet 上实现与 AlexNet 同等级别的精度,但 参数少了50倍 ;
通过模型压缩技术,实现了 SqueezeNet 模型压缩到小于0.5MB,相比 AlexNet 模型小了510倍 ;

模型轻量化优势
深度卷积神经网络(CNNs)的研究主要集中在 提高准确性 方面 ;
对于 同等精度级别 ,通常存在着多种 CNN 网络结构 ;
但是在同样的精度下,更小的 CNN 至少有三方面的 优势 :
(1)模型训练:更小的 CNN 在分布式训练中需要更少的跨服务器通信 ;
(2)模型导出:更小的 CNN 需要更少的带宽来将新模型从云导出到自动驾驶汽车 ;
(3)模型部署:较小的 CNN 更适合部署在FPGA等内存有限的硬件上 ;
模型压缩方法
(1)奇异值分解(singular value decomposition (SVD));
(2)网络剪枝(Network Pruning);
(3)深度压缩(Deep Compression);
设计目标:在保证同等级别准确率的同时,实现用更少参数的 CNN结构 ;
使用的策略:
(1)使用 1 x 1 卷积滤波器代替 3 x 3 卷积 (参数量少9倍);
(2)使用3x3个滤波器减少输入通道的数量,利用 squeeze layers 实现 ;
(3)在网络后期进行下采样操作,可以使卷积层有更大的激活特征图 ;
策略(1)(2)用于减少CNN参数量,策略(3)用于在有限的参数量下最大化CNN准确率;

Fire Module 组成: 主要包括 挤压层(squeeze) 和 拓展层(expand) ;
squeeze :只有 1 x 1 卷积滤波器 ;
expand :混合有 1 x 1 和 3 x 3 卷积滤波器 ;
并引入了三个调节维度的 超参数 :
S1x1 :squeeze 中 1 x 1 卷积滤波器个数 ;
e1x1 :expand 中 1 x 1 卷积滤波器个数 ;
e3x3 :expand 中 3 x 3 卷积滤波器个数 ;
上图中, S1x1 = 3 ,e1x1 = 4 ,e3x3 = 4 ;
当 S1x1 < ( e1x1 + e3x3 )时,squeeze 可以将输入通道的数量限制到 3 x 3 卷积滤波器 ;
其他细节:
(1)为使 1 x 1 和 3 x 3 卷积滤波器的输出激活具有相同的高度和宽度,在 3 x 3 卷积滤波器操作中添加一个1像素的零填充边界 ;
(2)使用 ReLU 函数作为挤压层(squeeze) 和 拓展层(expand)的激活函数 ;
(3)在 fire9 模块后应用了 50%的 Dropout ;
(4)在 SqueezeNet 中没有 全连接层 ,设计灵感来源于 NiN 网络 ;
(5)训练 SqueezeNet 时,学习率设置为 0.04 开始,整个训练过程使用线性降低学习率 ;

SqueezeNet 复现
# Here is the code :import torch
import torch.nn as nn
from torchinfo import summaryclass Fire(nn.Module):def __init__(self, in_channels, squeeze_channels, expand1x1_channels, expand3x3_channels):super(Fire, self).__init__()self.in_channels = in_channelsself.squeeze = nn.Conv2d(in_channels, squeeze_channels, kernel_size=1)self.squeeze_activation = nn.ReLU(inplace=True)self.expand1x1 = nn.Conv2d(squeeze_channels, expand1x1_channels,kernel_size=1)self.expand1x1_activation = nn.ReLU(inplace=True)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 outclass SqueezeNet(nn.Module):def __init__(self, version='1_0', num_classes=1000):super(SqueezeNet, self).__init__()self.num_classes = num_classesif 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 outdef _squeezenet(version, **kwargs):model = SqueezeNet(version, **kwargs)return modeldef squeezenet1_0(**kwargs):return _squeezenet('1_0', **kwargs)def squeezenet1_1(**kwargs):return _squeezenet('1_1', **kwargs)def test():net = squeezenet1_0()y = net(torch.randn(1, 3, 224, 224))print(y.size())summary(net, (1, 3, 224, 224))if __name__ == '__main__':test()
