輕量級深度學習網絡——ESPNet v2

ESPNet v2

在之前我們分享過兩種輕量級網絡MobileNet & MobileNet v2ShuffleNet & ShuffleNet v2 而這次分享的ESPNetv2在輕量化上更上一層樓,這篇文章來自CVPR2019,按照作者的說法輕輕鬆鬆吊打上面的兩種輕量級網絡,那麼就讓我們來看一下吧!
論文地址:https://arxiv.org/pdf/1811.11431.pdf
開源代碼:https://github.com/sacmehta/espnetv2

作者 Sachin Mehta, Mohammad Rastegari, Linda Shapiro, and Hannaneh Hajishirzi
這四個人分別來自華盛頓大學,微軟的艾倫人工智能研究所和XNOR.ai公司;
2018年,這四個人和Anat Caspi一同發佈了ESPNet,發表在了ECCV2018上。該網絡的主要優點是在參數比目前的主流輕量級網絡(如MobileNet, ShuffleNet)小很多的情況下,性能上卻相差無幾。
2019年,他們又發佈了ESPNet的升級版——ESPNetv2,並在CVPR上發表。該網絡在ESPNet的基礎上繼續被優化,參數相比前一代少了約四倍,而性能卻只下降了很少的一點,這使得 ESPNetv2在輕型網絡中佔有一席之地。

1 背景介紹

作者介紹了一種輕量、效率高、通用的卷積神經網絡ESPNetv2,用於對可視化數據和順序數據進行建模。相比前一代網絡,v2使用逐點羣卷積深度空洞可分離卷積。作者在四個不同的任務上使用該網絡進行測試包括對象分類,語義分割,對象檢測和語言建模,以此來證明ESPNetv2優於最目前比較先進的輕量級神經網絡,而事實也恰好是這樣。從結果來看,該網絡在這四個任務上的表現均優於大部分輕量級神經網絡(包括YOLOv2和ESPNet等)。
隨着GPU的高速發展,卷積神經網絡被越來越多地應用在了具有圖像識別功能的工具中,如無人駕駛和機器人。然而,性能越好的神經網絡往往需要更多的資源,這對於商業用途非常不利,我們試圖尋找不需要那麼多資源,還能夠達到我們性能要求的神經網絡,輕量級神經網絡的研究因此發展。
目前主要有三種減少網絡參數的方法:基於網絡壓縮的方法,基於低位表示的方法,分解卷積操作。本文介紹的ESPNetv2正是使用第三種方法來減輕網絡的。

2 相關工作

2.1 輕量化的CNN架構

深度可分離卷積結構:
(1)deep-wise卷積:進行輕量級過濾,一個卷積核負責一個通道,一個通道只被一個卷積核卷積;
(2)point-wise卷積:通過學習輸入通道的線性組合來沿着通道擴展特徵圖,卷積核的尺寸爲 1×1×M,M爲上一層deep-wise卷積的通道數。所以這裏的卷積運算會將上一步deep-wise卷積的map在深度方向上進行加權組合,生成新的Feature map。有幾個卷積核就有幾個輸出Feature map。

分組卷積: 將輸入通道與卷積核分組,每組單獨卷積;
假設上一層的輸出feature map有N個,即通道數channel=N,也就是說上一層有N個卷積核。再假設羣卷積的羣數目M。那麼該羣卷積層的操作就是,先將channel分成M份。每一個group對應N/M個channel,與之獨立連接。然後各個group卷積完成後將輸出疊在一起(concatenate),作爲這一層的輸出channel。

空洞深度可分離卷積: 深度可分離卷積+空洞卷積;
空洞卷積:爲了能夠在不增加參數數量的情況下提高卷積核的感受野;在標準卷積核中的每個參數中間插入一定數量的0元素便得到了空洞膨脹的卷積核;膨脹率(即相鄰元素之間的插0個數)爲r。

channel shuffle: ShuffleNet的核心
channel split: 通道分割

