BP神經網絡


      由於課題需要學習神經網絡也有一段時間了,每次只是調用一下matlab的newff函數設置幾個參數,就自以為掌握了。真是可笑,會了其實只是會使用,一知半解而已。

      本來想寫人工神經網絡,但是范圍太廣,無法駕馭,姑且就先寫BP吧,因為BP是目前應用最廣泛的神經網絡模型之一。

一.人工神經網絡

      人工神經網絡(ANN)的研究在一定程度上收到了生物學的啟發,因為生物的學習系統是有相互連接的神經元(neuron)組成的異常復雜的網絡。而人工神經網絡與此大體相似,它是由一系列簡單神經元相互密集連接構成,其中每一個神經元有一定數量的實值輸入(也可以是其他神經元的輸出),並產生單一的實數輸出(可能成為其他很多神經元的輸入)。

      下圖是一個人工神經元模型,可以幫助理解:

      其中:X1~Xn是從其他神經元傳來的輸入信號,Wij表示從神經元j到神經元i的連接權值,θ表示閾值(threshold),f為激活函數,Yi為神經元i輸出。

二.適合神經網絡學習的問題

      ANN適合具有以下特征的問題:

      1.實例是用很多“屬性-值”對表示的;

      2.目標函數的輸出可能是離散值、實數值或者由若干實數屬性或離散屬性組成的向量;

      3.訓練數據可能包含錯誤;

      4.可容忍長時間的訓練;

      5.可能需要快速求出目標函數值;

      6.人類能否理解學到的目標函數是不重要的。

      與其說這些是適合ANN的問題,不如說ANN本身特征如此,適用於這些問題。

三.BP算法

      BP算法是一種按誤差逆傳播算法訓練的多層前饋網絡。前饋網絡只在訓練過程中會有反饋信號,而在分類過程中數據只能向前傳送,直到到達輸出層,層間沒有向后的反饋信號。也就是說,訓練時權值W會根據反饋信號不斷更新,得到一個學習機net,而分類的時候直接使用net中訓練好的W,不需要再更新。

1.基本原理

      利用輸出后的誤差來估計輸出層的直接前導層的誤差,再用這個誤差估計前一層的誤差,如此一層一層的反傳下去,就獲得了所有其他各層的誤差估計。

2.三層BP網絡模型

      根據Kolrnogorov定理,一個3層BP神經網絡能夠實現對任意非線性函數進行逼近,一個典型的3層BP神經網絡模型如下:

3.激活函數

      激活函數必須處處可導,一般都使用S型函數。

4.算法推導

      好吧,到了最為繁瑣也最為重要的一部分,這也能夠區分“會使用”和“真正掌握”。

      首先給出一些定義:

      輸入層有n個神經元,隱含層有p個神經元,輸出層有q個神經元;

      輸入向量:x=(x1,x2,...,xn)

      隱含層輸入向量:hi=(hi1,hi2,...,hip)     

      隱含層輸出向量:ho=(ho1,ho2,...,hop)    

      輸出層輸入向量:yi=(yi1,yi2,...yiq)

      輸出層輸出向量:yo=(yo1,yo2,...yoq)

      期望輸出向量:d=(d1,d2,...,dq)

      輸入層與隱含層的連接權值:Wih

      隱含層與輸出層的連接權值:Who

      隱含層各神經元的閾值:bh

      輸出層各神經元的閾值:bo

      樣本數據個數:k=1,2,...,m

      激活函數:S型函數

      誤差函數:

       算法步驟:

      對給予的一個輸入樣本,計算隱含層各神經元的輸入輸出:

      利用網絡期望輸出和實際輸出,計算誤差函數對輸出層各神經元的偏導數δo(k):

      利用隱含層到輸出層的連接權值、輸出層的δo(k)和隱含層的輸出計算誤差函數對隱含層各神經元的偏導數δh(k):

 

      利用輸出層各神經元的δo(k)和隱含層各神經元的輸出來修正連接權值Who(k)

      隱含層各神經元的δh(k)和輸入層各神經元的輸入修正連接權Wih(k)

      計算全局誤差,判斷網絡誤差是否滿足要求。當誤差達到預設精度或學習次數大於設定的最大次數,則結束算法。否則,選取下一個學習樣本及對應的期望輸出,進入下一輪學習。

      當誤差達到預設精度或學習次數大於設定的最大次數,則結束算法。否則,選取下一個學習樣本及對應的期望輸出,進入下一輪學習。     

