1、Opencv249源码分析Decision TreesOpencv2.4.9源码分析Decision Trees因此求式18的最大值问题就转变为求式19的最大值问题。我们按照样本响应值是类的形式,还是数值的形式,把决策树分成了分类树和回归树,它们对应不同的计算公式。那么表示特征属性的形式也会有这两种形式,即类的形式和数值的形式,比如决定是否出去踢球,取决于两个条件:风力和气温。风力的表示形式是:无风、小风、中风、大风,气温的表示形式就是具体的摄氏度,如-1040之间。风力这个特征属性就是类的形式,而气温就是数值的形式。又比如决定发放房屋贷款,其金额取决于两个条件:是否有车有房和年薪。有车有房的
2、表示形式是:无车无房、有车无房、无车有房、有车有房,而年薪的表示形式就是具体的钱数,如020万。有车有房这个特征属性就是类的形式,年薪就是数值的形式。因此在分析样本的特征属性时,我们要把决策树分为四种情况:特征为类的分类树(如决定是否踢球的风力)、特征为数值的分类树(如决定是否踢球的温度)、特征为类的回归树(如发放贷款的有车有房)和特征为数值的回归树(如发放贷款的年薪)。由于特征形式不同,所以计算方法上有所不同:、特征为类的分类树:对于两类问题,即样本的分类(响应值)只有两种情况:响应值为0和1,按照特征属性的类别的样本响应值为1的数量的多少进行排序。例如我们采集20个样本来构建是否踢球分类树
3、,设出去踢球的响应值为1,不踢球的响应值为0,针对风力这个特征属性,响应值为1的样本有14个,无风有6个样本,小风有5个,中风2个,大风1个,则排序的结果为:大风中风小风 1),则还要随机的把样本均等分cv_folds个子集。最后一个参数_share赋值为false,表示重新构建一个决策树 data = new CvDTreeTrainData( _train_data, _tflag, _responses, _var_idx, _sample_idx, _var_type, _missing_mask, _params, false ); /调用do_train函数,对全体样本数据进行训练
4、 CV_CALL( result = do_train(0) ); _END_; return result; 下面的train函数与上面的train函数不同的地方就是矩阵的类型不同,一个是Mat,另一个是CvMat。cpp view plain copy 在CODE上查看代码片派生到我的代码片bool CvDTree:train( const Mat& _train_data, int _tflag, const Mat& _responses, const Mat& _var_idx, const Mat& _sample_idx, const Mat& _var_type, const
5、Mat& _missing_mask, CvDTreeParams _params ) CvMat tdata = _train_data, responses = _responses, vidx=_var_idx, sidx=_sample_idx, vtype=_var_type, mmask=_missing_mask; return train(&tdata, _tflag, &responses, vidx.data.ptr ? &vidx : 0, sidx.data.ptr ? &sidx : 0, vtype.data.ptr ? &vtype : 0, mmask.data
6、.ptr ? &mmask : 0, _params); 在该train函数中,用于决策树的样本数据的一些参数保存在了_data中:cpp view plain copy 在CODE上查看代码片派生到我的代码片bool CvDTree:train( CvMLData* _data, CvDTreeParams _params ) bool result = false; CV_FUNCNAME( CvDTree:train ); _BEGIN_; /提取出训练样本数据的一些参数 const CvMat* values = _data-get_values(); const CvMat* res
7、ponse = _data-get_responses(); const CvMat* missing = _data-get_missing(); const CvMat* var_types = _data-get_var_types(); const CvMat* train_sidx = _data-get_train_sample_idx(); const CvMat* var_idx = _data-get_var_idx(); CV_CALL( result = train( values, CV_ROW_SAMPLE, response, var_idx, train_sidx
8、, var_types, missing, _params ) ); _END_; return result; 该train函数多了一个_subsample_idx参数cpp view plain copy 在CODE上查看代码片派生到我的代码片bool CvDTree:train( CvDTreeTrainData* _data, const CvMat* _subsample_idx ) bool result = false; CV_FUNCNAME( CvDTree:train ); _BEGIN_; clear(); data = _data; data-shared = true
9、; /表示共享决策树 /调用do_train函数,只对_subsample_idx指定的由_sample_idx参数确定的训练样本集的索引进行训练 CV_CALL( result = do_train(_subsample_idx); _END_; return result; do_train函数,参数_subsample_idx表示训练样本集合的索引cpp view plain copy 在CODE上查看代码片派生到我的代码片bool CvDTree:do_train( const CvMat* _subsample_idx ) bool result = false; CV_FUNCNA
10、ME( CvDTree:do_train ); _BEGIN_; /提取出_subsample_idx指定的训练样本集合,如果_subsample_idx=0,则取所有样本数据 /root表示根节点 root = data-subsample_data( _subsample_idx ); CV_CALL( try_split_node(root); /递归调用try_split_node函数,构造决策树 if( root-split ) /如果得到了决策树 /分别确保左分支和右分支的正确性 CV_Assert( root-left ); CV_Assert( root-right ); /如
11、果用户所设置的交叉验证的子集大于1,则表示需要对决策树进行剪枝。在前面已经提到,在实例化CvDTreeTrainData时,会把cv_folds = 1重新设置为cv_folds = 0 if( data-params.cv_folds 0 ) CV_CALL( prune_cv() ); /剪枝 /如果决策树的数据不需要共享,则清空训练所需的数据 if( !data-shared ) data-free_train_data(); result = true; /正确返回标识 _END_; return result; /返回 try_split_node函数的作用是在真正分叉之前,先预处理
12、一下节点,试探一下该节点是否能够被分叉,能则调用split_node_data函数开始分叉,不能则退出,然后对得到的左分支和右分支再分别递归。cpp view plain copy 在CODE上查看代码片派生到我的代码片void CvDTree:try_split_node( CvDTreeNode* node ) CvDTreeSplit* best_split = 0; /n表示该节点的样本数,vi表示特征属性 int i, n = node-sample_count, vi; bool can_split = true; /标识该节点是否能够被分叉 double quality_scale; / calc_node_value函数见后面的分析 calc_node_value( node ); /如果该节点的样本数量小于所设定的阈值,或者目前决策树的深度超过了所设定的阈值,则该节点不能再被分叉 if( node-sample_coun
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1