神經網絡架構搜索: 預定義一個詞典,其中包含不同參數,例如:卷積層數、卷積單位、過濾器尺寸等;使用這個詞典在網絡空間中進行搜索;基於搜索的方法已經改進了MobileNetv2;
網絡壓縮: 主要通過散列、修剪、矢量量化和收縮等方法,以減小預訓練的模型大小;這裏主要強調的是對網絡結構和通道的剪枝:CNN有相當數量的冗餘權重,由於參數的稀疏性,在保證CNN精度的前提下,保留重要的權重(一般權重值越接近於0越不重要),去掉不重要的權重,簡化網絡模型複雜度。在CPU上無法實施,原因是數據查找和數據遷移操作佔用較多資源。
低位網絡: 通過量化模型的權重來減小模型大小和複雜度。使用較低位表示預訓練模型中的權重,來代替32位高精度的浮點數;在簡介中提及,減小後的模型通過使用邏輯門可以在CPU上實現快速計算。

3 ESPNetv2

在介紹ESPNET之前,我們需要對ESP所應用的各種卷積策略以及以及其參數量和感受野進行介紹和比對。

在這裏感謝一下這兩個博客的分享 (๑•̀ㅂ•́)و✧
https://blog.csdn.net/gwplovekimi/article/details/89890510
https://blog.csdn.net/sinat_37532065/article/details/85723068

3.1 傳統方法:2D卷積(看似3D實際是2D因爲只在平面移動)

在這裏插入圖片描述
參數數量 hwDinDouth*w*D_{in}*D_{out}

現在我們需要對傳統的網絡結構進行優化,保證不改變輸出大小的同時,降低網絡中的參數量,從而達到輕量化的目的。而ESPNET則是基於MoblieNet以及ShuffleNet提出的深度分離式卷積和分組卷積實現輕量化的目的的。

3.2 MoblieNet 深度可分離卷積(先將每個圖片的深度計算出來,最後再進行組合)

深度可分離卷積將整個過程劃分爲兩步,第一步分別對每一層通道應用一個單獨的卷積核,並將所有通道的輸出作爲第二步的輸入。第二步根據輸入圖像的深度應用所需數量的1*1*輸入圖像深度的卷積核進行卷積,從而得到目標輸出。

如圖所示,我們來舉一個具體的例子:

  • 第一步:在 2D 卷積中分別使用 3 個卷積核(每個過濾器的大小爲 3 x 3 x 1),而不使用大小爲 3 x 3 x 3 的單個過濾器。每個卷積核僅對輸入層的 1 個通道做卷積,這樣的卷積每次都得出大小爲 5 x 5 x 1 的映射,之後再將這些映射堆疊在一起創建一個 5 x 5 x 3 的圖像,最終得出一個大小爲 5 x 5 x 3 的輸出圖像。
  • 第二步是擴大深度,我們用大小爲 1x1x3 的卷積核做 1x1 卷積。每個 1x1x3 卷積覈對 5 x 5 x 3 輸入圖像做卷積後都得出一個大小爲 5 x 5 x1 的映射。這樣的話,做 128 次 1x1 卷積後,就可以得出一個大小爲 5 x 5 x 128 的層。深度可分離卷積完成這兩步後,同樣可以將一個 7 x 7 x 3 的輸入層轉換爲 5 x 5 x 128 的輸出層。

alt

參數數量 hw1Din+11DinDouth*w*1*D_{in}+1*1*D_{in}*D_{out}

3.3 ShuffleNet 分組卷積 (羣卷積)

alt

參數數量 hwDinkDoutkkh*w*\frac{D_{in}}{k}*\frac{D_{out}}{k}*k

上圖表示的是被拆分爲 2 個過濾器組的分組卷積。在每個過濾器組中,其深度僅爲名義上的 2D 卷積的一半(DinD_{in} / 2),而每個過濾器組都包含 DoutD_{out} /2 個過濾器。第一個過濾器組(紅色)對輸入層的前半部分做卷積([ : , : , 0 : DinD_{in}/2]),第二個過濾器組(藍色)對輸入層的後半部分做卷積([ : , : , DinD_{in}/2 : DinD_{in}])。最終,每個過濾器組都輸出了 DoutD_{out}/2 個通道。整體上,兩個組輸出的通道數爲 2 x DoutD_{out}/2 = DoutD_{out}。之後,我們再將這些通道堆疊到輸出層中,輸出層就有了 DoutD_{out} 個通道。

3.4 空洞卷積 Dilated Convolutions

爲了提高感受野,本網絡採用了空洞卷積。

在這裏插入圖片描述

