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:

(1)从当前树的根节点开始,根据当前节点的阈值th,判断是进入左节点(<th)还是进入右节点(>=th),直到到达,某个叶子节点,并输出预测值。

(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(int max_depth, int min_sample_count, float regression_accuracy, bool use_surrogates, int max_categories, const float* priors, bool calc_var_importance, int nactive_vars, int max_num_of_trees_in_the_forest, float forest_accuracy, int termcrit_type)

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

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

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

float regression_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>

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

/******************************************************************************/
// global definitions (for speed and ease of use)
//手写体数字识别

#define NUMBER_OF_TRAINING_SAMPLES 60000
#define ATTRIBUTES_PER_SAMPLE 784
#define NUMBER_OF_TESTING_SAMPLES 10000

#define NUMBER_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)

inline void revertInt(int&x)
{
    x=((x&0x000000ff)<<24)|((x&0x0000ff00)<<8)|((x&0x00ff0000)>>8)|((x&0xff000000)>>24);
};

int read_data_from_csv(const char* samplePath,const char* labelPath, Mat data, Mat classes,
                       int n_samples )
{
    FILE* sampleFile=fopen(samplePath,"rb");
    FILE* labelFile=fopen(labelPath,"rb");
    int mbs=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);
    unsigned char temp;
    for(int line = 0; line < n_samples; line++)
    {
        // for each attribute on the line in the file
        for(int attribute = 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));
            }
            else if (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);
    return 1; // all OK
}

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

int main( int argc, char** argv )
{

    for (int i=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;

    double result; // 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的参数******************************/
        float priors[] = {1,1,1,1,1,1,1,1,1,1};  // weights of each classification for classes
        CvRTParams params = 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 = new CvRTrees;
        bool train_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;
        int correct_class = 0;
        int wrong_class = 0;
        int false_positives [NUMBER_OF_CLASSES] = {0,0,0,0,0,0,0,0,0,0};

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

        for (int tsample = 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 (int i = 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
        return 0;
    }

    // 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的保存方式同理,但是个人不建议用链表的方式,在程序设计以及函数处理上太麻烦,但是在省空间上并没有太多的体现。

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

RandomForest随机森林总结

时间: 2024-12-23 17:34:36

RandomForest随机森林总结的相关文章

R包 randomForest 进行随机森林分析

randomForest 包提供了利用随机森林算法解决分类和回归问题的功能:我们这里只关注随机森林算法在分类问题中的应用 首先安装这个R包 install.packages("randomForest") 安装成功后,首先运行一下example library(randomForset) ?randomForset 通过查看函数的帮助文档,可以看到对应的example data(iris) set.seed(71) iris.rf <- randomForest(Species

tensorflow基础模型之RandomForest(随机森林)算法

随机森林算法原理请参照上篇:随机森林.数据依旧为MNIST数据集. 代码如下: from __future__ import print_function?# Ignore all GPUs, tf random forest does not benefit from it.import os?import tensorflow as tffrom tensorflow.contrib.tensor_forest.python import tensor_forestfrom tensorfl

随机森林

现在现将随机森林的学习的大纲进行列举: 1.决策树的算法: ID3,C4.5,CART,bagging,GBDT,RandomForest. 2.RandomForest的原理: 在m个样本中,有放回的随机抽取m个样本,作为训练集.将在n个特征中抽取k(k<n)个特征来构建决策树,通过构建T棵决策树组成随机森林.随机森林主要是建立在决策树的基础上的,通过一个随机的过程是不需要预剪枝和后剪枝的. 3.下载的代码.githup网址 4.算法流程图: 5.调优和集成: 后续有时间将其内容进行补充完整.

mllib之随机森林与梯度提升树

随机森林和GBTs都是集成学习算法,它们通过集成多棵决策树来实现强分类器. 集成学习方法就是基于其他的机器学习算法,并把它们有效的组合起来的一种机器学习算法.组合产生的算法相比其中任何一种算法模型更强大.准确. 随机森林和梯度提升树(GBTs).两者之间主要差别在于每棵树训练的顺序. 随机森林通过对数据随机采样来单独训练每一棵树.这种随机性也使得模型相对于单决策树更健壮,且不易在训练集上产生过拟合. GBTs则一次只训练一棵树,后面每一棵新的决策树逐步矫正前面决策树产生的误差.随着树的添加,模型

第九篇:随机森林(Random Forest)

前言 随机森林非常像<机器学习实践>里面提到过的那个AdaBoost算法,但区别在于它没有迭代,还有就是森林里的树长度不限制. 因为它是没有迭代过程的,不像AdaBoost那样需要迭代,不断更新每个样本以及子分类器的权重.因此模型相对简单点,不容易出现过拟合. 下面先来讲讲它的具体框架流程. 框架流程 随机森林可以理解为Cart树森林,它是由多个Cart树分类器构成的集成学习模式.其中每个Cart树可以理解为一个议员,它从样本集里面随机有放回的抽取一部分进行训练,这样,多个树分类器就构成了一个

【转载】Python 实现的随机森林http://lidandan1314.diandian.com/

Python 实现的随机森林 随机森林是一个高度灵活的机器学习方法,拥有广泛的应用前景,从市场营销到医疗保健保险. 既可以用来做市场营销模拟的建模,统计客户来源,保留和流失.也可用来预测疾病的风险和病患者的易感性. 随机森林是一个可做能够回归和分类. 它具备处理大数据的特性,而且它有助于估计或变量是非常重要的基础数据建模. 这是一篇关于使用Python来实现随机森林文章. 什么是随机森林? 随机森林是几乎任何预测问题(甚至非直线部分)的固有选择.它是一个相对较新的机器学习的策略(在90 年代产生

paper 56 :机器学习中的算法:决策树模型组合之随机森林(Random Forest)

周五的组会如约而至,讨论了一个比较感兴趣的话题,就是使用SVM和随机森林来训练图像,这样的目的就是 在图像特征之间建立内在的联系,这个model的训练,着实需要好好的研究一下,下面是我们需要准备的入门资料: [关于决策树的基础知识参考:http://blog.csdn.net/holybin/article/details/22914417] 在机器学习中,随机森林由许多的决策树组成,因为这些决策树的形成采用了随机的方法,所以叫做随机森林.随机森林中的决策树之间是没有关联的,当测试数据进入随机森

随机森林入门攻略(内含R、Python代码)

随机森林入门攻略(内含R.Python代码) 简介 近年来,随机森林模型在界内的关注度与受欢迎程度有着显著的提升,这多半归功于它可以快速地被应用到几乎任何的数据科学问题中去,从而使人们能够高效快捷地获得第一组基准测试结果.在各种各样的问题中,随机森林一次又一次地展示出令人难以置信的强大,而与此同时它又是如此的方便实用. 需要大家注意的是,在上文中特别提到的是第一组测试结果,而非所有的结果,这是因为随机森林方法固然也有自己的局限性.在这篇文章中,我们将向你介绍运用随机森林构建预测模型时最令人感兴趣

R语言︱决策树族——随机森林算法

笔者寄语:有一篇<有监督学习选择深度学习还是随机森林或支持向量机?>(作者Bio:SebastianRaschka)中提到,在日常机器学习工作或学习中,当我们遇到有监督学习相关问题时,不妨考虑下先用简单的假设空间(简单模型集合),例如线性模型逻辑回归.若效果不好,也即并没达到你的预期或评判效果基准时,再进行下换其他更复杂模型来实验. ---------------------------------------------- 一.随机森林理论介绍 1.1 优缺点 优点. (1)不必担心过度拟合