欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

单隐层BP神经网络C++实现

程序员文章站 2022-06-14 11:22:33
...

    这几天抽时间学习了一下很久之前就想学习的BP神经网络。通过阅读西瓜书的神经网络部分的原理和参考了网上几篇博客,我自己用C++编写、实现了一个单隐层BP神经网络。

    简单画了个示意图,好理解下面给出的公式:(注意:图中省略了其他的节点之间的连线)

单隐层BP神经网络C++实现

    西瓜书上的BP神经网络训练流程:

训练流程:

输入:训练集D={(xkyk)},学习率η

过程:

在(0,1)范围内随机初始化网络中的所有连接权和阈值

repeat

      for all (xkyk)ⅭD do

           计算当前样本的输出yk

           计算输出层神经元的梯度项gj

                   计算隐藏层神经元梯度项ek

           更新连接权值wh,j,vi,j与阈值θjϒh

      end for

until 达到停止条件

输出:连接权值与阈值确定的单隐层前馈神经网络

    在这里,我就不解释BP神经网络了,西瓜书上写得十分详细和明白,我直接上程序。

    关于矩阵处理,C++不如python来得快和方便,但是只要是算法都是能够运用任何一种语言描述出来的。这里,为了方便矩阵的运算,我加入了Eigen库。Eigen库不用安装,直接下载好然后再配置一下就好。关于Eigen配置和使用可以参照博客:https://blog.csdn.net/fengbingchun/article/details/47378515

    不说了,接下来上代码。

    1、单隐层BP神经网络类设计

    代码中的注释已经很详细了,我就不解释了。

#pragma once
#include <Eigen/dense>
using namespace Eigen;

//单隐层BP神经网络
class BP
{
public:
	//构造函数
	BP(int input,int output,int hide,double eta);
	//**函数
	double sigmoid(double x);
	//训练网络
	void BPtrain(MatrixXd input,MatrixXd output,int time);
	//对数据进行预测,输出结果
	void BPpredict(MatrixXd input);
	//获取输出节点的输出值
	MatrixXd getOutputValues(MatrixXd input);
	//获取隐藏节点的输出值
	MatrixXd getHidenValues(MatrixXd input);

	virtual ~BP();
private:
	//学习率
	double eta;
	//输入层个数
	int input_size;
	//输出层个数
	int output_size;
	//隐藏层个数
	int hide_size;
	//隐藏层节点阈值
	MatrixXd hide_threshold;
	//输出层节点阈值
	MatrixXd output_threshold;
	//输入到隐藏层的权值
	MatrixXd hide_w;
	//隐藏层到输出层的权值
	MatrixXd output_w;
};

 2、类的实现

 2.1构造函数

//构造函数,对输入层节点个数、输出层节点个数、隐藏层节点个数、学习率、隐藏层阈值、输出层阈值、隐藏层权值、输出层权值进行初始化
BP::BP(int input, int output, int hide, double eta):input_size(input),output_size(output),hide_size(hide),eta(eta)
{
	//将以下值随机初始化为-1~1之间的值
	//初始化隐藏层阈值
	hide_threshold = MatrixXd::Random(1, hide_size);
	//初始化输出层阈值
	output_threshold = MatrixXd::Random(1, output_size);
	//初始化隐藏层权值,行为隐藏节点数,列为输入节点数
	hide_w = MatrixXd::Random(hide_size, input_size);
	//初始化输出层权值,行为输出节点数,列为隐藏节点数
	output_w = MatrixXd::Random(output_size, hide_size);
}

 2.2 **函数

    函数原型:

        单隐层BP神经网络C++实现

//**函数,sigmoid函数
double BP::sigmoid(double x)
{
	return 1 / (1 + exp(x*(-1)));
}

 2.3隐藏层的输出

    公式:

       单隐层BP神经网络C++实现 

       输出: 单隐层BP神经网络C++实现

       v为隐层权值,x为网络的输入,γ为隐层阈值,α为隐层输入,b为隐层输出。 

//获取隐藏层的输出
MatrixXd BP::getHidenValues(MatrixXd input)
{
	MatrixXd alpha, hide_output(1,hide_size);
	//求隐藏节点的输入,即 隐藏层权值*输入值的累加
	alpha = input * hide_w.transpose();

	for (int h = 0; h < hide_size; h++)
	{
		//调用**函数,获得隐藏节点的输出值
		hide_output(0, h) = sigmoid(alpha(0, h) - hide_threshold(0, h));
	}		

	return hide_output;
}

 2.4 输出层输出

  单隐层BP神经网络C++实现

   单隐层BP神经网络C++实现

  w为输出层权值,b为隐层输出,β为输出层输入,θ为输出层阈值,y为网络计算的输出。 

//获取输出
MatrixXd BP::getOutputValues(MatrixXd input)
{
	MatrixXd beta, output(1, output_size), hide_output;
	//获取隐藏层输出
	hide_output = getHidenValues(input);
	//求输出层的输入值,即 隐藏层输出*权值的累加
	beta = hide_output * output_w.transpose();

	for (int j = 0; j < output_size; j++)
	{
		//求得最终的输出
		output(0, j) = sigmoid(beta(0, j) - output_threshold(0, j));
	}

	return output;
}

  2.5对神经网络进行训练

 公式:

 输出均方误差:单隐层BP神经网络C++实现 ,需要通过修改权值和阈值将其变为最小。

单隐层BP神经网络C++实现 ,单隐层BP神经网络C++实现,其中 单隐层BP神经网络C++实现

 单隐层BP神经网络C++实现单隐层BP神经网络C++实现,其中 单隐层BP神经网络C++实现

 w为输出层连接权值,η为学习率,b为隐层输出,g为输出层梯度项,θ为隐层阈值,单隐层BP神经网络C++实现为网络输出,y为给定输出,v为隐层连接权值,e为隐层梯度项,x为网络输入,γ为隐层阈值。

//训练神经网络
void BP::BPtrain(MatrixXd input, MatrixXd output, int time)
{
	MatrixXd train_output, hide_output;
	//输出神经元梯度项g
	MatrixXd output_gradient(1,output_size);
	//隐藏层神经元梯度项e
	MatrixXd hide_gradient(1,hide_size);
	//训练time次
	while (time>0)
	{
		--time;
		//对每次输入
		for (int t = 0; t < input.rows(); t++)
		{
			train_output = getOutputValues(input.row(t));
			hide_output = getHidenValues(input.row(t));
			//更新输出权值和阈值

			//计算输出层神经元梯度项

			for (int j = 0; j < output_size; j++)
			{
				output_gradient(0, j) = train_output(0, j) * (1 - train_output(0, j)) * (output(t, j) - train_output(0, j));
			}

			//修改输出权值
			MatrixXd temp = output_w; //暂存原值,以便计算后面的 sum
			output_w = output_w + eta * output_gradient.transpose() * hide_output;
			//修改输出神经元阈值
			output_threshold = output_threshold - eta * output_gradient;

			//计算隐藏层的神经元梯度
			for (int h = 0; h < hide_size; h++)
			{
				double sum = 0;
				for (int j = 0; j < output_size; j++)
				{
					sum += temp(j, h)*output_gradient(0, j);
				}
				hide_gradient(0, h) = hide_output(0, h) * (1 - hide_output(0, h)) * sum;
			}


			//修改隐藏层权值
			hide_w = hide_w + eta * hide_gradient.transpose() * input.row(t);
			//修改隐藏层神经元的阈值
			hide_threshold = hide_threshold - eta * hide_gradient;
			}
		}
		
}

 2.6测试输出函数 

    测试输出函数其实就是在训练好的网络上输入数据,调用getOutputValues函数得出网络的预测输出值。

//对给定输入预测神经元的输出
void BP::BPpredict(MatrixXd input)
{
	MatrixXd result;
	cout << "预测结果:" << endl;
	for (int t = 0; t < input.rows(); t++)
	{
		//即以输入的值获取输出值
		result = getOutputValues(input.row(t));
		cout << t << ":  " << result << endl;
	}
}

 3、测试主函数

int main()
{
	//输入5个点进行训练
	MatrixXd x(5, 2);
	x << 0, 1,
		1, 2,
		2, 1,
		2, 3,
		3, 0;
	MatrixXd y(5, 1);
	y << 1, 1, 0, 1, 0;
	//设置BP网络参数:输入节点数:2,输出节点数:1,隐藏层节点数:3,学习率:0.8
	BP test(2, 1, 3, 0.8);
	//进行训练,这里训练次数设为了1000次,更新各权值和阈值
	test.BPtrain(x, y, 1000);
	//输入3个点进行预测,点(4,2)对应的输出应该为0,(-1,2)为1,(1,-2)为0
	MatrixXd t(3, 2);
	t << 4, 2,
		-1, 2,
		1, -2;
	//输出预测结果
	test.BPpredict(t);


	system("pause");
	return 0;
}

 放上输出结果:

 输入3个点进行预测(4,2)(-1,2)(1,-2),对应的预计结果为0, 1, 0

 结果为:

单隐层BP神经网络C++实现

 随着训练次数的增加和隐层节点数的增加,预测结果的误差将会越来越小。

 给出源代码下载地址:https://download.csdn.net/download/m0_37543178/10674861