課程八 卷積神經網絡

@(斯坦福李飛飛深度學習上課筆記)

課程八 卷積神經網絡

內容列表

       1. 結構概述
       2. 用來構建卷積神經網絡的各種層
            2.1 卷積層
            2.2 匯聚層
            2.3 歸一化層
            2.4 全連接層
            2.5將全連接層轉化成卷積層
       3.卷積神經網絡的結構
         3.1 層的排列規律
         3.2層的尺寸設置規律
         3.3案例學習(LeNet / AlexNet / ZFNet / GoogLeNet / VGGNet)
         3.4計算上的考量

卷積神經網絡(CNNs / ConvNets)

卷積神經網絡和上一章講的常規神經網絡非常相似:它們都是由神經元組成,神經元中有具有學習能力的權重和偏差。每個神經元都得到一些輸入數據,進行內積運算後再進行激活函數運算。整個網絡依舊是一個可導的評分函數:該函數的輸入是原始的圖像像素,輸出是不同類別的評分。在最後一層(往往是全連接層),網絡依舊有一個損失函數(比如SVM或Softmax),並且在神經網絡中我們實現的各種技巧和要點依舊適用於卷積神經網絡。

那麼有哪些地方變化了呢?卷積神經網絡的結構基於一個假設,即輸入數據是圖像,基於該假設,我們就向結構中添加了一些特有的性質。這些特有屬性使得前向傳播函數實現起來更高效,並且大幅度降低了網絡中參數的數量。

1. 結構概述

回顧:常規神經網絡。在上一章中,神經網絡的輸入是一個向量,然後在一系列的隱層中對它做變換。每個隱層都是由若干的神經元組成,每個神經元都與前一層中的所有神經元連接。但是在一個隱層中,神經元相互獨立不進行任何連接。最後的全連接層被稱爲“輸出層”,在分類問題中,它輸出的值被看做是不同類別的評分值。

常規神經網絡對於大尺寸圖像效果不盡人意。在CIFAR-10中,圖像的尺寸是32x32x3(寬高均爲32像素,3個顏色通道),因此,對應的的常規神經網絡的第一個隱層中,每一個單獨的全連接神經元就有32x32x3=3072個權重。這個數量看起來還可以接受,但是很顯然這個全連接的結構不適用於更大尺寸的圖像。舉例說來,一個尺寸爲200x200x3的圖像,會讓神經元包含200x200x3=120,000個權重值。而網絡中肯定不止一個神經元,那麼參數的量就會快速增加!顯而易見,這種全連接方式效率低下,大量的參數也很快會導致網絡過擬合。

神經元的三維排列。卷積神經網絡針對輸入全部是圖像的情況,將結構調整得更加合理,獲得了不小的優勢。與常規神經網絡不同,卷積神經網絡的各層中的神經元是3維排列的:寬度、高度和深度(這裏的深度指的是激活數據體的第三個維度,而不是整個網絡的深度,整個網絡的深度指的是網絡的層數)。舉個例子,CIFAR-10中的圖像是作爲卷積神經網絡的輸入,該數據體的維度是32x32x3(寬度,高度和深度)。我們將看到,層中的神經元將只與前一層中的一小塊區域連接,而不是採取全連接方式。對於用來分類CIFAR-10中的圖像的卷積網絡,其最後的輸出層的維度是1x1x10,因爲在卷積神經網絡結構的最後部分將會把全尺寸的圖像壓縮爲包含分類評分的一個向量,向量是在深度方向排列的。下面是例子:
Alt text
左邊是一個3層的神經網絡。右邊是一個卷積神經網絡,圖例中網絡將它的神經元都排列成3個維度(寬、高和深度)。卷積神經網絡的每一層都將3D的輸入數據變化爲神經元3D的激活數據並輸出。在這個例子中,紅色的輸入層裝的是圖像,所以它的寬度和高度就是圖像的寬度和高度,它的深度是3(代表了紅、綠、藍3種顏色通道)。

卷積神經網絡是由層組成的。每一層都有一個簡單的API:用一些含或者不含參數的可導的函數,將輸入的3D數據變換爲3D的輸出數據。

2. 用來構建卷積網絡的各種層

一個簡單的卷積神經網絡是由各種層按照順序排列組成,網絡中的每個層使用一個可以微分的函數將激活數據從一個層傳遞到另一個層。卷積神經網絡主要由三種類型的層構成:卷積層匯聚(Pooling)層全連接層(全連接層和常規神經網絡中的一樣)。通過將這些層疊加起來,就可以構建一個完整的卷積神經網絡。

網絡結構例子:這僅僅是個概述,下面會更詳解的介紹細節。一個用於CIFAR-10圖像數據分類的卷積神經網絡的結構可以是[輸入層-卷積層-ReLU層-匯聚層-全連接層]。細節如下:

  • 輸入[32x32x3]存有圖像的原始像素值,本例中圖像寬高均爲32,有3個顏色通道。
  • 卷積層中,神經元與輸入層中的一個局部區域相連,每個神經元都計算自己與輸入層相連的小區域與自己權重的內積。卷積層會計算所有神經元的輸出。如果我們使用12個濾波器(也叫作核),得到的輸出數據體的維度就是[32x32x12]。
  • ReLU層將會逐個元素地進行激活函數操作,比如使用以0爲閾值的max(0,x)作爲激活函數。該層對數據尺寸沒有改變,還是[32x32x12]。
  • 匯聚層在在空間維度(寬度和高度)上進行降採樣(downsampling)操作,數據尺寸變爲[16x16x12]。
  • 全連接層將會計算分類評分,數據尺寸變爲[1x1x10],其中10個數字對應的就是CIFAR-10中10個類別的分類評分值。正如其名,全連接層與常規神經網絡一樣,其中每個神經元都與前一層中所有神經元相連接。

