RandomForest随机森林总结

RandomForest随机森林总结

1.随机森林原理介绍

随机森林,指的是利用多棵树对样本进行训练并预测的一种分类器。该分类器最早由Leo Breiman和Adele Cutler提出,并被注册成了商标。简单来说,随机森林就是由多棵CART(Classification And Regression Tree)构成的。对于每棵树,它们使用的训练集是从总的训练集中有放回采样出来的,这意味着,总的训练集中的有些样本可能多次出现在一棵树的训练集中,也可能从未出现在一棵树的训练集中。在训练每棵树的节点时,使用的特征是从所有特征中按照一定比例随机地无放回的抽取的,根据Leo Breiman的建议,假设总的特征数量为M,这个比例可以是sqrt(M),1/2sqrt(M),2sqrt(M)。

因此,随机森林的训练过程可以总结如下:

(1)给定训练集S,测试集T,特征维数F。确定参数:使用到的CART的数量t,每棵树的深度d,每个节点使用到的特征数量f,终止条件:节点上最少样本数s,节点上最少的信息增益m

对于第1-t棵树,i=1-t:

(2)从S中有放回的抽取大小和S一样的训练集S(i),作为根节点的样本,从根节点开始训练

(3)如果当前节点上达到终止条件,则设置当前节点为叶子节点,如果是分类问题,该叶子节点的预测输出为当前节点样本集合中数量最多的那一类c(j),概率p为c(j)占当前样本集的比例;如果是回归问题,预测输出为当前节点样本集各个样本值的平均值。然后继续训练其他节点。如果当前节点没有达到终止条件,则从F维特征中无放回的随机选取f维特征。利用这f维特征,寻找分类效果最好的一维特征k及其阈值th,当前节点上样本第k维特征小于th的样本被划分到左节点,其余的被划分到右节点。继续训练其他节点。有关分类效果的评判标准在后面会讲。

(4)重复(2)(3)直到所有节点都训练过了或者被标记为叶子节点。

(5)重复(2),(3),(4)直到所有CART都被训练过。

利用随机森林的预测过程如下:

对于第1-t棵树,i=1-t:

(2)重复执行(1)直到所有t棵树都输出了预测值。如果是分类问题,则输出为所有树中预测概率总和最大的那一个类,即对每个c(j)的p进行累计;如果是回归问题,则输出为所有树的输出的平均值。

注:有关分类效果的评判标准,因为使用的是CART,因此使用的也是CART的平板标准,和C3.0,C4.5都不相同。

对于分类问题(将某个样本划分到某一类),也就是离散变量问题,CART使用Gini值作为评判标准。定义为Gini=1-∑(P(i)*P(i)),P(i)为当前节点上数据集中第i类样本的比例。例如:分为2类,当前节点上有100个样本,属于第一类的样本有70个,属于第二类的样本有30个,则Gini=1-0.7×07-0.3×03=0.42,可以看出,类别分布越平均,Gini值越大,类分布越不均匀,Gini值越小。在寻找最佳的分类特征和阈值时,评判标准为:argmax(Gini-GiniLeft-GiniRight),即寻找最佳的特征f和阈值th,使得当前节点的Gini值减去左子节点的Gini和右子节点的Gini值最大。

对于回归问题,相对更加简单,直接使用argmax(Var-VarLeft-VarRight)作为评判标准,即当前节点训练集的方差Var减去减去左子节点的方差VarLeft和右子节点的方差VarRight值最大。

2.OpenCV函数使用

OpenCV提供了随机森林的相关类和函数。具体使用方法如下:

(1)首先利用CvRTParams定义自己的参数,其格式如下

CvRTParams::CvRTParams(intmax_depth,intmin_sample_count,floatregression_accuracy,booluse_surrogates,intmax_categories,constfloat* priors,boolcalc_var_importance,intnactive_vars,intmax_num_of_trees_in_the_forest,floatforest_accuracy,inttermcrit_type)

大部分参数描述都在http://docs.opencv.org/modules/ml/doc/random_trees.html上面有,说一下没有描述的几个参数的意义

booluse_surrogates:是否使用代理,指的是,如果当前的测试样本缺少某些特征,但是在当前节点上的分类or回归特征正是缺少的这个特征,那么这个样本就没法继续沿着树向下走了,达不到叶子节点的话,就没有预测输出,这种情况下,可以利用当前节点下面的所有子节点中的叶子节点预测输出的平均值,作为这个样本的预测输出。

const float*priors:先验知识,这个指的是,可以根据各个类别样本数量的先验分布,对其进行加权。比如:如果一共有3类,第一类样本占整个训练集的80%,其余两类各占10%,那么这个数据集里面的数据就很不平均,如果每类的样本都加权的话,就算把所有样本都预测成第一类,那么准确率也有80%,这显然是不合理的,因此我们需要提高后两类的权重,使得后两类的分类正确率也不会太低。

floatregression_accuracy:回归树的终止条件,如果当前节点上所有样本的真实值和预测值之间的差小于这个数值时,停止生产这个节点,并将其作为叶子节点。

后来发现这些参数在决策树里面有解释,英文说明在这里http://docs.opencv.org/modules/ml/doc/decision_trees.html#cvdtreeparams

具体例子如下,网上找了个别人的例子,自己改成了可以读取MNIST数据并且做分类的形式,如下:


#include <cv.h>//opencv general include file

#include <ml.h>//opencv machine learning include file

#include <stdio.h>

usingnamespacecv;//OpenCV API is in the C++ "cv" namespace

/******************************************************************************/

//global definitions (for speed and ease of use)

//手写体数字识别

#defineNUMBER_OF_TRAINING_SAMPLES 60000

#defineATTRIBUTES_PER_SAMPLE 784

#defineNUMBER_OF_TESTING_SAMPLES 10000

#defineNUMBER_OF_CLASSES 10

//N.B. classes are integer handwritten digits in range 0-9

/******************************************************************************/

//loads the sample database from file (which is a CSV text file)

inlinevoidrevertInt(int&x)

{

x=((x&0x000000ff)<<24)|((x&0x0000ff00)<<8)|((x&0x00ff0000)>>8)|((x&0xff000000)>>24);

};

intread_data_from_csv(constchar* samplePath,constchar*labelPath, Mat data, Mat classes,

intn_samples )

{

FILE* sampleFile=fopen(samplePath,"rb");

FILE* labelFile=fopen(labelPath,"rb");

intmbs=0,number=0,col=0,row=0;

fread(&mbs,4,1,sampleFile);

fread(&number,4,1,sampleFile);

fread(&row,4,1,sampleFile);

fread(&col,4,1,sampleFile);

revertInt(mbs);

revertInt(number);

revertInt(row);

revertInt(col);

fread(&mbs,4,1,labelFile);

fread(&number,4,1,labelFile);

revertInt(mbs);

revertInt(number);

unsignedchartemp;

for(intline =0; line < n_samples; line++)

{

//for each attribute on the line in the file

for(intattribute =0; attribute < (ATTRIBUTES_PER_SAMPLE +1); attribute++)

{

if(attribute <ATTRIBUTES_PER_SAMPLE)

{

//first 64 elements (0-63) in each line are the attributes

fread(&temp,1,1,sampleFile);

//fscanf(f, "%f,", &tmp);

data.at<float>(line, attribute) = static_cast<float>(temp);

//printf("%f,", data.at<float>(line, attribute));

}

elseif(attribute ==ATTRIBUTES_PER_SAMPLE)

{

//attribute 65 is the class label {0 ... 9}

fread(&temp,1,1,labelFile);

//fscanf(f, "%f,", &tmp);

classes.at<float>(line,0) = static_cast<float>(temp);

//printf("%f\n", classes.at<float>(line, 0));

}

}

}

fclose(sampleFile);

fclose(labelFile);

return1;//all OK

}

/******************************************************************************/

intmain(intargc,char**argv )

{

for(inti=0; i< argc; i++)

std::cout<<argv[i]<<std::endl;

//lets just check the version first

printf ("OpenCV version %s (%d.%d.%d)\n",

CV_VERSION,

CV_MAJOR_VERSION, CV_MINOR_VERSION, CV_SUBMINOR_VERSION);

//定义训练数据与标签矩阵

Mat training_data =Mat(NUMBER_OF_TRAINING_SAMPLES, ATTRIBUTES_PER_SAMPLE, CV_32FC1);

Mat training_classifications= Mat(NUMBER_OF_TRAINING_SAMPLES,1, CV_32FC1);

//定义测试数据矩阵与标签

Mat testing_data =Mat(NUMBER_OF_TESTING_SAMPLES, ATTRIBUTES_PER_SAMPLE, CV_32FC1);

Mat testing_classifications= Mat(NUMBER_OF_TESTING_SAMPLES,1, CV_32FC1);

//define all the attributes as numerical

//alternatives are CV_VAR_CATEGORICAL or CV_VAR_ORDERED(=CV_VAR_NUMERICAL)

//that can be assigned on a per attribute basis

Mat var_type= Mat(ATTRIBUTES_PER_SAMPLE +1,1, CV_8U );

var_type.setTo(Scalar(CV_VAR_NUMERICAL) );//all inputs are numerical

//this is a classification problem (i.e. predict a discrete number of class

//outputs) so reset the last (+1) output var_type element to CV_VAR_CATEGORICAL

var_type.at<uchar>(ATTRIBUTES_PER_SAMPLE,0) =CV_VAR_CATEGORICAL;

doubleresult;//value returned from a prediction

//加载训练数据集和测试数据集

if(read_data_from_csv(argv[1],argv[2], training_data, training_classifications, NUMBER_OF_TRAINING_SAMPLES) &&

read_data_from_csv(argv[3],argv[4], testing_data, testing_classifications, NUMBER_OF_TESTING_SAMPLES))

{

/********************************步骤1:定义初始化Random Trees的参数******************************/

floatpriors[] = {1,1,1,1,1,1,1,1,1,1};//weights of each classification for classes

CvRTParamsparams= CvRTParams(20,//max depth

50,//min sample count

0,//regression accuracy: N/A here

false,//compute surrogate split, no missing data

15,//max number of categories (use sub-optimal algorithm for larger numbers)

priors,//the array of priors

false,//calculate variable importance

50,//number of variables randomly selected at node and used to find the best split(s).

100,//max number of trees in the forest

0.01f,//forest accuracy

CV_TERMCRIT_ITER |    CV_TERMCRIT_EPS//termination cirteria

);

/****************************步骤2:训练 Random Decision Forest(RDF)分类器*********************/

printf("\nUsing training database: %s\n\n", argv[1]);

CvRTrees* rtree =newCvRTrees;

booltrain_result=rtree->train(training_data, CV_ROW_SAMPLE, training_classifications,

Mat(), Mat(), var_type, Mat(),params);

//float train_error=rtree->get_train_error();

//printf("train error:%f\n",train_error);

//perform classifier testing and report results

Mat test_sample;

intcorrect_class =0;

intwrong_class =0;

intfalse_positives [NUMBER_OF_CLASSES] = {0,0,0,0,0,0,0,0,0,0};

printf("\nUsing testing database: %s\n\n", argv[2]);

for(inttsample =0; tsample < NUMBER_OF_TESTING_SAMPLES; tsample++)

{

//extract a row from the testing matrix

test_sample =testing_data.row(tsample);

/********************************步骤3:预测*********************************************/

result= rtree->predict(test_sample, Mat());

printf("Testing Sample %i -> class result (digit %d)\n", tsample, (int) result);

//if the prediction and the (true) testing classification are the same

//(N.B. openCV uses a floating point decision tree implementation!)

if(fabs(result - testing_classifications.at<float>(tsample,0))

>=FLT_EPSILON)

{

//if they differ more than floating point error => wrong class

wrong_class++;

false_positives[(int) result]++;

}

else

{

//otherwise correct

correct_class++;

}

}

printf("\nResults on the testing database: %s\n"

"\tCorrect classification: %d (%g%%)\n"

"\tWrong classifications: %d (%g%%)\n",

argv[2],

correct_class, (double) correct_class*100/NUMBER_OF_TESTING_SAMPLES,

wrong_class, (double) wrong_class*100/NUMBER_OF_TESTING_SAMPLES);

for(inti =0; i < NUMBER_OF_CLASSES; i++)

{

printf("\tClass (digit %d) false postives    %d (%g%%)\n", i,

false_positives[i],

(double) false_positives[i]*100/NUMBER_OF_TESTING_SAMPLES);

}

//all matrix memory free by destructors

//all OK : main returns 0

return0;

}

//not OK : main returns -1

return-1;

}

MNIST样本可以在这个网址http://yann.lecun.com/exdb/mnist/下载,改一下路径可以直接跑的。

3.如何自己设计随机森林程序

有时现有的库无法满足要求,就需要自己设计一个分类器算法,这部分讲一下如何设计自己的随机森林分类器,代码实现就不贴了,因为在工作中用到了,因此比较敏感。

首先,要有一个RandomForest类,里面保存整个树需要的一些参数,包括但不限于:训练样本数量、测试样本数量、特征维数、每个节点随机提取的特征维数、CART树的数量、树的最大深度、类别数量(如果是分类问题)、一些终止条件、指向所有树的指针,指向训练集和测试集的指针,指向训练集label的指针等。还要有一些函数,至少要有train和predict吧。train里面直接调用每棵树的train方法即可,predict同理,但要对每棵树的预测输出做处理,得到森林的预测输出。

其次,要有一个sample类,这个类可不是用来存储训练集和对应label的,这是因为,每棵树、每个节点都有自己的样本集和,如果你的存储每个样本集和的话,需要的内存实在是太过巨大了,假设样本数量为M,特征维数为N,则整个训练集大小为M×N,而每棵树的每层都有这么多样本,树的深度为D,共有S棵树的话,则需要存储M×N×D×S的存储空间。这实在是太大了。因此,每个节点训练时用到的训练样本和特征,我们都用序号数组来代替,sample类就是干这个的。sample的函数基本需要两个就行,第一个是从现有训练集有放回的随机抽取一个新的训练集,当然,只包含样本的序号。第二个函数是从现有的特征中无放回的随机抽取一定数量的特征,同理,也是特征序号即可。

然后,需要一个Tree类,代表每棵树,里面保存树的一些参数以及一个指向所有节点的指针。

最后,需要一个Node类,代表树的每个节点。

需要说明的是,保存树的方式可以是最普通的数组,也可是是vector。Node的保存方式同理,但是个人不建议用链表的方式,在程序设计以及函数处理上太麻烦,但是在省空间上并没有太多的体现。

目前先写这么多,最后这部分我还会再扩充一些。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容