IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> Opencv学习笔记 - 使用opencvsharp和Boosting算法处理分类问题 -> 正文阅读

[人工智能]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 Dataicon-default.png?t=LA92https://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;	//样本总数20000
	int 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;
		else
			test_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算法并不是解决多分类问题的最快或最方便的方法。仅从这点来看,对于多分类问题,随机森林是更好的解决方法。

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-12-10 11:04:02  更:2021-12-10 11:06:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 1:31:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码