由此看來,卷積神經網絡一層一層地將圖像從原始像素值變換成最終的分類評分值。其中有的層含有參數,有的沒有。具體說來,卷積層和全連接層(CONV/FC)對輸入執行變換操作的時候,不僅會用到激活函數,還會用到很多參數(神經元的突觸權值和偏差)。而ReLU層和匯聚層則是進行一個固定不變的函數操作。卷積層和全連接層中的參數會隨着梯度下降被訓練,這樣卷積神經網絡計算出的分類評分就能和訓練集中的每個圖像的標籤吻合了。

小結

  • 簡單案例中卷積神經網絡的結構,就是一系列的層將輸入數據變換爲輸出數據(比如分類評分)。
  • 卷積神經網絡結構中有幾種不同類型的層(目前最流行的有卷積層、全連接層、ReLU層和匯聚層)。
  • 每個層的輸入是3D數據,然後使用一個可導的函數將其變換爲3D的輸出數據。
  • 有的層有參數,有的沒有(卷積層和全連接層有,ReLU層和匯聚層沒有)。
  • 有的層有額外的超參數,有的沒有(卷積層、全連接層和匯聚層有,ReLU層沒有)。
    Alt text

一個卷積神經網絡的激活輸出例子。左邊的輸入層存有原始圖像像素,右邊的輸出層存有類別分類評分。在處理流程中的每個激活數據體是鋪成一列來展示的。因爲對3D數據作圖比較困難,我們就把每個數據體切成層,然後鋪成一列顯示。最後一層裝的是針對不同類別的分類得分,這裏只顯示了得分最高的5個評分值和對應的類別。完整的網頁演示在我們的課程主頁。本例中的結構是一個小的VGG網絡,VGG網絡後面會有討論。

現在講解不同的層,層的超參數和連接情況的細節。

2.1 卷積層

卷積層是構建卷積神經網絡的核心層,它產生了網絡中大部分的計算量。

概述和直觀介紹:首先討論的是,在沒有大腦和生物意義上的神經元之類的比喻下,卷積層到底在計算什麼。卷積層的參數是有一些可學習的濾波器集合構成的。每個濾波器在空間上(寬度和高度)都比較小,但是深度和輸入數據一致。舉例來說,卷積神經網絡第一層的一個典型的濾波器的尺寸可以是5x5x3(寬高都是5像素,深度是3是因爲圖像應爲顏色通道,所以有3的深度)。在前向傳播的時候,讓每個濾波器都在輸入數據的寬度和高度上滑動(更精確地說是卷積),然後計算整個濾波器和輸入數據任一處的內積。當濾波器沿着輸入數據的寬度和高度滑過後,會生成一個2維的激活圖(activation map),激活圖給出了在每個空間位置處濾波器的反應。直觀地來說,網絡會讓濾波器學習到當它看到某些類型的視覺特徵時就激活,具體的視覺特徵可能是某些方位上的邊界,或者在第一層上某些顏色的斑點,甚至可以是網絡更高層上的蜂巢狀或者車輪狀圖案。

在每個卷積層上,我們會有一整個集合的濾波器(比如12個),每個都會生成一個不同的二維激活圖。將這些激活映射在深度方向上層疊起來就生成了輸出數據。

以大腦做比喻:如果你喜歡用大腦和生物神經元來做比喻,那麼輸出的3D數據中的每個數據項可以被看做是神經元的一個輸出,而該神經元只觀察輸入數據中的一小部分,並且和空間上左右兩邊的所有神經元共享參數(因爲這些數字都是使用同一個濾波器得到的結果)。現在開始討論神經元的連接,它們在空間中的排列,以及它們參數共享的模式。

局部連接:在處理圖像這樣的高維度輸入時,讓每個神經元都與前一層中的所有神經元進行全連接是不現實的。相反,我們讓每個神經元只與輸入數據的一個局部區域連接。該連接的空間大小叫做神經元的感受野(receptive field),它的尺寸是一個超參數(其實就是濾波器的空間尺寸)。在深度方向上,這個連接的大小總是和輸入量的深度相等。需要再次強調的是,我們對待空間維度(寬和高)與深度維度是不同的:連接在空間(寬高)上是局部的,但是在深度上總是和輸入數據的深度一致。

例1:假設輸入數據體尺寸爲[32x32x3](比如CIFAR-10的RGB圖像),如果感受野(或濾波器尺寸)是5x5,那麼卷積層中的每個神經元會有輸入數據體中[5x5x3]區域的權重,共5x5x3=75個權重(還要加一個偏差參數)。注意這個連接在深度維度上的大小必須爲3,和輸入數據體的深度一致。

例2:假設輸入數據體的尺寸是[16x16x20],感受野尺寸是3x3,那麼卷積層中每個神經元和輸入數據體就有3x3x20=180個連接。再次提示:在空間上連接是局部的(3x3),但是在深度上是和輸入數據體一致的(20)。

Alt text

左邊:紅色的是輸入數據體(比如CIFAR-10中的圖像),藍色的部分是第一個卷積層中的神經元。卷積層中的每個神經元都只是與輸入數據體的一個局部在空間上相連,但是與輸入數據體的所有深度維度全部相連(所有顏色通道)。在深度方向上有多個神經元(本例中5個),它們都接受輸入數據的同一塊區域(感受野相同)。至於深度列的討論在下文中有。

右邊:神經網絡章節中介紹的神經元保持不變,它們還是計算權重和輸入的內積,然後進行激活函數運算,只是它們的連接被限制在一個局部空間。

