輕量級卷積神經(jīng)網(wǎng)絡(luò)的設(shè)計技巧

這篇文章將從一個證件檢測網(wǎng)絡(luò)(Retinanet)的輕量化談起,簡潔地介紹,我在實操中使用到的設(shè)計原則和idea,并貼出相關(guān)的參考資料和成果供讀者參考。因此本文是一篇注重工程性、總結(jié)個人觀點的文章,存在不恰當(dāng)?shù)牡胤?,請讀者在評論區(qū)指出,方便交流。
目前已有的輕量網(wǎng)絡(luò)有:MobileNet V2和ShuffleNet v2為代表。在實際業(yè)務(wù)中,Retinanet僅需要檢測證件,不涉及過多的類別物體的定位和分類,因此,我認為僅僅更換上述兩個骨架網(wǎng)絡(luò)來優(yōu)化模型的性能是不夠的,需要針對證件檢測任務(wù),專門設(shè)計一個更加輕量的卷積神經(jīng)網(wǎng)絡(luò)來提取、糅合特征。
設(shè)計原則:
1. 更多的數(shù)據(jù)
輕量的淺層網(wǎng)絡(luò)特征提取能力不如深度網(wǎng)絡(luò),訓(xùn)練也更需要技巧。假設(shè)保證有足夠多的訓(xùn)練的數(shù)據(jù),輕量網(wǎng)絡(luò)訓(xùn)練會更加容易。
Facebook研究院的一篇論文[1]提出了“數(shù)據(jù)蒸餾”的方法。實際上,標注數(shù)據(jù)相對未知數(shù)據(jù)較少,我使用已經(jīng)訓(xùn)練好、效果達標的base resnet50的retinanet來進行自動標注,得到一批10萬張機器標注的數(shù)據(jù)。這為后來的輕量網(wǎng)絡(luò)設(shè)計奠定了數(shù)據(jù)基礎(chǔ)。我認為這是構(gòu)建一個輕量網(wǎng)絡(luò)必要的條件之一,網(wǎng)絡(luò)結(jié)構(gòu)的有效性驗證離不開大量的實驗結(jié)果來評估。
接下來,這一部分我將簡潔地介紹輕量CNN地設(shè)計的四個原則
2. 卷積層的輸入、輸出channels數(shù)目相同時,計算需要的MAC(memory access cost)最少

3. 過多的分組卷積會增加MAC
對于1x1的分組卷積(例如:MobileNetv2的深度可分離卷積采用了分組卷積),其MAC和FLOPS的關(guān)系為:

g代表分組卷積數(shù)量,很明顯g越大,MAC越大。詳細參考[2]
4. 網(wǎng)絡(luò)結(jié)構(gòu)的碎片化會減少可并行計算
這些碎片化更多是指網(wǎng)絡(luò)中的多路徑連接,類似于short-cut,bottle neck等不同層特征融合,還有如FPN。拖慢并行的一個很主要因素是,運算快的模塊總是要等待運算慢的模塊執(zhí)行完畢。

5. Element-wise操作會消耗較多的時間(也就是逐元素操作)
從表中第一行數(shù)據(jù)看出,當(dāng)移除了ReLU和short-cut,大約提升了20%的速度。

