BP神經網絡+c代碼

BP神經網絡的設計應注意以下幾個問題:

1.  網絡的層數。一般三層網絡結構就可以逼近任何有理函數。增加網絡層數雖然可以提高計算精度,減少誤差,但也使得網絡複雜化,增加網絡訓練時間。如果實在想增加層數,應優先增加隱含層的神經數。

2.  隱含層的神經單元數。網絡訓練精度的提高,可以通過採用一個隱含層而增加神經元數的方法獲得。具體設計上可以使隱含層是輸入層的2倍,然後再適當增加一點餘量。

3.  初始權值選擇。一般區隨機權值是(-1, 1)的隨機數。

4.  學習速率。學習速率決定每一次循環訓練所產生的權值變化量。高的學習速率可能導致系統的不穩定性,但低的學習速率導致較長的訓練時間,可能收斂變慢。一般取0.01~08。可以通過比較不同速率所得到誤差選擇合適的值。

5.  誤差選取。在網絡的訓練過程中,誤差的選取也應當通過對比訓練後確定一個合適的值,也即相對於所需要的隱含層的節點數確定。


計算步驟和原理如下圖:



測試數據如下:

114.6 1.1 0.71 85.0 346
132.4 0.97 0.54 73.0 410
103.5 0.96 0.66 67.0 385
179.3 0.88 0.59 89.0 446
92.7 1.15 0.44 154.0 300
115.0 0.74 0.65 252.0 453
163.6 0.85 0.58 220.0 495
139.5 0.70 0.59 217.0 478
76.7 0.95 0.51 162.0 341
42.1 1.08 0.47 110.0 326
77.8 1.19 0.57 91.0 364
100.6 0.82 0.59 83.0 456
55.3 0.96 0.4 69.0 300
152.1 1.04 0.49 77.0 433
81.0 1.08 0.54 96.0 336
29.8 0.83 0.49 120.0 289
248.6 0.79 0.5 147.0 483
64.9 0.59 0.5 147.0 483
95.7 1.02 0.48 160.0 384
89.9 0.96 0.39 105.0 314
121.8 0.83 0.60 140.0 401
78.5 0.89 0.44 94.0 280
90.0 0.95 0.43 89.0 301


代碼如下:

#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
#include "vector"
using namespace std;


#define noiseVar 0.01   //噪聲強度(防止過度擬合)
#define errorVar  0.001  //誤差設置 
#define alpha 0.35   //學習效率
#define loopNum 10000  //最大的循環次數
#define hideNum 10  //中間層隱節點數
#define dimNum 5  //維數
#define dimIn 4   //輸入層維數
#define dimOut 1   //輸出層維數
#define INF 99999


typedef vector<double> doubleVector;
doubleVector maxSamp;
doubleVector minSamp;


vector<doubleVector> getFileInf(char *File);  //獲取訓練樣本
vector<doubleVector> Normalization(vector<doubleVector> sample);  //樣本歸一化
double getRandNum();  //獲取隨機數
void startTrain(vector<doubleVector> sample);  //開始訓練
void useBP(vector<doubleVector> WX, vector<doubleVector> WY);  //使用BP神經網絡


void main()
{
char *File = "BP.txt";
vector<doubleVector> sample;


sample = getFileInf(File);   //獲取樣本
sample = Normalization(sample);  //樣本歸一化


startTrain(sample);  //開始訓練


}




//開始訓練
void startTrain(vector<doubleVector> sample)
{
int i, j, k, l ,m ,n;
vector<doubleVector> WX;  //輸入層與隱層間的權值
vector<doubleVector> WY;  //隱層間與輸出層的權值
doubleVector ThreX;    //輸入層與隱含層之間的閾值 
doubleVector ThreY;    //輸出層與隱含層之間的閾值
doubleVector temp;


//獲取隨機權重(0.01~0.8)
srand(time(NULL));
//輸入層與隱層間的權值
for(i=0; i<hideNum; i++)
{
temp.clear();
for(j=0; j<dimIn; j++)
temp.push_back(getRandNum());
WX.push_back(temp);
}


//隱層間與輸出層的權值
for(i=0; i<dimOut; i++)
{
temp.clear();
for(j=0; j<hideNum; j++)
temp.push_back(getRandNum());
WY.push_back(temp);
}


//輸入層與隱含層之間的閾值 
for(i=0; i<hideNum; i++)
ThreX.push_back(getRandNum());


//輸出層與隱含層之間的閾值
for(i=0; i<dimOut; i++)
ThreY.push_back(getRandNum());




//開始循環訓練
double sum;
doubleVector hideOut(hideNum);  //隱含層輸出
doubleVector oOut(dimOut);   //輸出層輸出
double oldE, newE;   //希望輸出與計算輸出的偏方差的均方值
doubleVector outError;  //輸出層的誤差
doubleVector hideError;  //隱含層的誤差
int flag = 0;


oldE = newE = 0;
for(i=0; i<loopNum; i++)
{
oldE = newE;
newE = 0;
for(j=0; j<sample.size(); j++)
{
outError.clear();
outError.clear();
hideOut.clear();
oOut.clear();
//隱含層各節點的輸入、輸出
for(k=0; k<hideNum; k++)
{
sum = 0;
for(l=0; l<dimIn; l++)
sum += sample[j][l]*WX[l][k];
hideOut.push_back(1/(1+exp(-(sum-ThreX[k]))));
}




//輸出層各節點的輸入、輸出
for(k=0; k<dimOut; k++)
{
sum = 0;
for(l=0; l<hideNum; l++)
sum += hideOut[l]*WY[k][l];
oOut.push_back(1/(1+exp(-sum-ThreY[k])));
}


//計算希望輸出與計算輸出的偏方差的均方值
for(k=0; k<dimOut; k++)
newE += (sample[j][dimIn+k]-oOut[k])*(sample[j][dimIn+k]-oOut[k])/2.0;




//計算輸出層各節點的誤差
for(k=0; k<dimOut; k++)
outError.push_back((sample[j][dimIn+k]-oOut[k])*oOut[k]*(1-oOut[k]));


//計算隱含層各節點誤差
for(k=0; k<hideNum; k++)
{
sum = 0;
for(l=0; l<dimOut; l++)
sum += outError[l]*WY[l][k];


hideError.push_back(sum*hideOut[k]*(1-hideOut[k]));
}




//修正輸入層與隱含層權值
for(m=0; m<hideNum; m++)
for(n=0; n<dimIn; n++)
WX[m][n] = WX[m][n]+alpha*hideError[m]*sample[j][n];


//修正隱層間與輸出層的權值
for(m=0; m<dimOut; m++)
for(n=0; n<hideNum; n++)
WY[m][n] = WY[m][n]+alpha*outError[m]*hideOut[n];


//修正輸入層與隱含層閥值
for(m=0; m<hideNum; m++)
ThreX[m] = ThreX[m]-alpha*hideError[m];


//修正隱層間與輸出層的閥值
for(m=0; m<dimOut; m++)
ThreX[m] = ThreY[m]-alpha*outError[m];

}

//誤差判斷
if(newE/sample.size()<errorVar)
{
printf("訓練結束!\n訓練次數爲: %d  %lf\n", i, newE);
break;
}
}


printf("隱含層權值:\n");
for(i=0; i<hideNum; i++)
{
for(j=0; j<dimIn; j++)
printf("%lf  ", WX[i][j]);
printf("\n");
}

printf("\n");


printf("輸出層權值:\n");
for(i=0; i<hideNum; i++)
{
for(j=0; j<dimOut; j++)
printf("%lf  ", WX[i][j]);
printf("\n");
}


useBP(WX, WY);


}




//使用BP神經網絡
void useBP(vector<doubleVector> WX, vector<doubleVector> WY)
{
int i, j;


double Input[dimIn];
doubleVector hideOut;
doubleVector oOut;
double sum;


while(1)
{
hideOut.clear();
oOut.clear();


printf("\n輸入數據:\n");
for(i=0; i<dimIn; i++)
scanf("%lf", &Input[i]);


//待測樣本歸一化
for(i=0; i<dimIn; i++)
Input[i] = (0.002+0.996*(Input[i]-minSamp[i]))/(maxSamp[i]-minSamp[i]);




//隱含層各節點的輸入、輸出
for(i=0; i<hideNum; i++)
{
sum = 0;
for(j=0; j<dimIn; j++)
sum += Input[j]+WX[j][i];
hideOut.push_back(1/(1+exp(-sum)));
}

//輸出層各節點的輸入、輸出
for(i=0; i<dimOut; i++)
{
sum = 0;
for(j=0; j<hideNum; j++)
sum += hideOut[j]*WY[i][j];
oOut.push_back(1/(1+exp(-sum)));
}


for(i=0; i<oOut.size(); i++)
oOut[i] = (oOut[i]*(maxSamp[dimIn+i]-minSamp[dimIn+i])-0.02)/0.996+minSamp[dimIn+i];


printf("期望結果:\n");
for(i=0; i<oOut.size(); i++)
printf("%lf   ", oOut[i]);
printf("\n\n");


}


}






//樣本歸一化
vector<doubleVector> Normalization(vector<doubleVector> sample)
{
vector<doubleVector> dst;
int i, j;


doubleVector max(dimNum, 0);
doubleVector min(dimNum, INF);
doubleVector temp;


//尋找樣本最大值和最小值
for(i=0; i<dimNum; i++)
for (j=0; j<sample.size(); j++)
{
if(max[i]<sample[j][i])
max[i] = sample[j][i];
if(min[i]>sample[j][i])
min[i] = sample[j][i];
}


minSamp = min;
maxSamp = max;


//樣本歸一化
for(i=0; i<sample.size(); i++)
{
temp.clear();
for(j=0; j<dimNum; j++)
temp.push_back((0.002+0.996*(sample[i][j]-min[j]))/(max[j]-min[j]));


dst.push_back(temp);
}

return dst;
}




//獲取文件數據
vector<doubleVector> getFileInf(char *File)
{
int i=1;
vector<doubleVector> dst;
doubleVector temp;
double num;

FILE *fp;

fp = fopen(File, "r");

if(fp == NULL)
{
printf("Open file error!\n");
exit(0);
}

//讀取文件的數據
while(fscanf(fp, "%lf", &num)!=EOF)
{
temp.push_back(num);
if(i%dimNum==0)
{
dst.push_back(temp);
temp.clear();
}
i++;
}

fclose(fp);

return dst;
}




//獲取隨機數
double getRandNum()
{
double num;


num = rand()%RAND_MAX;
num = 0.8*num/RAND_MAX;


return num;
}


發佈了38 篇原創文章 · 獲贊 34 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章