BP神经网络识别手写数字项目解析及matlab实现
BP神經(jīng)網(wǎng)絡(luò)指?jìng)鹘y(tǒng)的人工神經(jīng)網(wǎng)絡(luò),相比于卷積神經(jīng)網(wǎng)絡(luò)(CNN)來說要簡(jiǎn)單些。
人工神經(jīng)網(wǎng)絡(luò)具有復(fù)雜模式和進(jìn)行聯(lián)想、推理記憶的功能, 它是解決某些傳統(tǒng)方法所無法解決的問題的有力工具。目前, 它日益受到重視, 同時(shí)其他學(xué)科的發(fā)展, 為其提供了更大的機(jī)會(huì)。1986 年, Romelhart 和Mcclelland提出了誤差反向傳播算法(Error Back Propagation Algorithm) ,簡(jiǎn)稱BP 算法,由于多層前饋網(wǎng)絡(luò)的訓(xùn)練經(jīng)常采用誤差反向傳播算法, 人們也把多層前饋網(wǎng)絡(luò)稱為BP 網(wǎng)絡(luò)。
為了便于閱讀,下面說一下全文的邏輯順序:
1,通俗說下神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)和工作原理,簡(jiǎn)單好理解,推薦觀看
2,逆向傳播算法的數(shù)學(xué)推導(dǎo),如果覺得太復(fù)雜可以暫時(shí)跳過
3,matlab代碼和圖像庫
?
(1) 大白話講解傳統(tǒng)神經(jīng)網(wǎng)絡(luò)
首先,我們看一下神經(jīng)網(wǎng)絡(luò)的基本單元——單個(gè)的神經(jīng)元:
圖中圓形表示一個(gè)神經(jīng)元,我們知道,一個(gè)神經(jīng)元接收相鄰的神經(jīng)元傳來的刺激,神經(jīng)元對(duì)這些刺激以不同的權(quán)重進(jìn)行積累,到一定的時(shí)候產(chǎn)生自己的刺激將其傳遞給一些與它相鄰的神經(jīng)元。這樣工作的無數(shù)個(gè)神經(jīng)元便構(gòu)成了人腦對(duì)外界的感知。而人腦對(duì)世界的學(xué)習(xí)的機(jī)制就是通過調(diào)節(jié)這些相鄰連接的神經(jīng)元刺激的權(quán)重。
在圖中,周圍神經(jīng)元傳過來的刺激表示為Y,權(quán)重表示為W,圓形表示的神經(jīng)元得到的刺激是所有刺激按照權(quán)重累加起來,即
同時(shí)這個(gè)神經(jīng)元作為網(wǎng)絡(luò)的一份子,也像其他神經(jīng)元一樣需要向外傳播刺激信號(hào),但是不是直接把s傳播,而是傳播一個(gè)f(s)出去,為什么呢?其實(shí)無關(guān)大局,我們后面分析。其中f(s)學(xué)名稱為“激活函數(shù)”,常用的函數(shù)如下:
好了,理解到這里如果沒什么問題的話,恭喜你你已經(jīng)入門了,現(xiàn)在我們把這一個(gè)個(gè)的基本單元連接起來,就構(gòu)成我們最終的神經(jīng)網(wǎng)絡(luò)了。傳統(tǒng)的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)如下圖所示:
是不是覺得很亂?不著急我們一點(diǎn)一點(diǎn)看,由整體到細(xì)微來解剖它。首先整體上,它的結(jié)構(gòu)分為三部分,輸入層,隱藏層和輸出層,一般輸入層和輸出層各一個(gè),隱藏層若干個(gè),圖中畫出了一個(gè)。細(xì)微處,連接結(jié)構(gòu)上,后一層的每個(gè)神經(jīng)元都由前一層的所有神經(jīng)元連接進(jìn)來。
手寫數(shù)字識(shí)別實(shí)驗(yàn)使用的是三層的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu),即只有一個(gè)隱藏層,下面以此說明。
下面說明一下各層的表示和各層的關(guān)系:
輸入層:X=(x1,x2,x3…xn)
隱藏層:Y=(y1,y2,y3…ym)
輸出層:O=(o1,o2,o3…or)
兩個(gè)權(quán)重:
輸入層到隱藏層的權(quán)重:V=(V1,V2,V3…Vm),Vj是一個(gè)列向量,表示輸入層所有神經(jīng)元通過Vj加權(quán),得到隱藏層的第j個(gè)神經(jīng)元
隱藏層到輸出層的權(quán)重:W=(W1,W2,W3…Wr),Wk是一個(gè)列向量,表示隱藏層的所有神經(jīng)元通過Wk加權(quán),得到輸出層的第k個(gè)神經(jīng)元
根據(jù)我們上面說到的單個(gè)神經(jīng)元的刺激傳入和刺激傳出,相信到這里很多人應(yīng)該已經(jīng)得出下面的各層之間的關(guān)系了:
到這里,神經(jīng)網(wǎng)絡(luò)的工作過程就清楚一些了。實(shí)例說明一下,假設(shè)輸入是一張圖像, 16x16大小,轉(zhuǎn)換為一個(gè)二維的灰度值矩陣,再把每一行拼接在上一行的末尾,拼接成一個(gè)1x256的行向量,作為輸入層的輸入,即X,接下來按照公式2就可以計(jì)算出隱藏層,然后根據(jù)公式1又可以計(jì)算出輸出層,輸出層的輸出就得到了。在這個(gè)手寫數(shù)字識(shí)別的項(xiàng)目中,我使用的圖片輸入正是16x16,所以輸入層有256個(gè)神經(jīng)元,隱藏層的神經(jīng)元我取了64個(gè),最后的輸出層神經(jīng)元我取的是10個(gè),為什么是10個(gè)呢?因?yàn)閿?shù)字0到9一共10個(gè),期望上是,比如輸入一張寫著數(shù)字1的圖像,在輸出端得到的輸出是{1 0 0 0 0 0 0 0 0 0},輸入的圖像為2時(shí),輸出{ 0 1 0 0 0 0 0 0 0 0},以此類推,實(shí)際上輸出的未必就是剛好1和剛好0,經(jīng)過調(diào)參和訓(xùn)練,基本是輸出0.9多和正負(fù)的0.0多,不過也足夠了,僅僅用判斷最大值所在位置的方式就可以識(shí)別到圖像上的數(shù)字。
至此,我們已經(jīng)了解了整個(gè)網(wǎng)絡(luò)的結(jié)構(gòu)和正向工作的具體流程。可以說我們已經(jīng)對(duì)神經(jīng)網(wǎng)絡(luò)理解了有50%了。為什么才50%呢?仔細(xì)想想相信你會(huì)發(fā)現(xiàn),我們還不知道兩個(gè)在網(wǎng)絡(luò)中很重要的量,就是權(quán)重矩陣W和V。
如何求得W和V呢,這里要用到一種算法,就是誤差反向傳播算法(Error Back Propagation Algorithm) ,簡(jiǎn)稱BP 算法。說的很晦澀,我們來翻譯成人話。先來看看算法的工作過程,首先隨機(jī)地初始化W和V的值,然后代入一些圖片進(jìn)行計(jì)算,得到一個(gè)輸出,當(dāng)然由于W和V參數(shù)不會(huì)剛好很完美,輸出自然不會(huì)是像上文說的,剛好就是{1 0 0 0 0 0 0 0 0 0}這一類,所以存在誤差,根據(jù)這個(gè)誤差就可以反過來修正W和V的值,修正后的W和V可以使輸出更加的靠近于理想的輸出,這就是所謂的“誤差反向傳播”的意思,修正一次之后,再代入其他一些圖片,輸出離理想輸出又靠近了一點(diǎn),我們又繼續(xù)計(jì)算誤差,然后修正W和V的值,就這樣經(jīng)過很多次的迭代計(jì)算,最終多次修正得到了比較完美的W和V矩陣,它可以使得輸出非常靠近于理想的輸出,至此我們的工作完成度才是100%了。這種在輸出端計(jì)算誤差,根據(jù)誤差來作調(diào)節(jié)的思想,學(xué)自動(dòng)化的或者接觸過飛思卡爾一類的智能車比賽的同學(xué)體會(huì)應(yīng)該是比較深的,跟PID自控算法有很大相似性。
下面是數(shù)學(xué)推導(dǎo),關(guān)于實(shí)際輸出和理想輸出之間的誤差如何具體來調(diào)節(jié)W和V的值,調(diào)節(jié)多少的問題。上面說過了,暫時(shí)不理解的話可以先跳過推導(dǎo),看最后的結(jié)論就好,自己最后跟著代碼實(shí)踐一遍,有了更深的體會(huì),慢慢會(huì)理解的。
?
(2)逆向傳播算法的數(shù)學(xué)推導(dǎo)
輸出層的理想輸出:d=(d1,d2,d3…dr),例如{1 0 0 0 0 0 0 0 0 0}和{0 1 0 0 0 0 0 0 0 0}等
假設(shè)實(shí)際輸出和理想輸出之間的差距是E,明顯W是一個(gè)關(guān)于輸入X,權(quán)重W和V,輸出O的函數(shù)。要修正W,則需要知道具體的修正增量ΔW,離散情況下,表征微分增量,可以得到:
這樣,改變?chǔ)堑拇笮〖纯筛淖兠恳淮握{(diào)節(jié)的幅度,η大的話調(diào)節(jié)更快,小則調(diào)節(jié)慢,但是過大容易導(dǎo)致振蕩,這一點(diǎn)也跟PID中的比例系數(shù)P是一樣的。一般η的大小需要經(jīng)過多次嘗試來找到合適值。
好了,到這里神經(jīng)網(wǎng)絡(luò)就講解完畢,下面是一個(gè)較次要的內(nèi)容,我們上面說了,通過不斷迭代來調(diào)整權(quán)重W和V,那么如何衡量迭代是否可以停止了呢。一個(gè)自然的想法是判斷每次的輸出和理想輸出是否足夠接近,所以我們可以用算向量距離的方法,跟均方差是一個(gè) 道理,如下:
這樣,主要s足夠小,迭代就可以結(jié)束了。
(3)實(shí)踐
下面是我試驗(yàn)用的MATLAB代碼。
訓(xùn)練部分:recognize_handwriting_numbers_by_simple_NN_train.m
V=double(rand(256,64)); W=double(rand(64,10)); delta_V=double(rand(256,64)); delta_W=double(rand(64,10));yita=0.2;%縮放系數(shù),有的文章稱學(xué)習(xí)率 yita1=0.05;%我自己加的參數(shù),縮放激活函數(shù)的自變量防止輸入過大進(jìn)入函數(shù)的飽和區(qū),可以去掉體會(huì)一下變化 train_number=9;%訓(xùn)練樣本中,有多少個(gè)數(shù)字,一共9個(gè),沒有0 train_num=30;%訓(xùn)練樣本中,每種數(shù)字多少張圖,一共100張 x=double(zeros(1,256));%輸入層 y=double(zeros(1,64));%中間層,也是隱藏層 output=double(zeros(1,10));%輸出層 tar_output=double(zeros(1,10));%目標(biāo)輸出,即理想輸出 delta=double(zeros(1,10));%一個(gè)中間變量,可以不管%記錄總的均方差便于畫圖 s_record=1:1000; tic %計(jì)時(shí) for train_control_num=1:1000 %訓(xùn)練次數(shù)控制,在調(diào)參的最后發(fā)現(xiàn)1000次其實(shí)有多了,大概400次完全夠了s=0; %讀圖,輸入網(wǎng)絡(luò) for number=1:train_number ReadDir=['E:\Matlab\recognize_handwiting_numbers\train_lib\'];%讀取樣本的路徑 for num=1:train_num %控制多少張 photo_name=[num2str(number),num2str(num,'%05d'),'.png'];%圖片名 photo_index=[ReadDir,photo_name];%路徑加圖片名得到總的圖片索引 photo_matrix=imread(photo_index);%使用imread得到圖像矩陣 photo_matrix=uint8(photo_matrix<=230);%二值化,黑色是1 tmp=photo_matrix'; tmp=tmp(:);%以上兩步完成了圖像二維矩陣轉(zhuǎn)變?yōu)榱邢蛄?#xff0c;256維,作為輸入 %計(jì)算輸入層輸入 x=double(tmp');%轉(zhuǎn)化為行向量因?yàn)檩斎雽覺是行向量,并且化為浮點(diǎn)數(shù) %得到隱層輸入 y0=x*V; %激活 y=1./(1+exp(-y0*yita1)); %得到輸出層輸入 output0=y*W; output=1./(1+exp(-output0*yita1)); %計(jì)算預(yù)期輸出 tar_output=double(zeros(1,10)); tar_output(number)=1.0; %計(jì)算誤差 %按照公式計(jì)算W和V的調(diào)整,為了避免使用for循環(huán)比較耗費(fèi)時(shí)間,下面采用了直接矩陣乘法,更高效 delta=(tar_output-output).*output.*(1-output); delta_W=yita*repmat(y',1,10).*repmat(delta,64,1); tmp=sum((W.*repmat(delta,64,1))'); tmp=tmp.*y.*(1-y); delta_V=yita*repmat(x',1,64).*repmat(tmp,256,1); %計(jì)算均方差 s=s+sum((tar_output-output).*(tar_output-output))/10; %更新權(quán)值 W=W+delta_W; V=V+delta_V; end end s=s/train_number/train_num %不加分號(hào),隨時(shí)輸出誤差觀看收斂情況 train_control_num %不加分號(hào),隨時(shí)輸出迭代次數(shù)觀看運(yùn)行狀態(tài) s_record(train_control_num)=s;%記錄 end toc %計(jì)時(shí)結(jié)束 plot(1:1000,s_record);?
測(cè)試部分:recognize_handwriting_numbers_by_simple_NN_test.m
?
correct_num=0;%記錄正確的數(shù)量 incorrect_num=0;%記錄錯(cuò)誤數(shù)量 test_number=9;%測(cè)試集中,一共多少數(shù)字,9個(gè),沒有0 test_num=100;%測(cè)試集中,每個(gè)數(shù)字多少個(gè),最大100個(gè) % load W;%%之前訓(xùn)練得到的W保存了,可以直接加載進(jìn)來 % load V; % load yita1;%記錄時(shí)間 tic %計(jì)時(shí)開始 for number=1:test_number ReadDir=['E:\Matlab\recognize_handwiting_numbers\test_lib\']; for num=1:test_num %控制多少張 photo_name=[num2str(number),num2str(num,'%05d'),'.png']; photo_index=[ReadDir,photo_name]; photo_matrix=imread(photo_index); %大小改變 photo_matrix=imresize(photo_matrix,[16 16]); %二值化 photo_matrix=uint8(photo_matrix<=230);%黑色是1 %行向量 tmp=photo_matrix'; tmp=tmp(:); %計(jì)算輸入層輸入 x=double(tmp'); %得到隱層輸入 y0=x*V; %激活 y=1./(1+exp(-y0*yita1)); %得到輸出層輸入 o0=y*W; o=1./(1+exp(-o0*yita1)); %最大的輸出即是識(shí)別到的數(shù)字 [o,index]=sort(o); if index(10)==numbercorrect_num=correct_num+1 elseincorrect_num=incorrect_num+1;%顯示不成功的數(shù)字,顯示會(huì)比較花時(shí)間 % figure(incorrect_num) % imshow((1-photo_matrix)*255); % title(num2str(number)); end end end correct_rate=correct_num/test_number/test_num toc %計(jì)時(shí)結(jié)束?
使用的庫來自于http://www.ee.surrey.ac.uk/CVSSP/demos/chars74k/#download
使用其中的EnglishFnt.tgz庫,從其中的數(shù)字選出前100張作為訓(xùn)練,再另外取100張作為測(cè)試。圖片集有需要也可以郵件我發(fā)送。
?
運(yùn)行結(jié)果:
最后調(diào)參得到比較好的結(jié)果,使用η=0.2,η1=0.05,在我電腦上,i5-3210M,4G內(nèi)存,迭代1000次所需要的時(shí)間是468s,最終得到下面的曲線,橫坐標(biāo)是訓(xùn)練的迭代次數(shù),縱坐標(biāo)是均方差,可以看到,其實(shí)在350次迭代的時(shí)候已經(jīng)取得了很小的均方差,考慮時(shí)間和性能的折中的話,迭代350次已經(jīng)可以了。
最后訓(xùn)練結(jié)束,使用η=0.2,η1=0.05,用測(cè)試集測(cè)試,可以得到89.11%的準(zhǔn)確率,繼續(xù)調(diào)節(jié)參數(shù),曾經(jīng)可以得到90.56%的準(zhǔn)確率,不過也難再上去,估計(jì)瓶頸了。
如果你跟著本文的步驟一步一步下來,最后還自己動(dòng)手打一遍代碼,應(yīng)該會(huì)很好的理解了傳統(tǒng)類型的神經(jīng)網(wǎng)絡(luò),對(duì)接下來理解卷積神經(jīng)網(wǎng)絡(luò)應(yīng)該會(huì)有些幫助。
?
個(gè)人對(duì)神經(jīng)網(wǎng)絡(luò)的的一些理解:
? ? ? ?以識(shí)別圖片手寫數(shù)字為例,圖片中黑色像素點(diǎn)和白色像素點(diǎn)之間的空間編排關(guān)系構(gòu)成了我們看到的數(shù)字,如果我們能找到一個(gè)很厲害的方程組,方程的輸入?yún)?shù)是一張圖片,返回一個(gè)數(shù)字表示識(shí)別結(jié)果,那肯定是很理想的,但是可惜,很難找到的方程來映射圖片像素點(diǎn)的空間排布和識(shí)別結(jié)果這樣的一種關(guān)系。而神經(jīng)網(wǎng)絡(luò)剛好就完成了這樣的映射功能,所有的關(guān)于像素點(diǎn)空間排布信息的解析都隱藏于矩陣W和V之中,所以W和V實(shí)際上代表了這樣一種映射關(guān)系。而神經(jīng)網(wǎng)絡(luò)聰明的地方在于,當(dāng)這樣的映射關(guān)系未知時(shí),我們?cè)O(shè)計(jì)了一套機(jī)理讓它自己來尋找,這就是學(xué)習(xí)和訓(xùn)練的過程,而最后得到的W和V就是學(xué)習(xí)的結(jié)果,這個(gè)過程很像嬰兒開始認(rèn)東西的過程,一開始嬰兒的認(rèn)知體系是空白的,像剛隨機(jī)初始化的W和V,我們給它看一本書,像給了網(wǎng)絡(luò)一個(gè)輸入,告訴他這個(gè)是“書”,像告訴網(wǎng)絡(luò),理想輸出應(yīng)該是“書”,他會(huì)開始嘗試來理解,建立書這個(gè)物品到“書”這個(gè)字的映射關(guān)系,一開始他會(huì)犯錯(cuò),看到相似的比如一張紙也會(huì)認(rèn)為是書,但是在看到更多的書之后,并且我們告訴他這個(gè)是“書”之后,他又會(huì)不斷修正這樣一種映射關(guān)系,最后建立完整的對(duì)于書這樣一種物品到“書”這個(gè)字的映射過程,初步的認(rèn)知體系建立。現(xiàn)在可以說說上面的“激活函數(shù)”是干嘛用的了,可以想象,自然界的各種各樣的映射關(guān)系顯然是比較復(fù)雜的,關(guān)靠線性關(guān)系是無力描述的,而神經(jīng)網(wǎng)絡(luò)中用的是加權(quán)相加的運(yùn)算,如果沒有經(jīng)過激活函數(shù)處理,后層的所有輸入都可以由多層之前的信號(hào)經(jīng)過線性運(yùn)算來得到,一個(gè)現(xiàn)象模型顯然表現(xiàn)力是遠(yuǎn)遠(yuǎn)不夠的,所以我們?cè)谏窠?jīng)網(wǎng)絡(luò)模型中加入了非線性的因素,就是激活函數(shù)。
?
//
轉(zhuǎn)載:https://blog.csdn.net/huang_miao_xin/article/details/51364152
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的BP神经网络识别手写数字项目解析及matlab实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数字图像处理-1.图像获取
- 下一篇: Linux、Windows、RHEL操作