輕量級(jí)網(wǎng)絡(luò)論文-VoVNet詳解
摘要
1,介紹
2,高效網(wǎng)絡(luò)設(shè)計(jì)的影響因素
2.1,內(nèi)存訪問代價(jià)
2.2,GPU計(jì)算效率
3,建議的方法
3.1,重新思考密集連接
3.2,One-Shot Aggregation
3.3,構(gòu)建 VoVNet 網(wǎng)絡(luò)
4,實(shí)驗(yàn)
5,代碼解讀
參考資料
摘要
Youngwan Lee*作者于2019年發(fā)表的論文 An Energy and GPU-Computation Efficient Backbone Network for Real-Time Object Detection. 是對(duì)DenseNet網(wǎng)絡(luò)推理效率低的改進(jìn)版本。
因?yàn)?DenseNet 通過用密集連接,來聚合具有不同感受野大小的中間特征,因此它在對(duì)象檢測(cè)任務(wù)上表現(xiàn)出良好的性能。雖然特征重用(feature reuse)的使用,讓 DenseNet 以少量模型參數(shù)和 FLOPs,也能輸出有力的特征,但是使用 DenseNet 作為 backbone 的目標(biāo)檢測(cè)器卻表現(xiàn)出了運(yùn)行速度慢和效率低下的弊端。作者認(rèn)為是密集連接(dense connection)帶來的輸入通道線性增長(zhǎng),從而導(dǎo)高內(nèi)存訪問成本和能耗。為了提高 DenseNet 的效率,作者提出一個(gè)新的更高效的網(wǎng)絡(luò) VoVet,由 OSA(One-Shot Aggregation,一次聚合)組成。OSA 僅在模塊的最后一層聚合前面所有層的特征,這種結(jié)構(gòu)不僅繼承了 DenseNet 的多感受野表示多種特征的優(yōu)點(diǎn),也解決了密集連接效率低下的問題?;?VoVNet 的檢測(cè)器不僅速度比 DenseNet 快 2 倍,能耗也降低了 1.5-4.1 倍。另外,VoVNet 網(wǎng)絡(luò)的速度和效率還優(yōu)于 ResNet,并且其對(duì)于小目標(biāo)檢測(cè)的性能有了顯著提高。
1,介紹
隨著 CNN 模型:VGG、ResNet 和 DensNet 的巨大進(jìn)步,它們開始被廣泛用作目標(biāo)檢測(cè)器的 backbone,用來提取圖像特征。
ResNet 和 DenseNet 主要的區(qū)別在于它們聚合特征的方式,ResNet 是通過逐元素相加(element-wise add)和前面特征聚合,DenseNet 則是通過拼接(concatenation)的方式。Zhu 等人在論文32[1] 中認(rèn)為前面的特征圖攜帶的信息將在與其他特征圖相加時(shí)被清除。換句話說,通過 concatenation 的方式,早期的特征才能傳遞下去,因?yàn)樗A袅颂卣鞯脑夹问剑]有改變特征本身)。
最近的一些工作 [25, 17, 13] 表明具有多個(gè)感受野的抽象特征可以捕捉各種尺度的視覺信息。因?yàn)闄z測(cè)任務(wù)比分類更加需要多樣化尺度去識(shí)別對(duì)象,因此保留來自各個(gè)層的信息對(duì)于檢測(cè)尤為重要,因?yàn)榫W(wǎng)絡(luò)每一層都有不同的感受野。因此,在目標(biāo)檢測(cè)任務(wù)上,DenseNet 比 ResNet 有更好更多樣化的特征表示。
這是不是說明對(duì)于,多標(biāo)簽分類問題,用 VoVNet 作為 backbone,效果要比 ResNet 要好。因?yàn)榍罢呖梢詫?shí)現(xiàn)多感受野表示特征。
盡管使用 DenseNet 的檢測(cè)器的參數(shù)量和 FLOPs 都比 ResNet 小,但是前者的能耗能耗和速度卻更慢。這是因?yàn)?,還有其他因素 FLOPs 和模型尺寸(參數(shù)量)影響能耗。
首先,內(nèi)存訪問代價(jià) MAC 是影響能耗的關(guān)鍵因素。如圖 1(a) 所示,因?yàn)?DenseNet 中的所有特征圖都被密集連接用作后續(xù)層的輸入,因此內(nèi)存訪問成本與網(wǎng)絡(luò)深度成二次方增加,從而導(dǎo)致計(jì)算開銷和更多的能耗。

