Neural Network based on Eorr Back Propagation典型BP网络c++实现
#include <iostream> #include <fstream> #include <ctime> #include <cmath> using namespace std; /*常量区域*/ #define N 4 //样本的个数 #define IN 2 //输入层神经元个数 #define HN 2 //隐层神经元个数 #define ON 1 //输出层神经元个数 //定义存放学习样本的结构 class StudyData { public: float input[IN]; //输入样本 float teach[ON]; //期望输出(教师信号) StudyData(); virtual ~StudyData(); }; //定义BP神经网络的类结构 class BpNet { public: void GetOutput(); //从键盘输入数据,输出进行保存 void Work(char *weight, char *threshold); //将训练好的数据进行工作 double GetSumErr(); //得到总误差 //进行训练 void Train(char *sampleFileName, char *weight, char *threshold); void ReadWeight(char *weight, char *threshold); //读取权值阈值到神经网络中 void SaveBpNet(char *weight, char *threshold); //保存权值 void UpdateWeight(int m); //更新权值 void ErrorSignal(int m); //算误差信号 void NetInputOutput(int m); //算各层输出输入 void GetTrainingData(char *sampleFileName); //从外存获取样本集 void StartShow(void); StudyData studyData[N]; //存放多组学习样本的数组 float W[HN][IN]; //输入层到隐层权值数组 float V[ON][HN]; //隐层到输出层权值数组 float HU_HN[HN]; //隐层神经元阈值数组 float HU_ON[ON]; //输出层神经元阈值数组 float IN_HN[HN]; //隐层的输入 float OUT_HN[HN]; //隐层的输出 float IN_ON[ON]; //输出层的输入 float OUT_ON[ON]; //输出层的输出 float E[N]; //样本组误差数组,每个分量为一组样本的误差 float stuRate1; //输出层至隐层学习效率 float stuRate2; //隐层至输入层学习效率 float errSignalON[ON]; //δk,输出层的误差信号数组 float errSignalHN[HN]; //δj,隐层的误差信号数组 BpNet(); virtual ~BpNet(); };
// BpNet.cpp: implementation of the BpNet class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "BpNet.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// /*BpNet构造函数,对变量进行初始化*/ BpNet::BpNet() { srand(time(NULL)); //随机数种子 int i, j; for (i=0; i<HN; i++) //输入层到隐层权值初始化为0附近的小数 { for (j=0; j<IN; j++) { W[i][j] = (float)(((rand()/32767.0)*2-1)/2); } } for (i=0; i<ON; i++) //隐层到输出层权值初始化为0附近的小数 { for (j=0; j<HN; j++) { V[i][j] = (float)(((rand()/32767.0)*2-1)/2); } } for (i=0; i<HN; i++) //阈值初始化 { HU_HN[i] = 1.0; } for (i=0; i<ON; i++) //阈值初始化 { HU_ON[i] = 1.0; } stuRate1 = 0.5; //输出层到隐层的学习率初始化 stuRate2 = 0.5; //隐层到输入层的学习率初始化 //测试权值的输出 /* for (i=0; i<HN; i++) { for (j=0; j<IN; j++) { cout << W[i][j] << " "; } cout << endl; } cout << endl; for (i=0; i<ON; i++) { for (j=0; j<HN; j++) { cout << V[i][j] << " "; } cout << endl; }*/ } /*BpNet析构函数,对动态申请内存进行释放*/ BpNet::~BpNet() { } /*程序开始*/ void BpNet::StartShow() { cout << endl; cout << "*************BP算法的使用程序(C++版)********" << endl << endl; } /**从文件中读取样本, 并显示到屏幕上以便于进行核对**/ void BpNet::GetTrainingData(char *sampleFileName) { ifstream infile(sampleFileName, ios::in); //打开样本文件 if (!infile) //如果打开失败 { cerr << "打开样本文件出错" << endl; exit(1); } int i, j; //循环下标 float temp; for (i=0; i<N; i++) //i表示每一趟读取一个样本 { for (j=0; j<IN+ON; j++) { if (!(infile >> temp)) //从文件读入一个数 { cerr << "读入文件元素过程中出错,或以达到文件结尾" << endl; } if (j > (IN-1)) //读入教师信号 { studyData[i].teach[j-IN] = temp; } else //读入样本输入 { studyData[i].input[j] = temp; } } } infile.close(); //关闭文件 /*文件中的样本信息输出到屏幕*/ cout << "从指定文件中成功载入" << N * (IN + ON) << "个数据,显示如下:" << endl << endl; for (i=0; i<IN; i++) //进行排版 { cout << "输入值" << " "; } for (i=0; i<ON; i++) { cout << "期望值" << " "; } cout << endl; for (i=0; i<N; i++) { for (j=0; j<IN+ON; j++) { if (j > (IN-1)) //输出教师信号 { cout <<studyData[i].teach[j-IN] << " "; } else //输出样本输入 { cout <<studyData[i].input[j] << " "; } } cout << endl; } } StudyData::StudyData() { } StudyData::~StudyData() { } void BpNet::NetInputOutput(int m) { //求BP网络的神经网络各层进输入输出 //断言参数m合法 参数m表示第m组测试样本 int i, j; float sum = 0.0; for (i=0; i<HN; i++) //求隐层的净输入输出 { for (j=0; j<IN; j++) { sum += W[i][j] * studyData[m].input[j]; //算隐层第i个神经元不包含阈值的输入 } IN_HN[i] = sum + HU_HN[i]; //算隐层第i个神经元的净输入 OUT_HN[i] = 1.0 / (1.0 + exp(-IN_HN[i])); //算隐层第i个神经元的输出 } sum = 0.0; for (i=0; i<ON; i++) //求输出层的输入输出 { for (j=0; j<HN; j++) { sum += V[i][j] * OUT_HN[j]; //算输出层第i个神经元的输入(不含阈值) } IN_ON[i] = sum + HU_ON[i]; //算输出层第i个神经元的净输入 OUT_ON[i] = 1.0 / (1.0 + exp(-IN_ON[i])); //算输出层第i个神经元的输出 } //测试输入输出 打印到屏幕上 /* for (i=0; i<ON; i++) { cout << "输出层输出" << OUT_ON[i] << endl; } for (i=0; i<HN; i++) { cout << "隐层输入" << IN_HN[i] << endl; }*/ } void BpNet::ErrorSignal(int m) { //算误差信号δk,δj //断言m合法,m表示第m组样本 int k; float absErr[ON]; //期望-输出即绝对误差 float sqrErr = 0.0; //误差平方和 for (k=0; k<ON; k++) { absErr[k] = studyData[m].teach[k] - OUT_ON[k]; //算绝对误差 //算输出层的误差信号 errSignalON[k] = absErr[k] * OUT_ON[k] * (1.0-OUT_ON[k]); sqrErr += absErr[k] * absErr[k]; //算第m组样本的误差平方和 } E[m] = sqrErr / 2; //算第m组样本的总误差 int j; //下面算隐层的误差信号δj float sum; for (j=0; j<HN; j++) { sum = 0.0; for (k=0; k<ON; k++) { sum += errSignalON[k] * V[k][j]; } errSignalHN[j] = sum * OUT_HN[j] * (1-OUT_HN[j]);//得到隐层的误差信号 } } void BpNet::UpdateWeight(int m) { //更新两层权值,阈值 //断言m合法,m表示第m组测试样本 int i, j; float deltaWeight; //权值的改变量 for (i=0; i<ON; i++) //更新隐层到输出层权值 { for (j=0; j<HN; j++) { deltaWeight = stuRate1 * errSignalON[i] * OUT_HN[j];//计算权值的改变量 V[i][j] += deltaWeight; //更新权值 } HU_ON[i] += stuRate1 * errSignalON[i]; //更新输出层阈值 } for (i=0; i<HN; i++) //调整隐层到输出层的权值 { for (j=0; j<IN; j++) { //权值改变量 deltaWeight = stuRate2 * errSignalHN[i] * studyData[m].input[j]; W[i][j] += deltaWeight; //更新权值 } HU_HN[i] += stuRate2 * errSignalHN[i]; //更新隐层阈值 } } void BpNet::SaveBpNet(char *weight, char *threshold) { //保存权值到指定的文件名weight内 //保存阈值到指定的文件名threshold内 ofstream outfile(weight, ios::out); //打开保存权值的文件 if (!outfile) { cerr << "打开权值文件失败" << endl; exit(1); } int i, j; for (i=0; i<HN; i++) //输入层到隐层的权值写入 { for (j=0; j<IN; j++) { outfile << W[i][j] << " "; } } for (i=0; i<ON; i++) //将输出层到隐层的权值写入 { for (j=0; j<HN; j++) { outfile << V[i][j] << " "; } } outfile.close(); //关闭文件 ofstream outfile2(threshold, ios::out); //打开阈值文件 if (!outfile2) { cerr << "阈值文件打开失败" << endl; exit(1); } for (i=0; i<HN; i++) //写入隐层的阈值 { outfile2 << HU_HN[i] << " "; } for (i=0; i<ON; i++) //写入输出层的阈值 { outfile2 << HU_ON[i] << " "; } outfile2.close(); //关闭文件 } void BpNet::ReadWeight(char *weight, char *threshold) { //从训练好网络中读取权值、阈值等 ifstream infile1(weight, ios::in); //打开文件 if (!infile1) { cerr << "打开权值文件失败" << endl; exit(1); } int i, j; float temp; for (i=0; i<HN; i++) //读取输入层权值到隐层权值 { for (j=0; j<IN; j++) { if (infile1 >> temp) { W[i][j] = temp; } } } for (i=0; i<ON; i++) //读取输出层到隐层的权值 { for (j=0; j<HN; j++) { if (infile1 >> temp) { V[i][j] = temp; } } } infile1.close(); //关闭权值文件 ifstream infile2(threshold, ios::in); //打开阈值文件 if (!infile2) { cerr << "阈值文件打开失败" << endl; exit(1); } for (i=0; i<HN; i++) //读取隐层阈值 { if (infile2 >> temp) { HU_HN[i] = temp; /* cout << "隐层的阈值是" << HU_HN[i] << endl;*/ } } for (i=0; i<ON; i++) //读取输出层阈值 { if (infile2 >> temp) { HU_ON[i] = temp; /* cout << "输出层的阈值是" << HU_ON[i] << endl;*/ } } } double BpNet::GetSumErr() { //求总误差,即所有样本的总误差 int m; double sumErr = 0.0; for (m=0; m<N; m++) { sumErr += E[m]; } return sumErr; } void BpNet::Train(char *sampleFileName, char *weight, char *threshold) { //对给定文件名sampleFileName提取样本数据进行训练 //训练达到一定的精度后将权值存于weight //阈值存于threshold内 int limitStudyTimes = 400000; //限定内的学习次数 long int studyFileTimes = 0; //学习文件次数,即对sample整个文件学习一次,算一次 long int studySampleTimes = 0; //样本学习次数,一组样本学习一次算一次 double minErr = 0.000001; //最小的学习误差,达到这个精度后训练完毕 double sumErr; //由实际训练得到的样本总误差 int m; //表示sample文件第m组样本 StartShow(); //程序开始界面 cout << "现在是训练模式···" << endl; GetTrainingData(sampleFileName); //读取样本数据到内存 do { studyFileTimes++; for (m=0; m<N; m++) //N组样本进行学习训练,循环完毕即一个文件学习完毕 { NetInputOutput(m); //算网络内部输入输出 ErrorSignal(m); //算误差信号 UpdateWeight(m); //调整权值 } sumErr = GetSumErr(); //所有样本的总误差 if (studyFileTimes > limitStudyTimes) //超出学习的限定次数,估计收敛不了了,在下去就是无限循环,强制停止 { cout << "超出限定的学习次数,超时了,强制停止程序" << endl; break; } cout << "正在进行的训练次数: "<< studyFileTimes << "\r"; } while (sumErr > minErr); SaveBpNet(weight, threshold); //保存权值、阈值 cout << "学习次数是:" << studyFileTimes << endl; cout << "最后误差是" << sumErr << endl; cout << "权值文件" << weight << "已成功保存" << endl; cout << "阈值文件" << threshold << "已成功保存" << endl; } void BpNet::Work(char *weight, char *threshold) { //将训练完毕后得到的权值用于工作 //从键盘输入数据,由神经网络输出结果 ReadWeight(weight, threshold); //读取文件中训练完毕的权值阈值到神经网络中 char flag; //控制是否继续输入 int i; cout << "现在是工作模式···" << endl; while (1) { GetOutput(); //从键盘输入数据,神经网络的输出存于OUT_ON[]中 for (i=0; i<ON; i++) { cout << "输出为:" << OUT_ON[i] << " "; } cout << endl; cout << "你是否想要继续输入Y/N" << endl; cin >> flag; if ( ('n'==flag) || ('N'==flag) ) { break; } } } void BpNet::GetOutput() { //从键盘输入数据,神经网络的输出存于ON[]中 int i, j; float sum = 0.0; float input[IN]; cout << "请输入神经网络的" << IN << "个输入数据:(以空格隔开)" << endl; for (i=0; i<IN; i++) //输入数据 { cin >> input[i]; } for (i=0; i<HN; i++) //求隐层的净输入输出 { for (j=0; j<IN; j++) { sum += W[i][j] * input[j]; //算隐层第i个神经元不包含阈值的输入 } IN_HN[i] = sum + HU_HN[i]; //算隐层第i个神经元的净输入 OUT_HN[i] = 1.0 / (1.0 + exp(-IN_HN[i])); //算隐层第i个神经元的输出 } sum = 0.0; for (i=0; i<ON; i++) //求输出层的输入输出 { for (j=0; j<HN; j++) { sum += V[i][j] * OUT_HN[j]; //算输出层第i个神经元的输入(不含阈值) } IN_ON[i] = sum + HU_ON[i]; //算输出层第i个神经元的净输入 OUT_ON[i] = 1.0 / (1.0 + exp(-IN_ON[i])); //算输出层第i个神经元的输出 } }
0 1 1
1 0 1
1 1 0
#include "stdafx.h" #include "BpNet.h" int main(int argc, char* argv[]) { /*BP分为两大阶段的函数:用于训练的函数Train(),用于工作的函数Work()*/ BpNet bp; /*对亦或样本进行训练*/ // bp.Train("sample1.txt", "weight1.txt", "yuzhi1.txt"); /*对亦或样本训练结果进行工作测试*/ bp.Work("weight1.txt","yuzhi1.txt"); return 0; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。