1. 程式人生 > >mobileNet-一個典型的網路加速的例子

mobileNet-一個典型的網路加速的例子

論文連結:https://arxiv.org/abs/1704.04861

MXNet框架程式碼:https://github.com/miraclewkf/mobilenet-MXNet

moblileNet,顧名思義,可以在移動端使用的網路,那必然要求網路的計算量要小一些,不然移動端可扛不住啊,那MobileNet如何做到這個的呢??總結來看,其主要創新點就在於論文中反覆強調的depth-wise separable convolutions,作者將其操作分成兩步,一部分是depthwise convolution,另一部分是pointwise convolution,通過這種方法,將網路的複雜度降下來,使得可以在移動端、嵌入式

平臺中使用。

那麼MobileNet是怎麼做的呢?
首先介紹一下論文的核心:Depthwise Separable Convolution

Depthwise Separable Convoltion類似於一種因式分解,比如你求面積,可以使用長乘以寬一樣,moblieNet就是將一個完整的卷積分為兩部分:depthwise convolution以及1*1的pointwise convolution, depthwise對特徵圖的每個channel進行卷積,pointwise convolution將depthwise的結果進行合併。如下圖所示,圖(a)為一個標準的卷積計算,標準的卷積需要將filter與各個channel相乘,然後求和得到一個卷積結果。圖(b)代表著depthwise convolution,其將每個filter與每個channel單獨相乘,並不求和,得到與原來channel相同的特徵圖,然後利用圖(c)代表pointwise convolution,將各個特徵圖求和。

下面詳細說明一下:

在標準的卷積中假設輸入為 D F D F M

的特徵圖F,其中M為通道數(channel),輸出為 D G D G N 的特徵圖G,利用卷積核大小為 M D K D K N ,標準的卷積計算的計算量為: D K D K M N D F D F 這裡當然是估算,stride=1時,大概是這個數,至於moblileNet呢?首先使用M個 D K D K 的卷積核,如圖(b)所示,對每個channel進行處理(這裡其實就是一個group 卷積處理,group數量等於channel數量,就實現了每個卷積核對每個channel處理),這裡的計算量是 D K D K M D F D F ,但是這並沒有將channel資訊進行融合,所以作者接著使用N個1*1的卷積進行channel的合併,這裡的計算量是 M N D F D F ,所以總的計算量是原來的(這裡直接上圖,打公式有點費勁):

這裡作者介紹到,如果使用3*3的depthwise separable convolutions可以減少8-9倍的計算量,後面的表格也證明了這一點

網路block的實現如下所示,不管你用不用MXNET,程式碼應該可以看懂

def Conv(data, num_filter=1, kernel=(1, 1), stride=(1, 1), pad=(0, 0), num_group=1, name='', suffix=''):
    conv = mx.sym.Convolution(data=data, num_filter=num_filter, kernel=kernel, num_group=num_group, stride=stride, pad=pad, no_bias=True, name='%s%s_conv2d' % (name, suffix))
    bn = mx.sym.BatchNorm(data=conv, name='%s%s_batchnorm' % (name, suffix), fix_gamma=True)
    act = mx.sym.Activation(data=bn, act_type='relu', name='%s%s_relu' % (name, suffix))
    return act


def Conv_DPW(data, depth=1, stride=(1, 1), name='', idx=0, suffix=''):
    conv_dw = Conv(data, num_group=depth, num_filter=depth, kernel=(3, 3), pad=(1, 1), stride=stride, name="conv_%d_dw" % (idx), suffix=suffix)
    conv = Conv(conv_dw, num_filter=depth * stride[0], kernel=(1, 1), pad=(0, 0), stride=(1, 1), name="conv_%d" % (idx), suffix=suffix)
    return conv
Network Structure

MobileNet的結構如下圖(共28層):

MobileNet中的所有層都接了BN層以及ReLU層,下采樣層,採用depthwise convolution中的stride進行,最後接了全域性平均池化層

作者還提到了一點是:非結構化的稀疏矩陣,並不一定比密集矩陣快,除非是高程度的稀疏

下圖中介紹了採用這個方法各個層的引數佔比:

進一步減少引數的手段(一):Width Multiplier: Thinner Models

雖然到這裡,引數已經降低很多了,但是作者並不滿足,而是想辦法繼續降低引數量,怎麼做的呢?作者引入了一個引數 α ,被稱作寬度乘子,作用呢就是降低網路的寬度,同時將網路每層的輸入與輸出降低到原來的 α 倍, α 一般被設定為1,0.75,0.5,0.25

D K D K α M D F D F + α M N D F D F

進一步減少引數的手段(二):Resolution Multiplier: Reduced Representation

該方法通過按照比例降低輸入影象以及每層的特徵圖大小來減少引數量,同樣比較直觀,公式如下:

D K D K α M ρ D F ρ D F + α M N ρ D F ρ D F

論文主打就是降低網路的計算量,那麼該方法究竟可以降低多少計算量呢?看下面一張表格,這是一個layer的例子,只是用depthwise separable conv降低了約9倍的計算量,如果再加上上面的那兩個手段,降低了30來倍呀

做了這麼多,引數降低了這麼多,那麼網路的精度會怎麼樣呢?這裡是作者的實驗結果:

首先在imagenet上面的實驗,上面是全卷積網路,下面是mobileNet網路,可見,引數量降低了大約9倍,但是精度只是減少了約1%。:

接下來就是作者的一頓窮舉,各個引數,各個資料集,對比網路的引數量以及精度,這裡就不詳細介紹了,想了解的讀者可以檢視論文。

這裡介紹一個在COCO資料集上進行檢測的實驗結果: