富強、民主、文明、和諧、自由、平等、公正、法治、愛國、敬業、誠信、友善!

我在Gammalab的時候工程組有個特別聰明的同事叫

金戈

,人特別聰明,時常會問我一些我答不上來的問題。比如今天他就問我:“旭峰,人臉檢測為什麼要用三通道來做呢?灰度圖應該夠用了吧?”

我想了半天不知道怎麼回答他,於是說:“產品經理問的?”

金哥回覆我說:“我就是在思考如果產品經理問我我該怎麼回答。”

我…

anyway,本篇新坑就以金哥為榜樣來學習一下模型壓縮。模型壓縮是工程化必不可少的技術,可以說AI越落地,人們就越會關心模型壓縮。模型壓縮有多種技術,包括結構,量化,剪枝,蒸餾及各種其他方法。本文先從最基本的模型結構開始。

經典的

模型結構

論文包括:

MobileNet_v1: MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications

MobileNet_v2: MobileNetV2: Inverted Residuals and Linear Bottlenecks

MobileNet_v3: Searching for MobileNetV3

ShuffleNet_v1: ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices

ShuffleNet_v2: ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design

IGC_v1: Interleaved Group Convolutions for Deep Neural Networks

IGC_v2: IGCV2: Interleaved Structured Sparse Convolutional Neural Networks

IGC_v3: IGCV3: Interleaved Low-Rank Group Convolutions for Efficient Deep Neural Networks

SqueezeNet_v1: SQUEEZENET: ALEXNET-LEVEL ACCURACY WITH 50X FEWER PARAMETERS AND <0。5MB MODEL SIZE

SqueezeNext: SqueezeNext: Hardware-Aware Neural Network Design

本文我們會從基礎講起,從頭學習,作為一個入門的小綜述吧。

模型壓縮主要考察兩個指標,Params 和Flops,其中Params指的是模型的引數,因為我們訓練模型一般都是以float32為精度的,所以模型大小一般是引數的4倍。Flops全稱為floating point operations per second。意指每秒浮點運算速度。所以Params和Flops分別度量了模型的大小以及其算數。我們做模型壓縮,目標就是使Params和Flops儘可能地小。

金哥:但是我們工程當中…

我:你不要問這種我答不上來的問題,工程的環境是複雜的,我們這邊就單單討論Params和Flops。

好,我們繼續,既然制定了metrics,讓我們回憶一下標註卷積的Params和Flops吧。

金哥和你一起學模型壓縮——結構篇(1)

如圖所示,在一個標準的conv當中,我們的Params和Flops應該為:

Params:(K_h * K_w * C_in)* C_out

其中K_h和K_w 代表了kernel的input_size, C_in是input feature map的channel數,C_out是output feature map的channel數

Flops: (K_h * K_w * C_in * C_out) * (H_out * W_out)

其中H_out和W_out 分別代表了output feature map的size

在全連線層中,對應的Params和Flops分別為:

Params:C_in * C_out

Flops:C_in * C_out

金哥:你是不是忘記把bias給加上了?

我:由於bias網上貌似本身也有些分歧,這裡暫時就先忽略不計。

金哥:為什麼卷積的params和Flops和全連線計算不同?

我:好問題,要不你思考一下?為什麼卷積的計算方式和全連線不同,以及這種不同會造成什麼樣的影響和最佳化方式呢?

介紹完

標準卷積

的計算方式,我們接下來介紹一下模型壓縮中的經典方法Group Conv, 可以說,理解了Group Conv,也就算對模型壓縮入門了。

金哥和你一起學模型壓縮——結構篇(1)

所謂Group Conv,其實就是把input feature進行分組計算,為什麼要進行這步操作呢?因為早期大家都比較不富裕,

視訊記憶體

不夠,只好想歪招進行分組訓練。我們來看一下Group Conv的

Params和Flops

Param:G * (K_h * K_w * C_in / G) * (C_out / G) = K_h * K_w * C_in * C_out / G

Flops:K_h * K_w * C_in * C_out * H_out * H_in / G

其中G為group數。

透過公式我們可以發現,只要G足夠大,Params和Flops就足夠小。經典的MobileNet就是把Group Conv的一個特例,在MobileNet,G設定為了input feature map的channel數。

具體

group conv

怎麼用呢,也很簡單,我們看一下pytorch的引數:

金哥和你一起學模型壓縮——結構篇(1)

其中groups就代表了分組數,解釋得很清楚了吧,金哥你有什麼問題嗎?

