SVM 多分类 -SVM分类opencv3.0源代码

SVM的理论知识见 SVM的一些总结与认识 --入门级 

之前一直以为,用SVM做多分类,不就是用多个SVM分类么,请形状类似于一个二叉树,如下:

SVM 多分类 -SVM分类opencv3.0源代码_第1张图片

即,将所有样本当作输入,其中在训练第一个分类器SVM_1的时候,其正样本为属于类别1的样本,其负样本为剩余的其他所有样本,这就称为 一对其余法,这样做虽然训练的时间从道理上来讲是相对较快的,但是它会带来一系列的问题:

1. 有可能有一个样本在部分分类器 (多于一个,比如2个SVM中,或者更加极端的情况是在所有分类器) 中将其分为正(负)样本,简单的说,就是有不止一个分类器声称它     属于自己,那么便出现错误。相反,如果说,没有一个分类器声称它属于某一个分类器,那么同样出现误分类,

2.一经误分类,则结果误分类。

3.数据集倾斜。这个问题是最影响训练出来的分类器性能的情况,就是说,在训练其中一个训练器的时候,只有某一类作为正样本,其余类别作为负样本,那么导致正      负样本数量出现严重不平衡情况。

其优点明显弱于确定,所以这种情形在实际应用中是不可取的。

第二种方法:一对一

实际应用中,第一种方法得到的模型精度不高,训练时间也同样不占优势。那么这一对一的方法又是怎么回事儿呢?顾名思义:训练多个二分类SVM,也就是任意两个类别训练一个SVM,这样会有一个问题,比如:我有4类样本,按照这种方法多分类的话,需要训练6个SVM,推广到k 个类别则需要k(k-1)/2 个分类器。但他是怎么分类的呢?

简单的说,投票!所有分类器都对这个样本做一个分类(预测),得到的分类结果最多,那么就将这个样本分到这一类。这样做的好处就是,样本怎么样都会有一个预测值,而不会出现无类别的预测结果。目前流行的SVM工具包 LIBSVM--台湾大学.Chih-Chung Chung and Chih-Jen Lin 就是用的这种方法作为SVM多分类的方法。

第三种方法:DAG SVM

其结构形状如下:

SVM 多分类 -SVM分类opencv3.0源代码_第2张图片

这样分类时,我们可以先问分类器 1v5 (意思是它能回答“是第1类还是第5类”) ,如果回答是5,即往左走,再问“是2还是5”,这样一直问下去,这样做的好处其实是,我们在分类的时候,实际上只是调用了4个分类器。耗时更短,同样不会出现分类重叠或不可分类现象。

现在DAG方法根节点的选取(也就是如何选第一个参与分类的分类器),也有一些方法可以改善整体效果,我们总希望根节点少犯错误为好,因此参与第一次分类的两个类别,最好是差别特别特别大,大到以至于不太可能把他们分错;或者我们就总取在两类分类中正确率最高的那个分类器作根节点,或者我们让两类分类器在分类的时候,不光输出类别的标签,还输出一个类似“置信度”的东东,当它对自己的结果不太自信的时候,我们就不光按照它的输出走,把它旁边的那条路也走一走,等等。

---------------------- OpenCV3.0 SVM分类代码 -------------

下面是在OpenCV3.0+Vs2013的平台上(其配置方法见本人另一博文 win7平台下vs2013配置OpenCV3.0 )的SVM分类代码

#include 
#include 
#include "opencv2/imgcodecs.hpp"
#include 
#include 

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

int main(int, char**)
{
	// Data for visual representation
	int width = 512, height = 512;
	Mat image = Mat::zeros(height, width, CV_8UC3);

	// Set up training data
	//! [setup1]
	int labels[4] = { 1, -1, -1, 1 };
	float trainingData[4][2] = { { 1, 2 }, { -1, -10 }, { 1, -2 }, { 2, 1 } };
	//! [setup1]
	//! [setup2]
	Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
	Mat labelsMat(4, 1, CV_32SC1, labels);
	//! [setup2]


	// Train the SVM
	//! [init]

	Ptr svm = SVM::create();
	svm->setType(SVM::C_SVC);
	svm->setKernel(SVM::LINEAR);
	svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
	//! [init]
	//! [train]
	svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);
	//! [train]

	// Show the decision regions given by the SVM
	//! [show]
	Vec3b green(0, 255, 0), blue(255, 0, 0);
	for (int i = 0; i < image.rows; ++i)
		for (int j = 0; j < image.cols; ++j)
		{
		Mat sampleMat = (Mat_(1, 2) << j, i);
		float response = svm->predict(sampleMat);

		if (response == 1)
			image.at(i, j) = green;
		else if (response == -1)
			image.at(i, j) = blue;
		}
	//! [show]

	// Show the training data
	//! [show_data]
	int thickness = -1;
	int lineType = 8;
	circle(image, Point(1, 2), 5, Scalar(0, 0, 0), thickness, lineType);
	circle(image, Point(-1, -10), 5, Scalar(255, 255, 255), thickness, lineType);
	circle(image, Point(1, -2), 5, Scalar(255, 255, 255), thickness, lineType);
	circle(image, Point(2, 1), 5, Scalar(255, 255, 255), thickness, lineType);
	//! [show_data]

	// Show support vectors
	//! [show_vectors]
	thickness = 2;
	lineType = 8;
	Mat sv = svm->getSupportVectors();

	for (int i = 0; i < sv.rows; ++i)
	{
		const float* v = sv.ptr(i);
		circle(image, Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), thickness, lineType);
	}
	//! [show_vectors]

	Mat res;
	float teatData[1][2] = { { 1, -11 } };
	Mat query(1, 2, CV_32FC1, teatData);
	
	svm->predict(query, res);
	cout << res;
	imwrite("result.png", image);     // save the image

	imshow("SVM Simple Example", image);   // show it to the user
	waitKey(0);

}

需要说明的是:在OpenCV中,SVM多分类方式被隐藏参数,在调用函数svm->train() 时,定义函数参数时候,直接输入多类别样本以及其标签即可


你可能感兴趣的:(机器学习算法)