BP神经网络的C++实现

单隐层的BP神经网络的C++实现,激励函数选择了Sigmoid函数 1 / ( 1.0 + exp( - x - d ) ),x为每一层的输入,d为神经元的阈值。因为sigmoid函数输出在[0,1]之间,所以要对数据进行归一化处理,归一化处理的函数选择了线性归一化函数 x = ( x - min ) / ( max - min )。

#pragma once
#include<cassert>
#include<string>
using std::string;

enum Params
{
	InLayerNodesNum=1,//输入层节点数
	MidLayerNodesNum=8,//隐层节点数
	OutLayerNodesNum=1
};

/**
*  BP神经网络train的目的是找出这样的一组权值	_m_weight[Params::MidLayerNodesNum];//隐含层权值
*												_o_weight[Params::MidLayerNodesNum];//网络层权值
*  使输出与训练样本的真实输出间的误差小于阈值   _err
*  如果训练过程中迭代次数超出阈值 _it_nums 则认为训练失败 
*/

class BPNet
{
public:
	BPNet(){init();}
	~BPNet(){}
	
	int train(const double(*p)[Params::InLayerNodesNum],const double (*t)[Params::OutLayerNodesNum],int size);//
	int sim(const double(*p)[Params::InLayerNodesNum],double(*t)[Params::OutLayerNodesNum],int size);//
	bool save(){return save("data.b");}
	bool load(){return load("data.b");}
	bool save(const string& file);
	bool load(const string& file);

private:
	void init();
	void normalize(double **t,int size);//归一化处理
	void unnormalize(double **t,int size);//反归一化处理
private:
	
	double _m_w_rate;//输入层-隐含层权值学习率
	double _o_w_rate;//隐含层-网络层权学习率
	double _m_t_rate;//输入层-隐含层阈值学习率
	double _o_t_rate;//隐含层-网络层阈值学习率

	int _it_nums;//迭代次数
	double _err;//误差限

	double _mint,_maxt;
	
	double _m_threshold[Params::MidLayerNodesNum];//输入层-隐含层阈值
	double _o_threshold[Params::OutLayerNodesNum];//隐含层-网络层阈值
	double _m_weight[Params::InLayerNodesNum][Params::MidLayerNodesNum];//输入层-隐含层权值
	double _o_weight[Params::MidLayerNodesNum][Params::OutLayerNodesNum];//隐含层-网络层权值
};

#include "BPNet.h"

#include<cstdlib>
#include<ctime>
#include<cmath>

#include<fstream>
#include<iostream>

using std::ifstream;
using std::ofstream;


int BPNet::train(const double(*p)[Params::InLayerNodesNum],const double (*t)[Params::OutLayerNodesNum],int size)
{//
	double o1[Params::MidLayerNodesNum];
	double o2[Params::OutLayerNodesNum];
	double error1[Params::MidLayerNodesNum];
	double error2[Params::OutLayerNodesNum];
	double max_error=0;//记录最大误差

	double *perr=new double[size];

	double** t_temp=new double*[size];//保存t的数据,这里的设计很糟糕
	for(int i=0;i<size;++i)
	{
		t_temp[i]=new double[Params::OutLayerNodesNum];
		for(int j=0;j<Params::OutLayerNodesNum;++j)
			t_temp[i][j]=t[i][j];
	}

	normalize(t_temp,size);//归一化处理

	int i=0;
	for(;i<_it_nums;++i)
	{
		for(int j=0;j<size;++j)
		{
			//正向传播
				//计算隐含层输出
			for(int k=0;k<Params::MidLayerNodesNum;++k)
			{
				o1[k]=_m_weight[0][k]*p[j][0];
				for(int m=1;m<Params::InLayerNodesNum;++m)
					o1[k]+=_m_weight[m][k]*p[j][m];
				//激励函数
				o1[k]=1.0/(1+exp(-o1[k]-_m_threshold[k]));//隐含层各单元的输出
			}
				//计算输出层输出
			for(int k=0;k<Params::OutLayerNodesNum;++k)
			{
				o2[k]=_o_weight[0][k]*o1[0];
				for(int m=1;m<Params::MidLayerNodesNum;++m)
					o2[k]+=_o_weight[m][k]*o1[m];
				//激励函数
				o2[k]=1.0/(1+exp(-o2[k]-_o_threshold[k]));//隐含层各单元的输出 
			}

			//反向传播
			for(int k=0;k<Params::OutLayerNodesNum;++k)
			{
				//计算输出层误差
				error2[k]=(t_temp[j][k]-o2[k])*o2[k]*(1-o2[k]);
				//调整权值
				for(int m=0;m<Params::MidLayerNodesNum;++m)
					_o_weight[m][k]+=_o_w_rate*error2[k]*o1[m];
			}
				
			for(int k=0;k<Params::MidLayerNodesNum;++k)
			{
				//计算隐含层误差
				double d=0;
				for(int m=0;m<Params::OutLayerNodesNum;++m)
					d+=error2[m]*_o_weight[k][m];
				//调整权值
				error1[k]=d*o1[k]*(1-o1[k]);

				for(int m=0;m<Params::InLayerNodesNum;++m)
					_m_weight[m][k]+=_m_w_rate*error1[k]*p[j][m];
			}

			double e=0;
			for(int k=0;k<Params::OutLayerNodesNum;++k)
				e+=fabs(t_temp[j][k]-o2[k])*fabs(t_temp[j][k]-o2[k]);
			
			perr[j]=e/2;
			//更新阈值
			for(int k=0;k<OutLayerNodesNum;k++)  
				_o_threshold[k]+=_o_t_rate*error2[k]; //下一次的隐含层和输出层之间的新阈值  
			for(int k=0;k<MidLayerNodesNum;k++)  
				_m_threshold[k]+=_m_t_rate*error1[k]; //下一次的输入层和隐含层之间的新阈值  
		}

		max_error=perr[0];
		for(int j=1;j<size;++j)
			if(perr[j]>max_error)
				max_error=perr[j];
		if(max_error<_err)
			break;
	}

	delete[] perr;

	for(int i=0;i<size;++i)
		delete[] t_temp[i];
	delete[] t_temp;

	if(i>=_it_nums)
		return 0;
	return 1;
}

int BPNet::sim(const double(*p)[Params::InLayerNodesNum],double(*t)[Params::OutLayerNodesNum],int size)
{//
	double maxt,mint;

	double o1[Params::MidLayerNodesNum];

	double** t_temp=new double*[size];//保存t的数据,这里的设计很糟糕
	for(int i=0;i<size;++i)
		t_temp[i]=new double[Params::OutLayerNodesNum];

	for(int i=0;i<size;++i)
	{
		//正向传播
			//计算隐含层输出
		for(int k=0;k<Params::MidLayerNodesNum;++k)
		{
			o1[k]=_m_weight[0][k]*p[i][0];
			for(int m=1;m<Params::InLayerNodesNum;++m)
				o1[k]+=_m_weight[m][k]*p[i][m];
			o1[k]=1.0/(1.0+exp(-o1[k]-_m_threshold[k]));//隐含层各单元的输出 
		}
			//计算输出层输出
		for(int k=0;k<Params::OutLayerNodesNum;++k)
		{
			t_temp[i][k]=_o_weight[0][k]*o1[0];
			for(int m=1;m<Params::MidLayerNodesNum;++m)
				t_temp[i][k]+=_o_weight[m][k]*o1[m];
			t_temp[i][k]=1.0/(1.0+exp(-t_temp[i][k]-_o_threshold[k]));//隐含层各单元的输出 
		}
	}

	unnormalize(t_temp,size);//反归一化
	for(int i=0;i<size;++i)
		for(int j=0;j<Params::OutLayerNodesNum;++j)
			t[i][j]=t_temp[i][j];

	for(int i=0;i<size;++i)
		delete[] t_temp[i];
	delete[] t_temp;

	return 1;
}

void BPNet::init()
{
	_m_w_rate=0.9;//输入层-隐含层权值学习率
	_o_w_rate=0.9;//隐含层-网络层权值学习率
	_m_t_rate=0.9;//输入层-隐含层权值学习率
	_o_t_rate=0.9;//隐含层-网络层权值学习率

	_it_nums=1000;//迭代次数
	_err=0.00001;//误差限
	
	//初始化成随机数
	srand(time(0));
	for(int i=0;i<Params::InLayerNodesNum;++i)
	{
		for(int j=0;j<Params::MidLayerNodesNum;++j)
			_m_weight[i][j]=(2.0*(double)rand()/RAND_MAX)-1;
	}

	for(int i=0;i<Params::MidLayerNodesNum;++i)
	{
		for(int j=0;j<Params::OutLayerNodesNum;++j)
			_o_weight[i][j]=(2.0*(double)rand()/RAND_MAX)-1;
	}
	for(int i=0;i<Params::MidLayerNodesNum;++i)
		_m_threshold[i]=(2.0*(double)rand()/RAND_MAX)-1;
	for(int i=0;i<Params::OutLayerNodesNum;++i)
		_o_threshold[i]=(2.0*(double)rand()/RAND_MAX)-1;
}

bool BPNet::save(const string& file)
{
	ofstream ofile(file);
	for(int i=0;i<Params::InLayerNodesNum;++i)//保存输入层-隐含层权值
	{
		for(int j=0;j<Params::MidLayerNodesNum;++j)
			ofile<<_m_weight[i][j]<<' ';
		ofile<<'\n';
	}

	for(int i=0;i<Params::MidLayerNodesNum;++i)//保存隐含层-网络层权值
	{
		for(int j=0;j<Params::OutLayerNodesNum;++j)
			ofile<<_o_weight[i][j]<<' ';
		ofile<<'\n';
	}
	for(int i=0;i<Params::MidLayerNodesNum;++i)//保存输入层-隐含层阈值
		ofile<<_m_threshold[i]<<" ";
	ofile<<'\n';

	for(int i=0;i<Params::OutLayerNodesNum;++i)//保存隐含层-网络层阈值
		ofile<<_o_threshold[i]<<" ";
	ofile.close();
	return true;
}

bool BPNet::load(const string& file)
{
	ifstream ifile(file);
	char endLine;
	for(int i=0;i<Params::InLayerNodesNum;++i)//加载输入层-隐含层权值
	{
		for(int j=0;j<Params::MidLayerNodesNum;++j)
			ifile>>_m_weight[i][j];
	}

	for(int i=0;i<Params::MidLayerNodesNum;++i)//加载隐含层-网络层权值
	{
		for(int j=0;j<Params::OutLayerNodesNum;++j)
			ifile>>_o_weight[i][j];
	}
	for(int i=0;i<Params::MidLayerNodesNum;++i)//加载输入层-隐含层阈值
		ifile>>_m_threshold[i];

	for(int i=0;i<Params::OutLayerNodesNum;++i)//加载隐含层-网络层阈值
		ifile>>_o_threshold[i];

	ifile.close();
	return true;
}

void BPNet::normalize(double **t,int size)
{//归一化处理
	_mint=0x7FFFFFFF;//这里利用了“魔数”进行处理,不是最好的方法
	_maxt=-0x7FFFFFFF;

	//找到最大和最小值
	for(int i=0;i<size;++i)
	{
		for(int j=0;j<Params::OutLayerNodesNum;++j)
		{
			if(_mint>t[i][j])
				_mint=t[i][j];
			if(_maxt<t[i][j])
				_maxt=t[i][j];
		}
	}

	if(_mint==_maxt)
		_mint=0;

	double range=_maxt-_mint;

	//归一化
	//找到最大和最小值
	for(int i=0;i<size;++i)
	{
		for(int j=0;j<Params::OutLayerNodesNum;++j)
			t[i][j]=(t[i][j]-_mint)/range;
	}
}

void BPNet::unnormalize(double **t,int size)
{//反归一化处理
	double range=_maxt-_mint;

	//归一化
	//找到最大和最小值
	for(int i=0;i<size;++i)
	{
		for(int j=0;j<Params::OutLayerNodesNum;++j)
			t[i][j]=t[i][j]*range+_mint;
	}
}

#include"BPNet.h"
#include<cstdlib>
#include<ctime>
#include<iostream>
using std::cout;
using std::cin;
using std::endl;




int main()
{
	BPNet bpNet;
	const int N=50; 
	const double Pi=3.1415926;

	srand(time(0));

	double a[N][Params::InLayerNodesNum];
	double a2[N][Params::InLayerNodesNum];
	double b[N][Params::OutLayerNodesNum];
	double b2[N][Params::OutLayerNodesNum];
	double b3[N][Params::OutLayerNodesNum];

	for(int i=0;i<N;++i)
	{
		a[i][0]=((2.0*(double)rand()/RAND_MAX)-1)*(Pi/2-1)+1;
		a2[i][0]=((2.0*(double)rand()/RAND_MAX)-1)*(Pi/2-1)+1;

		b[i][0]=2*sin(a[i][0])-0.7;
		b2[i][0]=2*sin(a2[i][0])-0.7;
	}

	bpNet.train(a,b,N);
	bpNet.save();
	bpNet.load();
	bpNet.sim(a,b3,N);

	for(int i=0;i<N;++i)
		cout<<b[i][0]<<" -> "<<b3[i][0]<<endl;

	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值