神经网络原理及其c++实现
Posted HUSTLX
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了神经网络原理及其c++实现相关的知识,希望对你有一定的参考价值。
1引言
数字识别是模式识别领域 中的一个重要分支,数字识别一般通过特征匹配及特征判别的传统方法进行处理。特征匹配通常适用于规范化的印刷体字符的识别,而 特征判别多用于手写字符识别,这些方法还处于探索阶段,识别率还比较低。随着神经网络技术的飞速发展,其本身具有的高度并行性、较强的自组织能力和容错性、较好抑制噪声干扰能力等特点,为快速准确地进行数字识别开辟了新的途径。目前,对于指针式表而言,可以根据二值化后的指针指向来识别表盘的读数,而对于某些特殊类型的表盘,由于没有可判断读数的指针,只能通过表盘上的数字来读取表盘数据,这无疑增大了识别的难度,这类表盘中数字识别就显得格外重要。本文针对这种情况,在对待识别数字图像进行预处理和特征提取的基础上,利用粗糙集理论进行特征约简,以减少神经网络的规模和结构复杂度, 最后利用神经网络进行仪表表盘的数字识别。
2数字识别的基本过程
本文数字图像的识别过程[2]如图1所示,主要包括获取数字图像、数字图像预处理、特征提取、粗糙集特征约简和神经网络数字识别几个阶段:
图1 数字识别过程
其中图像预处理包括图像灰度化,图像二值化,自适应直方图均衡,自适应中值滤波等。
图2 原始图像和经过自适应直方图均衡并且提取表盘图像
自适应直方图均衡解决了不同图像光照不均等问题。但是该方法引入了类似椒噪声,再用自适应中值滤波进行滤波,得到的图像只剩下表盘区域。我们再用投影法便可得到每个数字所在的区域。我们把每个数字的小区域图像等间隔的取点作为神经网络的输入样本,训练神经网络,当样本数量足够多时,神经网络训练的参数也趋于稳定,再用测试样本进行检测我们训练的结果。
图3 自适应中值滤波后的图像和数字进行分割后的结果
3 bp神经网络
Bp神经网络结构有3层[4]:输入层、隐含层、输出层,如图6所示。
图4 三层BP网络结构
3层BP神经网络学习训练过程主要由4部分组成:输入模式顺传播(输入模式由输入层经隐含层向输出层传播计算)、输出误差逆传播(输出的误差由输出层经隐含层传向输入层)、循环记忆训练(模式传播与误差逆传播的计算过程反复交替循环进行)和学习结果判别(判定全局误差是否趋向极值)。
下面具体介绍和分析用梯度下降法训练BP神经网络,在第1次输入样品(1=1,2,……,N)进行训练时各个参数的表达及计算方法。
3.1确定参数
① 输入向量为X=[x1,x2,…,xn]T(n—输入层单元个数);
② 输出向量Y=[y1,y2,…,yq]T(q—输出层单元个数);
③ 希望输出向量O=[o1,o2,…,oq]T;
④ 隐含层输出向量B=[b1,b2,…,bp]T(p—隐含层单元个数);
⑤ 初始化输入层至隐含层的连接权值
Wj=[wj1,wj2,…,wjt,…,wjn]T,j=1,2,…p;
初始化隐含层至输出层的连接权值
Wk=[wk1,wk2,…,wkj,…,wkp]T,k=1,2,…q。
3.2输入模式顺序传播。
这一过程主要是利用输入模式求出它对应的实际输出。
① 计算隐含层各神经元的激活值:
(j=1,2,3…p) (1)
② 激活函数用S型函数,即:
这里之所以选S型函数为BP神经网络神经元的激活函数,是因为它是连续可微分的,而且更接近于生物神经元的信号输出形式。
③ 计算隐含层j单元的输出值:
阈值在学习过程中和权值一样不断被修改,阈值的作用反应在函数的输出曲线上。
图7 阈值的作用
3.3输出误差的传播。
在上一步的模式顺传播计算中得到网络的实际输出值,当这些实际的输出值与希望的输出值不一样时或者说误差大于所限定的数值时,就要对网络进行校正。
①输出层的校正误差为:
②隐含层各单元的校正误差为:
③对于输出层至隐含层连接权和输出层阈值的校正量为:
α为学习系数,α>0。
④隐含层至输入层的校正量为:
对于BP神经网络输入的每一组训练模式,一般都要经过数百次甚至上万次的循环记忆训练,才能使网络记住这一模式。这种循环记忆训练实际上就是反复重复上面介绍的输入模式。
判别的目的主要是检查输出误差是否已经小到可以允许的程度。学习或者说训练的过程是网络全局误差趋向于极小值的过程。
3.4各层神经元的选取
输入层神经元的个数由输入向量的维数确定,例如当我们每个数字图像提取64个点时,那么输入的神经元个数n就是64,输出层的个数由数字分类个数决定,我们对数字1可以很容易的检测出来剩下的0、2-9个数字共有9个数,所以分9类,故输出神经元个数m为9,隐含层神经元个数由下式确定[5]:
故可以取8到18的任意数,隐含层神经元的个数会影响计算量以及计算精度。
4.实验
我们令学习率为0.8,迭代次数上限1000000次,精度0.01,取测试样本输入训练好的神经网络,写好代码:
#include"bpnet.h" #define n 32 //输入层神经元个数 #define mid_num 16 //隐含层神经元个数 #define out_num 9 //输出层神经元个数 #define T 32 //样本个数 #define in_num 2 //样本个数 double v[mid_num][out_num]; //输出层的权矩阵 double sita_h[mid_num]; //隐藏层阈值 double sita_o[out_num]; //输出层阈值 double w[n][mid_num]; //输出(隐藏)层的权矩阵 double delta_o[out_num]; //输出层各联结权的修改量组成的向量 double delta_h[mid_num]; //隐藏层各联结权的修改量组成的向量 double hide_s[mid_num]; //隐藏层的激活值 double hide_o[mid_num]; //隐藏层的输出值 double output_s[out_num]; //输出层的激活值 double output_o[out_num]; //输出层的输出值 double input[T][n]; //输入层的输入向量 double output[T][out_num]; //期望的输出值 double E; //误差 double sigma=0.01; //精度控制参数 double alpha=0.8; //学习率 int M=1000000; //循环最大次数 //****初始化函数****// void initialize() { int i,j; for(i = 0; i < mid_num; i++) { hide_s[i] = 0; hide_o[i] = 0; delta_h[i] = 0; sita_h[i] = rand() / ( RAND_MAX + 1.0 ); } for(i = 0; i < out_num; i++) { sita_o[i] = rand() / ( RAND_MAX + 1.0 ); output_s[i] = 0; output_o[i] = 0; delta_o[i] = 0; } for(i = 0; i < mid_num; i++) for(j = 0; j < out_num; j++) v[i][j] = rand() / ( RAND_MAX + 1.0 ); for(i = 0; i < n; i++) for(j = 0; j < mid_num; j++) w[i][j] = rand() / ( RAND_MAX + 1.0 ); } int trial() { int i,j,t,N; double error; ofstream fout( "result.txt" ); if( !fout ) cout << " can\'t create bp.txt\\n " ; /*cout << "请输入学习率:alpha="; cin >> alpha; cout << endl << "请输入误差控制精度:sigma="; cin >> sigma; cout << endl << "请输入循环的最大次数:M="; cin >> M;*/ ifstream fin( "sample.txt" ); if( !fin ) cout << " can\'t open sample.txt\\n"; for(i = 0; i < T; i++) { cout <<endl<< i<<" 个输入\\n"<<endl; for(int p=0;p<32;p++) { fin >> input[i][p]; printf("%d",int(input[i][p])); } } for(i = 0; i < T; i++) { cout <<endl<< i<<" 个输出\\n"<<endl; for(int p=0;p<9;p++) { fin >> output[i][p]; printf("%d",int(output[i][p])); } } fin.close(); E = sigma + 1; N = 0; initialize(); while( E > sigma && N < M) { N++; for(t = 0; t < T; t++) { for(i = 0; i < mid_num; i++) { hide_s[i] = 0; for(j = 0; j < n; j++) hide_s[i] += w[j][i] * input[t][j]; hide_s[i] -= sita_h[i]; //计算隐含层各神经元的激活值hide_s hide_o[i] = 1.0 / ( 1.0 + exp(-hide_s[i]) ); //隐含层神经元的输出值 } for(i = 0; i < out_num; i++) { output_s[i] = 0; for(j = 0; j < mid_num; j++) output_s[i] += v[j][i] * hide_o[j]; output_s[i] -= sita_o[i]; //计算输出层各神经元的激活值output_s output_o[i] = 1.0 / ( 1.0 + exp(-output_s[i]) ); //输出层神经元的输出值 } for(i = 0; i < out_num; i++) { delta_o[i] = output_o[i] * ( 1 - output_o[i] ) * ( output[t][i] - output_o[i] ); //输出层的校正误差delta_o=(o-y)y(1-y) sita_o[i] -= alpha * delta_o[i]; //输出层阈值校正量 } for(i = 0; i < mid_num; i++) { double Z = 0; for(j = 0; j < out_num; j++) Z += v[i][j] * delta_o[j]; delta_h[i] = Z * hide_o[i] * ( 1 - hide_o[i] ); //隐含层的校正误差 sita_h[i] -= alpha * delta_h[i]; //隐含层阈值校正量 } for(i = 0; i < mid_num; i++) { for(j = 0; j < out_num; j++) { v[i][j] += alpha * hide_o[i] * delta_o[j]; //输出层至隐含层权值校正 } } for(i = 0; i < n; i++) for(j = 0; j < mid_num; j++) { w[i][j] += alpha * input[t][i] * delta_h[j]; //隐含层至输入层权值的校正 } } E = 0; for(t = 0; t < T; t++) { for(i = 0; i < mid_num; i++) { hide_s[i] = 0; for(j = 0; j < n; j++) hide_s[i] += w[j][i] * input[t][j]; hide_s[i] -= sita_h[i]; //计算隐含层各神经元的激活值hide_s hide_o[i] = 1.0 / ( 1.0 + exp(-hide_s[i]) ); //隐含层神经元的输出值 } for(i = 0; i < out_num; i++) { output_s[i] = 0; for(j = 0; j < mid_num; j++) output_s[i] += v[j][i] * hide_o[j]; output_s[i] -= sita_o[i]; //计算输出层各神经元的激活值output_s output_o[i] = 1.0 / ( 1.0 + exp(-output_s[i]) ); //输出层神经元的输出值 } error = 0; for(i = 0;i < out_num; i++) error += ( output_o[i] - output[t][i] ) * ( output_o[i] - output[t][i] ); E += error / 2; //误差计算 } } fout << "网络学习" << N << "次结束,误差为:" << E << ",各层的权值如下:" << endl; fout << endl << "输入层到隐藏层权值:" << endl; for(i = 0; i < n; i++) { //fout << "w[" << i << "]"; for(j = 0; j < mid_num; j++) { fout << w[i][j] <<\'\\t\'; } fout << endl ; } fout << endl <<"隐藏层到输出层权值:" << endl; for(i = 0; i < mid_num; i++) { //fout << "v[" << i << "]"; for(j = 0; j < out_num; j++) { fout << v[i][j] <<\'\\t\'; } fout << endl ; } fout << endl << "对应着样本实际输出为:"<< endl; for(t = 0; t < T; t++) { //fout << input[t][0] << \'\\t\' << input[t][1] << \'\\t\'; for(i = 0; i < mid_num; i++) { hide_s[i] = 0; for(j = 0; j < n; j++) hide_s[i] += w[j][i] * input[t][j]; hide_s[i] -= sita_h[i]; hide_o[i] = 1.0 / ( 1.0 + exp(-hide_s[i]) ); } for(i = 0; i < out_num; i++) { output_s[i] = 0; for(j = 0; j < mid_num; j++) output_s[i] += v[j][i] * hide_o[j]; output_s[i] -= sita_o[i]; output_o[i] = 1.0 / ( 1.0 + exp(-output_s[i]) ); fout << output_o[i] << \'\\t\'; } fout << endl; } fout.close(); printf("done!!!"); return 0; } int test() { int i,j,t,N; double error; ofstream fout( "result.txt" ); if( !fout ) cout << " can\'t create bp.txt\\n " ; ifstream fin( "trial.txt" ); if( !fin ) cout << " can\'t open sample.txt\\n"; for(i = 0; i < n; i++) { cout <<endl<<"w"<< i<<endl; for(int p=0;p<mid_num;p++) { fin >> w[i][p]; printf(" %f ",w[i][p]); } } for(i = 0; i < mid_num; i++) { cout <<endl<<"v"<< i<<endl; for(int p=0;p<out_num;p++) { fin >> v[i][p]; printf(" %f ",v[i][p]); } } for(i = 0; i < in_num; i++) { cout <<endl<< i<<" 个输入\\n"<<endl; for(int p=0;p<n;p++) { fin >> input[i][p]; printf("%d",int(input[i][p])); } } fin.close(); fout << endl << "对应着样本实际输出为:"<< endl; for(t = 0; t < in_num; t++) { //fout << input[t][0] << \'\\t\' << input[t][1] << \'\\t\'; for(i = 0; i < mid_num; i++) { hide_s[i] = 0; for(j = 0; j < n; j++) hide_s[i] += w[j][i] * input[t][j]; hide_s[i] -= sita_h[i]; hide_o[i] = 1.0 / ( 1.0 + exp(-hide_s[i]) ); } for(i = 0; i < out_num; i++) { output_s[i] = 0; for(j = 0; j < mid_num; j++) output_s[i] += v[j][i] * hide_o[j]; output_s[i] -= sita_o[i]; output_o[i] = 1.0 / ( 1.0 + exp(-output_s[i]) ); fout << output_o[i] << \'\\t\'; } fout << endl; } fout.close(); printf("done!!!"); return 0;: }
神经网络测试时,trial.txt文件前面是训练好的神经网络的参数:
0.377247 0.858411 -0.634805 0.507595 0.691088 0.519536 0.105074 0.960348 0.935243 -3.046 0.357988 0.491087
0.375012 0.828832 0.951367 0.432279 0.275154 2.63869 0.291745 0.739141 0.564582 -5.70708 0.76442 0.838058
0.400641 0.527232 1.45037 0.058791 1.01707 1.55471 0.0531387 0.53709 0.206088 3.01423 0.630553 0.671074
0.200186 0.850179 -1.31559 0.0763892 0.743755 5.06973 0.948105 0.289171 0.337871 -5.99671 0.732254 0.835663
0.708048 0.605176 0.881955 0.279621 0.141891 -5.15581 0.0731357 0.806125 0.853079 5.32735 0.11355 0.556637
0.017665 0.142064 0.0192649 0.759464 0.706458 1.01862 0.0816154 0.443103 0.213161 5.95356 0.2939 0.453217
0.242033 0.602425 -5.78124 0.599064 0.187205 -2.20553 0.959521 0.706918 0.94056 -1.40149 0.351149 0.197002
1.00086 0.455077 1.79714 0.0997744 0.631607 1.84406 0.43637 0.93598 0.0495626 -1.30654 0.297147 0.231462
0.776241 0.43548 3.0892 0.587198 0.633927 -0.517372 0.457637 0.605914 0.648855 1.7846 0.835886 0.633717
0.730424 0.605941 -6.01408 0.247359 0.763198 5.53306 0.918252 0.25396 0.203278 -3.64636 0.705638 0.61288
0.353938 0.490971 -5.15456 0.74475 0.613168 1.87483 0.694388 0.807908 0.147756 -1.99769 0.874812 0.92539
0.619933 0.738314 -6.46902 0.587859 0.995886 0.457699 0.567457 0.314481 0.18674 -1.79019 0.877299 0.851464
0.753193 0.175494 4.77418 0.0747461 0.105844 -2.62336 0.814459 0.555395 0.462717 -0.846603 0.310971 0.479186
0.510134 0.176028 1.07502 0.688057 0.339516 -0.399121 0.956348 0.88411 0.738688 2.2697 0.949 0.138311
0.0717262 0.79374 -4.47487 0.686007 0.957391 -7.2034 0.878786 0.658836 0.323527 -1.55444 0.518652 0.248108
0.290131 0.896138 0.649528 0.630392 0.110866 -4.30107 0.370895 0.496849 0.790859 8.92541 0.38592 0.683163
0.540558 0.641344 -3.19087 0.0886569 0.737851 -0.577588 0.627259 0.872157 0.504676 -5.11777 0.514084 0.415044
0.788252 0.555508 2.66023 0.973441 0.626731 -0.574999 0.385226 0.135119 0.743803 2.17861 0.620822 0.040573
0.335512 0.0534692 1.51597 0.134474 0.853338 0.721896 0.595418 0.726102 0.854994 5.29114 0.133573 0.712013
0.618638 0.206371 1.96753 0.0878029 0.625576 -1.3427 0.314514 0.369832 0.663497 2.95017 0.810152 0.518827
0.62022 0.835713 1.71622 0.13708 0.660553 0.679894 0.254888 0.14714 0.868766 -0.664971 0.435085 0.599524
0.307424 0.549136 -0.154131 0.301203 0.11472 3.00481 0.132712 0.288762 0.790066 0.127832 0.788714 0.746273
0.618885 0.356983 1.19457 0.261181 0.873147 0.575007 0.245541 0.544089 0.984175 3.2499 0.0871343 0.528641
0.430027 0.100405 0.352517 0.871845 0.253838 -1.23204 0.123602 0.93716 0.0912947 2.14616 0.0649301 0.337019
0.91647 0.38311 6.68484 0.88988 0.835064 -2.39749 0.836507 0.696528 0.397337 0.227614 0.00881625 0.52464
0.957914 0.422008 3.00306 0.621519 0.277795 -5.21993 0.949012 0.44137 0.901673 2.82637 0.945287 0.622713
0.789526 0.58565 -3.6062 0.208824 0.403906 2.7528 0.418193 0.0882874 0.671148 -2.46241 0.0721788 0.820473
0.667946 0.686849 -2.17367 0.497317 0.690848 1.01549 0.685628 0.732831 0.77806 3.40165 0.876567 0.653006
0.713841 0.105793 2.03294 0.993234 0.0860135 3.14267 0.145836 0.989826 0.632768 -3.62082 0.932041 0.383594
0.694326 0.209136 0.148397 0.206527 0.519482 3.66009 0.105346 0.902985 0.442806 -4.25958 0.778145 0.164006
0.974441 0.778894 6.48005 0.261923 0.458774 -0.833779 0.755149 0.114259 0.405322 -3.91584 0.991908 0.0494735
0.257562 0.202866 2.07583 0.17988 0.629609 -0.679001 0.939315 0.200052 0.781164 1.22227 0.659771 0.921545
0.422288 -2.50635 -1.46338 -1.6023 -1.84221 -0.344169 -0.307905 -1.62429 -3.31261
0.196554 -1.94979 -0.994689 -1.23098 -2.14057 -0.365164 -0.080708 -1.40259 -3.4215
-8.9893 9.34044 -10.3186 9.27844 -1.89813 -9.48953 7.49168 -10.1012 12.5381
1.183 -2.39131 -0.47187 -1.04568 -1.07435 -0.238901 -0.0490415 -1.64092 -3.48751
0.280129 -1.77857 -1.43888 -1.23298 -2.11657 -0.215215 -0.81794 -2.21149 -3.01541
-16.4326 -8.78439 -8.49366 8.21918 4.66897 8.06705 -12.8712 15.9745 19.8687
0.453258 -2.16244 -0.50957 -0.867093 -2.09136 -0.284455 -0.487313 -1.49971 -3.33198
0.556833 -2.26427 -1.31253 -1.38828 -1.79405 -0.0785346 -0.361075 -1.23356 -3.1663
0.327637 -2.45429 -0.806304 -1.11871 -2.15539 -0.186563 -0.372242 -2.07658 -2.96845
-8.28416 18.0912 14.9721 -16.9528 16.9704 -23.0413 -8.14845 4.80364 6.79369
0.837821 -1.80192 -0.883725 -1.42135 -2.03776 -0.0541501 -0.400628 -2.07058 -3.41988
0.715436 -2.20815 -1.18571 -1.01918 -1.52575 -0.177048 -0.146913 -1.73517 -3.79789
例如:
输入向量:
0
1 0 0 0 1 1 0 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 0 0
1
1 1 1 0 1 0 1 1 0 1 0 0 1 1 0 0 1 1 1 0 1 1 1 1 0 1 1 0 1 0 1 1
2
1 0 0 0 0 0 1 1 1 1 0 0 1 1 1 0 1 0 1 0 1 1 1 0 0 0 0 1 1 0 0 1
3
1 1 0 0 0 0 1 1 1 0 0 1 1 1 0 1 0 0 1 1 0 1 1 1 1 0 1 1 1 1 0 0
对应着样本实际输出为:
0
4.60587e-006 0.999625 0.0122094 8.20651e-009 0.154652 1.09484e-015 0.0240042 9.64179e-010 2.47071e-005
1
1.8857e-009 1.02738e-008 1.29456e-012 0.997627 9.50407e-007 0.0181077 0.000289101 4.42784e-005 0.880562
2
1.52949e-005 1.79338e-012 3.39587e-008 0.0418522 5.61276e-006 0.991913 2.94107e-007 0.327807 2.77761e-005
3
3.69813e-013 0.293486 2.50337e-006 3.09886e-005 0.947757 3.49155e-012 7.02339e-008 0.00738104 0.999895
5结论
实验表明盘数字识别,结果是令人鼓舞的。利用神经网络的自适应学习能力,可以从数字图像中发掘有用信息,从而找到影响分类的非线性映射关系,为快速、准确地进行数字识别提供了一种新的途径。但是,由于在神经网络的应用研究中,研究对象不同、研究对象的复杂程度不同以及一些固有特性的差异,神经网络模型的结构也不同,不同的网络结构对网络的训练和识别效果都会产生影响。除此之外,网络初始权值的选取以及训练程度等对网络也都有影响。对于数字图像本身,预处理的算法、特征向量的提取方法等因素对数字识别率的影响也很大。这些问题仍旧需要进一步研究和提高。
以上是关于神经网络原理及其c++实现的主要内容,如果未能解决你的问题,请参考以下文章
[ C++ ] STL priority_queue(优先级队列)使用及其底层模拟实现,容器适配器,deque(双端队列)原理了解