Efficient CNN 介紹(一):MobilenetV1
前言
在這篇文章,主要會大略的介紹 MobilenetV1 透過了什麼樣的方式達到 Efficient CNN 的目的,因此假如想知道更加詳細的話,建議可以閱讀Mobilenet系列的論文,相信可以收穫多多的。
Introduction
在早期的深度學習中,大家普遍都遇到了一個問題:
越深的網路,錯誤率越高
而造成這個問題主要原因是梯度消失以及梯度爆炸(gradient vanishing / gradient exploding),也就是在透過 back propagation 計算梯度的時候,隨著一層一層 chain rule 的計算,梯度也因為不斷的相乘而越來越大或是越來越小,使得到了網路的前幾層時,已經沒有辦法好好更新網路中的 weight 了。
在 2015 年,ResNet 的透過了 residual learning 的方式解決了過去大家普遍遇到的問題,並使得 CNN(Convolution Neural Network) 為了使得準確度提昇到更高的情況下,更深更複雜。
但是隨著 CNN 為了更好的準確度而更深更複雜的情況下,大家開始遇到了一個問題:
太耗資源了!
在實際應用面上,不論是手機攝影機或是其他的嵌入式系統,我們並沒有辦法像一般電腦一樣,隨便都插上幾張 2080Ti 的GPU,因此在這樣的情況下,我們所需要的是:
更小更快同時可以兼顧準確度的 CNN
也就是今天的主題,Efficient CNN。
MobilenetV1
在2017年 Google 所提出的 MobilenetV1 中,透過 Depthwise Separable Convolution 將傳統的 Convolution 拆解成了兩個步驟,以大幅的減少傳統 Convolution 的運算量:
- Depthwise convolution
- 1×1 convolution (pointwise convolution)
而單純看上面的名詞可能完全沒有辦法了解他們的是如何減少網路的運算量的,因此接下來我們會來做更加詳細的介紹,而在正式開始講解 Depthwise Separable Convolution 之前,先來複習一下傳統 Convolution 是如何運作的。
傳統 Convlution
首先我們先舉個例子,假如我們有一個 input 的尺寸為 32 * 32 * 2 的照片,我們希望透過傳統的 Convolution 使得其輸出為一個尺寸為 32 * 32 * 3 的 feature map,如下圖:
而為了達到這個目的我們需要一個 size 為 K * K * 2 * 3 的 kernel,但其實我們也可以將這個 kernel 想像成為 3 個 K * K * 2 的 kernels,而每個 kernel 對input 做完 convolution 之後會得到 output 的一層 channel feature map,如下圖:
因此在這樣的情況下,我們可以計算出傳統 Convolution 做完一次 convolution 的運算量:
K * K * M * N * F * F
K : kernel 的大小
M : Input 的 channel 數量 (e.g., 2)
N : Output 的 channel 數量 (e.g., 3)
F : Output feature 的大小 (e.g., 32)
Depthwise Separable Convolution
在複習完傳統的 Convolution 之後,終於要正式介紹 MobilenetV1 中的主角,也就是 Depthwise Separable Convolution 了,而 Depthwise Separable Convolution 分成了兩個部份,也就是 Depthwise convolution 以及 1×1 convolution (pointwise convolution),首先我們先講解 Depthwise convolution
Depthwise convolution
顧名思義, Depthwise convolution 與傳統 Convolution 的差別就是 “Depthwise” ,也就是說:
對於 Input 中的每層 channel(depth) 各自單獨做 Convolution
聽起來有點抽象,因此我們看一下下面的示意圖:
在上圖當中,跟傳統的 Convolution 不一樣的地方在於,我們將一個 32 * 32 * 2 的 Input,切割成了兩個 32 * 32 * 1的 Input,而這樣切割之後原先我們尺寸為 K * K * 2 的 Kernel 就可以簡化為 K * K * 1的尺寸,跟上面傳統的 convolution 相比,應該看得出來 Depthwise 的方式減少了一定的運算量!
但是,不知道大家有沒有發現,經過了 Depthwise convolution 之後,我們的 Output feature map 的尺寸依然是 32 * 32 * 2,而並非我們所想要的尺寸 32 * 32 * 3,因此接下來的工作就是交由 1×1 convolution (pointwise convolution) 了!
1×1 convolution (pointwise convolution)
1×1 convolution (pointwise convolution) 最主要的目的就是將經過 Depthwise convolution 後的 output feature map 轉換成為我們所希望的 output 尺寸(在這個例子中為 32 * 32 * 3),因此我們先來看一下以下的示意圖:
K * K * M * F * F + M * N * F * F如上述一樣,原先經過 Depthwise convoltion 的 output feature map 32 * 32 * 2,我們利用了 3 個 1 * 1 * 2 的 kernel 進行 convolution 之後,便可以得到一個尺寸為 32 * 32 * 3 的 output feature map,但是到這邊可能大家會發現,這樣做不就跟傳統的 convolution 一樣嗎?
沒錯, 1×1 convolution (pointwise convolution) 所作的事情基本上跟傳統 Convolution 一模一樣,最主要的差別在於 1×1 這個部份:
1×1 相較於傳統 K×K 的 convolution 可以減少 K² 的運算量
因此在經過了 Depthwise convolution 以及 1×1 convolution (pointwise convolution) 之後,我們依然可以得到我們原先所想要的 32 * 32 * 3 的 output feature map
而所需要的運算量:
K * K * M * F * F + M * N * F * F
K : kernel 的大小
M : Input 的 channel 數量 (e.g., 2)
N : Output 的 channel 數量 (e.g., 3)
F : Output feature 的大小 (e.g., 32)
大家有沒有發現,在傳統 Convolution 當中相乘的運算量,在經過了Depthwise Separable Convolution 之後,中間有一項變成了加法,這樣看起來可能看不出來他們的差別,所以我們用剛剛的例子實際算一次
傳統 Convolution 運算量:
K * K * 2 * 3 * 32 * 32 = 6144 * K²
Depthwise Separable Convolution 運算量:
K * K * 2 * 32 * 32 + 3 * 2 * 32 * 32 = 2048 * K² + 6144
這樣看下來,就可以發現 Depthwise Separable Convolution 相比於傳統 Convolution,減少了大量的運算量,卻可以達到一樣的目的了!
Experiments
講了那麼多,可能大家難免會覺得有點空虛,因此我們直接看一下 MobilnetV1的實驗結果:
在 ImageNet 的表現上,相比於 GoogleNet 以及 VGG16 , MobilenetV1 可以在差不多的準確度的情況下,大幅的降低運算量以及參數量!
總結
MobilenetV1 透過利用 Depthwise Separable Convolution 大幅的減少了過去使用傳統 Convolution 的運算量,並且可以達到相同的準確度。
Reference
- MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications. Andrew G. Howard, Menglong Zhu, Bo Chen, Dmitry Kalenichenko, Weijun Wang, Tobias Weyand, Marco Andreetto, Hartwig Adam