從圖 (a) 中可以看出,DenseBlock 中的每一層的輸入都是前面所有層 feature map 的疊加。而圖 (b)只有最后一層的輸入是前面所有層 feature map 的疊加。
其次,關(guān)于 GPU 的并行計(jì)算,DenseNet 有計(jì)算瓶頸的限制。一般來說,當(dāng)操作的張量更大時(shí),GPU 的并行利用率會(huì)更高[19,29,13]。然而,由于為了線性增加輸入通道,需要 DenseNet 采用 1×1 卷積 bottleneck 架構(gòu)來減少輸入維度和 FLOPs,這導(dǎo)致使用較小的操作數(shù)張量增加層數(shù)。作為結(jié)果就是 GPU 計(jì)算變得低效??偨Y(jié)就是,bottleneck 結(jié)構(gòu)中的 卷積會(huì)導(dǎo)致 GPU 并行利用率。
本文的目的在于將 DenseNet 改進(jìn)的更高效,同時(shí),還保留對(duì)目標(biāo)檢測(cè)有益的連接聚合(concatenative aggregation)操作。
作者認(rèn)為 DenseNet 網(wǎng)絡(luò) DenseBlock 中間層的密集連接(
dense connections)會(huì)導(dǎo)致網(wǎng)絡(luò)效率低下,并假設(shè)相應(yīng)的密集連接是多余的。
作者使用 OSA 模塊構(gòu)建了 VoVNet 網(wǎng)絡(luò),為了驗(yàn)證有效性,將其作為 DSOD、RefineDet 和 Mask R-CNN 的 backbone 來做對(duì)比實(shí)驗(yàn)。實(shí)驗(yàn)結(jié)果表明,基于 VoVNet 的檢測(cè)器優(yōu)于 DenseNet 和 ResNet,速度和能耗都更優(yōu)。
2,高效網(wǎng)絡(luò)設(shè)計(jì)的影響因素
作者認(rèn)為,MobileNet v1 [8], MobileNet v2 [21], ShuffleNet v1 [31], ShuffleNet v2 [18], and Pelee 模型主要是通過使用 DW 卷積和 帶 卷積的 bottleneck 結(jié)構(gòu)來減少 FLOPs 和模型尺寸(參數(shù)量)。
這里我覺得作者表達(dá)不嚴(yán)謹(jǐn),因?yàn)?shufflenetv2 在論文中已經(jīng)聲明過,F(xiàn)LOPs 和模型參數(shù)量不是模型運(yùn)行速度的唯一決定因素。
實(shí)際上,減少 FLOPs 和模型大小并不總能保證減少 GPU 推理時(shí)間和實(shí)際能耗,典型的例子就是 DenseNet 和 ResNet 的對(duì)比,還有就是在 GPU 平臺(tái)上, Shufflenetv2 在同等參數(shù)條件下,運(yùn)行速度比 MobileNetv2 更快。這些現(xiàn)象告訴我們,FLOPs 和 模型尺寸(參數(shù))是衡量模型實(shí)用性(practicality)的間接指標(biāo)。為了設(shè)計(jì)更高效的網(wǎng)絡(luò),我們需要使用直接指標(biāo) FPS,除了上面說的 FLOPs 和模型參數(shù)量會(huì)影響模型的運(yùn)行速度(FPS),還有以下幾個(gè)因素。
2.1,內(nèi)存訪問代價(jià)
這個(gè) Shufflenetv2 作者已經(jīng)解釋得很清楚了,本文的作者的描述基本和 Shufflenetv2 一致。我這里直接給結(jié)論:
MAC對(duì)能耗的影響超過了計(jì)算量FLOPs[28]。卷積層輸入輸出通道數(shù)相等時(shí), MAC取得最小值。即使模型參數(shù)量一致,只要 MAC不同,那么模型的運(yùn)行時(shí)間也是不一致的(ShuffleNetv2 有實(shí)驗(yàn)證明)。
論文 [28] Designing energy-efficient convolutional neural networks using energyaware pruning.
2.2,GPU計(jì)算效率
其實(shí)這個(gè)內(nèi)容和 shufflenetv2 論文中的 G3 原則(網(wǎng)絡(luò)碎片化會(huì)降低 GPU 并行度)基本一致。
為提高速度而降低 FLOPs 的網(wǎng)絡(luò)架構(gòu)基于這樣一種理念,即設(shè)備中的每個(gè)浮點(diǎn)運(yùn)算都以相同的速度進(jìn)行處理。但是,當(dāng)模型部署在 GPU 上時(shí),不是這樣的,因?yàn)?GPU 是并行處理機(jī)制能同時(shí)處理多個(gè)浮點(diǎn)運(yùn)算進(jìn)程。我們用 GPU 計(jì)算效率來表示 GPU 的運(yùn)算能力。
通過減少 FLOPs是來加速的前提是,設(shè)備中的每個(gè)浮點(diǎn)運(yùn)算都以相同的速度進(jìn)行處理;GPU 特性: 擅長(zhǎng) parallel computation,tensor越大,GPU使用效率越高。把大的卷積操作拆分成碎片的小操作將不利于 GPU計(jì)算。因此,設(shè)計(jì) layer數(shù)量少的網(wǎng)絡(luò)是更好的選擇。MobileNet使用額外的 1x1 卷積來減少計(jì)算量,不過這不利于 GPU 計(jì)算。為了衡量 GPU 利用率,引入有一個(gè)新指標(biāo):(每秒完成的計(jì)算量 FLOPs per Second),F(xiàn)LOP/s 高,則GPU利用率率也高。
3,建議的方法
3.1,重新思考密集連接
1,DenseNet 的優(yōu)點(diǎn):
在計(jì)算第 層的輸出時(shí),要用到之前所有層的輸出的 concat 的結(jié)果。這種密集的連接使得各個(gè)層的各個(gè)尺度的特征都能被提取,供后面的網(wǎng)絡(luò)使用。這也是它能得到比較高的精度的原因,而且密集的連接更有利于梯度的回傳(ResNet shorcut 操作的加強(qiáng)版)。
2,DenseNet 缺點(diǎn)(導(dǎo)致了能耗和推理效率低的):
密集連接會(huì)增加輸入通道大小,但輸出通道大小保持不變,導(dǎo)致的輸入和輸出通道數(shù)都不相等。因此,DenseNet 具有具有較高的 MAC。 DenseNet 采用了 bottleneck結(jié)構(gòu),這種結(jié)構(gòu)將一個(gè) 卷積分成了兩個(gè)計(jì)算(1x1+3x3 卷積),這帶來了更多的序列計(jì)算(sequential computations),導(dǎo)致會(huì)降低推理速度。
密集連接會(huì)導(dǎo)致計(jì)算量增加,所以不得不采用 卷積的
bottleneck結(jié)構(gòu)。
圖 7 的第 1 行是 DenseNet 各個(gè)卷積層之間的相互關(guān)系的大小。第 塊代表第 層和第 層之間這個(gè)卷積權(quán)值的平均 范數(shù)(按特征圖數(shù)量歸一化后的 L1 范數(shù))的大小,也就相當(dāng)于是表征 和 之間的關(guān)系。
圖 2. 訓(xùn)練后的 DenseNet(頂部) 和 VoVNet(中間和底部) 中卷積層的濾波器權(quán)重的絕對(duì)值的平均值。像素塊的顏色表示的是相互連接的網(wǎng)絡(luò)層(i, j)的權(quán)重的平均 范數(shù)(按特征圖數(shù)量歸一化后的 L1 范數(shù))的值。OSA Module (x/y) 指的是 OSA 模塊由 層和 個(gè)通道組成。

如圖 2 頂部圖所示, Hu 等人[9]通過評(píng)估每層輸入權(quán)重歸一化后的 L1 范數(shù)來說明密集連接的連通性(connectivity),這些值顯示了前面所有層對(duì)相應(yīng)層的歸一化影響,1 表示影響最大,0 表示沒有影響(兩個(gè)層之間的權(quán)重沒有關(guān)系)。
這里重點(diǎn)解釋下連通性的理解。兩層之間的輸入權(quán)重的絕對(duì)值相差越大,即 L1 越大,那么說明卷積核的權(quán)重越不一樣,前面層對(duì)后面層影響越大(
connectivity),即連通性越好(大)。從實(shí)用性角度講,我們肯定希望相互連接的網(wǎng)絡(luò)層的連通性越大越好(歸一化后是 0~1 范圍),這樣我的密集連接才起作用了嘛。不然,耗費(fèi)了計(jì)算量、犧牲了效率,但是連通性結(jié)果又差,那我還有必要設(shè)計(jì)成密集連接(dense connection)。作者通過圖 2 后面的兩張圖也證明了DenseBlock 模塊中各個(gè)層之間的聯(lián)系大部分都是沒用,只有少部分是有用的,即密集連接中大部分網(wǎng)絡(luò)層的連接是無效的。
在 Dense Block3 中,對(duì)角線附近的紅色框表示中間層(intermediate layers)上的聚合處于活動(dòng)狀態(tài),但是分類層(classification layer)只使用了一小部分中間特征。相比之下,在 Dense Block1 中,過渡層(transition layer)很好地聚合了其大部分輸入特征,而中間層則沒有。
Dense Block3 的分類層和 Dense Block1 的過渡層都是模塊的最后一層。
通過前面的觀察,我們先假設(shè)中間層的聚集強(qiáng)度和最后一層的聚集強(qiáng)度之間存在負(fù)相關(guān)(中間層特征層的聚合能力越好,那么最后層的聚合能力就越弱)。如果中間層之間的密集連接導(dǎo)致了每一層的特征之間存在相關(guān)性,則密集連接會(huì)使后面的中間層產(chǎn)生更好的特征的同時(shí)與前一層的特征相似,則假設(shè)成立。在這種情況下,因?yàn)檫@兩種特征代表冗余信息,所以最后一層不需要學(xué)習(xí)聚合它們,從而前中間層對(duì)最終層的影響變小。
因?yàn)樽詈笠粚拥奶卣鞫际峭ㄟ^聚集(aggregated)所有中間層的特征而產(chǎn)生的,所以,我們當(dāng)然希望中間層的這些特征能夠互補(bǔ)或者相關(guān)性越低越好。因此,進(jìn)一步提出假設(shè),相比于造成的損耗,中間特征層的 dense connection 產(chǎn)生的作用有限。為了驗(yàn)證假設(shè),我們重新設(shè)計(jì)了一個(gè)新的模塊 OSA,該模塊僅在最后一層聚合塊中其他層的特征(intermediate features),把中間的密集連接都去掉。
3.2,One-Shot Aggregation
為了驗(yàn)證我們的假設(shè),中間層的聚合強(qiáng)度和最后一層的聚合強(qiáng)度之間存在負(fù)相關(guān),并且密集連接是多余的,我們與 Hu 等人進(jìn)行了相同的實(shí)驗(yàn),實(shí)驗(yàn)結(jié)果是圖 2 中間和底部位置的兩張圖。

