⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bp.h

📁 是关于利用BP神经网络来识别数字图像的
💻 H
📖 第 1 页 / 共 2 页
字号:
{   
	//循环变量
    int i,j,k;
    BYTE* lpSrc; 
	
    //  建立保存特征向量的二维数组
	double **data;
	
	// 为这个数组申请二维存储空间
	data = alloc_2d_dbl(num,lSwidth*lSheight);
	
	// 将归一化的样本的每个象素作为一个特征点提取出来
	
	//逐个数据扫描
	for(k=0;k<num;k++)  
	{ 
		//对每个数据逐行扫描
		for(i=0;i<lSheight;i++)
		{  
			//对每个数据逐列扫描
			for(j=k*lSwidth;j<(k+1)*lSwidth;j++)
			{
				
				// 指向图像第i行第j列个象素的指针
				lpSrc = lpDIBBits + i*lLineByte + j;
				
				//如果这个象素是黑色的
				if(*(lpSrc)==0)
					//将特征向量的相应位置填1
					data[k][i*lSwidth+j-k*lSwidth]=1;
				//如果这个象素是其他的   
				if(*(lpSrc)!=0)
					//将特征向量的相应位置填0
					data[k][i*lSwidth+j-k*lSwidth]=0;		
			}
		}
	}
	
	return(data);  
}

