卷積神經網路入門詳解
本文主要內容為 CS231n 課程的學習筆記,主要參考 學習視訊 和對應的 課程筆記翻譯 ,感謝各位前輩對於深度學習的辛苦付出。在這裡我主要記錄下自己覺得重要的內容以及一些相關的想法,希望能與大家多多交流~
0. 回顧之前所寫過的部落格
在之前的部落格《十四、卷積神經網路(1):介紹卷積神經網路》《十五、卷積神經網路(2):卷積神經網路的結構》中只是介紹性的給出了一些關於卷積神經網路的知識。這主要因為《Neural Networks and Deep Learning》這本書中的大部分章節在介紹神經網路,而僅在最後一個部分介紹了卷積神經網路,只介紹了卷積神經網路中的相關概念,如“感受野”、“卷積層”、“池化層”、“權值共享”等等,而且也只介紹了一種較為簡單的卷積神經網路結構。而且需要指出的是,在這上述兩節內容中的影象也是單通道的影象,並沒有考慮多通道影象。
作為補充和完善,在本文中將對卷積神經網路中的多個問題具體展開講解。
1. 卷積神經網路結構概述
如果用全連線神經網路處理大尺寸影象具有三個明顯的缺點:
(1)首先將影象展開為向量會丟失空間資訊;
(2)其次引數過多效率低下,訓練困難;
(3)同時大量的引數也很快會導致網路過擬合。
而使用卷積神經網路可以很好地解決上面的三個問題。
與全連線神經網路不同,卷積神經網路的各層中的神經元是3維排列的:寬度、高度和深度。其中的寬度和高度是很好理解的,因為本身卷積就是一個二維模板,但是在卷積神經網路中的深度指的是啟用資料體的第三個維度,而不是整個網路的深度,整個網路的深度指的是網路的層數。舉個例子來理解什麼是寬度,高度和深度,假如使用CIFAR-10中的影象是作為卷積神經網路的輸入,該輸入資料體
圖1中左側是一個3層的神經網路;右側是一個卷積神經網路,將它的神經元在成3個維度(寬、高和深度)進行排列。卷積神經網路的每一層都將3D的輸入資料變化為神經元3D的啟用資料並輸出。在圖1的右側,紅色的輸入層代表輸入影象,所以它的寬度和高度就是影象的寬度和高度,它的深度是3(代表了紅、綠、藍3種顏色通道),與紅色相鄰的藍色部分是經過卷積和池化之後的啟用值(也可以看做是神經元) ,後面是接著的卷積池化層。
2. 構建卷積神經網路的各種層
卷積神經網路主要由這幾類層構成:輸入層、卷積層,ReLU層、池化(Pooling)層和全連線層(全連線層和常規神經網路中的一樣)。通過將這些層疊加起來,就可以構建一個完整的卷積神經網路。在實際應用中往往將卷積層與ReLU層共同稱之為卷積層,所以卷積層經過卷積操作也是要經過啟用函式的。具體說來,卷積層和全連線層(CONV/FC)對輸入執行變換操作的時候,不僅會用到啟用函式,還會用到很多引數,即神經元的權值w和偏差b;而ReLU層和池化層則是進行一個固定不變的函式操作。卷積層和全連線層中的引數會隨著梯度下降被訓練,這樣卷積神經網路計算出的分類評分就能和訓練集中的每個影象的標籤吻合了。
2.1 卷積層
卷積層是構建卷積神經網路的核心層,它產生了網路中大部分的計算量。注意是計算量而不是引數量。
2.1.1 卷積層作用
1. 濾波器的作用或者說是卷積的作用。卷積層的引數是有一些可學習的濾波器集合構成的。每個濾波器在空間上(寬度和高度)都比較小,但是深度和輸入資料一致(這一點很重要,後面會具體介紹)。直觀地來說,網路會讓濾波器學習到當它看到某些型別的視覺特徵時就啟用,具體的視覺特徵可能是某些方位上的邊界,或者在第一層上某些顏色的斑點,甚至可以是網路更高層上的蜂巢狀或者車輪狀圖案。
2. 可以被看做是神經元的一個輸出。神經元只觀察輸入資料中的一小部分,並且和空間上左右兩邊的所有神經元共享引數(因為這些數字都是使用同一個濾波器得到的結果)。
3. 降低引數的數量。這個由於卷積具有“權值共享”這樣的特性,可以降低引數數量,達到降低計算開銷,防止由於引數過多而造成過擬合。
2.1.2 感受野(重點理解)
在處理影象這樣的高維度輸入時,讓每個神經元都與前一層中的所有神經元進行全連線是不現實的。相反,我們讓每個神經元只與輸入資料的一個區域性區域連線。該連線的空間大小叫做神經元的感受野(receptive field),它的尺寸是一個超引數(其實就是濾波器的空間尺寸)。在深度方向上,這個連線的大小總是和輸入量的深度相等。需要再次強調的是,我們對待空間維度(寬和高)與深度維度是不同的:連線在空間(寬高)上是區域性的,但是在深度上總是和輸入資料的深度一致,這一點會在下面舉例具體說明。
在圖 2 中展現的卷積神經網路的一部分,其中的紅色為輸入資料,假設輸入資料體尺寸為[32x32x3](比如CIFAR-10的RGB影象),如果感受野(或濾波器尺寸)是5x5,那麼卷積層中的每個神經元會有輸入資料體中[5x5x3]區域的權重,共5x5x3=75個權重(還要加一個偏差引數)。注意這個連線在深度維度上的大小必須為3,和輸入資料體的深度一致。其中還有一點需要注意,對應一個感受野有75個權重,這75個權重是通過學習進行更新的,所以很大程度上這些權值之間是不相等(也就對於同一個卷積核,它對於與它連線的輸入的每一層的權重都是獨特的,不是同樣的權重重複輸入層層數那麼多次就可以的)。在這裡相當於前面的每一個層對應一個傳統意義上的卷積模板,每一層與自己卷積模板做完卷積之後,再將各個層的結果加起來,再加上偏置,注意是一個偏置,無論輸入輸入資料是多少層,一個卷積核就對應一個偏置。
2.1.3 神經元的空間排列
感受野講解了卷積層中每個神經元與輸入資料體之間的連線方式,但是尚未討論輸出資料體中神經元的數量,以及它們的排列方式。3個超引數控制著輸出資料體的尺寸:深度(depth),步長(stride)和零填充(zero-padding)。
(1) 輸出資料體的深度:它是一個超引數,和使用的濾波器的數量一致,而每個濾波器在輸入資料中尋找一些不同的東西,即影象的某些特徵。如圖2 所示,將沿著深度方向排列、感受野相同的神經元集合稱為深度列(depth column),也有人使用纖維(fibre)來稱呼它們。
(2) 在滑動濾波器的時候,必須指定步長。當步長為1,濾波器每次移動1個畫素;當步長為2,濾波器滑動時每次移動2個畫素,當然步長也可以是不常用的3,或者更大的數字,但這些在實際中很少使用 ,這個操作會讓輸出資料體在空間上變小。
(3) 有時候將輸入資料體用0在邊緣處進行填充是很方便的。這個零填充(zero-padding)的尺寸是一個超引數。零填充有一個良好性質,即可以控制輸出資料體的空間尺寸(最常用的是用來保持輸入資料體在空間上的尺寸,使得輸入和輸出的寬高都相等)。
輸出資料體在空間上的尺寸 可以通過輸入資料體尺寸,卷積層中神經元的感受野尺寸(F),步長(S),濾波器數量(K)和零填充的數量(P)計算輸出出來。
一般說來,當步長S=1時,零填充的值是P=(F-1)/2,這樣就能保證輸入和輸出資料體有相同的空間尺寸。
步長的限制:注意這些空間排列的超引數之間是相互限制的。舉例說來,當輸入尺寸W=10,不使用零填充 P=0,濾波器尺寸 F=3,此時步長 S=2 是行不通,因為 (W-F+2P)/S+1=(10-3+0)/2+1=4.5,結果不是整數,這就是說神經元不能整齊對稱地滑過輸入資料體。因此,這些超引數的設定就被認為是無效的,一個卷積神經網路庫可能會報出一個錯誤,通過修改零填充值、修改輸入資料體尺寸,或者其他什麼措施來讓設定合理。在後面的卷積神經網路結構小節中,讀者可以看到合理地設定網路的尺寸讓所有的維度都能正常工作,是相當讓人頭痛的事;而使用零填充和遵守其他一些設計策略將會有效解決這個問題。
2.1.4 權值共享
在卷積層中權值共享是用來控制引數的數量。假如在一個卷積核中,每一個感受野採用的都是不同的權重值(卷積核的值不同),那麼這樣的網路中引數數量將是十分巨大的。
權值共享是基於這樣的一個合理的假設:如果一個特徵在計算某個空間位置 的時候有用,那麼它在計算另一個不同位置 的時候也有用。基於這個假設,可以顯著地減少引數數量。換言之,就是將深度維度上一個單獨的2維切片看做深度切片(depth slice),比如一個數據體尺寸為[55x55x96]的就有96個深度切片,每個尺寸為[55x55],其中在每個深度切片上的結果都使用同樣的權重和偏差獲得的。在這樣的引數共享下,假如一個例子中的第一個卷積層有96個卷積核,那麼就有96個不同的權重集了,一個權重集對應一個深度切片,如果卷積核的大小是 11x11的,影象是RGB 3 通道的,那麼就共有96x11x11x3=34,848個不同的權重,總共有34,944個引數(因為要+96個偏差),並且在每個深度切片中的55x55 的結果使用的都是同樣的引數。
在反向傳播的時候,都要計算每個神經元對它的權重的梯度,但是需要把同一個深度切片上的所有神經元對權重的梯度累加,這樣就得到了對共享權重的梯度。這樣,每個切片只更新一個權重集。這樣做的原因可以通過下面這張圖進行解釋
如上圖所示,左側的神經元是將每一個感受野展開為一列之後串聯起來(就是展開排成一列,同一層神經元之間不連線)。右側的 Deep1i 是深度為1的神經元的第 i 個, Deep2i 是深度為2的神經元的第 i 個,同一個深度的神經元的權值都是相同的,黃色的都是相同的(上面4個與下面4個的引數相同),藍色都是相同的。所以現在回過頭來看上面說的卷積神經網路的反向傳播公式對梯度進行累加求和也是基於這點考慮(同一深度的不同神經元共用一組引數,所以累加);而每個切片只更新一個權重集的原因也是這樣的,因為從圖3 中可以看到,不同深度的神經元不會公用相同的權重,所以只能更新一個權重集。
注意,如果在一個深度切片中的所有權重都使用同一個權重向量,那麼卷積層的前向傳播在每個深度切片中可以看做是在計算神經元權重和輸入資料體的卷積(這就是“卷積層”名字由來)。這也是為什麼總是將這些權重集合稱為濾波器(filter)(或卷積核(kernel)),因為它們和輸入進行了卷積。
注意,有時候引數共享假設可能沒有意義,特別是當卷積神經網路的輸入影象是一些明確的中心結構時候。這時候我們就應該期望在圖片的不同位置學習到完全不同的特徵(而一個卷積核滑動地與影象做卷積都是在學習相同的特徵)。一個具體的例子就是輸入影象是人臉,人臉一般都處於圖片中心,而我們期望在不同的位置學習到不同的特徵,比如眼睛特徵或者頭髮特徵可能(也應該)會在圖片的不同位置被學習。在這個例子中,通常就放鬆引數共享的限制,將層稱為區域性連線層(Locally-Connected Layer)。
2.1.5 卷積層的超引數及選擇
由於引數共享,每個濾波器包含 個權重(字元的具體含義在2.1.3中有介紹),卷積層一共有 $F\cdot F\cdot D_1\cdot K $個權重和 個偏置。在輸出資料體中,第d個深度切片(空間尺寸是),用第d個濾波器和輸入資料進行有效卷積運算的結果(使用步長S),最後在加上第d個偏差。
對這些超引數,常見的設定是 。同時設定這些超引數也有一些約定俗成的慣例和經驗,可以在下面的“卷積神經網路結構”中檢視。
2.1.6 卷積層演示
因為3D資料難以視覺化,所以所有的資料(輸入資料體是藍色,權重資料體是紅色,輸出資料體是綠色)都採取將深度切片按照列的方式排列展現。輸入資料體的尺寸是,卷積層引數。就是說,有2個濾波器,濾波器的尺寸是,它們的步長是2。因此,輸出資料體的空間尺寸是。注意輸入資料體使用了零填充,所以輸入資料體外邊緣一圈都是0。下面的例子在綠色的輸出啟用資料上迴圈演示,展示了其中每個元素都是先通過藍色的輸入資料和紅色的濾波器逐元素相乘,然後求其總和,最後加上偏差得來。
無法正常顯示,請參考http://cs231n.github.io/convolutional-networks/ 動圖
圖 4. 卷積層演示過程
2.1.7 用矩陣乘法實現卷積
卷積運算本質上就是在濾波器和輸入資料的區域性區域間做點積。卷積層的常用實現方式就是利用這一點,將卷積層的前向傳播變成一個巨大的矩陣乘法。
(1) 輸入影象的區域性區域被 l操作拉伸為列。比如輸入是[227x227x3],要與尺寸為11x11x3的濾波器以步長為4進行卷積,就依次取輸入中的[11x11x3]資料塊,然後將其拉伸為長度為11x11x3=363的列向量。重複進行這一過程,因為步長為4,所以經過卷積後的寬和高均為(227-11)/4+1=55,共有55x55=3,025個個神經元。因為每一個神經元實際上都是對應有 363 的列向量構成的感受野,即一共要從輸入上取出 3025 個 363 維的列向量。所以經過im2col操作得到的輸出矩陣 的尺寸是[363x3025],其中每列是拉伸的感受野。注意因為感受野之間有重疊,所以輸入資料體中的數字在不同的列中可能有重複。
(2) 卷積層的權重也同樣被拉伸成行。舉例,如果有96個尺寸為[11x11x3]的濾波器,就生成一個矩陣,尺寸為[96x363]。
(3) 現在卷積的結果和進行一個大矩陣乘法 是等價的了,能得到每個濾波器和每個感受野間的點積。在我們的例子中,這個操作的輸出是[96x3025],給出了每個濾波器在每個位置的點積輸出。注意其中的 計算的是矩陣乘法而不是點積。
(4) 結果最後必須被重新變為合理的輸出尺寸[55x55x96]。
這個方法的缺點就是佔用記憶體太多,因為在輸入資料體中的某些值在中被複制了多次;優點在於矩陣乘法有非常多的高效底層實現方式(比如常用的BLAS API)。還有,同樣的im2col思路可以用在池化操作中。反向傳播:卷積操作的反向傳播(同時對於資料和權重)還是一個卷積(但是和空間上翻轉的濾波器)。使用一個1維的例子比較容易演示。這兩部分中,不是很懂如何用矩陣的形式進行匯聚操作和反向傳播。
2.1.8 其他形式的卷積操作
1x1卷積:一些論文中使用了1x1的卷積,這個方法最早是在論文Network in Network中出現。人們剛開始看見這個1x1卷積的時候比較困惑,尤其是那些具有訊號處理專業背景的人。因為訊號是2維的,所以1x1卷積就沒有意義。但是,在卷積神經網路中不是這樣,因為這裡是對3個維度進行操作,濾波器和輸入資料體的深度是一樣的。比如,如果輸入是[32x32x3],那麼1x1卷積就是在高效地進行3維點積(因為輸入深度是3個通道);另外的一種想法是將這種卷積的結果看作是全連線層的一種實現方式,詳見本文2.4.2 部分。
擴張卷積:最近一個研究(Fisher Yu和Vladlen Koltun的論文)給卷積層引入了一個新的叫擴張(dilation)的超引數。到目前為止,我們只討論了卷積層濾波器是連續的情況。但是,讓濾波器中元素之間有間隙也是可以的,這就叫做擴張。如圖5 為進行1擴張。
在某些設定中,擴張卷積與正常卷積結合起來非常有用,因為在很少的層數內更快地彙集輸入圖片的大尺度特徵。比如,如果上下重疊2個3x3的卷積層,那麼第二個卷積層的神經元的感受野是輸入資料體中5x5的區域(可以成這些神經元的有效感受野是5x5,如圖5 所示)。如果我們對卷積進行擴張,那麼這個有效感受野就會迅速增長。
2.2 池化層
通常在連續的卷積層之間會週期性地插入一個池化層。它的作用是逐漸降低資料體的空間尺寸,這樣的話就能減少網路中引數的數量,使得計算資源耗費變少,也能有效控制過擬合。匯聚層使用 MAX 操作,對輸入資料體的每一個深度切片獨立進行操作,改變它的空間尺寸。最常見的形式是匯聚層使用尺寸2x2的濾波器,以步長為2來對每個深度切片進行降取樣,將其中75%的啟用資訊都丟掉。每個MAX操作是從4個數字中取最大值(也就是在深度切片中某個2x2的區域),深度保持不變。
匯聚層的一些公式:輸入資料體尺寸 ,有兩個超引數:空間大小和步長;輸出資料體的尺寸,其中
這裡面與之前的卷積的尺寸計算的區別主要在於兩點,首先在池化的過程中基本不會進行另補充;其次池化前後深度不變。
在實踐中,最大池化層通常只有兩種形式:一種是,也叫重疊匯聚(overlapping pooling),另一個更常用的是。對更大感受野進行池化需要的池化尺寸也更大,而且往往對網路有破壞性。
普通池化(General Pooling):除了最大池化,池化單元還可以使用其他的函式,比如平均池化(average pooling)或L-2正規化池化(L2-norm pooling)。平均池化歷史上比較常用,但是現在已經很少使用了。因為實踐證明,最大池化的效果比平均池化要好。
反向傳播:回顧一下反向傳播的內容,其中max(x,y)函式的反向傳播可以簡單理解為將梯度只沿最大的數回傳。因此,在向前傳播經過匯聚層的時候,通常會把池中最大元素的索引記錄下來(有時這個也叫作道岔(switches)),這樣在反向傳播的時候梯度的路由就很高效。
下面將簡單介紹平均池化和最大池化反向傳播的計算過程。首先是平均池化,在前向傳播的過程中,是計算視窗中的均值,很容易理解;在反向傳播的過程中,是將要反向傳播的梯度組分在平均分在四個部分傳回去,具體如下所示
forward: [1 3; 2 2] -> [2]
backward: [2] -> [0.5 0.5; 0.5 0.5]
對於最大池化,就如上面所說的,會把池中最大元素的索引記錄下來,在反向傳播的時候只對這樣的位置進行反向傳播,忽略其他的位置
forward: [1 3; 2 2] -> 3
backward: [3] -> [0 3; 0 0]
不使用匯聚層:很多人不喜歡匯聚操作,認為可以不使用它。比如在Striving for Simplicity: The All Convolutional Net一文中,提出使用一種只有重複的卷積層組成的結構,拋棄匯聚層。通過在卷積層中使用更大的步長來降低資料體的尺寸。有發現認為,在訓練一個良好的生成模型時,棄用匯聚層也是很重要的。比如變化自編碼器(VAEs:variational autoencoders)和生成性對抗網路(GANs:generative adversarial networks)。現在看起來,未來的卷積網路結構中,可能會很少使用甚至不使用匯聚層。
2.3 歸一化層
在卷積神經網路的結構中,提出了很多不同型別的歸一化層,有時候是為了實現在生物大腦中觀測到的抑制機制。但是這些層漸漸都不再流行,因為實踐證明它們的效果即使存在,也是極其有限的。
2.4 全連線層
這個常規神經網路中一樣,它們的啟用可以先用矩陣乘法,再加上偏差。
2.4.1 將卷積層轉化成全連線層
對於任一個卷積層,都存在一個能實現和它一樣的前向傳播函式的全連線層。該全連線層的權重是一個巨大的矩陣,除了某些特定塊(感受野),其餘部分都是零;而在非 0 部分中,大部分元素都是相等的(權值共享),具體可以參考圖3。如果把全連線層轉化成卷積層,以輸出層的 Deep11 為例,與它有關的輸入神經元只有上面四個,所以在權重矩陣中與它相乘的元素,除了它所對應的4個,剩下的均為0,這也就解釋了為什麼權重矩陣中有為零的部分;另外要把“將全連線層轉化成卷積層”和“用矩陣乘法實現卷積”區別開,這兩者是不同的,後者本身還是在計算卷積,只不過將其展開為矩陣相乘的形式,並不是"將全連線層轉化成卷積層",所以除非權重中本身有零,否則用矩陣乘法實現卷積的過程中不會出現值為0的權重。
2.4.2 將全連線層轉化成卷積層
任何全連線層都可以被轉化為卷積層。比如,一個K=4096的全連線層,輸入資料體的尺寸是 ,這個全連線層可以被等效地看做一個