本文是對VGG模型的介紹和詳解,引用了其他博主的文章,僅供個人學習。
簡介:這篇文章是以比賽為目的——解決ImageNet中的1000類圖像分類和定位問題。在此過程中,作者做了六組實驗,對應6個不同的網絡模型,這六個網絡深度逐漸遞增的同時,也有各自的特點。實驗表明最后兩組,即深度最深的兩組16和19層的VGGNet網絡模型在分類和定位任務上的效果最好。作者因此斬獲2014年分類第二(第一是GoogLeNet),定位任務第一。
論文鏈接:Very deep convolutional networks for large-scale image recognition(2014)
需要注意的是,在VGGNet的6組實驗中,后面的4個網絡均使用了pre-trained model A的某些層來做參數初始化。雖然作者沒有提該方法帶來的性能增益,但我認為是很大的。不過既然是開篇,先來看看VGG的特點:
- 小卷積核。作者將卷積核全部替換為3x3(極少用了1x1);
- 小池化核。相比AlexNet的3x3的池化核,VGG全部為2x2的池化核;
- 層數更深特征圖更寬。基於前兩點外,由於卷積核專注於擴大通道數、池化專注於縮小寬和高,使得模型架構上更深更寬的同時,計算量的增加放緩;
- 全連接轉卷積。網絡測試階段將訓練階段的三個全連接替換為三個卷積,測試重用訓練時的參數,使得測試得到的全卷積網絡因為沒有全連接的限制,因而可以接收任意寬或高為的輸入。
小卷積核
說到網絡深度,這里就不得不提到卷積,雖然AlexNet有使用了11x11和5x5的大卷積,但大多數還是3x3卷積,對於stride=4的11x11的大卷積核,我認為在於一開始原圖的尺寸很大因而冗余,最為原始的紋理細節的特征變化用大卷積核盡早捕捉到,后面的更深的層數害怕會丟失掉較大局部范圍內的特征相關性,后面轉而使用更多3x3的小卷積核(和一個5x5卷積)去捕捉細節變化。而VGGNet則清一色使用3x3卷積。因為卷積不僅涉及到計算量,還影響到感受野。前者關系到是否方便部署到移動端、是否能滿足實時處理、是否易於訓練等,后者關系到參數更新、特征圖的大小、特征是否提取的足夠多、模型的復雜度和參數量等等。
計算量
在計算量這里,為了突出小卷積核的優勢,我拿同樣conv3x3、conv5x5、conv7x7、conv9x9和conv11x11,在224x224x3的RGB圖上(設置pad=1,stride=4,output_channel=96)做卷積,卷積層的參數規模和得到的feature map的大小如下:
從上表可以看出,大卷積核帶來的特征圖和卷積核得參數量並不大,無論是單獨去看卷積核參數或者特征圖參數,不同kernel大小下這二者加和的結構都是30萬的參數量,也就是說,無論大的卷積核還是小的,對參數量來說影響不大甚至持平。
增大的反而是卷積的計算量,在表格中列出了計算量的公式,最后要乘以2,代表乘加操作。為了盡可能證一致,這里所有卷積核使用的stride均為4,可以看到,conv3x3、conv5x5、conv7x7、conv9x9、conv11x11的計算規模依次為:1600萬,4500萬,1.4億、2億,這種規模下的卷積,雖然參數量增長不大,但是計算量是驚人的。
總結一下,我們可以得出兩個結論:
- 同樣stride下,不同卷積核大小的特征圖和卷積參數差別不大;
- 越大的卷積核計算量越大。
其實對比參數量,卷積核參數的量級在十萬,一般都不會超過百萬。相比全連接的參數規模是上一層的feature map和全連接的神經元個數相乘,這個計算量也就更大了。其實一個關鍵的點——多個小卷積核的堆疊比單一大卷積核帶來了精度提升,這也是最重要的一點。
感受野
說完了計算量我們再來說感受野。這里給出一張VGG作者的PPT,作者在VGGNet的實驗中只用了兩種卷積核大小:1x1和3x3。作者認為兩個3x3的卷積堆疊獲得的感受野大小,相當一個5x5的卷積;而3個3x3卷積的堆疊獲取到的感受野相當於一個7x7的卷積。
見下圖,輸入的8個元素可以視為feature map的寬或者高,當輸入為8個神經元經過三層conv3x3的卷積得到2個神經元。三個網絡分別對應stride=1,pad=0的conv3x3、conv5x5和conv7x7的卷積核在3層、1層、1層時的結果。因為這三個網絡的輸入都是8,也可看出2個3x3的卷積堆疊獲得的感受野大小,相當1層5x5的卷積;而3層的3x3卷積堆疊獲取到的感受野相當於一個7x7的卷積。
- input=8,3層conv3x3后,output=2,等同於1層conv7x7的結果;
- input=8,2層conv3x3后,output=2,等同於2層conv5x5的結果。
或者我們也可以說,三層的conv3x3的網絡,最后兩個輸出中的一個神經元,可以看到的感受野相當於上一層是3,上上一層是5,上上上一層(也就是輸入)是7。
此外,倒着看網絡,也就是backprop的過程,每個神經元相對於前一層甚至輸入層的感受野大小也就意味着參數更新會影響到的神經元數目。在分割問題中卷積核的大小對結果有一定的影響,在上圖三層的conv3x3中,最后一個神經元的計算是基於第一層輸入的7個神經元,換句話說,反向傳播時,該層會影響到第一層conv3x3的前7個參數。從輸出層往回forward同樣的層數下,大卷積影響(做參數更新時)到的前面的輸入神經元越多。
優點
既然說到了VGG清一色用小卷積核,結合作者和自己的觀點,這里整理出小卷積核比用大卷積核的三點優勢:
-
更多的激活函數、更豐富的特征,更強的辨別能力。卷積后都伴有激活函數,更多的卷積核的使用可使決策函數更加具有辨別能力,此外就卷積本身的作用而言,3x3比7x7就足以捕獲特征的變化:3x3的9個格子,最中間的格子是一個感受野中心,可以捕獲上下左右以及斜對角的特征變化。主要在於3個堆疊起來后,三個3x3近似一個7x7,網絡深了兩層且多出了兩個非線性ReLU函數,(特征多樣性和參數參數量的增大)使得網絡容量更大(關於model capacity,AlexNet的作者認為可以用模型的深度和寬度來控制capacity),對於不同類別的區分能力更強(此外,從模型壓縮角度也是要摒棄7x7,用更少的參數獲得更深更寬的網絡,也一定程度代表着模型容量,后人也認為更深更寬比矮胖的網絡好);
-
卷積層的參數減少。相比5x5、7x7和11x11的大卷積核,3x3明顯地減少了參數量,這點可以回過頭去看上面的表格。比方input channel數和output channel數均為C,那么3層conv3x3卷積所需要的卷積層參數是:3x(Cx3x3xC)=27C^2,而一層conv7x7卷積所需要的卷積層參數是:Cx7x7xC=49C^2。conv7x7的卷積核參數比conv3x3多了(49-27)/27x100% ≈ 81%;
- 小卷積核代替大卷積核的正則作用帶來性能提升。作者用三個conv3x3代替一個conv7x7,認為可以進一步分解(decomposition)原本用7x7大卷積核提到的特征,這里的分解是相對於同樣大小的感受野來說的。關於正則的理解我覺得還需要進一步分析。
其實最重要的還是多個小卷積堆疊在分類精度上比單個大卷積要好。
小池化核
這里的“小”是相對於AlexNet的3x3的池化核來說的。不過在說池化前,先說一下CS231n的博客里的描述網絡結構的layer pattern,一般常見的網絡都可以表示為:INPUT -> [[CONV -> RELU]*N -> POOL?]*M -> [FC -> RELU]*K -> FC的形式,其中,?表示pool是一個可選項。這樣的pattern因為可以對小卷積核堆疊,很自然也更適合描述深層網絡的構建,例如INPUT -> FC表示一個線性分類器。
不過從layer pattern中的[[CONV -> RELU]*N -> POOL?]*M部分,可以看出卷積層一般后面接完激活函數就緊跟池化層。對於這點我的理解是,池化做的事情是根據對應的max或者average方式進行特征篩選,還是在做特征工程上的事情。
2012年的AlexNet,其pooling的kernel size全是奇數,里面所有池化采用kernel size為3x3,stride為2的max-pooling。而VGGNet所使用的max-pooling的kernel size均為2x2,stride為2的max-pooling。pooling kernel size從奇數變為偶數。小kernel帶來的是更細節的信息捕獲,且是max-pooling更見微的同時進一步知躇。
在當時也有average pooling,但是在圖像任務上max-pooling的效果更勝一籌,所以圖像大多使用max-pooling。在這里我認為max-pooling更容易捕捉圖像上的變化,梯度的變化,帶來更大的局部信息差異性,更好地描述邊緣、紋理等構成語義的細節信息,這點尤其體現在網絡可視化上。
概述全連接和特征圖
全連接
VGG最后三個全連接層在形式上完全平移AlexNet的最后三層,VGGNet后面三層(三個全連接層)為:
- FC4096-ReLU6-Drop0.5,FC為高斯分布初始化(std=0.005),bias常數初始化(0.1)
- FC4096-ReLU7-Drop0.5,FC為高斯分布初始化(std=0.005),bias常數初始化(0.1)
- FC1000(最后接SoftMax1000分類),FC為高斯分布初始化(std=0.005),bias常數初始化(0.1)
超參數上只有最后一層fc有變化:bias的初始值,由AlexNet的0變為0.1,該層初始化高斯分布的標准差,由AlexNet的0.01變為0.005。超參數的變化,我的理解是,作者自己的感性理解指導認為,我以貢獻bias來降低標准差,相當於標准差和bias間trade-off,或許作者實驗validate發現這個值比之前AlexNet設置的(std=0.01,bias=0)要更好。
特征圖
網絡在隨層數遞增的過程中,通過池化也逐漸忽略局部信息,特征圖的寬度高度隨着每個池化操作縮小50%,5個池化l操作使得寬或者高度變化過程為:224->112->56->28->14->7,但是深度depth(或說是channel數),隨着5組卷積在每次增大一倍:3->64->128->256->512->512。特征信息從一開始輸入的224x224x3被變換到7x7x512,從原本較為local的信息逐漸分攤到不同channel上,隨着每次的conv和pool操作打散到channel層級上。
特征圖的寬高從512后開始進入全連接層,因為全連接層相比卷積層更考慮全局信息,將原本有局部信息的特征圖(既有width,height還有channel)全部映射到4096維度。也就是說全連接層前是7x7x512維度的特征圖,估算大概是25000,這個全連接過程要將25000映射到4096,大概是5000,換句話說全連接要將信息壓縮到原來的五分之一。VGGNet有三個全連接,我的理解是作者認為這個映射過程的學習要慢點來,太快不易於捕捉特征映射來去之間的細微變化,讓backprop學的更慢更細一些(更逐漸)。
換句話說,維度在最后一個卷積后達到7x7x512,即大概25000,緊接着壓縮到4096維,可能是作者認為這個過程太急,又接一個fc4096作為緩沖,同時兩個fc4096后的relu又接dropout0.5去過渡這個過程,因為最后即將給1k-way softmax,所以又接了一個fc1000去降低softmax的學習壓力。
feature map維度的整體變化過程是:先將local信息壓縮,並分攤到channel層級,然后無視channel和local,通過fc這個變換再進一步壓縮為稠密的feature map,這樣對於分類器而言有好處也有壞處,好處是將local信息隱藏於/壓縮到feature map中,壞處是信息壓縮都是有損失的,相當於local信息被破壞了(分類器沒有考慮到,其實對於圖像任務而言,單張feature map上的local信息還是有用的)。
但其實不難發現,卷積只增加feature map的通道數,而池化只減少feature map的寬高。如今也有不少做法用大stride卷積去替代池化,未來可能沒有池化。
卷積組
說到特征圖的變化,我們可以進一步切分網絡觀察整體結構,再次拿出CS231n的博客里的描述網絡結構的layer pattern:INPUT -> [[CONV -> RELU]*N -> POOL?]*M -> [FC -> RELU]*K -> FC,以pooling操作為切分點對整個網絡分組的話,我們會得到五組卷積,五組卷積中有2種卷積組的形式,切分后的VGG網絡可以描述成下面這樣:
- 前兩組卷積形式一樣,每組都是:conv-relu-conv-relu-pool;
- 中間三組卷積形式一樣,每組都是:conv-relu-conv-relu-conv-relu-pool;
- 最后三個組全連接fc層,前兩組fc,每組都是:fc-relu-dropout;最后一個fc僅有fc。
雖然CS231n里將這種形式稱為layer pattern,但我更喜歡把以卷積起始池化為止的最短結構稱之為“卷積組”。
不難發現VGG有兩種卷積組,第二種([conv-relu]-[conv-relu]-[conv-relu]-pool)比第一種([conv-relu]-[conv-relu]-pool) 多了一個[conv-relu]。我的理解是:
-
多出的relu對網絡中層進一步壓榨提煉特征。結合一開始單張feature map的local信息更多一些,還沒來得及把信息分攤到channel級別上,那么往后就慢慢以增大conv filter的形式遞增地擴大channel數,等到了網絡的中層,channel數升得差不多了(信息分攤到channel上得差不多了),那么還想抽local的信息,就通過再加一個[conv-relu]的形式去壓榨提煉特征。有點類似傳統特征工程中,已有的特征在固定的模型下沒有性能提升了,那就用更多的非線性變換對已有的特征去做變換,產生更多的特征的意味;
- 多出的conv對網絡中層進一步進行學習指導和控制不要將特征信息漂移到channel級別上。
-
上一點更多的是relu的帶來的理解,那么多出的[conv-relu]中conv的意味就是模型更強的對數據分布學習過程的約束力/控制力,做到信息backprop可以回傳回來的學習指導。本身多了relu特征變換就加劇(權力釋放),那么再用一個conv去控制(權力回收),也在指導網絡中層的收斂;
- 其實conv本身關注單張feature map上的局部信息,也是在嘗試去盡量平衡已經失衡的channel級別(depth)和local級別(width、height)之間的天平。這個conv控制着特征的信息量不要過於向着channel級別偏移。
關於Layer pattern,CS231n的博客給出如下觀點:
-
串聯和串聯中帶有並聯的網絡架構。近年來,GoogLeNet在其網絡結構中引入了Inception模塊,ResNet中引入了Residual Block,這些模塊都有自己復雜的操作。換句話說,傳統一味地去串聯網絡可能並不如這樣串聯為主線,帶有一些並聯同類操作但不同參數的模塊可能在特征提取上更好。 所以我認為這里本質上依舊是在做特征工程,只不過把這個過程放在block或者module的小的網絡結構里,畢竟kernel、stride、output的大小等等超參數都要自己設置,目的還是產生更多豐富多樣的特征。
-
用在ImageNet上pre-trained過的模型。設計自己模型架構很浪費時間,尤其是不同的模型架構需要跑數據來驗證性能,所以不妨使用別人在ImageNet上訓練好的模型,然后在自己的數據和問題上在進行參數微調,收斂快精度更好。 我認為只要性能好精度高,選擇什么樣的模型架構都可以,但是有時候要結合應用場景,對實時性能速度有要求的,可能需要多小網絡,或者分級小網絡,或者級聯的模型,或者做大網絡的知識蒸餾得到小網絡,甚至對速度高精度不要求很高的,可以用傳統方法。
層維度
要說到layer pattern,不得不提到sizing pattern,其實這里相當於前面feature map維度變化的補充,這也是CS231n里所講到的。對於每種層,有不同的默認設定:
輸入層
大都是2的N次方,這和網絡中卷積或者池化層出現的stride為2的次數有關,比方VGGNet中每個pattern的卷積不會對feature map的寬度和高度有改變,而每個pattern結束前總會做一個stride為2的下采樣,因為有5組,那么做5次就是32,所以VGGNet網絡input大小一般都是32的倍數,即,n是下采樣的次數,a是最終卷積和池化得到的feature map大小,如224或者384。
卷積層
現在常用的是小卷積核如3x3或者1x1。卷積為了保留feature map不變,通常會采取pad為1的操作,其實具體來說應該是:為了保證卷積后的feature map的寬度和高度不變,那么有pad=(F-1)/2,但我覺得這個有點問題,可以改成更一般的形式,不過首先可以看看計算下一層feature map寬高的公式:
因為要保證和一樣,有,那么可以導出:
當Stride=1時,那么pad=(F-1)/2。因為現在stride=1的3x3卷積用的多,所以大家會默認說是pad=1(關於這點上,也是由於實驗發現這樣保留feature map的寬高情況下,性能好的緣故,我認為填補主要是針對stride大於1的情況帶來的邊界問題,如果input尺寸不是事先設定的,那么就會有邊界無法卷積到的問題帶來信息丟失。不過這種填補我認為也有一定問題,就是說原本conv3x3去計算對應位置的3x3,而填補后為0,這樣相當於少算了一些值,這肯定還是有影響的)。但若stride不是1,那么要保證前后feature map的寬高一樣,就要根據上面的公式計算得出。
另一個點是通常與Input比較接近的conv會采用大卷積核。關於接近input層使用較大的卷積核這點,我認為先是考慮到后面的操作,先盡可能用大的卷積核cover更多的原始信息(雖然經過了卷積有一些變換),第二點在於大卷積核帶來的大感受野,后面的卷積層能的一個神經元能看到更大的input,第三點是GPU的顯存受限,經典的例子就是AlexNet使用stride=4的conv11x11,目的就是從一開始就減少顯存占用,其實這里的大stride,我覺得起到了一些正則的作用。但缺點也很明顯,因為卷積核變大,矩陣乘法實現卷積時,若沒有大stride,那么第一個矩陣的列數,也就是第二個矩陣的行數,會變大,帶來大的計算量。所以在AlexNet中,大卷積核也對應使用了大的stride值。
池化層
常見2x2的max-pooling,少見3x3或者更大的kernel。更大的kernel帶來的問題是信息丟失帶來的信息損失,此外,stride通常為2;
其實按照以上的設定看來,也是有好處的。卷積專注於保留空間信息前提下的channel變換,而池化則專注於空間信息的變換(下采樣)。
全連接轉卷積
VGG比較神奇的一個特點就是“全連接轉卷積”。
上圖是VGG網絡最后三層的替換過程,上半部分是訓練階段,此時最后三層都是全連接層(輸出分別是4096、4096、1000),下半部分是測試階段(輸出分別是1x1x4096、1x1x4096、1x1x1000),最后三層都是卷積層。下面我們來看一下詳細的轉換過程(以下過程都沒有考慮bias,略了):
先看訓練階段,有4096個輸出的全連接層FC6的輸入是一個7x7x512的feature map,因為全連接層的緣故,不需要考慮局部性, 可以把7x7x512看成一個整體,25508(=7x7x512)個輸入的每個元素都會與輸出的每個元素(或者說是神經元)產生連接,所以每個輸入都會有4096個系數對應4096個輸出,所以網絡的參數(也就是兩層之間連線的個數,也就是每個輸入元素的系數個數)規模就是7x7x512x4096。對於FC7,輸入是4096個,輸出是4096個,因為每個輸入都會和輸出相連,即每個輸出都有4096條連線(系數),那么4096個輸入總共有4096x4096條連線(系數),最后一個FC8計算方式一樣,略。
再看測試階段,由於換成了卷積,第一個卷積后要得到4096(或者說是1x1x4096)的輸出,那么就要對輸入的7x7x512的feature map的寬高(即width、height維度)進行降維,同時對深度(即Channel/depth維度)進行升維。要把7x7降維到1x1,那么干脆直接一點,就用7x7的卷積核就行,另外深度層級的升維,因為7x7的卷積把寬高降到1x1,那么剛好就升高到4096就好了,最后得到了1x1x4096的feature map。這其中卷積的參數量上,把7x7x512看做一組卷積參數,因為該層的輸出是4096,那么相當於要有4096組這樣7x7x512的卷積參數,那么總共的卷積參數量就是:
[7x7x512]x4096,這里將7x7x512用中括號括起來,目的是把這看成是一組,就不會懵。
第二個卷積依舊得到1x1x4096的輸出,因為輸入也是1x1x4096,三個維度(寬、高、深)都沒變化,可以很快計算出這層的卷積的卷積核大小也是1x1,而且,通道數也是4096,因為對於輸入來說,1x1x4096是一組卷積參數,即一個完整的filter,那么考慮所有4096個輸出的情況下,卷積參數的規模就是[1x1x4096]x4096。第三個卷積的計算一樣,略。
其實VGG的作者把訓練階段的全連接替換為卷積是參考了OverFeat的工作,如下圖是OverFeat將全連接換成卷積后,帶來可以處理任意分辨率(在整張圖)上計算卷積,而無需對原圖resize的優勢。
不過可以看到,訓練階段用的是crop或者resize到14x14的輸入圖像,而測試階段可以接收任意維度,如果使用未經crop的原圖作為輸入(假設原圖比crop或者resize到訓練尺度的圖像要大),這會帶來一個問題:feature map變大了。比方VGG訓練階段用224x224x3的圖作為模型輸入,經過5組卷積和池化,最后到7x7x512維度,最后經過無論是三個卷積或者三個全連接,維度都會到1x1x4096->1x1x4096->1x1x1000,而使用384x384x3的圖做模型輸入,到五組卷積和池化做完(即),那么feature map變為12x12x512,經過三個由全連接變的三個卷積,即feature map經歷了6x6x4096->6x6x4096->6x6x1000的變化過程后,再把這個6x6x1000的feature map進行average,最終交給SoftMax的是1x1x1000的feature map進行分類。
以上便是將全連接轉換成卷積以及將轉換后的全卷積網絡應用到測試階段的方式。其實進一步來看卷積與全連接,二者最明顯的差異不外乎一個前者是局部連接,但其實二者都有用到全局信息,只是卷積是通過層層堆疊來利用的,而全連接就不用說了,全連接的方式直接將上一層的特征圖全部用上,稀疏性比較大,而卷積從網絡深度這一角度,基於輸入到當前層這一過程逐級逐層榨取的方式利用全局信息。
1×1卷積
VGG在最后的三個階段都用到了1x1卷積核,選用1x1卷積核的最直接原因是在維度上繼承全連接,然而作者首先認為1x1卷積可以增加決策函數(decision function,這里的決策函數我認為就是softmax)的非線性能力,非線性是由激活函數ReLU決定的,本身1x1卷積則是線性映射,即將輸入的feature map映射到同樣維度的feature map。作者還提到“Network in Network” architecture of Lin et al. (2014).這篇文章就大量使用了1x1卷積核。
此外,查了知乎簡單總結下1x1卷積的特點(就不說加入非線性這個conv自帶的特點了):
- 專注於跨通道的特征組合:conv1x1根本不考慮單通道上像素的局部信息(不考慮局部信息),專注於那一個卷積核內部通道的信息整合。conv3x3既考慮跨通道,也考慮局部信息整合;
-
對feature map的channel級別降維或升維:例如224x224x100的圖像(或feature map)經過20個conv1x1的卷積核,得到224x224x20的feature map。尤其當卷積核(即filter)數量達到上百個時,3x3或5x5卷積的計算會非常耗時,所以1x1卷積在3x3或5x5卷積計算前先降低feature map的維度。
關於小卷積核前人就有使用,如Ciresan et al. (2011)還有Goodfellow et al. (2014),后者使用11層的網絡解決街道數量的識別問題(street number classification,我也沒看懂是回歸還是分類),結果顯示更深的網絡可以帶來更好地網絡性能。而作者在小卷積核的基礎上使用了更多層數,2014年ImageNet分類比賽的第一名使用GoogLeNet,Szegedy et al., (2014)也使用了更小的卷積核、更深達到22層的網絡,使用了5x5、3x3和1x1卷積(實際還用到了7x7的卷積,第一層卷積)。但GoogLeNet的拓撲結構比較復雜,上圖是Inception module的結構圖,可以看到module內直接使用了常見的三種卷積核進行並聯,並將最后的結果feature map直接concate到一起,而VGGNet則是使用傳統卷積核串聯的方式。
參考資料:
- 原文鏈接:這里