金哥:Group Conv有什麼理論依據嗎?還是僅僅為了減少引數而被應用的?

我:好問題,我們稍後就分析。還有嗎?

金哥:有,你有沒有覺得你的字實在是太難看了?

我:有。

金哥:那你還寫?

我:我只是以前看別人寫覺得很好看…。

好了我們不糾結字的問題了,迴歸主題,group conv究竟有沒有道理呢?我們透過一個經典的backbone來重新認識一下group conv——resnext。在resnext中,作者總結了一種神經網路的正規化:split-transform-merge,以經典的Inception為例,我們看一下Inception的經典結構:

金哥和你一起學模型壓縮——結構篇(1)

可以看到,Inception在一個input上有不同的分支(也就是不同的group)來進行不同的非線性變換來獲取不同的feature特徵進行融合以獲取更好的特徵。他的操作方式為:1)split:先把feature轉換為一個low-dimension的

特徵向量

,在Incetion中,就是conv1*1;2)transform:運用不同的transform方式進行非線性變換;3)merge:融合特徵,獲得了更好的特徵表示。

Split-transform-merge

的核心在於(其實也就是Inception的核心),在計算複雜度比較低的情況下,獲取和那些密集複雜網路相同的特徵表示能力。但是Inception的問題在於,他人工設計的痕跡太重,因此在resnext中,作者把網路的blocks改成了:

金哥和你一起學模型壓縮——結構篇(1)

其中a是resnext的網路結構,b是Xception的網路結構,c是原始的resnet。我們可以發現,其實Xception和Resnext非常像,兩者的思想也非常接近。

金哥:那你為什麼要用resnext舉例子?

我:你看看resnext最後一個作者,是我校友好嗎?

金哥:那你就是瞧不上google咯?

我:google爸爸要我我肯定真香啊。

金哥:你這人怎麼這麼沒用原則…

好吧,讓我們看一下resnext的效果:

金哥和你一起學模型壓縮——結構篇(1)

金哥和你一起學模型壓縮——結構篇(1)

在相似的引數條件下,resnext的效果要顯著好於resnet。

進一步思考,為什麼resnext這種結構更近優秀呢?首先,group conv最讓人覺得困惑的地方其實在於,在一般的CNN中,卷積的channel其實是相互作用的。Group conv,極端一點,把G設定為input feature的通道數的話,其實就完全孤立了各個通道,這顯然也是不符合常識的。那為什麼依舊使用group conv這種操作呢?一種可能性是,就如同卷積大獲成功的原因之一是,我們運用卷積能有效地抽取區域性特徵(因此代替了全連線)。那通道是否也有這樣的性質呢?顯然,不是每個通道都應該獲得同樣的關注。

2017ImageNet的冠軍SENet就提供了一種

通道關係

的解決方案:

金哥和你一起學模型壓縮——結構篇(1)

SENet的block其實也非常簡單,對於一個input feature map 分成兩支,一支是先經過Global Average Pooling加一個全連線層過sigmoid轉換為一個

關係向量

,另一支直接過網路,然後把過sigmoid後的權值乘到channel上以控制channel的重要性。今天看來這個結構相當簡單,類似attention的思想也比較符合常識。當然,我們也需要意識到的是,這個結構的成功是基於ImageNet這樣大規模的資料集上的,普通小資料集用這樣的attention,很可能調參不成反被慘虐。當然,這個模型結構的成功一定程度上說明了某些channel是冗餘的,因此group conv還有一種可能不錯的解釋就是,一定程度上防止了過擬合?

金哥:你這麼說你心裡不慌呢?

我:不慌…

Ok,鋪墊了這麼多,讓我們來進入正題,看一看經典的小模型框架。我們從MobileNet_v1開始入手。

MobileNet_v1的核心在於兩點,Depthwise + Pointwise

金哥和你一起學模型壓縮——結構篇(1)

如圖所示,左邊是經典的一個小block,右邊是MobileNet_v1的block。其中DepthWise其實就是Group Conv的一個特殊版。

金哥和你一起學模型壓縮——結構篇(1)

Params:K_h * K_w * C_in

Flops: (K_h * K_w * C_in) * (H_out * W_out)

和標準的卷積相比,引數量少了C_out倍。正如前文所說,獨立的通道也是有問題的。因此MobileNet_v1巧妙地加上PointWise,也就是1*1的conv層,既可以把維度調整到和其他的backbone一致作為對比,也可以使打通通道,好操作。透過Depthwise及Pointwise的操作,引數量下降了:

