(二)ShuffleNet_v1----2017論文解讀

ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices

ShuffleNet:一種用於移動設備的極其高效的卷積神經網絡

Abstract

我們介紹了一種名爲ShuffleNet的計算效率極高的CNN架構,該架構是專爲計算能力非常有限(例如10-150 MFLOP)的移動設備設計的。新架構利用了兩個新的操作,逐點組卷積和通道混洗,可以在保持準確性的同時大大降低計算成本。 ImageNet分類和MS COCO對象檢測的實驗證明了ShuffleNet優於其他結構的性能,例如在40個MFLOP的計算預算下,比最近的MobileNet [12]在ImageNet分類任務上的top-1錯誤要低(絕對7.8%)。在基於ARM的移動設備上,ShuffleNet的實際速度是AlexNet的13倍,同時保持了相當的準確性。

1 Introduction

建立更深,更大的卷積神經網絡(CNN)是解決主要視覺識別任務的主要趨勢[21,9,33,5,28,24]。最精確的CNN通常具有數百個層和數千個通道[9、34、32、40],因此需要數十億個FLOP進行計算。本報告探討了相反的極端情況:在非常有限的計算預算中以數十或數百個MFLOP追求最佳準確性,重點放在無人機,機器人和智能手機等常見的移動平臺上。請注意,許多現有的作品[16,22,43,42,38,27]專注於修剪,壓縮或代表“基本”網絡體系結構的低位。在這裏,我們旨在探索一種專爲我們所需的計算範圍而設計的高效基本架構。

我們注意到,由於代價高昂的密集1×1卷積,諸如Xception [3]和ResNeXt [40]之類的最新基礎架構在極小的網絡中的效率降低。我們建議使用逐點分組卷積來減少1×1卷積的計算複雜度。 爲了克服羣卷積帶來的副作用,我們提出了一種新穎的通道隨機操作,以幫助信息流過特徵通道。基於這兩種技術,我們構建了一種高效的體系結構,稱爲ShuffleNet。 與流行的結構[30,9,40]相比,在給定的計算複雜度預算的情況下,我們的ShuffleNet允許更多的特徵映射通道,這有助於編碼更多的信息,並且對於超小型網絡的性能尤爲關鍵。

我們根據具有挑戰性的ImageNet分類[4,29]和MS COCO對象檢測[23]任務評估我們的模型。一系列受控實驗顯示了我們設計原理的有效性以及優於其他結構的性能。與最先進的架構MobileNet [12]相比,ShuffleNet可以顯着提高性能,例如。在40個MFLOP級別上,ImageNet top-1錯誤絕對降低了7.8%。

我們還檢查了實際硬件(即基於ARM的現成計算核心)上的加速情況。 ShuffleNet模型比AlexNet [21]達到了約13倍的實際提速(理論提速爲18倍),同時保持了相當的精度。

2 Related Work

在這裏插入圖片描述
​ 圖1.具有兩個堆疊的組卷積的通道混洗。 GConv代表組卷積。 a)具有相同組數的兩個堆疊的卷積層。每個輸出通道僅與組中的輸入通道相關。 b)當GConv2在GConv1之後從不同組獲取數據時,輸入和輸出通道完全相關; c)與b)使用通道混洗等效的實現。

高效的模型設計最近幾年,深層神經網絡在計算機視覺任務中取得了成功[21、36、28],其中模型設計發揮了重要作用。在嵌入式設備上運行高質量深度神經網絡的需求不斷增長,鼓勵了對有效模型設計的研究[8]。例如,與簡單堆疊卷積層相比,GoogLeNet [33]以較低的複雜性增加了網絡的深度。 SqueezeNet [14]在保持精度的同時顯着減少了參數和計算量。 ResNet [9,10]利用高效的瓶頸結構來實現令人印象深刻的性能。 SENet [13]引入了一種架構單元,它以較低的計算成本提高了性能。與我們同時進行的是一項非常近期的工作[46],它採用強化學習和模型搜索來探索有效的模型設計。 擬議的移動NASNet模型在性能上與我們的ShuffleNet模型相當(對於ImageNet分類錯誤,其@ 564 MFLOP爲26.0%,而524 MFLOP爲26.3%)。 但是[46]沒有報告極小的模型的結果(例如,複雜度小於150 MFLOP),也沒有評估移動設備上的實際推理時間。

分組卷積 分組卷積的概念最早出現在AlexNet [21]中,用於在兩個GPU上分配模型,已在ResNeXt [40]中得到了充分證明。 Xception [3]中提出的深度可分離卷積概括了Inception系列[34,32]中的可分離卷積的思想。最近,MobileNet [12]利用深度可分離卷積並獲得了輕量模型中的最新結果。我們的工作以一種新穎的形式概括了羣卷積和深度可分離卷積。

據我們所知,儘管在CNN庫cuda-convnet [20]支持“隨機稀疏卷積”層(相當於隨機信道)的前提下,在有效模型設計的先前工作中很少提及信道隨機化操作的想法。洗牌,然後是組卷積層。這種“隨機混洗”操作具有不同的目的,以後很少被利用。最近,另一項並行的工作[41]也採用了這種思想進行兩階段卷積。然而,[41]沒有專門研究信道改組本身的有效性及其在微型模型設計中的使用。

模型加速 此方向旨在加快推理速度,同時保持預訓練模型的準確性。修剪網絡連接[6、7]或通道[38]可在保持性能的同時減少預訓練模型中的冗餘連接。在文獻中提出了量化[31、27、39、45、44]和因式分解[22、16、18、37],以減少計算中的冗餘以加速推理。在不修改參數的情況下,通過FFT [25,35]和其他方法[2]實現的優化卷積算法可以減少實踐中的時間消耗。提煉[11]將知識從大型模型轉移到小型模型,這使得對小型模型的訓練更加容易。

3 Approach

3.1. Channel Shuffle for Group Convolutions

現代卷積神經網絡[30、33、34、32、9、10]通常由具有相同結構的重複構建塊組成。其中,最先進的網絡,例如Xception [3]和ResNeXt [40],將有效的深度可分離卷積或組卷積引入到構建塊中,從而在表示能力和計算成本之間取得了很好的折衷。但是,我們注意到,兩種設計都沒有完全考慮1×1卷積(在[12]中也稱爲點狀卷積),這需要相當大的複雜性。例如,在ResNeXt [40]中,只有3×3層配備了組卷積。結果,對於ResNeXt中的每個殘差單元,逐點卷積佔據93.4%的乘積(基數= 32,如[40]中所建議)。在小型網絡中,昂貴的逐點卷積導致通道數量有限,無法滿足複雜性約束,這可能會嚴重影響精度。
在這裏插入圖片描述
​ 圖2. ShuffleNet單元。 a)具有深度卷積(DWConv)[3,12]的瓶頸單元[9]; b)具有按點分組卷積(GConv)和通道混洗的ShuffleNet單元; c)步幅= 2的ShuffleNet單元

爲了解決這個問題,一個簡單的解決方案是在1×1層上應用通道稀疏連接,例如組卷積。 通過確保每個卷積僅在相應的輸入通道組上運行,組卷積顯着降低了計算成本。 但是,如果多個組卷積堆疊在一起,則會產生一個副作用:某個通道的輸出僅從一小部分輸入通道派生。圖1(a)展示了兩個堆疊的組卷積層的情況。 顯然,某個組的輸出僅與該組內的輸入有關。 此屬性阻止通道組之間的信息流並削弱表示。

如果我們允許組卷積從不同組中獲取輸入數據(如圖1(b)所示),則輸入和輸出通道將完全相關。具體來說,對於從上一個組層生成的特徵圖,我們可以先將每個組中的通道劃分爲幾個子組,然後再將下一個層中的每個組提供給不同的子組。這可以通過通道混洗操作有效而優雅地實現(圖1(c)):假設一個具有g個組的卷積層,其輸出具有g×n個通道;我們首先將輸出通道的尺寸調整爲(g,n),進行轉置,然後再變平,作爲下一層的輸入。請注意,即使兩個卷積具有不同數量的組,該操作仍然會生效。此外,信道混洗也是可區分的,這意味着可以將其嵌入到網絡結構中以進行端到端訓練。

通道混洗操作可以構建具有多個組卷積層的更強大的結構。在下一個小節中,我們將介紹一個具有信道混洗和組卷積的高效網絡單元。

3.2. ShuffleNet Unit

利用信道隨機播放操作的優勢,我們提出了一種專門爲小型網絡設計的新型ShuffleNet單元。我們從圖2(a)中的瓶頸單元[9]的設計原理開始。這是一個剩餘的塊。在其剩餘分支中,對於3×3層,我們在瓶頸特徵圖上應用了經濟的3×3深度卷積計算方法[3]。然後,我們將第一個1×1層替換爲逐點組卷積,然後進行通道隨機混合操作,以形成ShuffleNet單元,如圖2(b)所示。第二次逐點分組卷積的目的是恢復通道尺寸以匹配快捷方式路徑。爲簡單起見,我們在第二個逐點層之後不應用額外的通道隨機播放操作,因爲它會產生可比的得分。批處理歸一化(BN)[15]和非線性的用法與[9,40]相似,不同之處在於我們不按[3]的建議在深度卷積後使用ReLU。對於ShuffleNet跨步應用的情況,我們只需進行兩個修改(見圖2(c)):(i)在快捷路徑上添加3×3平均池; (ii)用通道級聯替換逐元素加法,這使得擴展通道尺寸變得容易,而額外的計算成本卻很少。
在這裏插入圖片描述
由於具有通道混洗的逐點分組卷積,可以高效地計算ShuffleNet單元中的所有分量。 與ResNet [9](瓶頸設計)和ResNeXt [40]相比,我們的結構在相同設置下具有較低的複雜性。 例如,給定輸入大小c×h×w和瓶頸通道m,ResNet單位需要hw(2cm + 9m2)FLOP,ResNeXt則需要hw(2cm + 9m2 / g)FLOP,而我們的ShuffleNet單位僅需要hw(2cm / g + 9m)FLOP,其中g表示卷積的組數。 換句話說,給定計算預算,ShuffleNet可以使用更寬的特徵圖。 我們發現這對於小型網絡至關重要,因爲小型網絡通常沒有足夠的通道來處理信息。

此外,在ShuffleNet中,深度卷積僅對瓶頸特徵圖執行。儘管深度卷積通常具有非常低的理論複雜度,但我們發現很難在低功率移動設備上有效實現,這可能是由於與其他密集操作相比更差的計算/內存訪問比所致。在[3]中也提到了這種缺點,它具有基於TensorFlow [1]的運行時庫。在ShuffleNet單元中,我們故意僅在瓶頸上使用深度卷積,以儘可能避免開銷。

3.3. Network Architecture

在ShuffleNet單元的基礎上,我們在表1中介紹了整個ShuffleNet架構。所提議的網絡主要由分成三個階段的ShuffleNet單元堆棧組成。在步幅= 2的情況下應用第一個構建塊級。階段中的其他超參數保持不變,並且對於下一個階段,輸出通道加倍。類似於[9],我們將每個ShuffleNet單元的瓶頸通道數設置爲輸出通道的1/4。我們的目的是提供儘可能簡單的參考設計,儘管我們發現進一步的超參數調整可能會產生更好的結果。

在ShuffleNet單元中,組號g控制逐點卷積的連接稀疏性。表1探索了不同的組號,我們調整了輸出通道以確保總體計算成本大致不變(〜140 MFLOP)。顯然,對於給定的複雜性約束,較大的組數會導致更多的輸出通道(因此,需要更多的卷積濾波器),這有助於編碼更多信息,儘管由於受限的相應輸入通道,這也可能導致單個卷積濾波器的性能下降。在第4.1.1節中,我們將研究此數字在不同計算約束下的影響。

爲了將網絡定製爲所需的複雜度,我們可以簡單地在通道數上應用比例因子s。例如,我們在表1中將網絡表示爲“ ShuffleNet 1×”,然後“ ShuffleNet s×”表示將ShuffleNet 1×中的過濾器數量縮放s倍,因此總體複雜度將約爲ShuffleNet 1×s^2倍。

4 Experiments

我們主要在ImageNet 2012分類數據集上評估模型[29,4]。 我們遵循[40]中使用的大多數訓練設置和超參數,但有兩個例外:(i)將權重衰減設置爲4e-5而不是1e-4,並使用線性衰減學習率策略(從0.5降低 至0); (ii)我們在數據預處理中使用略微較少的積極規模擴展。 在[12]中也引用了類似的修改,因爲這樣的小型網絡通常遭受欠擬合而不是過度擬合的困擾。 在4個GPU上訓練3×105迭代的模型需要1或2天的時間,其批處理大小設置爲1024。爲進行基準測試,我們在ImageNet驗證集上比較了單作物top-1性能。 從256×輸入圖像中裁剪224×224中心視圖並評估分類準確性。 我們對所有模型使用完全相同的設置,以確保公平地進行比較。
在這裏插入圖片描述

4.1. Ablation Study

ShuffleNet的核心思想在於逐點羣組卷積和通道隨機操作。在本小節中,我們分別評估它們。

4.1.1 Pointwise Group Convolutions

爲了評估逐點羣卷積的重要性,我們比較了複雜度相同的ShuffleNet模型,其羣數範圍爲1到8。如果羣數等於1,則不涉及逐點羣卷積,然後ShuffleNet單元變爲“ Xception-喜歡” [3]結構。爲了更好地理解,我們還將網絡的寬度縮放到3個不同的複雜度,並分別比較它們的分類性能。結果如表2所示。

從結果可以看出,具有組卷積(g> 1)的模型的性能始終優於沒有逐點組卷積(g = 1)的模型。較小的模型往往會從團體中受益更多。例如,對於ShuffleNet 1x,最佳條目(g = 8)比同類條目好1.2%,而對於ShuffleNet 0.5x和0.25x,差距分別變爲3.5%和4.4%。請注意,對於給定的複雜性約束,組卷積允許更多的特徵圖通道,因此我們假設性能增益來自有助於編碼更多信息的更寬的特徵圖。另外,較小的網絡涉及較薄的特徵圖,這意味着它從擴大的特徵圖中受益更多。

表2還顯示,對於某些模型(例如ShuffleNet 0.5x),當組數變得相對較大(例如g = 8)時,分類得分會飽和甚至下降。隨着組數的增加(因此,特徵圖會更寬),每個卷積濾波器的輸入通道會越來越少,這可能會損害表示能力。有趣的是,我們還注意到,對於ShuffleNet等較小的模型,如0.25×較大的組號,往往會始終如一地獲得更好的結果,這表明較寬的特徵圖爲較小的模型帶來了更多好處。

4.1.2 Channel Shuffle vs. No Shuffle

隨機操作的目的是爲多個組卷積層啓用跨組信息流。表3比較了帶/不帶通道混洗的ShuffleNet結構(例如,組號設置爲3或8)的性能。評估是在三種不同的複雜度範圍內進行的。顯然,頻道改組可以持續提高不同設置的分類得分。特別地,當組數相對較大(例如,g = 8)時,具有信道混洗的模型在性能上優於對應的模型,這表明了跨組信息交換的重要性。

4.2. Comparison with Other Structure Units

VGG [30],ResNet [9],GoogleNet [33],ResNeXt [40]和Xception [3]中最近的領先卷積單元已經在大型模型(例如≥1GFLOP)上追求了最新的結果,但是沒有充分探索低複雜性條件。在本節中,我們調查了各種構建基塊,並在相同的複雜性約束下與ShuffleNet進行了比較。
在這裏插入圖片描述
爲了公平比較,我們使用表1所示的總體網絡架構。我們將Stage 2-4中的ShuffleNet單元替換爲其他結構,然後調整通道數以確保複雜度保持不變。我們探索的結構包括:

VGG-like。遵循VGG網絡的設計原理[30],我們使用兩層3×3卷積作爲基本構建塊。與[30]不同,我們在每個卷積之後添加了一個批處理歸一化層[15],以簡化端到端的訓練。

Xception-like。 [3]中提出的原始結構涉及不同階段的精美設計或超參數,我們發現很難在小模型上進行公平比較。取而代之的是,我們從ShuffleNet(也等效於g = 1的ShuffleNet)中刪除了逐點分組卷積和通道shuffle操作。派生結構與[3]中的“深度可分離卷積”想法相同,在此稱爲Xception-like結構。

ResNeXt。如[40]中所建議的,我們使用基數= 16和瓶頸比率= 1:2的設置。我們還將探索其他設置,例如瓶頸比率= 1:4,並獲得相似的結果。

我們使用完全相同的設置來訓練這些模型。結果如表4所示。在不同的複雜度下,我們的ShuffleNet模型要比其他大多數模型大得多。 有趣的是,我們發現了特徵圖通道與分類精度之間的經驗關係。 例如,在38個MFLOP的複雜性下,類VGG,ResNet,ResNeXt,類Xception,ShuffleNet的第4階段(請參見表1)的輸出通道分別爲50192、192、288、576,與 提高準確性。 由於ShuffleNet的高效設計,對於給定的計算預算,我們可以使用更多的通道,因此通常可以獲得更好的性能。

請注意,以上比較不包括GoogleNet或Inception系列[33、34、32]。我們發現將這樣的Inception結構生成到小型網絡並非易事,因爲Inception模塊的原始設計涉及太多的超參數。作爲參考,第一個GoogleNet版本[33]具有31.3%的top-1錯誤,但代價是1.5 GFLOP(請參見表6)。然而,更復雜的盜版版本[34,32]更加準確,但是複雜度也大大增加。最近,金等提出了一種輕量級的網絡結構,稱爲PVANET [19],它採用了Inception單元。我們重新實現的PV ANET(輸入大小爲224×224)具有29.7%的分類錯誤,計算複雜度爲557 MFLOP,而我們的ShuffleNet 2x模型(g = 3)在524 MFLOP的情況下獲得了26.3%(參見表6)。

4.3. Comparison with MobileNets and Other Frameworks

最近霍華德等。已經提出了MobileNets [12],其主要側重於移動設備的有效網絡架構。 MobileNet從[3]中採用了深度可分離卷積的思想,並在小型模型上獲得了最新的結果。
在這裏插入圖片描述
表5比較了各種複雜性級別下的分類得分。 顯然,我們的ShuffleNet模型在所有複雜性方面都優於MobileNet。 儘管我們的ShuffleNet網絡是專爲小型機型(<150 MFLOP)設計的,但我們發現它在計算成本方面比MobileNet更好,例如, 在500個MFLOP的代價下,其精度比MobileNet 1×高3.1%。 對於較小的網絡(約40個MFLOP),ShuffleNet比MobileNet超出7.8%。 請注意,我們的ShuffleNet架構包含50層,而MobileNet僅包含28層。 爲了更好地理解,我們還通過在階段2-4中刪除一半的塊來嘗試在26層體系結構上使用ShuffleNet(請參閱表5中的“ ShuffleNet 0.5×淺(g = 3)”)。 結果表明,較淺的模型仍然比相應的MobileNet更好,這表明ShuffleNet的有效性主要來自其有效的結構,而不是深度。

表6比較了我們的ShuffleNet和一些流行的模型。結果表明,以相似的精度ShuffleNet比其他的效率更高。例如,ShuffleNet 0.5×理論上比具有類似分類分數的AlexNet [21]快18倍。我們將在4.5節中評估實際的運行時間。

還值得注意的是,簡單的體系結構設計使向ShuffeNets輕鬆配備諸如[13,26]之類的最新進展。例如,在[13]中,作者提出了擠壓和激發(SE)塊,該塊可以在大型ImageNet模型上獲得最新的結果。我們發現SE模塊也可以與主鏈ShuffleNets結合使用,例如,將ShuffleNet 2×的top-1誤差提高到24.7%(如表5所示)。有趣的是,儘管理論複雜性的增加可以忽略不計,但我們發現帶有SE模塊的ShuffleNets通常比移動設備上的“原始” ShuffleNets慢25%到40%,這意味着實際的加速評估對於低成本架構設計至關重要。在第4.5節中,我們將進一步討論。

4.4. Generalization Ability

爲了評估遷移學習的泛化能力,我們在MS COCO對象檢測任務上測試了ShuffleNet模型[23]。我們採用Faster-RCNN [28]作爲檢測框架,並使用公開發布的Caffe代碼[28,17]進行默認設置的訓練。與[12]類似,模型在不包括5000個最小圖像的COCO序列+ val數據集上進行訓練,我們對最小集合進行測試。表7顯示了在兩種輸入分辨率下訓練和評估的結果的比較。將ShuffleNet 2x和MobileNet的複雜度相媲美(524 vs. 569 MFLOP),我們的ShuffleNet 2x在兩個分辨率上都遠遠超過MobileNet。我們的ShuffleNet 1×在600×分辨率下也可以與MobileNet取得可比的結果,但複雜度降低了約4倍。我們猜想,這一重大收益部分是由於ShuffleNet的架構設計簡單而沒有花哨。

4.5. Actual Speedup Evaluation

最後,我們評估具有ARM平臺的移動設備上ShuffleNet模型的實際推理速度。儘管具有較大組號(例如g = 4或g = 8)的ShuffleNets通常具有更好的性能,但我們發現它在當前的實現中效率較低。根據經驗,g = 3通常會在準確性和實際推理時間之間取得適當的折衷。如表8所示,測試使用了三種輸入分辨率。由於內存訪問和其他開銷,我們發現理論上每降低4倍的理論複雜度通常會導致實際速度提高2.6倍。儘管如此,與AlexNet [21]相比,我們的ShuffleNet 0.5×模型在可比的分類精度下(理論上的速度爲18倍)仍可實現約13倍的實際速度,這比以前的AlexNet級別的模型或諸如[14]的速度方法要快得多。[14、16、22、42、43、38]。

pytorch源碼

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import math

import torch
import torch.nn as nn
import torch.nn.functional as F


class Bottleneck(nn.Module):  # shuffle Net 模仿的是resnet bottleblock的結構
    def __init__(self, in_planes, out_planes, stride, groups):
        super(Bottleneck, self).__init__()
        self.stride = stride

        mid_planes = out_planes // 4  # 每個ShuffleNet unit的bottleneck通道數爲輸出的1/4(和ResNet設置一致)
        self.groups = 1 if in_planes == 24 else groups  # 第一層卷積之後是24,所以不必group
        self.conv1 = nn.Conv2d(in_planes, mid_planes, kernel_size=1, groups=self.groups, bias=False)
        self.bn1 = nn.BatchNorm2d(mid_planes)
        self.conv2 = nn.Conv2d(mid_planes, mid_planes, kernel_size=3, stride=stride, padding=1, groups=mid_planes, bias=False)  # 這裏應該用dw conv的
        self.bn2 = nn.BatchNorm2d(mid_planes)
        self.conv3 = nn.Conv2d(mid_planes, out_planes, kernel_size=1, groups=groups, bias=False)
        self.bn3 = nn.BatchNorm2d(out_planes)
        if stride == 2:
            self.shortcut = nn.Sequential(nn.AvgPool2d(3, stride=2, padding=1))  # 每個階段第一個block步長是2,下個階段通道翻倍

    @staticmethod
    def shuffle_channels(x, groups):
        '''Channel shuffle: [N,C,H,W] -> [N,g,C/g,H,W] -> [N,C/g,g,H,W] -> [N,C,H,W]'''
        '''一共C個channel要分成g組混合的channel,先把C reshape成(g, C/g)的形狀,然後轉置成(C/g, g)最後平坦成C組channel'''
        N, C, H, W = x.size()
        return x.view(N, groups, C // groups, H, W).permute(0, 2, 1, 3, 4).contiguous().view(N, C, H, W)  # 因爲x之前view過了,他的內存不連續了,需要contiguous來規整一下

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.shuffle_channels(out, self.groups)
        out = F.relu(self.bn2(self.conv2(out)))
        out = self.bn3(self.conv3(out))

        if self.stride == 2:
            res = self.shortcut(x)
            out = F.relu(torch.cat([out, res], 1))  # shuffle-net 對做了下采樣的res用的是cat而不是+
        else:
            out = F.relu(out + x)
        return out


class ShuffleNet(nn.Module):

    def __init__(self, out_planes, num_blocks, groups, num_classes=None, depth_multiplier=1.):  # depth_multiplier是控制通道數的縮放因子
        super(ShuffleNet, self).__init__()
        self.num_classes = num_classes
        self.in_planes = int(24 * depth_multiplier)  # 通常第一個卷積的輸出爲24
        self.out_planes = [int(depth_multiplier * x) for x in out_planes]

        self.conv1 = nn.Conv2d(3, self.in_planes, kernel_size=3, stride=2, bias=False)
        self.bn1 = nn.BatchNorm2d(self.in_planes)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        # self.bn1 = nn.BatchNorm2d(24)
        # self.in_planes = 24
        layers = []
        for out_plane, num_block in zip(out_planes, num_blocks):
            layers.append(self._make_layer(out_plane, num_block, groups))
        self.layers = nn.ModuleList(layers)  # 把list裏面的每一個元素變成一個module
        if num_classes is not None:
            self.avgpool = nn.AdaptiveMaxPool2d(1)
            self.fc = nn.Linear(out_planes[-1], num_classes)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()

    def _make_layer(self, out_planes, num_blocks, groups):
        layers = []
        for i in range(num_blocks):
            stride = 2 if i == 0 else 1  # 如果是第一個block就是2 否則就是1
            cat_planes = self.in_planes if i == 0 else 0  # 因爲第一個要下采樣並且cat,所以爲了下一個block加的時候能夠通道匹配,要先減掉cat的通道數
            layers.append(Bottleneck(self.in_planes, out_planes - cat_planes, stride=stride, groups=groups))
            self.in_planes = out_planes  # 第一個過後input就都是out_planes
        return nn.Sequential(*layers)

    @property
    def layer_channels(self):
        return self.out_planes

    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(x)))
        x = self.maxpool(x)

        c = []
        for i in range(len(self.layers)):
            x = self.layers[i](x)
            c.append(x)

        if self.num_classes is not None:
            x = self.avgpool(x)
            x = x.view(x.size(0), -1)
            x = self.fc(x)
            return x
        else:  # 返回每一個階段的特徵
            return c


def shufflenet(**kwargs):  # group = 3 論文實驗中效果最好的
    planes = [240, 480, 960]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=3, **kwargs)
    return model


def shufflenet_4(**kwargs):  # group = 4
    planes = [272, 544, 1088]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=4, **kwargs)
    return model


def shufflenet_2(**kwargs):
    planes = [200, 400, 800]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=2, **kwargs)
    return model


def shufflenet_1(**kwargs):
    planes = [144, 288, 576]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=1, **kwargs)
    return model


源碼鏈接:https://blog.csdn.net/weixin_44538273/article/details/88856239

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