/****************************************************
* 函数名称 BpTrain()
* 
* 参数:
*   double **data_in    -指向输入的特征向量数组的指针    
*	double **data_out   -指向理想输出数组的指针
*	int n_in            -输入层结点的个数 
*   int * n_hidden      -BP网络各个隐层神经元的数目
*   double min_ex       -训练时允许的最大均方误差
*   double momentum     -BP网络的相关系数
*   double eta          -BP网络的训练步长
*   int num             -输入样本的个数
*	etaFactor			-补偿调整因子
*	slope				-陡度因子
*	threshold			-参数调整阈值
* 函数功能:
*     根据输入的特征向量和期望的理想输出对BP网络尽行训练
*     训练结束后将权值保存并将训练的结果显示出来
********************************************************/
void BpTrain(double ** data_in, double** data_out,int n_in,int * n_hidden,double min_ex,double momentum,double eta ,int num,double etaFactor,double slopeFactor,double threshold)
{	
	
	//循环变量   
	int i,j,k,l;
	//一层的神经元数量
	vector<int> neuronNum;
	//一层的数据指针
	vector<double *>unites;
	//一层的权值
	vector<double * *>weights;
	//一层的上一次调整权值
	vector<double * *>modWeights;
	//一层的delta
	vector<double * >deltas;
	//误差
	double ex;
	//临时存放下一次误差,预设一个很大的值
	double exb=1000000;
	//初始步长备份,用来最后打印初始步长
	double etab=eta;
	//出现误差反复的迭代次数
	int * revNum=(int *)malloc(sizeof(int));
	//指向理想目标输出的指针
	double* target; 
	//用来格式化输出的字符串
	CString str,str1;
	//初始陡度
	double slope=1;


	//初值化各向量,这里规定一切向量的初始下标从1开始
	neuronNum.resize(layerNum+1);
	unites.resize(layerNum+1);
	deltas.resize(layerNum+1);
	weights.resize(layerNum);
	modWeights.resize(layerNum);

	//设定每层神经元的数量,第一层128个,最后一层4个,
	neuronNum[1]=128;
	str.Format("样本个数:%d\n\n输入层神经元个数:  %d",num,neuronNum[1]);
	neuronNum[layerNum]=4;

	//从变量n_hidden中提取隐层神经元应该具有的神经元个数
	for(i=2;i<=layerNum-1;i++)
	{
		neuronNum[i]=n_hidden[i-1];
		str1.Format("\n第%d隐层神经元个数:%d",i-1,neuronNum[i]);
		str=str+str1;
	}
	str1.Format("\n输出层神经元个数:  %d",neuronNum[layerNum]);
	str=str+str1;


	//根据层数和每层神经元的数量,为各个数据结构申请内存空间
	for(i=1;i<=layerNum;i++)
		unites[i]=alloc_1d_dbl(neuronNum[i]);
	for(i=1;i<=layerNum-1;i++)
	{
		//delta[i]包含的元素个数与其下一层的神经元个数相同,而不是上一层的神经元个数,所以使用neuronNum[i+1],不用neuron[i]。
		//再加上下标从1开始,这样一来,就导致delta[0]和delta[1]都不能使用
		deltas[i+1]=alloc_1d_dbl(neuronNum[i+1]);
		//weight[0],modWeights[0]都不能使用
		weights[i]=alloc_2d_dbl(neuronNum[i]+1,neuronNum[i+1]+1);
		modWeights[i]=alloc_2d_dbl(neuronNum[i]+1,neuronNum[i+1]+1);
	}
	target = alloc_1d_dbl(neuronNum[layerNum] + 1);

	
	//随机初始化权值矩阵
	for(i=1;i<=layerNum-1;i++)
	{
		::bpnn_randomize_weights(weights[i],neuronNum[i],neuronNum[i+1]);
		::bpnn_zero_weights(modWeights[i],neuronNum[i],neuronNum[i+1]);
	}	
	

	/*-----------用于只含有一个隐层,隐层含有11个神经元时,读取固定权值时的测试-----------//
	r_weight(weights[1],neuronNum[1],neuronNum[2],"E:\\weights\\1784\\win.dat");		//
	r_weight(weights[2],neuronNum[2],neuronNum[3],"E:\\weights\\1784\\who.dat");		//	
	//----------------------------------------------------------------------------------*/
	
	
	//为产生随机序列撒种,t1,t2用作计时
	time_t t1,t2;
	bpnn_initialize((unsigned)time(&t1));



	//bp网的训练
	for(j=0,l=0;l<50000;l++)
	{
		ex=0;
		for(k=0;k<num;k++)
		{
			//将提取的样本的特征向量输送到输入层上
			double hehe=0;
			for(i=1;i<=n_in;i++)
			{
				unites[1][i] = data_in[k][i-1];
				hehe+=unites[1][i];
			}
			//将预定的理想输出输送到BP网络的理想输出单元
			for(i=1;i<=neuronNum[layerNum];i++)
				target[i]=data_out[k][i-1];

			//向前传播
			for(i=1;i<=layerNum-1;i++)
				bpnn_layerforward(unites[i],unites[i+1],weights[i],neuronNum[i],neuronNum[i+1],slope);

			//求层间的delta
			bpnn_output_error(deltas[layerNum],target,unites[layerNum],neuronNum[layerNum]);
			for(i=layerNum-1;i>=2;i--)	
				bpnn_hidden_error(deltas[i],neuronNum[i],deltas[i+1],neuronNum[i+1],weights[i],unites[i]);

			//根据delta从前向后调整权值矩阵w
			for(i=1;i<=layerNum-1;i++)
				bpnn_adjust_weights(deltas[i+1],neuronNum[i+1],unites[i],neuronNum[i],weights[i],modWeights[i],eta, momentum);
			
			//误差统计
			for(i=1;i<=neuronNum[layerNum];i++)		
				ex+=(unites[layerNum][i]-data_out[k][i-1])*(unites[layerNum][i]-data_out[k][i-1]);
		}
		//计算均方误差
		ex=ex/double(num*neuronNum[layerNum]);
		if(ex>exb)//误差增加
		{
			revNum[j++]=l;
			revNum=(int*)realloc(revNum,(j+1)*sizeof(int));	
			eta/=etaFactor;
			if(threshold>0) 
				slope/=slopeFactor;
		}
		else 
			if((exb-ex)/exb<threshold)
			{
				eta*=etaFactor;
				slope*=slopeFactor;
			}
		exb=ex;

		//如果均方误差已经足够的小,跳出循环,训练完毕  
		if(ex<min_ex)break;
	}
	
	time(&t2);	
	if(ex<=min_ex)
	{		
		str1.Format("\n参数汇总:\n初始步长:%.4f,动量因子:%.4f\n参数调整阈值:%.4f,步长因子:%.4f,陡度因子:%.4f",etab,momentum,threshold,etaFactor,slopeFactor);
		str=str+str1;

		str1.Format("\n\n迭代%d次,用时%d秒,\n平均误差%.15f",l,t2-t1,ex);
		str=str+str1;

		str1.Format("\n\n出现误差反复情况的迭代次数为:\n");
		str=str+str1;

		for(i=0;i<j;i++)
		{
			str1.Format("%d,",revNum[i]);
			str=str+str1;
		}
		::MessageBox(NULL,str,"训练情况汇总",NULL);
	}
	
	if(ex>min_ex)
	{		
		str1.Format("\n参数汇总:\n初始步长:%.4f,动量因子:%.4f\n参数调整阈值:%.4f步长因子:%.4f,陡度因子:%.4f",etab,momentum,threshold,etaFactor,slopeFactor);
		str=str+str1;

		str1.Format("\n\n迭代%d次,用时%d秒,平均误差%.15f\n我已经尽了最大努力了还是达不到您的要求\n请调整参数重新训练吧!",l,t2-t1,ex);
		str=str+str1;

		str1.Format("\n\n出现误差反复情况的迭代次数为:\n");
		str=str+str1;

		for(i=0;i<j;i++)
		{
			str1.Format("%d,",revNum[i]);
			str=str+str1;
		}
		::MessageBox(NULL,str,"训练情况汇总",NULL);
	}		

	//把修改后最终的权值矩阵写入文件,默认存放位置是E盘的根目录,格式:i-j.dat,i和j分别表示前后的层号
	char filename[30];
	for(i=1;i<layerNum;i++)
	{
		str.Format("%d-%d.dat",i,i+1);
		for(int j=0;((*(ATL::CSimpleStringT<char,1>*)(&str))).GetAt(j);j++)
			filename[j]=((*(ATL::CSimpleStringT<char,1>*)(&str))).GetAt(j);
		filename[j]=0;
		w_weight(weights[i],neuronNum[i],neuronNum[i+1],filename);
	}

	//把本次训练每层的神经元个数写入文件,文件名是:Num.dat
	FILE *fp;
	int * buffer;
	char fName[]="Num.dat";
	fp=fopen(fName,"wb+");
	buffer=(int * )malloc(sizeof(int)*(layerNum));
	for(i=1;i<=layerNum;i++)
		buffer[i-1]=neuronNum[i];
	fwrite((char*)buffer,sizeof(int),layerNum,fp);
	fclose(fp);
	free(buffer);

	//清除所有的vector释放内存
	neuronNum.clear();
	unites.clear();
	deltas.clear();
	weights.clear();
	modWeights.clear();
}
/*******************************************
* 函数名称
* CodeRecognize()
* 参量
*  double **data_in     -指向待识别样本特征向量的指针
*  int num              -待识别的样本的个数 
*  int n_in             -Bp网络输入层结点的个数              
*  int n_hidden         -Bp网络隐层结点的个数
*  int n_out            -Bp网络输出层结点的个数
* 函数功能:  
*    读入输入样本的特征相量并根据训练所得的权值 
*    进行识别,将识别的结果写入result.txt 
****************************************/
void CodeRecognize(double **data_in, int num ,int n_in,int n_hidden,int n_out)
{
	//循环变量   
	int i,k,l;	
	// 指向识别结果的指针 
	int *recognize;
	//一层的神经元数量
	vector<int> neuronNum;
	//一层的数据指针
	vector<double *>unites;
	//一层的权值
	vector<double * *>weights;
	//读写文件指针
	FILE * fp;
	//读写文件缓冲
	int * buffer;	

	//初值化各向量,这里规定一切向量的初始下标从1开始
	neuronNum.resize(layerNum+1);
	unites.resize(layerNum+1);
	weights.resize(layerNum);

	//为存放识别的结果申请存储空间
	recognize=(int*)malloc(num*sizeof(int));

	//读取每层的神经元个数,默认从E盘的根目录下读取
	char fName[]="Num.dat";
	if((fp=fopen(fName,"rb"))==NULL)
		return;
	buffer=(int * )malloc(layerNum*sizeof(int));
	fread((char*)buffer,sizeof(int),layerNum,fp);
	for(i=1;i<=layerNum;i++)
		neuronNum[i]=buffer[i-1];
	fclose(fp);
	free(buffer);

	//根据层数和每层神经元的数量,为各个数据结构申请内存空间
	for(i=1;i<=layerNum;i++)
		unites[i]=alloc_1d_dbl(neuronNum[i]);
	for(i=1;i<=layerNum-1;i++)
		//weight[0]不能使用
		weights[i]=alloc_2d_dbl(neuronNum[i]+1,neuronNum[i+1]+1);

	
	//读取识别需要使用的权值,默认位置是从E盘的根目录读取
	char filename[30];
	CString str,str1;
	for(i=1;i<layerNum;i++)
	{
		str.Format("%d-%d.dat",i,i+1);
		for(int j=0;((*(ATL::CSimpleStringT<char,1>*)(&str))).GetAt(j);j++)
			filename[j]=((*(ATL::CSimpleStringT<char,1>*)(&str))).GetAt(j);
		filename[j]=0;
		if(r_weight(weights[i],neuronNum[i],neuronNum[i+1],filename)==false)
		return;
	}	
	
	//逐个样本扫描
	for(k=0;k<num;k++)
	{ 
		//将提取的样本的特征向量输送到输入层上
		for(i=1;i<=neuronNum[1];i++)
			unites[1][i]=data_in[k][i-1];
		
		//前向输入激活
		for(i=1;i<=layerNum-1;i++)
			bpnn_layerforward(unites[i],unites[i+1],weights[i],neuronNum[i],neuronNum[i+1],1);
		
		//根据输出结果进行识别
		int result=0 ;
		//考察每一位的输出
		for(i=1;i<=neuronNum[layerNum];i++)
		{
			//如果大于0.5判为1
			if(unites[layerNum][i]>0.5)
			{
				//str.Format("%.10f",unites[layerNum][i]);
				//AfxMessageBox(str);
				result+=(int)pow(double(2),double(4-i));
			}
		}
		
		//如果判定的结果小于等于9,认为合理
		if(result<=9)
			recognize[k]=result;
		//如果判定的结果大于9,认为不合理将结果定位为一个特殊值20
		if(result>9)
			recognize[k]=20;
	}	
	
	//将识别结果写到文本中
	fp=fopen("result.txt","w+");
	
	for(i=0;i<num;i++)
	{  
		if(recognize[i]==20)
			fprintf(fp,"无法识别,");
		else
			fprintf(fp,"%d,",recognize[i]);		
	}
	fclose(fp);
	
	//将识别的结果显示出来
	for(i=0;i<num;i++)
	{
		if(recognize[i]!=20)
			str.Format("%d ",recognize[i]);

		if(recognize[i]==20)
			str.Format("无法识别 ");		
		str1+=str;
	}
	
	//通知用户训练完成	
	::MessageBox(NULL,str1,"识别结果",NULL);
	
	//清空所有vector释放存储空间
	unites.clear();
	neuronNum.clear();
	weights.clear();
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -