推荐一篇非常详细的对BP的解答:http://www.2cto.com/kf/201610/553336.html
这里主要讲解的是昨天未涉及和讲解到的ANN的详细和拓展的部分。
具体涉及到的是:
BACKPROP和RPROP两种训练方式的原理和区别。
实现神经网络时每个函数的具体参数及意义。
BACKPROP
前馈神经网络是神经网络的一种,也是最常用的一种神经网络。它包括一个输入层,一个输出层和若干个隐含层,因此具有该种拓扑结构的神经网络又称为多层感知器(MLP)。如图所示,该MLP包括一个输入层,一个输出层和一个隐含层,其中某一层的神经元只能通过一个方向连接到下一层的神经元。Backprop(backward propagation oferrors,误差的反向传播,简称BP)算法的核心思想是:通过前向通路(箭头的方向)得到误差,再把该误差反向传播实现权值w的修正。
目标值t有J种可能的值,即t={t1, t2,…,tJ},设样本x经过前向通路得到的最终输出为y={y1L,y2L,…,yJL },则该样本的平方误差为:之所以式中的平方误差函数要除以2,是为了便于后面的求导运算,因为它并不影响误差的变化趋势。
显然,为了减小E,每层神经元的输出又由上层的所有神经元的输出经加权激活后得到,因此可以说误差E是全体权值w的函数,通过改变权值w,就可达到使误差E最小的目的
Backprop算法是一种迭代的方法,也就是我们不必通过一次改变权值w来达到使E最小的目的,我们只需渐进的减小E即可,误差越大,那么权值的变化就也越大,而当权值改变时,误差就要重新计算。这样两者相互作用不断迭代,直到误差小于某个值(即收敛)为止。该方法就是我们常用的梯度下降法。
误差E对权值w的导数为w的变化率,即:
式中,η表示学习效率,它的取值在0和1之间,它起到控制收敛速度和准确性的作用。如果η过大,导致振荡,则很难收敛,如果η过小,则需要更长的时间收敛。为了改变因η选取的不好而带来的问题,又引入了被称为“动量(momentum)”的参数μ,则w的变化率改写为:
在反向传播的过程中,所有权值都经过了上述计算后,就得了更新后的所有权值。用新得到的权值计算下一个样本,因为样本是一个一个的进入MLP,每完成一个样本的计算就更新一次权值。为了增加鲁棒性,在每次迭代之前,可以把全体样本打乱顺序,这样每次迭代的过程中提取样本的顺序就会不相同。
首先要解决的问题是初始化权值,即第一次权值如何选择。一般的做法是随机选择很小的值作为初始权值,但这样做收敛较慢。比较好的方法是采用Nguyen-Widrow算法初始化权值。它的基本思想是每个神经元都有属于自己的一个区间范围,通过初始化权值就可以限制它的区间位置,当改变权值时,该神经元也只是在自己的区间范围内变化,因此该方法可以大大提高收敛速度。
Nguyen-Widrow算法初始化MLP权值的方法为:对于所有连接输出层的权值和偏移量,初始值为一个在正负1之间的随机数。对于中间层的权值,初始为:RPROP
以上我们介绍了经典的Backprop算法,该算法还是有一些不足之处。首先是它的学习效率η是需要我们事先确定好;另外权值的变化是基于误差梯度的变化率,虽然这点乍一看,似乎没有问题,但我们不敢保证它永远正确有效。为此Riedmiller等人提出了RPROP算法(resilient backpropagation),用以改善Backprop算法。
RPROP算法的权值变化率并不是基于误差梯度的变化率,而是基于它的符号:
常数η+必须大于1,常数η-必须在0和1之间
关于Δ(t)的初始值和它的变化范围。Riedmiller等人已经给出了Δ(0)初始化为0.1是比较正确的选择,而Δmax(t)=50,Δmin(t)=10-6可以有效的防止溢出。
函数参数
create函数:MLP模型的构建:
void CvANN_MLP::create( const CvMat* _layer_sizes, int _activ_func,
double _f_param1, double _f_param2 )
{
CV_FUNCNAME( "CvANN_MLP::create" );
__BEGIN__;
// l_count表示MLP的层数,buf_sz表示开辟存储权值的内存空间的大小
int i, l_step, l_count, buf_sz = 0;
int *l_src, *l_dst;
clear(); //清除和初始化一些全局变量
//判断_layer_sizes的格式、数据类型是否正确,_layer_sizes必须是相量形式,数据类型必须为CV_32SC1,不对则报错
if( !CV_IS_MAT(_layer_sizes) ||
(_layer_sizes->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_src[0]);
//创建相量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_src[i*l_step]; //得到当前层的神经元的数量
//满足条件:0 < i && i < l_count-1,说明i为隐含层,该if语句的作用是,如果是隐含层,则神经元的数量一定要大于1,如果是输入层或输出层,则神经元的数量至少应为1,否则程序报错
if( n < 1 + (0 < i && i < l_count-1))
CV_ERROR( CV_StsOutOfRange,
"there should be at least one input and one output "
"and every hidden layer must have more than 1 neuron" );
//把当前层的神经元的数量赋值给变量layer_sizes所对应的层
l_dst[i] = n;
//记录下MLP层中数量最多的神经元的数量
max_count = MAX( max_count, n );
//统计该MLP一共有多少个权值,其中也包括偏移量
if( i > 0 )
buf_sz += (l_dst[i-1]+1)*n;
}
// l_dst[0]表示输入层神经元的数量,l_dst[l_count-1]表示输出层神经元的数量
buf_sz += (l_dst[0] + l_dst[l_count-1]*2)*2;
//创建相量wbuf,用于存储权值
CV_CALL( wbuf = cvCreateMat( 1, buf_sz, CV_64F ));
//为weights开辟内存空间
CV_CALL( weights = (double**)cvAlloc( (l_count+2)*sizeof(weights[0]) ));
//weights[0]指向wbuf的首地址,它表示输入层规范化所用的系数
weights[0] = wbuf->data.db;
//定义weights[1]首地址
weights[1] = weights[0] + l_dst[0]*2;
// weights[1]至weights[l_count]表示MLP相应层的所有权值,包括偏移量(即公式中的+ 1),它存放在数组的最后一个位置上
for( i = 1; i < l_count; i++ )
weights[i+1] = weights[i] + (l_dst[i-1] + 1)*l_dst[i];
// weights[l_count]和weights[l_count+1]都表示输出层规范化所用到的系数,训练时用的是weights[l_count+1]内的值,预测时用的是weights[l_count]内的值
weights[l_count+1] = weights[l_count] + l_dst[l_count-1]*2;
__END__;
}
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 < 0 || _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) < FLT_EPSILON )
_f_param2 = 1.;
break;
default: //线性激活函数
min_val = max_val = min_val1 = max_val1 = 0.;
_f_param1 = 1.;
_f_param2 = 0.;
}
f_param1 = _f_param1; //赋值α
f_param2 = _f_param2; //赋值β
__END__;
}
总结来说:
α和β均为函数的系数。在系统进行构建的时候,不但需要指定激励函数的类型,还要在需要使用参数的函数中初始化参数
CV_WRAP virtual void create( const cv::Mat& layerSizes,
int activateFunc=CvANN_MLP::SIGMOID_SYM,
double fparam1=0, double fparam2=0 );
第一个参数是输入层,隐藏层,输出层,的感知器的个数信息
第二个参数是激励函数的类型
第三个和第四个参数分别数系数α和β的值,默认为0,也可以自行设置参数,但是如果参数过小会被重新赋值
CvANN_MLP_TrainParams的初始化:训练参数的确定
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.;
}
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 < 0 )
bp_moment_scale = 0.1;
bp_moment_scale = MIN( bp_moment_scale, 1 );
}
//如果输入参数_train_method为除了RPROP和BACKPROP以外的值,则程序给出的算法为RPROP
else
train_method = RPROP;
}
//关于CvTermCriteria 的构造函数:
#define CV_TERMCRIT_ITER 1
#define CV_TERMCRIT_NUMBER CV_TERMCRIT_ITER
#define CV_TERMCRIT_EPS 2
typedef struct CvTermCriteria
{
//【1】int type--type of the termination criteria,one of:
//【1】int type---迭代算法终止条件的类型,是下面之一:
//【1】CV_TERMCRIT_ITER---在完成最大的迭代次数之后,停止算法
//【2】CV_TERMCRIT_EPS----当算法的精确度小于参数double epsilon指定的精确度时,停止算法
//【3】CV_TERMCRIT_ITER+CV_TERMCRIT_EPS--无论是哪一个条件先达到,都停止算法
int type; /* may be combination of
CV_TERMCRIT_ITER
CV_TERMCRIT_EPS */
//【2】Maximum number of iterations
//【2】最大的迭代次数
int max_iter;
//【3】Required accuracy
//【3】所要求的精确度
double epsilon;
}
总结来讲,train函数的使用首先需要包含两个参数:
CvTermCriteria类型的term_cri 和int类型的train_method,第一个表示训练迭代的停止条件,第二个则表示使用的训练方法。
如果使用的是 RPROP:
可以根据上面所提到的计算公式式2式3得到,我们需要指明参数值Δ(0)、η+、η-、Δmin(t)、Δmax(t),而这五个参数分别对应的是:
Δ(0): rp_dw0(一般情况下是0.1)
η+ rp_dw_plus (必须大于1)
η-: rp_dw_minus (必须在0和1之间)
Δmin(t): rp_dw_min (一般设为:10^-6 /FLT_EPSILON,不能小于0)
Δmax(t) :rp_dw_max(Riedmiller的结论值50)
如果使用的是 :BACKPROP
只需要指明两个参数即可:
bp_dw_scale, bp_moment_scale ;分别代表式1中的η和μ。用来计算权值的变化率。其取值都应当尽量合理且大于0。
训练MLP模型:
int CvANN_MLP::train( const CvMat* _inputs, const CvMat* _outputs,
const CvMat* _sample_weights, const CvMat* _sample_idx,
CvANN_MLP_TrainParams _params, int flags )
//_inputs表示MLP的输入数据,即待训练的样本数据
//_outputs表示MLP的输出数据,即待训练的样本响应值或分类标签
//_sample_weights表示事先定义好的样本的权值
//_sample_idx表示真正被用于训练的样本数据的索引,即有一部分样本不用于训练MLP
//_params表示MLP模型参数
//flags表示控制算法的参数,可以为UPDATE_WEIGHTS、NO_INPUT_SCALE和NO_OUTPUT_SCALE,以及它们的组合,这些变量的含义为:UPDATE_WEIGHTS表示算法需要更新网络的权值;NO_INPUT_SCALE表示算法无需规范化MLP的输入数据,规范化的意思就是使输入样本的特征属性均值为0,标准方差为1;NO_OUTPUT_SCALE表示算法无需归一化MLP的输出数据
//该函数返回迭代次数
{
const int MAX_ITER = 1000; //表示最大迭代次数
const double DEFAULT_EPSILON = FLT_EPSILON; //定义一个常数
double* sw = 0; //表示样本的权值
CvVectors x0, u; //分别表示MLP的输入数据和输出数据
int iter = -1; //表示训练MLP所需的迭代次数
//初始化首地址指针
x0.data.ptr = u.data.ptr = 0;
CV_FUNCNAME( "CvANN_MLP::train" );
__BEGIN__;
int max_iter;
double epsilon;
params = _params; //MLP模型参数
// initialize training data
//调用prepare_to_train函数,为MLP模型的训练准备参数,该函数在后面给出详细的介绍
CV_CALL( prepare_to_train( _inputs, _outputs, _sample_weights,
_sample_idx, &x0, &u, &sw, flags ));
// ... and link weights
//如果没有定义UPDATE_WEIGHTS,则需要调用init_weights函数进行权值的初始化,该函数在后面给出了详细的介绍
if( !(flags & UPDATE_WEIGHTS) )
init_weights();
//得到最大迭代次数
max_iter = params.term_crit.type & CV_TERMCRIT_ITER ? params.term_crit.max_iter : MAX_ITER;
max_iter = MAX( max_iter, 1 ); //最大迭代次数必须大于等于1
//得到用前后两次误差之差来判断终止迭代的系数
epsilon = params.term_crit.type & CV_TERMCRIT_EPS ? params.term_crit.epsilon : DEFAULT_EPSILON;
epsilon = MAX(epsilon, DBL_EPSILON);
//重新定义终止MLP训练的条件
params.term_crit.type = CV_TERMCRIT_ITER + CV_TERMCRIT_EPS;
params.term_crit.max_iter = max_iter;
params.term_crit.epsilon = epsilon;
//如果是BACKPROP算法,则调用train_backprop函数,如果是RPROP算法,则调用train_rprop函数,这个两个函数在后面有详细的介绍
if( params.train_method == CvANN_MLP_TrainParams::BACKPROP )
{
CV_CALL( iter = train_backprop( x0, u, sw ));
}
else
{
CV_CALL( iter = train_rprop( x0, u, sw ));
}
__END__;
//释放内存空间
cvFree( &x0.data.ptr );
cvFree( &u.data.ptr );
cvFree( &sw );
return iter; //返回迭代次数
}
//为训练MLP模型准备参数:
bool CvANN_MLP::prepare_to_train( const CvMat* _inputs, const CvMat* _outputs,
const CvMat* _sample_weights, const CvMat* _sample_idx,
CvVectors* _ivecs, CvVectors* _ovecs, double** _sw, int _flags )
{
bool ok = false; //该函数正确返回的标识
CvMat* sample_idx = 0; //表示样本数据的索引
CvVectors ivecs, ovecs; //分别表示输入层和输出层的数据
double* sw = 0; //表示样本的权值
int count = 0;
CV_FUNCNAME( "CvANN_MLP::prepare_to_train" );
ivecs.data.ptr = ovecs.data.ptr = 0; //初始化为0
assert( _ivecs && _ovecs ); //确保输入参数_ivecs和_ovecs有效
__BEGIN__;
const int* sidx = 0; //该指针指向sample_idx
// sw_type和sw_count分别表示样本权值数据的类型和数量
int i, sw_type = 0, sw_count = 0;
int sw_step = 0; //示样本权值数据的步长
double sw_sum = 0; //表示样本权值的累加和
//通过判断layer_sizes是否被正确赋值,来确保MLP模型是否被构建好
if( !layer_sizes )
CV_ERROR( CV_StsError,
"The network has not been created. Use method create or the appropriate constructor" );
//判断输入参数_inputs是否正确
if( !CV_IS_MAT(_inputs) || (CV_MAT_TYPE(_inputs->type) != CV_32FC1 &&
CV_MAT_TYPE(_inputs->type) != CV_64FC1) || _inputs->cols != layer_sizes->data.i[0] )
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.i[layer_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(sw[0]) )); //为sw分配空间
}
//为MLP的输入和输出数据开辟一块内存空间
CV_CALL( ivecs.data.ptr = (uchar**)cvAlloc( count*sizeof(ivecs.data.ptr[0]) ));
CV_CALL( ovecs.data.ptr = (uchar**)cvAlloc( count*sizeof(ovecs.data.ptr[0]) ));
ivecs.type = CV_MAT_TYPE(_inputs->type); //指定类型
ovecs.type = CV_MAT_TYPE(_outputs->type); //指定类型
ivecs.count = ovecs.count = count; //相量的长度,即维数
for( i = 0; i < count; i++ ) //遍历所有的待训练样本数据
{
int idx = sidx ? sidx[i] : i; //表示样本索引值
//给MLP的输入和输出数据赋值
ivecs.data.ptr[i] = _inputs->data.ptr + idx*_inputs->step;
ovecs.data.ptr[i] = _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.fl[si*sw_step] :
_sample_weights->data.db[si*sw_step];
sw[i] = w; //赋值
if( w < 0 ) //权值不能小于0
CV_ERROR( CV_StsOutOfRange, "some of sample weights are negative" );
sw_sum += w; //权值累加
}
}
// normalize weights
if( sw ) //如果sw被定义,归一化样本权值
{
sw_sum = sw_sum > DBL_EPSILON ? 1./sw_sum : 0; //倒数
for( i = 0; i < count; i++ )
sw[i] *= sw_sum; //归一化
}
//调用calc_input_scale和calc_output_scale函数,依据_flags分别对输入数据(样本值)和输出数据(样本响应值)进行规范化处理,这两个函数在后面给出详细的介绍
calc_input_scale( &ivecs, _flags );
CV_CALL( calc_output_scale( &ovecs, _flags ));
ok = true; //标识函数返回值
__END__;
if( !ok ) //没有正确对训练过程进行预处理,则清空一些变量
{
cvFree( &ivecs.data.ptr );
cvFree( &ovecs.data.ptr );
cvFree( &sw );
}
cvReleaseMat( &sample_idx ); //释放空间
*_ivecs = ivecs; //赋值
*_ovecs = ovecs; //赋值
*_sw = sw; //赋值
return ok;
}
在这里我们只需要拿出最关键的信息即可:
int CvANN_MLP::train( const CvMat* _inputs, const CvMat* _outputs,
const CvMat* _sample_weights, const CvMat* _sample_idx,
CvANN_MLP_TrainParams _params, int flags )
//_inputs表示MLP的输入数据,即待训练的样本数据
//_outputs表示MLP的输出数据,即待训练的样本响应值或分类标签
//_sample_weights表示事先定义好的样本的权值
//_sample_idx表示真正被用于训练的样本数据的索引,即有一部分样本不用于训练MLP
//_params表示MLP模型参数
//flags表示控制算法的参数,可以为UPDATE_WEIGHTS、NO_INPUT_SCALE和NO_OUTPUT_SCALE,以及它们的组合,这些变量的含义为:UPDATE_WEIGHTS表示算法需要更新网络的权值;NO_INPUT_SCALE表示算法无需规范化MLP的输入数据,规范化的意思就是使输入样本的特征属性均值为0,标准方差为1;NO_OUTPUT_SCALE表示算法无需归一化MLP的输出数据
//该函数返回迭代次数
在进行训练的时候,
第一个表示样本数组:格式为样本个数每个样本的输入的数据个数,数据类型格式是CV_32FC1
第二个表示样本标记数组:格式为样本的个数每个样本的结果可能性个数/输出的数据个数,数据类型格式是CV_32FC1
第三个表示样本权值:格式为单行或单列数组,通常是单行,列数为(真正需要训练的)输入样本的样本个数,数据类型格式为CV_32FC1或者CV_64FC1(这个参数通常在使用RPROP训练方法时使用)
第四个表示真正被用于训练的样本数据的索引,其格式通常是单行,列数是样本个数,数据类型格式是 CV_8UC1, CV_8UC3,通常情况下每个输入的样本都需要被训练,所以这里一般不需设置。
第五个表示上面构造好的训练参数
最后一个表示控制算法的参数,一般不进行设置。
使用模型:进行数据预测
CV_WRAP virtual float predict( const cv::Mat& inputs, CV_OUT cv::Mat& outputs ) const;
//inputs,一个测试用例,单行,列数为一个输入样本的输入数据个数,数据类型格式为CV_32FC1
//outputs,一个输出样本,单行,列数为一个检测结果的可能数目,其内容是每种可能的拟合率
图像进行特征提取,把它保存在inputs里,通过调用predict函数,我们得到一个输出向量,它是一个1*nClass的行向量,
其中每一列说明它与该类的相似程度(0-1之间),也可以说是置信度。我们只用对output求一个最大值,就可得到结果。
这个函数的返回值是一个无用的float值,可以忽略。
由于该结果是单行的,通常在得到后寻找最大比率的列数,也就是相对应的得到的结果最可能的下标数(应与样本标记数组的相对应),查找最大值方法:
Point maxLoc;
double maxVal;
//minMaxLoc函数的参数为:
//1.输入的源图像
//2.原图像中最小的值
//3.原图像中最大的值
//4.原图像中最小的值所在坐标
//5.原图像中最大的值所在坐标
minMaxLoc(output, 0, &maxVal, 0, &maxLoc);//找到最大的相似度
//maxVal是最大的相似度,maxLoc是该像素的坐标,由于是行向量,所以y均为1,取的x即可
int result = maxLoc.x;