Opencv249源码分析GradientBoostedTrees详解.docx
- 文档编号:9586973
- 上传时间:2023-02-05
- 格式:DOCX
- 页数:46
- 大小:507.51KB
Opencv249源码分析GradientBoostedTrees详解.docx
《Opencv249源码分析GradientBoostedTrees详解.docx》由会员分享,可在线阅读,更多相关《Opencv249源码分析GradientBoostedTrees详解.docx(46页珍藏版)》请在冰豆网上搜索。
Opencv249源码分析GradientBoostedTrees详解
Opencv2.4.9源码分析——GradientBoostedTrees
一、原理
梯度提升树(GBT,GradientBoostedTrees,或称为梯度提升决策树)算法是由Friedman于1999年首次完整的提出,该算法可以实现回归、分类和排序。
GBT的优点是特征属性无需进行归一化处理,预测速度快,可以应用不同的损失函数等。
从它的名字就可以看出,GBT包括三个机器学习的优化算法:
决策树方法、提升方法和梯度下降法。
前两种算法在我以前的文章中都有详细的介绍,在这里我只做简单描述。
决策树是一个由根节点、中间节点、叶节点和分支构成的树状模型,分支代表着数据的走向,中间节点包含着训练时产生的分叉决策准则,叶节点代表着最终的数据分类结果或回归值,在预测的过程中,数据从根节点出发,沿着分支在到达中间节点时,根据该节点的决策准则实现分叉,最终到达叶节点,完成分类或回归。
提升算法是由一系列“弱学习器”构成,这些弱学习器通过某种线性组合实现一个强学习器,虽然这些弱学习器的分类或回归效果可能仅仅比随机分类或回归要好一点,但最终的强学习器却可以得到一个很好的预测结果。
二、源码分析
下面介绍OpenCV的GBT源码。
首先给出GBT算法所需参数的结构体CvGBTreesParams:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
CvGBTreesParams:
:
CvGBTreesParams(int_loss_function_type,int_weak_count,
float_shrinkage,float_subsample_portion,
int_max_depth,bool_use_surrogates)
:
CvDTreeParams(3,10,0,false,10,0,false,false,0)
{
loss_function_type=_loss_function_type;
weak_count=_weak_count;
shrinkage=_shrinkage;
subsample_portion=_subsample_portion;
max_depth=_max_depth;
use_surrogates=_use_surrogates;
}
loss_function_type表示损失函数的类型,CvGBTrees:
:
SQUARED_LOSS为平方损失函数,CvGBTrees:
:
ABSOLUTE_LOSS为绝对值损失函数,CvGBTrees:
:
HUBER_LOSS为Huber损失函数,CvGBTrees:
:
DEVIANCE_LOSS为偏差损失函数,前三种用于回归问题,后一种用于分类问题
weak_count表示GBT的优化迭代次数,对于回归问题来说,weak_count也就是决策树的数量,对于分类问题来说,weak_count×K为决策树的数量,K表示类别数量
shrinkage表示收缩因子v
subsample_portion表示训练样本占全部样本的比例,为不大于1的正数
max_depth表示决策树的最大深度
use_surrogates表示是否使用替代分叉节点,为true,表示使用替代分叉节点
CvDTreeParams结构详见我的关于决策树的文章
CvGBTrees类的一个构造函数:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
CvGBTrees:
:
CvGBTrees(constcv:
:
Mat&trainData,inttflag,
constcv:
:
Mat&responses,constcv:
:
Mat&varIdx,
constcv:
:
Mat&sampleIdx,constcv:
:
Mat&varType,
constcv:
:
Mat&missingDataMask,
CvGBTreesParams_params)
{
data=0;//表示样本数据集合
weak=0;//表示一个弱学习器
default_model_name="my_boost_tree";
//orig_response表示样本的响应值,sum_response表示拟合函数Fm(x),sum_response_tmp表示Fm+1(x)
orig_response=sum_response=sum_response_tmp=0;
//subsample_train和subsample_test分别表示训练样本集和测试样本集
subsample_train=subsample_test=0;
//missing表示缺失的特征属性,sample_idx表示真正用到的样本的索引
missing=sample_idx=0;
class_labels=0;//表示类别标签
class_count=1;//表示类别的数量
delta=0.0f;//表示Huber损失函数中的参数δ
clear();//清除一些全局变量和已有的所有弱学习器
//GBT算法的学习
train(trainData,tflag,responses,varIdx,sampleIdx,varType,missingDataMask,_params,false);
}
GBT算法的学习构建函数:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
bool
CvGBTrees:
:
train(constCvMat*_train_data,int_tflag,
constCvMat*_responses,constCvMat*_var_idx,
constCvMat*_sample_idx,constCvMat*_var_type,
constCvMat*_missing_mask,
CvGBTreesParams_params,bool/*_update*/)//updateisnotsupported
//_train_data表示样本数据集合
//_tflag表示样本矩阵的存储格式
//_responses表示样本的响应值
//_var_idx表示要用到的特征属性的索引
//_sample_idx表示要用到的样本的索引
//_var_type表示特征属性的类型,是连续值还是离散值
//_missing_mask表示缺失的特征属性的掩码
//_params表示构建GBT模型的一些必要参数
{
CvMemStorage*storage=0;//开辟一块内存空间
params=_params;//构建GBT模型所需的参数
boolis_regression=problem_type();//表示该GBT模型是否用于回归问题
clear();//清空一些全局变量和已有的所有弱学习器
/*
n-countofsamples
m-countofvariables
*/
intn=_train_data->rows;//n表示训练样本的数量
intm=_train_data->cols;//m表示样本的特征属性的数量
//如果参数_tflag为CV_ROW_SAMPLE,则表示训练样本以行的形式储存的,即_train_data矩阵的每一行为一个样本,那么n和m无需交换;否则如果_tflag为CV_COL_SAMPLE,则表示样本是以列的形式储存的,那么n和m就需要交换。
总之,在后面的程序中,n表示训练样本的数量,m表示样本的特征属性的数量
if(_tflag!
=CV_ROW_SAMPLE)
{
inttmp;
CV_SWAP(n,m,tmp);
}
//new_responses表示每个样本的伪响应值,因为构建GBT决策树使用的是伪响应值
CvMat*new_responses=cvCreateMat(n,1,CV_32F);
cvZero(new_responses);//伪响应值初始为零
//实例化CvDTreeTrainData类,并通过该类内的set_data函数设置用于决策树的训练样本数据data
data=newCvDTreeTrainData(_train_data,_tflag,new_responses,_var_idx,
_sample_idx,_var_type,_missing_mask,_params,true,true);
if(_missing_mask)//如果给出了缺失特征属性的掩码
{
missing=cvCreateMat(_missing_mask->rows,_missing_mask->cols,
_missing_mask->type);//初始化missing
cvCopy(_missing_mask,missing);//赋值_missing_mask给missing
}
//初始化orig_response矩阵的大小,该变量表示样本的原始真实响应值
orig_response=cvCreateMat(1,n,CV_32F);
//step表示样本响应值的步长
intstep=(_responses->cols>_responses->rows)?
1:
_responses->step/CV_ELEM_SIZE(_responses->type);
//根据样本响应值_responses的数据类型,为orig_response赋值
switch(CV_MAT_TYPE(_responses->type))
{
caseCV_32FC1:
//32位浮点型数据
{
for(inti=0;i orig_response->data.fl[i]=_responses->data.fl[i*step]; };break; caseCV_32SC1: //32位整型数据 { for(inti=0;i orig_response->data.fl[i]=(float)_responses->data.i[i*step]; };break; default: //其他数据类型报错 CV_Error(CV_StsUnmatchedFormats,"Responseshouldbea32fC1or32sC1vector."); } if(! is_regression)//如果构建的GBT模型是用于分类问题 { class_count=0;//表示样本类别的数量 //为每个样本定义一个掩码,用于判断样本的类别 unsignedchar*mask=newunsignedchar[n]; memset(mask,0,n);//掩码清零 //computethecountofdifferentoutputclasses for(inti=0;i //如果当前样本的掩码没有被置1,则说明当前样本属于新的类别 if(! mask[i]) { class_count++;//样本类别数加1 //判断当前样本以后的所有样本的响应值是否与当前样本的响应值相同,即是否属于同一类,如果是同一类,则把样本掩码置1,说明它不再是新的类别 for(intj=i;j if(int(orig_response->data.fl[j])==int(orig_response->data.fl[i])) mask[j]=1; } delete[]mask;//删除mask变量 //初始化样本类别标签,并赋首地址指针 class_labels=cvCreateMat(1,class_count,CV_32S); class_labels->data.i[0]=int(orig_response->data.fl[0]); intj=1;//表示所有样本类别标签的索引值 for(inti=1;i { intk=0;//表示已得到的样本类别标签的索引值 //while循环用于判断是否有新的类别标签出现。 如果orig_response->data.fl[i]等于class_labels->data.i[k],说明当前样本的类别存在于已经得到的类别标签中,则退出while循环,继续for循环;如果k≥j,说明已经遍历完类别标签 while((int(orig_response->data.fl[i])-class_labels->data.i[k])&&(k k++;//索引值加1 if(k==j)//说明得到了新的类别标签 { //赋值新的类别标签 class_labels->data.i[k]=int(orig_response->data.fl[i]); j++;//索引值加1 } } } //insidegbtlearningproccessonlyregressiondecisiontreesarebuilt //GBT模型用到的是回归树,所以要把data->is_classifier赋值为false data->is_classifier=false; //preproccessingsampleindices //如果_sample_idx不为0,需要预处理那些被指定使用的样本数据 if(_sample_idx) { intsample_idx_len=get_len(_sample_idx);//被指定的要使用的样本数据的数量 switch(CV_MAT_TYPE(_sample_idx->type))//判断样本的数据类型 { caseCV_32SC1: //32位整型 { sample_idx=cvCreateMat(1,sample_idx_len,CV_32S);//初始化 //遍历指定的样本数据,赋值 for(inti=0;i sample_idx->data.i[i]=_sample_idx->data.i[i]; }break; //8位有、无符号位的整型,8位样本数据存储在32位数据中,即每32位数据包括4个8位样本数据,以节省内存空间 caseCV_8S: caseCV_8U: { //变量active_samples_count表示8位样本数据需要多少个32位数据 intactive_samples_count=0; for(inti=0;i active_samples_count+=int(_sample_idx->data.ptr[i]); sample_idx=cvCreateMat(1,active_samples_count,CV_32S);//初始化 active_samples_count=0; //为sample_idx赋值,赋的不是真正的样本值,而是索引值 for(inti=0;i if(int(_sample_idx->data.ptr[i])) sample_idx->data.i[active_samples_count++]=i; }break; //其他数据类型报错 default: CV_Error(CV_StsUnmatchedFormats,"_sample_idxshouldbea32sC1,8sC1or8uC1vector."); } //按从小到大的顺序对样本数据进行排序存放,以便后续处理 icvSortFloat(sample_idx->data.fl,sample_idx_len,0); } else//全体样本数据都用于构建GBT模型 { sample_idx=cvCreateMat(1,n,CV_32S);//初始化 for(inti=0;i sample_idx->data.i[i]=i;//赋样本的索引值 } //初始化矩阵变量sum_response和sum_response_tmp sum_response=cvCreateMat(class_count,n,CV_32F); sum_response_tmp=cvCreateMat(class_count,n,CV_32F); cvZero(sum_response);//sum_response矩阵清零 delta=0.0f;//Huber损失函数的参数δ赋值为0 /* inthecaseofaregressionproblemtheinitialguess(thezeroterm inthesum)issettothemeanofallthetrainingresponses,thatis thebestconstantmodel */ //base_value表示F0(x) //如果是回归问题,通过调用find_optimal_value函数得到F0(x),find_optimal_value函数详见后面的介绍 if(is_regression)base_value=find_optimal_value(sample_idx); /* inthecaseofaclassificationproblemtheinitialguess(thezeroterm inthesum)issettozeroforallthetreessequences */ //如果是分类问题,F0(x)设置为0 elsebase_value=0.0f; /* currentpredicitiononalltrainingsamplesissettobe equaltothebase_value */ cvSet(sum_response,cvScalar(base_value));//使sum_response等于base_value //初始化弱学习器weak,如果是回归问题,class_count为1,即一个弱学习器就是一个决策树;如果是分类问题,class_count为类别的数量,即一个弱学习器是由class_count个决策树构成 weak=newpCvSeq[class_count]; //初始化弱学习器 for(inti=0;i { storage=cvCreateMemStorage(); weak[i]=cvCreateSeq(0,sizeof(CvSeq),sizeof(CvDTree*),storage); storage=0; } //subsampleparamsanddata rng=&cv: : theRNG();//实例化RNG类,用于得到随机数,以便随机选取训练样本数据 intsamples_count=get_len(sample_idx);//得到样本总数 //如果subsample_portion太接近0或太接近1,则subsample_portion重新赋值为1 params.subsample_portion=params.subsample_portion<=FLT_EPSILON|| 1-params.subsample_portion<=FLT_EPSILON ? 1: params.subsample_portion; //得到训练样本的数量 inttrain_sample_count=cvFloor(params.subsample_portion*samples_count); //train_sample_count为0,则样本总数就是训练样本数量 if(train_sample_count==0) train_sample_count=samples_count; inttest_sample_count=samples_count-train_sample_count;//得到测试样本数量 //开辟一个大小为samples_count内存空间,idx_data指向该空间的首地址,该空间的前train_sample_count个单位存放着训练样本所对应的全体样本的索引值,后test_sample_count个单位存放着测试样本所对应的全体样本的索引值 int*idx_data=newint[samples_count]; //初始化subsample_train subsample_train=cvCreateMatHeader(1,train_sample_count,CV_32SC1); *subsample_train=cvMat(1,train_sample_count,CV_32SC1,idx_data); //初始化subsample_test if(test_sample_count) { subsample_test=cvCreateMatHeader(1,test_sam
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Opencv249 源码 分析 GradientBoostedTrees 详解