從圖 2(中間)可以觀察到,隨著中間層上的密集連接被剪掉,最終層中的聚合變得更加強(qiáng)烈。同時(shí),藍(lán)色的部分 (聯(lián)系大部分不緊密的部分) 明顯減少了很多,也就是說 OSA 模塊的每個(gè)連接都是相對(duì)有用的。
從圖 2(底部)的可以觀察到,OSA 模塊的過渡層的權(quán)重顯示出與 DenseNet 不同的模式:來自淺層的特征更多地聚集在過渡層上。由于來自深層的特征對(duì)過渡層的影響不大,我們可以在沒有顯著影響的情況下減少 OSA 模塊的層數(shù),得到。令人驚訝的是,使用此模塊(5 層網(wǎng)絡(luò)),我們實(shí)現(xiàn)了 5.44% 的錯(cuò)誤率,與 DenseNet-40 (模塊里有 12 層網(wǎng)絡(luò))的錯(cuò)誤率(5.24%)相似。這意味著通過密集連接構(gòu)建深度中間特征的效果不如預(yù)期(This implies that building deep intermediate feature via dense connection is less effective than expected)。
One-Shot Aggregation(只聚集一次)是指 OSA 模塊的 concat 操作只進(jìn)行一次,即只有最后一層的輸入是前面所有層 feature map 的 concat(疊加)。OSA 模塊的結(jié)構(gòu)圖如圖 1(b) 所示。

在 OSA 模塊中,每一層產(chǎn)生兩種連接,一種是通過 conv 和下一層連接,產(chǎn)生 receptive field 更大的 feature map,另一種是和最后的輸出層相連,以聚合足夠好的特征。
為了驗(yàn)證 OSA 模塊的有效性,作者使用 dense block 和 OSA 模塊構(gòu)成 DenseNet-40網(wǎng)絡(luò),使兩種模型參數(shù)量一致,做對(duì)比實(shí)驗(yàn)。OSA 模板版本在 CIFAR-10 數(shù)據(jù)集上的精度達(dá)到了 93.6,和 dense block 版本相比,只下降了 1.2%。再根據(jù) MAC 的公式,可知 MAC 從 3.7M 減少為 2.5M。MAC 的降低是因?yàn)?OSA 中的中間層具有相同大小的輸入輸出通道數(shù),從而使得 MAC 可以取最小值(lower boundary)。
因?yàn)?OSA 模塊中間層的輸入輸出通道數(shù)一致,所以沒必要使用 bottleneck 結(jié)構(gòu),這又進(jìn)一步提高了 GPU 利用率。
3.3,構(gòu)建 VoVNet 網(wǎng)絡(luò)
因?yàn)?OSA 模塊的多樣化特征表示和效率,所以可以通過僅堆疊幾個(gè)模塊來構(gòu)建精度高、速度快的 VoVNet 網(wǎng)絡(luò)?;趫D 2 中淺層深度更容易聚合的認(rèn)識(shí),作者認(rèn)為可以配置比 DenseNet 具有更大通道數(shù)的但更少卷積層的 OSA 模塊。
如下圖所示,分別構(gòu)建了 VoVNet-27-slim,VoVNet-39, VoVNet-57。注意,其中downsampling 層是通過 3x3 stride=2 的 max pooling 實(shí)現(xiàn)的,conv 表示的是 Conv-BN-ReLU 的順序連接。

VOVNet 由 5 個(gè)階段組成,各個(gè)階段的輸出特征大小依次降為原來的一半。VOVNet-27 前 2 個(gè) stage 的連接圖如下所示。

4,實(shí)驗(yàn)
GPU 的能耗計(jì)算公式如下:

實(shí)驗(yàn)1:VoVNet vs. DenseNet. 對(duì)比不同 backbone 下的目標(biāo)檢測(cè)模型性能(PASCALVOC)

對(duì)比指標(biāo):
Flops:模型需要的計(jì)算量 FPS:模型推斷速度img/s Params:參數(shù)數(shù)量 Memory footprint:內(nèi)存占用 Enegry Efficiency:能耗 Computation Efficiency:GPU 計(jì)算效率(GFlops/s) mAP(目標(biāo)檢測(cè)性能評(píng)價(jià)指標(biāo))
現(xiàn)象與總結(jié):
現(xiàn)象 1:相比于 DenseNet-67,PeleeNet 減少了 Flops,但是推斷速度沒有提升,與之相反,VoVNet-27-slim 稍微增加了Flops,而推斷速度提升了一倍。同時(shí),VoVNet-27-sli m的精度比其他模型都高。 現(xiàn)象 2:VoVNet-27-slim 的內(nèi)存占用、能耗、GPU 利用率都是最高的。 結(jié)論 1:相比其他模型,VoVNet做到了準(zhǔn)確率和效率的均衡,提升了目標(biāo)檢測(cè)的整體性能。
實(shí)驗(yàn)2:Ablation study on 1×1 conv bottleneck.

結(jié)論 2:可以看出,1x1 bottleneck 增加了 GPU Inference 時(shí)間,降低了 mAP,盡管它減少了參數(shù)數(shù)量和計(jì)算量。
因?yàn)?1x1 bottleneck 增加了網(wǎng)路的總層數(shù),需要更多的激活層,從而增加了內(nèi)存占用。
實(shí)驗(yàn)3:GPU-Computation Efficiency.

圖3(a) VoVNet 兼顧準(zhǔn)確率和 Inference 速度 圖3(b) VoVNet 兼顧準(zhǔn)確率和 GPU 使用率 圖3(c) VoVNet 兼顧準(zhǔn)確率和能耗 圖3(d) VoVNet 兼顧能耗和 GPU 使用率
實(shí)驗(yàn)室4:基于RefineDet架構(gòu)比較VoVNet、ResNet和DenseNet。

結(jié)論 4:從 COCO 數(shù)據(jù)集測(cè)試結(jié)果看,相比于 ResNet,VoVnet在 Inference 速度,內(nèi)存占用,能耗,GPU 使用率和準(zhǔn)確率上都占據(jù)優(yōu)勢(shì)。盡管很多時(shí)候,VoVNet 需要更多的計(jì)算量以及參數(shù)量。
對(duì)比 DenseNet161(k=48) 和 DenseNet201(k=32)可以發(fā)現(xiàn),深且”瘦“的網(wǎng)絡(luò),GPU 使用率更低。 另外,作者發(fā)現(xiàn)相比于 ResNet,VoVNet 在小目標(biāo)上的表現(xiàn)更好。
實(shí)驗(yàn) 5:Mask R-CNN from scratch.
通過替換 Mask R-CNN 的 backbone,也發(fā)現(xiàn) VoVNet 在Inference 速度和準(zhǔn)確率上優(yōu)于 ResNet。

5,代碼解讀
雖然 VoVNet 在 CenterMask 論文[2] 中衍生出了升級(jí)版本 VoVNetv2,但是本文的代碼解讀還是針對(duì)原本的 VoVNet,代碼來源這里[3]。
1,定義不同類型的卷積函數(shù)
def conv3x3(in_channels, out_channels, module_name, postfix,
stride=1, groups=1, kernel_size=3, padding=1):
"""3x3 convolution with padding. conv3x3, bn, relu的順序組合
"""
return [
('{}_{}/conv'.format(module_name, postfix),
nn.Conv2d(in_channels, out_channels,
kernel_size=kernel_size,
stride=stride,
padding=padding,
groups=groups,
bias=False)),
('{}_{}/norm'.format(module_name, postfix),
nn.BatchNorm2d(out_channels)),
('{}_{}/relu'.format(module_name, postfix),
nn.ReLU(inplace=True)),
]
def conv1x1(in_channels, out_channels, module_name, postfix,
stride=1, groups=1, kernel_size=1, padding=0):
"""1x1 convolution"""
return [
('{}_{}/conv'.format(module_name, postfix),
nn.Conv2d(in_channels, out_channels,
kernel_size=kernel_size,
stride=stride,
padding=padding,
groups=groups,
bias=False)),
('{}_{}/norm'.format(module_name, postfix),
nn.BatchNorm2d(out_channels)),
('{}_{}/relu'.format(module_name, postfix),
nn.ReLU(inplace=True)),
]
2,其中 OSA 模塊結(jié)構(gòu)的代碼如下。
class _OSA_module(nn.Module):
def __init__(self,
in_ch,
stage_ch,
concat_ch,
layer_per_block,
module_name,
identity=False):
super(_OSA_module, self).__init__()
self.identity = identity # 默認(rèn)不使用恒等映射
self.layers = nn.ModuleList()
in_channel = in_ch
# stage_ch: 每個(gè) stage 內(nèi)部的 channel 數(shù)
for i in range(layer_per_block):
self.layers.append(nn.Sequential(
OrderedDict(conv3x3(in_channel, stage_ch, module_name, i))))
in_channel = stage_ch
# feature aggregation
in_channel = in_ch + layer_per_block * stage_ch
# concat_ch: 1×1 卷積輸出的 channel 數(shù)
# 也從 stage2 開始,每個(gè) stage 最開始的輸入 channnel 數(shù)
self.concat = nn.Sequential(
OrderedDict(conv1x1(in_channel, concat_ch, module_name, 'concat')))
def forward(self, x):
identity_feat = x
output = []
output.append(x)
for layer in self.layers: # 中間所有層的順序連接
x = layer(x)
output.append(x)
# 最后一層的輸出要和前面所有層的 feature map 做 concat
x = torch.cat(output, dim=1)
xt = self.concat(x)
if self.identity:
xt = xt + identity_feat
return xt
3,定義 _OSA_stage,每個(gè) stage 有多少個(gè) OSA 模塊,由 _vovnet 函數(shù)的 block_per_stage 參數(shù)指定。
class _OSA_stage(nn.Sequential):
"""
in_ch: 每個(gè) stage 階段最開始的輸入通道數(shù)(feature map 數(shù)量)
"""
def __init__(self,
in_ch,
stage_ch,
concat_ch,
block_per_stage,
layer_per_block,
stage_num):
super(_OSA_stage, self).__init__()
if not stage_num == 2:
self.add_module('Pooling',
nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True))
module_name = f'OSA{stage_num}_1'
self.add_module(module_name,
_OSA_module(in_ch,
stage_ch,
concat_ch,
layer_per_block,
module_name))
for i in range(block_per_stage-1):
module_name = f'OSA{stage_num}_{i+2}'
self.add_module(module_name,
_OSA_module(concat_ch,
stage_ch,
concat_ch,
layer_per_block,
module_name,
identity=True))
4,定義 VOVNet,
class VoVNet(nn.Module):
def __init__(self,
config_stage_ch,
config_concat_ch,
block_per_stage,
layer_per_block,
num_classes=1000):
super(VoVNet, self).__init__()
# Stem module --> stage1
stem = conv3x3(3, 64, 'stem', '1', 2)
stem += conv3x3(64, 64, 'stem', '2', 1)
stem += conv3x3(64, 128, 'stem', '3', 2)
self.add_module('stem', nn.Sequential(OrderedDict(stem)))
stem_out_ch = [128]
# vovnet-57,in_ch_list 結(jié)果是 [128, 256, 512, 768]
in_ch_list = stem_out_ch + config_concat_ch[:-1]
self.stage_names = []
for i in range(4): #num_stages
name = 'stage%d' % (i+2)
self.stage_names.append(name)
self.add_module(name,
_OSA_stage(in_ch_list[i],
config_stage_ch[i],
config_concat_ch[i],
block_per_stage[i],
layer_per_block,
i+2))
self.classifier = nn.Linear(config_concat_ch[-1], num_classes)
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight)
elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
nn.init.constant_(m.bias, 0)
def forward(self, x):
x = self.stem(x)
for name in self.stage_names:
x = getattr(self, name)(x)
x = F.adaptive_avg_pool2d(x, (1, 1)).view(x.size(0), -1)
x = self.classifier(x)
return x
5,VoVNet 各個(gè)版本的實(shí)現(xiàn)。vovnet57 中有 4 個(gè) stage,每個(gè) stage 的 OSP 模塊數(shù)目依次是 [1,1,4,3],每個(gè) 個(gè) stage 內(nèi)部對(duì)應(yīng)的通道數(shù)都是一樣的,分別是 [128, 160, 192, 224]。每個(gè) stage 最后的輸出通道數(shù)分別是 [256, 512, 768, 1024],由 concat_ch 參數(shù)指定。
所有版本的 vovnet 的 OSA 模塊中的卷積層數(shù)都是 5。
def _vovnet(arch,
config_stage_ch,
config_concat_ch,
block_per_stage,
layer_per_block,
pretrained,
progress,
**kwargs):
model = VoVNet(config_stage_ch, config_concat_ch,
block_per_stage, layer_per_block,
**kwargs)
if pretrained:
state_dict = load_state_dict_from_url(model_urls[arch],
progress=progress)
model.load_state_dict(state_dict)
return model
def vovnet57(pretrained=False, progress=True, **kwargs):
r"""Constructs a VoVNet-57 model as described in
`"An Energy and GPU-Computation Efficient Backbone Networks"
<https://arxiv.org/abs/1904.09730>`_.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
return _vovnet('vovnet57', [128, 160, 192, 224], [256, 512, 768, 1024],
[1,1,4,3], 5, pretrained, progress, **kwargs)
def vovnet39(pretrained=False, progress=True, **kwargs):
r"""Constructs a VoVNet-39 model as described in
`"An Energy and GPU-Computation Efficient Backbone Networks"
<https://arxiv.org/abs/1904.09730>`_.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
return _vovnet('vovnet39', [128, 160, 192, 224], [256, 512, 768, 1024],
[1,1,2,2], 5, pretrained, progress, **kwargs)
def vovnet27_slim(pretrained=False, progress=True, **kwargs):
r"""Constructs a VoVNet-39 model as described in
`"An Energy and GPU-Computation Efficient Backbone Networks"
<https://arxiv.org/abs/1904.09730>`_.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
return _vovnet('vovnet27_slim', [64, 80, 96, 112], [128, 256, 384, 512],
[1,1,1,1], 5, pretrained, progress, **kwargs)
參考資料
32: https://arxiv.org/abs/1801.05895
[2]CenterMask 論文: https://link.zhihu.com/?target=https%3A//arxiv.org/pdf/1911.06667.pdf
[3]這里: https://github.com/stigma0617/VoVNet.pytorch/blob/master/models_vovnet/vovnet.py
[4]論文筆記VovNet(專注GPU計(jì)算、能耗高效的網(wǎng)絡(luò)結(jié)構(gòu)): https://zhuanlan.zhihu.com/p/79677425
[5]實(shí)時(shí)目標(biāo)檢測(cè)的新backbone網(wǎng)絡(luò):VOVNet: https://zhuanlan.zhihu.com/p/393740052
