SqueezeNet詳細解讀
概要:SqueezeNet的工作為以下幾個方面:
- 提出了新的網路架構Fire Module,通過減少引數來進行模型壓縮
- 使用其他方法對提出的SqeezeNet模型進行進一步壓縮
- 對引數空間進行了探索,主要研究了壓縮比和3∗33∗3卷積比例的影響
這篇文章是 SQUEEZENET: ALEXNET-LEVEL ACCURACY WITH 50X FEWER PARAMETERS AND <0.5MB MODEL SIZE 的解讀,在精簡部分內容的同時補充了相關的概念。如有錯誤,敬請指正。
論文連結:http://arxiv.org/abs/1602.07360
程式碼連結:https://github.com/DeepScale/SqueezeNet
ABSTRACT
近來深層卷積網路的主要研究方向集中在提高正確率。對於相同的正確率水平,更小的CNN架構可以提供如下的優勢:
(1)在分散式訓練中,與伺服器通訊需求更小
(2)引數更少,從雲端下載模型的資料量小
(3)更適合在FPGA等記憶體受限的裝置上部署。
基於這些優點,本文提出SqeezeNet。它在ImageNet上實現了和AlexNet相同的正確率,但是隻使用了1/50的引數。更進一步,使用模型壓縮技術,可以將SqueezeNet壓縮到0.5MB,這是AlexNet的1/510。
1 INTRODUCTION AND MOTIVATION
對於一個給定的正確率,通常可以找到多種CNN架構來實現與之相近的正確率。其中,引數數量更少的CNN架構有如下優勢:
(1)更高效的分散式訓練
伺服器間的通訊是分散式CNN訓練的重要限制因素。對於分散式 資料並行 訓練方式,通訊需求和模型引數數量正相關。小模型對通訊需求更低。
(2)減小下載模型到客戶端的額外開銷
比如在自動駕駛中,經常需要更新客戶端模型。更小的模型可以減少通訊的額外開銷,使得更新更加容易。
(3)便於FPGA和嵌入式硬體上的部署
2 RELATED WORK
2.1 MODEL COMPRESSION
常用的模型壓縮技術有:
(1)奇異值分解(singular value decomposition (SVD))1
(2)網路剪枝(Network Pruning)2:使用網路剪枝和稀疏矩陣
(3)深度壓縮(Deep compression)3:使用網路剪枝,數字化和huffman編碼
(4)硬體加速器(hardware accelerator)4
2.2 CNN MICROARCHITECTURE
在設計深度網路架構的過程中,如果手動選擇每一層的濾波器顯得過於繁複。通常先構建由幾個卷積層組成的小模組,再將模組堆疊形成完整的網路。定義這種模組的網路為CNN microarchitecture。
2.3 CNN MACROARCHITECTURE
與模組相對應,定義完整的網路架構為CNN macroarchitecture。在完整的網路架構中,深度是一個重要的引數。
2.4 NEURAL NETWORK DESIGN SPACE EXPLORATION
由於超引數繁多,深度神經網路具有很大的設計空間(design space)。通常進行設計空間探索的方法有:
(1)貝葉斯優化
(2)模擬退火
(3)隨機搜尋
(4)遺傳演算法
3 SQUEEZENET: PRESERVING ACCURACY WITH FEW PARAMETERS
3.1 ARCHITECTURAL DESIGN STRATEGIES
使用以下三個策略來減少SqueezeNet設計引數
(1)使用1∗11∗1卷積代替3∗33∗3 卷積:引數減少為原來的1/9
(2)減少輸入通道數量:這一部分使用squeeze layers來實現
(3)將欠取樣操作延後,可以給卷積層提供更大的啟用圖:更大的啟用圖保留了更多的資訊,可以提供更高的分類準確率
其中,(1)和(2)可以顯著減少引數數量,(3)可以在引數數量受限的情況下提高準確率。
3.2 THE FIRE MODULE
Fire Module是SqueezeNet中的基礎構建模組,如下定義 Fire Module :
- squeeze convolution layer:只使用1∗11∗1 卷積 filter,即以上提到的策略(1)
- expand layer:使用1∗11∗1 和3∗33∗3 卷積 filter的組合
- Fire module中使用3ge可調的超引數:s1x1s1x1(squeeze convolution layer中1∗11∗1 filter的個數)、e1x1e1x1(expand layer中1∗11∗1 filter的個數)、e3x3e3x3(expand layer中3∗33∗3 filter的個數)
- 使用Fire module的過程中,令s1x1s1x1 < e1x1e1x1 + e3x3e3x3,這樣squeeze layer可以限制輸入通道數量,即以上提到的策略(2)
3.3 THE SQUEEZENET ARCHITECTURE
SqueezeNet以卷積層(conv1)開始,接著使用8個Fire modules (fire2-9),最後以卷積層(conv10)結束。每個fire module中的filter數量逐漸增加,並且在conv1, fire4, fire8, 和 conv10這幾層之後使用步長為2的max-pooling,即將池化層放在相對靠後的位置,這使用了以上的策略(3)。
如上圖,左邊為原始的SqueezeNet,中間為包含simple bypass的改進版本,最右側為使用complex bypass的改進版本。在下表中給出了更多的細節。
因為這是一篇講解網路壓縮的文章,這裡順便提一下引數計算的方法。以上表中的fire2模組為例:maxpool1層的輸出為55∗55∗9655∗55∗96,一共有96個通道。之後緊接著的Squeeze層有16個1∗1∗961∗1∗96的卷積filter,注意這裡是多通道卷積,為了避免與二維卷積混淆,在卷積尺寸末尾寫上了通道數。這一層的輸出尺寸為55∗55∗1655∗55∗16,之後將輸出分別送到expand層中的1∗1∗161∗1∗16(64個)和3∗3∗163∗3∗16(64個)進行處理,注意這裡不對16個通道進行切分。為了得到大小相同的輸出,對3∗3∗163∗3∗16的卷積輸入進行尺寸為1的zero padding。分別得到55∗55∗6455∗55∗64和55∗55∗6455∗55∗64大小相同的兩個feature map。將這兩個feature map連線到一起得到55∗55∗12855∗55∗128大小的feature map。考慮到bias引數,這裡的引數總數為:
可以看出,Squeeze層由於使用1∗11∗1卷積極大地壓縮了引數數量,並且進行了降維操作,但是對應的代價是輸出特徵圖的通道數(維數)也大大減少。之後的expand層使用不同尺寸的卷積模板來提取特徵,同時將兩個輸出連線到一起,又將維度升高。但是3∗3∗163∗3∗16的卷積模板引數較多,遠超1∗11∗1卷積的引數,對減少引數十分不利,所以作者又針對3∗3∗163∗3∗16卷積進行了剪枝操作以減少引數數量。從網路整體來看,feature map的尺寸不斷減小,通道數不斷增加,最後使用平均池化將輸出轉換成1∗1∗10001∗1∗1000完成分類任務。
3.3.1 OTHER SQUEEZENET DETAILS
以下是網路設計中的一些要點:
(1)為了使 1∗11∗1 和 3∗33∗3 filter輸出的結果又相同的尺寸,在expand modules中,給3∗33∗3 filter的原始輸入新增一個畫素的邊界(zero-padding)。
(2)squeeze 和 expand layers中都是用ReLU作為啟用函式
(3)在fire9 module之後,使用Dropout,比例取50%
(4)注意到SqueezeNet中沒有全連線層,這借鑑了Network in network的思想
(5)訓練過程中,初始學習率設定為0.04,,在訓練過程中線性降低學習率。更多的細節參見本專案在github中的配置檔案。
(6)由於Caffee中不支援使用兩個不同尺寸的filter,在expand layer中實際上是使用了兩個單獨的卷積層(1∗11∗1filter 和 3∗33∗3 filter),最後將這兩層的輸出連線在一起,這在數值上等價於使用單層但是包含兩個不同尺寸的filter。
在github上還有SqueezeNet在其他框架下的實現:MXNet、Chainer、Keras、Torch。
4 EVALUATION OF SQUEEZENET
在表2中,以AlexNet為標準來比較不同壓縮方法的效果。
SVD方法能將預訓練的AlexNet模型壓縮為原先的1/5,top1正確率略微降低。網路剪枝的方法能將模型壓縮到原來的1/9,top1和top5正確率幾乎保持不變。深度壓縮能將模型壓縮到原先的1/35,正確率基本不變。SqeezeNet的壓縮倍率可以達到50以上,並且正確率還能有 略微的提升。注意到幾時使用未進行壓縮的32位數值精度來表示模型,SqeezeNet也比壓縮率最高的模型更小,同時表現也更好。
如果將深度壓縮(Deep Compression)的方法用在SqeezeNet上,使用33%的稀疏表示和8位精度,會得到一個僅有0.66MB的模型。進一步,如果使用6位精度,會得到僅有0.47MB的模型,同時正確率不變。
此外,結果表明深度壓縮不僅對包含龐大引數數量的CNN網路作用,對於較小的網路,比如SqueezeNet,也是有用的。將SqueezeNet的網路架構創新和深度壓縮結合起來可以將原模型壓縮到1/510。
5 CNN MICROARCHITECTURE DESIGN SPACE EXPLORATION
5.1 CNN MICROARCHITECTURE METAPARAMETERS
在SqueezeNet中,每一個Fire module有3個維度的超引數,即s1x1s1x1 、 e1x1e1x1 和 e3x3e3x3。SqueezeNet一共有8個Fire modules,即一共24個超引數。下面討論其中一些重要的超引數的影響。為方便研究,定義如下引數:
- baseebasee:Fire module中expand filter的個數
- freqfreq:Fire module的個數
- increeincree:在每freqfreq個Fire module之後增加的expand filter個數
- eiei:第ii 個Fire module中,expand filters的個數
- SRSR:壓縮比,即the squeeze ratio ,為squeeze layer中filter個數除以Fire module中filter總個數得到的一個比例
- pct3x3pct3x3:在expand layer有1∗11∗1和3∗33∗3兩種卷積,這裡定義的引數是3∗33∗3卷積個佔卷積總個數的比例
下圖為實驗結果:
5.2 SQUEEZE RATIO
Figure 3 中左圖給出了壓縮比(SR)的影響。壓縮比小於0.25時,正確率開始顯著下降。
5.3 TRADING OFF 1X1 AND 3X3 FILTERS
Figure 3 中右圖給出了3∗33∗3卷積比例的影響,在比例小於25%時,正確率開始顯著下降,此時模型大小約為原先的44%。超過50%後,模型大小顯著增加,但是正確率不再上升。
6 CNN MACROARCHITECTURE DESIGN SPACE EXPLORATION
受ResNet啟發,這裡探究旁路連線(bypass conection)的影響。在Figure 2中展示了三種不同的網路架構。下表給出了實驗結果:
注意到使用旁路連線後正確率確實有一定提高。
7 Conclusions
在SqueezeNet提出後,Dense-Sparse-Dense (DSD)5使用了新的方法來進行壓縮同時提高了精度。
8 Implementation
在這裡我們分析SqeezeNet的PyTorch實現,以加深對網路架構的理解。原始碼可見:https://github.com/pytorch/vision/blob/master/torchvision/models/squeezenet.py
為方便分析,除去其中不必要的程式碼。
8.1 Fire module的實現
首先Fire類是對torch.nn.Modules類進行擴充得到的,需要繼承Modules類,並實現__init__()
方法,以及forward()方法
。其中,__init__()
方法用於定義一些新的屬性,這些屬性可以包括Modules的例項,如一個torch.nn.Conv2d,nn.ReLU等。即建立該網路中的子網路,在建立這些子網路時,這些網路的引數也被初始化。接著使用super(Fire, self).__init__()
呼叫基類初始化函式對基類進行初始化。
首先,在Fire類的__init__()
函式中,定義瞭如下幾個新增的屬性:
1. inplanes:輸入向量
2. squeeze:sqeeze layer,由二維1∗11∗1卷積組成。其中,參考PyTorch文件,torch.nn.Conv2d
的定義為class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
,程式碼中inplanes為輸入通道,squeeze_planes為輸出通道,卷積模板尺寸為1∗11∗1.
3. expand1x1:expand layer中的1∗11∗1卷積。
4. expand3x3:expand layer中的3∗33∗3卷積。注意,為了使 1∗11∗1 和 3∗33∗3 filter輸出的結果有相同的尺寸,在expand modules中,給3∗3 filter的原始輸入新增一個畫素的邊界(zero-padding)
5. 所有的啟用函式都選擇ReLU。注意inplace=True
引數可以在原始的輸入上直接進行操作,不會再為輸出分配額外的記憶體,可以節省一部分記憶體,但同時也會破壞原始的輸入。
之後實現foward
方法,整個流程如下:
首先,將輸入x經過squeeze layer進行卷積操作,再經過 squeeze_activation()
進行啟用,然後將輸出分別送到expand1x1expand1x1和expand3x3expand3x3中進行卷積和啟用操作,最後,使用torch.cat()
可以將expand1x1_activation
和 expand3x3_activation
這兩個維度相同的輸出張量連線在一起。注意這裡的dim=1,即按照列連線,最終得到若干行。
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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
以上實現了SqeezeNet中最重要的Fire module,為搭建整體網路做好了準備。接下來定義SqueezeNet類,它同樣繼承自nn.Module,這裡實現了version=1.0和version=1.1兩個SqueezeNet版本。區別在於1.0只有 AlexNet的1/50的引數,而1.1在1.0的基礎上進一步壓縮,引數略微減少,計算量降低為1.0的40%左右。SqueezeNet類定義瞭如下屬性:
1. num_classes:分類的類別個數
2. self.features:定義了主要的網路層,nn.Sequential()
是PyTorch中的序列容器(sequential container),可以按照順序將Modules新增到其中,這也是網路巨集觀架構實現的重要步驟。要理解這一部分程式碼,最好結合表1中的細節,並且自己計算一遍卷積操作後的feature map的尺寸。注意表1中的輸入圖片尺寸是227∗227227∗227而不是224∗224224∗224,否則跟之後的輸出尺寸對不上。
3. 注意nn.MaxPool2d()
函式中ceil_mode=True
會對池化結果進行向上取整而不是向下取整。
4. 最後一個卷積層的初始化方法與其他層不同,在接下來的for迴圈中,定義了不同層的初始化方法,可以看到,最後一層使用了均值為0,方差為0.01的正太分佈初始化方法,其餘層使用He Kaiming論文中的均勻分佈初始化方法。同時這裡使用了num_classes
引數,可以調整分類類別數目。並且所有的bias初始化為零。
5. self.classifier:定義了網路末尾的分類器模組,注意其中使用了nn.Dropout()
6. forward()方法:可以看到由features模組和classifier模組構成。
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.AvgPool2d(13)
)
for m in self.modules():
if isinstance(m, nn.Conv2d):
if m is final_conv:
init.normal(m.weight.data, mean=0.0, std=0.01)
else:
init.kaiming_uniform(m.weight.data)
if m.bias is not None:
m.bias.data.zero_()
def forward(self, x):
x = self.features(x)
x = self.classifier(x)
return x.view(x.size(0), self.num_classes)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
在之後的程式碼中定義了完整的squeezenet1_0
和squeezenet1_1
,如果需要的話,可以使用PyTorch的預訓練模型。
def squeezenet1_0(pretrained=False, **kwargs):
r"""SqueezeNet model architecture from the `"SqueezeNet: AlexNet-level
accuracy with 50x fewer parameters and <0.5MB model size"
<https://arxiv.org/abs/1602.07360>`_ paper.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = SqueezeNet(version=1.0, **kwargs)
if pretrained:
model.load_state_dict(model_zoo.load_url(model_urls['squeezenet1_0']))
return model
def squeezenet1_1(pretrained=False, **kwargs):
r"""SqueezeNet 1.1 model from the `official SqueezeNet repo
<https://github.com/DeepScale/SqueezeNet/tree/master/SqueezeNet_v1.1>`_.
SqueezeNet 1.1 has 2.4x less computation and slightly fewer parameters
than SqueezeNet 1.0, without sacrificing accuracy.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = SqueezeNet(version=1.1, **kwargs)
if pretrained:
model.load_state_dict(model_zoo.load_url(model_urls['squeezenet1_1']))
return model
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
Refference
- E.L Denton, W. Zaremba, J. Bruna, Y. LeCun, and R. Fergus. Exploiting linear structure within
convolutional networks for efficient evaluation. In NIPS, 2014. ↩ - S. Han, J. Pool, J. Tran, and W. Dally. Learning both weights and connections for efficient neural
networks. In NIPS, 2015b. ↩ - S. Han, H. Mao, and W. Dally. Deep compression: Compressing DNNs with pruning, trained
quantization and huffman coding. arxiv:1510.00149v3, 2015a. ↩ - Song Han, Xingyu Liu, Huizi Mao, Jing Pu, Ardavan Pedram, Mark A Horowitz, and William J
Dally. Eie: Efficient inference engine on compressed deep neural network. International Symposium
on Computer Architecture (ISCA), 2016a. ↩ - Song Han, Jeff Pool, Sharan Narang, Huizi Mao, Shijian Tang, Erich Elsen, Bryan Catanzaro, John
Tran, and William J. Dally. Dsd: Regularizing deep neural networks with dense-sparse-dense
training flow. arXiv:1607.04381, 2016b. ↩
相關文章
- 手寫 Promise 詳細解讀Promise
- Oracle SCN機制詳細解讀Oracle
- 矩陣分解--超詳細解讀矩陣
- Android BLE藍芽詳細解讀Android藍芽
- 詳細解讀go語言中的chnanelGoNaN
- Redis 主從複製詳細解讀Redis
- 手寫 call apply bind 詳細解讀APP
- A*尋路演算法詳細解讀演算法
- 詳細解讀阿里手冊之MySQL阿里MySql
- 詳細解讀微服務的兩種模式微服務模式
- Java面試-List中的sort詳細解讀Java面試
- 生命週期詳細解讀(含部分原始碼)原始碼
- C++指標的概念解讀 超詳細C++指標
- 詳細解讀Service Mesh的資料面Envoy
- [轉帖]記憶體分析之GCViewer詳細解讀記憶體GCView
- 詳細解讀!推薦演算法架構——召回演算法架構
- 史上最詳細ConvLstm的pytorch程式碼解讀分析PyTorch
- 【UGUI原始碼分析】Unity遮罩之Mask詳細解讀UGUI原始碼Unity遮罩
- sed編輯器的使用以及詳細解讀
- Robot Framework(12)- 詳細解讀 RF 的變數和常量Framework變數
- winscp操作說明,winscp操作說明的詳細解讀
- 全網最詳細解讀《GIN-HOW POWERFUL ARE GRAPH NEURAL NETWORKS》!!!
- 詳細解讀:不同RAID級別的優缺點對比AI
- 帶你詳細解讀十條關於SQL效能優化!SQL優化
- 【恩墨學院】深入剖析 - Oracle SCN機制詳細解讀Oracle
- Java註解最全詳解(超級詳細)Java
- nginx 詳解 - 詳細配置說明Nginx
- nginx 詳解 – 詳細配置說明Nginx
- SpringBoot註解最全詳解(整合超詳細版本)Spring Boot
- 從12個方面詳細解讀Pulsar的主題與訂閱
- 超詳細的 Bert 文字分類原始碼解讀 | 附原始碼文字分類原始碼
- 【UGUI原始碼分析】Unity遮罩之RectMask2D詳細解讀UGUI原始碼Unity遮罩
- redux v3.7.2原始碼詳細解讀與學習之composeRedux原始碼
- SpringBoot註解大全(詳細)Spring Boot
- Servlet、HTTP詳細解釋!ServletHTTP
- JPS 命令詳細解釋
- dart類詳細講解Dart
- table細線表格詳解