BP算法(C语言)实现神经网络(双层感知机)

来源:互联网 发布:天猫魔盒下架原因 知乎 编辑:程序博客网 时间:2024/04/30 05:29

使用C语言在VS下完成标准BP算法对一个双层神经网络(隐藏层神经元个数不确定)进行模型训练从而确定相应参数(即各层神经元阙值及相关权值矩阵)。通过查阅机器学习书籍了解了各相应参数每次微调的变化量相应的公式推导过程从而实现编码过程中参数的调整更新过程。

公式推导细节参考周志华机器学习第五章第三节5.3误差逆传播算法。

对于完成的标准BP算法,其用户手册如下:

    输入神经元个数,即训练样本维数为3,输出神经元个数为2,中间隐藏层节点数可根据用户需求自动调整。这里给出了12个训练样本进行训练从而确定参数模型。

训练样本集和其对应输出样本:{0.4, 0.3, 0.8},        {1, 1}

                            {0.8,0.6, 0.2},        {1, 0}

                            {0.3,0.7, 0.3},        {0, 0}

                            {0.2,0.9, 0.7},        {0, 1}

                            {0.9,0.5, 0.6},        {1, 1}

                            {0.2,0.1, 0.4},        {1, 0}

                            {0.6,0.8, 0.1},        {0, 0}

                            {0.5,0.7, 0.9},        {0, 1}

                            {0.5,0.4, 0.4},        {1, 0}

                            {0.45,0.4, 0.6},       {1, 1}

                            {0.4,0.55, 0.6},       {0, 1}

                            {0.5,0.6, 0.45},       {0, 0}

具体输入与输出相对应关系:

输入样本可以看作是单位体积的正方体空间内的一个点坐标,输出向量的第一个元素代表平面y=x将正方体分成的两个部分,其中x>=y部分则输出结果为1,否则结果就是0;

输出向量的第二个元素代表平面z=0.5将正方体分成的上下两个部分,其中z>=0.5部分输出结果为1;否则就为0.

// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"/* object:标准BP算法对双层神经网络分类(C语言)Author:Zetn.liu*/# define N_out  2# define N_in3# define N_sample12//BP人工神经网络结构体定义typedef struct{int hidden;//隐藏层神经元数目float V[N_in][10];  //输入到隐藏层权值矩阵(10表示隐藏层神经元个数最多为10)float W[10][N_out]; //隐藏层到输出层神经元权值矩阵float H[10];   //隐藏层神经元阙值float O[N_out];    //输出层神经元阙值float rate;        //学习率float accuracy;   //精确度//int max_loop;   //最大循环次数(即全部训练集训练的最大次数)}BPNet;//sigmoid funchion achive//function: 1/(1+pow(e,-x))float sigmoid(float x){return 1/(1 + exp(-x));}//初始化神经网络权重矩阵&&阙值int InitBPNet(BPNet *BP)///////////////////////{printf("请输入中间隐藏层节点数,不超过10个:\n");scanf_s("%d", &(*BP).hidden);printf("请输入学习率:\n");scanf_s("%f", &(*BP).rate);printf("请输入精确度:\n");scanf_s("%f", &(*BP).accuracy);//printf("请输入最大循环次数:\n");//scanf_s("%f", &(*BP).max_loop);int i,j;srand((unsigned)time(NULL));//初始化随机种子//初始化神经网络权值矩阵以及阙值for(i = 0; i < N_in; i++)for(j = 0; j < (*BP).hidden; j++)(*BP).V[i][j] = rand()/(RAND_MAX+1.0);for(i = 0; i < (*BP).hidden; i++)for(j = 0; j < N_out; j++)(*BP).W[i][j] = rand()/(RAND_MAX+1.0);for(i = 0; i < (*BP).hidden; i++)(*BP).H[i] = rand()/(RAND_MAX+1.0);for(i = 0; i < N_out; i++)(*BP).O[i] = rand()/(RAND_MAX+1.0);return 1;}//带标记训练样本集训练神经网络, x为样本,y为理想输出。int TrainBPNet(BPNet *BP,float x[N_sample][N_in],int y[N_sample][N_out]){float rate = (*BP).rate;float accuracy = (*BP).accuracy;int hidden = (*BP).hidden;//int maxloop = (*BP).max_loop;float v[N_in][10],w[10][N_out];float H[10],O[N_out];float dealt_h[10],dealt_O[N_out];//训练过程中权变化量float out1[10],out2[N_out];int i,j,k,n;float temp;//复制结构体中输入到中间层的权值矩阵for(i = 0; i < N_in; i++)for(j = 0; j < hidden; j++)v[i][j] = (*BP).V[i][j];//复制结构体中中间层到输出层的权值矩阵for(i = 0; i < hidden; i++)for(j = 0; j < N_out; j++)w[i][j] = (*BP).W[i][j];//复制结构体隐藏层和输出层阙值for(i = 0; i < hidden; i++)H[i] = (*BP).H[i];for(i = 0; i < N_out; i++)O[i] = (*BP).O[i];float e = accuracy + 1;//确保首次循环能够进行//for(n= 0; e > accuracy && n < maxloop; n++)for(n= 0; e > accuracy; n++){e = 0;//初始化e值for(i = 0; i < N_sample; i++){//计算中间层输出向量for(k = 0; k < hidden; k++){temp = 0;for(j = 0; j < N_in; j++){temp += x[i][j] * v[j][k];}out1[k] = sigmoid(temp - H[k]);}//计算输出层输出向量for(k = 0; k < N_out; k++){temp = 0;for(j = 0; j < hidden; j++)temp += out1[j] * w[j][k];out2[k] = sigmoid(temp - O[k]);}//计算输出层的权修改量,即输出层神经元梯度项(dealt_O[])、for(j = 0; j < N_out; j++)dealt_O[j] = out2[j]*(1-out2[j])*(y[i][j]-out2[j]);//计算隐藏层的权修改量,即隐藏层神经元梯度项(dealt_h[])for(j = 0; j < hidden; j++){temp = 0;for(k = 0; k < N_out; k++)temp += dealt_O[k]*w[j][k]; dealt_h[j] = out1[j]*(1-out1[j])*temp;}//输出层权值矩阵的修改for(j = 0; j < hidden; j++)for(k = 0; k < N_out; k++)w[j][k] += rate * out1[j]*dealt_O[k];//输出层阙值修改for(j = 0; j < N_out; j++)O[j] -= rate * dealt_O[j];//中间层权值矩阵修改for(j = 0; j < N_in; j++)for(k = 0; k < hidden; k++)v[j][k] += rate * x[i][j] * dealt_h[k];//中间层阙值修改for(j = 0; j < hidden; j++)H[j] -= rate * dealt_h[j];//计算输出误差以比较和精度的大小从而终止循环//训练集一次训练完成后累积误差for(j = 0; j < N_out; j++)e += (y[i][j] - out2[j]) * (y[i][j] - out2[j]);}if((n % 10) == 0)printf("累计误差为:%f\n", e);}printf("总共循环次数:%d\n", n);//修改后的隐藏层权值矩阵&&隐藏层阙值printf("修改后的隐藏层权值矩阵:\n");for(i = 0; i < N_in; i++){for(j = 0; j < hidden; j++)printf("%f", v[i][j]);printf("\n");}printf("修改后的隐藏层阙值:\n");for(i = 0; i < hidden; i++)printf("%f", H[i]);printf("\n");//修改后输出层权值矩阵&&输出层阙值printf("修改后的输出层权值矩阵:\n");for(i = 0; i < hidden; i++){for(j = 0; j < N_out; j++)printf("%f", w[i][j]);printf("\n");}printf("修改后的输出层阙值数组:\n");for(i = 0; i < N_out; i++)printf("%f", O[i]);printf("\n");//将结果复制回结构体for(i = 0; i < N_in; i++)for(j = 0; j < hidden; j++)(*BP).V[i][j] = v[i][j];for(i = 0; i < hidden; i++)for(j = 0; j < N_out; j++)(*BP).W[i][j] = w[i][j];for(i = 0; i < hidden; i++)(*BP).H[i] = H[i];for(i = 0; i < N_out; i++)    (*BP).O[i] = O[i];printf("BP神经网络训练结束!\n");return 1;}//测试数据集进行分类过程int UseBPNet(BPNet *BP,float x[N_sample][N_in]){float input[N_in];float out1[10];//中间隐藏层输出float out2[N_out];//输出层输出    ********* output type!!! ************while(1){printf("输入三个数作为输入向量:\n");int i,j;for(i = 0;i < N_in; i++ )scanf_s("%f", &input[i]);float temp;//中间层输出模块for(i = 0; i < (*BP).hidden; i++){temp = 0;for(j = 0; j < N_in; j++)temp += input[j] * (*BP).V[j][i];out1[i] = sigmoid(temp - (*BP).H[i]);}//输出层输出模块for(i = 0; i < N_out; i++){temp = 0;for(j = 0; j < (*BP).hidden; j++ )temp += out1[j] * (*BP).W[j][i];out2[i] = sigmoid(temp - (*BP).O[i]);}printf("输出为:");for(i = 0; i < N_out; i++ )printf("%d", int(out2[i] + 0.1));//printf("%.6f ", out2[i]);printf("\n");}return 1;}//****************************int main(){//训练样本float x[N_sample][N_in] = {{0.4,0.3,0.8},{0.8,0.6,0.2},{0.3,0.7,0.3},{0.2,0.9,0.7},{0.9,0.5,0.6},{0.2,0.1,0.4},{0.6,0.8,0.1},{0.5,0.7,0.9},{0.5,0.4,0.4},{0.45,0.4,0.6},{0.4,0.55,0.6},{0.5,0.6,0.45},};int y[N_sample][N_out] = {{1,1},{1,0},{0,0},{0,1},{1,1},{1,0},{0,0},{0,1},{1,0},{1,1},{0,1},{0,0}};BPNet BP;InitBPNet(&BP);  //初始化过程TrainBPNet(&BP, x, y);//训练过程UseBPNet(&BP, x);//测试过程return 1;}



0 0
原创粉丝点击