神经网络常用于机器学习中的分类,常用的分类算法有:朴素贝叶斯,遗传算法,神经网络,支持向量机等。
在互联网发达的今天,有很多东西需要进行分类,在分类之前,我们常常是有一些数据,找出这些数据符合什么样的
模型,然后根据这些已有数据来预测将来,神经网络就是用来进行这种数据建模的。
神经网络一般情况是有个输入,有
个输出,在输入层和输出层之间通常还有若干个隐含层。实际上,在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