CNN 反向传播推导
CNN卷積神經(jīng)網(wǎng)絡(luò)推導(dǎo)和實現(xiàn)
本文的論文來自:
Notes on Convolutional Neural Networks, Jake Bouvrie。
這個主要是CNN的推導(dǎo)和實現(xiàn)的一些筆記,再看懂這個筆記之前,最好具有CNN的一些基礎(chǔ)。這里也先列出一個資料供參考:
[1] Deep Learning(深度學(xué)習)學(xué)習筆記整理系列之(七)
[2] LeNet-5, convolutional neural networks
[3]卷積神經(jīng)網(wǎng)絡(luò)
[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中的“卷積特征提取”和“池化”。
另外,這里有個matlab的Deep Learning的toolbox,里面包含了CNN的代碼,在下一個博文中,我將會詳細注釋這個代碼。這個筆記對這個代碼的理解非常重要。
下面是自己對其中的一些知識點的理解:
《Notes on Convolutional Neural Networks》
一、介紹
這個文檔討論的是CNNs的推導(dǎo)和實現(xiàn)。CNN架構(gòu)的連接比權(quán)值要多很多,這實際上就隱含著實現(xiàn)了某種形式的規(guī)則化。這種特別的網(wǎng)絡(luò)假定了我們希望通過數(shù)據(jù)驅(qū)動的方式學(xué)習到一些濾波器,作為提取輸入的特征的一種方法。
本文中,我們先對訓(xùn)練全連接網(wǎng)絡(luò)的經(jīng)典BP算法做一個描述,然后推導(dǎo)2D CNN網(wǎng)絡(luò)的卷積層和子采樣層的BP權(quán)值更新方法。在推導(dǎo)過程中,我們更強調(diào)實現(xiàn)的效率,所以會給出一些Matlab代碼。最后,我們轉(zhuǎn)向討論如何自動地學(xué)習組合前一層的特征maps,特別地,我們還學(xué)習特征maps的稀疏組合。
二、全連接的反向傳播算法
典型的CNN中,開始幾層都是卷積和下采樣的交替,然后在最后一些層(靠近輸出層的),都是全連接的一維網(wǎng)絡(luò)。這時候我們已經(jīng)將所有兩維2D的特征maps轉(zhuǎn)化為全連接的一維網(wǎng)絡(luò)的輸入。這樣,當你準備好將最終的2D特征maps輸入到1D網(wǎng)絡(luò)中時,一個非常方便的方法就是把所有輸出的特征maps連接成一個長的輸入向量。然后我們回到BP算法的討論。(更詳細的基礎(chǔ)推導(dǎo)可以參考UFLDL中“反向傳導(dǎo)算法”)。
2.1、Feedforward Pass前向傳播
在下面的推導(dǎo)中,我們采用平方誤差代價函數(shù)。我們討論的是多類問題,共c類,共N個訓(xùn)練樣本。
這里表示第n個樣本對應(yīng)的標簽的第k維。表示第n個樣本對應(yīng)的網(wǎng)絡(luò)輸出的第k個輸出。對于多類問題,輸出一般組織為“one-of-c”的形式,也就是只有該輸入對應(yīng)的類的輸出節(jié)點輸出為正,其他類的位或者節(jié)點為0或者負數(shù),這個取決于你輸出層的激活函數(shù)。sigmoid就是0,tanh就是-1.
因為在全部訓(xùn)練集上的誤差只是每個訓(xùn)練樣本的誤差的總和,所以這里我們先考慮對于一個樣本的BP。對于第n個樣本的誤差,表示為:
傳統(tǒng)的全連接神經(jīng)網(wǎng)絡(luò)中,我們需要根據(jù)BP規(guī)則計算代價函數(shù)E關(guān)于網(wǎng)絡(luò)每一個權(quán)值的偏導(dǎo)數(shù)。我們用l來表示當前層,那么當前層的輸出可以表示為:
輸出激活函數(shù)f(.)可以有很多種,一般是sigmoid函數(shù)或者雙曲線正切函數(shù)。sigmoid將輸出壓縮到[0, 1],所以最后的輸出平均值一般趨于0 。所以如果將我們的訓(xùn)練數(shù)據(jù)歸一化為零均值和方差為1,可以在梯度下降的過程中增加收斂性。對于歸一化的數(shù)據(jù)集來說,雙曲線正切函數(shù)也是不錯的選擇。
2.2、Backpropagation Pass反向傳播
反向傳播回來的誤差可以看做是每個神經(jīng)元的基的靈敏度sensitivities(靈敏度的意思就是我們的基b變化多少,誤差會變化多少,也就是誤差對基的變化率,也就是導(dǎo)數(shù)了),定義如下:(第二個等號是根據(jù)求導(dǎo)的鏈式法則得到的)
因為?u/?b=1,所以?E/?b=?E/?u=δ,也就是說bias基的靈敏度?E/?b=δ和誤差E對一個節(jié)點全部輸入u的導(dǎo)數(shù)?E/?u是相等的。這個導(dǎo)數(shù)就是讓高層誤差反向傳播到底層的神來之筆。反向傳播就是用下面這條關(guān)系式:(下面這條式子表達的就是第l層的靈敏度,就是)
公式(1)
這里的“?”表示每個元素相乘。輸出層的神經(jīng)元的靈敏度是不一樣的:
最后,對每個神經(jīng)元運用delta(即δ)規(guī)則進行權(quán)值更新。具體來說就是,對一個給定的神經(jīng)元,得到它的輸入,然后用這個神經(jīng)元的delta(即δ)來進行縮放。用向量的形式表述就是,對于第l層,誤差對于該層每一個權(quán)值(組合為矩陣)的導(dǎo)數(shù)是該層的輸入(等于上一層的輸出)與該層的靈敏度(該層每個神經(jīng)元的δ組合成一個向量的形式)的叉乘。然后得到的偏導(dǎo)數(shù)乘以一個負學(xué)習率就是該層的神經(jīng)元的權(quán)值的更新了:
公式(2)
對于bias基的更新表達式差不多。實際上,對于每一個權(quán)值(W)ij都有一個特定的學(xué)習率ηIj。
三、Convolutional Neural Networks 卷積神經(jīng)網(wǎng)絡(luò)
3.1、Convolution Layers 卷積層
我們現(xiàn)在關(guān)注網(wǎng)絡(luò)中卷積層的BP更新。在一個卷積層,上一層的特征maps被一個可學(xué)習的卷積核進行卷積,然后通過一個激活函數(shù),就可以得到輸出特征map。每一個輸出map可能是組合卷積多個輸入maps的值:
這里Mj表示選擇的輸入maps的集合,那么到底選擇哪些輸入maps呢?有選擇一對的或者三個的。但下面我們會討論如何去自動選擇需要組合的特征maps。每一個輸出map會給一個額外的偏置b,但是對于一個特定的輸出map,卷積每個輸入maps的卷積核是不一樣的。也就是說,如果輸出特征map j和輸出特征map k都是從輸入map i中卷積求和得到,那么對應(yīng)的卷積核是不一樣的。
3.1.1、Computing the Gradients梯度計算
我們假定每個卷積層l都會接一個下采樣層l+1 。對于BP來說,根據(jù)上文我們知道,要想求得層l的每個神經(jīng)元對應(yīng)的權(quán)值的權(quán)值更新,就需要先求層l的每一個神經(jīng)節(jié)點的靈敏度δ(也就是權(quán)值更新的公式(2))。為了求這個靈敏度我們就需要先對下一層的節(jié)點(連接到當前層l的感興趣節(jié)點的第l+1層的節(jié)點)的靈敏度求和(得到δl+1),然后乘以這些連接對應(yīng)的權(quán)值(連接第l層感興趣節(jié)點和第l+1層節(jié)點的權(quán)值)W。再乘以當前層l的該神經(jīng)元節(jié)點的輸入u的激活函數(shù)f的導(dǎo)數(shù)值(也就是那個靈敏度反向傳播的公式(1)的δl的求解),這樣就可以得到當前層l每個神經(jīng)節(jié)點對應(yīng)的靈敏度δl了。
然而,因為下采樣的存在,采樣層的一個像素(神經(jīng)元節(jié)點)對應(yīng)的靈敏度δ對應(yīng)于卷積層(上一層)的輸出map的一塊像素(采樣窗口大小)。因此,層l中的一個map的每個節(jié)點只與l+1層中相應(yīng)map的一個節(jié)點連接。
為了有效計算層l的靈敏度,我們需要上采樣upsample 這個下采樣downsample層對應(yīng)的靈敏度map(特征map中每個像素對應(yīng)一個靈敏度,所以也組成一個map),這樣才使得這個靈敏度map大小與卷積層的map大小一致,然后再將層l的map的激活值的偏導(dǎo)數(shù)與從第l+1層的上采樣得到的靈敏度map逐元素相乘(也就是公式(1))。
在下采樣層map的權(quán)值都取一個相同值β,而且是一個常數(shù)。所以我們只需要將上一個步驟得到的結(jié)果乘以一個β就可以完成第l層靈敏度δ的計算。
我們可以對卷積層中每一個特征map j重復(fù)相同的計算過程。但很明顯需要匹配相應(yīng)的子采樣層的map(參考公式(1)):
up(.)表示一個上采樣操作。如果下采樣的采樣因子是n的話,它簡單的將每個像素水平和垂直方向上拷貝n次。這樣就可以恢復(fù)原來的大小了。實際上,這個函數(shù)可以用Kronecker乘積來實現(xiàn):
好,到這里,對于一個給定的map,我們就可以計算得到其靈敏度map了。然后我們就可以通過簡單的對層l中的靈敏度map中所有節(jié)點進行求和快速的計算bias基的梯度了:
公式(3)
最后,對卷積核的權(quán)值的梯度就可以用BP算法來計算了(公式(2))。另外,很多連接的權(quán)值是共享的,因此,對于一個給定的權(quán)值,我們需要對所有與該權(quán)值有聯(lián)系(權(quán)值共享的連接)的連接對該點求梯度,然后對這些梯度進行求和,就像上面對bias基的梯度計算一樣:
這里,是中的在卷積的時候與逐元素相乘的patch,輸出卷積map的(u, v)位置的值是由上一層的(u, v)位置的patch與卷積核k_ij逐元素相乘的結(jié)果。
咋一看,好像我們需要煞費苦心地記住輸出map(和對應(yīng)的靈敏度map)每個像素對應(yīng)于輸入map的哪個patch。但實際上,在Matlab中,可以通過一個代碼就實現(xiàn)。對于上面的公式,可以用Matlab的卷積函數(shù)來實現(xiàn):
我們先對delta靈敏度map進行旋轉(zhuǎn),這樣就可以進行互相關(guān)計算,而不是卷積(在卷積的數(shù)學(xué)定義中,特征矩陣(卷積核)在傳遞給conv2時需要先翻轉(zhuǎn)(flipped)一下。也就是顛倒下特征矩陣的行和列)。然后把輸出反旋轉(zhuǎn)回來,這樣我們在前向傳播進行卷積的時候,卷積核才是我們想要的方向。
3.2、Sub-sampling Layers 子采樣層
對于子采樣層來說,有N個輸入maps,就有N個輸出maps,只是每個輸出map都變小了。
down(.)表示一個下采樣函數(shù)。典型的操作一般是對輸入圖像的不同nxn的塊的所有像素進行求和。這樣輸出圖像在兩個維度上都縮小了n倍。每個輸出map都對應(yīng)一個屬于自己的乘性偏置β和一個加性偏置b。
3.2.1、Computing the Gradients 梯度計算
這里最困難的是計算靈敏度map。一旦我們得到這個了,那我們唯一需要更新的偏置參數(shù)β和b就可以輕而易舉了(公式(3))。如果下一個卷積層與這個子采樣層是全連接的,那么就可以通過BP來計算子采樣層的靈敏度maps。
我們需要計算卷積核的梯度,所以我們必須找到輸入map中哪個patch對應(yīng)輸出map的哪個像素。這里,就是必須找到當前層的靈敏度map中哪個patch對應(yīng)與下一層的靈敏度map的給定像素,這樣才可以利用公式(1)那樣的δ遞推,也就是靈敏度反向傳播回來。另外,需要乘以輸入patch與輸出像素之間連接的權(quán)值,這個權(quán)值實際上就是卷積核的權(quán)值(已旋轉(zhuǎn)的)。
在這之前,我們需要先將核旋轉(zhuǎn)一下,讓卷積函數(shù)可以實施互相關(guān)計算。另外,我們需要對卷積邊界進行處理,但在Matlab里面,就比較容易處理。Matlab中全卷積會對缺少的輸入像素補0 。
到這里,我們就可以對b和β計算梯度了。首先,加性基b的計算和上面卷積層的一樣,對靈敏度map中所有元素加起來就可以了:
而對于乘性偏置β,因為涉及到了在前向傳播過程中下采樣map的計算,所以我們最好在前向的過程中保存好這些maps,這樣在反向的計算中就不用重新計算了。我們定義:
這樣,對β的梯度就可以用下面的方式計算:
3.3、Learning Combinations of Feature Maps 學(xué)習特征map的組合
大部分時候,通過卷積多個輸入maps,然后再對這些卷積值求和得到一個輸出map,這樣的效果往往是比較好的。在一些文獻中,一般是人工選擇哪些輸入maps去組合得到一個輸出map。但我們這里嘗試去讓CNN在訓(xùn)練的過程中學(xué)習這些組合,也就是讓網(wǎng)絡(luò)自己學(xué)習挑選哪些輸入maps來計算得到輸出map才是最好的。我們用αij表示在得到第j個輸出map的其中第i個輸入map的權(quán)值或者貢獻。這樣,第j個輸出map可以表示為:
需要滿足約束:
這些對變量αij的約束可以通過將變量αij表示為一個組無約束的隱含權(quán)值cij的softmax函數(shù)來加強。(因為softmax的因變量是自變量的指數(shù)函數(shù),他們的變化率會不同)。
因為對于一個固定的j來說,每組權(quán)值cij都是和其他組的權(quán)值獨立的,所以為了方面描述,我們把下標j去掉,只考慮一個map的更新,其他map的更新是一樣的過程,只是map的索引j不同而已。
Softmax函數(shù)的導(dǎo)數(shù)表示為:
這里的δ是Kronecker delta。對于誤差對于第l層變量αi的導(dǎo)數(shù)為:
最后就可以通過鏈式規(guī)則去求得代價函數(shù)關(guān)于權(quán)值ci的偏導(dǎo)數(shù)了:
3.3.1、Enforcing Sparse Combinations 加強稀疏性組合
為了限制αi是稀疏的,也就是限制一個輸出map只與某些而不是全部的輸入maps相連。我們在整體代價函數(shù)里增加稀疏約束項?(α)。對于單個樣本,重寫代價函數(shù)為:
然后尋找這個規(guī)則化約束項對權(quán)值ci求導(dǎo)的貢獻。規(guī)則化項?(α)對αi求導(dǎo)是:
然后,通過鏈式法則,對ci的求導(dǎo)是:
所以,權(quán)值ci最后的梯度是:
3.4、Making it Fast with MATLAB
CNN的訓(xùn)練主要是在卷積層和子采樣層的交互上,其主要的計算瓶頸是:
1)前向傳播過程:下采樣每個卷積層的maps;
2)反向傳播過程:上采樣高層子采樣層的靈敏度map,以匹配底層的卷積層輸出maps的大小;
3)sigmoid的運用和求導(dǎo)。
對于第一和第二個問題,我們考慮的是如何用Matlab內(nèi)置的圖像處理函數(shù)去實現(xiàn)上采樣和下采樣的操作。對于上采樣,imresize函數(shù)可以搞定,但需要很大的開銷。一個比較快速的版本是使用Kronecker乘積函數(shù)kron。通過一個全一矩陣ones來和我們需要上采樣的矩陣進行Kronecker乘積,就可以實現(xiàn)上采樣的效果。對于前向傳播過程中的下采樣,imresize并沒有提供在縮小圖像的過程中還計算nxn塊內(nèi)像素的和的功能,所以沒法用。一個比較好和快速的方法是用一個全一的卷積核來卷積圖像,然后簡單的通過標準的索引方法來采樣最后卷積結(jié)果。例如,如果下采樣的域是2x2的,那么我們可以用2x2的元素全是1的卷積核來卷積圖像。然后再卷積后的圖像中,我們每個2個點采集一次數(shù)據(jù),y=x(1:2:end,1:2:end),這樣就可以得到了兩倍下采樣,同時執(zhí)行求和的效果。
對于第三個問題,實際上有些人以為Matlab中對sigmoid函數(shù)進行inline的定義會更快,其實不然,Matlab與C/C++等等語言不一樣,Matlab的inline反而比普通的函數(shù)定義更非時間。所以,我們可以直接在代碼中使用計算sigmoid函數(shù)及其導(dǎo)數(shù)的真實代碼。
轉(zhuǎn)載至:https://blog.csdn.net/zouxy09/article/details/9993371
第二部分
卷積神經(jīng)網(wǎng)絡(luò)的反向傳播:
首先回顧一下一般的前饋神經(jīng)網(wǎng)絡(luò)的反向傳播:
詳細內(nèi)容可參看:神經(jīng)網(wǎng)絡(luò)基礎(chǔ)和反向傳播推導(dǎo)
1,CNN的前向傳播
a)對于卷積層,卷積核與輸入矩陣對應(yīng)位置求積再求和,作為輸出矩陣對應(yīng)位置的值。如果輸入矩陣inputX為M*N大小,卷積核為a*b大小,那么輸出Y為(M-a+1)*(N-b+1)大小。
b)對于池化層,按照池化標準把輸入張量縮小。
c)對于全連接層,按照普通網(wǎng)絡(luò)的前向傳播計算。
2,CNN反向傳播的不同之處:
首先要注意的是,一般神經(jīng)網(wǎng)絡(luò)中每一層輸入輸出a,z都只是一個向量,而CNN中的a,z是一個三維張量,即由若干個輸入的子矩陣組成。其次:
由于卷積層可以有多個卷積核,各個卷積核的處理方法是完全相同且獨立的,為了簡化算法公式的復(fù)雜度,我們下面提到卷積核都是卷積層中若干卷積核中的一個。接下來看具體的CNN反向傳播步驟。
3,已知池化層的誤差,反向推導(dǎo)上一隱藏層的誤差
在前向傳播時,池化層我們會用MAX或者Average對輸入進行池化,池化的區(qū)域大小已知。現(xiàn)在我們反過來,要從縮小后區(qū)域的誤差,還原前一層較大區(qū)域的誤差。這個過程叫做upsample。假設(shè)我們的池化區(qū)域大小是2x2。第l層誤差的第k個子矩陣δlk為:
如果池化區(qū)域表示為a*a大小,那么我們把上述矩陣上下左右各擴展a-1行和列進行還原:
如果是MAX,假設(shè)我們之前在前向傳播時記錄的最大值位置分別是左上,右下,右上,左下,則轉(zhuǎn)換后的矩陣為:
如果是Average,則進行平均,轉(zhuǎn)換后的矩陣為:
上邊這個矩陣就是誤差矩陣經(jīng)過upsample之后的矩陣,那么,由后一層誤差推導(dǎo)出前一層誤差的公式為:
上式和普通網(wǎng)絡(luò)的反向推導(dǎo)誤差很類似:
可以看到,只有第一項不同。
4,已知卷積層的誤差,反向推導(dǎo)上一隱藏層的誤差
公式如下:
我們再看一次普通網(wǎng)絡(luò)的反向推導(dǎo)誤差的公式:
可以看到區(qū)別在于,下一層的權(quán)重w的轉(zhuǎn)置操作,變成了旋轉(zhuǎn)180度的操作,也就是上下翻轉(zhuǎn)一次,左右再翻轉(zhuǎn)一次,這其實就是“卷積”一詞的意義(我們可簡單理解為數(shù)學(xué)上的trick),可參考下圖,Q是下一層的誤差,周圍補0方便計算,W是180度翻轉(zhuǎn)后的卷積核,P是W和Q做卷積的結(jié)果:
5,已知卷積層的誤差,推導(dǎo)該層的W,b的梯度
經(jīng)過以上各步驟,我們已經(jīng)算出每一層的誤差了,那么:
a)對于全連接層,可以按照普通網(wǎng)絡(luò)的反向傳播算法求該層W,b的梯度。
b)對于池化層,它并沒有W,b,也不用求W,b的梯度。
c)只有卷積層的W,b需要求出,先看w:
再對比一下普通網(wǎng)絡(luò)的求w梯度的公式,發(fā)現(xiàn)區(qū)別在于,對前一層的輸出做翻轉(zhuǎn)180度的操作:
而對于b,則稍微有些特殊,因為在CNN中,誤差δ是三維張量,而b只是一個向量,不能像普通網(wǎng)絡(luò)中那樣直接和誤差δ相等。通常的做法是將誤差δ的各個子矩陣的項分別求和,得到一個誤差向量,即為b的梯度:
本文內(nèi)容來自:
1,Michael Nielsen的《Neural Networks and Deep Learning》中文翻譯
2,http://www.cnblogs.com/pinard/p/6494810.html
3,http://blog.csdn.net/yunpiao123456/article/details/52437794
總結(jié)
以上是生活随笔為你收集整理的CNN 反向传播推导的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL使用Amoeba作为Proxy
- 下一篇: 应运而生的web页面响应布局