SVM算法及OpenCV源码分析

来源:互联网 发布:秦九韶算法解方程 编辑:程序博客网 时间:2024/05/16 08:56
关于SVM原理,请参看:
系统学习机器学习之SVM(一)
系统学习机器学习之SVM(二)
系统学习机器学习之SVM(三)--Liblinear,LibSVM使用整理,总结
系统学习机器学习之SVM(四)--SVM算法总结

原文:
http://blog.csdn.net/zhaocj/article/details/51297907

源码分析 

OpenCV 2.4.9的SVM程序是基于LibSVMv2.6。LibSVM是由台湾大学林智仁等开发的用于SVM分类和回归的开源机器学习工具包。

在进行源码分析之前,我们先给出用于SVM算法的训练参数结构变量CvSVMParams:



svm_type表示OpenCV能够实现的SVM的类型:C-SVC、ν-SVC、单类SVM、ε-SVR和ν-SVR,对应的变量分别为:CvSVM::C_SVC、CvSVM::NU_SVC、CvSVM::ONE_CLASS、CvSVM::EPS_SVR和CvSVM::NU_SVR

kernel_type表示OpenCV能够实现的核函数的类型:线性核函数、多项式核函数、高斯核函数和Sigmoid核函数,对应的变量分别为:CvSVM::LINEAR、CvSVM::POLY、CvSVM::RBF和CvSVM::SIGMOID

degree表示多项式核函数(式54)中的参数q

gamma表示多项式核函数(式54)、高斯核函数(式56)和Sigmoid核函数(式57)中的参数γ

coef0表示多项式核函数(式54)和Sigmoid核函数(式57)中的参数p

C表示惩罚参数C

nu表示ν-SVC和ν-SVR的参数ν

p表示ε-SVR的参数ε

class_weights表示不同分类的权值,该值与参数C相乘后,实现了不同分类的不同惩罚力度,该值越大,该类别的误分类数据的惩罚就越大

term_crit:SVM表示广义SMO算法的迭代过程的终止条件,该变量是结构数据类型:


下面是类CvSVM的缺省构造函数:


下面是SVM的训练函数:


do_train函数的详细讲解:


用于求解C-SVC类型的广义SMO算法:

用于求解ν-SVC类型的广义SMO算法:


用于求解单类SVM类型的广义SMO算法:


用于求解ε-SVR类型的广义SMO算法:


用于求解ν-SVR类型的广义SMO算法:


create函数主要用于初始化和设置广义SMO算法的一些参数:


广义SMO算法的迭代过程:


第一类SVM(C-SVC、单类SVM和ε-SVR)的选取工作集βiβj的函数:


第二类SVM(ν-SVC和ν-SVR)的选取工作集βiβj的函数:


第一类SVM(C-SVC、单类SVM和ε-SVR)的计算决策函数中参数b的函数:


第二类SVM(ν-SVC和ν-SVR)的计算决策函数中参数b的函数:

优化那些使用线性核函数(式53)的SVM,目的是用一个支持向量来代表所有的支持向量:

在前面应用get_row函数计算矩阵Q(即式148中的Q=zizjK(xi,xj))的第i列首地址时,涉及到计算核函数K(xi,xj),即调用calc函数。在用于SVM对样本进行预测时,需要计算决策函数,此时也需要计算核函数K(xi,xj)。但两者还是有区别的,在计算Q时,要用到所有的样本,而计算决策函数时,只需要用到支持向量即可,也就是说在计算Q时,用于表示支持向量的变量用全体样本来替代。

下面我们就来介绍calc函数:


计算线性核函数:


计算多项式核函数:


计算Sigmoid核函数:


计算高斯(径向基函数)核函数:


除了calc_rbf函数以外,calc_linear函数,calc_poly函数和calc_sigmoid函数都会调用calc_non_rbf_base函数,因为这3个核函数都需要计算xi·xj


下面介绍SVM预测样本函数predict。该函数有许多形式,如:

float CvSVM::predict(const Mat& sample,bool returnDFVal=false )

float CvSVM::predict(const CvMat* sample,bool returnDFVal=false )

float CvSVM::predict(const CvMat* samples,CvMat* results)

其中,sample表示需要预测的样本数据,returnDFVal定义该函数的返回类型,为true,并且是两类分类问题时,该函数返回决策函数中sgn函数内的具体值,否则该函数返回分类标签(SVC),或返回估计函数(SVR),results表示相对应样本的预测输出,如果只预测一个样本,则该函数返回预测结果,如果预测多个样本,则必须使用results参数来返回预测结果。

不管哪种形式的predict函数,最终都会调用下面这个函数:



下面是train_auto的函数原型

C++: bool CvSVM::train_auto(const Mat& trainData,

        const Mat& responses

const Mat& varIdx

const Mat& sampleIdx

CvSVMParams params,

int k_fold=10

CvParamGrid Cgrid=CvSVM::get_default_grid(CvSVM::C)

CvParamGrid gammaGrid=CvSVM::get_default_grid(CvSVM::GAMMA)

CvParamGrid pGrid=CvSVM::get_default_grid(CvSVM::P)

CvParamGrid nuGrid=CvSVM::get_default_grid(CvSVM::NU)

CvParamGrid coeffGrid=CvSVM::get_default_grid(CvSVM::COEF)

CvParamGrid degreeGrid=CvSVM::get_default_grid(CvSVM::DEGREE)

bool balanced=false

)

      自动训练函数的参数注释(13个)
  • 前5个参数参考构造函数的参数注释。
  • k_fold: 交叉验证参数。训练集被分成k_fold的自子集。其中一个子集是用来测试模型,其他子集则成为训练集。所以,SVM算法复杂度是执行k_fold的次数。
  • *Grid: (6个)对应的SVM迭代网格参数。
  • balanced: 如果是true则这是一个2类分类问题。这将会创建更多的平衡交叉验证子集。
        自动训练函数的使用说明
  • 这个方法根据CvSVMParams中的最佳参数C, gamma, p, nu, coef0, degree自动训练SVM模型。
  • 参数被认为是最佳的交叉验证,其测试集预估错误最小。
  • 如果没有需要优化的参数,相应的网格步骤应该被设置为小于或等于1的值。例如,为了避免gamma的优化,设置gamma_grid.step = 0,gamma_grid.min_val, gamma_grid.max_val 为任意数值。所以params.gamma 由gamma得出。
  • 最后,如果参数优化是必需的,但是相应的网格却不确定,你可能需要调用函数CvSVM::get_default_grid(),创建一个网格。例如,对于gamma,调用CvSVM::get_default_grid(CvSVM::GAMMA)。
  • 该函数为分类运行 (params.svm_type=CvSVM::C_SVC 或者 params.svm_type=CvSVM::NU_SVC) 和为回归运行 (params.svm_type=CvSVM::EPS_SVR 或者 params.svm_type=CvSVM::NU_SVR)效果一样好。如果params.svm_type=CvSVM::ONE_CLASS,没有优化,并指定执行一般的SVM。
         这里需要注意的是,对于需要的优化的参数虽然train_auto可以自动选择最优值,但在代码中也要先赋初始值,要不然编译能通过,但运行时会报错。下面是示例代码
    CvSVMParams param;  param.svm_type = CvSVM::EPS_SVR;  param.kernel_type = CvSVM::RBF;  param.C = 1;  //给参数赋初始值param.p = 5e-3;  //给参数赋初始值param.gamma = 0.01;  //给参数赋初始值param.term_crit = cvTermCriteria(CV_TERMCRIT_EPS, 100, 5e-3); //对不用的参数step设为0CvParamGrid nuGrid = CvParamGrid(1,1,0.0);CvParamGrid coeffGrid = CvParamGrid(1,1,0.0);CvParamGrid degreeGrid = CvParamGrid(1,1,0.0);CvSVM regressor;regressor.train_auto(PCA_training,tr_label,NULL,NULL,param,10,regressor.get_default_grid(CvSVM::C),regressor.get_default_grid(CvSVM::GAMMA),regressor.get_default_grid(CvSVM::P),nuGrid,coeffGrid,degreeGrid);

          用上面的代码的就可以自动训练优化出参数了,最后想查看优化后的参数值可以使用CvSVMParams params_re = regressor.get_params()函数来获得各优化后的参数值。
    CvSVMParams params_re = regressor.get_params();regressor.save("training_srv.xml");float C = params_re.C;float P = params_re.p;float gamma = params_re.gamma;printf("\nParms: C = %f, P = %f,gamma = %f \n",C,P,gamma);
  • 应用实例

     

    下面我们给出一个具体的SVM应用实例。本次的实例来源于http://archive.ics.uci.edu/ml/中的risi数据,目的是用于判断样本是属于哪类植物:setosa,versicolor,virginica。每类植物各选择20个样本进行训练,而每个样本具有以下4个特征属性:花萼长,花萼宽,花瓣长,花瓣宽。


    输出结果为:

    result:  V

    0 0
    原创粉丝点击