欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

opencv3/C++ 机器学习-提升算法/Boosting

程序员文章站 2022-07-14 13:46:22
...

提升算法/Boosting

提升算法/Boosting是一种提高弱分类器准确度的方法。通常在分类问题中,boosting通过改变训练样本的权重,学习多个分类器,并将这些分类器线性组合,提高分类性能。
Kearns 和 Valiant分别在1988和1989年提出了“强可学习”和“弱可学习”的概念。1990年Robert Schapire在论文The Strength of Weak Learnabili中证明了一组弱分类器可以创建一个强大的分类器。 Boosting 就是通过组合许多弱分类器(只比随机选择好一点)来产生一个强大的分类器组。且弱分类器的设计简单没有太大计算量。
boosting算法有很多,如AdaBoost,LPBoost,TotalBoost,BrownBoost,xgboost,MadaBoost,LogitBoost等等。大多数boosting算法都是改变训练数据的权值分布,针对不同的训练数据分布调用弱学习算法学习一系列弱分类器。因此,许多boosting算法之间的差异在于对训练样本和假设进行加权的方法不同。众多boosting算法中最具代表性的是AdaBoost算法。

AdaBoost

AdaBoost算法中:
- 对于训练样本的权值:每一轮都增大前一轮弱分类器误判的样本的权值,并减小正确分类的样本的权值。如下图所示:(图片来自百度百科)
opencv3/C++ 机器学习-提升算法/Boosting
这样被误判的样本由于权值增大而受到后一轮弱分类器的更大关注。
- 对于弱分类器的权值:增大误差率小的分类器的权值,减小误差率大的分类器的权值。这样,通过加权多数表决的方法将弱分类器组合成一个强分类器。

OpenCV中介绍了离散AdaBoost和实数Adaboost算法步骤::

  1. 给定N个样本 (xi,yi),其中 xiRk,yi1,+1.
  2. 初始化权值 wi=1N,i=1,...,N.
  3. 重复  form=1,2,...,M:
    ① 根据每个训练数据的 wi计算 fm(x)1,+1
    ②计算 errm=Ew[1yfm(x)],cm=log((1errm)/errm)
    ③更新权值 wiwiexp[cm1yifm(xi)],i=1,2,...,N
  4. 输出分类器 sign(m=1Mcmfm(x))
    作为传统boosting算法,这两种算法只能解决二分类问题。对于多类的分类问题可以使用AdaBoost.MH算法将其简化为两类分类的问题,但需要较大的训练数据。

OpenCV Boost类

常用函数
  • virtual void setBoostType(int val);
    boosting算法的类型。
    DISCRETE :Discrete AdaBoost.
    REAL:Real AdaBoost
    LOGIT:LogitBoost
    GENTLE:Gentle AdaBoost
    通常选Gentle AdaBoost 或 Real AdaBoost;默认值是Boost :: REAL

  • virtual void setWeakCount(int val);
    弱分类器的数量。默认值是100。

  • virtual void setWeightTrimRate(double val);
    0和1之间的阈值,用于节省计算时间。总权重 1 - weight_trim_rate的样本不参与下一次迭代训练。参数设置为0可关闭此功能。 默认值为0.95。

  • virtual void setMaxDepth(int val);
    树的最大可能深度。训练算法在节点深度小于maxDepth的情况下分割节点。根节点具有零深度。如果符合其他终止标准或修剪树,则实际深度会更小。默认值为INT_MAX。

  • virtual void setUseSurrogates(bool val);
    若为true,则建立替代分裂点。 这些分裂点可以处理丢失的数据并正确计算变量的重要性。 默认值为false。

  • virtual void setPriors(const cv::Mat &val);
    先验类概率数组,按类标签值排序。默认值为空Mat。可用于将决策树偏好调整为某个类别。例如,如果检测一些罕见的异常情况,原训练过程可能包含比异常情况更多的正常情况,因此仅通过将每种情况视为正常就可以实现非常好的分类性能。为避免这种情况,可以指定先验值,人为地增大异常概率(0.5或甚至更大),因此错误分类的异常的权重变得更大,并且树被适当地调整。也可以将此参数视为预测类别的权重,这些预测类别决定了错误分类的相对权重。也就是说,若第一类别的权重为1,第二类别的权重为10,则预测第二类别的每个错误相当于预测第一类别时出现10个错误。

Boosting分类器示例

使用Boosting模型创建分类器,对OpenCV自带的数据letter-recognition.data进行处理。
letter-recognition.data内容:
opencv3/C++ 机器学习-提升算法/Boosting

示例:

#include "opencv2/core/core.hpp"
#include "opencv2/ml/ml.hpp"

#include <cstdio>
#include <vector>
#include <iostream>

using namespace std;
using namespace cv;
using namespace cv::ml;

// 从文件中读取data和responses 
int read_num_class_data( const string& filename, int var_count, Mat* _data, Mat* _responses )
{
    const int M = 1024;
    char buf[M+2];
    Mat el_ptr(1, var_count, CV_32F); 
    vector<int> responses;
    _data->release();
    _responses->release();
    //f指向存储数据的地址
    FILE* f = fopen( filename.c_str(), "rt" );
    if( !f )
    {
        cout << "Could not read the database " << filename << endl;
        return -1;
    }

    for(;;)
    {
        char* ptr;
        int i;
        //fgets从文件中读取一行数据存入缓冲区
        //strchr查找字符串buf中首次出现,的位置
        if( !fgets( buf, M, f ) || !strchr( buf, ',' ) )
            break;
        responses.push_back((int)buf[0]);
        ptr = buf+2;
        for( i = 0; i < var_count; i++ )
        {
            int n = 0;
            //读取格式化的字符串中的数据
            sscanf( ptr, "%f%n", &el_ptr.at<float>(i), &n );
            ptr += n + 1;
        }
        if( i < var_count )
            break;
        _data->push_back(el_ptr);
    }
    fclose(f);

    Mat(responses).copyTo(*_responses);
    cout << "The database " << filename << " is loaded.\n";
    return 0;
}

int main()
{
    string data_filename = "E:/image/image/data/letter-recognition.data";
    Mat data;
    Mat responses;
    read_num_class_data( data_filename, 16, &data, &responses );

    const int class_count = 26;
    Ptr<Boost> model;
    int nsamples_all = data.rows;
    int ntrain_samples = (int)(nsamples_all*0.5);
    int var_count = data.cols;
    // 创建Boosted Tree分类器
    // 目前在MLL中增强型树分类器只能训练二分类问题,因此我们通过“展开”每个训练样本的次数来改变训练数据库的次数,就像我们拥有的类数(26)一样。
    Mat new_data( ntrain_samples*class_count, var_count + 1, CV_32F );
    Mat new_responses( ntrain_samples*class_count, 1, CV_32S );
    // 1.展开数据库类型掩码
    printf( "Unrolling the database...\n");
    for( int i = 0; i < ntrain_samples; i++ )
    {
        const float* data_row = data.ptr<float>(i);
        for( int j = 0; j < class_count; j++ )
        {
            float* new_data_row = (float*)new_data.ptr<float>(i*class_count+j);
            memcpy(new_data_row, data_row, var_count*sizeof(data_row[0]));
            new_data_row[var_count] = (float)j;
            new_responses.at<int>(i*class_count + j) = responses.at<int>(i) == j+'A';
        }
    }
    Mat var_type( 1, var_count + 2, CV_8U );
    var_type.setTo(Scalar::all(VAR_ORDERED));
    var_type.at<uchar>(var_count) = var_type.at<uchar>(var_count+1) = VAR_CATEGORICAL;
    //创建训练数据
    Ptr<TrainData> tdata = TrainData::create(new_data, ROW_SAMPLE, new_responses, noArray(), noArray(), noArray(), var_type);
    vector<double> priors(2);
    priors[0] = 1;
    priors[1] = 26;
    cout << "Training the classifier...\n"<< endl;
    //创建模型
    model = Boost::create();
    //算法类型
    model->setBoostType(Boost::GENTLE);
    //弱分类器的数量
    model->setWeakCount(100);
    //0和1之间的阈值
    model->setWeightTrimRate(0.95);
    //树的最大可能深度
    model->setMaxDepth(5);
    //是否建立替代分裂点
    model->setUseSurrogates(false);
    //先验类概率数组
    model->setPriors(Mat(priors));
    //训练模型
    model->train(tdata);
    ////将分类器保存到文件中
    //model->save( "filename_to_save.xml" );

    // 计算训练和测试数据的预测误差
    Mat temp_sample( 1, var_count + 1, CV_32F );
    float* tptr = temp_sample.ptr<float>();   
    double train_hr = 0, test_hr = 0;
    for( int i = 0; i < nsamples_all; i++ )
    {
        int best_class = 0;
        double max_sum = -DBL_MAX;
        const float* ptr = data.ptr<float>(i);
        for( int k = 0; k < var_count; k++ )
            tptr[k] = ptr[k];
        for( int j = 0; j < class_count; j++ )
        {
            tptr[var_count] = (float)j;
            float s = model->predict( temp_sample, noArray(), StatModel::RAW_OUTPUT );
            if( max_sum < s )
            {
                max_sum = s;
                best_class = j + 'A';
            }
        }
        //判断预测是否正确(绝对值小于最小值FLT_EPSILON)
        double r = std::abs(best_class - responses.at<int>(i)) < FLT_EPSILON ? 1 : 0;
        if( i < ntrain_samples )
            train_hr += r;
        else
            test_hr += r;
    }
    test_hr /= nsamples_all-ntrain_samples;
    train_hr = ntrain_samples > 0 ? train_hr/ntrain_samples : 1.;
    printf( "Recognition rate: train = %.1f%%, test = %.1f%%\n", train_hr*100., test_hr*100. );
    cout << "Number of trees: " << model->getRoots().size() << endl;

    return 0;
}

opencv3/C++ 机器学习-提升算法/Boosting


Wikipedia:Boosting (machine learning)
OpenCV3.1.0-Machine Learning Overview-Boosting

相关标签: opencv boosting