空間排列:上文講解了卷積層中每個神經元與輸入數據體之間的連接方式,但是尚未討論輸出數據體中神經元的數量,以及它們的排列方式。3個超參數控制着輸出數據體的尺寸:深度(depth),步長(stride)和零填充(zero-padding)。下面是對它們的討論:

  1. 首先,輸出數據體的深度是一個超參數:它和使用的濾波器的數量一致,而每個濾波器在輸入數據中尋找一些不同的東西。舉例來說,如果第一個卷積層的輸入是原始圖像,那麼在深度維度上的不同神經元將可能被不同方向的邊界,或者是顏色斑點激活。我們將這些沿着深度方向排列、感受野相同的神經元集合稱爲深度列(depth column),也有人使用纖維(fibre)來稱呼它們。
  2. 其次,在滑動濾波器的時候,必須指定步長。當步長爲1,濾波器每次移動1個像素。當步長爲2(或者不常用的3,或者更多,這些在實際中很少使用),濾波器滑動時每次移動2個像素。這個操作會讓輸出數據體在空間上變小。
  3. 在下文可以看到,有時候將輸入數據體用0在邊緣處進行填充是很方便的。這個零填充(zero-padding)的尺寸是一個超參數。零填充有一個良好性質,即可以控制輸出數據體的空間尺寸(最常用的是用來保持輸入數據體在空間上的尺寸,這樣輸入和輸出的寬高都相等)。

輸出數據體在空間上的尺寸可以通過輸入數據體尺寸(W),卷積層中神經元的感受野尺寸(F),步長(S)和零填充的數量(P)的函數來計算。(這裏假設輸入數組的空間形狀是正方形,即高度和寬度相等)輸出數據體的空間尺寸爲(W-F +2P)/S+1。比如輸入是7x7,濾波器是3x3,步長爲1,填充爲0,那麼就能得到一個5x5的輸出。如果步長爲2,輸出就是3x3。下面是例子:

Alt text
空間排列的圖示。在本例中只有一個空間維度(x軸),神經元的感受野尺寸F=3,輸入尺寸W=5,零填充P=1。左邊:神經元使用的步長S=1,所以輸出尺寸是(53+2)/1+1=5 。右邊:神經元的步長S=2,則輸出尺寸是(53+2)/2+1=3 。注意當步長S=3時是無法使用的,因爲它無法整齊地穿過數據體。從等式上來說,因爲(5-3+2)=4是不能被3整除的。

本例中,神經元的權重是[1,0,-1],顯示在圖的右上角,偏差值爲0。這些權重是被所有黃色的神經元共享的(參數共享的內容看下文相關內容)。

使用零填充:在上面左邊例子中,注意輸入維度是5,輸出維度也是5。之所以如此,是因爲感受野是3並且使用了1的零填充。如果不使用零填充,則輸出數據體的空間維度就只有3,因爲這就是濾波器整齊滑過並覆蓋原始數據需要的數目。一般說來,當步長S=1時,零填充的值是P=(F1)/2 ,這樣就能保證輸入和輸出數據體有相同的空間尺寸。這樣做非常常見,在介紹卷積神經網絡的結構的時候我們會詳細討論其原因。

步長的限制:注意這些空間排列的超參數之間是相互限制的。舉例說來,當輸入尺寸W=10,不使用零填充則P=0,濾波器尺寸F=3,這樣步長S=2就行不通,因爲(WF+2P)/S+1=(103+0)/2+1=4.5 ,結果不是整數,這就是說神經元不能整齊對稱地滑過輸入數據體。因此,這些超參數的設定就被認爲是無效的,一個卷積神經網絡庫可能會報出一個錯誤,或者修改零填充值來讓設置合理,或者修改輸入數據體尺寸來讓設置合理,或者其他什麼措施。在後面的卷積神經網絡結構小節中,讀者可以看到合理地設置網絡的尺寸讓所有的維度都能正常工作,這件事可是相當讓人頭痛的。而使用零填充和遵守其他一些設計策略將會有效解決這個問題。

真實案例:Krizhevsky構架贏得了2012年的ImageNet挑戰,其輸入圖像的尺寸是[227x227x3]。在第一個卷積層,神經元使用的感受野尺寸F=11,步長S=4,不使用零填充P=0。因爲(227-11)/4+1=55,卷積層的深度K=96,則卷積層的輸出數據體尺寸爲[55x55x96]。55x55x96個神經元中,每個都和輸入數據體中一個尺寸爲[11x11x3]的區域全連接。在深度列上的96個神經元都是與輸入數據體中同一個[11x11x3]區域連接,但是權重不同。有一個有趣的細節,在原論文中,說的輸入圖像尺寸是224x224,這是肯定錯誤的,因爲(224-11)/4+1的結果不是整數。這件事在卷積神經網絡的歷史上讓很多人迷惑,而這個錯誤到底是怎麼發生的沒人知道。我的猜測是Alex忘記在論文中指出自己使用了尺寸爲3的額外的零填充。

參數共享:在卷積層中使用參數共享是用來控制參數的數量。就用上面的例子,在第一個卷積層就有55x55x96=290,400個神經元,每個有11x11x3=364個參數和1個偏差。將這些合起來就是290400x364=105,705,600個參數。單單第一層就有這麼多參數,顯然這個數目是非常大的。

