Author :Horizon Max
✨ 编程技巧篇:各种操作小结
机器视觉篇:会变魔术 OpenCV
深度学习篇:简单入门 PyTorch
神经网络篇:经典网络模型
算法篇:再忙也别忘了 LeetCode
SqueezeNet 在 ImageNet 上实现与 AlexNet 同等级别的精度,但 参数少了50倍 ;
通过模型压缩技术,实现了 SqueezeNet 模型压缩到小于0.5MB,相比 AlexNet 模型小了510倍 ;
论文地址:SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size
近年来,深度卷积神经网络(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 ;
中图:带简单旁路的 SqueezeNet ;
右图:带复杂旁路的 SqueezeNet ;
结构探索主要分为两个部分:
微观结构探索
( 每个模块层的维度和配置 ):
使用了以下超参数进行实验分析:
Squeeze Ratio(挤压比,SR): S1x1 = SR * ( e1x1 + e3x3 );
Percentage of 3x3 filters :从 “mostly 1x1” 到 “mostly 3x3” 进行测试 ;
宏观结构探索
( 模块与其他层之间的高层次端到端结构 ):
作者通过前面所说到的三个结构对 SqueezeNet 的宏观结构进行了实验探索 ;
( SqueezeNet - 带简单旁路的 SqueezeNet - 带复杂旁路的 SqueezeNet )
作者使用了三种宏观架构对 SqueezeNet
进行了训练,并对它们的准确率和模型大小进行了对比 ;
有趣的是,简单旁路比复杂旁路具有更高的精度提高 ;
# Here is the code :
import torch
import torch.nn as nn
from torchinfo import summary
class Fire(nn.Module):
def __init__(self, in_channels, squeeze_channels, expand1x1_channels, expand3x3_channels):
super(Fire, self).__init__()
self.in_channels = in_channels
self.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 out
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()
y = net(torch.randn(1, 3, 224, 224))
print(y.size())
summary(net, (1, 3, 224, 224))
if __name__ == '__main__':
test()
输出结果:
torch.Size([1, 1000])
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
SqueezeNet -- --
├─Sequential: 1-1 [1, 512, 13, 13] --
│ └─Conv2d: 2-1 [1, 96, 109, 109] 14,208
│ └─ReLU: 2-2 [1, 96, 109, 109] --
│ └─MaxPool2d: 2-3 [1, 96, 54, 54] --
│ └─Fire: 2-4 [1, 128, 54, 54] --
│ │ └─Conv2d: 3-1 [1, 16, 54, 54] 1,552
│ │ └─ReLU: 3-2 [1, 16, 54, 54] --
│ │ └─Conv2d: 3-3 [1, 64, 54, 54] 1,088
│ │ └─ReLU: 3-4 [1, 64, 54, 54] --
│ │ └─Conv2d: 3-5 [1, 64, 54, 54] 9,280
│ │ └─ReLU: 3-6 [1, 64, 54, 54] --
│ └─Fire: 2-5 [1, 128, 54, 54] --
│ │ └─Conv2d: 3-7 [1, 16, 54, 54] 2,064
│ │ └─ReLU: 3-8 [1, 16, 54, 54] --
│ │ └─Conv2d: 3-9 [1, 64, 54, 54] 1,088
│ │ └─ReLU: 3-10 [1, 64, 54, 54] --
│ │ └─Conv2d: 3-11 [1, 64, 54, 54] 9,280
│ │ └─ReLU: 3-12 [1, 64, 54, 54] --
│ └─Fire: 2-6 [1, 256, 54, 54] --
│ │ └─Conv2d: 3-13 [1, 32, 54, 54] 4,128
│ │ └─ReLU: 3-14 [1, 32, 54, 54] --
│ │ └─Conv2d: 3-15 [1, 128, 54, 54] 4,224
│ │ └─ReLU: 3-16 [1, 128, 54, 54] --
│ │ └─Conv2d: 3-17 [1, 128, 54, 54] 36,992
│ │ └─ReLU: 3-18 [1, 128, 54, 54] --
│ └─MaxPool2d: 2-7 [1, 256, 27, 27] --
│ └─Fire: 2-8 [1, 256, 27, 27] --
│ │ └─Conv2d: 3-19 [1, 32, 27, 27] 8,224
│ │ └─ReLU: 3-20 [1, 32, 27, 27] --
│ │ └─Conv2d: 3-21 [1, 128, 27, 27] 4,224
│ │ └─ReLU: 3-22 [1, 128, 27, 27] --
│ │ └─Conv2d: 3-23 [1, 128, 27, 27] 36,992
│ │ └─ReLU: 3-24 [1, 128, 27, 27] --
│ └─Fire: 2-9 [1, 384, 27, 27] --
│ │ └─Conv2d: 3-25 [1, 48, 27, 27] 12,336
│ │ └─ReLU: 3-26 [1, 48, 27, 27] --
│ │ └─Conv2d: 3-27 [1, 192, 27, 27] 9,408
│ │ └─ReLU: 3-28 [1, 192, 27, 27] --
│ │ └─Conv2d: 3-29 [1, 192, 27, 27] 83,136
│ │ └─ReLU: 3-30 [1, 192, 27, 27] --
│ └─Fire: 2-10 [1, 384, 27, 27] --
│ │ └─Conv2d: 3-31 [1, 48, 27, 27] 18,480
│ │ └─ReLU: 3-32 [1, 48, 27, 27] --
│ │ └─Conv2d: 3-33 [1, 192, 27, 27] 9,408
│ │ └─ReLU: 3-34 [1, 192, 27, 27] --
│ │ └─Conv2d: 3-35 [1, 192, 27, 27] 83,136
│ │ └─ReLU: 3-36 [1, 192, 27, 27] --
│ └─Fire: 2-11 [1, 512, 27, 27] --
│ │ └─Conv2d: 3-37 [1, 64, 27, 27] 24,640
│ │ └─ReLU: 3-38 [1, 64, 27, 27] --
│ │ └─Conv2d: 3-39 [1, 256, 27, 27] 16,640
│ │ └─ReLU: 3-40 [1, 256, 27, 27] --
│ │ └─Conv2d: 3-41 [1, 256, 27, 27] 147,712
│ │ └─ReLU: 3-42 [1, 256, 27, 27] --
│ └─MaxPool2d: 2-12 [1, 512, 13, 13] --
│ └─Fire: 2-13 [1, 512, 13, 13] --
│ │ └─Conv2d: 3-43 [1, 64, 13, 13] 32,832
│ │ └─ReLU: 3-44 [1, 64, 13, 13] --
│ │ └─Conv2d: 3-45 [1, 256, 13, 13] 16,640
│ │ └─ReLU: 3-46 [1, 256, 13, 13] --
│ │ └─Conv2d: 3-47 [1, 256, 13, 13] 147,712
│ │ └─ReLU: 3-48 [1, 256, 13, 13] --
├─Sequential: 1-2 [1, 1000, 1, 1] --
│ └─Dropout: 2-14 [1, 512, 13, 13] --
│ └─Conv2d: 2-15 [1, 1000, 13, 13] 513,000
│ └─ReLU: 2-16 [1, 1000, 13, 13] --
│ └─AdaptiveAvgPool2d: 2-17 [1, 1000, 1, 1] --
==========================================================================================
Total params: 1,248,424
Trainable params: 1,248,424
Non-trainable params: 0
Total mult-adds (M): 823.27
==========================================================================================
Input size (MB): 0.60
Forward/backward pass size (MB): 34.77
Params size (MB): 4.99
Estimated Total Size (MB): 40.37
==========================================================================================