BP神经网络c++代码实现

本文详细介绍了BP神经网络的概念,包括其学习规则、反向传播机制和梯度下降法的应用。通过实例演示了如何使用BP算法处理鸢尾花数据集,展示了神经网络的权重调整过程和测试结果。
摘要由CSDN通过智能技术生成

BP神经网络概念

首先从名称中可以看出,Bp神经网络可以分为两个部分,bp和神经网络。bp是 Back Propagation 的简写 ,意思是反向传播。

BP网络能学习和存贮大量的输入-输出模式映射关系,而无需事前揭示描述这种映射关系的数学方程。它的学习规则是使用最速下降法,通过反向传播来不断调整网络的权值和阈值,使网络的误差平方和最小。

其主要的特点是:信号是正向传播的,而误差是反向传播的。

举一个例子,某厂商生产一种产品,投放到市场之后得到了消费者的反馈,根据消费者的反馈,厂商对产品进一步升级,优化,一直循环往复,直到实现最终目的——生产出让消费者更满意的产品。产品投放就是“信号前向传播”,消费者的反馈就是“误差反向传播”。这就是BP神经网络的核心。

单隐层前馈神经网络,与多隐层前馈神经网络(最下层为输入层,最上层为输出层)
在这里插入图片描述

BP神经网络公式推导

第 j 个输出神经元的输入: y ^ j = f ( β j − θ j ) 其中 θ j 为输出层的阈值 第j个输出神经元的输入:\hat{y}_{j}=f(\beta _{j}-\theta _{j}) 其中\theta _{j}为输出层的阈值 j个输出神经元的输入:y^j=f(βjθj)其中θj为输出层的阈值
第 h 个隐层神经元的输出: b h = f ( α h − γ h ) 其中 γ h 为隐藏层的阈值 第h个隐层神经元的输出:b_{h}=f(\alpha _{h}-\gamma _{h})其中\gamma _{h}为隐藏层的阈值 h个隐层神经元的输出:bh=f(αhγh)其中γh为隐藏层的阈值
在这里插入图片描述
假设隐层和输出层神经元都使用Sigmoid函数
在这里插入图片描述
在这里插入图片描述
其中BP神经网络采用梯度下降法来调整参数。梯度下降在机器学习中应用十分广泛,它的主要目的是通过迭代找到目标函数的最小值,或者收敛到最小值。梯度下降的基本过程就和下山的场景很类似。首先我们有一个可微分的函数,这个函数就代表着一座山。目标是找到这个函数的最小值,也就是山底。那最快的下山的方式就是找到当前位置最陡峭的方向,然后沿着此方向向下走,对应到函数中,就是找到给定点的梯度,然后朝着梯度相反的方向,就能让函数值下降的最快。
所以,我们重复利用这个方法,反复求取梯度,最后就能到达局部的最小值
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
标准BP算法伪代码如下
在这里插入图片描述
学习率 η \eta η控制着算法每一轮迭代中的更新步长,若 η \eta η太大可能会错过 最低点,容易产生震荡;若 η \eta η太小则收敛速度可能过慢.
在这里插入图片描述

BP神经网络的应用

针对鸢尾花数据集构建一个神经网络

在这里插入图片描述
数据预处理后得到(其中Iris-setosa:0.000000,Iris-versicolor:0.500000,Iris-virginica:1.000000)
在这里插入图片描述
针对鸢尾花数据集构建一个神经网络
因数据有四个属性,所以输入层为4个神经元加1个“哑结点”,
鸢尾花是三分类问题,输出层用一个神经元
隐层用3个神经元加1个“哑结点” ,隐层神经元数量是可以调整的
在这里插入图片描述
BP算法的实现过程
S1: 从文件读入训练样本,存放到 x[N][M+1] 和 y[N]中
S2: 初始化权重 v[M+1][H] 和 w[H+1]
S3: 开始训练神经网络
3-1:对每一个样本进行以下计算(最外层循环)
3-2:计算隐层的输出,结果存放在b[H+1]中
3-3:计算最后的输出,结果存放在ty[N]中
3-4:按公式,调整权重w[H+1]
3-5:按公式,调整权重v[M+1][H]
3-6:计算这个样本的误差
3-7:累计所有样本的总误差
S4: 判断总误差是否小于某个给定值(如1e-4),
如果误差大于该值,则重复S3,否则结束训练
S5: 将训练好的权重 v[M+1][H] 和 w[H+1] 输出并存入文件
S6: 用测试样本集考查神经网络分类的正确率
具体代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N=15;//训练样本总数
const int M=4;//一个样本的属性个数
const int H=3;//隐藏神经元个数
const double E=0.9;//学习率
double x[N][M+1];//x为样本输入,x[i][0]=-1
double w[H+1];//隐层到输出层的权重,用w[0]表示阈值
double v[M+1][H+1];//输入层到隐层的权重,v[0][i]表示阈值
double y[N];//训练样本的已知分类标记
double b[H+1];//隐层的计算输出
double ty[N];//神经网络计算输出
double sigmoid(double x) {
	return 1.0/(1.0+exp(-1*x));
}
void input() {
	freopen("data3.txt","r",stdin);//将测试样本数据放入data3.txt文件中,并打开读入
	int k=0;
	while(cin>>x[k][1]>>x[k][2]>>x[k][3]>>x[k][4]>>y[k])k++;//输入样本
	for(int i=0; i<=H; i++) {//参数随机初始化
		int r=rand()%101;
		w[i]=r*1.0/100;
	}
	for(int i=0; i<N; i++)x[i][0]=-1;//哑结点初始化
	b[0]=-1;
	for(int i=0; i<=M; i++) {
		for(int j=1; j<=H; j++) {
			int r=rand()%101;
			v[i][j]=r*1.0/100;
		}
	}
}
double g(int j) {
	return ty[j]*(1-ty[j])*(y[j]-ty[j]);
}
double e(int h,int j) {
	return b[h]*(1-b[h])*w[h]*g(j);
}
double dw(int h,int j) {//计算w的调整度
	return E*g(j)*b[h];
}
double dv(int i,int h,int j) {//计算v的调整度
	return E*e(h,j)*x[j][i];
}
void BP() {
	double sume=100;
	while(sume>1e-2) {
		sume=0;
		for(int k=0; k<N; k++) {
			for(int i=1; i<=H; i++) {//计算每一隐层输出
				b[i]=0;
				for(int j=0; j<=M; j++) {
					b[i]+=x[k][j]*v[j][i];
				}
				b[i]=sigmoid(b[i]);
			}
			ty[k]=0;
			for(int i=0; i<=H; i++) {//计算每一输出层输出
				ty[k]+=b[i]*w[i];
			}
			ty[k]=sigmoid(ty[k]);
			for(int i=0; i<=H; i++) {//计算权重w
				w[i]+=dw(i,k);
			}
			for(int i=0; i<=M; i++) { //计算权重v
				for(int j=1; j<=H; j++) {
					v[i][j]+=dv(i,j,k);
				}
			}
			//计算误差
			sume+=abs(ty[k]-y[k]);
		}
		cout<<sume<<endl;
	}
}
void test() {//测试样本结果 
	vector<vector<double>> v= {
		{ 0,13.2526,1.82329,-5.42751},
		{  0,-13.4099,-2.62366,-1.10806},
		{  0,3.30317,-5.1121,-13.1777},
		{  0,14.5049,0.292959,-3.93443},
		{ 0,14.4629,1.01794,4.40286}
	};
	vector<double> w= {
		8.47695,
		14.7531,
		4.77114,
		12.343
	};
	double sume=0;
	for(int k=0; k<N; k++) {
		for(int i=1; i<=H; i++) {//计算每一隐层输出
			b[i]=0;
			for(int j=0; j<=M; j++) {
				b[i]+=x[k][j]*v[j][i];
			}
			b[i]=sigmoid(b[i]);
		}
		ty[k]=0;
		for(int i=0; i<=H; i++) {//计算每一输出层输出
			ty[k]+=b[i]*w[i];
		}
		ty[k]=sigmoid(ty[k]);
		sume+=abs(ty[k]-y[k]);
		cout<<ty[k]<<endl;//测试输出 
	}
	cout<<sume<<endl;//误差总和输出 
}
int main() {
	input();//输入数据并且初始化
	BP();
	for(int i=0; i<=M; i++) {
		for(int j=0; j<=H; j++) {
			cout<<" "<<v[i][j];
		}
		cout<<endl;
	}
	cout<<endl;
	for(int i=0; i<=H; i++) {
		cout<<w[i]<<endl;
	}
	test();
	return 0;
}

输出结果(w[M+1][H+1]和v[H+1])
在这里插入图片描述
测试结果(每个测试样本的模拟输出,以及最后的误差总和)
在这里插入图片描述
可以看到模拟输出与样本结果误差相似

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值