/***************************************
* Function:
* 神经网络BP算法
* Lnstree by 2012
****************************************/
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <ctype.h>
#define Ni 1 // 输入层神经元 的个数
#define Nm 4 // 隐藏层神经元 的个数
#define No 1 // 输出层神经元 的个数
#define L 100 // 学习的样本个数
#define Enom 0.02 // 确定误差上限,当神经元对待学习样本的拟合度小于该值时
// 可以认为学习结束
#define loopMax 100000 // 确定最大的迭代次数,当迭代达到loopmax时误差之后仍大于Enom时
// 可以认为学习失败
#define e 2.71828
double E; // 训练每轮迭代误差和
double a, u, n; // BP 的网络参数
double W1[Ni][Nm], W2[Nm][No]; // 网络连接的权值
double D1[Ni][Nm], D2[Nm][No]; // 每轮对连接权值的修正值
double D11[Ni][Nm], D22[Nm][No]; // 对上一轮的连接权值修正,第一轮为0
double pi[L][Ni], pm[L][Nm], po[L][No]; // 三层神经网络的输入
double T[L][No]; //神经网络的期望输出
double Xm[L][Nm], Xo[L][No]; // 隐层和输出层的神经元输出
double Qm[L][Nm], Qo[L][No]; // 对应各连接的$值
double a1[Ni][Nm],a2[Nm][No]; // 神经网络的学习率
void proceedR();
void Initiate(); // 训练时调用的函数,对神经网络初始话
void proceed(); // 训练时调用的函数,信号正向传播
void forQ(); // 训练时调用函数, 求误差偏导,将误差反传
void amend(); // 训练时调用的函数, 根据误差反向传递结果修改权值
double newa(double a, double D); // 修改网络学习速率
double cal(double d); // 计算Signoid函数值
double vcal(double d); // 计算signoid函数的导数
int main()
{
long int i;
int flag;
char choice;
for (;;)
{
flag = 0;
Initiate(); // 初始化神经网络
for (i = 0; i < loopMax; i++)
{
proceed();
if (E < Enom)
{
flag = 1;
break;
}
if( i >= loopMax)
{
flag = -1;
break;
}
forQ();
amend();
}
if (flag > 0)
proceedR();
else
printf("训练失败");
for (;;)
{
printf("是否继续?");
scanf("%c", &choice);
choice = toupper(choice);
if (choice == 'Y') break;
if (choice == 'N') exit(0);
}
}
}
// 函数定义
void Initiate()
{
int i,j,random;
double x, step;
int stime;
long ltime;
ltime = time(NULL);
stime = (unsigned)ltime / 2;
srand(stime);
a=0.02;
u = 1;
n = 1;
printf("本程序将用于拟合sin()函数。\n\n");
for (i = 0; i < Nm; i++)
{
for (j = 0; j < Ni; j++)
{
random = rand()% 100 - 50;
x = random;
x = x/ 100;
W1[j][i] = x; // 设定初始权值为-0.5~0.5
D11[j][i] = 0;
D1[j][i] = 0;
a1[j][i] = 0.01;
}
for (j = 0; j < No; j++)
{
random = rand()% 100 - 50;
x = random;
x = x/ 100;
W2[i][j] = x; // 设定初始权值为-0.5~0.5
D22[i][j] = 0;
D2[i][j] = 0;
a2[i][j] = 0.01;
}
}
step = 1.0 / L; // 将待训练的区间等分步长为step
for (i = 0; i < L; i++)
{
x = i;
pi[i][0] = x*step; // 初始化输入
T[i][0] = sin(pi[i][0]); // 期望输出
}
printf("初始化成功\n\n下面对神经网络进行训练");
}
void proceed()
{
int i, j, k;
E=0 ;
for( i=0; i<L; i++ )
{
for( j=0; j<Nm; j++ )
{
pm[i][j] = 0;
for( k=0; k<Ni; k++ )
{
pm[i][j] = pi[i][k] * W1[k][j] + pm[i][j];
}
Xm[i][j] = cal( pm[i][j] );
}
for( j=0; j<No; j++)
{
po[i][j] = 0;
for( k=0; k<Nm; k++)
{
po[i][j] = Xm[i][k] * W2[k][j] + po[i][j];
}
Xo[i][j] = cal( po[i][j] );
E = E + ( Xo[i][j] - T[i][j] ) * ( Xo[i][j] - T[i][j] ) / 2;
}
}
}
void forQ()
{
int i,j,k;
for( i=0; i<L; i++ )
{
for( j=0; j<No; j++)
{
Qo[i][j] = ( T[i][j] - Xo[i][j] )* vcal( Xo[i][j] );
}
for(j=0; j<Nm; j++)
{
Qm[i][j]=0;
for( k=0; k<No; k++)
{
Qm[i][j] = Qo[i][k] * W2[j][k] + Qm[i][j];
}
Qm[i][j] = Qm[i][j] * vcal( Xm[i][j] );
}
}
}
void amend()
{
int i,j,k;
double D;
for( i=0; i<Nm; i++)
{
for( j=0; j<Ni; j++)
{
D1[j][i]=0;
}
for( j=0; j<No; j++)
{
D2[i][j]=0;
}
}
for( i=0; i<Ni; i++)
{
for( j=0; j<Nm; j++)
{
for( k=0; k<L; k++)
{
D1[i][j] = Qm[k][j] * pi[k][j] + D1[i][j];
}
D = D1[i][j] * D11[i][j] ;//为D11付初值
a1[i][j] = newa( a1[i][j] , D ); // a 付初值
W1[i][j] = W1[i][j] + a1[i][j] * ( n * D1[i][j] + ( 1 - n ) * D11[i][j] );
D11[i][j] = D1[i][j];
}
}
for( i=0; i<Nm; i++)
{
for( j=0; j<No; j++)
{
for( k=0; k<L; k++)
{
D2[i][j] = Qo[k][j] * Xm[k][i] + D2[i][j];
}
D = D2[i][j] * D22[i][j] ;//为D11付初值
a2[i][j] = newa( a2[i][j] , D );
W2[i][j] = W2[i][j] + a2[i][j] * ( n * D2[i][j] + ( 1 - n ) * D22[i][j] );
D22[i][j] = D2[i][j];
}
}
}
void proceedR()
{
int i,j;
float x;
double input, output;
char choice;
for(;;)
{
for (;;)
{
printf("在此输入需要计算的值(0,1):");
scanf("%f", &x);
input = (double) x;
if ((input >= 0.0) && (input <= 1))
{
break;
}
printf("请输入0,1的值!\n");
for (;;)
{
printf("是否继续(Y/N):");
scanf("%c", &choice);
choice = toupper(choice);
if (choice == 'y') break;
if (choice == 'N') exit(0);
}
}
for (i = 0; i < Nm; i++)
{
pm[0][i] = 0;
for (j = 0; j < Ni; j++)
{
pm[0][i] = input * W1[j][i] + pm[0][i];
}
Xm[0][i] = cal(pm[0][i]);
}
for (i = 0; i < No; i++)
{
po[0][i] = 0;
for (j= 0; j < Nm; j++)
{
po[0][i] = Xm[0][j] * W2[j][i] + po[0][i];
}
}
output = cal (po[0][0]);
printf("输入的%20f 对应的结果 %f\n", input, output);
printf("输入的%20f 正常的的结果 %f\n",input, sin(input));
}
}
// S型激励函数
double cal(double d)
{
d = -(d * u);
d = exp(d);
d = 1/(1 + d);
return d;
}
// S型激励函数的导数
double vcal(double d)
{
return u*d*(1 - d);
}
double newa(double a, double D)
{
if (D > 0)
{
if (a <= 0.04)
{
a = a*2;
}
else
{
a = 0.08;
}
}
else if (D < 0)
{
if (a >= 0.02)
{
a = a/2;
}
else
{
a = 0.01;
}
}
return a;
}
注:该程序参考C语言编程百例,不错的一本书,有兴趣可以看看。