爲了能夠在不增加參數數量的情況下提高卷積核的感受野,便誕生了空洞卷積;
簡單而言,在標準卷積核中的每個參數中間插入一定數量的0元素便得到了卷積膨脹的卷積核;
這樣一來,對於一個N*N的卷積核,設膨脹率(即相鄰元素之間的插0個數)爲R,則其感受野增大到 [(N1)R+1]2[(N-1)R+1]^2

3.5 ESPNetv1

下面我們來介紹一下ESPNetv1

alt

HFF (hierarchical feature fusion)
爲了解決由於引入空洞卷積帶來的網格效應(gridding artifact),將不同劃分的特徵圖求和,之後再進行組合的過程。
類比一個(whMNw*h*M*N)大小的標準卷積核,即設輸入的feature map大小爲128128M128*128*M,輸出的feature map大小爲128128N128*128*N。ESP模塊的具體計算如下:

① 經過Reduce以後,變爲128128d128*128*d
② 經過Split後,可以得到K張128128d128*128*d的feature map;
③ 經過HFF融合以後,變爲128128(dK)128*128*(d*K),注意d=N/K,所以輸出大小爲128128N128*128*N

如:當 n=3, N=M=128, K=4 時,一個標準卷積核參數量是一個ESP模塊參數量的3.6倍,但ESP模塊的感受野可以達到171717*17

這裏還沒有應用深度可分離卷積,而是隻採用了空洞卷積,到達了提高卷積核感受野的作用。同時本網絡也沒用應用分組卷積,所以相比較於MobileNet 和 ShuffleNet,ESPNet的優勢就在於其感受野,但參數量方面的確不佔優勢,這也就是ESPNETv2的主要研究方向。

3.6 EESP Unit

與ESP Unit相比,首先使用逐點羣卷積(group point-wise convolution)替代了逐點卷積(point-wise convolutions)。然後使用深度空洞可分離卷積(depth-wise dilated separable convolutions)替代了333*3空洞卷積(dilated convolutions)。這樣的改動減少了卷積核的參數量。ESP Unit與EESP Unit參數量之比爲:
Md+n2d2KMdg+(n2+d)dK\frac{Md+n^2d^2K}{\frac{Md}{g}+(n^2+d)dK}

M=240M=240g=K=4g=K=4d=MK=60d=\frac{M}{K}=60,EESP Unit的參數量是ESP Unit的1/7。
在深度空洞可分離卷積與逐點羣卷積中間添加HFF,去除了空洞卷積帶來的柵格重影。

alt

圖1c與圖1b中的主要區別就是,用k組的group point-wise convolution代替了單獨計算k點的point-wise convolutions。從複雜度方面來看,二者沒有差別(見公式1&2),但是,從實現方面來看,k點的point-wise conventions相當於用了k個卷積核而k組的group point-wise convention只用了一個卷積核,因此group point-wise convention更利於實現。經過如上的改動所獲得的Unit,稱之爲EESP Unit。
(1)d=Nkd=\frac{N}{k}\tag{1}

(2) d2k=Nd,  NkNkk=Nd\therefore\, d^2\cdot k=N\cdot d,\;\frac{N}{k}\cdot \frac{N}{k}\cdot k=N\cdot d\tag{2}

alt
上圖表示的是論文中涉及的幾種卷積層對應的參數量以及其感知域的對比,從中可見,EESP應用的深度空洞可分離卷積具有最少的參數最大的感受野

最後爲了直觀的表示參數量,我們對所有網絡進行實例對比:
當n=3,N=M=128,K=4時,根據本例子對參數量進行計算:

網格 傳統2D卷積 深度可分離卷積 分組卷積 ESPNet ESPNetv2
參數量 M*n2*N n2*1*M+1*1*N*M n2*(M/K)*(N/K)*K MN/K + (nN)2/K (n2*(N/K)+(N/K)2)*K+1*1*(N/K)2*K
大小 147456 17536 36864 40960 9344

由此可以見EESP相較於其他幾種卷積方法,在參數量上的確有明顯的減少。

3.7 Strided EESP與圖像輸入快速連接

