Notes on Convolutional Neural Networks

CNN卷積神經網絡推導和實現

轉自:http://blog.csdn.net/zouxy09

       本文的論文來自:

Notes on Convolutional Neural Networks, Jake Bouvrie

         這個主要是CNN的推導和實現的一些筆記,再看懂這個筆記之前,最好具有CNN的一些基礎。這裏也先列出一個資料供參考:

[1] Deep Learning(深度學習)學習筆記整理系列之(七)

[2] LeNet-5, convolutional neural networks

[3]卷積神經網絡

[4] Neural Network for Recognition of Handwritten Digits

[5] Deep learning:三十八(Stacked CNN簡單介紹)

[6] Gradient-based learning applied to document recognition.

[7]Imagenet classification with deep convolutional neural networks.

[8] UFLDL中的“卷積特徵提取”和“池化”。

        另外,這裏有個matlabDeep Learningtoolbox,裏面包含了CNN的代碼,在下一個博文中,我將會詳細註釋這個代碼。這個筆記對這個代碼的理解非常重要。

         下面是自己對其中的一些知識點的理解:

 

Notes on Convolutional Neural Networks

一、介紹

         這個文檔討論的是CNNs的推導和實現。CNN架構的連接比權值要多很多,這實際上就隱含着實現了某種形式的規則化。這種特別的網絡假定了我們希望通過數據驅動的方式學習到一些濾波器,作爲提取輸入的特徵的一種方法。

         本文中,我們先對訓練全連接網絡的經典BP算法做一個描述,然後推導2D CNN網絡的卷積層和子採樣層的BP權值更新方法。在推導過程中,我們更強調實現的效率,所以會給出一些Matlab代碼。最後,我們轉向討論如何自動地學習組合前一層的特徵maps,特別地,我們還學習特徵maps的稀疏組合。

 

二、全連接的反向傳播算法

         典型的CNN中,開始幾層都是卷積和下采樣的交替,然後在最後一些層(靠近輸出層的),都是全連接的一維網絡。這時候我們已經將所有兩維2D的特徵maps轉化爲全連接的一維網絡的輸入。這樣,當你準備好將最終的2D特徵maps輸入到1D網絡中時,一個非常方便的方法就是把所有輸出的特徵maps連接成一個長的輸入向量。然後我們回到BP算法的討論。(更詳細的基礎推導可以參考UFLDL中“反向傳導算法”)。

2.1Feedforward Pass前向傳播

         在下面的推導中,我們採用平方誤差代價函數。我們討論的是多類問題,共c類,共N個訓練樣本。

         這裏表示第n個樣本對應的標籤的第k維。表示第n個樣本對應的網絡輸出的第k個輸出。對於多類問題,輸出一般組織爲“one-of-c”的形式,也就是隻有該輸入對應的類的輸出節點輸出爲正,其他類的位或者節點爲0或者負數,這個取決於你輸出層的激活函數。sigmoid就是0tanh就是-1.

         因爲在全部訓練集上的誤差只是每個訓練樣本的誤差的總和,所以這裏我們先考慮對於一個樣本的BP。對於第n個樣本的誤差,表示爲:

       傳統的全連接神經網絡中,我們需要根據BP規則計算代價函數E關於網絡每一個權值的偏導數。我們用l來表示當前層,那麼當前層的輸出可以表示爲:

       輸出激活函數f(.)可以有很多種,一般是sigmoid函數或者雙曲線正切函數。sigmoid將輸出壓縮到[0, 1],所以最後的輸出平均值一般趨於。所以如果將我們的訓練數據歸一化爲零均值和方差爲1,可以在梯度下降的過程中增加收斂性。對於歸一化的數據集來說,雙曲線正切函數也是不錯的選擇。

 

