基于C++的关于BP神经网络逼近非线性函数的算法
最近这些年神经网络已经变得越来越火,在机器人、游戏AI等各种不同领域我们都能看到神经网络的身影,而现在主流的研究环境都是python、matlab或者是octave等高级语言,我不否认使用这些高级语言的确可以更快的投入到对算法层面的研究,不过每个人都有着自己独特的癖好,而我就是喜欢对一些底层算法进行研究后,通过自己喜爱的编程语言表达出来,这也使我萌发写这篇文章的想法。希望本文也能给读者们带来不一样的感受。
关于神经网络,相信很多人首先了解到的就是梯度下降算法,但是关于其中的偏导数很多刚接触神经网络的萌新们都不知道怎么去使用代码表达出来,而我也自己参考了很多大神的资料用c++的形式将其表现了出来,当然逼近函数只是一个简单的应用,我们关键还是得了解算法的本质。
1.头文件引用和全局变量的定义
#include<iostream>
#include<string>
#include<fstream>
#include<math.h>
#include<random>
using namespace std;
fstream ofs;
uniform_int_distribution<unsigned> u(0,999);
default_random_engine e; // 生成无符号随机整数
int i,j,n;
int flag,Num;
#define range 500//样本大小
#define PI 3.141592
double OUT_Y[range],Value_k[range],Value_k_1[range];
2.定义BP网络中常用的激活函数-Sigmoid函数
double sim(double input)
{
return 1/(1+exp(-input));
}//S函数
3.四种非线性函数和BP网络参数结构体
double out(double input)
{
if(flag==1)return 1/input;
else if(flag==2)return log10(input);
else if(flag==3)return exp(-input);
else if(flag==4)return sin(input);
}
struct BP
{
double Xi[range];//输入值
double yi[range][10];//第一隐层输出值
double Yi[range];//输出值
double Value[range];//期望值
double Ei[range];//误差值
double Eva;//平均误差
double W1[10];//第一隐层权值
double W2[10];//输出层权值
double B1[10];//第一隐层阈值
double B2;//输出层阈值
double yita;//学习步长
double dB1[10];//第一隐层阈值改变量
double dB2;//输出层阈值改变量
double dW1[10];//第一隐层权值改变量
double dW2[10];//输出层权值改变量
}neural;
4.主函数部分
neural.yita=0.005;
neural.B2=(double)u(e)/1000;
for(i=0;i<10;i++)
{
neural.W1[i]=(double)u(e)/1000;
neural.W2[i]=(double)u(e)/1000;
neural.B1[i]=(double)u(e)/1000;
}
cout<<"请选择逼近的函数:"<<endl<<"1为1/x"<<endl<<"2为lgx"<<endl<<"3为e^(-x)"<<endl<<"4为sinx"<<endl;
scanf("%d",&flag);
cout<<"请输入训练次数(600次效果就已经很好)"<<endl;
scanf("%d",&Num);
while(n<Num)
{
for(i=0;i<range;i++)
{
if(flag==1)neural.Xi[i]=(double)u(e)/10+1;
else if(flag==2)neural.Xi[i]=(double)u(e)/100+1;
else if(flag==3)neural.Xi[i]=(double)u(e)/100;
else if(flag==4)neural.Xi[i]=(double)u(e)/300;
}
for(i=0;i<range;i++)
{
for(j=0;j<10;j++)
{
neural.yi[i][j]=sim(neural.Xi[i]*neural.W1[j]-neural.B1[j]);
neural.Yi[i]+=neural.yi[i][j]*neural.W2[j];
}
neural.Yi[i]=neural.Yi[i]-neural.B2;//计算输出值
neural.Value[i]=out(neural.Xi[i]);//qiwang
neural.Ei[i]=neural.Yi[i]-neural.Value[i];
梯度下降///
neural.dB2=-1*neural.yita*neural.Ei[i];
for(j=0;j<10;j++)
{
neural.dW2[j]=neural.yita*neural.Ei[i]*neural.yi[i][j];
neural.dB1[j]=neural.W2[j]*neural.yi[i][j]*(1-neural.yi[i][j])*neural.Ei[i]*neural.yita*(-1);
neural.dW1[j]=neural.W2[j]*neural.yi[i][j]*(1-neural.yi[i][j])*neural.Ei[i]*neural.yita*neural.Xi[i];
}
更新权值和阈值///
neural.B2=neural.B2-neural.dB2;
for(j=0;j<10;j++)
{
neural.B1[j]=neural.B1[j]-neural.dB1[j];
neural.W1[j]=neural.W1[j]-neural.dW1[j];
neural.W2[j]=neural.W2[j]-neural.dW2[j];
neural.yi[i][j]=0;
neural.Eva=0;
}
neural.Yi[i]=0;
}
for(j=0;j<range;j++)
{
neural.Eva+=neural.Ei[j];
}
neural.Eva=neural.Eva*neural.Eva/(2*range);//计算平均误差
n++;
}
测试集
for(i=0;i<range;i++)
{
neural.Xi[i]=(double)u(e)/300;
}
for(i=0;i<range;i++)
{
for(j=0;j<10;j++)
{
neural.yi[i][j]=sim(neural.Xi[i]*neural.W1[j]-neural.B1[j]);
neural.Yi[i]+=neural.yi[i][j]*neural.W2[j];
}
neural.Yi[i]=neural.Yi[i]-neural.B2;//计算输出值
neural.Value[i]=out(neural.Xi[i]);//qiwang
neural.Ei[i]=neural.Yi[i]-neural.Value[i];
OUT_Y[i]=neural.Yi[i];
neural.Yi[i]=0;
}
for(i=0;i<range;i++)
{
cout<<"输入值为:"<<neural.Xi[i]<<" 计算值为:"<<OUT_Y[i]<<" 期望值为:"<<neural.Value[i]<<endl;
}
system("color 07");
system("pause");
}
5.总结
以上就是BP网络逼近四种不同非线性函数的源代码,因为本文主要是讲解BP网络的代码化,所以没有提及BP网络的数学表达式,如果有兴趣的话可以在很容易在网上找到对应公式,我这次是采用了单隐层,每层10个神经元就可以很好的达到了逼近的效果,训练500组后平均误差已经到了小数点后两位,如果代码存在什么问题或者有什么可以改善的地方,欢迎大家指正。