神经网络常用于机器学习中的分类,常用的分类算法有:朴素贝叶斯,遗传算法,神经网络,支持向量机等。
在互联网发达的今天,有很多东西需要进行分类,在分类之前,我们常常是有一些数据,找出这些数据符合什么样的
模型,然后根据这些已有数据来预测将来,神经网络就是用来进行这种数据建模的。
神经网络一般情况是有个输入,有个输出,在输入层和输出层之间通常还有若干个隐含层。实际上,在1989年
Robert Hecht-Nielsen证明了对于任何闭区间内的一个连续函数都可以用一个隐含层的BP网络来逼近,因而一个
3层的BP网络就可以完成任意的维到维的映射。
在BP(Back Propagation)神经网络中,输入层和输出层的节点个数都是确定的,那么隐含层的节点应该设置为
多少才是最合适的呢?
实际上,隐含层的节点个数的多少是对神经网络的性能有影响的,有一个经验公式如下:
其中,为隐含层的节点个数,为输入层的节点个数,为输出层的节点个数,为1~10之间的调节常数。
BP神经网络的原理
简单来说,BP神经网络分为3层,输入层,隐含层,输出层。每层之间每两个节点之间都有一个权值,每一个节
点都有一个阀值,并且还有一个激活函数,每一个神经元的模型如下:
BP神经网络在正常训练过程中,分为两个步骤反复调整:
(1)工作信号正向传递子过程
(2)误差信号反向传递子过程
BP神经网络算法基本流程如下:
(1)构造初始神经网络,随机初始化神经网络的权值和阀值。
(2)对于训练样本集中的每一个样本,输入到神经网络,计算每个神经元的输出。
(3)利用能量函数计算神经网络对于该样本的能量值,计算每个神经元节点所产生的误差,反向传递该误差,
修正各个权值和阀值。
(4)重复(2)和(3),直到算法终止条件成立退出。
在上述算法流程中,每个神经元的数学公式为:
激活函数一般选择S型激活函数。
BP神经网络代码:
#include <iostream> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <time.h> #include <math.h> using namespace std; const double A = 30.0; const double B = 10.0; const int MAX = 500; //最大训练次数 const double COEF = 0.0035; //网络的学习效率 const double BCOEF = 0.001; //网络的阀值调整效率 const double ERROR = 0.002; //网络训练中允许的误差 const double ACCURACY = 0.0005; //网络要求精度 int cnt, t; double differ,is; double sample[41][4]= { {0,0,0,0}, {5,1,4,19.020}, {5,3,3,14.150}, {5,5,2,14.360}, {5,3,3,14.150}, {5,3,2,15.390}, {5,3,2,15.390}, {5,5,1,19.680}, {5,1,2,21.060}, {5,3,3,14.150}, {5,5,4,12.680}, {5,5,2,14.360}, {5,1,3,19.610}, {5,3,4,13.650}, {5,5,5,12.430}, {5,1,4,19.020}, {5,1,4,19.020}, {5,3,5,13.390}, {5,5,4,12.680}, {5,1,3,19.610}, {5,3,2,15.390}, {1,3,1,11.110}, {1,5,2,6.521}, {1,1,3,10.190}, {1,3,4,6.043}, {1,5,5,5.242}, {1,5,3,5.724}, {1,1,4,9.766}, {1,3,5,5.870}, {1,5,4,5.406}, {1,1,3,10.190}, {1,1,5,9.545}, {1,3,4,6.043}, {1,5,3,5.724}, {1,1,2,11.250}, {1,3,1,11.110}, {1,3,3,6.380}, {1,5,2,6.521}, {1,1,1,16.000}, {1,3,2,7.219}, {1,5,3,5.724} }; double b[4][10],bc[4][10]; double w[4][10][10],wc[4][10][10]; double netin[4][10],o[4][10],d[4][10]; //计算NN网络隐含层和输出层的输出 void NetworkOut(int m,int n) { int k = 2; //隐含层各节点的输出 for(int i=1; i<=m; i++) //m为隐含层结点的个数 { netin[k][i] = 0; for(int j=1; j<=3; j++) //隐含层的每个结点均有3个输入变量 netin[k][i] += o[k-1][j] * w[k][j][i]; netin[k][i] -= b[k][i]; o[k][i] = A / (1 + exp(-netin[k][i] / B)); } k = 3; //输出层各节点的输出 for(int i=1; i<=n; i++) { netin[k][i] = 0; for(int j=1; j<=m; j++) netin[k][i] += o[k-1][j] * w[k][j][i]; netin[k][i] -= b[k][i]; o[k][i] = A / (1 + exp(-netin[k][i] / B)); } } //计算NN网络的反向传播误差 void CalcInvError(int m,int n) { t = cnt - 1; d[3][1] = (o[3][1] - sample[t][3]) * (A / B) * exp(-netin[3][1] / B) / pow(1 + exp(-netin[3][1] / B), 2); //隐含层的误差 int k = 2; for(int i=1; i<=m; i++) { double tmp = 0; for(int j=1; j<=n; j++) tmp += w[k+1][i][j] * d[k+1][j]; d[k][i] = tmp * (A / B) * exp(-netin[k][i] / B) / pow(1 + exp(-netin[k][i] / B), 2); } } //计算网络权值W的调整量 void Calcwc(int m,int n) { int k = 3; //输出层与隐含层之间的权值调整 for(int i=1; i<=m; i++) for(int j=1; j<=n; j++) wc[k][i][j] = -COEF * d[k][j] * o[k-1][i] + 0.5 * wc[k][i][j]; k = 2; //输入层与隐含层之间的权值调整 for(int i=1; i<=m; i++) for(int j=1; j<=n; j++) wc[k][i][j] = -COEF * d[k][j] * o[k-1][i] + 0.5 * wc[k][i][j]; } //计算网络阀值的调整量 void Calcbc(int m,int n) { for(int i=1; i<=m; i++) bc[2][i] = BCOEF * d[2][i]; for(int i=1; i<=n; i++) bc[3][i] = BCOEF * d[3][i]; } //调整网络权值 void ChangeWeight(int m,int n) { for(int i=1; i<=3; i++) { for(int j=1; j<=m; j++) { //为了保证较好的鲁棒性,计算权值时乘惯性系数0.9 w[2][i][j] = 0.9 * w[2][i][j] + wc[2][i][j]; //printf("w[2][%d][%d] = %lf\n",i,j,w[2][i][j]); } } for(int i=1; i<=m; i++) { for(int j=1; j<=n; j++) { w[3][i][j] = 0.9 * w[3][i][j] + wc[3][i][j]; // printf("w[3][%d][%d] = %lf\n",i,j,w[3][i][j]); } } } //调整网络阀值 void Changeb(int m,int n) { for(int i=1; i<=m; i++) b[2][i] += bc[2][i]; for(int i=1; i<=n; i++) b[3][i] += bc[3][i]; } //清除网络权值变化量和网络阀值变化量 void Clear() { memset(wc,0,sizeof(wc)); memset(bc,0,sizeof(bc)); } double Random() { int t = 100; t += rand() % 400; return t / 5000.0; } //初始化网络权值 void Init() { srand(time(0)); for(int i=0; i<4; i++) { for(int j=0; j<10; j++) { b[i][j] = Random(); //初始化网络阀值 for(int k=0; k<10; k++) w[i][j][k] = Random(); //初始化网络权值 } } } //计算网络单个样本误差 void CalcDiffer() { t = cnt - 1; differ = 0.5 * (o[3][1] - sample[t][3]) * (o[3][1] - sample[t][3]); } void CalcList() { is = 0; for(int i=0; i<=19; i++) { o[1][1] = sample[i][0]; o[1][2] = sample[i][1]; o[1][3] = sample[i][2]; NetworkOut(8,1); is += (o[3][1] - sample[i][3]) * (o[3][1] - sample[i][3]); } is /= 20; } //训练网络 void trainNN() { Init(); for(int time=1; time<=MAX; time++) { cnt = 0; while(cnt <= 40) { o[1][1] = sample[cnt][0]; o[1][2] = sample[cnt][1]; o[1][3] = sample[cnt][2]; cnt++; Clear(); NetworkOut(8,1); CalcDiffer(); while(differ > ERROR) { CalcInvError(8,1); Calcwc(8,1); Calcbc(8,1); ChangeWeight(8,1); Changeb(8,1); NetworkOut(8,1); CalcDiffer(); } } printf("This is %d times trainning NN...\n",time); CalcList(); printf("is == %lf\n\n\n",is); if(is < ACCURACY) break; } } int main() { printf("Please wait for the trainning NN...\n\n"); trainNN(); printf("The trainning complete!\n"); char ch = 'y'; while(ch == 'Y' || ch == 'y') { printf("Please input the data to be tested!\n"); for(int i=1; i<=3; i++) { double x; scanf("%lf",&x); o[1][i] = x; } NetworkOut(8,1); double ans = o[3][1]; printf("The answer is %lf\n",ans); printf("Still test? [Yes] or [No]\n"); ch = getchar(); } return 0; }
原文地址:http://blog.csdn.net/acdreamers/article/details/27183751