简单BP神经网络 - 异或问题分类(C++实现+详细助解)

本文详细介绍了神经网络的基本概念,包括隐藏层、输出层的权值初始化,以及BP算法的前向传播、后向传播过程。通过实例代码展示了如何使用BP算法训练一个三层神经网络,并讨论了不同类型的激活函数。适合初学者理解神经网络的实践应用。
摘要由CSDN通过智能技术生成

神经网络概念及基础https://www.cnblogs.com/maybe2030/p/5597716.html

本例具体公式使用按步推导参照:https://www.cnblogs.com/charlotte77/p/5629865.html

激活函数(激励函数)理解与总结:https://blog.csdn.net/tyhj_sf/article/details/79932893

 

废话不多说上代码!(代码来源参考至:https://www.cnblogs.com/semen/p/6883438.html

#include <iostream>
#include<stdlib.h>
#include <math.h>
#include <string>
using namespace std;
//简单 输入-隐藏-输出 三层Bp网络

#define  innode 2       // 输入层节点数
#define  hiddennode 10   // 隐层节点数
#define  outnode 1      // 输出层节点数
#define  sample 4       // 训练样本数
#define  speed 0.1      // 训练速率
#define  Kernel 1       // 传递函数类型

class BP_Net
{
private:
	double W_Hid[hiddennode]; //隐藏层数据存储
	double W_Out[outnode];    //输出层数据存储
	double W_InToHid [hiddennode][innode];  //输入层到隐藏层的网络权值
	double W_HidToOut[outnode][hiddennode]; //隐藏层到输出层的网络权值
	double W_Rand(double low, double high); //权值随机填充
	
public:
	double Err;                  //临时误差
	double E_Sum;                //总误差
	double R_Predict[outnode];   //预测结果
	BP_Net(const char Name[]);   //构造
	~BP_Net();                   //反构造
	void   BP_Init(double low, double high); //网络权值初始化
	void   BP_Predict(double TestData[]);     //模型数据预测
	//数据模型训练
	void   BP_Train(double InData[sample][innode], double Target[sample][outnode]);
	//激活函数选择  传递方向选择(前向、后向)
	double T_Kernel(unsigned char method,unsigned char T_Direction, double Value);
};

BP_Net::BP_Net(const char Name[]):E_Sum(1),Err(0){
    cout<<Name<<" :"<<endl;
}
BP_Net::~BP_Net(){}

void BP_Net::BP_Init(double low, double high)
{
	unsigned int N_W_InToHid  = hiddennode*innode;
	unsigned int N_W_HidToOut = hiddennode*outnode;
	unsigned int m = max(N_W_InToHid,N_W_HidToOut);
	double* P_Hid  = W_Hid;
	double* P_Out  = W_Out;
	double* P_InToHid  = (double*)W_InToHid;
	double* P_HidToOut = (double*)W_HidToOut;

	for (int i = 0; i < m; i++)
    {
    	if(i < hiddennode)   P_Hid[i]      = W_Rand(low, high);
    	if(i < outnode)      P_Out[i]      = W_Rand(low, high);
    	if(i < N_W_InToHid)  P_InToHid[i]  = W_Rand(low, high);
    	if(i < N_W_HidToOut) P_HidToOut[i] = W_Rand(low, high);
    }
}

double BP_Net::T_Kernel(unsigned char method,unsigned char T_Direction, double Value)
{
		if(method == 1){ //sigmod
			if(T_Direction<0.5)//前向
				return 1.0 / (1 + exp(-Value));
            else  //反向
				return Value*(1 - Value);
		}
		else{ //Tanh
			if(T_Direction<0.5)//反向
				return (exp(Value) - exp(-Value)) / (exp(Value) + exp(-Value));
            else //反向
				return pow((exp(Value) - exp(-Value)) / (exp(Value) + exp(-Value)), 2);
		}
}

double BP_Net::W_Rand(double low, double high)
{
    double val;
    val = ((double)rand() / (double)RAND_MAX)*(high - low) + low;
    return val;
}


void BP_Net::BP_Train(double InData[sample][innode], double Target[sample][outnode])
{
	double I_Out[outnode]; //输出点输入
	double O_Out[outnode]; //输出点输出
	double E_Out[outnode]; //输出点误差
	double I_Hid[hiddennode];
	double O_Hid[hiddennode];
	double E_Hid[hiddennode];

	double Once_In[innode];              //输入结点值
	double Once_Target[outnode];         //输出目标值
	unsigned int M = max(innode,outnode);
	unsigned int M1 = max(outnode,hiddennode);
	for (int i = 0; i < sample; i++)     // 监督数据训练
    {
		for (int j = 0; j < M; j++)      //前期初值准备
		{
			if(j < innode)              //提取一次输入结点数据
            	Once_In[j] = InData[i][j];
			if(j < outnode)
				Once_Target[j] = Target[i][j]; //提取一次拟合目标数据
		}
		
		/*前向传播*/
		for (int j = 0; j < hiddennode; j++)  //输入层到隐藏层的传播
        {
			I_Hid[j] = 0; // 结点累加和 初始化
			for (int k = 0; k < innode; k++) //计算每个隐藏结点的权值和
				I_Hid[j] += Once_In[k] * W_InToHid[j][k];
			O_Hid[j] = T_Kernel(Kernel, 0, I_Hid[j] + W_Hid[j]); //用激活函数得结点输出值
        }
        for (int j = 0; j < outnode; j++)  //隐藏层到输出层的传播
        {
			I_Out[j] = 0;
			for (int k = 0; k < hiddennode; k++)
				I_Out[j] += O_Hid[k] * W_HidToOut[j][k];
			O_Out[j] = T_Kernel(Kernel, 0, I_Out[j] + W_Out[j]);
        }

		/*后向传播*/
		for (int j = 0; j < outnode; j++)  //输出层到隐藏层的传播
        {
		 	double E_Temp = Once_Target[j] - O_Out[j]; //每个输出节点的误差
			Err += pow(E_Temp,2);            //模型总输出误差
			// 偏导 (总误差E_Sum)/(某输出结点O_Out)  *  偏导 (某输出结点O_Out)/(激发函数运算Net)
        	E_Out[j] =  (E_Temp) * T_Kernel(Kernel, 1, O_Out[j]);
	 		for (int k = 0; k < hiddennode; k++) //输出层到隐藏层每条网络权值更新
				// E_Out[j] * 偏导 (激发函数运算Net)/(某输条网络权值W)
				W_HidToOut[j][k] += E_Out[j] * O_Hid[k] * speed;
        }
		E_Sum = Err*0.5;
        for (int j = 0; j < hiddennode; j++)
        {
        	E_Hid[j] = 0;
        	for (int k = 0; k < outnode; k++) //输出层到隐藏层的传播
				// 偏导 (某输出结点E_Out)/(激发函数运算Net) * 偏导 (激发函数运算Net)/(某输条网络新输入值)
				E_Hid[j] += E_Out[k] * W_HidToOut[k][j];

			// 偏导 (某输出结点E_Out)/(某隐层结点O_Hid) * 偏导 (某隐层结点O_Hid)/(激发函数运算Net)
			E_Hid[j] = E_Hid[j] * T_Kernel(Kernel, 1, O_Hid[j]);
			for (int k = 0; k < innode; k++)//隐藏层到输入层每条网络权值更新
				 // E_Hid[j] * 偏导 (激发函数运算Net)/(某输条网络权值W)
				 W_InToHid[j][k] += E_Hid[j] * Once_In[k] * speed;
        }
        
        //更新各个节点本身的值
		for (int j = 0; j < M1; j++)
		{
			if(j < outnode)     W_Out[j] = E_Out[j] * speed;
			if(j < hiddennode)  W_Hid[j] = E_Hid[j] * speed;
        }
    }
}

void BP_Net::BP_Predict(double TestData[])
{
    double I_Out[outnode]; //输出点输入
	double O_Out[outnode]; //输出点输出
 	double I_Hid[hiddennode];
 	double O_Hid[hiddennode];
	/*前向传播*/
	for (int j = 0; j < hiddennode; j++)  //输入层到隐藏层的传播
    {
		I_Hid[j] = 0; // 结点累加和 初始化
		for (int k = 0; k < innode; k++) //计算每个隐藏结点的权值和
			I_Hid[j] += TestData[k] * W_InToHid[j][k];
		O_Hid[j] = T_Kernel(Kernel, 0, I_Hid[j] + W_Hid[j]); //用激活函数得结点输出值
    }
    for (int j = 0; j < outnode; j++)  //隐藏层到输出层的传播
    {
		I_Out[j] = 0;
		for (int k = 0; k < hiddennode; k++)
			I_Out[j] += O_Hid[k] * W_HidToOut[j][k];
		O_Out[j] = T_Kernel(Kernel, 0, I_Out[j] + W_Out[j]);
		R_Predict[j] = O_Out[j];
		cout<<R_Predict[j]<<endl;
    }
}

/***************<使用示例>*****************/
//训练样本
double X[sample][innode] = {
    {1,1},
    {1,0},
    {0,1},
    {0,0}
};
//训练样本对应标签
double Y[sample][outnode] = {
    {1},
    {0},
    {0},
    {1}
};

int main(int argc, char*argv[])
{
	BP_Net bp("BP1");
	bp.BP_Init(-1.0,1.0);
	int t = 0;
    while (bp.E_Sum > 0.001 && t <30000)
    {
  		bp.Err = 0.0;
        bp.BP_Train(X, Y);
        t++;
    }
    double m[2] = { 0,1 };
    bp.BP_Predict(m);
	return 0;
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值