爲了在多尺度下能夠有效地學習特徵,對圖1c的網絡做了四點改動(如下圖所示):
1)對DDConv添加stride屬性。
2)右邊的shortcut中帶了平均池化操作,實現維度匹配。
3)將相加的特徵融合方式替換爲concat形式,增加特徵的維度。
4)融合原始輸入圖像的下采樣信息,使得特徵信息更加豐富。具體做法是先將圖像下采樣到與特徵圖的尺寸相同的尺寸,然後使用第一個卷積,一個標準的3×3卷積,用於學習空間表示。再使用第二個卷積,一個逐點卷積,用於學習輸入之間的線性組合,並將其投影到高維空間。

alt

3.8 ESPNetv2的網絡結構

ESPNetv2網絡使用EESP單元構建。在每個空間級別,ESPNetv2重複多次EESP單元以增加網絡的深度。其中在每個卷積層之後使用batch normalization和PRelu,但在最後一個組級卷積層除外,在該層中,PRelu是在element-wise sum操作之後應用的。

  • Batch normalization
    對於每個隱層神經元,把逐漸向非線性函數映射後向取值區間極限飽和區靠攏的輸入分佈強制拉回到均值爲0方差爲1的比較標準的正態分佈,使得非線性變換函數的輸入值落入對輸入比較敏感的區域,以此避免梯度消失問題。然而如果只是單純的把數值拉回0-1正態分佈,那就等價於把非線性函數變爲線性函數,這就使模型的表示能力變差,失去“深度”的意義。所以BN爲了保證非線性的獲得,對變換後的滿足均值爲0方差爲1的x又進行了scale加上shift操作(y=scale*x+shift),這樣的一番操作之後可以找到一個線性和非線性的較好平衡點,既能享受非線性的較強表達能力的好處,又避免太靠非線性區兩頭使得網絡收斂速度太慢

  • PRelu(Parametric Rectified Linear Unit)

    alt

不同計算複雜度的ESPNet V2網絡,用於將224×224輸入分類爲ImageNet數據集中的1000個類(如下圖所示)。
alt

4. 實驗

爲了展示ESPNetv2的威力,作者在四種任務上與其他網絡進行了評估和比較:(1)object classification,(2)semantic segmentation,(3)object detection,(4)language modeling

4.1 圖像分類

數據集:
作者用的是ImageNet1000類的數據集,這其中包含着1.28M訓練圖像和50K驗證圖像。作者用來評估性能的指標是single crop top-1 classification accuracy

single crop: 先將圖像resize到某個大小,比如256xN,然後選擇圖像正中央部分,比如244x244。
top-1: 對於一個測試圖像,模型預測的概率最大的分類是正確分類,才認爲是該模型對該圖像分類正確

訓練:
作者是用搭配CUDA 9.0和cuDNN的PyTorch框架來進行訓練的。並且爲了優化,使用了熱重啓的隨機梯度下降(SGD with warm restart),即在每個迭代次數(epoch)t時,用這個公式來計算學習率:
ηt=ηmax(t  mod  T)  ηmin\eta_t=\eta_{max}-(t\; mod\; T)\,\cdot\, \eta_{min}

其中 ηmax\eta_{max}ηmin\eta_{min} 是學習率變化範圍的最大值和最小值,TT 是學習率重啓的循環週期。通過下圖我們能很清晰地看出學習率是如何變化的:

alt
作者在實驗中設置 ηmax=0.1\eta_{max}=0.1ηmin=0.5\eta_{min}=0.5T=5T=5, batch size設置爲512,訓練300個epoch,使用交叉熵損失函數。並且爲了快速收斂,在以下epoch的時候將學習率減半:{ 50,100,130,160,190,220,250,280 }。數據增強用的是標準的數據增強策略(除了color-based normalization沒有用),這與最近的輕量級架構形成了對比,後者用了更少的scale augmentation來防止欠擬合。初始化網絡權重的方法是用的何愷明大佬的方法(引用的這個論文:Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification)

