SVM的理论知识见 SVM的一些总结与认识 --入门级
之前一直以为,用SVM做多分类,不就是用多个SVM分类么,请形状类似于一个二叉树,如下:
即,将所有样本当作输入,其中在训练第一个分类器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
其结构形状如下:
这样分类时,我们可以先问分类器 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() 时,定义函数参数时候,直接输入多类别样本以及其标签即可