2.2Backpropagation Pass反向傳播

         反向傳播回來的誤差可以看做是每個神經元的基的靈敏度sensitivities(靈敏度的意思就是我們的基b變化多少,誤差會變化多少,也就是誤差對基的變化率,也就是導數了),定義如下:(第二個等號是根據求導的鏈式法則得到的)

         因爲∂u/∂b=1,所以∂E/∂b=∂E/∂u=δ,也就是說bias基的靈敏度∂E/∂b=δ和誤差E對一個節點全部輸入u的導數∂E/∂u是相等的。這個導數就是讓高層誤差反向傳播到底層的神來之筆。反向傳播就是用下面這條關係式:(下面這條式子表達的就是第l層的靈敏度,就是)

 公式(1

         這裏的“”表示每個元素相乘。輸出層的神經元的靈敏度是不一樣的:

         最後,對每個神經元運用delta(即δ)規則進行權值更新。具體來說就是,對一個給定的神經元,得到它的輸入,然後用這個神經元的delta(即δ)來進行縮放。用向量的形式表述就是,對於第l層,誤差對於該層每一個權值(組合爲矩陣)的導數是該層的輸入(等於上一層的輸出)與該層的靈敏度(該層每個神經元的δ組合成一個向量的形式)的叉乘。然後得到的偏導數乘以一個負學習率就是該層的神經元的權值的更新了:

 公式(2

         對於bias基的更新表達式差不多。實際上,對於每一個權值(W)ij都有一個特定的學習率ηIj

 

三、Convolutional Neural Networks 卷積神經網絡

3.1Convolution Layers 卷積層

         我們現在關注網絡中卷積層的BP更新。在一個卷積層,上一層的特徵maps被一個可學習的卷積核進行卷積,然後通過一個激活函數,就可以得到輸出特徵map。每一個輸出map可能是組合卷積多個輸入maps的值:

       這裏Mj表示選擇的輸入maps的集合,那麼到底選擇哪些輸入maps呢?有選擇一對的或者三個的。但下面我們會討論如何去自動選擇需要組合的特徵maps。每一個輸出map會給一個額外的偏置b,但是對於一個特定的輸出map,卷積每個輸入maps的卷積核是不一樣的。也就是說,如果輸出特徵map j和輸出特徵map k都是從輸入map i中卷積求和得到,那麼對應的卷積核是不一樣的。

3.1.1Computing the Gradients梯度計算

         我們假定每個卷積層l都會接一個下采樣層l+1 。對於BP來說,根據上文我們知道,要想求得層l的每個神經元對應的權值的權值更新,就需要先求層l的每一個神經節點的靈敏度δ(也就是權值更新的公式(2))。爲了求這個靈敏度我們就需要先對下一層的節點(連接到當前層l的感興趣節點的第l+1層的節點)的靈敏度求和(得到δl+1),然後乘以這些連接對應的權值(連接第l層感興趣節點和第l+1層節點的權值)W。再乘以當前層l的該神經元節點的輸入u的激活函數f的導數值(也就是那個靈敏度反向傳播的公式(1)的δl的求解),這樣就可以得到當前層l每個神經節點對應的靈敏度δl了。

      然而,因爲下采樣的存在,採樣層的一個像素(神經元節點)對應的靈敏度δ對應於卷積層(上一層)的輸出map的一塊像素(採樣窗口大小)。因此,層l中的一個map的每個節點只與l+1層中相應map的一個節點連接。

     爲了有效計算層l的靈敏度,我們需要上採樣upsample 這個下采樣downsample層對應的靈敏度map(特徵map中每個像素對應一個靈敏度,所以也組成一個map),這樣才使得這個靈敏度map大小與卷積層的map大小一致,然後再將層lmap的激活值的偏導數與從第l+1層的上採樣得到的靈敏度map逐元素相乘(也就是公式(1)。

        在下采樣層map的權值都取一個相同值β,而且是一個常數。所以我們只需要將上一個步驟得到的結果乘以一個β就可以完成第l層靈敏度δ的計算。

       我們可以對卷積層中每一個特徵map j重複相同的計算過程。但很明顯需要匹配相應的子採樣層的map(參考公式(1))

        up(.)表示一個上採樣操作。如果下采樣的採樣因子是n的話,它簡單的將每個像素水平和垂直方向上拷貝n次。這樣就可以恢復原來的大小了。實際上,這個函數可以用Kronecker乘積來實現:

       好,到這裏,對於一個給定的map,我們就可以計算得到其靈敏度map了。然後我們就可以通過簡單的對層l中的靈敏度map中所有節點進行求和快速的計算bias基的梯度了:

 公式(3

       最後,對卷積核的權值的梯度就可以用BP算法來計算了(公式(2)。另外,很多連接的權值是共享的,因此,對於一個給定的權值,我們需要對所有與該權值有聯繫(權值共享的連接)的連接對該點求梯度,然後對這些梯度進行求和,就像上面對bias基的梯度計算一樣:

       這裏,中的在卷積的時候與逐元素相乘的patch,輸出卷積map(u, v)位置的值是由上一層的(u, v)位置的patch與卷積核k_ij逐元素相乘的結果。

      咋一看,好像我們需要煞費苦心地記住輸出map(和對應的靈敏度map)每個像素對應於輸入map的哪個patch。但實際上,在Matlab中,可以通過一個代碼就實現。對於上面的公式,可以用Matlab的卷積函數來實現:

       我們先對delta靈敏度map進行旋轉,這樣就可以進行互相關計算,而不是卷積(在卷積的數學定義中,特徵矩陣(卷積核)在傳遞給conv2時需要先翻轉(flipped)一下。也就是顛倒下特徵矩陣的行和列)。然後把輸出反旋轉回來,這樣我們在前向傳播進行卷積的時候,卷積核纔是我們想要的方向。

 

3.2Sub-sampling Layers 子採樣層

         對於子採樣層來說,有N個輸入maps,就有N個輸出maps,只是每個輸出map都變小了。

        down(.)表示一個下采樣函數。典型的操作一般是對輸入圖像的不同nxn的塊的所有像素進行求和。這樣輸出圖像在兩個維度上都縮小了n倍。每個輸出map都對應一個屬於自己的乘性偏置β和一個加性偏置b

 

3.2.1Computing the Gradients 梯度計算

         這裏最困難的是計算靈敏度map。一旦我們得到這個了,那我們唯一需要更新的偏置參數β和b就可以輕而易舉了(公式(3)。如果下一個卷積層與這個子採樣層是全連接的,那麼就可以通過BP來計算子採樣層的靈敏度maps

         我們需要計算卷積核的梯度,所以我們必須找到輸入map中哪個patch對應輸出map的哪個像素。這裏,就是必須找到當前層的靈敏度map中哪個patch對應與下一層的靈敏度map的給定像素,這樣纔可以利用公式(1那樣的δ遞推,也就是靈敏度反向傳播回來。另外,需要乘以輸入patch與輸出像素之間連接的權值,這個權值實際上就是卷積核的權值(已旋轉的)。

      在這之前,我們需要先將核旋轉一下,讓卷積函數可以實施互相關計算。另外,我們需要對卷積邊界進行處理,但在Matlab裏面,就比較容易處理。Matlab中全卷積會對缺少的輸入像素補

      到這裏,我們就可以對b和β計算梯度了。首先,加性基b的計算和上面卷積層的一樣,對靈敏度map中所有元素加起來就可以了:

       而對於乘性偏置β,因爲涉及到了在前向傳播過程中下采樣map的計算,所以我們最好在前向的過程中保存好這些maps,這樣在反向的計算中就不用重新計算了。我們定義:

這樣,對β的梯度就可以用下面的方式計算:

 

3.3Learning Combinations of Feature Maps 學習特徵map的組合

         大部分時候,通過卷積多個輸入maps,然後再對這些卷積值求和得到一個輸出map,這樣的效果往往是比較好的。在一些文獻中,一般是人工選擇哪些輸入maps去組合得到一個輸出map。但我們這裏嘗試去讓CNN在訓練的過程中學習這些組合,也就是讓網絡自己學習挑選哪些輸入maps來計算得到輸出map纔是最好的。我們用αij表示在得到第j個輸出map的其中第i個輸入map的權值或者貢獻。這樣,第j個輸出map可以表示爲:

         需要滿足約束:

         這些對變量αij的約束可以通過將變量αij表示爲一個組無約束的隱含權值cijsoftmax函數來加強。(因爲softmax的因變量是自變量的指數函數,他們的變化率會不同)。

         因爲對於一個固定的j來說,每組權值cij都是和其他組的權值獨立的,所以爲了方面描述,我們把下標j去掉,只考慮一個map的更新,其他map的更新是一樣的過程,只是map的索引j不同而已。

         Softmax函數的導數表示爲:

        這裏的δ是Kronecker delta。對於誤差對於第l層變量αi的導數爲:

         最後就可以通過鏈式規則去求得代價函數關於權值ci的偏導數了:

 

3.3.1Enforcing Sparse Combinations 加強稀疏性組合

         爲了限制αi是稀疏的,也就是限制一個輸出map只與某些而不是全部的輸入maps相連。我們在整體代價函數裏增加稀疏約束項Ω(α)。對於單個樣本,重寫代價函數爲:

然後尋找這個規則化約束項對權值ci求導的貢獻。規則化項Ω(α)αi求導是:

         然後,通過鏈式法則,對ci的求導是:

         所以,權值ci最後的梯度是:

 

3.4、Making it Fast with MATLAB

        CNN的訓練主要是在卷積層和子採樣層的交互上,其主要的計算瓶頸是:

1)前向傳播過程:下采樣每個卷積層的maps

2)反向傳播過程:上採樣高層子採樣層的靈敏度map,以匹配底層的卷積層輸出maps的大小;

3sigmoid的運用和求導。

         對於第一和第二個問題,我們考慮的是如何用Matlab內置的圖像處理函數去實現上採樣和下采樣的操作。對於上採樣,imresize函數可以搞定,但需要很大的開銷。一個比較快速的版本是使用Kronecker乘積函數kron。通過一個全一矩陣ones來和我們需要上採樣的矩陣進行Kronecker乘積,就可以實現上採樣的效果。對於前向傳播過程中的下采樣,imresize並沒有提供在縮小圖像的過程中還計算nxn塊內像素的和的功能,所以沒法用。一個比較好和快速的方法是用一個全一的卷積核來卷積圖像,然後簡單的通過標準的索引方法來採樣最後卷積結果。例如,如果下采樣的域是2x2的,那麼我們可以用2x2的元素全是1的卷積核來卷積圖像。然後再卷積後的圖像中,我們每個2個點採集一次數據,y=x(1:2:end,1:2:end),這樣就可以得到了兩倍下采樣,同時執行求和的效果。

         對於第三個問題,實際上有些人以爲Matlab中對sigmoid函數進行inline的定義會更快,其實不然,MatlabC/C++等等語言不一樣,Matlabinline反而比普通的函數定義更非時間。所以,我們可以直接在代碼中使用計算sigmoid函數及其導數的真實代碼。


發佈了7 篇原創文章 · 獲贊 13 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章