結果:
如圖所示是ESPNetv2與其他最先進的輕量級網絡的性能對比:
alt

  • 跟ShuffleNetv1一樣,ESPNetv2也使用了逐點羣卷積(group point-wise convolution),但沒有用在ShuffleNetv1裏很有效的 通道混洗(channel shuffle),卻達到了比ShuffleNetv1更好的性能。(言外之意:我沒用這個trick我就這麼nb了┗|`O′|┛ 嗷~~)
  • 跟MobileNet相比,ESPNetv2也達到了更好的性能,尤其是在小計算開銷的情況下。在28百萬FLOPs時,ESPNetv2的性能比MobileNetv1(34百萬FLOPs)好10%,比MobileNetv2(30百萬FLOPs)好2%。
  • ESPNetv2達到了堪比ShuffleNetv2的準確率,但卻沒有使用通道分片(channel split),而正是這個trick使得ShuffleNetv2比ShuffleNetv1擁有更好的性能。所以作者相信如果用上了這兩個trick(channel shuffle,channel split),可以使網絡更加輕量,準確率更高。(無形裝逼最爲致命w(゚Д゚)w)
  • 和其他輕量級網絡比,在300百萬FLOPs的計算開銷下,ESPNetv2性能更好,比如:比CondenseNet準確率高1.1%。(就是吊打你們(´ー∀ー`))

多標籤分類:
爲了評估在遷移學習中的泛化能力,作者在MSCOCO的多目標分類任務上對我們的模型進行評估。作者是在驗證集(40504張圖像)上使用F1分數(F1 score)進行評估的。作者微調(finetune)了ESPNetv2(284百萬FLOPs)和ShuffleNetv2(299百萬FLOPs)到100個迭代次數,並且使用和訓練ImageNet數據集時一樣的數據增強和訓練設置,除了一點不一樣:ηmax=0.005\eta_{max}=0.005,ηmin=0.001\eta_{min}=0.001,並且學習率在50和80個epoch的時候減半。使用二元交叉熵損失函數來進行優化。結果如下圖所示:

alt

性能分析:
邊緣設備的計算資源和能源開銷都比較有限。爲這種設備設計的輕量級網絡應該達到更少的能量消耗,更小的延遲並保持着高準確率。作者又拿出了MobileNet和ShuffleNet進行比較(這倆網絡好慘(*゜—゜*)),在以下兩種設備上進行測試:(1)高端顯卡(NVIDIA GTX 1080 Ti)(2)嵌入式設備(NVIDIA Jetson TX2)。爲了公平比較,作者使用PyTorch作爲深度學習框架。下面兩張圖第一張圖比較了推理時間和功耗,第二張圖比較了網絡的複雜性和準確性:

alt

alt

ESPNetv2的推斷速度比最快的網絡(ShuffleNetv2)要略微慢一些,在兩臺設備上都是這樣,但是它在保持相似的準確率的同時卻更省電。(吊打的心永不變┑( ̄Д  ̄)┍)這說明ESPNetv2在準確率、能源消耗和延遲之間有着很好的tradeoff。對於任何運行在邊緣設備上的網絡,這是一個非常理想的特性。

4.2 語義分割

數據集:
作者在兩個數據集上進行性能評估,分別是Cityscapes和PASCAL VOC 2012數據集。

訓練:
作者分兩步來訓練網絡。第一步,用小分辨率的圖像進行訓練(PASCAL VOC 2012的256x256,CityScapes的512x256),用隨機梯度下降對網絡進行100個epoch的訓練,初始學習率設置爲0.007。第二步,提高圖像分辨率(PASCAL VOC 2012的384x384,CityScapes的1024x512)然後微調ESPNetv2到1000個epoch,用初始學習率爲0.003的隨機梯度下降並且使用之前提到的循環學習率策略。在最開始的50個epoch,循環週期設爲5,之後的設爲50.我們用平均交併比(mean intersection over union)作爲指標評估準確率。爲了評估,我們使用最近鄰插值將分割後的mask上採樣到與輸入圖像相同的大小。
結果:
作者在CityScapes和PASCAL VOC 2012這兩個數據集上比較了ESPNetv2與其他最先進的網絡的性能,如下圖所示:

alt

作者說可以看出ESPNetv2的性能與其他網絡相比還是很有競爭力的,但卻更輕量。在同等的算力條件下,ESPNetv2比現存的網絡(比如ENet,ESPNet)nb了一大截。值得注意的是,ESPNetv2的準確率比ICNet、ERFNet和ContextNet等其他網絡要低2-3%,但是它FLOPs要低了9-12倍。(總而言之就是NB<(ˉ^ˉ)>)

4.3 目標檢測

