Matlab实现CNN(一)
卷積神經(jīng)網(wǎng)絡(luò)CNN是深度學(xué)習(xí)的一個(gè)重要組成部分,由于其優(yōu)異的學(xué)習(xí)性能(尤其是對(duì)圖片的識(shí)別)。近年來(lái)研究異?;鸨?#xff0c;出現(xiàn)了很多模型LeNet、Alex net、ZF net等等。由于大多高校在校生使用matlab比較多,而網(wǎng)上的教程代碼基本都基于caffe框架或者python,對(duì)于新入門(mén)的同學(xué)來(lái)說(shuō)甚是煎熬,所以本文采用matlab結(jié)合MNIst手寫(xiě)數(shù)據(jù)庫(kù)完成對(duì)手寫(xiě)數(shù)字的識(shí)別。本人水平有限,如有紕漏,還望各路大神,幫忙指正。?
一、卷積網(wǎng)絡(luò)原理
1、動(dòng)機(jī)
卷積神經(jīng)網(wǎng)絡(luò)(CNN)是多層感知機(jī)(MLP)的一個(gè)變種模型,它是從生物學(xué)概念中演化而來(lái)的。從Hubel和Wiesel早期對(duì)貓的視覺(jué)皮層的研究工作,我們知道在視覺(jué)皮層存在一種細(xì)胞的復(fù)雜分布,,這些細(xì)胞對(duì)于外界的輸入局部是很敏感的,它們被稱為“感受野”(細(xì)胞),它們以某種方法來(lái)覆蓋整個(gè)視覺(jué)域。這些細(xì)胞就像一些濾波器一樣,它們對(duì)輸入的圖像是局部敏感的,因此能夠更好地挖掘出自然圖像中的目標(biāo)的空間關(guān)系信息。
此外,視覺(jué)皮層存在兩類相關(guān)的細(xì)胞,S細(xì)胞(Simple Cell)和C(Complex Cell)細(xì)胞。S細(xì)胞在自身的感受野內(nèi)最大限度地對(duì)圖像中類似邊緣模式的刺激做出響應(yīng),而C細(xì)胞具有更大的感受野,它可以對(duì)圖像中產(chǎn)生刺激的模式的空間位置進(jìn)行精準(zhǔn)地定位。
視覺(jué)皮層作為目前已知的最為強(qiáng)大的視覺(jué)系統(tǒng),廣受關(guān)注。學(xué)術(shù)領(lǐng)域出現(xiàn)了很多基于它的神經(jīng)啟發(fā)式模型。比如:NeoCognitron [Fukushima], HMAX [Serre07] 以及本教程要討論的重點(diǎn) LeNet-5 [LeCun98]。
2、稀疏連接
CNNs通過(guò)加強(qiáng)神經(jīng)網(wǎng)絡(luò)中相鄰層之間節(jié)點(diǎn)的局部連接模式(Local Connectivity Pattern)來(lái)挖掘自然圖像(中的興趣目標(biāo))的空間局部關(guān)聯(lián)信息。第m層隱層的節(jié)點(diǎn)與第m-1層的節(jié)點(diǎn)的局部子集,并具有空間連續(xù)視覺(jué)感受野的節(jié)點(diǎn)(就是m-1層節(jié)點(diǎn)中的一部分,這部分節(jié)點(diǎn)在m-1層都是相鄰的)相連??梢杂孟旅娴膱D來(lái)表示這種連接。
假設(shè),m-1層為視網(wǎng)膜輸入層(接受自然圖像)。根據(jù)上圖的描述,在m-1層上面的m層的神經(jīng)元節(jié)點(diǎn)都具有寬度為3的感受野,m層每一個(gè)節(jié)點(diǎn)連接下面的視網(wǎng)膜層的3個(gè)相鄰的節(jié)點(diǎn)。m+1層的節(jié)點(diǎn)與它下面一層的節(jié)點(diǎn)有著相似的連接屬性,所以m+1層的節(jié)點(diǎn)仍與m層中3個(gè)相鄰的節(jié)點(diǎn)相連,但是對(duì)于輸入層(視網(wǎng)膜層)連接數(shù)就變多了,在本圖中是5。這種結(jié)構(gòu)把訓(xùn)練好的濾波器(corresponding to the input producing the strongest response)構(gòu)建成了一種空間局部模式(因?yàn)槊總€(gè)上層節(jié)點(diǎn)都只對(duì)感受野中的,連接的局部的下層節(jié)點(diǎn)有響應(yīng))。根據(jù)上面圖,多層堆積形成了濾波器(不再是線性的了),它也變得更具有全局性了(如包含了一大片的像素空間)。比如,在上圖中,第m+1層能夠?qū)挾葹?的非線性特征進(jìn)行編碼(就像素空間而言)。
3、權(quán)值共享
在CNNs中,每一個(gè)稀疏濾波器hi在整個(gè)感受野中是重復(fù)疊加的,這些重復(fù)的節(jié)點(diǎn)形式了一種特征圖(feature map),這個(gè)特種圖可以共享相同的參數(shù),比如相同的權(quán)值矩陣和偏置向量。?
在上圖中,屬于同一個(gè)特征圖的三個(gè)隱層節(jié)點(diǎn),因?yàn)樾枰蚕硐嗤伾臋?quán)重, 他們的被限制成相同的。在這里, 梯度下降算法仍然可以用來(lái)訓(xùn)練這些共享的參數(shù),只需要在原算法的基礎(chǔ)上稍作改動(dòng)即可。共享權(quán)重的梯度可以對(duì)共享參數(shù)的梯度進(jìn)行簡(jiǎn)單的求和得到。
二、網(wǎng)絡(luò)的分析?
上面這些內(nèi)容,基本就是CNN的精髓所在了,下面結(jié)合LeNet做具體的分析。?
結(jié)構(gòu)圖:?
?
?
LeNet算上輸入輸出一共為八層,下面逐層分析。?
第一層:數(shù)據(jù)輸入層?
CNN的強(qiáng)項(xiàng)在于圖片的處理,lenet的輸入為32*32的矩陣圖片。這里需要注意的點(diǎn):?
1、數(shù)據(jù)的歸一化,這里的歸一化是廣義的,不一定要?dú)w到0-1,但要是相同的一個(gè)區(qū)間范圍,一般我們的灰度圖為0-255。?
2、數(shù)據(jù)的去均值,如果樣本有非零的均值,而且與測(cè)試部分的非零均值不一致,可能就會(huì)導(dǎo)致識(shí)別率的下降。當(dāng)然這不一定發(fā)生,我們這么做是為了增加系統(tǒng)的魯棒性。?
?
第二層:卷積層c1?
卷積層是卷積神經(jīng)網(wǎng)絡(luò)的核心,通過(guò)不同的卷積核,來(lái)獲取圖片的特征。卷積核相當(dāng)于一個(gè)濾波器,不同的濾波器提取不同特征。打個(gè)比方,對(duì)于手寫(xiě)數(shù)字識(shí)別,某一個(gè)卷積核提取‘一’,另一個(gè)卷積核提取‘|’,所以這個(gè)數(shù)字很有可能就判定為‘7’。當(dāng)然實(shí)際要比這復(fù)雜度得多,但原理大概就是這個(gè)樣子。?
第三層:pooling層?
基本每個(gè)卷積層后邊都會(huì)接一個(gè)pooling層,目的是為了降維。一般都將原來(lái)的卷積層的輸出矩陣大小變?yōu)樵瓉?lái)的一半,方便后邊的運(yùn)算。另外,pooling層增加了系統(tǒng)的魯棒性,把原來(lái)的準(zhǔn)確描述變?yōu)榱烁怕悦枋?#xff08;原來(lái)矩陣大小為28*28,現(xiàn)在為14*14,必然有一部分信息丟失,一定程度上防止了過(guò)擬合)。?
第四層:卷積層?
與之前類似,在之前的特征中進(jìn)一步提取特征,對(duì)原樣本進(jìn)行更深層次的表達(dá)。注意:這里不是全連接。這里不是全連接。這里不是全連接。X代表連接,空白代表不連。?
?
第五層:pooling層?
與之前類似。?
第六層:卷積層(全連接)?
這里有120個(gè)卷積核,這里是全連接的。將矩陣卷積成一個(gè)數(shù),方便后邊網(wǎng)絡(luò)進(jìn)行判定。?
第七層:全連接層?
和MLP中的隱層一樣,獲得高維空間數(shù)據(jù)的表達(dá)。?
第八層:輸出層?
這里一般采用RBF網(wǎng)絡(luò),每個(gè)RBF的中心為每個(gè)類別的標(biāo)志,網(wǎng)絡(luò)輸出越大,代表越不相似,輸出的最小值即為網(wǎng)絡(luò)的判別結(jié)果。?
三、卷積網(wǎng)絡(luò)的BP訓(xùn)練?
前面的都很好理解,卷積神經(jīng)網(wǎng)絡(luò)的難度在于BP過(guò)程。網(wǎng)上zouxy09的博文寫(xiě)的很好,可以看一下,自己搞明白。傳送門(mén):CNN的BP推導(dǎo)?
四、代碼部分?
關(guān)于MNIST數(shù)據(jù)集,網(wǎng)上有很多現(xiàn)成的代碼對(duì)其進(jìn)行提取,但提取出來(lái)的都是亂序的很不利于使用。這里有提取好的分類后的,詳情傳送門(mén)?
簡(jiǎn)單起見(jiàn),我們的代碼選用一層卷積層。?
CNN_simple_mian.m
init_kernel.m
function [kernel_c1,kernel_f1]=init_kernel(layer_c1_num,layer_f1_num) %% 卷積核初始化 for n=1:layer_c1_numkernel_c1(:,:,n)=(2*rand(5,5)-ones(5,5))/12; end for n=1:layer_f1_numkernel_f1(:,:,n)=(2*rand(12,12)-ones(12,12)); end endconvolution.m
function [state]=convolution(data,kernel) %實(shí)現(xiàn)卷積層操作 [data_row,data_col]=size(data); [kernel_row,kernel_col]=size(kernel); for m=1:data_col-kernel_col+1for n=1:data_row-kernel_row+1state(m,n)=sum(sum(data(m:m+kernel_row-1,n:n+kernel_col-1).*kernel));end end endpooling.m
function state=pooling(data,pooling_a) %% 實(shí)現(xiàn)取樣層pooling操作 [data_row,data_col]=size(data); [pooling_row,pooling_col]=size(pooling_a); for m=1:data_col/pooling_colfor n=1:data_row/pooling_rowstate(m,n)=sum(sum(data(2*m-1:2*m,2*n-1:2*n).*pooling_a));end end endconvolution_f1.m
function [state_f1,state_f1_temp]=convolution_f1(state_s1,kernel_f1,weight_f1) %% 完成卷積層2操作 layer_f1_num=size(weight_f1,2); layer_s1_num=size(weight_f1,1);%% for n=1:layer_f1_numcount=0;for m=1:layer_s1_numtemp=state_s1(:,:,m)*weight_f1(m,n);count=count+temp;endstate_f1_temp(:,:,n)=count;state_f1(:,:,n)=convolution(state_f1_temp(:,:,n),kernel_f1(:,:,n)); end endCNN_upweight.m
function [kernel_c1,kernel_f1,weight_f1,weight_output,bias_c1,bias_f1]=CNN_upweight(yita,Error_cost,classify,train_data,state_c1,state_s1,state_f1,state_f1_temp,...output,kernel_c1,kernel_f1,weight_f1,weight_output,bias_c1,bias_f1) %%% 完成參數(shù)更新,權(quán)值和卷積核 %% 結(jié)點(diǎn)數(shù)目 layer_c1_num=size(state_c1,3); layer_s1_num=size(state_s1,3); layer_f1_num=size(state_f1,2); layer_output_num=size(output,2);[c1_row,c1_col,~]=size(state_c1); [s1_row,s1_col,~]=size(state_s1);[kernel_c1_row,kernel_c1_col]=size(kernel_c1(:,:,1)); [kernel_f1_row,kernel_f1_col]=size(kernel_f1(:,:,1)); %% 保存網(wǎng)絡(luò)權(quán)值 kernel_c1_temp=kernel_c1; kernel_f1_temp=kernel_f1;weight_f1_temp=weight_f1; weight_output_temp=weight_output; %% Error計(jì)算 label=zeros(1,layer_output_num); label(1,classify+1)=1; delta_layer_output=output-label; %% 更新weight_output for n=1:layer_output_numdelta_weight_output_temp(:,n)=delta_layer_output(1,n)*state_f1'; end weight_output_temp=weight_output_temp-yita*delta_weight_output_temp;%% 更新bias_f1以及kernel_f1 for n=1:layer_f1_numcount=0;for m=1:layer_output_numcount=count+delta_layer_output(1,m)*weight_output(n,m);end%bias_f1delta_layer_f1(1,n)=count*(1-tanh(state_f1(1,n)).^2);delta_bias_f1(1,n)=delta_layer_f1(1,n);%kernel_f1delta_kernel_f1_temp(:,:,n)=delta_layer_f1(1,n)*state_f1_temp(:,:,n); end bias_f1=bias_f1-yita*delta_bias_f1; kernel_f1_temp=kernel_f1_temp-yita*delta_kernel_f1_temp; %% 更新weight_f1 for n=1:layer_f1_numdelta_layer_f1_temp(:,:,n)=delta_layer_f1(1,n)*kernel_f1(:,:,n); end for n=1:layer_s1_numfor m=1:layer_f1_numdelta_weight_f1_temp(n,m)=sum(sum(delta_layer_f1_temp(:,:,m).*state_s1(:,:,n)));end end weight_f1_temp=weight_f1_temp-yita*delta_weight_f1_temp;%% 更新 bias_c1 for n=1:layer_s1_numcount=0;for m=1:layer_f1_numcount=count+delta_layer_f1_temp(:,:,m)*weight_f1(n,m); enddelta_layer_s1(:,:,n)=count;delta_layer_c1(:,:,n)=kron(delta_layer_s1(:,:,n),ones(2,2)/4).*(1-tanh(state_c1(:,:,n)).^2);delta_bias_c1(1,n)=sum(sum(delta_layer_c1(:,:,n))); end bias_c1=bias_c1-yita*delta_bias_c1; %% 更新 kernel_c1 for n=1:layer_c1_numdelta_kernel_c1_temp(:,:,n)=rot90(conv2(train_data,rot90(delta_layer_c1(:,:,n),2),'valid'),2); end kernel_c1_temp=kernel_c1_temp-yita*delta_kernel_c1_temp;%% 網(wǎng)絡(luò)權(quán)值更新 kernel_c1=kernel_c1_temp; kernel_f1=kernel_f1_temp;weight_f1=weight_f1_temp; weight_output=weight_output_temp;end程序運(yùn)行結(jié)果:?
檢驗(yàn)200個(gè),196個(gè)識(shí)別正確,4個(gè)識(shí)別錯(cuò)誤。?
總結(jié)
以上是生活随笔為你收集整理的Matlab实现CNN(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 图解VS2010打包全过程
- 下一篇: 计算机组成原理课后习题答案一