以上是從此篇論文[2]中轉(zhuǎn)譯過來的設(shè)計原則,在實操中,這四條原則需要靈活使用。
根據(jù)以上幾個原則進行網(wǎng)絡(luò)的設(shè)計,可以將模型的參數(shù)量、訪存量降低很大一部分。
接下來介紹一些自己總結(jié)的經(jīng)驗。
6. 網(wǎng)絡(luò)的層數(shù)不宜過多
通常18層的網(wǎng)絡(luò)屬于深層網(wǎng)絡(luò),在設(shè)計時,應(yīng)選擇一個參考網(wǎng)絡(luò)基線,我選擇的是resnet18。由于Retinanet使用了FPN特征金字塔網(wǎng)絡(luò)來融合各個不同尺度范圍的特征,因此Retinanet仍然很“重”,需要盡可能壓縮骨架網(wǎng)絡(luò)的冗余,減少深度。
7. 首層卷積層用空洞卷積和深度可分離卷積替換
一個3x3,d=2的空洞卷積在感受野上,可以看作等效于5x5的卷積,提供比普通3x3的卷積更大的感受野,這在網(wǎng)絡(luò)的淺層設(shè)計使用它有益。計算出網(wǎng)絡(luò)各個層占有的MAC和參數(shù)量,將參數(shù)量和計算量“重”的卷積層替換成深度可分離卷積層,可以降低模型的參數(shù)量。
這里提供一個計算pytorch 模型的MAC和FLOPs的python packages[3]。
if __name__ == "__main__":
from ptflops import get_model_complexity_info
net = SNet(num_classes=1)
x = torch.Tensor(1, 3, 224, 224)
net.eval()
if torch.cuda.is_available():
net = net.cuda()
x = x.cuda()
with torch.cuda.device(0):
flops, params = get_model_complexity_info(net, (224, 224), print_per_layer_stat=True, as_strings=True, is_cuda=True)
print("FLOPS:", flops)
print("PARAMS:", params)
output:
(regressionModel): RegressionModel(
0.045 GMac, 27.305% MACs,
(conv1): Conv2d(0.009 GMac, 5.257% MACs, 128, 256, kernel_size=(1, 1), stride=(1, 1))
(act1): ReLU(0.0 GMac, 0.041% MACs, )
(conv2): Conv2d(0.017 GMac, 10.472% MACs, 256, 256, kernel_size=(1, 1), stride=(1, 1))
(act2): ReLU(0.0 GMac, 0.041% MACs, )
(conv3): Conv2d(0.017 GMac, 10.472% MACs, 256, 256, kernel_size=(1, 1), stride=(1, 1))
(act3): ReLU(0.0 GMac, 0.041% MACs, )
(output): Conv2d(0.002 GMac, 0.982% MACs, 256, 24, kernel_size=(1, 1), stride=(1, 1))
)
(classificationModel): ClassificationModel(
0.044 GMac, 26.569% MACs,
(conv1): Conv2d(0.009 GMac, 5.257% MACs, 128, 256, kernel_size=(1, 1), stride=(1, 1))
(act1): ReLU(0.0 GMac, 0.041% MACs, )
(conv2): Conv2d(0.017 GMac, 10.472% MACs, 256, 256, kernel_size=(1, 1), stride=(1, 1))
(act2): ReLU(0.0 GMac, 0.041% MACs, )
(conv3): Conv2d(0.017 GMac, 10.472% MACs, 256, 256, kernel_size=(1, 1), stride=(1, 1))
(act3): ReLU(0.0 GMac, 0.041% MACs, )
(output): Conv2d(0.0 GMac, 0.245% MACs, 256, 6, kernel_size=(1, 1), stride=(1, 1))
(output_act): Sigmoid(0.0 GMac, 0.000% MACs, )
)8. Group Normalization 替換 Batch Normalization
BN在諸多論文中已經(jīng)被證明了一些缺陷,而訓(xùn)練目標檢測網(wǎng)絡(luò)耗費顯存,開銷巨大,通常凍結(jié)BN來訓(xùn)練,原因是小批次會讓BN失效,影響訓(xùn)練的穩(wěn)定性。建議一個BN的替代--GN,pytorch 0.4.1內(nèi)置了GN的支持。
9. 減少不必要的shortcut連接和RELU層
網(wǎng)絡(luò)不夠深,沒有必要使用shortcut連接,不必要的shortcut會增加計算量。RELU與shortcut一樣都會增加計算量。同樣RELU沒有必要每一個卷積后連接(需要實際訓(xùn)練考慮刪減RELU)。
10. 善用1x1卷積
1x1卷積可以改變通道數(shù),而不改變特征圖的空間分辨率,參數(shù)量低,計算效率也高。如使用kernel size=3,stride=1,padding=1,可以保證特征圖的空間分辨率不變,1x1的卷積設(shè)置stride=1,padding=0達到相同的目的,而且1x1卷積運算的效率目前有很多底層算法支持,效率更高。[5x1] x [1x5] 兩個卷積可以替換5x5卷積,同樣可以減少模型參數(shù)。
11. 降低通道數(shù)
降低通道數(shù)可以減少特征圖的輸出大小,顯存占用量下降明顯。參考原則2
12. 設(shè)計一個新的骨架網(wǎng)絡(luò)找對參考網(wǎng)絡(luò)
一個好的骨架網(wǎng)絡(luò)需要大量的實驗來支撐它的驗證,因此在工程上,參考一些實時網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計自己的骨架網(wǎng)絡(luò),事半功倍。我在實踐中,參考了這篇[4]paper的骨架來設(shè)計自己的輕量網(wǎng)絡(luò)。
總結(jié)
我根據(jù)以上的原則和經(jīng)驗對Retinanet進行瘦身,不僅局限于骨架的新設(shè)計,F(xiàn)PN支路瘦身,兩個子網(wǎng)絡(luò)(回歸網(wǎng)絡(luò)和分類網(wǎng)絡(luò))均進行了修改,期望性能指標FPS提升到63,增幅180%。
FPS

mAP

Model size

好消息!
小白學(xué)視覺知識星球
開始面向外開放啦??????
下載1:OpenCV-Contrib擴展模塊中文版教程 在「小白學(xué)視覺」公眾號后臺回復(fù):擴展模塊中文教程,即可下載全網(wǎng)第一份OpenCV擴展模塊教程中文版,涵蓋擴展模塊安裝、SFM算法、立體視覺、目標跟蹤、生物視覺、超分辨率處理等二十多章內(nèi)容。 下載2:Python視覺實戰(zhàn)項目52講 在「小白學(xué)視覺」公眾號后臺回復(fù):Python視覺實戰(zhàn)項目,即可下載包括圖像分割、口罩檢測、車道線檢測、車輛計數(shù)、添加眼線、車牌識別、字符識別、情緒檢測、文本內(nèi)容提取、面部識別等31個視覺實戰(zhàn)項目,助力快速學(xué)校計算機視覺。 下載3:OpenCV實戰(zhàn)項目20講 在「小白學(xué)視覺」公眾號后臺回復(fù):OpenCV實戰(zhàn)項目20講,即可下載含有20個基于OpenCV實現(xiàn)20個實戰(zhàn)項目,實現(xiàn)OpenCV學(xué)習(xí)進階。 交流群
歡迎加入公眾號讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器、自動駕駛、計算攝影、檢測、分割、識別、醫(yī)學(xué)影像、GAN、算法競賽等微信群(以后會逐漸細分),請掃描下面微信號加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺SLAM“。請按照格式備注,否則不予通過。添加成功后會根據(jù)研究方向邀請進入相關(guān)微信群。請勿在群內(nèi)發(fā)送廣告,否則會請出群,謝謝理解~