爲了探究ESPNetV2在目標檢測任務上的表現,作者用ESPNetV2替換掉了SSD(Single Shot Detector)中所利用的VGG,並分別在PASCAL VOC2007和COCO數據集上進行測試。通過表3可以發現,ESPNetv2比較好地權衡了效能和精確度這兩個評估標準。相較於傳統利用VGG16的SSD,新SSD取得了非常大的效能的提升。即使與YOLO9000進行對比,用ESPNetv2替換VGG16的SSD在效能上也有可觀的提升。

alt

4.4 語言建模

這個不是我們研究的主要方向,就偷個懶了(ヽ(✿゚▽゚)ノ)

5 消融實驗

這一部分對ESPNetv2中所應用的一些trick進行對照實驗

5.1 Convolution

這一部分驗證了深度可分離空洞卷積的作用。作者分別將深度可分離空洞卷積替換至深度可分離卷積和標準的空洞卷積並進行效能和準確率的測試。試驗結果表明,相較於深度可分離卷積,深度可分離空洞卷積提升了準確率,但由於兩種方法所需要的參數一樣,所以FLOPs並沒有發生變化。相較於標準的空洞卷積,深度可分離空洞卷積大大減少了FLOPs( n2N2K(n2NK+N2K2)×K\frac{n^2N^2}{K}\to \left( \frac{n^2N}{K}+\frac{N^2}{K^2}\right)\times K )

alt

5.2 HFF

HFF這一結構是在ESPNet中提出來的,ESPNetv2延用了這一設計。這一設計的目的是爲了解決由空洞卷積引發的著名的gridding artifact問題。下面的一幅圖表示了什麼是gridding:

alt

上圖(a)代表着三個空洞卷積層,它們的卷積核的大小都是333*3。可以看出,左圖中的紅色像素是由最上層的9個像素計算來的,而這9個像素映射到底層(即最右圖所示)是由這777*7個像素計算得到的。雖然空洞卷積可以在相同的卷積核大小下獲得更大的感受野,但是當空洞率越大,則對輸入的採樣越稀疏,一些局部信息也因此而丟失;並且長距離間隔的信息可能並不相關,打斷了局部信息之間的連續性特徵,下圖中的中間一行即表明了gridding所造成的影響。

alt

現今消除gridding artifact的方法大多需要比較多的運算參數(比如添加新的卷積層),而HFF只是進行簡單的矩陣運算,在一定程度上消除了高空洞率所引起的採樣稀疏問題,更滿足ESPNet減少參數和運算的設計要求。下圖展示了HFF去除gridding artifact的效果。通過對比實驗,還能發現HFF提升了分類的精準度,這是因爲多個分支不同感受野的卷積結果通過HFF共享信息,學習到了更豐富的特徵。

alt

5.3 Shortcut connection

Shortcut結構幫助緩解在卷積過程中下采樣造成的信息丟失,可以使一個卷積單元學到更多的特徵,通過實驗對比,Shortcut結構提升了Top1 準確率。

5.4 Cyclic learning

作者在4.1節的圖像分類實驗中,提到了本文所使用的週期學習策略(cyclic learning rate)。以往的策略是設置一個初始學習率,然後令其逐步減小直到損失函數不再下降。Cyclical Learning Rate則是在一個合理範圍內進行週期變化,這樣在大多數情況下都可以得到一個接近最優學習率的學習率。如下圖所示,當變化區間設置的足夠合理時,相當於能以更少的步驟提高模型的準確率,即提升模型的收斂速度。

alt

表中R3的實驗是利用固定學習率的策略(初始學習率爲0.1,總共訓練90個epoch,每過30個epoch學習率衰減1/10),R4的實驗則是利用cyclic learning rate的策略,與R3的區別是每個週期內學習率在區間0.1-0.5之間波動,實驗結果表明循環學習的策略在0.1和0.5之間找到了一個更好的局部最小值。R5則是利用4.1節的訓練方法(cyclic learning rate,總共300個epoch),相較於R4,R5對R3的Top1準確率提升更爲明顯。

alt

6 總結

本文介紹了一種輕權重、低功耗的網絡ESPNetv2,它能更好地對圖像中的空間信息進行編碼。這個網絡是一個通用的網絡,具有良好的泛化能力,可以用於廣泛的任務,包括序列建模。這個網絡在不同的任務(如對象分類、檢測、分割和語言建模)中提供了最先進的性能,同時更節能。

7 代碼

在這裏我們對核心代碼 EESP 模塊進行一下解釋

from torch.nn import init
import torch.nn.functional as F
from cnn.cnn_utils import *
import math
import torch

__author__ = "Sachin Mehta"
__version__ = "1.0.1"
__maintainer__ = "Sachin Mehta"

class EESP(nn.Module):
    '''
    EESP類定義了兩個函數,初始化函數和前向傳播,前向傳播按照
        REDUCE ---> SPLIT ---> TRANSFORM --> MERGE
    進行運算
    '''

    def __init__(self, nIn, nOut, stride=1, k=4, r_lim=7, down_method='esp'): #down_method --> ['avg' or 'esp']
        '''
        :param nIn: number of input channels 輸入通道數
        :param nOut: number of output channels 輸出通道數
        :param stride: factor by which we should skip (useful for down-sampling). If 2, then down-samples the feature map by 2 步長
        :param k: # of parallel branches 並行卷積的分支個數
        :param r_lim: A maximum value of receptive field allowed for EESP block EESP模塊的最大感受野
        :param g: number of groups to be used in the feature map reduction step. 分組卷積的參數
        '''
        super().__init__()
        self.stride = stride#初始化步長
        n = int(nOut / k)
        n1 = nOut - (k - 1) * n
        assert down_method in ['avg', 'esp'], 'One of these is suppported (avg or esp)'
        assert n == n1, "n(={}) and n1(={}) should be equal for Depth-wise Convolution ".format(n, n1)#分支深度卷積中,膨脹率最大時的維度要和輸出維度相同
        
        self.proj_1x1 = CBR(nIn, n, 1, stride=1, groups=k)#初始化2D卷積,然後歸一化,再用PRELU去線性化
        
        map_receptive_ksize = {3: 1, 5: 2, 7: 3, 9: 4, 11: 5, 13: 6, 15: 7, 17: 8}#對3*3卷積核,膨脹率和膨脹卷積核大小之間的對應關係
        self.k_sizes = list()
        for i in range(k):#膨脹率和膨脹卷積核大小之間的對應關係
            ksize = int(3 + 2 * i)
            # 到達邊界後
            ksize = ksize if ksize <= r_lim else 3
            self.k_sizes.append(ksize)
        
        self.k_sizes.sort()
        self.spp_dw = nn.ModuleList()
        for i in range(k):#初始化膨脹卷積函數
            #Transform
            d_rate = map_receptive_ksize[self.k_sizes[i]]#每輪的膨脹率
            self.spp_dw.append(CDilated(n, n, kSize=3, stride=stride, groups=n, d=d_rate))#將所有分支的膨脹函數裝到spp_dw中

        self.conv_1x1_exp = CB(nOut, nOut, 1, 1, groups=k)#卷積操作後歸一化
        self.br_after_cat = BR(nOut)#規範化,BR函數爲PRELU和歸一化
        self.module_act = nn.PReLU(nOut)#去線性化
        self.downAvg = True if down_method == 'avg' else False

    def forward(self, input):   "前向傳播算法"

        # Reduce,將M維輸入降維到D=N/K維
        output1 = self.proj_1x1(input)
        output = [self.spp_dw[0](output1)]
        # 計算每個分支的輸出並依次融合
        # Split --> Transform --> HFF
        for k in range(1, len(self.spp_dw)):
            out_k = self.spp_dw[k](output1)#每個分支進行DDConv
            # HFF,從最小的膨脹卷積核的輸出開始,逐級疊加,爲了改善網格效應
            out_k = out_k + output[k - 1]
            #保存下來每個分支的結果再融合
            output.append(out_k)
        # Merge
        expanded = self.conv_1x1_exp( # 先將輸出拼接
            self.br_after_cat( # 然後規範化
                torch.cat(output, 1) # 使用1*1的卷積核進行2d卷積操作,再歸一化
            )
        )
        del output
        # 步長爲二,下采樣,輸出變小
        if self.stride == 2 and self.downAvg:
            return expanded

        # 如果輸入和輸出向量的維度相同,則加和再輸出
        if expanded.size() == input.size():
            expanded = expanded + input

        return self.module_act(expanded)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章