金哥和你一起學模型壓縮——結構篇(1)

嗯,這個倍數…。。其中D_k代表了kernel_size, D_F代表了output的feature size。

當然,良心的google還為廣大工程師提供了兩個超參,分別是Width Multiplier

寬度乘子

及Resolution Multiplier解析度乘子。其中寬度乘子主要控制input_channels和out_channels的數目,一般來說設定為[1, 0。75, 0。5],於是整體的引數就可以計算為:

金哥和你一起學模型壓縮——結構篇(1)

來看一下效果:

金哥和你一起學模型壓縮——結構篇(1)

可以看到,當

乘子

為0。75和0。5時,網路依舊能保持不錯的效能,amazing!

解析度因子

也很好理解,透過縮小圖片解析度減少計算量,我們也來看一下實驗效果:

金哥和你一起學模型壓縮——結構篇(1)

由實驗可以看出,圖片解析度的降低並沒有顯著降低精度。這個現象符合常識嗎,特別是ImageNet這種有1000類標籤的大資料集?暫時沒有想到合理的解釋,也希望有大神推薦一下相關的paper。後面實驗各種nb的結果就不放了,大家有興趣可以自己去看一下。

OK,我們來總結一下MobileNet_v1, 巧妙地利用了Depthwise + Pointwise的結構,既降低了引數量,又使模型保持了不錯的資訊特徵。各種實驗也印證了這的確是個好backbone。現在問題來了,baseline這麼優秀,後面人怎麼吃飯呢?

事實證明總有大神能夠突破創新的,讓我們一起來看一下ShuffleNet_v1吧。講ShuffleNet前,我們不妨想想MobileNet還有什麼問題,我們從Params的角度來分析一下:

金哥和你一起學模型壓縮——結構篇(1)

這是MobilNet_v1的引數分佈,可以看到,Conv1*1佔據了大量的引數量。因此,如何有效地降低Conv1*1是最大的問題。

根據我們前面的分析,當想減少引數的時候,Group Conv是種非常有效的手段,但是Group Conv的缺點在於,無法有效地使通道之間交換資訊,在MobileNet中是用Conv1*1解決了這個問題,但是我們本身的目的就是為了解決Conv1*1引數量過高的問題,怎麼辦呢?ShuffleNet提出了Channel Shuffle for Group Conv來解決這個問題。

首先,讓我們來看一下總體bottleneck上的改進:

金哥和你一起學模型壓縮——結構篇(1)

可以看到,最左邊其實是MobileNet_v1的結構。ShuffleNet首先把conv1*1換成了Group Conv1*1,大大縮減了計算量,其次用Channel Shuffle的方法打破了Group之間的孤立狀態,之後再連上DWConv3*3以及GConv1*1,引數量我就不寫了,眼見的在各個環節上都有降低,並且實實在在解決了Conv1*1的問題。

接下來我們來分析一下神秘的

Channel Shuffl

e到底是怎麼一回事:

金哥和你一起學模型壓縮——結構篇(1)

非常簡單的思路,從Group Conv得到feature進行concat後把所有feature map打亂shuffle傳遞到下一層以進行不同group的資訊溝通,好想法,非常簡單有效。但是這個shuffle規則是人工設定的,感覺還是並不合理。並且實現相當困難,在此給曠世的團隊鼓鼓掌,的確厲害。

感受一下這個恐怖的Flops:

金哥和你一起學模型壓縮——結構篇(1)

看一下模型效果:

金哥和你一起學模型壓縮——結構篇(1)

其中1,0。5代表了channels數scale的設定,越小則channel相應越小。可見在較小的模型中,channels越少則效果越差(顯然),group數越多反而效果越好。看一下論文的解釋:

金哥和你一起學模型壓縮——結構篇(1)

ShuffleNet後文還有大量翔實的實驗,比較有興趣,這裡不多介紹了,大家可以去當個彩蛋看一下,非常有意思。

大概花了差不多一天不到的時間學習並整理模型壓縮的結構篇,說實話還是蠻累的。最缺的是實驗驗證以及程式碼的理解,這些以後有機會慢慢補上。噢,差點忘了金哥了,金哥你有啥要補充的。

金哥:那之前立下的MobileNet_v2, ShuffleNet_v2, Mobile Net_v3 這些flag呢?

我:em…。讓我們下次再好好討論吧!