5.編程實驗     

      好了,看懂了BP的算法步驟,再給出一個BP算法C語言實例幫助理解: 

#include"stdafx.h"
#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>
#include<ctime>
using namespace std;

//輸入一對數(x,y),輸出為xxxx(x=0或1)
//判斷(x,y)在第幾象限,若在第一象限,則輸出1000,以此類推;
//樣本為隨機的1000對(x,y)
//對指定輸入對求其所在象限;

const int inputnode=2;//輸入層神經元個數
const int hidenode=4;//隱含層神經元個數
const int outputnode=4;//輸出層神經元個數
const int sample_num=1000;//樣本個數

double w_ih[inputnode][hidenode];//輸入層隱含層間權值矩陣
double w_ho[hidenode][outputnode];//隱含層輸出層間權值矩陣

double sita_h[hidenode];//隱含層間閾值
double sita_o[outputnode];//輸出層間閾值

double sample[sample_num][inputnode];
double sample_label[sample_num][outputnode];

double error[sample_num];
double totalerror=0;

double x[sample_num][inputnode];//網絡輸入
double a[sample_num][hidenode];//隱層的輸出
double y[sample_num][outputnode];//網絡輸出
double d[sample_num][outputnode];//樣本參考輸出

double delta_ih[sample_num][hidenode];//輸入到隱層的權值修正
double delta_ho[sample_num][outputnode];//隱層到輸出的權值修正

const int epochs=10000;//迭代次數
const double goal=0.15;//誤差目標精度
const double lr=0.01;//學習速率

int count=0;

void init()
{
    //生成樣本
    int i,j;
    srand((unsigned)time(NULL));
    for(i=0;i<sample_num;i++)
    {    
        sample[i][0]=(double)(rand()%20000-10000)/10000;
        sample[i][1]=(double)(rand()%20000-10000)/10000;
        if(sample[i][0]>0&&sample[i][1]>0)
        {
            sample_label[i][0]=1;
            sample_label[i][1]=0;
            sample_label[i][2]=0;
            sample_label[i][3]=0;
        }
        if(sample[i][0]<0&&sample[i][1]>0)
        {
            sample_label[i][0]=0;
            sample_label[i][1]=1;
            sample_label[i][2]=0;
            sample_label[i][3]=0;
        }
        if(sample[i][0]<0&&sample[i][1]<0)
        {
            sample_label[i][0]=0;
            sample_label[i][1]=0;
            sample_label[i][2]=1;
            sample_label[i][3]=0;
        }
        if(sample[i][0]>0&&sample[i][1]<0)
        {
            sample_label[i][0]=0;
            sample_label[i][1]=0;
            sample_label[i][2]=0;
            sample_label[i][3]=1;
        }
    }
    //初始化權值矩陣
    for(i=0;i<inputnode;i++)
    {
        for(j=0;j<hidenode;j++)
        {
            w_ih[i][j]=(double)(rand()%20000-10000)/100000;
        }
    }
    for(i=0;i<hidenode;i++)
    {
        for(j=0;j<outputnode;j++)
        {
            w_ho[i][j]=(double)(rand()%20000-10000)/100000;
        }
    }
    //初始化閾值
    for(i=0;i<hidenode;i++)
    {
        sita_h[i]=(double)(rand()%1000)/1000;
    }
    for(i=0;i<outputnode;i++)
    {
        sita_o[i]=(double)(rand()%1000)/1000;
    }
    //初始化誤差
    for(i=0;i<sample_num;i++)
    {
        error[i]=0;
    }
    //初始化權值調整系數
    for(i=0;i<sample_num;i++)
    {
        for(j=0;j<hidenode;j++)
        {
            delta_ih[i][j]=0;
        }
    }
    for(i=0;i<sample_num;i++)
    {
        for(j=0;j<outputnode;j++)
        {
            delta_ho[i][j]=0;
        }
    }
}

