Opencv2.4.9源码分析——Neural Networks.doc_第1页
Opencv2.4.9源码分析——Neural Networks.doc_第2页
Opencv2.4.9源码分析——Neural Networks.doc_第3页
Opencv2.4.9源码分析——Neural Networks.doc_第4页
Opencv2.4.9源码分析——Neural Networks.doc_第5页
已阅读5页,还剩40页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Opencv2.4.9源码分析Neural Networks一、原理神经网络(Neural Networks)是一种模仿生物神经系统的机器学习算法。该算法的提出最早可追述至上个世纪四十年代,这几乎与电子计算机的历史同步。但它的发展并非一帆风顺,也经历了初创阶段黄金阶段停滞阶段复兴阶段,直到目前的高速发展阶段。年初由Google公司开发的神经网络围棋AlphaGo击败世界围棋冠军李世石,使神经网络技术更是受到世人的注目,因为它的意义要远大于1997年IBM的超级计算机深蓝击败国际象棋大师卡斯帕罗夫。与生物神经系统相似,人工神经网络也是由若干个神经元构成。如图1所示,x1、x2、xn为该神经元的输入,y为该神经元的输出。显然,不同的输入对神经元的作用是不同的,因此用权值w1、w2、wn来表示这种影响程度的不同。神经元内部包括两个部分,第一个部分是对输入的加权求和,第二个部分是对求和的结果进行“激活”,得到输出。加权求和的公式为:对于MLP,我们可以用Backprop(backward propagation oferrors,误差的反向传播,简称BP)算法实现它的建模,该算法具有结构简单、易于实现等特点。Backprop算法是一种监督的机器学习算法,输入层的神经元数量一般为样本的特征属性的数量,输出层的神经元的数量一般为样本的所有的可能目标值的数量,如果是分类问题,则为样本的分类数量,因此,与其他机器学习算法不同,在MLP中,样本对应的响应值应该是一个相量,相量的维数与输出层的神经元的数量一致。而隐含层的层数以及各层神经元的数量则根据实际情况进行选取。Backprop算法的核心思想是:通过前向通路(箭头的方向)得到误差,再把该误差反向传播实现权值w的修正。MLP的误差可以用平方误差函数来进行表示。设某个样本x对应的目标值为t,样本x有n个特征属性,即x=x1, x2,xn,目标值t有J种可能的值,即t=t1, t2,tJ,因此该MLP的输入层(即第一层)一共有n个神经元,输出层(即第L层,设MLP一共有L层)一共有J个神经元。设样本x经过前向通路得到的最终输出为y=y1L,y2L,yJL ,输出y的下标表示y所在层的神经元索引,上标表示y所在的层,则该样本的平方误差为:之所以式10中的平方误差函数要除以2,是为了便于后面的求导运算,因为它并不影响误差的变化趋势。显然,MLP算法的目标就是使E最小。由图1可知,式10中的yjL是由上一层(即第L-1层)所有神经元的输出经加权激活后得到,而第L-1层神经元的输出又由第L-2层的所有神经元的输出经加权激活后得到,因此可以说误差E是全体权值w的函数,通过改变权值w,就可达到使误差E最小的目的。Backprop算法是一种迭代的方法,也就是我们不必通过一次改变权值w来达到使E最小的目的,我们只需渐进的减小E即可。w和E的关系可以形象的比作山坡,山的高度是误差,平面的维度空间是权值,山坡越陡(误差大),平面维度空间的变化(权值的变化)就越大,权值的变化与误差有关,而当权值改变时,误差就要重新计算。这样两者相互作用,即不断迭代,直到误差小于某个值(即收敛)为止。该方法就是我们常用的梯度下降法。误差E对权值w的导数为w的变化率,即:由式36就得到了基于wkhl的误差导数,再把该结果带入式11或式12中就得到了该权值的变化率,最后由式13就得到了更新后的权值。在反向传播的过程中,所有权值都经过了上述计算后,就得了更新后的所有权值。用新得到的权值计算下一个样本,以此类推,直至所有样本都经过MLP计算为止,此时就完成了所有样本的一次迭代过程。在每次迭代完成后,我们可以比较两次迭代的误差大小,如果两个误差之差满足我们的设计要求,则可以终止迭代,否则继续下次迭代。该方法也称作在线方法,因为样本是一个一个的进入MLP,每完成一个样本的计算就更新一次权值。为了增加鲁棒性,在每次迭代之前,可以把全体样本打乱顺序,这样每次迭代的过程中提取样本的顺序就会不相同。除了在线方法,还有一种方法称为批量方法,即把所有样本的误差累加在一起,用该累加误差计算误差的导数,进而得到权值的变化率。在完成上述计算的过程之前,首先要解决的问题就是初始化权值,即第一次权值如何选择。一般的做法是随机选择很小的值作为初始权值,但这样做收敛较慢。比较好的方法是采用Nguyen-Widrow算法初始化权值。它的基本思想是每个神经元都有属于自己的一个区间范围,通过初始化权值就可以限制它的区间位置,当改变权值时,该神经元也只是在自己的区间范围内变化,因此该方法可以大大提高收敛速度。Nguyen-Widrow算法初始化MLP权值的方法为:对于所有连接输出层的权值和偏移量,初始值为一个在正负1之间的随机数。对于中间层的权值,初始为:式中,常数+必须大于1,常数-必须在0和1之间。式40和式41中的E/w由式36得到。关于式40和式41,还有一些问题需要解决,那就是(t)的初始值和它的变化范围。Riedmiller等人已经给出了(0)初始化为0.1是比较正确的选择,而max(t)=50,min(t)=10-6可以有效的防止溢出。 二、源码分析 OpenCV的神经网络实现了MLP算法,具体为BACKPROP算法和RPROP算法两种,BACKPROP算法使用的是在线方法,RPROP算法使用的是批量方法。 结构CvANN_MLP_TrainParams表示MLP训练算法所需的参数,它的构造函数为:cpp view plain copy 在CODE上查看代码片派生到我的代码片CvANN_MLP_TrainParams:CvANN_MLP_TrainParams() /表示训练迭代的终止条件,默认为迭代次数(大于1000)和权值变化率(小于0.01) term_crit = cvTermCriteria( CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 1000, 0.01 ); /具体应用的MLP算法,默认为RPROP train_method = RPROP; / bp_dw_scale为式13中的,bp_moment_scale为式13中的 bp_dw_scale = bp_moment_scale = 0.1; /RPROP算法所需的参数(式40和式41),依次为(0)、+、-、min(t)、max(t) rp_dw0 = 0.1; rp_dw_plus = 1.2; rp_dw_minus = 0.5; rp_dw_min = FLT_EPSILON; rp_dw_max = 50.; cpp view plain copy 在CODE上查看代码片派生到我的代码片CvANN_MLP_TrainParams:CvANN_MLP_TrainParams( CvTermCriteria _term_crit, int _train_method, double _param1, double _param2 ) term_crit = _term_crit; train_method = _train_method; bp_dw_scale = bp_moment_scale = 0.1; rp_dw0 = 1.; rp_dw_plus = 1.2; rp_dw_minus = 0.5; rp_dw_min = FLT_EPSILON; rp_dw_max = 50.; if( train_method = RPROP ) /RPROP算法 rp_dw0 = _param1; /输入参数_param1表示(0) if( rp_dw0 FLT_EPSILON ) /(0)不能太小 rp_dw0 = 1.; rp_dw_min = _param2; /输入参数_param2表示min(t) rp_dw_min = MAX( rp_dw_min, 0 ); /min(t)不能小于0 else if( train_method = BACKPROP ) /BACKPROP算法 bp_dw_scale = _param1; /输入参数_param1表示 /确保在一个合理的范围内 if( bp_dw_scale = 0 ) bp_dw_scale = 0.1; bp_dw_scale = MAX( bp_dw_scale, 1e-3 ); bp_dw_scale = MIN( bp_dw_scale, 1 ); bp_moment_scale = _param2; /输入参数_param2表示 /确保在一个合理的范围内 if( bp_moment_scale cols != 1 & _layer_sizes-rows != 1) | CV_MAT_TYPE(_layer_sizes-type) != CV_32SC1 ) CV_ERROR( CV_StsBadArg, The array of layer neuron counters must be an integer vector ); /调用set_activ_func函数,设置激活函数,该函数在后面给出详细介绍 CV_CALL( set_activ_func( _activ_func, _f_param1, _f_param2 ); /l_count为相量_layer_sizes的维数,即MLP的层数L l_count = _layer_sizes-rows + _layer_sizes-cols - 1; l_src = _layer_sizes-data.i; /_layer_sizes的首地址指针 /_layer_sizes元素的步长 l_step = CV_IS_MAT_CONT(_layer_sizes-type) ? 1 : _layer_sizes-step / sizeof(l_src0); /创建相量layer_sizes CV_CALL( layer_sizes = cvCreateMat( 1, l_count, CV_32SC1 ); l_dst = layer_sizes-data.i; /layer_sizes的首地址指针 max_count = 0; /表示某层中,最多的神经元的数量 for( i = 0; i l_count; i+ ) /遍历MLP的所有层 int n = l_srci*l_step; /得到当前层的神经元的数量 /满足条件:0 i & i l_count-1,说明i为隐含层,该if语句的作用是,如果是隐含层,则神经元的数量一定要大于1,如果是输入层或输出层,则神经元的数量至少应为1,否则程序报错 if( n 1 + (0 i & i 0 ) buf_sz += (l_dsti-1+1)*n; / l_dst0表示输入层神经元的数量,l_dstl_count-1表示输出层神经元的数量 buf_sz += (l_dst0 + l_dstl_count-1*2)*2; /创建相量wbuf,用于存储权值 CV_CALL( wbuf = cvCreateMat( 1, buf_sz, CV_64F ); /为weights开辟内存空间 CV_CALL( weights = (double*)cvAlloc( (l_count+2)*sizeof(weights0) ); /weights0指向wbuf的首地址,它表示输入层规范化所用的系数 weights0 = wbuf-data.db; /定义weights1首地址 weights1 = weights0 + l_dst0*2; / weights1至weightsl_count表示MLP相应层的所有权值,包括偏移量(即公式中的+ 1),它存放在数组的最后一个位置上 for( i = 1; i l_count; i+ ) weightsi+1 = weightsi + (l_dsti-1 + 1)*l_dsti; / weightsl_count和weightsl_count+1都表示输出层规范化所用到的系数,训练时用的是weightsl_count+1内的值,预测时用的是weightsl_count内的值 weightsl_count+1 = weightsl_count + l_dstl_count-1*2; _END_; 设置激活函数:cpp view plain copy 在CODE上查看代码片派生到我的代码片void CvANN_MLP:set_activ_func( int _activ_func, double _f_param1, double _f_param2 ) CV_FUNCNAME( CvANN_MLP:set_activ_func ); _BEGIN_; /判断激活函数是否为线性、对称SIGMOR、或高斯中的一种 if( _activ_func GAUSSIAN ) CV_ERROR( CV_StsOutOfRange, Unknown activation function ); activ_func = _activ_func; /赋值 /根据不同的激活函数类型,赋予不同的参数 switch( activ_func ) case SIGMOID_SYM: /对称SIGMOID激活函数 max_val = 0.95; min_val = -max_val; max_val1 = 0.98; min_val1 = -max_val1; /如果用户定义的对称SIGMOID激活函数的参数过小,则重新赋值 if( fabs(_f_param1) FLT_EPSILON ) _f_param1 = 2./3; if( fabs(_f_param2) FLT_EPSILON ) _f_param2 = 1.7159; break; case GAUSSIAN: /高斯激活函数 max_val = 1.; min_val = 0.05; max_val1 = 1.; min_val1 = 0.02; /如果用户定义的高斯激活函数的参数过小,则重新赋值 if( fabs(_f_param1) FLT_EPSILON ) _f_param1 = 1.; if( fabs(_f_param2) type) != CV_32FC1 & CV_MAT_TYPE(_inputs-type) != CV_64FC1) | _inputs-cols != layer_sizes-data.i0 ) CV_ERROR( CV_StsBadArg, input training data should be a floating-point matrix with the number of rows equal to the number of training samples and the number of columns equal to the size of 0-th (input) layer ); /判断输入参数_outputs是否正确 if( !CV_IS_MAT(_outputs) | (CV_MAT_TYPE(_outputs-type) != CV_32FC1 & CV_MAT_TYPE(_outputs-type) != CV_64FC1) | _outputs-cols != layer_sizes-data.ilayer_sizes-cols - 1 ) CV_ERROR( CV_StsBadArg, output training data should be a floating-point matrix with the number of rows equal to the number of training samples and the number of columns equal to the size of last (output) layer ); /确保样本的输入和输出的数量一致,即每个样本都必须有一个响应值或分类标签 if( _inputs-rows != _outputs-rows ) CV_ERROR( CV_StsUnmatchedSizes, The numbers of input and output samples do not match ); /如果定义了_sample_idx,则需要掩码一些样本数据 if( _sample_idx ) /得到真正用于训练的样本 CV_CALL( sample_idx = cvPreprocessIndexArray( _sample_idx, _inputs-rows ); sidx = sample_idx-data.i; /指针赋值 count = sample_idx-cols + sample_idx-rows - 1; /得到训练样本的数量 else count = _inputs-rows; /得到训练样本的数量 if( _sample_weights ) /如果定义了_sample_weights if( !CV_IS_MAT(_sample_weights) ) /确保_sample_weights格式正确 CV_ERROR( CV_StsBadArg, sample_weights (if passed) must be a valid matrix ); sw_type = CV_MAT_TYPE(_sample_weights-type); /数据类型 sw_count = _sample_weights-cols + _sample_weights-rows - 1; /数量 /判断sw_type格式是否正确,sw_count是否与样本的数量一致 if( (sw_type != CV_32FC1 & sw_type != CV_64FC1) | (_sample_weights-cols != 1 & _sample_weights-rows != 1) | (sw_count != count & sw_count != _inputs-rows) ) CV_ERROR( CV_StsBadArg, sample_weights must be 1d floating-point vector containing weights of all or selected training samples ); sw_step = CV_IS_MAT_CONT(_sample_weights-type) ? 1 : _sample_weights-step/CV_ELEM_SIZE(sw_type); /得到步长 CV_CALL( sw = (double*)cvAlloc( count*sizeof(sw0) ); /为sw分配空间 /为MLP的输入和输出数据开辟一块内存空间 CV_CALL( ivecs.data.ptr = (uchar*)cvAlloc( count*sizeof(ivecs.data.ptr0) ); CV_CALL( ovecs.data.ptr = (uchar*)cvAlloc( count*sizeof(ovecs.data.ptr0) ); ivecs.type = CV_MAT_TYPE(_inputs-type); /指定类型 ovecs.type = CV_MAT_TYPE(_outputs-type); /指定类型 ivecs.count = ovecs.count = count; /相量的长度,即维数 for( i = 0; i data.ptr + idx*_inputs-step; ovecs.data.ptri = _outputs-data.ptr + idx*_outputs-step; if( sw ) /如果sw被定义 int si = sw_count = count ? i : idx; /得到样本索引值 double w = sw_type = CV_32FC1 ? /得到当前样本的权值 (double)_sample_weights-data.flsi*sw_step : _sam

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论