卷积神经网络(Convolutional Neural Networks,CNNS/ConvNets)
本文翻譯自?Convolutional Neural Networks(CNNs / ConvNets),更多內容請訪問:http://cs231n.github.io/。
原來譯文:https://blog.csdn.net/Consu_Yasin/article/details/78052411?
???????卷積神經網絡非常類似于普通的神經網絡:它們都是由具有可以學習的權重和偏置的神經元組成。每一個神經元接收一些輸入,然后進行點積和可選的非線性運算。而整個網絡仍然表示一個可微的得分函數:從原始的圖像像素映射到類得分。在最后一層(全連接層)也有損失函數(例如 SVM / Softmax),而且訓練普通神經網絡的已有技巧 / 技術都可以應用。
???????然而差別在哪里呢?卷積網絡結構具有特殊的圖像輸入,這個顯式的輸入假設使得網絡結構具有一些特定的性質。這些性質使得前向函數能夠高效的實現,并且極大的減少網絡的參數個數。
結構概覽
???????回顧:普通神經網絡。神經網絡接收一個輸入(向量),然后通過一系列的隱藏層對輸入進行變換。每一個隱藏層都由神經元的集合組成,每一個神經元與前一層的所有神經元連接,而同一層的神經元之間完全獨立,也不共享任何連接。最后的全連接層稱為“輸出層”,在分類情形下它表示類得分。
???????普通神經網絡不能適用所有圖像。CIFAR-10 數據集中的圖像大小只有 32x32x3 (寬 32,高 32,3 個顏色通道),因此一個普通神經網絡的第一個隱藏層的一個全連接神經元的參數個數是 32*32* 3 = 3072。這樣的大小看起來好像很容易處理,但顯然全連接結構不能擴展到大圖像。例如,一張更可觀的圖像,它的大小為 200x200x3,那么一個神經元的就有 200*200*3 = 120,000 個參數。而且,我們幾乎總是需要很多這樣的神經元,因此參數合在一起會快速的增長。很明顯,這樣的全連接是浪費的,而且大量的參數會很快的導致過擬合。
???????神經元的 3D 方體(?,volume)。卷積神經網絡充分的利用了輸入是圖像這一事實,而且以一種更有意義的方式來限制它們的結構。特別的,不像普通神經網絡,卷積網絡中的層以 3 維(寬、高、深度,這里的深度不是指整個網絡的深度——網絡中所有層的個數,而是指激活方體的第三個維度)的方式來排列神經元。例如,CIFAR-10 中的圖像是維數為 32x32x3(寬、高、深度)的激活輸入方體。我們馬上就會看到,卷積網絡層中的神經元不是以全連接的方式連接到前一層的所有神經元,而是只與一個小局部相連。而且,依據卷積網絡結構,用于 CIFAR-10 的卷積網絡的最后的輸出層的維數是 1x1x10,就把整張圖像簡化成一個沿著深度方向的類得分向量。下圖是兩種網絡的一個可視化:
左圖:普通的 3 層神經網絡。右圖:卷積網絡,同一層的神經元使用三維(寬、高、深度)來可視化。卷積網絡的每一層把神經元的激活的 3D 的輸入方體變換為 3D 的輸出方體。在這個例子里,紅色的輸入層保存圖像,所以它的寬度和高度是圖像的維數,而深度是 3 (紅、綠、藍通道)。
卷積網絡由層組成。每一層都有一個 API:它把 3D 的輸入方體通過一個可微函數(參數可有可無)變成一個 3D 的輸出方體。
組成卷積網絡的層
???????正如我們在前面說的,一個簡單的卷積網絡是層的一個序列,每一個層通過一個可微函數把一個激活方體變成另一個。我們使用三種主要類型的層:卷積層(Convolutional Layer)、池化層(Pooling Layer)和全連接層(Fully-Connected Layer)來建立卷積網絡結構。我們將這三種類型的層堆疊起來形成整個卷積網絡的結構(architecture)。
???????結構例子:概覽。我們會在以后更詳細的描述卷積網絡,但一個用于 CIFAR-10 分類的簡單卷積網絡具有結構 [INPUT - CONV - RELU - POOL - FC]。更詳細的:
- INPUT [32x32x3] 保留圖像的原始像素值,這里的情形是寬 32,高 32,和具有 R,G,B 三個顏色通道的圖像。
- CONV 層計算與輸入局部連接的神經元的輸出,每一個神經元計算它們的權重與局部連接的輸入單元的權重的點積。如果我們使用 12 個過濾器,則得到 [32x32x12] 的單元。
- RELU 層逐元素的使用激活函數?max{0, x}。它保持單元的大小 [32x32x12] 不變。
- POOL 層沿著空間維度(寬、高)進行降采樣運算,得到如 [16x16x12] 的單元。
- FC(fully-connected)層計算類得分,得到的單元大小為 [1x1x10],其中 10 個數中的每一個對應到一個類得分(CIFAR-10 總共 10 個類別)。顧名思義,與普通神經網絡一樣,這個層的每一個神經元都與前一層的所有神經元連接。
???????使用這種方法,卷積網絡一層一層的把原始圖像的原始像素值變換成最后的類得分。注意到一些層包含參數,而另一些層沒有。特別的,CONV / FC 層進行的變換不僅是輸入方體的激活的函數,也是神經元的參數(權重和偏置)的函數。另一方面,RELU / POOL 層實現固定的函數。CONV / FC 層的參數通過梯度下降算法訓練,使得卷積網絡計算的類得分和訓練集中的每一張圖像的類標號一致。
???????綜合來說:
- 卷積網絡結構是把圖像方體變成輸出方體(如保存類得分)的變換的層列表的最簡單的情形
- 只有幾種不同類型的層而已(例如,當前 CONV / FC / RELU / POOL 最流行)
- 每一層都接收 3D 的輸入方體,然后通過一個可微函數把它變成 3D 輸出方體
- 每一層可能有參數,也可能沒有(例如,CONV / FC 層有,而 RELU / POOL 層沒有)
- 每一層可能有額外的超參數,也可能沒有(例如,CONV / FC / POOL 有,而 RELU 沒有)
一個樣例卷機網絡的激活。初始的方體保存原始圖像像素(左邊),而最后的方體保存類得分(右邊)。沿著處理路徑的每一個激活的方體由列來顯示。由于很難可視化 3D 方體,所以通過行來展示每一個方體的切片。最后一層的方體保存每一個類的得分,這里我們僅列出了排名前 5 的得分,及它們的標簽。訪問網站查看完整的?web-based demo。這里展示的是小型VGGNet(后面會討論)的結構。
???????現在我們來描述特定層的超參數和它們連接的細節。
卷積層
???????卷積層(convolutional layer,Conv layer)是卷積網絡的核心模塊,它對卷積網絡的計算性能提升最多。
???????直觀概覽。首先來看看沒有腦 / 神經元類比的卷積層的計算。它的參數由可學習的過濾器組成。每一個過濾器都是一個小的沿著寬和高方向的空間區塊,但也可以擴展到輸入方體的整個深度。例如,卷積網絡的第一層的一個典型的過濾器具有 5x5x3 的大小(也就是寬和高 5 個像素,而 3 是因為圖像有 3 個顏色通道)。在前向傳播階段,我們沿著輸入方體的寬和高滑動(更精確的,卷積)每一個過濾器,在每一個位置計算輸入和過濾器元素之間的點積。這將產生一個 2 維的激活映射(activation map),它是對過濾器在每一個空間位置的響應。直觀的,網絡學習的過濾器會在看到某些類型的可見特征(例如,網絡第一層的某些方向的邊緣,或者某些顏色的斑塊,或網絡上層的最終的蜂窩似或輪似的模式)的時候激活。在每一個 CONV 層我們有過濾器的一個集合,它們每一個都產生一個分離的 2 維激活映射。沿著深度維數方向把這些激活映射堆疊起來就產生了輸出方體。
???????腦觀點。如果從腦 / 神經元的類比來看,3D 輸出方體的每一個元素都可以解釋成神經元的輸出,這些神經元只依賴輸入的一個小區域,而且它們與左右空間的神經元共享參數(因為它們都使用相同的過濾器)。現在我們來闡述神經元連接的細節,它們的空間排列,以及它們的參數共享方式。
???????局部連接。當我們處理高維輸入(如圖像)的時候,把神經元與它前一層的所有神經元連接是不現實的。實際上,我們只把它和前一層的一個小區域相連。這個連接的空間范圍是稱為神經元的感受野(receptive field)的一個超參數,它等價于過濾器的大小。沿著深度軸的連接范圍總是等于輸入方體的深度。需要再次強調的是我們在處理空間維度(寬和高)和深度維度的時候是不對稱的:連接在沿著輸入方體的空間(寬和高)方向是局部的,而在深度方向則總是等于整個深度。
???????例1。假設輸入方體大小為 [32x32x3](例如一張 RGB CIFAR-10 圖像)。如果感受野(或過濾器大小)為 5x5,則卷積層的每一個神經元連接到輸入方體的 [5x5x3] 的區域,總共有 5*5*3 = 75 個權重(再加 1 個偏置參數)。注意,沿著深度方向的連接范圍必須是 3,因為它就是輸入方體的深度。
???????例2。假設輸入方體的大小是 [16x16x20]。則使用 3x3 大小的感受野,卷積層的每一個神經元與輸入方體有 3*3*20 = 180 個連接。再次注意,連接在空間上是局部的(如 3x3),但在輸入深度上是整體的。
左圖:一個樣例輸入方體(紅色,例如一張 32x32x3 的 CIFAR-10 圖像),和一個樣例第一個卷積層的神經元方體。卷積層的每一個神經元在空間上都連接到輸入方體的一個局部,但要連接整個深度(例如,所有的顏色通道)。注意,沿著深度有多個神經元(如圖片上有 5 個),每一個都連接到輸入的相同區域(見下文 depth column 的討論)。右圖:與普通神經網絡的神經元一樣:它們仍然要計算權重與輸入的點積,然后再進行非線性激活,只是它們的連接被限制到局部空間。
???????空間排列。我們已經解釋了卷積層的每一個神經元與輸入方體的連接,但我們還沒有討論輸出方體中神經元的個數以及它們的排列方式。有三個超參數控制著輸出方體的大小:深度(depth),步幅(stride)和?0-填充(zero-padding)。我們分別討論如下:
???????輸出方體的大小可以通過輸入方體的大小(W),卷積層神經元的感受野的大小(F),使用的過濾器的步幅(S),以及邊界 0-填充的量(P)的一個函數來計算。你可以確認一下,計算輸出神經元個數的正確公式是 (W?-?F?+ 2?P) /?S?+ 1。例如,對于 7x7 的輸入,步幅為 1 的 3x3 過濾器,以及 0 個填充,我們得到 5x5 的輸出。如果步幅為 2,則得到 3x3 的輸出。讓我們來看一個更圖形化的例子:
空間排列的說明。在這個例子中,空間維數只有一個(x-軸),一個神經元的感受野的大小為 F = 3,輸入大小為 W = 5,0-填充個數 P = 1。左圖:步幅 S = 1,從而輸出大小為 (5 - 3 + 2) / 1 + 1 = 5。右圖:步幅 S = 2,輸出大小為 (5 - 3 + 2) / 2 + 1 = 3。注意,步幅 S = 3 不能用,因為它不匹配方體的大小。用方程的術語來說,這可由 (5 - 3 + 2) = 4 不被 3 整除來確定。
這個例子中,神經元的權重是 [1, 0, -1](圖中最右側),偏置是 0。這些權重被所有黃色的神經元共享(見下文參數共享)。
???????使用 0-填充。注意到在上面例子的左圖,輸入和輸出的維數都是 5。這之所以成立,是因為感受野是 3 而我們使用了 1 的 0-填充。如果沒有使用 0-填充,則輸出的空間維數只有 3。一般的,如果步幅?S?= 1,則設置 0-填充?P?= (F?- 1) / 2 就能確保輸入方體和輸出方體具有相同的空間維數。使用這種方式的 0-填充非常常見,我們在進一步講卷積結構的時候會討論這樣做的充足理由。
???????步幅的限制。再次注意到空間排列超參數是兩兩相互制約的。例如,當輸入大小是?W?= 10 時,如果沒有 0-填充?P?= 0,并且過濾器的大小?F?= 3,則不能使用步幅?S?= 2,因為 (W?-?F?+ 2?P) /?S?+ 1 = (10 - 3 + 0) / 2 + 1 = 4.5,也就是說,不是一個整數,這意味著神經元不是整齊和對稱的與輸入做卷積。因此,超參數這樣設置是無效的,而且一個卷積庫(ConvNet library)會拋出一個例外,或者使用 0 的 0-填充,或者截斷輸入,或其它方式來匹配這些超參數。我們將在卷積網絡結構那一節看到,合適的設置卷積網絡的大小使得所有的維數都匹配確實令人頭痛,而這是 0-填充以及其它一些設計指導會幫我們顯著緩解的。
???????現實例子。Krizhevsky 等人的結構贏得了 2012 年的 ImageNet 比賽,它接受的圖像大小是 [227x227x3]。在第一個卷積層,神經元的感受野大小?F?= 11,步幅?S?= 4,0-填充?P?= 0。因為 (227 - 11) / 4 + 1 = 55,以及卷積層具有深度?K?= 96,所以卷積層的輸出方體大小為 [55x55x96]。這個有 55*55*96 個神經元的方體中的每一個神經元都與輸入方體的大小為 [11x11x3] 的區域相連。另外,每一個深度列(depth column)中的 96 個神經元都連接到輸入方體的相同 [11x11x3] 的區域,當然它們的權重不一樣。有趣的是,他們的論文中說輸入圖像大小是 224x224,這顯然是不正確的,因為 (224 - 11) / 4 + 1 并不是一個整數。這在卷積網絡的歷史中困擾了很多人,而且很少有人知道發生了什么。我們自己最好的猜測是 Alex 使用了 3 個額外像素的 0-填充,而他沒有在論文中指出。
???????參數共享。卷積層中使用參數共享方式來控制參數個數。使用上面現實的例子,我們已經知道在第一個卷積層中有 55*55*96 = 290,400 個神經元,每一個神經元都有 11*11*3 = 363 個權重和 1 個偏置。加起來,在第一個卷積層就有 290400*363 =105,705,600 個參數。顯然,這個數非常大。
???????一個被證明能夠極大的減少參數個數的合理的假設是:如果一個特征對某個空間位置 (x,y)的計算是有用的,則它對不同位置 (x2,y2)的計算也是有用的。換句話說,如果記一個深度的二維切片為?depth slice?(例如,一個大小為 [55x55x96] 的方體有 96 個 depth slice,每一個大小為 [55x55]),我們將限制每一個 depth slice,使得它的所有神經元都使用相同的權重和偏置。使用這種參數共享模式,我們的例子的第一個卷積層只有 96 個權重集(每一個 depth slice 對應一個),因此共有 96*11*11*3 = 34,848 個權重,或者 34,944 個參數(+96 個偏置)。也就是,每一個 depth slice 中的所有 55*55 個神經元都使用相同的參數。在實際的反向傳播過程中,方體中的每一個神經元都會對參數求梯度,但每個 depth slice 中的這些梯度都會加到一起,而且權重也只更新一次。
???????注意到,如果一個 depth slice 中的所有神經元都使用相同的權重向量,則卷積層在前向傳播過程時,每個 depth slice 計算神經元的權重與輸入方體的卷積(因此得名:卷積層)。這也是為什么通常把權重集稱為過濾器(filter)(或核,kernel),它與輸入進行卷積。
?
學到的過濾器的例子(Krizhevsky et al)。96 個過濾器中的每一個都具有大小 [11x11x3],而且都被每一個 depth slice 中的 55*55 個神經元共享。注意到參數共享這個假設是合理的:如果在圖像中某個位置檢測到一個水平邊緣是重要的,則由于圖像的平移不變結構,直觀的說,在其他位置也是有用的。從而,不需要在卷積層的輸出方體中的 55*55 個位置中的每一個都重新去學習檢測水平邊緣。
???????注意,有時參數共享假設可能沒有意義。特別是如果卷積網絡的輸入圖像具有特定的中心結構這種情況更是如此,在這里,我們期望,比如,學到圖像的一側相對于另一側完全不同的特征。一個實際例子是,當輸入是居于圖像中心的人臉時。你可能期望在不同的空間位置學習到不同的眼睛或頭發特征。這種情況通常會放松參數共享模式,取而代之的是局部連接層(Locally-Connected Layer)。
???????Numpy 例子。為了讓討論變得更精確,我們把相同的想法用代碼和特定的例子來表達。假設輸入方體是一個 numpy 數組?X。則:
- 在位置?(x, y)中?depth column(或?fibre)是激活 X[x, y, :]。
- 在深度?d?的?depth slice,或等價的,activation map?是激活 X[:, :, d]。
???????卷積層例子。假設輸入方體具有形狀?X.shape: (11, 11, 4)。再假設我們不使用 0-填充(P?= 0),過濾器大小?F?= 5,步幅?S?= 2。則輸出方體的的空間大小是 (11 - 5) / 2 + 1 = 4,即寬和高是 4。輸出方體的激活映射(稱它為?V)看起來如下(在這個例子中只有其中的一些元素被計算):
- V[0, 0, 0] = np.sum(X[:5, :5, :] * W0) + b0
- V[1, 0, 0] = np.sum(X[2:7, :5, :] * W0) + b0
- V[2, 0, 0] = np.sum(X[4:9, :5, :] * W0) + b0
- V[3, 0, 0] = np.sum(X[6:11, :5, :] * W0) + b0
???????記住在 numpy 中, 上面的運算符 * 指數組中的元素級乘法。同時要注意神經元的權重向量是?W0,而?b0?是偏置。這里,W0?假設具有形狀?W0.shape: (5, 5, 4),因為過濾器大小是 5 以及輸入方體的深度是 4。而且在每一個點,我們像通常的神經網絡一樣計算點積。另外,我們使用相同的權重和偏置(由于參數共享),同時沿著寬度方向的維數以 2 的步長(也就是步幅)增長。要構造輸出方體的第二個激活映射,我們有:
- V[0, 0, 1] = np.sum(X[:5, :5, :] * W1) + b1
- V[1, 0, 1] = np.sum(X[2:7, :5, :] * W1) + b1
- V[2, 0, 1] = np.sum(X[4:9, :5, :] * W1) + b1
- V[3, 0, 1] = np.sum(X[6:11, :5, :] * W1) + b1
- V[0, 1, 1] = np.sum(X[:5, 2:7, :] * W1) + b1(沿著 y 方向的例子)
- V[2, 3, 1] = np.sum(X[4:9, 6:11, :] * W1) + b1(沿著兩個方向的例子)
這里我們看到,V?的深度維數的下標是 1,因為我們計算的是第二個激活映射,而且現在使用的參數(W1)也不同。為了簡單起見,卷積層的輸出數組?V?的其它部分并沒有全部計算。另外,這些激活映射之后通常都會使用像 ReLU 這樣的激活函數,雖然這里并沒有演示。
???????總結。總而言之,卷積層:
- 接收一個大小為?W1×H1×D1W1×H1×D1?方體
- 需要 4 個超參數:?
- 濾波器個數?KK,
- 濾波器大小?FF,
- 步幅?SS,
- 0-填充數量?PP
- 產生一個大小為?W2×H2×D2W2×H2×D2?的方體:?
- W2=(W1?F+2P)/S+1W2=(W1?F+2P)/S+1
- H2=(H1?F+2P)/S+1H2=(H1?F+2P)/S+1?(寬和高對稱的計算)
- D2=KD2=K
- 使用參數共享,每個濾波器引入?F?F?D1F?F?D1?個權重,總共?(F?F?D1)?K(F?F?D1)?K?個權重和?KK?個偏置
- 輸出方體中,第?dd?個 depth slice(具有大小W2×H2W2×H2)是第?dd?個濾波器與輸入方體進行步幅為SS的有效卷積(valid convolution),再使用第dd個偏置的結果
常用的超參數設置是?F=3,S=1,P=1F=3,S=1,P=1。然而也有其它常見約定和經驗法則來設置超參數。見下面的卷積網絡結構一節。
???????卷積演示。下面是一個卷積層的演示動圖。由于 3D 方體很難可視化,所有方體(輸入方體(藍色),權重方體(紅色),輸出方體(綠色))使用 depth slice 的按行堆疊來進行可視化。輸入方體大小為?W1=5,H1=5,D1=3W1=5,H1=5,D1=3,卷積層參數為K=2,F=3,S=2,P=1K=2,F=3,S=2,P=1。也就是說,我們有兩個大小為?3×33×3?的過濾器,并且步幅為 2。因此,輸出方體的空間大小為 (5 - 3 + 2) / 2 + 1 = 3。另外,因為對輸入方體使用了?P=1P=1?的填充,使得輸入方體的外邊界為 0。下面的可視化是對輸出激活(綠色)的重復,顯示每一個元素都是通過高亮輸入(藍色)和過濾器(紅色)的元素相乘,然后相加,再加上偏置得來的。
動圖鏈接
???????作為矩陣乘法實現。注意,卷積運算本質上是過濾器與輸入的局部區域之間的點積。卷積層的常用實現模式充分利用了這一點,把卷積層的前向傳播格式化為大的矩陣乘法,如下:
???????這個方法的缺點是它要使用大量的內存,因為輸入方體電話中一些值會在?X_col?中重復多次。然而,它的好處是存在很多的我們可以充分利用的矩陣乘法的高效實現(比如,經常使用的?BLAS?API)。另外,相同的?im2col?的思想也可以用于進行池化運算(我們將在后面討論)。
???????反向傳播。(對于數據和權重的)卷積運算的反向過程也是卷積(只不過過濾器是空間翻轉的)。這可以使用非常簡單的 1 維例子來推導(這里略過)。
???????1x1 卷積。在?Network in Network?首次提出后,一些論文也使用 1x1 的卷積。一些具有信號處理背景的人第一次看到 1x1 卷積的時候會很困惑,因為正常信號都是 2 維的,因此 1x1 的卷積沒有意義(它只是進行逐點的伸縮)。然而,在卷積網絡中情況卻不是這樣,因為我們現在是在 3 維輸入方體進行操作,而且過濾器永遠在輸入方體的整個深度上擴展。例如,如果輸入大小是 [32x32x3],則使用 1x1 卷積進行 3 維點積是有效的(因為輸入深度有 3 個通道)。
???????Dilated convolutions。最近的發展(例如,Fisher Yu 和 Vladlen Koltun 的論文)往卷積層上又引入了一個稱為?dilation?的超參數。到目前為止,我們討論的卷積過濾器都是接觸的,然而,也可以有單元之間是分離的過濾器。例如,1 維的時候,一個大小為 3 的過濾器在輸入 x 上進行計算:w[0]*x[0]+w[1]*x[1]+w[2]*x[2],這是 dilation 為 0 的情況。對于 dilation 為 1 的過濾器,則計算?w[0]*x[0]+w[1]*x[2]+w[2]*x[4]。換句話說,權重使用的時候是有間隔的。當與 0-dilated 的過濾器進行聯合使用的時候,這變得非常有用,因為它只用很少的層就能高度的融合輸入的空間信息。例如,如果你堆疊兩個 3x3 的卷積層,你可以確認第二個層的神經元是輸入的的 5x5 的圖塊的函數(我們說這些神經元的有效感受野是 5x5)。假如我們使用 dilated convolution,則這些有效感受野會更快的增長。
池化層
???????在卷積結構中通常會在相繼的卷積層之間周期性的插入池化層(Pooling Layer),它的作用是逐漸的減少表示的空間大小,從而減少網絡的參數的個數核計算量,進而控制過擬合。池化層在輸入的每個 depth slice 上使用 MAX 操作獨立的計算,改變它的空間大小。池化層最常用的形式是在輸入的每個 depth slice 上使用 2x2 大小的過濾器和步幅為 2 的降采樣,忽略其中 75% 的激活。每一個 MAX 操作都是對 4 個數(depth slice 中的 2x2 小區域)取最大。另外,網絡的深度保持不變。一般的,池化層:
- 接收大小為W1×H1×D1W1×H1×D1?的方體
- 需要 2 個超參數:?
- 空間范圍?FF
- 步幅SS
- 產生大小為?W2×H2×D2W2×H2×D2?的方體,其中:?
- W2=(W1?F)/S+1W2=(W1?F)/S+1
- H2=(H1?F)/S+1H2=(H1?F)/S+1
- D2=D1D2=D1
- 不引入新的參數,因為它是計算輸入的固定函數
- 池化層通常不使用 0-填充
???????值得注意的是實際中常用的最大池化層(max pooling layer)只有兩種:F=3,S=2F=3,S=2的池化層(也稱為重疊池化)和更常見的?F=2,S=2F=2,S=2?的池化層。具有更大感受野的池化大小是有害的。
???????一般池化。除了最大池化,池化單元還可以進行其它函數運算,比如平均池化(average pooling)和?L2-范數池化(L2-norm pooling)。平均池化在過去較為常用,因為相比之下,最大池化在實際中性能更好。
池化層對輸入方體的每一個 depth slice 在空間上進行獨立的降采樣。左圖:在這個例子中,對大小為 [224x224x64] 的輸入方體進行過濾器大小為 2 和步幅為 2 的池化,得到大小為 [112x112x64] 的輸出方體。這里注意到方體的深度保持不變。右圖:最常用的降采樣操作是取最大值的最大池化(max pooling),這里顯示的是步幅為 2 的最大池化,每一次都對 4 個數(2x2 的小方塊)取最大值。
???????反向傳播。對 max(x, y) 的反向傳播操作可以簡單的解釋成只是對正向過程的最大值的輸入進行梯度運算。因此,在池化層的正向過程中通常要追蹤最大激活(有時也稱為開關(switches))的下標,使得反向傳播很高效。
???????不使用池化。很多人不喜歡池化運算,并且認為我們可以不需要它。例如,Striving for Simplicity: The All Convolutional Net?建議忽略池化層而用只有不停重復的卷積層的結構來取代。為了減小表示的大小,他們建議在卷積層中使用大的步幅。不使用池化層對訓練好的生成模型,例如變分自編碼器(variational autoencodes, VAEs)或生成對抗網絡(generative adversarial networks, GANs),也被發現是重要的。現在看起來,未來不使用池化層的結構會很少。
標準化層
???????在卷積網絡結構中使用了很多類型的標準化層(normalization layer),有時是為了實現在生物腦中觀察到的抑制模式的意圖。然而這些層已經不再受歡迎,因為在實際使用時它們的作用很小(如果有的話)。要了解各種類型的標準化,參考 Alex Krizhevsky 的分析?cuba-convnet library API。
全連接層
???????正如普通神經網絡中那樣,全連接層(fully-connected layer,FC layer)中的神經元與它前一層的所有激活都先連。因此,這種激活可以通過矩陣乘法,然后加上偏置來計算。(請參考這一系列的?Neural Network?獲取更多信息)
全連接層轉化為卷積層
???????全連接層(FC layer)和卷積層(CONV layer)僅有的區別是:卷積層中的神經元只連接到輸入的局部區域,而且很多的神經元共享參數。然而,這兩種層中的神經元都要計算點積,因此它們的函數形式是一樣的。從而,將全連接層轉化為卷積層是可行的:
- 對任意卷積層,存在一個全連接層能實現相同的前向函數,對應的權重矩陣是一個只在特定塊非零(由于局部連接)的大矩陣,而且很多塊的權重相等(由于參數共享)。
- 反過來,任意的全連接層可以轉化為卷積層。例如,對于具有?K=4096K=4096?個神經元,且與大小為?7×7×5127×7×512?的輸入方體相連的全連接層,可以等價的的表示成?F=7,P=0,S=1,K=4096F=7,P=0,S=1,K=4096?的卷積層。換句話說,我們令過濾器的大小恰好等于輸入方體的大小,從而得到?1×1×40961×1×4096?的輸出,也就是對輸入方體的每一個 depth slice 進行卷積,它的結果與原來全連接層的一樣。
???????FC->CONV 的轉化。在這兩個轉化中,將全連接層轉化為卷積層在實際中特別有用。考慮一個卷積網絡結構,它以 224x224x3 的圖像作為輸入,然后使用一系列的卷積層和池化層將圖像簡化為大小為 7x7x512 的激活方體(在后面我們將要看到的 AlexNet 中,這由 5 個池化層得到,每一個在空間上使用因子為 2 的降采樣,使得最后的空間大小為 224/2/2/2/2/2 = 7)。之后,AlexNet 使用兩個大小為 4096 的全連接層,最后再接一個具有 1000 個神經元的全連接層來計算類得分。我們可以把這三個全連接層轉化為卷積層:
- 將第一個全連接層用具有大小為?F=7F=7?的過濾器的卷積層替換,把 [7x7x512] 的輸入方體變為 [1x1x4096] 的輸出方體
- 將第二個全連接層用具有大小為?F=1F=1?的過濾器的卷積層替換,得到 [1x1x4096] 的輸出方體
- 類似的,替換最后一個全連接層,其中?F=1F=1,最后的輸出大小為 [1x1x1000]
???????每一個這樣的轉化在實際中都要將全連接層的權重矩陣重新塑形為卷積層的過濾器。這種轉化只用一個前向傳播過程,在大圖像中沿著空間位置滑動的進行原來的卷積,被證明是非常高效的。
???????例如,如果一張 224x224 的圖像給出 [7x7x512] 的輸出,也就是簡化 32 倍,那么對于一張 384x384 的圖像,在轉化結構下將給出等價的大小為 [12x12x512] 的方體,因為 384 / 32 = 12。然后再通過我們剛剛由全連接層轉化來的卷積層,給出大小為 [6x6x1000] 的最終方體,因為 (12 - 7) / 1 + 1 = 6。注意,對于一幅 384x384 的圖像,我們不再得到大小為 [1x1x1000] 的單個類得分向量,而是得到類得分的整個 6x6 的數組。
對 384x384 的圖像使用步幅為 32 個像素的 224x224 剪切,再獨立的使用原來的卷積網絡(含有全連接層),得到的結果和轉化來的卷積網絡的是一樣的。
???????自然的,轉化來的卷積網絡只計算一次,比原來的卷積網絡計算 36 次要高效很多,因為這 36 次其實可以共享計算。這種技術在實際中經常用來得到更好的性能,例如,通常把圖像調整為更大的形狀,然后使用轉化的卷積網絡來計算多個空間位置的類得分,再對這些類得分進行平均。
???????最后,如果步幅小于 32 個像素,我們怎樣能高效的將原來的卷積網絡應用到圖像?我們可以多次使用前向過程來得到。例如,如果我們想要使用 16 個像素的步幅,那我們可以將兩個用轉化來的卷積網絡得到的方體組合起來:首先是對原始圖像的,然后是對沿著寬和高各平移 16 個像素的圖像的。
- 一個用 IPython Notebook 寫的?Net Surgery?顯示怎樣實際的用代碼(使用 Caffe)進行轉化。
卷積網絡結構
???????我們已經知道卷積網絡通常只由三種類型的層組成:卷積層(CONV layer)、池化層(POOL layer,如果沒有特別聲明,都假設是 MAX pool)和全連接層(fully-connected layer,FC layer)。我們也會顯式的寫出整流線性單元(RELU)激活函數,它非線性的應用到每個元素。這一節我們討論怎樣將這些層堆疊起來形成整個卷積網絡。
層模式
???????卷積網絡最常見的形式是堆疊一些 CONV-RELU 層,之后是 POOL 層,然后重復這個模式直到圖像在空間上已經被融合成小的尺寸。在某些節點,還通常要過渡到全連接層。最后的全連接層保存輸出,例如類得分。換句話說,最常用的卷積網絡遵循下述模式:
INPUT -> [[CONV -> RELU]*N -> POOL?]*M -> [FC -> RELU]*K -> FC- 1
其中?*?表示重復,POOL??表示可選的池化層。另外,N >= 0(且通常?N <= 3),M >= 0,K >= 0(且通常?K < 3)。例如,一些經常見到的卷積網絡結構有如下模式:
- INPUT -> FC,實現線性分類器。這里?N = M = K = 0。
- INPUT -> CONV -> RELU -> FC。
- INPUT -> [CONV -> RELU -> POOL]*2 -> FC -> RELU -> FC。這里我們看到 POOL 層之間只有一個 CONV 層。
- INPUT -> [CONV -> RELU -> CONV -> RELU -> POOL]*3 -> [FC -> RELU]*2 -> FC。這里 POOL 層之間堆疊了兩個 CONV 層。這對大而深的網絡來說是個好想法,因為在具有破壞性的池化運算之前堆疊多個 CONV 層能夠對輸入方體提取出更多復雜的特征。
???????相比大感受野的卷積層優先選擇堆疊小過濾器的卷積層。假設你接連堆疊三個 3x3 的卷積層(當然,它們之間還有非線性運算)。在第一個卷積層上的神經元對輸入方體有 3x3 的視野,而第二層上的神經元對第一層也有 3x3 的視野,從而對輸入方體有 5x5 的視野。類似的,第三層的神經元對第二層有 3x3 的視野,從而對輸入方體有 7x7 的視野。假設我們只用一個具有 7x7 感受野的卷積層替代這三個 3x3 的卷積層。這些神經元雖然在空間范圍上對輸入方體的感受野也恰好是 7x7,但是卻有一些缺點。首先,這些神經元只對輸入計算了線性函數,而三個卷積層的堆疊卻包含非線性從而使得特征更富有表示性。第二,如果我們假設所有方體的通道數都是?CC,那么單個 7x7 卷積層具有?C×(7×7×C)=49C2C×(7×7×C)=49C2?個參數,而三個 3x3 卷積層只有?3×(C×(3×3×C))=27C23×(C×(3×3×C))=27C2?個參數。直觀上,相比于單個具有大過濾器的卷積層,堆疊多個小過濾器的卷積層能表達輸入的更強大的特征,而且參數更少。但這也有實現上的缺點,就是我們需要更多的內存來保存中間卷積層的結果,好讓我們能進行反向傳播。
???????背離趨勢。值得注意的是,由層進行線性堆疊的約定范式最近已經發生改變,如 Google 的 Inception 結構,以及最近微軟亞洲研究院(Microsoft Research Asia)的(state of the art)殘差網絡,這兩個網絡(細節見下面案例研究一節)都具有錯綜復雜的連接結構。
???????實踐:在 ImageNet 上使用什么網絡最好。如果你正在為網絡結構設計而苦苦思索,那么很高興告訴你 90% 及以上的應用是不需要擔憂這個問題的。我把這總結為一個觀點:“不要做英雄”,與其為一個問題設計自己的結構,不如看看當前哪些網絡在 ImageNet 上性能最好,然后下載一個預訓練模型再用你自己的數據來精調。你幾乎不需要痛苦的訓練或設計一個卷積網絡。我在?Deep Learning school?也說了這一觀點。
層大小模式
???????到目前為止,我們還沒有注意卷積網絡中每一層的超參數的常用設置。我們將首先陳述一些結構大小的常用經驗法則,然后基于這些法則來做一些分析:
???????輸入層(input layer)(保存圖像)應當可以被 2 除很多次。常見的大小包括 32(如 CIFAR-10),64,96(如 STL-10),或者 224(如常用的 ImageNet 卷積網絡),384 和 512。
???????卷積層(conv layer)應該使用小過濾器(比如 3x3,至多 5x5),使用步幅?S=1S=1,而且最重要的是使用 0-填充來確保卷積層不會改變輸入的空間維數。也就是說,如果?F=1F=1,則用?P=1P=1?的 0-填充來保持輸入的原始大小。當?F=5F=5?時,P=2P=2。對一般的?FF,設置?P=(F?1)/2P=(F?1)/2?來保持輸入的大小。如果你一定要使用大尺寸的過濾器(比如 7x7 或更大),則通常是在輸入圖像之后的第一個卷積層使用。
???????池化層(pool layer)負責對輸入的空間維數進行降采樣。最常用的設置是使用步幅為 2 (S=2S=2)的 2x2(F=2F=2)感受野的最大池化(max-pooling),這恰好忽略了輸入方體激活的 75%(因為對寬和高進行 2 個單位的降采樣)。另外一個不常用的設置是使用步幅為 2 的 3x3 感受野。很少見到用于最大池化的感受野的大小超過 3,因為這樣的池化太高度聚合而過多損失信息導致性能很差。
???????減輕頭痛程度。上面的模式令人很愉快,因為所有的卷積層都保持輸入的空間尺寸,而池化層則只負責在空間上進行降采樣。如果換一種方式,我們使用步幅超過 1 或者不對卷積層的輸入進行 0-填充,則我們要非常仔細的在卷積網絡結構中跟蹤輸入方體以確保所有的步幅和過濾器起作用,以及卷積網絡結構能好的和對稱的串聯起來。
???????為什么在卷積層中使用步幅 1。小的步幅在實際中效果更好。而且步幅為 1 使得卷積層只需要改變輸入方體的深度,而將所有空間降采樣的任務留給池化層。
???????為什么使用填充。使用 0-填充,除了在上面說的能在經過卷積層之后還保持空間大小的好處之外,它實際上還能提升性能。如果不使用 0-填充而僅進行有效卷積,則在每一個卷積層之后方體的大小都會輕微減小,而且邊界信息會很快的消失。
???????內存限制的讓步。在某些情況下(特別是早期的卷積網絡結構),在上面所說的經驗法則下內存消耗增長得非常快。例如,使用三個 3x3 的卷積層,每個具有 64 個過濾器,對 224x224x3 的圖像先進行 1 個像素的填充,然后再進行卷積過濾,得到三個大小為 [224x224x64] 的激活方體。所有這些加在一起將近 1000 萬個激活項,或者 72MB 的內存(每張圖像,對激活和梯度都是)。因為 GPU 通常都有內存瓶頸,所以必須做出讓步。在實際中,人們傾向于只對網絡的第一個卷積層讓步。比如,一個讓步是在第一個卷積層使用步幅為 2 的 7x7 的過濾器(如將要看到的 ZFNet)。另一個例子是 AlexNet 使用步幅為 4 的 11x11 的過濾器。
案例研究
???????在卷積網絡領域,有很多的結構都有命名。最常見的是:
- LeNet。在上世紀九十年代,Yann LeCun 首次使得卷積網絡獲得成功應用。其中,最有名的結構是?LeNet,它用于讀取郵政編碼、數字等。
- AlexNet。在計算機視覺領域,第一個最受歡迎的卷積網絡是?AlexNet,它由 Alex Krizhevsky,IIya Sutskever 和 Geoff Hinton 建立。AlexNet 在 2012 年的?ImageNet ILSVRC challenge?上提交結果,表現遠超第二名(相比第二名的 top 5 錯誤率 26%,它只有 16%)。這個網絡的結構非常類似 LeNet,只是它更深、更大,而且有多個卷積層堆疊在一起(此前,通常一個卷積層之后緊跟著池化層)。
- ZFNet。2013 年的 ILSVRC 由 Matthew Zeiler 和 Rob Fergus 提出的卷積網絡獲得冠軍,即現在有名的?ZFNet(Zeiler & Fergus Net 的縮寫)。它是由調整 AlexNet 的超參數改善而來,特別是擴大了 AlexNet 的中間卷積層大小,以及減小了第一個卷積層的步幅和過濾器大小。
- GoogLeNet。2014 年的 ILSVRC 的冠軍是由來源于谷歌的?Szegedy et al.?提出的卷積網絡。它的主要貢獻是發展了?Inception Module,可以極大的減少網絡的參數個數(4M 相比于 AlexNet 的 60 M)。而且,在卷積網絡頂層用平均池化層(Average Polling)替換了全連接層,消減了大量的無關緊要的參數。GoogLeNet 有很多的后續版本,最近的版本是?Inception-v4。
- VGGNet。2014 年的 ILSVRC 的亞軍是?VGGNet,由 Karen Simonyan 和 Andrew Zisserman 提出。它的主要貢獻是證明網絡深度是好的性能的關鍵因素。他們最后最好的網絡包含 16 個 CONV / FC 層,一個從始至終只進行 3x3 的卷積和 2x2 的池化的異常齊次的結構。他們的?pretrained model?可以公開下載(使用 Caffe 編寫)。VGGNet 的一個缺點是:它的計算代價很高昂,使用大量內存和參數(140M)。大量的參數來源于第一個全連接層,而且現在已經知道這些全連接層就算被移除也不會讓性能下降,但卻可以顯著的減少參數個數。
- ResNet。殘差網絡(Residual Network,ResNet)由何愷明(Kaiming He)等人建立,它是 ILSVRC 2015 年的冠軍。它使用了特殊的?skip connections?和大量的應用了?batch normalization。網絡結構的最后也沒有使用全連接層。讀者可以參考愷明的報告(視頻,幻燈片),最近一些實驗用 Torch 復現了他們的結果。到目前為止,ResNet 是最好的(the state of the art)卷積神經網絡,也是實際使用卷積網絡的默認選擇(截止到 2016 年 5 月 10 日)。特別的,最近的發展?Kaiming He et al. Identity Mappings in Deep Residual Networks(發表于 2016 年 4 月) 調整了原來的網絡結構。
???????VGGNet細節。作為一個案例研究,讓我們更詳細的來分解一下?VGGNet。整個 VGGNet 由進行步幅為 1,1 個單位填充的 3x3 卷積的卷積層,和進行步幅為 2(沒有填充)的 2x2 最大池化的池化層組成。我們可以把處理過程的每一步的表示的大小寫出來,也可以追蹤表示大小和參數個數:
INPUT: [224x224x3] memory: 224*224*3=150K weights: 0 CONV3-64: [224x224x64] memory: 224*224*64=3.2M weights: (3*3*3)*64 = 1,728 CONV3-64: [224x224x64] memory: 224*224*64=3.2M weights: (3*3*64)*64 = 36,864 POOL2: [112x112x64] memory: 112*112*64=800K weights: 0 CONV3-128: [112x112x128] memory: 112*112*128=1.6M weights: (3*3*64)*128 = 73,728 CONV3-128: [112x112x128] memory: 112*112*128=1.6M weights: (3*3*128)*128 = 147,456 POOL2: [56x56x128] memory: 56*56*128=400K weights: 0 CONV3-256: [56x56x256] memory: 56*56*256=800K weights: (3*3*128)*256 = 294,912 CONV3-256: [56x56x256] memory: 56*56*256=800K weights: (3*3*256)*256 = 589,824 CONV3-256: [56x56x256] memory: 56*56*256=800K weights: (3*3*256)*256 = 589,824 POOL2: [28x28x256] memory: 28*28*256=200K weights: 0 CONV3-512: [28x28x512] memory: 28*28*512=400K weights: (3*3*256)*512 = 1,179,648 CONV3-512: [28x28x512] memory: 28*28*512=400K weights: (3*3*512)*512 = 2,359,296 CONV3-512: [28x28x512] memory: 28*28*512=400K weights: (3*3*512)*512 = 2,359,296 POOL2: [14x14x512] memory: 14*14*512=100K weights: 0 CONV3-512: [14x14x512] memory: 14*14*512=100K weights: (3*3*512)*512 = 2,359,296 CONV3-512: [14x14x512] memory: 14*14*512=100K weights: (3*3*512)*512 = 2,359,296 CONV3-512: [14x14x512] memory: 14*14*512=100K weights: (3*3*512)*512 = 2,359,296 POOL2: [7x7x512] memory: 7*7*512=25K weights: 0 FC: [1x1x4096] memory: 4096 weights: 7*7*512*4096 = 102,760,448 FC: [1x1x4096] memory: 4096 weights: 4096*4096 = 16,777,216 FC: [1x1x1000] memory: 1000 weights: 4096*1000 = 4,096,000TOTAL memory: 24M * 4 bytes ~= 93MB / image (only forward! ~*2 for bwd) TOTAL params: 138M parameters- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
???????注意到,與通常的卷積網絡一樣,大量的內存(和計算時間)都使用在早期的卷積層,而大部分的參數都來源于最后的全連接層。在這個特殊案例下,第一個全連接層包含整個網絡的 140M 個參數中的 100M 個。
計算考量
???????在構造卷積網絡結構的時候,最大的瓶頸是內存瓶頸。很多現代的 GPU 都有 3 / 4 / 6 GB 的內存限制,就算是最好的 GPU 也只有大約 12 GB 的內存。有三個主要的內存占用項:
- 來源于中間方體大小:它們是卷積網絡的每一層的激活(activations)的原始數值,以及它們的梯度(大小相等)。通常大量的激活都集中在卷積網絡的淺層(也就是前面的卷積層)。它們需要保留,因為在反向傳播的時候要用到,當然在測試階段,卷積網絡聰明的實現是只保存任一層當前的激活而忽略前面層的激活,這在原則上可以大量的減少內存。
- 來源于參數大小:它們是網絡參數(parameters)的數值,反向傳播時的梯度,和通常還包括使用 momentum,Adagrad,或者 RMSProp 優化時的緩存。因此,保存參數向量的內存必須要乘以至少是 3 或者更大的因子。
- 每一個卷積網絡的實現都要維護各種各樣(miscellaneous)的內存,比如圖像數據批量,可能是它們的增強版本等等。
???????一旦你對值(activations,gradients,misc)的總個數有一個大概的估計,這個數應當轉化為多少 GB。取值的個數,乘以 4 就得到原始 bytes 數(因為每一個浮點數是 4 bytes,如果是雙精度浮點數則是 8 bytes),然后除以 1024 多次來得到多少 KB,MB,GB 的內存。如果你的網絡與內存不匹配,一個讓它匹配的常用啟發式方法是減小批量大小,因為大量的內存通常由激活消耗。
其它資源
???????與實現有關的其它資源:
- 卷積網絡性能的 Soumith 基準
- ConvNetJS CIFAR-10 demo?允許你在網頁上設置卷積網絡結構,查看實時計算的結果
- Caffe,一個流行的卷積網絡庫
- State of the art ResNets in Torch7
總結
以上是生活随笔為你收集整理的卷积神经网络(Convolutional Neural Networks,CNNS/ConvNets)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 神经网络中常用的激活函数
- 下一篇: CNN经典网络模型:LeNet,Alex