void train()
{
    cout<<"BP網絡開始訓練:"<<endl;
    int i,j,k;
    for(int loop=0;loop<epochs;loop++)
    {
        totalerror=0;
        int count=0;
        for(i=0;i<sample_num;i++)
        {
            //讀入樣本
            x[count][0]=sample[count][0];
            x[count][1]=sample[count][1];
            d[count][0]=sample_label[count][0];
            d[count][1]=sample_label[count][1];
            d[count][2]=sample_label[count][2];
            d[count][3]=sample_label[count][3];
            //計算隱含層輸出
            double net_h[hidenode];
            for(j=0;j<hidenode;j++)
            {
                net_h[j]=w_ih[0][j]*x[count][0]+w_ih[1][j]*x[count][1]-sita_h[j];
                a[count][j]=1/(1+exp(-net_h[j]));
            }
            //計算輸出層輸出
            double net_o[outputnode];
            for(k=0;k<outputnode;k++)
            {
                net_o[k]=0;
                for(j=0;j<hidenode;j++)
                {
                    net_o[k]+=w_ho[j][k]*a[count][j];
                }
                net_o[k]-=sita_o[k];
                y[count][k]=1/(1+exp(-net_o[k]));
            }
            //計算樣本誤差
            for(k=0;k<outputnode;k++)
            {
                error[count]+=(d[count][k]-y[count][k])*(d[count][k]-y[count][k]);
            }
            error[count]/=2;
            //計算隱含層輸出層間權值調整系數
            for(k=0;k<outputnode;k++)
            {
                delta_ho[count][k]=(d[count][k]-y[count][k])*y[count][k]*(1-y[count][k]);
            }
            //計算輸入層到隱含層的權值調整系數
            for(j=0;j<hidenode;j++)
            {
                for(k=0;k<outputnode;k++)
                {
                    delta_ih[count][j]+=delta_ho[count][k]*w_ho[j][k]*a[count][j]*(1-a[count][j]);
                }
            }
            totalerror+=error[count];
            count++;
        }        
        cout<<"第"<<loop+1<<"次迭代,"<<"總體方差為:"<<totalerror<<endl;
        //調整w_ih
        double temp=0.0;
        for(i=0;i<inputnode;i++)
        {
            for(j=0;j<hidenode;j++)
            {
                temp=0;
                for(k=0;k<sample_num;k++)
                {
                    temp+=delta_ih[k][j]*x[k][i];
                }
                w_ih[i][j]+=lr*temp;
            }
        }
        //調整sita_h
        for(j=0;j<hidenode;j++)
        {
            temp=0;
            for(k=0;k<sample_num;k++)
            {
                temp-=delta_ih[k][j];
            }
            sita_h[j]+=0.005*temp;
        }
        //調整w_ho
        for(i=0;i<hidenode;i++)
        {
            for(j=0;j<outputnode;j++)
            {
                temp = 0;
                for (k=0;k<sample_num;k++)
                {
                    temp+=delta_ho[k][j]*a[k][i];
                }
                w_ho[i][j]+=lr*temp;
            }
        }
        //調整sita_o
        for (i=0;i<outputnode;i++)
        {
            temp=0;
            for(j=0;j<sample_num;j++)
            {
                temp-=delta_ho[j][i];
            }
            sita_o[i]+=0.005*temp;
        }
        if(totalerror<goal) break;
    }
}

void test(double x0,double y0)
{
    int flag;
    int j,k;
    double net_h[hidenode];
    double net_o[outputnode];
    double hide[hidenode];
    double output[outputnode];
    for(j=0;j<hidenode;j++)
    {
        net_h[j]=w_ih[0][j]*x0+w_ih[1][j]*y0-sita_h[j];
        hide[j]=1/(1+exp(-net_h[j]));
    }
    for(k=0;k<outputnode;k++)
    {
        net_o[k]=0;
        for(j=0;j<hidenode;j++)
        {
            net_o[k]+=w_ho[j][k]*hide[j];
        }
        net_o[k]-=sita_o[k];
        output[k]=1/(1+exp(-net_o[k]));
    }
    if(output[0]>0.5) flag=1;
    if(output[1]>0.5) flag=2;
    if(output[2]>0.5) flag=3;
    if(output[3]>0.5) flag=4;
    cout<<"("<<x0<<","<<y0<<")"<<"在第"<<flag<<"象限"<<endl;
}

int main()
{
    init();
    clock_t starttime=clock();
    train();
    clock_t endtime=clock();
    cout<<"訓練時間為:"<<endtime-starttime<<"毫秒"<<endl;
    while(1)
    {
        double a,b;
        cout<<"請輸入實數對(x,y):";
        cin>>a>>b;
        test(a,b);
    }
    return 0;
}

 

參考文獻:

1. 《機器學習》Tom Mitchell著

2.  其他

 

如有任何問題,歡迎批評指正,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM