在上篇中介紹的輸入層與隱含層的連接稱為全連接,如果輸入數據是小塊圖像,比如8×8,那這種方法是可行的,但是如果輸入圖像是96×96,假設隱含層神經元100個,那么就有一百萬個(96×96×100)參數需要學習,向前或向后傳播計算時計算時間也會慢很多。
解決這類問題的一種簡單方法是對隱含單元和輸入單元間的連接加以限制:每個隱含單元僅僅只能連接輸入單元的一部分。例如,每個隱含單元僅僅連接輸入圖像的一小片相鄰區域。這也是卷積神經網絡的基本思想,它是一種特殊的MLP,這個概念是從生物里面演化過來的. 根據Hubel和Wiesel早期在貓的視覺皮層上的工作, 我們知道在視覺皮層上面存在一種細胞的復雜分布,這些細胞對一些局部輸入是很敏感的,它們被成為感知野, 並通過這種特殊的組合方式來覆蓋整個視野. 這些過濾器對輸入空間是局部敏感的,因此能夠更好得發覺自然圖像中不同物體的空間相關性。
對一副圖片的局部特性的提取在整個視野上是可重復的,比如我們在96×96圖像中選取8×8作為樣本,在樣本上學習到的特征應用到整幅圖像上,即利用8×8中學習到的特征在96×96圖像上做卷積,從而獲得不同的特征值,所需要學習的參數也從96×96降到了8×8,這樣成為一個特征圖。我們需要從一副圖像上學習的特征肯定不止一種,所以需要建立n個特征圖來學習不同的特征。即使是這樣,算法的復雜度也比之前全連接的方法大大的降低了。
上圖即一個特征圖的三個隱藏神經元對m-1層輸入做卷積,其中共享三個w參數。
卷積神經網絡的另外一個步驟是池化(pooling),它把輸入圖像分割成不重疊的矩形,對於每個矩形取最大值(max pooling),另外一種池化方法叫做(mean pooling),它對每個矩形取平均值。
池化的優勢是(1)它降低了上層的計算復雜度 (2)它提供了一種變換不變量的。對於第二種益處,我們可以假設把一個池化層和一個卷積層組合起來,對於單個像素,輸入圖像可以有8個方向的變換。如果共有最大層在2*2的窗口上面實現,這8個可能的配置中,有3個可以准確的產生和卷積層相同的結果。如果窗口變成3*3,則產生精確結果的概率變成5/8.可見,池化對位置信息提供了附加的魯棒性,它以一種非常聰明的方式減少了中間表示的維度。下圖就是簡單的池化過程,右側矩陣的每個值是左側每個紅色矩陣中元素的最大值(或平均值)。
下圖是卷積層和池化層的結合,相同顏色的神經元共享權重。
卷積神經網絡的訓練過程與全聯通神經網絡類似,首先隨機初始化參數,再利用向后傳導算法來訓練參數。CNN反向傳播求導時的具體過程可以參考論文Notes on Convolutional Neural Networks, Jake Bouvrie,該論文講得很全面。
下面是tornadomeet的博客中對CNN反向傳播過程的介紹,很通俗易懂。
問題二:當接在卷積層的下一層為pooling層時,求卷積層的誤差敏感項。
假設第l(小寫的l,不要看成數字'1'了)層為卷積層,第l+1層為pooling層,且pooling層的誤差敏感項為: ,卷積層的誤差敏感項為:
, 則兩者的關系表達式為:
這里符號●表示的是矩陣的點積操作,即對應元素的乘積。卷積層和unsample()后的pooling層節點是一一對應的,所以下標都是用j表示。后面的符號表示的是第l層第j個節點處激發函數的導數(對節點輸入的導數)。
其中的函數unsample()為上采樣過程,其具體的操作得看是采用的什么pooling方法了。但unsample的大概思想為:pooling層的每個節點是由卷積層中多個節點(一般為一個矩形區域)共同計算得到,所以pooling層每個節點的誤差敏感值也是由卷積層中多個節點的誤差敏感值共同產生的,只需滿足兩層見各自的誤差敏感值相等,下面以mean-pooling和max-pooling為例來說明。
假設卷積層的矩形大小為4×4, pooling區域大小為2×2, 很容易知道pooling后得到的矩形大小也為2*2(本文默認pooling過程是沒有重疊的,卷積過程是每次移動一個像素,即是有重疊的,后續不再聲明),如果此時pooling后的矩形誤差敏感值如下:
則按照mean-pooling,首先得到的卷積層應該是4×4大小,其值分布為(等值復制):
因為得滿足反向傳播時各層見誤差敏感總和不變,所以卷積層對應每個值需要平攤(除以pooling區域大小即可,這里pooling層大小為2×2=4)),最后的卷積層值
分布為:
mean-pooling時的unsample操作可以使用matalb中的函數kron()來實現,因為是采用的矩陣Kronecker乘積。C=kron(A, B)表示的是矩陣B分別與矩陣A中每個元素相乘,然后將相乘的結果放在C中對應的位置。比如:
如果是max-pooling,則需要記錄前向傳播過程中pooling區域中最大值的位置,這里假設pooling層值1,3,2,4對應的pooling區域位置分別為右下、右上、左上、左下。則此時對應卷積層誤差敏感值分布為:
當然了,上面2種結果還需要點乘卷積層激發函數對應位置的導數值了,這里省略掉。
問題三:當接在pooling層的下一層為卷積層時,求該pooling層的誤差敏感項。
假設第l層(pooling層)有N個通道,即有N張特征圖,第l+1層(卷積層)有M個特征,l層中每個通道圖都對應有自己的誤差敏感值,其計算依據為第l+1層所有特征核的貢獻之和。下面是第l+1層中第j個核對第l層第i個通道的誤差敏感值計算方法:
符號★表示的是矩陣的卷積操作,這是真正意義上的離散卷積,不同於卷積層前向傳播時的相關操作,因為嚴格意義上來講,卷積神經網絡中的卷積操作本質是一個相關操作,並不是卷積操作,只不過它可以用卷積的方法去實現才這樣叫。而求第i個通道的誤差敏感項時需要將l+1層的所有核都計算一遍,然后求和。另外因為這里默認pooling層是線性激發函數,所以后面沒有乘相應節點的導數。
舉個簡單的例子,假設拿出第l層某個通道圖,大小為3×3,第l+1層有2個特征核,核大小為2×2,則在前向傳播卷積時第l+1層會有2個大小為2×2的卷積圖。如果2個特征核分別為:
反向傳播求誤差敏感項時,假設已經知道第l+1層2個卷積圖的誤差敏感值:
離散卷積函數conv2()的實現相關子操作時需先將核旋轉180度(即左右翻轉后上下翻轉),但這里實現的是嚴格意義上的卷積,所以在用conv2()時,對應的參數核不需要翻轉(在有些toolbox里面,求這個問題時用了旋轉,那是因為它們已經把所有的卷積核都旋轉過,這樣在前向傳播時的相關操作就不用旋轉了。並不矛盾)。且這時候該函數需要采用'full'模式,所以最終得到的矩陣大小為3×3,(其中3=2+2-1),剛好符第l層通道圖的大小。采用'full'模式需先將第l+1層2個卷積圖擴充,周圍填0,padding后如下:
擴充后的矩陣和對應的核進行卷積的結果如下情況:
可以通過手動去驗證上面的結果,因為是離散卷積操作,而離散卷積等價於將核旋轉后再進行相關操作。而第l層那個通道的誤差敏感項為上面2者的和,呼應問題三,最終答案為:
那么這樣問題3這樣解的依據是什么呢?其實很簡單,本質上還是bp算法,即第l層的誤差敏感值等於第l+1層的誤差敏感值乘以兩者之間的權值,只不過這里由於是用了卷積,且是有重疊的,l層中某個點會對l+1層中的多個點有影響。比如說最終的結果矩陣中最中間那個0.3是怎么來的呢?在用2×2的核對3×3的輸入矩陣進行卷積時,一共進行了4次移動,而3×3矩陣最中間那個值在4次移動中均對輸出結果有影響,且4次的影響分別在右下角、左下角、右上角、左上角。所以它的值為2×0.2+1×0.1+1×0.1-1×0.3=0.3, 建議大家用筆去算一下,那樣就可以明白為什么這里可以采用帶'full'類型的conv2()實現。
問題四:求與卷積層相連那層的權值、偏置值導數。
前面3個問題分別求得了輸出層的誤差敏感值、從pooling層推斷出卷積層的誤差敏感值、從卷積層推斷出pooling層的誤差敏感值。下面需要利用這些誤差敏感值模型中參數的導數。這里沒有考慮pooling層的非線性激發,因此pooling層前面是沒有權值的,也就沒有所謂的權值的導數了。現在將主要精力放在卷積層前面權值的求導上(也就是問題四)。
假設現在需要求第l層的第i個通道,與第l+1層的第j個通道之間的權值和偏置的導數,則計算公式如下:
其中符號⊙表示矩陣的相關操作,可以采用conv2()函數實現。在使用該函數時,需將第l+1層第j個誤差敏感值翻轉。
例如,如果第l層某個通道矩陣i大小為4×4,如下:
第l+1層第j個特征的誤差敏感值矩陣大小為3×3,如下:
很明顯,這時候的特征Kij導數的大小為2×2的,且其結果為:
而此時偏置值bj的導數為1.2 ,將j區域的誤差敏感值相加即可(0.8+0.1-0.6+0.3+0.5+0.7-0.4-0.2=1.2),因為b對j中的每個節點都有貢獻,按照多項式的求導規則(和的導數等於導數的和)很容易得到。
為什么采用矩陣的相關操作就可以實現這個功能呢?由bp算法可知,l層i和l+1層j之間的權值等於l+1層j處誤差敏感值乘以l層i處的輸入,而j中某個節點因為是由i+1中一個區域與權值卷積后所得,所以j處該節點的誤差敏感值對權值中所有元素都有貢獻,由此可見,將j中每個元素對權值的貢獻(尺寸和核大小相同)相加,就得到了權值的偏導數了(這個例子的結果是由9個2×2大小的矩陣之和),同樣,如果大家動筆去推算一下,就會明白這時候為什么可以用帶'valid'的conv2()完成此功能。
池化矩陣一般為2×2,對於非常大的圖像可能會使用4×4,但取值時要謹慎,這可能會損失掉太多的輸入信息。
由於特征圖中神經元的數量會隨着層數的深入而減小,靠近輸入層的特征圖數量應該更多點來平衡每一層的計算量,為了不使輸入信息丟失,應該使每一層的激活值數量(特征圖數量乘以像素值)一致,特征圖數量大小取決於算法的復雜度,數量太多會使得計算復雜度大幅上升。