CNN卷积神经网络推导和实现
《Notes on Convolutional Neural Networks》
一、介紹
???????? 這個(gè)文檔討論的是CNNs的推導(dǎo)和實(shí)現(xiàn)。CNN架構(gòu)的連接比權(quán)值要多很多,這實(shí)際上就隱含著實(shí)現(xiàn)了某種形式的規(guī)則化。這種特別的網(wǎng)絡(luò)假定了我們希望通過(guò)數(shù)據(jù)驅(qū)動(dòng)的方式學(xué)習(xí)到一些濾波器,作為提取輸入的特征的一種方法。
???????? 本文中,我們先對(duì)訓(xùn)練全連接網(wǎng)絡(luò)的經(jīng)典BP算法做一個(gè)描述,然后推導(dǎo)2D CNN網(wǎng)絡(luò)的卷積層和子采樣層的BP權(quán)值更新方法。在推導(dǎo)過(guò)程中,我們更強(qiáng)調(diào)實(shí)現(xiàn)的效率,所以會(huì)給出一些Matlab代碼。最后,我們轉(zhuǎn)向討論如何自動(dòng)地學(xué)習(xí)組合前一層的特征maps,特別地,我們還學(xué)習(xí)特征maps的稀疏組合。
?
二、全連接的反向傳播算法
???????? 典型的CNN中,開(kāi)始幾層都是卷積和下采樣的交替,然后在最后一些層(靠近輸出層的),都是全連接的一維網(wǎng)絡(luò)。這時(shí)候我們已經(jīng)將所有兩維2D的特征maps轉(zhuǎn)化為全連接的一維網(wǎng)絡(luò)的輸入。這樣,當(dāng)你準(zhǔn)備好將最終的2D特征maps輸入到1D網(wǎng)絡(luò)中時(shí),一個(gè)非常方便的方法就是把所有輸出的特征maps連接成一個(gè)長(zhǎng)的輸入向量。然后我們回到BP算法的討論。(更詳細(xì)的基礎(chǔ)推導(dǎo)可以參考UFLDL中“反向傳導(dǎo)算法”)。
2.1、Feedforward Pass前向傳播
???????? 在下面的推導(dǎo)中,我們采用平方誤差代價(jià)函數(shù)。我們討論的是多類問(wèn)題,共c類,共N個(gè)訓(xùn)練樣本。
???????? 這里表示第n個(gè)樣本對(duì)應(yīng)的標(biāo)簽的第k維。表示第n個(gè)樣本對(duì)應(yīng)的網(wǎng)絡(luò)輸出的第k個(gè)輸出。對(duì)于多類問(wèn)題,輸出一般組織為“one-of-c”的形式,也就是只有該輸入對(duì)應(yīng)的類的輸出節(jié)點(diǎn)輸出為正,其他類的位或者節(jié)點(diǎn)為0或者負(fù)數(shù),這個(gè)取決于你輸出層的激活函數(shù)。sigmoid就是0,tanh就是-1.
???????? 因?yàn)樵谌坑?xùn)練集上的誤差只是每個(gè)訓(xùn)練樣本的誤差的總和,所以這里我們先考慮對(duì)于一個(gè)樣本的BP。對(duì)于第n個(gè)樣本的誤差,表示為:
?????? 傳統(tǒng)的全連接神經(jīng)網(wǎng)絡(luò)中,我們需要根據(jù)BP規(guī)則計(jì)算代價(jià)函數(shù)E關(guān)于網(wǎng)絡(luò)每一個(gè)權(quán)值的偏導(dǎo)數(shù)。我們用l來(lái)表示當(dāng)前層,那么當(dāng)前層的輸出可以表示為:
?????? 輸出激活函數(shù)f(.)可以有很多種,一般是sigmoid函數(shù)或者雙曲線正切函數(shù)。sigmoid將輸出壓縮到[0, 1],所以最后的輸出平均值一般趨于0 。所以如果將我們的訓(xùn)練數(shù)據(jù)歸一化為零均值和方差為1,可以在梯度下降的過(guò)程中增加收斂性。對(duì)于歸一化的數(shù)據(jù)集來(lái)說(shuō),雙曲線正切函數(shù)也是不錯(cuò)的選擇。
?
2.2、Backpropagation Pass反向傳播
???????? 反向傳播回來(lái)的誤差可以看做是每個(gè)神經(jīng)元的基的靈敏度sensitivities(靈敏度的意思就是我們的基b變化多少,誤差會(huì)變化多少,也就是誤差對(duì)基的變化率,也就是導(dǎo)數(shù)了),定義如下:(第二個(gè)等號(hào)是根據(jù)求導(dǎo)的鏈?zhǔn)椒▌t得到的)
???????? 因?yàn)?span style="font-family:Calibri">?u/?b=1,所以?E/?b=?E/?u=δ,也就是說(shuō)bias基的靈敏度?E/?b=δ和誤差E對(duì)一個(gè)節(jié)點(diǎn)全部輸入u的導(dǎo)數(shù)?E/?u是相等的。這個(gè)導(dǎo)數(shù)就是讓高層誤差反向傳播到底層的神來(lái)之筆。反向傳播就是用下面這條關(guān)系式:(下面這條式子表達(dá)的就是第l層的靈敏度,就是)
?公式(1)
???????? 這里的“?”表示每個(gè)元素相乘。輸出層的神經(jīng)元的靈敏度是不一樣的:
???????? 最后,對(duì)每個(gè)神經(jīng)元運(yùn)用delta(即δ)規(guī)則進(jìn)行權(quán)值更新。具體來(lái)說(shuō)就是,對(duì)一個(gè)給定的神經(jīng)元,得到它的輸入,然后用這個(gè)神經(jīng)元的delta(即δ)來(lái)進(jìn)行縮放。用向量的形式表述就是,對(duì)于第l層,誤差對(duì)于該層每一個(gè)權(quán)值(組合為矩陣)的導(dǎo)數(shù)是該層的輸入(等于上一層的輸出)與該層的靈敏度(該層每個(gè)神經(jīng)元的δ組合成一個(gè)向量的形式)的叉乘。然后得到的偏導(dǎo)數(shù)乘以一個(gè)負(fù)學(xué)習(xí)率就是該層的神經(jīng)元的權(quán)值的更新了:
?公式(2)
???????? 對(duì)于bias基的更新表達(dá)式差不多。實(shí)際上,對(duì)于每一個(gè)權(quán)值(W)ij都有一個(gè)特定的學(xué)習(xí)率ηIj。
?
三、Convolutional Neural Networks 卷積神經(jīng)網(wǎng)絡(luò)
3.1、Convolution Layers 卷積層
???????? 我們現(xiàn)在關(guān)注網(wǎng)絡(luò)中卷積層的BP更新。在一個(gè)卷積層,上一層的特征maps被一個(gè)可學(xué)習(xí)的卷積核進(jìn)行卷積,然后通過(guò)一個(gè)激活函數(shù),就可以得到輸出特征map。每一個(gè)輸出map可能是組合卷積多個(gè)輸入maps的值:
?????? 這里Mj表示選擇的輸入maps的集合,那么到底選擇哪些輸入maps呢?有選擇一對(duì)的或者三個(gè)的。但下面我們會(huì)討論如何去自動(dòng)選擇需要組合的特征maps。每一個(gè)輸出map會(huì)給一個(gè)額外的偏置b,但是對(duì)于一個(gè)特定的輸出map,卷積每個(gè)輸入maps的卷積核是不一樣的。也就是說(shuō),如果輸出特征map j和輸出特征map k都是從輸入map i中卷積求和得到,那么對(duì)應(yīng)的卷積核是不一樣的。
3.1.1、Computing the Gradients梯度計(jì)算
???????? 我們假定每個(gè)卷積層l都會(huì)接一個(gè)下采樣層l+1 。對(duì)于BP來(lái)說(shuō),根據(jù)上文我們知道,要想求得層l的每個(gè)神經(jīng)元對(duì)應(yīng)的權(quán)值的權(quán)值更新,就需要先求層l的每一個(gè)神經(jīng)節(jié)點(diǎn)的靈敏度δ(也就是權(quán)值更新的公式(2))。為了求這個(gè)靈敏度我們就需要先對(duì)下一層的節(jié)點(diǎn)(連接到當(dāng)前層l的感興趣節(jié)點(diǎn)的第l+1層的節(jié)點(diǎn))的靈敏度求和(得到δl+1),然后乘以這些連接對(duì)應(yīng)的權(quán)值(連接第l層感興趣節(jié)點(diǎn)和第l+1層節(jié)點(diǎn)的權(quán)值)W。再乘以當(dāng)前層l的該神經(jīng)元節(jié)點(diǎn)的輸入u的激活函數(shù)f的導(dǎo)數(shù)值(也就是那個(gè)靈敏度反向傳播的公式(1)的δl的求解),這樣就可以得到當(dāng)前層l每個(gè)神經(jīng)節(jié)點(diǎn)對(duì)應(yīng)的靈敏度δl了。
????? 然而,因?yàn)橄虏蓸拥拇嬖?#xff0c;采樣層的一個(gè)像素(神經(jīng)元節(jié)點(diǎn))對(duì)應(yīng)的靈敏度δ對(duì)應(yīng)于卷積層(上一層)的輸出map的一塊像素(采樣窗口大小)。因此,層l中的一個(gè)map的每個(gè)節(jié)點(diǎn)只與l+1層中相應(yīng)map的一個(gè)節(jié)點(diǎn)連接。
???? 為了有效計(jì)算層l的靈敏度,我們需要上采樣upsample 這個(gè)下采樣downsample層對(duì)應(yīng)的靈敏度map(特征map中每個(gè)像素對(duì)應(yīng)一個(gè)靈敏度,所以也組成一個(gè)map),這樣才使得這個(gè)靈敏度map大小與卷積層的map大小一致,然后再將層l的map的激活值的偏導(dǎo)數(shù)與從第l+1層的上采樣得到的靈敏度map逐元素相乘(也就是公式(1))。
??????? 在下采樣層map的權(quán)值都取一個(gè)相同值β,而且是一個(gè)常數(shù)。所以我們只需要將上一個(gè)步驟得到的結(jié)果乘以一個(gè)β就可以完成第l層靈敏度δ的計(jì)算。
?????? 我們可以對(duì)卷積層中每一個(gè)特征map j重復(fù)相同的計(jì)算過(guò)程。但很明顯需要匹配相應(yīng)的子采樣層的map(參考公式(1)):
??????? up(.)表示一個(gè)上采樣操作。如果下采樣的采樣因子是n的話,它簡(jiǎn)單的將每個(gè)像素水平和垂直方向上拷貝n次。這樣就可以恢復(fù)原來(lái)的大小了。實(shí)際上,這個(gè)函數(shù)可以用Kronecker乘積來(lái)實(shí)現(xiàn):
?????? 好,到這里,對(duì)于一個(gè)給定的map,我們就可以計(jì)算得到其靈敏度map了。然后我們就可以通過(guò)簡(jiǎn)單的對(duì)層l中的靈敏度map中所有節(jié)點(diǎn)進(jìn)行求和快速的計(jì)算bias基的梯度了:
?公式(3)
?????? 最后,對(duì)卷積核的權(quán)值的梯度就可以用BP算法來(lái)計(jì)算了(公式(2))。另外,很多連接的權(quán)值是共享的,因此,對(duì)于一個(gè)給定的權(quán)值,我們需要對(duì)所有與該權(quán)值有聯(lián)系(權(quán)值共享的連接)的連接對(duì)該點(diǎn)求梯度,然后對(duì)這些梯度進(jìn)行求和,就像上面對(duì)bias基的梯度計(jì)算一樣:
?????? 這里,是中的在卷積的時(shí)候與逐元素相乘的patch,輸出卷積map的(u, v)位置的值是由上一層的(u, v)位置的patch與卷積核k_ij逐元素相乘的結(jié)果。
????? 咋一看,好像我們需要煞費(fèi)苦心地記住輸出map(和對(duì)應(yīng)的靈敏度map)每個(gè)像素對(duì)應(yīng)于輸入map的哪個(gè)patch。但實(shí)際上,在Matlab中,可以通過(guò)一個(gè)代碼就實(shí)現(xiàn)。對(duì)于上面的公式,可以用Matlab的卷積函數(shù)來(lái)實(shí)現(xiàn):
?????? 我們先對(duì)delta靈敏度map進(jìn)行旋轉(zhuǎn),這樣就可以進(jìn)行互相關(guān)計(jì)算,而不是卷積(在卷積的數(shù)學(xué)定義中,特征矩陣(卷積核)在傳遞給conv2時(shí)需要先翻轉(zhuǎn)(flipped)一下。也就是顛倒下特征矩陣的行和列)。然后把輸出反旋轉(zhuǎn)回來(lái),這樣我們?cè)谇跋騻鞑ミM(jìn)行卷積的時(shí)候,卷積核才是我們想要的方向。
?
3.2、Sub-sampling Layers 子采樣層
???????? 對(duì)于子采樣層來(lái)說(shuō),有N個(gè)輸入maps,就有N個(gè)輸出maps,只是每個(gè)輸出map都變小了。
??????? down(.)表示一個(gè)下采樣函數(shù)。典型的操作一般是對(duì)輸入圖像的不同nxn的塊的所有像素進(jìn)行求和。這樣輸出圖像在兩個(gè)維度上都縮小了n倍。每個(gè)輸出map都對(duì)應(yīng)一個(gè)屬于自己的乘性偏置β和一個(gè)加性偏置b。
?
3.2.1、Computing the Gradients 梯度計(jì)算
???????? 這里最困難的是計(jì)算靈敏度map。一旦我們得到這個(gè)了,那我們唯一需要更新的偏置參數(shù)β和b就可以輕而易舉了(公式(3))。如果下一個(gè)卷積層與這個(gè)子采樣層是全連接的,那么就可以通過(guò)BP來(lái)計(jì)算子采樣層的靈敏度maps。
???????? 我們需要計(jì)算卷積核的梯度,所以我們必須找到輸入map中哪個(gè)patch對(duì)應(yīng)輸出map的哪個(gè)像素。這里,就是必須找到當(dāng)前層的靈敏度map中哪個(gè)patch對(duì)應(yīng)與下一層的靈敏度map的給定像素,這樣才可以利用公式(1)那樣的δ遞推,也就是靈敏度反向傳播回來(lái)。另外,需要乘以輸入patch與輸出像素之間連接的權(quán)值,這個(gè)權(quán)值實(shí)際上就是卷積核的權(quán)值(已旋轉(zhuǎn)的)。
????? 在這之前,我們需要先將核旋轉(zhuǎn)一下,讓卷積函數(shù)可以實(shí)施互相關(guān)計(jì)算。另外,我們需要對(duì)卷積邊界進(jìn)行處理,但在Matlab里面,就比較容易處理。Matlab中全卷積會(huì)對(duì)缺少的輸入像素補(bǔ)0 。
????? 到這里,我們就可以對(duì)b和β計(jì)算梯度了。首先,加性基b的計(jì)算和上面卷積層的一樣,對(duì)靈敏度map中所有元素加起來(lái)就可以了:
?????? 而對(duì)于乘性偏置β,因?yàn)樯婕暗搅嗽谇跋騻鞑ミ^(guò)程中下采樣map的計(jì)算,所以我們最好在前向的過(guò)程中保存好這些maps,這樣在反向的計(jì)算中就不用重新計(jì)算了。我們定義:
這樣,對(duì)β的梯度就可以用下面的方式計(jì)算:
?
3.3、Learning Combinations of Feature Maps 學(xué)習(xí)特征map的組合
???????? 大部分時(shí)候,通過(guò)卷積多個(gè)輸入maps,然后再對(duì)這些卷積值求和得到一個(gè)輸出map,這樣的效果往往是比較好的。在一些文獻(xiàn)中,一般是人工選擇哪些輸入maps去組合得到一個(gè)輸出map。但我們這里嘗試去讓CNN在訓(xùn)練的過(guò)程中學(xué)習(xí)這些組合,也就是讓網(wǎng)絡(luò)自己學(xué)習(xí)挑選哪些輸入maps來(lái)計(jì)算得到輸出map才是最好的。我們用αij表示在得到第j個(gè)輸出map的其中第i個(gè)輸入map的權(quán)值或者貢獻(xiàn)。這樣,第j個(gè)輸出map可以表示為:
???????? 需要滿足約束:
???????? 這些對(duì)變量αij的約束可以通過(guò)將變量αij表示為一個(gè)組無(wú)約束的隱含權(quán)值cij的softmax函數(shù)來(lái)加強(qiáng)。(因?yàn)?span style="font-family:Calibri">softmax的因變量是自變量的指數(shù)函數(shù),他們的變化率會(huì)不同)。
???????? 因?yàn)閷?duì)于一個(gè)固定的j來(lái)說(shuō),每組權(quán)值cij都是和其他組的權(quán)值獨(dú)立的,所以為了方面描述,我們把下標(biāo)j去掉,只考慮一個(gè)map的更新,其他map的更新是一樣的過(guò)程,只是map的索引j不同而已。
???????? Softmax函數(shù)的導(dǎo)數(shù)表示為:
??????? 這里的δ是Kronecker delta。對(duì)于誤差對(duì)于第l層變量αi的導(dǎo)數(shù)為:
???????? 最后就可以通過(guò)鏈?zhǔn)揭?guī)則去求得代價(jià)函數(shù)關(guān)于權(quán)值ci的偏導(dǎo)數(shù)了:
?
3.3.1、Enforcing Sparse Combinations 加強(qiáng)稀疏性組合
???????? 為了限制αi是稀疏的,也就是限制一個(gè)輸出map只與某些而不是全部的輸入maps相連。我們?cè)谡w代價(jià)函數(shù)里增加稀疏約束項(xiàng)?(α)。對(duì)于單個(gè)樣本,重寫代價(jià)函數(shù)為:
然后尋找這個(gè)規(guī)則化約束項(xiàng)對(duì)權(quán)值ci求導(dǎo)的貢獻(xiàn)。規(guī)則化項(xiàng)?(α)對(duì)αi求導(dǎo)是:
???????? 然后,通過(guò)鏈?zhǔn)椒▌t,對(duì)ci的求導(dǎo)是:
???????? 所以,權(quán)值ci最后的梯度是:
?
3.4、Making it Fast with MATLAB
??????? CNN的訓(xùn)練主要是在卷積層和子采樣層的交互上,其主要的計(jì)算瓶頸是:
1)前向傳播過(guò)程:下采樣每個(gè)卷積層的maps;
2)反向傳播過(guò)程:上采樣高層子采樣層的靈敏度map,以匹配底層的卷積層輸出maps的大小;
3)sigmoid的運(yùn)用和求導(dǎo)。
???????? 對(duì)于第一和第二個(gè)問(wèn)題,我們考慮的是如何用Matlab內(nèi)置的圖像處理函數(shù)去實(shí)現(xiàn)上采樣和下采樣的操作。對(duì)于上采樣,imresize函數(shù)可以搞定,但需要很大的開(kāi)銷。一個(gè)比較快速的版本是使用Kronecker乘積函數(shù)kron。通過(guò)一個(gè)全一矩陣ones來(lái)和我們需要上采樣的矩陣進(jìn)行Kronecker乘積,就可以實(shí)現(xiàn)上采樣的效果。對(duì)于前向傳播過(guò)程中的下采樣,imresize并沒(méi)有提供在縮小圖像的過(guò)程中還計(jì)算nxn塊內(nèi)像素的和的功能,所以沒(méi)法用。一個(gè)比較好和快速的方法是用一個(gè)全一的卷積核來(lái)卷積圖像,然后簡(jiǎn)單的通過(guò)標(biāo)準(zhǔn)的索引方法來(lái)采樣最后卷積結(jié)果。例如,如果下采樣的域是2x2的,那么我們可以用2x2的元素全是1的卷積核來(lái)卷積圖像。然后再卷積后的圖像中,我們每個(gè)2個(gè)點(diǎn)采集一次數(shù)據(jù),y=x(1:2:end,1:2:end),這樣就可以得到了兩倍下采樣,同時(shí)執(zhí)行求和的效果。
???????? 對(duì)于第三個(gè)問(wèn)題,實(shí)際上有些人以為Matlab中對(duì)sigmoid函數(shù)進(jìn)行inline的定義會(huì)更快,其實(shí)不然,Matlab與C/C++等等語(yǔ)言不一樣,Matlab的inline反而比普通的函數(shù)定義更非時(shí)間。所以,我們可以直接在代碼中使用計(jì)算sigmoid函數(shù)及其導(dǎo)數(shù)的真實(shí)代碼。
總結(jié)
以上是生活随笔為你收集整理的CNN卷积神经网络推导和实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c c++ 函数内数组初值_C/C Pl
- 下一篇: python静态变量_python静态变