作一個合理的假設:如果一個特徵在計算某個空間位置(x,y)的時候有用,那麼它在計算另一個不同位置(x2,y2)的時候也有用。基於這個假設,可以顯著地減少參數數量。換言之,就是將深度維度上一個單獨的2維切片看做深度切片(depth slice),比如一個數據體尺寸爲[55x55x96]的就有96個深度切片,每個尺寸爲[55x55]。在每個深度切片上的神經元都使用同樣的權重和偏差。在這樣的參數共享下,例子中的第一個卷積層就只有96個不同的權重集了,一個權重集對應一個深度切片,共有96x11x11x3=34,848個不同的權重,或34,944個參數(+96個偏差)。在每個深度切片中的55x55個權重使用的都是同樣的參數。在反向傳播的時候,都要計算每個神經元對它的權重的梯度,但是需要把同一個深度切片上的所有神經元對權重的梯度累加,這樣就得到了對共享權重的梯度。這樣,每個切片只更新一個權重集。

注意,如果在一個深度切片中的所有權重都使用同一個權重向量,那麼卷積層的前向傳播在每個深度切片中可以看做是在計算神經元權重和輸入數據體的卷積(這就是“卷積層”名字由來)。這也是爲什麼總是將這些權重集合稱爲濾波器(filter)(或卷積核(kernel)),因爲它們和輸入進行了卷積。

Alt text
Krizhevsky等學習到的濾波器例子。這96個濾波器的尺寸都是[11x11x3],在一個深度切片中,每個濾波器都被55x55個神經元共享。注意參數共享的假設是有道理的:如果在圖像某些地方探測到一個水平的邊界是很重要的,那麼在其他一些地方也會同樣是有用的,這是因爲圖像結構具有平移不變性。所以在卷積層的輸出數據體的55x55個不同位置中,就沒有必要重新學習去探測一個水平邊界了。

注意有時候參數共享假設可能沒有意義,特別是當卷積神經網絡的輸入圖像是一些明確的中心結構時候。這時候我們就應該期望在圖片的不同位置學習到完全不同的特徵。一個具體的例子就是輸入圖像是人臉,人臉一般都處於圖片中心。你可能期望不同的特徵,比如眼睛特徵或者頭髮特徵可能(也應該)會在圖片的不同位置被學習。在這個例子中,通常就放鬆參數共享的限制,將層稱爲局部連接層(Locally-Connected Layer)。

Numpy例子:爲了讓討論更加的具體,我們用代碼來展示上述思路。假設輸入數據體是numpy數組X。那麼:

  • 一個位於(x,y)的深度列(或纖維)將會是X[x,y,:]。
  • 在深度爲d處的深度切片,或激活圖應該是X[:,:,d]。

卷積層例子:假設輸入數據體X的尺寸X.shape:(11,11,4),不使用零填充(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(因爲步長爲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的深度維度上的第二層(即index1),因爲是在計算第二個激活圖,所以這次試用的參數集就是W1了。在上面的例子中,爲了簡潔略去了卷積層對於輸出數組V中其他部分的操作。還有,要記得這些卷積操作通常後面接的是ReLU層,對激活圖中的每個元素做激活函數運算,這裏沒有顯示。

小結: 我們總結一下卷積層的性質:

  • 輸入數據體的尺寸爲
    W1×H1×D1
  • 4個超參數:
    • 濾波器的數量K
    • 濾波器的空間尺寸F
    • 步長S
    • 零填充數量P
  • 輸出數據體的尺寸爲
    W2×H2×D2 ,其中:

W2=(W1F+2P)/S+1

H2=(H1F+2P)/S+1 (寬度和高度的計算方法相同)

D2=K

  • 由於參數共享,每個濾波器包含F\cdot F\cdot D_1個權重,卷積層一共有F\cdot F\cdot D_1\cdot K個權重和K個偏置。
  • 在輸出數據體中,第d個深度切片(空間尺寸是W_2\times H_2),用第d個濾波器和輸入數據進行有效卷積運算的結果(使用步長S),最後在加上第d個偏差。

對這些超參數,常見的設置是F=3,S=1,P=1。同時設置這些超參數也有一些約定俗成的慣例和經驗,可以在下面的卷積神經網絡結構章節中查看。

卷積層演示:下面是一個卷積層的運行演示。因爲3D數據難以可視化,所以所有的數據(輸入數據體是藍色,權重數據體是紅色,輸出數據體是綠色)都採取將深度切片按照列的方式排列展現。輸入數據體的尺寸是W_1=5,H_1=5,D_1=3,卷積層參數K=2,F=3,S=2,P=1。就是說,有2個濾波器,濾波器的尺寸是3\cdot 3,它們的步長是2.因此,輸出數據體的空間尺寸是(5-3+2)/2+1=3。注意輸入數據體使用了零填充P=1,所以輸入數據體外邊緣一圈都是0。下面的例子在綠色的輸出激活數據上循環演示,展示了其中每個元素都是先通過藍色的輸入數據和紅色的濾波器逐元素相乘,然後求其總和,最後加上偏差得來。

Alt text
用矩陣乘法實現:卷積運算本質上就是在濾波器和輸入數據的局部區域間做點積。卷積層的常用實現方式就是利用這一點,將卷積層的前向傳播變成一個巨大的矩陣乘法:

  1. 輸入圖像的局部區域被im2col操作拉伸爲列。比如,如果輸入是[227x227x3],要與尺寸爲11x11x3的濾波器以步長爲4進行卷積,就取輸入中的[11x11x3]數據塊,然後將其拉伸爲長度爲11x11x3=363的列向量。重複進行這一過程,因爲步長爲4,所以輸出的寬高爲(227-11)/4+1=55,所以得到im2col操作的輸出矩陣X_col的尺寸是[363x3025],其中每列是拉伸的感受野,共有55x55=3,025個。注意因爲感受野之間有重疊,所以輸入數據體中的數字在不同的列中可能有重複。
  2. 卷積層的權重也同樣被拉伸成行。舉例,如果有96個尺寸爲[11x11x3]的濾波器,就生成一個矩陣W_row,尺寸爲[96x363]。
  3. 現在卷積的結果和進行一個大矩陣乘np.dot(W_row, X_col)是等價的了,能得到每個濾波器和每個感受野間的點積。在我們的例子中,這個操作的輸出是[96x3025],給出了每個濾波器在每個位置的點積輸出。
  4. 結果最後必須被重新變爲合理的輸出尺寸[55x55x96]。

這個方法的缺點就是佔用內存太多,因爲在輸入數據體中的某些值在X_col中被複制了多次。但是,其優點是矩陣乘法有非常多的高效實現方式,我們都可以使用(比如常用的BLAS API)。還有,同樣的im2col思路可以用在匯聚操作中。

反向傳播:卷積操作的反向傳播(同時對於數據和權重)還是一個卷積(但是是和空間上翻轉的濾波器)。使用一個1維的例子比較容易演示。

1x1卷積:一些論文中使用了1x1的卷積,這個方法最早是在論文Network in Network中出現。人們剛開始看見這個1x1卷積的時候比較困惑,尤其是那些具有信號處理專業背景的人。因爲信號是2維的,所以1x1卷積就沒有意義。但是,在卷積神經網絡中不是這樣,因爲這裏是對3個維度進行操作,濾波器和輸入數據體的深度是一樣的。比如,如果輸入是[32x32x3],那麼1x1卷積就是在高效地進行3維點積(因爲輸入深度是3個通道)。

擴張卷積:最近一個研究(Fisher Yu和Vladlen Koltun的論文)給卷積層引入了一個新的叫擴張(dilation)的超參數。到目前爲止,我們只討論了卷積層濾波器是連續的情況。但是,讓濾波器中元素之間有間隙也是可以的,這就叫做擴張。舉例,在某個維度上濾波器w的尺寸是3,那麼計算輸入x的方式是:w[0]x[0]+w[1]x[1]+w[2]x[2] ,此時擴張爲0。如果擴張爲1,那麼計算爲:w[0]x[0]+w[1]x[2]+w[2]x[4] 。換句話說,操作中存在1的間隙。在某些設置中,擴張卷積與正常卷積結合起來非常有用,因爲在很少的層數內更快地彙集輸入圖片的大尺度特徵。比如,如果上下重疊2個3x3的卷積層,那麼第二個卷積層的神經元的感受野是輸入數據體中5x5的區域(可以成這些神經元的有效感受野是5x5)。如果我們對卷積進行擴張,那麼這個有效感受野就會迅速增長。

2.2 匯聚層

通常,在連續的卷積層之間會週期性地插入一個匯聚層。它的作用是逐漸降低數據體的空間尺寸,這樣的話就能減少網絡中參數的數量,使得計算資源耗費變少,也能有效控制過擬合。匯聚層使用MAX操作,對輸入數據體的每一個深度切片獨立進行操作,改變它的空間尺寸。最常見的形式是匯聚層使用尺寸2x2的濾波器,以步長爲2來對每個深度切片進行降採樣,將其中75%的激活信息都丟掉。每個MAX操作是從4個數字中取最大值(也就是在深度切片中某個2x2的區域)。深度保持不變。匯聚層的一些公式:

  • 輸入數據體尺寸W1H1D1
  • 有兩個超參數:
    • 空間大小F
    • 步長S
  • 輸出數據體尺寸W2H2D2 , 其中

W2=(W1F)/S+1

H2=(H1F)/S+1

D2=D1

  • 因爲對輸入進行的是固定函數計算,所以沒有引入參數
  • 在匯聚層中很少使用零填充

在實踐中,最大匯聚層通常只有兩種形式:一種是F=3,S=2 ,也叫重疊匯聚(overlapping pooling),另一個更常用的是F=2,S=2 。對更大感受野進行匯聚需要的匯聚尺寸也更大,而且往往對網絡有破壞性。

普通匯聚(General Pooling):除了最大匯聚,匯聚單元還可以使用其他的函數,比如平均匯聚(average pooling)或L-2範式匯聚(L2-norm pooling)。平均匯聚歷史上比較常用,但是現在已經很少使用了。因爲實踐證明,最大匯聚的效果比平均匯聚要好。
Alt text

匯聚層在輸入數據體的每個深度切片上,獨立地對其進行空間上的降採樣。左邊:本例中,輸入數據體尺寸[224x224x64]被降採樣到了[112x112x64],採取的濾波器尺寸是2,步長爲2,而深度不變。右邊:最常用的降採樣操作是取最大值,也就是最大匯聚,這裏步長爲2,每個取最大值操作是從4個數字中選取(即2x2的方塊區域中)。

反向傳播:回顧一下反向傳播的內容,其中max(x,y)函數的反向傳播可以簡單理解爲將梯度只沿最大的數回傳。因此,在向前傳播經過匯聚層的時候,通常會把池中最大元素的索引記錄下來(有時這個也叫作道岔(switches)),這樣在反向傳播的時候梯度的路由就很高效。

不使用匯聚層:很多人不喜歡匯聚操作,認爲可以不使用它。比如在Striving for Simplicity: The All Convolutional Net一文中,提出使用一種只有重複的卷積層組成的結構,拋棄匯聚層。通過在卷積層中使用更大的步長來降低數據體的尺寸。有發現認爲,在訓練一個良好的生成模型時,棄用匯聚層也是很重要的。比如變化自編碼器(VAEs:variational autoencoders)和生成性對抗網絡(GANs:generative adversarial networks)。現在看起來,未來的卷積網絡結構中,可能會很少使用甚至不使用匯聚層。

2.3 歸一化層

在卷積神經網絡的結構中,提出了很多不同類型的歸一化層,有時候是爲了實現在生物大腦中觀測到的抑制機制。但是這些層漸漸都不再流行,因爲實踐證明它們的效果即使存在,也是極其有限的。對於不同類型的歸一化層,可以看看Alex Krizhevsky的關於cuda-convnet library API的討論。

2.4 全連接層

在全連接層中,神經元對於前一層中的所有激活數據是全部連接的,這個常規神經網絡中一樣。它們的激活可以先用矩陣乘法,再加上偏差。更多細節請查看神經網絡章節。

2.5 把全連接層轉化成卷積層

全連接層和卷積層之間唯一的不同就是卷積層中的神經元只與輸入數據中的一個局部區域連接,並且在卷積列中的神經元共享參數。然而在兩類層中,神經元都是計算點積,所以它們的函數形式是一樣的。因此,將此兩者相互轉化是可能的:

  • 對於任一個卷積層,都存在一個能實現和它一樣的前向傳播函數的全連接層。權重矩陣是一個巨大的矩陣,除了某些特定塊(這是因爲有局部連接),其餘部分都是零。而在其中大部分塊中,元素都是相等的(因爲參數共享)。
  • 相反,任何全連接層都可以被轉化爲卷積層。比如,一個K=4096 的全連接層,輸入數據體的尺寸是7×7×512 ,這個全連接層可以被等效地看做一個F=7,P=0,S=1,K=4096 的卷積層。換句話說,就是將濾波器的尺寸設置爲和輸入數據體的尺寸一致了。因爲只有一個單獨的深度列覆蓋並滑過輸入數據體,所以輸出將變成1×1×4096 ,這個結果就和使用初始的那個全連接層一樣了。

全連接層轉化爲卷積層:在兩種變換中,將全連接層轉化爲卷積層在實際運用中更加有用。假設一個卷積神經網絡的輸入是224x224x3的圖像,一系列的卷積層和匯聚層將圖像數據變爲尺寸爲7x7x512的激活數據體(在AlexNet中就是這樣,通過使用5個匯聚層來對輸入數據進行空間上的降採樣,每次尺寸下降一半,所以最終空間尺寸爲224/2/2/2/2/2=7)。從這裏可以看到,AlexNet使用了兩個尺寸爲4096的全連接層,最後一個有1000個神經元的全連接層用於計算分類評分。我們可以將這3個全連接層中的任意一個轉化爲卷積層:

  • 針對第一個連接區域是[7x7x512] 的全連接層,令其濾波器尺寸爲F=7,這樣輸出數據體就爲[1x1x4096] 了。
  • 針對第二個全連接層,令其濾波器尺寸爲F=1,這樣輸出數據體爲[1x1x4096]
  • 對最後一個全連接層也做類似的,令其F=1,最終輸出爲[1x1x1000]

實際操作中,每次這樣的變換都需要把全連接層的權重W重塑成卷積層的濾波器。那麼這樣的轉化有什麼作用呢?它在下面的情況下可以更高效:讓卷積網絡在一張更大的輸入圖片上滑動(:即把一張更大的圖片的不同區域都分別帶入到卷積網絡,得到每個區域的得分),得到多個輸出,這樣的轉化可以讓我們在單個向前傳播的過程中完成上述的操作。

舉個例子,如果我們想讓224x224尺寸的浮窗,以步長爲32在384x384的圖片上滑動,把每個經停的位置都帶入卷積網絡,最後得到6x6個位置的類別得分。上述的把全連接層轉換成卷積層的做法會更簡便。如果224x224的輸入圖片經過卷積層和匯聚層之後得到了[7x7x512]的數組,那麼,384x384的大圖片直接經過同樣的卷積層和匯聚層之後會得到[12x12x512]的數組(因爲途徑5個匯聚層,尺寸變爲384/2/2/2/2/2=12 )。然後再經過上面由3個全連接層轉化得到的3個卷積層,最終得到[6x6x1000]的輸出(因爲(12 - 7)/1 + 1 = 6)。這個結果正是浮窗在原圖經停的6x6個位置的得分!(這一段的翻譯與原文不同,經過了譯者較多的修改,使更容易理解)

面對384x384的圖像,讓(含全連接層)的初始卷積神經網絡以32像素的步長獨立對圖像中的224x224塊進行多次評價,其效果和使用把全連接層變換爲卷積層後的卷積神經網絡進行一次前向傳播是一樣的。

自然,相較於使用被轉化前的原始卷積神經網絡對所有36個位置進行迭代計算,使用轉化後的卷積神經網絡進行一次前向傳播計算要高效得多,因爲36次計算都在共享計算資源。這一技巧在實踐中經常使用,一次來獲得更好的結果。比如,通常將一張圖像尺寸變得更大,然後使用變換後的卷積神經網絡來對空間上很多不同位置進行評價得到分類評分,然後在求這些分值的平均值。

最後,如果我們想用步長小於32的浮窗怎麼辦?用多次的向前傳播就可以解決。比如我們想用步長爲16的浮窗。那麼先使用原圖在轉化後的卷積網絡執行向前傳播,然後分別沿寬度,沿高度,最後同時沿寬度和高度,把原始圖片分別平移16個像素,然後把這些平移之後的圖分別帶入卷積網絡。(譯者注:這一段的翻譯與原文不同,經過了譯者較多的修改,使更容易理解)

  • Net Surgery上一個使用Caffe演示如何在進行變換的IPython Note教程。

3. 卷積神經網絡的結構

卷積神經網絡通常是由三種層構成:卷積層,匯聚層(除非特別說明,一般就是最大值匯聚)和全連接層(簡稱FC)。ReLU激活函數也應該算是是一層,它逐元素地進行激活函數操作。在本節中將討論在卷積神經網絡中這些層通常是如何組合在一起的。

3.1 層的排列規律

卷積神經網絡最常見的形式就是將一些卷積層和ReLU層放在一起,其後緊跟匯聚層,然後重複如此直到圖像在空間上被縮小到一個足夠小的尺寸,在某個地方過渡成成全連接層也較爲常見。最後的全連接層得到輸出,比如分類評分等。換句話說,最常見的卷積神經網絡結構如下:

INPUT>[[CONV>RELU]N>POOL?]M>[FC>RELU]K>FC

其中*指的是重複次數,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。此處在每個匯聚層之間有一個卷積層。
  • INPUT -> [CONV -> RELU -> CONV -> RELU -> POOL]*3 -> [FC -> RELU]*2 -> FC。此處每個匯聚層前有兩個卷積層,這個思路適用於更大更深的網絡,因爲在執行具有破壞性的匯聚操作前,多重的卷積層可以從輸入數據中學習到更多的複雜特徵。

幾個小濾波器卷積層的組合比一個大濾波器卷積層好:假設你一層一層地重疊了3個3x3的卷積層(層與層之間有非線性激活函數)。在這個排列下,第一個卷積層中的每個神經元都對輸入數據體有一個3x3的視野。第二個卷積層上的神經元對第一個卷積層有一個3x3的視野,也就是對輸入數據體有5x5的視野。同樣,在第三個卷積層上的神經元對第二個卷積層有3x3的視野,也就是對輸入數據體有7x7的視野。假設不採用這3個3x3的卷積層,二是使用一個單獨的有7x7的感受野的卷積層,那麼所有神經元的感受野也是7x7,但是就有一些缺點。首先,多個卷積層與非線性的激活層交替的結構,比單一卷積層的結構更能提取出深層的更好的特徵。其次,假設所有的數據有C個通道,那麼單獨的7x7卷積層將會包含C×(7×7×C)=49C2 個參數,而3個3x3的卷積層的組合僅有3×(C×(3×3×C))=27C2 個參數。直觀說來,最好選擇帶有小濾波器的卷積層組合,而不是用一個帶有大的濾波器的卷積層。前者可以表達出輸入數據中更多個強力特徵,使用的參數也更少。唯一的不足是,在進行反向傳播時,中間的卷積層可能會導致佔用更多的內存。

最新進展:傳統的將層按照線性進行排列的方法已經受到了挑戰,挑戰來自谷歌的Inception結構和微軟亞洲研究院的殘差網絡(Residual Net)結構。這兩個網絡(下文案例學習小節中有細節)的特徵更加複雜,連接結構也不同。

3.2 層的尺寸設置規律

到現在爲止,我們都沒有提及卷積神經網絡中每層的超參數的使用。現在先介紹設置結構尺寸的一般性規則,然後根據這些規則進行討論:

輸入層(包含圖像的)應該能被2整除很多次。常用數字包括32(比如CIFAR-10),64,96(比如STL-10)或224(比如ImageNet卷積神經網絡),384和512。

卷積層應該使用小尺寸濾波器(比如3x3或最多5x5),使用步長S=1。還有一點非常重要,就是對輸入數據進行零填充,這樣卷積層就不會改變輸入數據在空間維度上的尺寸。比如,當F=3,那就使用P=1來保持輸入尺寸。當F=5,P=2,一般對於任意F,當P=(F-1)/2的時候能保持輸入尺寸。如果必須使用更大的濾波器尺寸(比如7x7之類),通常只用在第一個面對原始圖像的卷積層上。

匯聚層負責對輸入數據的空間維度進行降採樣。最常用的設置是用用2x2感受野(即F=2)的最大值匯聚,步長爲2(S=2)。注意這一操作將會把輸入數據中75%的激活數據丟棄(因爲對寬度和高度都進行了2的降採樣)。另一個不那麼常用的設置是使用3x3的感受野,步長爲2。最大值匯聚的感受野尺寸很少有超過3的,因爲匯聚操作過於激烈,易造成數據信息丟失,這通常會導致算法性能變差。

減少尺寸設置的問題:上文中展示的兩種設置是很好的,因爲所有的卷積層都能保持其輸入數據的空間尺寸,匯聚層只負責對數據體從空間維度進行降採樣。如果使用的步長大於1並且不對卷積層的輸入數據使用零填充,那麼就必須非常仔細地監督輸入數據體通過整個卷積神經網絡結構的過程,確認所有的步長和濾波器都尺寸互相吻合,卷積神經網絡的結構美妙對稱地聯繫在一起。

爲什麼在卷積層使用1的步長?在實際應用中,更小的步長效果更好。上文也已經提過,步長爲1可以讓空間維度的降採樣全部由匯聚層負責,卷積層只負責對輸入數據體的深度進行變換。

爲何使用零填充?使用零填充除了前面提到的可以讓卷積層的輸出數據保持和輸入數據在空間維度的不變,還可以提高算法性能。如果卷積層值進行卷積而不進行零填充,那麼數據體的尺寸就會略微減小,那麼圖像邊緣的信息就會過快地損失掉。

因爲內存限制所做的妥協:在某些案例(尤其是早期的卷積神經網絡結構)中,基於前面的各種規則,內存的使用量迅速飆升。例如,使用64個尺寸爲3x3的濾波器對224x224x3的圖像進行卷積,零填充爲1,得到的激活數據體尺寸是[224x224x64]。這個數量就是一千萬的激活數據,或者就是72MB的內存(每張圖就是這麼多,激活函數和梯度都是)。因爲GPU通常因爲內存導致性能瓶頸,所以做出一些妥協是必須的。在實踐中,人們傾向於在網絡的第一個卷積層做出妥協。例如,可以妥協可能是在第一個卷積層使用步長爲2,尺寸爲7x7的濾波器(比如在ZFnet中)。在AlexNet中,濾波器的尺寸的11x11,步長爲4。

3.3 案例學習

下面是卷積神經網絡領域中比較有名的幾種結構:

  • LeNet: 第一個成功的卷積神經網絡應用,是Yann LeCun在上世紀90年代實現的。當然,最著名還是被應用在識別數字和郵政編碼等的LeNet結構。
  • AlexNet:AlexNet卷積神經網絡在計算機視覺領域中受到歡迎,它由Alex Krizhevsky,Ilya Sutskever和Geoff Hinton實現。AlexNet在2012年的ImageNet ILSVRC 競賽中奪冠,性能遠遠超出第二名(16%的top5錯誤率,第二名是26%的top5錯誤率)。這個網絡的結構和LeNet非常類似,但是更深更大,並且使用了層疊的卷積層來獲取特徵(之前通常是隻用一個卷積層並且在其後馬上跟着一個匯聚層)。
  • ZF Net:Matthew Zeiler和Rob Fergus發明的網絡在ILSVRC 2013比賽中奪冠,它被稱爲 ZFNet(Zeiler & Fergus Net的簡稱)。它通過修改結構中的超參數來實現對AlexNet的改良,具體說來就是增加了中間卷積層的尺寸,讓第一層的步長和濾波器尺寸更小。
  • GoogLeNet:ILSVRC 2014的勝利者是谷歌的Szeged等實現的卷積神經網絡。它主要的貢獻就是實現了一個奠基模塊,它能夠顯著地減少網絡中參數的數量(AlexNet中有60M,該網絡中只有4M)。還有,這個論文中沒有使用卷積神經網絡頂部使用全連接層,而是使用了一個平均匯聚,把大量不是很重要的參數都去除掉了。GooLeNet還有幾種改進的版本,最新的一個是Inception-v4。
  • VGGNet:ILSVRC 2014的第二名是Karen Simonyan和 Andrew Zisserman實現的卷積神經網絡,現在稱其爲VGGNet。它主要的貢獻是展示出網絡的深度是算法優良性能的關鍵部分。他們最好的網絡包含了16個卷積/全連接層。網絡的結構非常一致,從頭到尾全部使用的是3x3的卷積和2x2的匯聚。他們的預訓練模型是可以在網絡上獲得並在Caffe中使用的。VGGNet不好的一點是它耗費更多計算資源,並且使用了更多的參數,導致更多的內存佔用(140M)。其中絕大多數的參數都是來自於第一個全連接層。後來發現這些全連接層即使被去除,對於性能也沒有什麼影響,這樣就顯著降低了參數數量。
  • ResNet:殘差網絡(Residual Network)是ILSVRC2015的勝利者,由何愷明等實現。它使用了特殊的跳躍鏈接,大量使用了批量歸一化(batch normalization)。這個結構同樣在最後沒有使用全連接層。讀者可以查看何愷明的的演講(視頻,PPT),以及一些使用Torch重現網絡的實驗。ResNet當前最好的卷積神經網絡模型(2016年五月)。何開明等最近的工作是對原始結構做一些優化,可以看論文Identity Mappings in Deep Residual Networks,2016年3月發表。

VGGNet的細節:我們進一步對VGGNet的細節進行分析學習。整個VGGNet中的卷積層都是以步長爲1進行3x3的卷積,使用了1的零填充,匯聚層都是以步長爲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,000

TOTAL memory: 24M * 4 bytes ~= 93MB / image (only forward! ~*2 for bwd)
TOTAL params: 138M parameters

注意,大部分的內存和計算時間都被前面的卷積層佔用,大部分的參數都用在後面的全連接層,這在卷積神經網絡中是比較常見的。在這個例子中,全部參數有140M,但第一個全連接層就包含了100M的參數。

3.4 計算上的考量

在構建卷積神經網絡結構時,最大的瓶頸是內存瓶頸。大部分現代GPU的內存是3/4/6GB,最好的GPU大約有12GB的內存。要注意三種內存佔用來源:

  • 來自中間數據體尺寸:卷積神經網絡中的每一層中都有激活數據體的原始數值,以及損失函數對它們的梯度(和激活數據體尺寸一致)。通常,大部分激活數據都是在網絡中靠前的層中(比如第一個卷積層)。在訓練時,這些數據需要放在內存中,因爲反向傳播的時候還會用到。但是在測試時可以聰明點:讓網絡在測試運行時候每層都只存儲當前的激活數據,然後丟棄前面層的激活數據,這樣就能減少巨大的激活數據量。
  • 來自參數尺寸:即整個網絡的參數的數量,在反向傳播時它們的梯度值,以及使用momentum、Adagrad或RMSProp等方法進行最優化時的每一步計算緩存。因此,存儲參數向量的內存通常需要在參數向量的容量基礎上乘以3或者更多。
    卷積神經網絡實現還有各種零散的內存佔用,比如成批的訓練數據,擴充的數據等等。

一旦對於所有這些數值的數量有了一個大略估計(包含激活數據,梯度和各種雜項),數量應該轉化爲以GB爲計量單位。把這個值乘以4,得到原始的字節數(因爲每個浮點數佔用4個字節,如果是雙精度浮點數那就是佔用8個字節),然後多次除以1024分別得到佔用內存的KB,MB,最後是GB計量。如果你的網絡工作得不好,一個常用的方法是降低批尺寸(batch size),因爲絕大多數的內存都是被激活數據消耗掉了。

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