1. 為什么要用shortcut連接
在實踐中,單純的前饋深層網(wǎng)絡(luò)特別容易出現(xiàn)梯度消失的問題,并且因為網(wǎng)絡(luò)太深,參數(shù)太多,訓(xùn)練和測試耗時極大,這個時候就出現(xiàn)了殘差網(wǎng)絡(luò)ResNet和Highway Network,以ResNet為例,其在上下兩層之間構(gòu)建了一個shortcut,使得下一層可以共享前一層的信息,其基本結(jié)構(gòu)如下圖所示:
假設(shè)一個組合函數(shù)(里面可以包括BN,激活函數(shù),卷積操作等),并且假設(shè)第l ll層的輸出為
,那么傳統(tǒng)的卷積網(wǎng)絡(luò)可以表示為(1-1):
而在resnet中,因為引入了殘差操作,因此公式化為(1-2):
然而作者認(rèn)為,殘差網(wǎng)絡(luò)resnet在合并不同層的信息的時候,用的是向量加法操作(summation),這有可能會阻礙(impede)網(wǎng)絡(luò)中的信息流動。在《Deep networks with stochastic depth》的工作中,提出了一個觀點,就是resnet中多達(dá)上百層的卷積層對整體的效果貢獻(xiàn)是很少的,有許多層其實是冗余的,可以在訓(xùn)練的過程中隨機(jī)丟掉(drop),實際上整個網(wǎng)絡(luò)并不需要那么多參數(shù)就可以表現(xiàn)得很好。(類似于dropout的做法,只不過是dropout是對一個層里面的神經(jīng)元輸出進(jìn)行丟棄,而stochastic depth是對整個層進(jìn)行隨機(jī)丟棄,都可以看成是一種正則手段。)
作者基于這種觀察,將淺層的輸出合并起來,作為后面若干層的輸入,使得每一層都可以利用到前幾個層提取出來的特征,在傳統(tǒng)的CNN中,因為是串行的連接,L層網(wǎng)絡(luò)就有L個連接,而在DenseNet中,因為采用了密集連結(jié),所以L層網(wǎng)絡(luò)有個連接,整個Dense連接見下圖所示:
其中的這幾層是經(jīng)過了密集連結(jié)的一個單元,作者稱之為“Dense Block”,我們可以發(fā)現(xiàn),其實這個block結(jié)構(gòu)還是很簡單的,就是在block中位于l ll層的輸入同時合并(concatenate)了上層的所有輸出,因此其輸入feature map有
個,其中
是原始圖片的通道數(shù),k稱為增長率,我們待會再談。同樣的,用公式去表達(dá)這個過程,就如式(1-3)所示:
2. 關(guān)于DenseNet的更多細(xì)節(jié)
2.1 composite function和transition layer
在本文中,一個組合單元包括有BN層,ReLU函數(shù)和卷積層,依次疊加。其中這里卷積層采用了3 × 3大小的卷積核。在CNN中,為了減少計算量,是需要進(jìn)行下采樣的,經(jīng)常由池化操作進(jìn)行。但是為了在dense block中可以合并輸入,其輸出輸入的張量尺寸不能改變,因此在dense block中不能使用池化操作,而是應(yīng)該在連接兩個dense block之間應(yīng)用池化,這個中間層,作者稱之為transition layer。在transition layer中,依次疊加了一個BN層,1$\times1 大 小 的 卷 積 和 一 個 2 1大小的卷積和一個21大小的卷積和一個2\times2 的 均 值 池 化 。 其 中 的 1 2的均值池化。其中的12的均值池化。其中的1\times$1卷積是為了進(jìn)一步壓縮張量的,這個我們后面談。
2.2 growth rate
如果每一個函數(shù)都輸出k個特征圖,那么在第l 層就會有
個輸入特征圖,其中的
是初始圖片的通道數(shù)。對比DenseNet和其他傳統(tǒng)網(wǎng)絡(luò),前者特點就是可以做的很狹窄,因此這里的k不必取得很大(比如傳統(tǒng)的基本上都是64,32,48等等),在這里
,這樣就大大減少了參數(shù)量。這個超參數(shù)k kk就稱之為growth rate。
為什么不需要那么多的特征圖呢?作者認(rèn)為特征圖可以看成是網(wǎng)絡(luò)的一種全局的狀態(tài),而在densenet中,因為某一層和前面幾層都有連接,因此一旦全局狀態(tài)一旦寫入,就可以在后面的層里面直接挪用了,而不用像傳統(tǒng)的網(wǎng)絡(luò)一樣在層與層之間復(fù)制這些狀態(tài)。
2.3 bottleneck layer
這個層的引入原因很直接就是為了減少輸入量。盡管每一個層只是產(chǎn)生了k個輸出,但是經(jīng)過若干層的疊加后,也會擴(kuò)充的很大,會導(dǎo)致后面層的計算復(fù)雜度和參數(shù)問題,因此可以用的卷積操作進(jìn)行降維,在本文中,具體操作是:在原先的3 × 3的卷積層前面加入一個1 × 1的卷積。具體的設(shè)置如:BN-ReLU-Conv(1×1)-BN-ReLU-Conv(3×3) 。作者統(tǒng)一讓每個1x1卷積輸出4 k個特征圖。(也就是說不管輸入的特征圖有多少通道,都會縮為4k個。)將具有bottleneck layer的densenet稱之為DenseNet-B。
(為什么1x1能夠降維?這個降維主要是體現(xiàn)在通道方向上的降維,可以將原先N個通道降到M個通道。)
2.4 compression
為了進(jìn)一步增加模型的緊致性,作者在transition layer也進(jìn)行了縮減特征圖的操作,同樣地,也是通過卷積實現(xiàn),如果一個block輸出了m個特征圖,那么在接下來的transition layer中就縮減為⌊ θ m ⌋ 個特征圖,其中的
為縮減系數(shù),有
。將同時具有bottleneck layer和transition layer(
)的densenet稱為DenseNet-BC。
3. 實驗部分
在ImageNet上,整個網(wǎng)絡(luò)的配置如:
實驗結(jié)果如:
其中藍(lán)色的表示最好結(jié)果,可以看到DenseNet的確是比其他網(wǎng)絡(luò)有著更為優(yōu)良的表現(xiàn),同時其參數(shù)數(shù)量更少。
在同樣的參數(shù)數(shù)量和運算量下的驗證準(zhǔn)確率對比:
可以發(fā)現(xiàn),在同樣的參數(shù)數(shù)量和運算量情況下,DenseNet無論是那種網(wǎng)絡(luò)配置,都比ResNet的結(jié)果要好。
然后在測試集上進(jìn)行對比,可以發(fā)現(xiàn)結(jié)論和驗證集上的一致。
總而言之,實驗結(jié)果證明了DenseNet的有效性,并且在同樣參數(shù)數(shù)量和運算量的程度下,DenseNet可以表現(xiàn)得比同類型的ResNet更好,并且作者在實驗部分強(qiáng)調(diào)了他的DenseNet在ImageNet上的結(jié)果是沒有經(jīng)過調(diào)優(yōu)的,因此還可以繼續(xù)提高結(jié)果。
4. 分析與討論
為什么這個模型能夠work呢?作者給了幾個解釋:
采用了緊致性的模型,有著更少的參數(shù),減少了過擬合。隱式深度監(jiān)督(implict Deep supervision),因為存在有shortcut的存在,作為優(yōu)化目標(biāo)的loss function不必要從最后一層提取信息,可以直接取得前面層次的信息進(jìn)行學(xué)習(xí),類似與將分類器網(wǎng)絡(luò)連接在所有隱藏層里面進(jìn)行學(xué)習(xí)的deeply-supervised nets(DSN)網(wǎng)絡(luò)。類似于ResNet,DenseNet這種shortcut的方式有利于梯度的傳導(dǎo),減少了梯度消失。其實,這種密集連結(jié)的策略也類似于隨機(jī)丟棄層的Stochastic depth網(wǎng)絡(luò),因為在Stochastic depth網(wǎng)絡(luò)中,池化層是不會被丟棄的,而中間的卷積層可能會被丟棄,也就是在訓(xùn)練的時候,其實可以看出某兩個非直接連接的層有可能因為丟棄的原因而連接起來,因此兩者有點類似的地方。
按照設(shè)計,DenseNet應(yīng)該可以通過前面層的信息中直接獲取特征圖從而得到提升小姑的,雖說實驗效果的確是比resnet的要好,但是前面層的信息的短接(shortcut)真的對分類結(jié)果有貢獻(xiàn)嗎?作者又補(bǔ)充了一個實驗:
作者訓(xùn)練了一個網(wǎng)絡(luò),然后將一個block中的源層(source layer)和目標(biāo)層(target layer)之間的權(quán)值取均值,然后繪制成了一個熱值圖,如上。其中每一個圖都是一個block的層層連接權(quán)值均值圖。顏色越深表示目標(biāo)層從源層里面得到了更多的貢獻(xiàn),我們可以發(fā)現(xiàn),目標(biāo)層除了最近鄰的層之外,的確是在前面的若干層中獲得了不少信息的。
5. 其他
因為現(xiàn)在的框架對于densenet沒有直接的支持,因此實現(xiàn)的時候往往是采用concatenate實現(xiàn)的,將之前層的輸出與當(dāng)前層的輸出拼接在一起,然后傳給下一層。對于大多數(shù)框架(如 Torch 和 TensorFlow),每次拼接操作都會開辟新的內(nèi)存來保存拼接后的特征。這樣就導(dǎo)致一個 L 層的網(wǎng)絡(luò),要消耗相當(dāng)于 L(L+1)/2 層網(wǎng)絡(luò)的內(nèi)存(第 l 層的輸出在內(nèi)存里被存了 (L-l+1) 份)。
這里有一些框架的解決方案,可供參考,具體內(nèi)容見引用1:
- Torch implementation: https://github.com/liuzhuang13/DenseNet/tree/master/models
- PyTorch implementation: https://github.com/gpleiss/efficient_densenet_pytorch
- MxNet implementation: https://github.com/taineleau/efficient_densenet_mxnet
- Caffe implementation:https://github.com/Tongcheng/DN_CaffeScript
Reference
- 《CVPR 2017最佳論文作者解讀:DenseNet 的“what”、“why”和“how”|CVPR 2017》
- 《DenseNet算法詳解》