opencv——SVM参数详解

         SVM是一种训练机器学习的算法,可以用于解决分类和回归问题,同时还使用了一种称之为kernel trick(支持向量机的核函数)的技术进行数据的转换,然后再根据这些转换信息,在可能的输出之中找到一个最优的边界(超平面)。简单来说,就是做一些非常复杂的数据转换工作,然后根据预定义的标签或者输出进而计算出如何分离用户的数据。

         支持向量机方法是建立在统计学习理论的VC 维理论和结构风险最小原理基础上的,根据有限的样本信息在模型的复杂性(即对特定训练样本的学习精度,Accuracy)和学习能力(即无错误地识别任意样本的能力)之间寻求最佳折衷,以期获得最好的推广能力(或称泛化能力)。

  • 支持向量机较其他传统机器学习算法的优点:
  • 1、小样本,并不是说样本的绝对数量少(实际上,对任何算法来说,更多的样本几乎总是能带来更好的效果),而是说与问题的复杂度比起来,SVM算法要求的样本数是相对比较少的。SVM解决问题的时候,和样本的维数是无关的(甚至样本是上万维的都可以,这使得SVM很适合用来解决文本分类的问题,当然,有这样的能力也因为引入了核函数)。

  • 2、结构风险最小。(对问题真实模型的逼近与问题真实解之间的误差,就叫做风险,更严格的说,误差的累积叫做风险)。

  • 3、非线性,是指SVM擅长应付样本数据线性不可分的情况,主要通过松弛变量(也有人叫惩罚变量)和核函数技术来实现,这一部分是SVM的精髓。(关于文本分类这个问题究竟是不是线性可分的,尚没有定论,因此不能简单的认为它是线性可分的而作简化处理,在水落石出之前,只好先当它是线性不可分的反正线性可分也不过是线性不可分的一种特例而已,我们向来不怕方法过于通用)。

  • SVM的强大离不开一个很重要的东西--核函数:

1、为何需要核函数?

很多情况下低维空间向量集是难于划分的,解决办法是将它们映射到高维空间。但这个办法带来的艰苦就是策画错杂度的增长,而核函数正好奇妙地解决了这个问题。也就是说,只要选用恰当的核函数,就可以获得高维空间的分类函数(超平面)。在SVM理论中,采取不合的核函数将导致不合的SVM算法。在断定了核函数之后,因为断定核函数的已知数据也存在必然的误差,推敲到推广性题目,是以引入了败坏系数以及处罚系数两个参变量来加以校订。

其实核函数的本质作用可以简练概括为:将低维空间的线性不可分类问题,借助核函数转化为高维空间的线性可分,进而可以在高维空间找到分类的最优边界(超平面)。(下图引自July‘s 支持向量机通俗导论(理解SVM的三层境界))。若要要分类下图红色和蓝色样本点:

                           

扫描二维码关注公众号,回复: 4428830 查看本文章

2、核函数的分类

(1)线性核函数

(2)多项式核函数

(3)径向基(RBF)核函数(高斯核函数)

(4)Sigmoid核函数(二层神经收集核函数)

3、Opencv中的核函数定义:

CvSVM::LINEAR : 线性内核,没有任何向映射至高维空间,线性区分(或回归)在原始特点空间中被完成,这是最快的选择。

.

CvSVM::POLY : 多项式内核:

.

CvSVM::RBF : 基于径向的函数,对于大多半景象都是一个较好的选择:

.

CvSVM::SIGMOID : Sigmoid函数内核:

.

  • Opencv中SVM参数设置:

Opencv中SVM参数设置使用kernel方法定义如下:

    enum KernelTypes {
        /** Returned by SVM::getKernelType in case when custom kernel has been set */
        CUSTOM=-1,
        /** Linear kernel. No mapping is done, linear discrimination (or regression) is
        done in the original feature space. It is the fastest option. \f$K(x_i, x_j) = x_i^T x_j\f$. */
        LINEAR=0,
        /** Polynomial kernel:
        \f$K(x_i, x_j) = (\gamma x_i^T x_j + coef0)^{degree}, \gamma > 0\f$. */
        POLY=1,
        /** Radial basis function (RBF), a good choice in most cases.
        \f$K(x_i, x_j) = e^{-\gamma ||x_i - x_j||^2}, \gamma > 0\f$. */
        RBF=2,
        /** Sigmoid kernel: \f$K(x_i, x_j) = \tanh(\gamma x_i^T x_j + coef0)\f$. */
        SIGMOID=3,
        /** Exponential Chi2 kernel, similar to the RBF kernel:
        \f$K(x_i, x_j) = e^{-\gamma \chi^2(x_i,x_j)}, \chi^2(x_i,x_j) = (x_i-x_j)^2/(x_i+x_j), \gamma > 0\f$. */
        CHI2=4,
        /** Histogram intersection kernel. A fast kernel. \f$K(x_i, x_j) = min(x_i,x_j)\f$. */
        INTER=5
    };

kernel_type:SVM的内核类型(4种):

上面已经介绍过了就不再多说了。

svm_type:指定SVM的类型(5种):

1、CvSVM::C_SVC : C类支撑向量分类机。 n类分组 (n≥2),容许用异常值处罚因子C进行不完全分类。

2、CvSVM::NU_SVC : 类支撑向量分类机。n类似然不完全分类的分类器。参数为庖代C(其值在区间【0,1】中,nu越大,决定计划鸿沟越腻滑)。

3、CvSVM::ONE_CLASS : 单分类器,所有的练习数据提取自同一个类里,然后SVM建树了一个分界线以分别该类在特点空间中所占区域和其它类在特点空间中所占区域。

4、CvSVM::EPS_SVR : 类支撑向量回归机。练习集中的特点向量和拟合出来的超平面的间隔须要小于p。异常值处罚因子C被采取。

5、CvSVM::NU_SVR : 类支撑向量回归机。 庖代了 p。

degree:内核函数(POLY)的参数degree。

gamma:内核函数(POLY/ RBF/ SIGMOID)的参数

coef0:内核函数(POLY/ SIGMOID)的参数coef0。

Cvalue:SVM类型(C_SVC/ EPS_SVR/ NU_SVR)的参数C。

nu:SVM类型(NU_SVC/ ONE_CLASS/ NU_SVR)的参数 

p:SVM类型(EPS_SVR)的参数

class_weights:C_SVC中的可选权重,赋给指定的类,乘以C今后变成 。所以这些权重影响不合类此外错误分类处罚项。权重越大,某一类此外误分类数据的处罚项就越大。

term_crit:SVM的迭代练习过程的中断前提,解决项目组受束缚二次最优题目。您可以指定的公差和/或最大迭代次数。

  • Opencv中SVM分类问题代码流程:

(1)获得练习样本及制作其类别标签(trainingDataMat,labelsMat

(2)设置练习参数(CvSVMParams)

(3)对SVM进行训练(SVM::train)

(4)对新的输入样本进行猜测(SVM::predict),并输出结果类型(对应标签)

  • SVM多类分类问题的几种方法:

目前,构造SVM多类分类器的方法主要有两类:一类是直接法,直接在目标函数上进行修改,将多个分类面的参数求解合并到一个最优化问题中,通过求解该最优化问题“一次性”实现多类分类。这种方法看似简单,但其计算复杂度比较高,实现起来比较困难,只适合用于小型问题中;另一类是间接法,主要是通过组合多个二分类器来实现多分类器的构造,常见的方法有one-against-oneone-against-all两种。

1、一对多法(one-versus-rest,简称OVRSVMs)。训练时依次把某个类别的样本归为一类,其他剩余的样本归为另一类,这样k个类别的样本就构造出了k个SVM。分类时将未知样本分类为具有最大分类函数值的那类。

假如我有四类要划分(也就是4个Label),他们是A、B、C、D。于是我在抽取训练集的时候,分别抽取A所对应的向量作为正集,B,C,D所对应的向量作为负集;B所对应的向量作为正集,A,C,D所对应的向量作为负集;C所对应的向量作为正集,A,B,D所对应的向量作为负集;D所对应的向量作为正集,A,B,C所对应的向量作为负集,这四个训练集分别进行训练,然后的得到四个训练结果文件,在测试的时候,把对应的测试向量分别利用这四个训练结果文件进行测试,最后每个测试都有一个结果f1(x),f2(x),f3(x),f4(x).于是最终的结果便是这四个值中最大的一个。

PS:这种方法有种缺陷,因为训练集是1:M,这种情况下存在偏差.因而不是很实用.

2、一对一法(one-versus-one,简称OVOSVMs或者pairwise)。其做法是在任意两类样本之间设计一个SVM,因此k个类别的样本就需要设计k(k-1)/2个SVM。当对一个未知样本进行分类时,最后得票最多的类别即为该未知样本的类别。Libsvm中的多类分类就是根据这个方法实现的。

还是假设有四类A,B,C,D四类。在训练的时候我选择A,B;A,C; A,D; B,C;B,D;C,D所对应的向量作为训练集,然后得到六个训练结果,在测试的时候,把对应的向量分别对六个结果进行测试,然后采取投票形式,最后得到一组结果。

3、层次支持向量机(H-SVMs)。层次分类法首先将所有类别分成两个子类,再将子类进一步划分成两个次级子类,如此循环,直到得到一个单独的类别为止。

4、DAG-SVMS是由Platt提出的决策导向的循环图DDAG导出的,是针对“一对一”SVMS存在误分、拒分现象提出的。

这里仅仅是对几种多分类方法的简要说明,如果直接调用Opencv的predict方法,并不需要关心多分类算法的具体实现,来看看下面的例子:

  • Opencv中SVM多类分类问题编程实例:
  • void svmm_test()
    {
    	//float trainingData1[16][2] = { { 0, 0 },{ 4, 1 },{ 4, 5 },{ -1, 6 },{ 3,11 },{ -2,10 },{ 4,30 },{ 0,25 },{ 10,13 },{ 15,12 },{ 25,40 },{ 11,35 },{ 8,1 },{ 9,6 },{ 15,5 },{ 20,-1 } };
    	//Mat trainingData = Mat(16, 2, CV_32FC1, trainingData1);
    	//
    	//Mat_<int> trainingLabels(1, 16);
    	//trainingLabels << 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4;
    
    	float trainingData1[10][2] = { { 0, 0 },{ 4, 0 },{ 0, 4 },{ 0, -4 },{ -4,0 },{ 8,0 },{ 0,8 },{ 0,-8 },{ -8,0 },{ 15,0 } };
    	Mat trainingData = Mat(10, 2, CV_32FC1, trainingData1);
    	
    	Mat_<int> trainingLabels(1, 10);
    	trainingLabels << 1, 1, 1, 1, 1, 2, 2, 2, 2, 2;
    
    	Ptr<ml::SVM> svm = ml::SVM::create();
    
    	/*一些设置的东西
    	svm->setType(ml::SVM::C_SVC);//设置svm类型
    	svm->setKernel(ml::SVM::POLY);//设置核函数
    	svm->setDegree(0.5);
    	svm->setGamma(1);
    	svm->setCoef0(1);
    	svm->setNu(0.5);
    	svm->setP(0);
    	svm->setDegree(0.5);
    	svm->setCoef0(1);
    	svm->setGamma(1);
    	*/
    
    	svm->setType(ml::SVM::C_SVC);
    	//svm->setKernel(ml::SVM::LINEAR);
    	svm->setKernel(ml::SVM::RBF);
    	svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
    	svm->train(trainingData, ml::ROW_SAMPLE, trainingLabels);
    	Mat_<float> testFeatures(1, 2);
    	testFeatures << 1, 20;
    	
    	Mat res;
    
    	for (int i = -15; i < 15; i++)
    	{
    		for (int j = -15; j < 15; j++)
    		{
    			testFeatures << i, j;
    			svm->predict(testFeatures, res);
    			int response = res.at<float>(0, 0);
    			cout << response << " ";
    		}
    		cout << endl;
    	}
    }

猜你喜欢

转载自blog.csdn.net/qq_30263737/article/details/83338958