BP人工神经网络的介绍与实现
神經(jīng)網(wǎng)絡(luò)概念與適合領(lǐng)域
神經(jīng)網(wǎng)絡(luò)最早的研究是 40 年代心理學(xué)家 Mcculloch 和數(shù)學(xué)家 Pitts 合作提出的 ,他們提出的MP模型拉開(kāi)了神經(jīng)網(wǎng)絡(luò)研究的序幕。
神經(jīng)網(wǎng)絡(luò)的發(fā)展大致經(jīng)過(guò) 3 個(gè)階段:1947~1969 年為初期,在這期間科學(xué)家們提出了許多神經(jīng)元模型和學(xué)習(xí)規(guī)則,如 MP 模型、HEBB 學(xué)習(xí)規(guī)則和感知器等;60 年代末期至 80 年代中期,神經(jīng)網(wǎng)絡(luò)控制與整個(gè)神經(jīng)網(wǎng)絡(luò)研究一樣,處于低潮。在此期間,科學(xué)家們做了大量的工作,如 Hopfield 教授對(duì)網(wǎng)絡(luò)引入能量函數(shù)的概念,給出了網(wǎng)絡(luò)的穩(wěn)定性判據(jù),提出了用于聯(lián)想記憶和優(yōu)化計(jì)算的途徑。1984年,Hiton 教授提出 Bol tzman 機(jī)模型;1986年 Kumelhart 等人提出誤差反向傳播神經(jīng)網(wǎng)絡(luò),簡(jiǎn)稱(chēng) BP 網(wǎng)絡(luò)。目前,BP網(wǎng)絡(luò)已成為廣泛使用的網(wǎng)絡(luò)。1987年至今為發(fā)展期,在此期間,神經(jīng)網(wǎng)絡(luò)受到國(guó)際重視,各個(gè)國(guó)家都展開(kāi)研究,形成神經(jīng)網(wǎng)絡(luò)發(fā)展的另一個(gè)高潮。
人工神經(jīng)網(wǎng)絡(luò)(ANN)受到生物學(xué)的啟發(fā)是生物神經(jīng)網(wǎng)絡(luò)的一種模擬和近似,它從結(jié)構(gòu)、實(shí)現(xiàn)機(jī)理和功能上模擬生物神經(jīng)網(wǎng)絡(luò)。從系統(tǒng)觀點(diǎn)看,人工神經(jīng)元網(wǎng)絡(luò)是由大量神經(jīng)元通過(guò)極其豐富和完善的連接而構(gòu)成的自適應(yīng)非線性動(dòng)態(tài)系統(tǒng)。人工神經(jīng)網(wǎng)絡(luò),因?yàn)樯锏膶W(xué)習(xí)系統(tǒng)是由相互連接的神經(jīng)元組成的異常復(fù)雜的網(wǎng)絡(luò),其中每一個(gè)神經(jīng)元單元有一定數(shù)量的實(shí)值輸入,并產(chǎn)生單一的實(shí)數(shù)值輸出。1960 年威德羅和霍夫率先把神經(jīng)網(wǎng)絡(luò)用于自動(dòng)控制研究。神經(jīng)網(wǎng)絡(luò)以其獨(dú)特的結(jié)構(gòu)和處理信息的方法,在許多實(shí)際應(yīng)用領(lǐng)域中取得了顯著的成效,主要應(yīng)用如下:自動(dòng)控制領(lǐng)域、處理組合優(yōu)化問(wèn)題、模式識(shí)別、圖像處理、傳感器信號(hào)處理、機(jī)器人控制、信號(hào)處理、衛(wèi)生保健、醫(yī)療、經(jīng)濟(jì)、化工領(lǐng) 域、焊接領(lǐng)域、地理領(lǐng)域、數(shù)據(jù)挖掘、電力系統(tǒng)、交通、軍事、礦業(yè)、農(nóng)業(yè)和氣象等領(lǐng)域。
神經(jīng)網(wǎng)絡(luò)基本結(jié)構(gòu)
人工神經(jīng)網(wǎng)絡(luò)由神經(jīng)元模型構(gòu)成,這種由許多神經(jīng)元組成的信息處理網(wǎng)絡(luò)具有并行分布結(jié)構(gòu)。每個(gè)神經(jīng)元具有單一輸出,并且能夠與其它神經(jīng)元連接;存在許多(多重)輸出連接方法,每種連接方法對(duì)應(yīng)一個(gè)連接權(quán)系數(shù)。可把 ANN 看成是以處理單元 PE(processing element) 為節(jié)點(diǎn),用加權(quán)有向弧(鏈)相互連接而成的有向圖。令來(lái)自其它處理單元(神經(jīng)元)i的信息為Xi,它們與本處理單元的互相作用強(qiáng)度為 Wi,i=0,1,…,n-1,處理單元的內(nèi)部閾值為 θ。那么本神經(jīng)元的輸入為:
而處理單元的輸出為:
式中,xi為第 i 個(gè)元素的輸入,wi 為第 i 個(gè)元素與本處理單元的互聯(lián)權(quán)重。f 稱(chēng)為激發(fā)函數(shù)(activation function)或作用函數(shù)。它決定節(jié)點(diǎn)(神經(jīng)元)的輸出。該輸出為 1 或 0 取決于其輸入之和大于或小于內(nèi)部閾值 θ。
下圖所示神經(jīng)元單元由多個(gè)輸入Xi,i=1,2,...,n和一個(gè)輸出y組成。中間狀態(tài)由輸入信號(hào)的權(quán)和表示,而輸出為:
訓(xùn)練網(wǎng)絡(luò)
神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)被設(shè)計(jì)完成,有了輸入、輸出參數(shù)后,我們就要對(duì)網(wǎng)絡(luò)進(jìn)行訓(xùn)練。神經(jīng)網(wǎng)絡(luò)的訓(xùn)練有包括感知器訓(xùn)練、delta 規(guī)則訓(xùn)練和反向傳播算法等訓(xùn)練,其中感知器訓(xùn)練是基礎(chǔ)。
感知器和 delta 訓(xùn)練規(guī)則
理解神經(jīng)網(wǎng)絡(luò)的第一步是從對(duì)抽象生物神經(jīng)開(kāi)始,本文用到的人工神經(jīng)網(wǎng)絡(luò)系統(tǒng)是以被稱(chēng)為感知器的單元為基礎(chǔ),如圖所示。感知器以一個(gè)實(shí)數(shù)值向量作為輸入,計(jì)算這些輸入的線性組合,如果結(jié)果大于某個(gè)閾值,就輸出 1,否則輸出 -1,如果 x 從 1 到 n,則感知器計(jì)算公式如下:
其中每個(gè) wi 是一個(gè)實(shí)數(shù)常量,或叫做權(quán)值,用來(lái)決定輸入 xi 對(duì)感知器輸出的貢獻(xiàn)率。特別地,-w0是閾值。
盡管當(dāng)訓(xùn)練樣例線性可分時(shí),感知器法則可以成功地找到一個(gè)權(quán)向量,但如果樣例不是線性可分時(shí)它將不能收斂,因此人們?cè)O(shè)計(jì)了另一個(gè)訓(xùn)練法則來(lái)克服這個(gè)不足,這個(gè)訓(xùn)練規(guī)則叫做 delta 規(guī)則。感知器訓(xùn)練規(guī)則是基于這樣一種思路--權(quán)系數(shù)的調(diào)整是由目標(biāo)和輸出的差分方程表達(dá)式?jīng)Q定。而 delta 規(guī)則是基于梯度降落這樣一種思路。這個(gè)復(fù)雜的數(shù)學(xué)概念可以舉個(gè)簡(jiǎn)單的例子來(lái)表示。從給定的幾點(diǎn)來(lái)看,向南的那條路徑比向東那條更陡些。向東就像從懸崖上掉下來(lái),但是向南就是沿著一個(gè)略微傾斜的斜坡下來(lái),向西象登一座陡峭的山,而北邊則到了平地,只要慢慢的閑逛就可以了。所以您要尋找的是到達(dá)平地的所有路徑中將陡峭的總和減少到最小的路徑。在權(quán)系數(shù)的調(diào)整中,神經(jīng)網(wǎng)絡(luò)將會(huì)找到一種將誤差減少到最小的權(quán)系數(shù)的分配方式。這部分我們不做詳細(xì)介紹,如有需要大家可參考相關(guān)的人工智能書(shū)籍。
反向傳播算法
人工神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)為學(xué)習(xí)實(shí)數(shù)值和向量值函數(shù)提供了一種實(shí)際的方法,對(duì)于連續(xù)的和離散的屬性都可以使用。并且對(duì)訓(xùn)練數(shù)據(jù)中的噪聲具有很好的健壯性。反向傳播算法是最常見(jiàn)的網(wǎng)絡(luò)學(xué)習(xí)算法。這是我們所知用來(lái)訓(xùn)練神經(jīng)網(wǎng)絡(luò)很普遍的方法,反向傳播算法是一種具有很強(qiáng)學(xué)習(xí)能力的系統(tǒng),結(jié)構(gòu)比較簡(jiǎn)單,且易于編程。
魯梅爾哈特(Rumelhart)和麥克萊蘭(Meclelland)于 1985 年發(fā)展了 BP 網(wǎng)絡(luò)學(xué)習(xí)算法,實(shí)現(xiàn)了明斯基的多層網(wǎng)絡(luò)設(shè)想。BP網(wǎng)絡(luò)不僅含有輸入節(jié)點(diǎn)和輸出節(jié)點(diǎn),而且含有一層或多層隱(層)節(jié)點(diǎn)。輸入信號(hào)先向前傳遞到隱藏節(jié)點(diǎn),經(jīng)過(guò)作用后,再把隱藏節(jié)點(diǎn)的輸出信息傳遞到輸出節(jié)點(diǎn),最后給出輸出結(jié)果。節(jié)點(diǎn)的激發(fā)函數(shù)一般選用 S 型函數(shù)。
反向傳播(back-propagation,BP)算法是一種計(jì)算單個(gè)權(quán)值變化引起網(wǎng)絡(luò)性能變化值的較為簡(jiǎn)單的方法。由于BP算法過(guò)程包含從輸出節(jié)點(diǎn)開(kāi)始,反向地向第一隱含層(即最接近輸入層的隱含層)傳播由總誤差引起的權(quán)值修正,所以稱(chēng)為"反向傳播"。反向傳播特性與所求解問(wèn)題的性質(zhì)和所作細(xì)節(jié)選擇有極為密切的關(guān)系。
對(duì)于由一系列確定的單元互連形成的多層網(wǎng)絡(luò),反向傳播算法可用來(lái)學(xué)習(xí)這個(gè)多層網(wǎng)絡(luò)的權(quán)值。它采用梯度下降方法試圖最小化網(wǎng)絡(luò)輸出值和目標(biāo)值之間的誤差平方,因?yàn)槲覀円紤]多個(gè)輸出單元的網(wǎng)絡(luò),而不是像以前只考慮單個(gè)單元,所以我們要重新計(jì)算誤差E,以便對(duì)所有網(wǎng)絡(luò)輸出的誤差求和:
Outpus 是網(wǎng)絡(luò)輸出單元的集合,tkd 和 okd 是與訓(xùn)練樣例 d 和第 k 個(gè)輸出單元的相關(guān)輸出值.
反向傳播算法的一個(gè)迷人特性是:它能夠在網(wǎng)絡(luò)內(nèi)部的隱藏層發(fā)現(xiàn)有用的中間表示:
1.訓(xùn)練樣例僅包含網(wǎng)絡(luò)輸入和輸出,權(quán)值調(diào)節(jié)的過(guò)程可以自由地設(shè)置權(quán)值,來(lái)定義任何隱藏單元表示,這些隱藏單元表示在使誤差E達(dá)到最小時(shí)最有效。
2.引導(dǎo)反向傳播算法定義新的隱藏層特征,這些特征在輸入中沒(méi)有明確表示出來(lái),但能捕捉輸入實(shí)例中與學(xué)習(xí)目標(biāo)函數(shù)最相關(guān)的特征
反向傳播訓(xùn)練神經(jīng)元的算法如下:
C++簡(jiǎn)單實(shí)現(xiàn)與測(cè)試
以下C++代碼實(shí)現(xiàn)了BP網(wǎng)絡(luò),通過(guò)8個(gè)3位二進(jìn)制樣本對(duì)應(yīng)一個(gè)期望輸出,訓(xùn)練BP網(wǎng)絡(luò),最后訓(xùn)練好的網(wǎng)絡(luò)可以將輸入的三位二進(jìn)制數(shù)對(duì)應(yīng)輸出一位十進(jìn)制數(shù)。
//將三位二進(jìn)制數(shù)轉(zhuǎn)為一位十進(jìn)制數(shù) #include <iostream> #include <cmath> using namespace std;#define innode 3 //輸入結(jié)點(diǎn)數(shù) #define hidenode 10//隱含結(jié)點(diǎn)數(shù) #define outnode 1 //輸出結(jié)點(diǎn)數(shù) #define trainsample 8//BP訓(xùn)練樣本數(shù)class BpNet { public:void train(double p[trainsample][innode ],double t[trainsample][outnode]);//Bp訓(xùn)練double p[trainsample][innode]; //輸入的樣本double t[trainsample][outnode]; //樣本要輸出的double *recognize(double *p);//Bp識(shí)別void writetrain(); //寫(xiě)訓(xùn)練完的權(quán)值void readtrain(); //讀訓(xùn)練好的權(quán)值,這使的不用每次去訓(xùn)練了,只要把訓(xùn)練最好的權(quán)值存下來(lái)就OK BpNet();virtual ~BpNet();public:void init();double w[innode][hidenode];//隱含結(jié)點(diǎn)權(quán)值double w1[hidenode][outnode];//輸出結(jié)點(diǎn)權(quán)值double b1[hidenode];//隱含結(jié)點(diǎn)閥值double b2[outnode];//輸出結(jié)點(diǎn)閥值double rate_w; //權(quán)值學(xué)習(xí)率(輸入層-隱含層)double rate_w1;//權(quán)值學(xué)習(xí)率 (隱含層-輸出層)double rate_b1;//隱含層閥值學(xué)習(xí)率double rate_b2;//輸出層閥值學(xué)習(xí)率double e;//誤差計(jì)算double error;//允許的最大誤差double result[outnode];// Bp輸出 };BpNet::BpNet() {error=1.0;e=0.0;rate_w=0.9; //權(quán)值學(xué)習(xí)率(輸入層--隱含層)rate_w1=0.9; //權(quán)值學(xué)習(xí)率 (隱含層--輸出層)rate_b1=0.9; //隱含層閥值學(xué)習(xí)率rate_b2=0.9; //輸出層閥值學(xué)習(xí)率 }BpNet::~BpNet() {}void winit(double w[],int n) //權(quán)值初始化 {for(int i=0;i<n;i++)w[i]=(2.0*(double)rand()/RAND_MAX)-1; }void BpNet::init() {winit((double*)w,innode*hidenode);winit((double*)w1,hidenode*outnode);winit(b1,hidenode);winit(b2,outnode); }void BpNet::train(double p[trainsample][innode],double t[trainsample][outnode]) {double pp[hidenode];//隱含結(jié)點(diǎn)的校正誤差double qq[outnode];//希望輸出值與實(shí)際輸出值的偏差double yd[outnode];//希望輸出值double x[innode]; //輸入向量double x1[hidenode];//隱含結(jié)點(diǎn)狀態(tài)值double x2[outnode];//輸出結(jié)點(diǎn)狀態(tài)值double o1[hidenode];//隱含層激活值double o2[hidenode];//輸出層激活值for(int isamp=0;isamp<trainsample;isamp++)//循環(huán)訓(xùn)練一次樣品 {for(int i=0;i<innode;i++)x[i]=p[isamp][i]; //輸入的樣本for(int i=0;i<outnode;i++)yd[i]=t[isamp][i]; //期望輸出的樣本//構(gòu)造每個(gè)樣品的輸入和輸出標(biāo)準(zhǔn)for(int j=0;j<hidenode;j++){o1[j]=0.0;for(int i=0;i<innode;i++)o1[j]=o1[j]+w[i][j]*x[i];//隱含層各單元輸入激活值x1[j]=1.0/(1+exp(-o1[j]-b1[j]));//隱含層各單元的輸出// if(o1[j]+b1[j]>0) x1[j]=1;//else x1[j]=0; }for(int k=0;k<outnode;k++){o2[k]=0.0;for(int j=0;j<hidenode;j++)o2[k]=o2[k]+w1[j][k]*x1[j]; //輸出層各單元輸入激活值x2[k]=1.0/(1.0+exp(-o2[k]-b2[k])); //輸出層各單元輸出// if(o2[k]+b2[k]>0) x2[k]=1;// else x2[k]=0; }for(int k=0;k<outnode;k++){qq[k]=(yd[k]-x2[k])*x2[k]*(1-x2[k]); //希望輸出與實(shí)際輸出的偏差for(int j=0;j<hidenode;j++)w1[j][k]+=rate_w1*qq[k]*x1[j]; //下一次的隱含層和輸出層之間的新連接權(quán) }for(int j=0;j<hidenode;j++){pp[j]=0.0;for(int k=0;k<outnode;k++)pp[j]=pp[j]+qq[k]*w1[j][k];pp[j]=pp[j]*x1[j]*(1-x1[j]); //隱含層的校正誤差for(int i=0;i<innode;i++)w[i][j]+=rate_w*pp[j]*x[i]; //下一次的輸入層和隱含層之間的新連接權(quán) }for(int k=0;k<outnode;k++){e+=fabs(yd[k]-x2[k])*fabs(yd[k]-x2[k]); //計(jì)算均方差 }error=e/2.0;for(int k=0;k<outnode;k++)b2[k]=b2[k]+rate_b2*qq[k]; //下一次的隱含層和輸出層之間的新閾值for(int j=0;j<hidenode;j++)b1[j]=b1[j]+rate_b1*pp[j]; //下一次的輸入層和隱含層之間的新閾值 } }double *BpNet::recognize(double *p) {double x[innode]; //輸入向量double x1[hidenode]; //隱含結(jié)點(diǎn)狀態(tài)值double x2[outnode]; //輸出結(jié)點(diǎn)狀態(tài)值double o1[hidenode]; //隱含層激活值double o2[hidenode]; //輸出層激活值for(int i=0;i<innode;i++)x[i]=p[i];for(int j=0;j<hidenode;j++){o1[j]=0.0;for(int i=0;i<innode;i++)o1[j]=o1[j]+w[i][j]*x[i]; //隱含層各單元激活值x1[j]=1.0/(1.0+exp(-o1[j]-b1[j])); //隱含層各單元輸出//if(o1[j]+b1[j]>0) x1[j]=1;// else x1[j]=0; }for(int k=0;k<outnode;k++){o2[k]=0.0;for(int j=0;j<hidenode;j++)o2[k]=o2[k]+w1[j][k]*x1[j];//輸出層各單元激活值x2[k]=1.0/(1.0+exp(-o2[k]-b2[k]));//輸出層各單元輸出//if(o2[k]+b2[k]>0) x2[k]=1;//else x2[k]=0; }for(int k=0;k<outnode;k++){result[k]=x2[k];}return result; }void BpNet::writetrain() {FILE *stream0;FILE *stream1;FILE *stream2;FILE *stream3;int i,j;//隱含結(jié)點(diǎn)權(quán)值寫(xiě)入if(( stream0 = fopen("w.txt", "w+" ))==NULL){cout<<"創(chuàng)建文件失敗!";exit(1);}for(i=0;i<innode;i++){for(j=0;j<hidenode;j++){fprintf(stream0, "%f\n", w[i][j]);}}fclose(stream0);//輸出結(jié)點(diǎn)權(quán)值寫(xiě)入if(( stream1 = fopen("w1.txt", "w+" ))==NULL){cout<<"創(chuàng)建文件失敗!";exit(1);}for(i=0;i<hidenode;i++){for(j=0;j<outnode;j++){fprintf(stream1, "%f\n",w1[i][j]);}}fclose(stream1);//隱含結(jié)點(diǎn)閥值寫(xiě)入if(( stream2 = fopen("b1.txt", "w+" ))==NULL){cout<<"創(chuàng)建文件失敗!";exit(1);}for(i=0;i<hidenode;i++)fprintf(stream2, "%f\n",b1[i]);fclose(stream2);//輸出結(jié)點(diǎn)閥值寫(xiě)入if(( stream3 = fopen("b2.txt", "w+" ))==NULL){cout<<"創(chuàng)建文件失敗!";exit(1);}for(i=0;i<outnode;i++)fprintf(stream3, "%f\n",b2[i]);fclose(stream3);}void BpNet::readtrain() {FILE *stream0;FILE *stream1;FILE *stream2;FILE *stream3;int i,j;//隱含結(jié)點(diǎn)權(quán)值讀出if(( stream0 = fopen("w.txt", "r" ))==NULL){cout<<"打開(kāi)文件失敗!";exit(1);}float wx[innode][hidenode];for(i=0;i<innode;i++){for(j=0;j<hidenode;j++){fscanf(stream0, "%f", &wx[i][j]);w[i][j]=wx[i][j];}}fclose(stream0);//輸出結(jié)點(diǎn)權(quán)值讀出if(( stream1 = fopen("w1.txt", "r" ))==NULL){cout<<"打開(kāi)文件失敗!";exit(1);}float wx1[hidenode][outnode];for(i=0;i<hidenode;i++){for(j=0;j<outnode;j++){fscanf(stream1, "%f", &wx1[i][j]);w1[i][j]=wx1[i][j];}}fclose(stream1);//隱含結(jié)點(diǎn)閥值讀出if(( stream2 = fopen("b1.txt", "r" ))==NULL){cout<<"打開(kāi)文件失敗!";exit(1);}float xb1[hidenode];for(i=0;i<hidenode;i++){fscanf(stream2, "%f",&xb1[i]);b1[i]=xb1[i];}fclose(stream2);//輸出結(jié)點(diǎn)閥值讀出if(( stream3 = fopen("b2.txt", "r" ))==NULL){cout<<"打開(kāi)文件失敗!";exit(1);}float xb2[outnode];for(i=0;i<outnode;i++){fscanf(stream3, "%f",&xb2[i]);b2[i]=xb2[i];}fclose(stream3); }//輸入樣本 double X[trainsample][innode]= {{0,0,0},{0,0,1},{0,1,0},{0,1,1},{1,0,0},{1,0,1},{1,1,0},{1,1,1}}; //期望輸出樣本 double Y[trainsample][outnode]={{0},{0.1429},{0.2857},{0.4286},{0.5714},{0.7143},{0.8571},{1.0000}};int main() {BpNet bp;bp.init();int times=0;while(bp.error>0.0001){bp.e=0.0;times++;bp.train(X,Y);cout<<"Times="<<times<<" error="<<bp.error<<endl;}cout<<"trainning complete..."<<endl;double m[innode]={1,1,1};double *r=bp.recognize(m);for(int i=0;i<outnode;++i)cout<<bp.result[i]<<" ";double cha[trainsample][outnode];double mi=100;double index;for(int i=0;i<trainsample;i++){for(int j=0;j<outnode;j++){//找差值最小的那個(gè)樣本cha[i][j]=(double)(fabs(Y[i][j]-bp.result[j]));if(cha[i][j]<mi){mi=cha[i][j];index=i;}}}for(int i=0;i<innode;++i)cout<<m[i];cout<<" is "<<index<<endl;cout<<endl;return 0; }總結(jié)
以上是生活随笔為你收集整理的BP人工神经网络的介绍与实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 神经网络模型分类
- 下一篇: easyNeurons 神经网络入门教程