卷積神經(jīng)網(wǎng)絡CNN是深度學習的一個重要組成部分,由于其優(yōu)異的學習性能(尤其是對圖片的識別)。近年來研究異?;鸨?,出現(xiàn)了很多模型LeNet、Alex net、ZF net等等。由于大多高校在校生使用matlab比較多,而網(wǎng)上的教程代碼基本都基于caffe框架或者python,對于新入門的同學來說甚是煎熬,所以本文采用matlab結合MNIst手寫數(shù)據(jù)庫完成對手寫數(shù)字的識別。本人水平有限,如有紕漏,還望各路大神,幫忙指正。
一、卷積網(wǎng)絡原理
1、動機
卷積神經(jīng)網(wǎng)絡(CNN)是多層感知機(MLP)的一個變種模型,它是從生物學概念中演化而來的。從Hubel和Wiesel早期對貓的視覺皮層的研究工作,我們知道在視覺皮層存在一種細胞的復雜分布,,這些細胞對于外界的輸入局部是很敏感的,它們被稱為“感受野”(細胞),它們以某種方法來覆蓋整個視覺域。這些細胞就像一些濾波器一樣,它們對輸入的圖像是局部敏感的,因此能夠更好地挖掘出自然圖像中的目標的空間關系信息。
此外,視覺皮層存在兩類相關的細胞,S細胞(Simple Cell)和C(Complex Cell)細胞。S細胞在自身的感受野內(nèi)最大限度地對圖像中類似邊緣模式的刺激做出響應,而C細胞具有更大的感受野,它可以對圖像中產(chǎn)生刺激的模式的空間位置進行精準地定位。
視覺皮層作為目前已知的最為強大的視覺系統(tǒng),廣受關注。學術領域出現(xiàn)了很多基于它的神經(jīng)啟發(fā)式模型。比如:NeoCognitron [Fukushima], HMAX [Serre07] 以及本教程要討論的重點 LeNet-5 [LeCun98]。
2、稀疏連接
CNNs通過加強神經(jīng)網(wǎng)絡中相鄰層之間節(jié)點的局部連接模式(Local Connectivity Pattern)來挖掘自然圖像(中的興趣目標)的空間局部關聯(lián)信息。第m層隱層的節(jié)點與第m-1層的節(jié)點的局部子集,并具有空間連續(xù)視覺感受野的節(jié)點(就是m-1層節(jié)點中的一部分,這部分節(jié)點在m-1層都是相鄰的)相連??梢杂孟旅娴膱D來表示這種連接。
假設,m-1層為視網(wǎng)膜輸入層(接受自然圖像)。根據(jù)上圖的描述,在m-1層上面的m層的神經(jīng)元節(jié)點都具有寬度為3的感受野,m層每一個節(jié)點連接下面的視網(wǎng)膜層的3個相鄰的節(jié)點。m+1層的節(jié)點與它下面一層的節(jié)點有著相似的連接屬性,所以m+1層的節(jié)點仍與m層中3個相鄰的節(jié)點相連,但是對于輸入層(視網(wǎng)膜層)連接數(shù)就變多了,在本圖中是5。這種結構把訓練好的濾波器(corresponding to the input producing the strongest response)構建成了一種空間局部模式(因為每個上層節(jié)點都只對感受野中的,連接的局部的下層節(jié)點有響應)。根據(jù)上面圖,多層堆積形成了濾波器(不再是線性的了),它也變得更具有全局性了(如包含了一大片的像素空間)。比如,在上圖中,第m+1層能夠?qū)挾葹?的非線性特征進行編碼(就像素空間而言)。
3、權值共享
在CNNs中,每一個稀疏濾波器hi在整個感受野中是重復疊加的,這些重復的節(jié)點形式了一種特征圖(feature map),這個特種圖可以共享相同的參數(shù),比如相同的權值矩陣和偏置向量。
在上圖中,屬于同一個特征圖的三個隱層節(jié)點,因為需要共享相同顏色的權重, 他們的被限制成相同的。在這里, 梯度下降算法仍然可以用來訓練這些共享的參數(shù),只需要在原算法的基礎上稍作改動即可。共享權重的梯度可以對共享參數(shù)的梯度進行簡單的求和得到。
二、網(wǎng)絡的分析
上面這些內(nèi)容,基本就是CNN的精髓所在了,下面結合LeNet做具體的分析。
結構圖:
%%% matlab實現(xiàn)LeNet-5%%% 作者:xd.wp%%% 時間:2016.10.22 14:29%% 程序說明% 1、池化(pooling)采用平均2*2% 2、網(wǎng)絡結點數(shù)說明:% 輸入層:28*28% 第一層:24*24(卷積)*20% tanh% 第二層:12*12(pooling)*20% 第三層:100(全連接)% 第四層:10(softmax)% 3、網(wǎng)絡訓練部分采用800個樣本,檢驗部分采用100個樣本clear all;clc;%% 網(wǎng)絡初始化layer_c1_num=20;layer_s1_num=20;layer_f1_num=100;layer_output_num=10;%權值調(diào)整步進yita=0.01;%bias初始化bias_c1=(2*rand(1,20)-ones(1,20))/sqrt(20);bias_f1=(2*rand(1,100)-ones(1,100))/sqrt(20);%卷積核初始化[kernel_c1,kernel_f1]=init_kernel(layer_c1_num,layer_f1_num);%pooling核初始化pooling_a=ones(2,2)/4;%全連接層的權值weight_f1=(2*rand(20,100)-ones(20,100))/sqrt(20);weight_output=(2*rand(100,10)-ones(100,10))/sqrt(100);disp('網(wǎng)絡初始化完成......');%% 開始網(wǎng)絡訓練disp('開始網(wǎng)絡訓練......');for iter=1:20for n=1:20 for m=0:9 %讀取樣本 train_data=imread(strcat(num2str(m),'_',num2str(n),'.bmp')); train_data=double(train_data); % 去均值% train_data=wipe_off_average(train_data); %前向傳遞,進入卷積層1 for k=1:layer_c1_num state_c1(:,:,k)=convolution(train_data,kernel_c1(:,:,k)); %進入激勵函數(shù) state_c1(:,:,k)=tanh(state_c1(:,:,k)+bias_c1(1,k)); %進入pooling1 state_s1(:,:,k)=pooling(state_c1(:,:,k),pooling_a); end %進入f1層 [state_f1_pre,state_f1_temp]=convolution_f1(state_s1,kernel_f1,weight_f1); %進入激勵函數(shù) for nn=1:layer_f1_num state_f1(1,nn)=tanh(state_f1_pre(:,:,nn)+bias_f1(1,nn)); end %進入softmax層 for nn=1:layer_output_num output(1,nn)=exp(state_f1*weight_output(:,nn))/sum(exp(state_f1*weight_output)); end %% 誤差計算部分 Error_cost=-output(1,m+1);% if (Error_cost<-0.98)% break;% end %% 參數(shù)調(diào)整部分 [kernel_c1,kernel_f1,weight_f1,weight_output,bias_c1,bias_f1]=CNN_upweight(yita,Error_cost,m,train_data,... state_c1,state_s1,... state_f1,state_f1_temp,... output,... kernel_c1,kernel_f1,weight_f1,weight_output,bias_c1,bias_f1); end endenddisp('網(wǎng)絡訓練完成,開始檢驗......');count=0;for n=1:20 for m=0:9 %讀取樣本 train_data=imread(strcat(num2str(m),'_',num2str(n),'.bmp')); train_data=double(train_data); % 去均值% train_data=wipe_off_average(train_data); %前向傳遞,進入卷積層1 for k=1:layer_c1_num state_c1(:,:,k)=convolution(train_data,kernel_c1(:,:,k)); %進入激勵函數(shù) state_c1(:,:,k)=tanh(state_c1(:,:,k)+bias_c1(1,k)); %進入pooling1 state_s1(:,:,k)=pooling(state_c1(:,:,k),pooling_a); end %進入f1層 [state_f1_pre,state_f1_temp]=convolution_f1(state_s1,kernel_f1,weight_f1); %進入激勵函數(shù) for nn=1:layer_f1_num state_f1(1,nn)=tanh(state_f1_pre(:,:,nn)+bias_f1(1,nn)); end %進入softmax層 for nn=1:layer_output_num output(1,nn)=exp(state_f1*weight_output(:,nn))/sum(exp(state_f1*weight_output)); end [p,classify]=max(output); if (classify==m+1) count=count+1; end fprintf('真實數(shù)字為%d 網(wǎng)絡標記為%d 概率值為%d \n',m,classify-1,p); endend
init_kernel.m
function [kernel_c1,kernel_f1]=init_kernel(layer_c1_num,layer_f1_num)%% 卷積核初始化for n=1:layer_c1_num kernel_c1(:,:,n)=(2*rand(5,5)-ones(5,5))/12;endfor n=1:layer_f1_num kernel_f1(:,:,n)=(2*rand(12,12)-ones(12,12));endend
convolution.m
function [state]=convolution(data,kernel)%實現(xiàn)卷積層操作[data_row,data_col]=size(data);[kernel_row,kernel_col]=size(kernel);for m=1:data_col-kernel_col+1 for n=1:data_row-kernel_row+1 state(m,n)=sum(sum(data(m:m+kernel_row-1,n:n+kernel_col-1).*kernel)); endendend
pooling.m
function state=pooling(data,pooling_a)%% 實現(xiàn)取樣層pooling操作[data_row,data_col]=size(data);[pooling_row,pooling_col]=size(pooling_a);for m=1:data_col/pooling_col for n=1:data_row/pooling_row state(m,n)=sum(sum(data(2*m-1:2*m,2*n-1:2*n).*pooling_a)); endendend
convolution_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_num count=0; for m=1:layer_s1_num temp=state_s1(:,:,m)*weight_f1(m,n); count=count+temp; end state_f1_temp(:,:,n)=count; state_f1(:,:,n)=convolution(state_f1_temp(:,:,n),kernel_f1(:,:,n));endend
CNN_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ù)更新,權值和卷積核%% 結點數(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)絡權值kernel_c1_temp=kernel_c1;kernel_f1_temp=kernel_f1;weight_f1_temp=weight_f1;weight_output_temp=weight_output;%% Error計算label=zeros(1,layer_output_num);label(1,classify+1)=1;delta_layer_output=output-label;%% 更新weight_outputfor n=1:layer_output_num delta_weight_output_temp(:,n)=delta_layer_output(1,n)*state_f1';endweight_output_temp=weight_output_temp-yita*delta_weight_output_temp;%% 更新bias_f1以及kernel_f1for n=1:layer_f1_num count=0; for m=1:layer_output_num count=count+delta_layer_output(1,m)*weight_output(n,m); end %bias_f1 delta_layer_f1(1,n)=count*(1-tanh(state_f1(1,n)).^2); delta_bias_f1(1,n)=delta_layer_f1(1,n); %kernel_f1 delta_kernel_f1_temp(:,:,n)=delta_layer_f1(1,n)*state_f1_temp(:,:,n);endbias_f1=bias_f1-yita*delta_bias_f1;kernel_f1_temp=kernel_f1_temp-yita*delta_kernel_f1_temp;%% 更新weight_f1for n=1:layer_f1_num delta_layer_f1_temp(:,:,n)=delta_layer_f1(1,n)*kernel_f1(:,:,n);endfor n=1:layer_s1_num for m=1:layer_f1_num delta_weight_f1_temp(n,m)=sum(sum(delta_layer_f1_temp(:,:,m).*state_s1(:,:,n))); endendweight_f1_temp=weight_f1_temp-yita*delta_weight_f1_temp;%% 更新 bias_c1for n=1:layer_s1_num count=0; for m=1:layer_f1_num count=count+delta_layer_f1_temp(:,:,m)*weight_f1(n,m); end delta_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)));endbias_c1=bias_c1-yita*delta_bias_c1;%% 更新 kernel_c1for n=1:layer_c1_num delta_kernel_c1_temp(:,:,n)=rot90(conv2(train_data,rot90(delta_layer_c1(:,:,n),2),'valid'),2);endkernel_c1_temp=kernel_c1_temp-yita*delta_kernel_c1_temp;%% 網(wǎng)絡權值更新kernel_c1=kernel_c1_temp;kernel_f1=kernel_f1_temp;weight_f1=weight_f1_temp;weight_output=weight_output_temp;end
程序運行結果:
檢驗200個,196個識別正確,4個識別錯誤。
聯(lián)系客服