Opencv学习笔记 - 使用opencvsharp和Boosting算法处理分类问题
决策树非常有用,但单独使用时它并不是表现最佳的分类器。改进的方法随机森林和Boosting算法。随机森林与Boosting算法都是在内部循环中使用决策树的,因此继承了决策树的许多优良属性,它们通常是机器学习库中最佳的“开箱即用”监督学习技术。Boosting算法又被称为OpenCV机器学习库中最好用的监督学习算法。
一、Boosting算法原理
1、Boosting算法的基本思想
随机森林采用的是Bagging算法。Bagging算法的特点是对多个弱学习器进行独立学习,之后取平均,而Boosting算法是对多个弱学习器依次进行学习。其基本思想是:在逐个训练弱分类器时,每次迭代都会调整样本的权重,改变权重的依据是该样本被上一个弱分类器成功分类的难度;给被错误分类的样本加上较大的权重,让接下来的弱学习器更多地关注这些困难样本,尽量避免将其再分错;最后,通过线性组合的方式集成所有弱学习器,构成强学习器。
用于集成的弱学习器都非常简单,通常是只有一个节点的决策树,又称为决策树桩(Decision Stumps),或是只有很少节点的决策树。弱学习器是使用加权样本训练而成的,最后又通过加权“投票”的方式构成强学习器。因此,Boosting算法的关键是样本权重和弱分类器的“投票”权重如何分配。
2、Boosting算法的学习流程
Boosting算法使用加权的原始样本集依次训练k个弱学习器,Boosting算法使用加权的原始样本集依次训练k个弱学习器。
在上述学习过程中,一开始就能够被正确分类的样本的权重会慢慢变小,此时有可能造成简单的样本反而不能被正确分类的情况。因此,Boosting算法不仅边学习边更新样本的权重,还会把学习过程中得到的所有弱分类器结合在一起,对其加权求和后得到最终的强分类器。
3、AdaBoost算法
Boosting算法更新样本权重的方法有很多,其中最早提出的是AdaBoost(Adaptive Boosting)算法(自适应增强算法),大多数Boosting算法都是在AdaBoost算法基础上改进的。AdaBoost算法流程如下所示:
二、OpenCV函数实现
AdaBoost算法一次学习一组分类器,组中的每个分类器都是一个“弱”分类器(仅略高于随机猜测的表现)。这些弱分类器通常由决策树桩组成。在训练过程中,决策树桩从数据中学习分类决策,并从分类决策对数据的准确性中学习“投票”的权重。在每次训练分类器之前,都要对数据先进行加权,以便更多地关注上一轮中分类错误的数据。此过程将迭代进行,直到最终的强分类器对数据集的总分类误差降至设定的阈值以下,或者达到设置的迭代次数为止。当有大量训练数据可用时,此算法通常很有效。
OpenCV实现了下表中列出的四种Boosting算法。
其中,Real AdaBoost算法和Gentle AdaBoost算法的效果最好。Real AdaBoost算法是一种利用置信度评估的预测技术,在分类问题中有很好的效果;GentleAdaBoost算法赋予异常数据较小的权重,因此在解决回归问题时效果较好。
尽管从理论上讲,LogitBoost算法和Gentle AdaBoost算法都可用于回归任务和二分类任务,但是在OpenCV实现的Boosting算法中,目前还只支持用分类数据进行训练。原始的Boosting算法只能用在二分类任务中,对于多分类任务则需要使用一些技巧才能完成。
创建AdaBoost模型
cv::ml::Boost::create
以下是创建AdaBoost模型时用到的主要函数
(1)获取AdaBoost算法的类型,用于获取Boost算法的类型,默认值为Boost::REAL。返回int值:DISCRETE = 0,REAL =1, LOGIT = 2, GENTLE = 3。
cv::ml::Boost::getBoostType()
(2)设置AdaBoost算法的类型,上表
cv::ml::Boost::setBoostType(int val)
(3)获取弱分类器的数量
int getWeakCount()
(4)设置弱分类器的数量
void cv::ml::Boost::setWeakCount(int val)
(6)设置权重修剪率,通过setWeightTrimRate函数设置一个介于0和1(含)之间的阈值,该阈值可隐式地丢弃Boosting算法迭代过程中一些不重要的训练样本。
void setWeightTrimRate(double val)
三、二分类任务 - Mushroom数据集
mushroom数据集下载地址和介绍请看下文第3节
Opencv学习笔记 - 使用opencvsharp和随机森林进行分类和回归问题_bashendixie5的博客-CSDN博客随机森林(Random Forest,RF)是一种简单易用的机器学习算法。即使在没有超参数调整的情况下,随机森林在大多数情况下仍可获得还算不错的结果。可用于分类任务和回归任务,是常用的机器学习算法之一。随机森林是一种监督学习算法,它构建的“森林”是决策树的集合,通常使用Bagging算法进行集成。随机森林首先使用训练出来的分类器集合对新样本进行分类,然后用多数投票或者对输出求均值的方法统计所有决策树的结果。由于森林中的每一棵决策树都具有...https://blog.csdn.net/bashendixie5/article/details/121805198
1、c++代码参考
void opencv_adaboost_mushroom()
{//读取数据const char* file_name = "D:/Project/deeplearn/dataset/mushroom/agaricus-lepiota.data";cv::Ptr<TrainData> daraset_forest = TrainData::loadFromCSV(file_name,0, //从数据文件开头跳过的行数0, //样本的标签从此列开始(就是说第一列是标签)1, //样本输入特征向量从此列开始(从第二列开始往后都是数据)"cat[0-22]");//验证数据int n_samples = daraset_forest->getNSamples();int n_features = daraset_forest->getNVars();cout << "每个样本有" << n_features << "个特征" << endl;if (n_samples == 0){cout << "读取文件错误" << file_name << endl;exit(-1);}else{cout << "从" << file_name << "中,读取了" << n_samples << "个样本" << endl;}//划分训练集与测试集,按80%和20%比例划分daraset_forest->setTrainTestSplitRatio(0.8, false);int n_train_samples = daraset_forest->getNTrainSamples();int n_test_samples = daraset_forest->getNTestSamples();cout << "Training samples:" << n_train_samples << endl << "Test samples:" << n_test_samples << endl;//创建模型Ptr<Boost> boost = Boost::create();boost->setBoostType(Boost::GENTLE);boost->setWeakCount(100);boost->setWeightTrimRate(0.95);boost->setMaxDepth(5);boost->setUseSurrogates(false);//训练模型//训练随机森林模型cout << "开始训练..." << endl;boost->train(daraset_forest);cout << "训练成功..." << endl;//测试cv::Mat results_train, results_test;float forest_train_error = boost->calcError(daraset_forest, false, results_train);float forest_test_error = boost->calcError(daraset_forest, true, results_test);//统计输出结果int t = 0, f = 0, total = 0;cv::Mat expected_responses_forest = daraset_forest->getTestResponses();//获取测试集标签std::vector<cv::String> names_forest;daraset_forest->getNames(names_forest);for (int i = 0; i < daraset_forest->getNTestSamples(); i++){float responses = results_test.at<float>(i, 0);float expected = expected_responses_forest.at<float>(i, 0);cv::String r_str = names_forest[(int)responses];cv::String e_str = names_forest[(int)expected];if (responses == expected){t++;}else{f++;}total++;}cout << "正确答案:" << t << endl;cout << "错误答案:" << f << endl;cout << "测试样本数:" << total << endl;cout << "训练数据集错误:" << forest_train_error << "%" << endl;cout << "测试数据集错误:" << forest_test_error << "%" << endl;}
训练结果精度如下:
正确答案:1617
错误答案:8
测试样本数:1625
训练数据集错误:0%
测试数据集错误:0.492308%
2、c#、opencvsharp代码参考
opencvsharp没给实现数据集划分,所以还是人工智能手动划分数据集,打开agaricus-lepiota.data,copy一份,我这里是前7000条进行训练,后1124条用于测试。
int[,] att = GetTArray(@"D:\Project\deeplearn\dataset\mushroom\agaricus-lepiota.handsplit.train.data", 7000);
int[] label = GetTLabel(@"D:\Project\deeplearn\dataset\mushroom\agaricus-lepiota.handsplit.train.data", 7000);
InputArray array = InputArray.Create(att);
InputArray outarray = InputArray.Create(label);OpenCvSharp.ML.RTrees dtrees = OpenCvSharp.ML.RTrees.Create();
dtrees.ActiveVarCount = false;
dtrees.CalculateVarImportance = true;
dtrees.TermCriteria = new TermCriteria(CriteriaType.Eps | CriteriaType.MaxIter, 100, 0.01);dtrees.Train(array, OpenCvSharp.ML.SampleTypes.RowSample, outarray);//输出属性重要性分数
Mat var_importance = dtrees.GetVarImportance();
if (!var_importance.Empty())
{double rt_imp_sum = Cv2.Sum(var_importance)[0];int n = (int)var_importance.Total();//矩阵元素总个数for (int i = 0; i < n; i++){Console.WriteLine(i + ":" + (100f * var_importance.At<float>(i) / rt_imp_sum));}
}//测试
int t = 0;
int f = 0;
List<int[]> test_arr = GetTestArray(@"D:\Project\deeplearn\dataset\mushroom\agaricus-lepiota.handsplit.test.data");
int[] test_label = GetTLabel(@"D:\Project\deeplearn\dataset\mushroom\agaricus-lepiota.handsplit.test.data", 1124);
for (int i = 0; i < test_arr.Count; i++)
{Mat p = new Mat(1, 22, OpenCvSharp.MatType.CV_32F, test_arr[i]);float rrr = dtrees.Predict(p);System.Console.WriteLine("" + rrr);if(test_label[i] == (int)rrr){t++;}else{f++;}
}
System.Console.WriteLine("正确数量:" + t);
System.Console.WriteLine("错误数量:" + f);
测试集预测结果:
正确数量:1124
错误数量:0
四、多分类任务 - 英文字母分类
数据集下载地址,对应含义,见官网:
letter-recognition | Machine Learning Datahttps://networkrepository.com/letter-recognition.php 与决策树或随机森林分类器不同,目前在OpenCV中实现的Boosting算法仅能处理二分类问题。本节介绍一种展开技巧来解决多分类问题,以增加训练和预测时的计算量为代价。此技巧不仅适用于Boosting分类器,也适用于其他类型的二分类器。
示例代码7-2演示的是分类A~Z,共26个字母。数据来源于Letter Recognition数据集。该数据集有20000个样本,下载下来的数据文件的每行表示一个样本,其中,第1个数据为样本标签(26个大写英文字母之一),第2~17个特征为16维整型特征向量。在展开技巧中,训练集从1个扩展成26个。
展开时,数据集将从20000个样本扩展到:样本数×类别数=20000×26个样本。原来的标签变成新增的特征。同时,这些扩展后的样本特征向量的新标签变为1或0:即true或false。通过学习数据来回答测试样本是否为a、是否为b、……是否为z的问题,最终得到答案。
关键代码分析如下:首先,创建一个数组var_type,该数组指示如何处理每个特征和标签。其次,创建训练数据结构。在原始数据中不仅有var_count个特征(在本示例中,原始样本有16个特征),还有一列用于存放标签(0或1)。另外,标签和原始数据之间还有一列,用于存放原始数据中的标签(即字符型变量)。因此扩展后一共有var_count+2维向量。
再次,构造分类器。需要注意的是:在展开技巧中有先验知识,即必须使用setPriors(Mat(priors))方法设置priors参数。我们以预测一个字母是否为a为例:预测一个字母是a与预测这个字母非a的两种分类代价并不相同,因为展开使得非a的可能性更高,所以说一个字母是a(即y=1)要比说一个字母不是a(即y=0)的代价要高25倍。因为展开使得有25个样本向量对应负标签y=0,而只有1个样本向量对应正标签y=1,所以正标签需要相应地增加权重。
最后,进行模型创建与设置、训练模型等常规动作。
1、c++代码参考
static bool 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);int i;vector<int> responses;_data->release();_responses->release();FILE* f = fopen(filename.c_str(), "rt");if (!f){cout << "Could not read the database " << filename << endl;return false;}for (;;){char* ptr;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 true;
}void opencv_adaboost_letterrecognition()
{time_t now = time(0);char* dt = ctime(&now);//读取数据const char* file_name = "D:/Project/deeplearn/dataset/letter-recognition/letter-recognition.csv";const int class_count = 26;Mat data, responses, weak_responses;bool ok = read_num_class_data(file_name, 16, &data, &responses);if (!ok){cout << "read num class data fail" << endl;return;}int i, j, k;Ptr<Boost> boost;int nsamples_all = data.rows; //样本总数20000int ntrain_samples = (int)(nsamples_all * 0.5); //一半用于训练int var_count = data.cols; //特征维数Mat new_data(ntrain_samples* class_count, var_count+1, CV_32F);Mat new_responses(ntrain_samples * class_count, 1, CV_32S);now = time(0);dt = ctime(&now);for (i=0; i< ntrain_samples; i++) //遍历训练集{const float* data_row = data.ptr<float>(i);for (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;cv::Ptr<TrainData> tdata = TrainData::create(new_data, //扩展的26倍向量ROW_SAMPLE,new_responses, //扩展的响应cv::noArray(),cv::noArray(),cv::noArray(),var_type);vector<double> priors(2);priors[0] = 1;priors[1] = 26;now = time(0);dt = ctime(&now);cout << "训练需要一段时间" << endl;boost = Boost::create();boost->setBoostType(Boost::GENTLE);boost->setWeakCount(100);boost->setWeightTrimRate(0.95);boost->setMaxDepth(5);boost->setUseSurrogates(false);boost->setPriors(Mat(priors));boost->train(tdata);//cout << "训练完成" << endl;Mat temp_sample(1, var_count + 1, CV_32F);float* tptr = temp_sample.ptr<float>();now = time(0);dt = ctime(&now);cout << "测试开始" << endl;double train_tr = 0, test_tr = 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 (k = 0; k < var_count; k++){tptr[k] = ptr[k];}for (j = 0; j < class_count; j++){tptr[var_count] = (float)j;float s = boost->predict(temp_sample, noArray(), StatModel::RAW_OUTPUT);if (max_sum < s){max_sum = s;best_class = j + 'A';}}double r = std::abs(best_class - responses.at<int>(i)) < FLT_EPSILON ? 1 : 0;if (i < ntrain_samples)train_tr += r;elsetest_tr += r;}test_tr /= nsamples_all - ntrain_samples;train_tr = ntrain_samples > 0 ? (train_tr / ntrain_samples) : 1.;now = time(0);dt = ctime(&now);printf("识别率:train = %.1f%%, test=%.1f%%\n", train_tr*100., test_tr * 100.);
}
当前超参下运行结果:
识别率:train = 84.2%, test=80.2%
2、 c#、opencvsharp代码参考
没有实现,可以参考c++的代码。
五、小结
Boosting算法只能直接处理二分类问题,但是可以通过使用展开技巧来处理多分类问题。在使用展开技巧时,将根据类别多少引入额外的计算开销,因此OpenCV中的Boosting算法并不是解决多分类问题的最快或最方便的方法。仅从这点来看,对于多分类问题,随机森林是更好的解决方法。
Opencv学习笔记 - 使用opencvsharp和Boosting算法处理分类问题相关推荐
- Opencv学习笔记 - 使用opencvsharp和决策树进行训练和预测
一.决策树 决策树是最早的机器学习算法之一,起源于对人类某些决策过程的模仿,属于监督学习算法.决策树的优点是易于理解,有些决策树既可以做分类,也可以做回归.在排名前十的数据挖掘算法中有两种是决策树[1 ...
- Opencv学习笔记 - 使用opencvsharp和期望最大化
一.期望最大化概述 期望最大化的受欢迎程度在很大程度上是因为它是从观察中学习参数的有效且稳健的程序.然而,通常可用于训练概率模型的唯一数据是不完整的.例如,在医学诊断中可能会出现缺失值,其中患者病史通 ...
- Python OpenCV学习笔记之:使用Grabcut算法进行图像背景和前景分割
为什么80%的码农都做不了架构师?>>> # -*- coding: utf-8 -*- """ 图像分割 """i ...
- 【OpenCV学习笔记 020】K-Means聚类算法介绍及实现
一.K-Means算法介绍 在数据挖掘中,K-Means算法是一种cluster analysis的算法,其主要是来计算数据聚集的算法,主要通过不断地取离种子点最近均值的算法. 问题 K-Means算 ...
- Opencv学习笔记 - OpenCV 4机器学习算法简介
在机器学习中,一些比较流行方法的包括:支持向量机(SVM).人工神经网络(ANN).聚类.k-最近邻.决策树和深度学习.OpenCV支持并实现几乎所有这些方法,并有详细的文档说明(包含在Main mo ...
- OpenCV学习笔记(二十六)——小试SVM算法ml OpenCV学习笔记(二十七)——基于级联分类器的目标检测objdect OpenCV学习笔记(二十八)——光流法对运动目标跟踪Video Ope
OpenCV学习笔记(二十六)--小试SVM算法ml 总感觉自己停留在码农的初级阶段,要想更上一层,就得静下心来,好好研究一下算法的东西.OpenCV作为一个计算机视觉的开源库,肯定不会只停留在数字图 ...
- 分水岭算法java,OpenCV 学习笔记 04 深度估计与分割——GrabCut算法与分水岭算法...
1 使用普通摄像头进行深度估计 1.1 深度估计原理 这里会用到几何学中的极几何(Epipolar Geometry),它属于立体视觉(stereo vision)几何学,立体视觉是计算机视觉的一个分 ...
- OpenCv学习笔记4--图像分割之GrabCut算法
说明: 本文章是opencv学习笔记系列的第四篇小结,可能前几篇内容太多,排版也不甚合理,所以为了更好的观看体验,这次的内容会稍微少那么一点点,再次重申 所有代码在我的github主页https:// ...
- OpenCV学习笔记(二十一)——绘图函数core OpenCV学习笔记(二十二)——粒子滤波跟踪方法 OpenCV学习笔记(二十三)——OpenCV的GUI之凤凰涅槃Qt OpenCV学习笔记(二十
OpenCV学习笔记(二十一)--绘图函数core 在图像中,我们经常想要在图像中做一些标识记号,这就需要绘图函数.OpenCV虽然没有太优秀的GUI,但在绘图方面还是做得很完整的.这里就介绍一下相关 ...
最新文章
- R语言构建回归模型并进行模型诊断(线性关系不满足时)、进行变量变换(Transforming variables)、使用car包中的boxTidwell函数对预测变量进行Box–Tidwell变换
- 1.%@Page%中的Codebehind、AutoEventWireup、Inherits有何作用?
- 一个蚂蚁前端程序员,曾经的辛酸面试历程
- android 剪切板广播发送者,Android使用剪切板传递数据
- WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形
- 浅谈js函数三种定义方式 四种调用方式 调用顺序
- redis专题:redis的常用数据结构及使用场景
- 计算机组成原理第二版知识大纲
- Java中的文件压缩
- SVN客户端服务器搭建与使用(一)
- paip. VS2010版本控制tfs使用总结
- 大唐移动骨干集体出走 TD人才考验大唐
- 华为路由器时区以及时间配置
- 四月一个晴朗的早晨,遇见一个百分之百的女孩
- 化云为雨,华为云为什么要深入经济的“毛细血管”?
- 雷达原理---时频分析--6.利用小波分析进行奇异点定位和消除
- 焦距、光圈和景深的关系and实拍图像
- 保险丝的作用,参数及选型应用,你真的懂了吗——电子元器件篇
- 桌面画图工具:Pointofix(fertig)
- 戴尔linux改win7教程视频,戴尔笔记本win10改win7系统BIOS设置详细教程
热门文章
- 下载一首网络歌曲 该歌曲地址获取不到歌曲的大小,但可以正常播放
- c语言照样解决脑筋急转弯
- String类的常用AP1
- android MediaPlayer 的SeekTo 方法
- tree命令详解(输出目录树层结构,显示目录和文件)
- ChatGPT 玩「脱」了,写了份毁灭人类计划书,还遭到了 Stack Overflow 的封杀.........
- experiment-02-实验二 numpy的基本使用
- Borland C++ Builder (BCB) 安装TeeChart Pro v8.01的步聚与方法
- 计算机体系结构考试题及知识点,2018年10月自考02325计算机系统结构真题及答案...
- VASP(1)_参数测试_晶格常数测试