📄 bp.h
字号:
{
//循环变量
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 + -