支持向量机介绍


1.目标

本介绍中你将了解到:

    使用OpenCV函数CvSVMware::train创建一个基于SVM的分类器,并且通过CvSVM::predict测试其性能。

 

2.什么是SVM?

    支持向量机是一个由分隔超平面来正式定义有识别能力的分类器。换句话说,根据已标注训练数据(有监督学习),该算法(SVM)输出一个最优的超平面,用来分类新的样本。

    什么情况下能够得到最优超平面?我们来思考一下下面的简单问题:

    对于一组线性可分割的二维点,这些点属于两类之一,找出一条分割直线。

支持向量机介绍_第1张图片

    注意:在这个例子中我们都是在笛卡尔平面处理点和线,而不是超平面或者高维空间的向量。这是该问题的简化。这样处理是因为我们对容易想象的例子能更好的建立直觉,这点很重要。但是,同样的概念可应用于多维空间的分类问题。

   

    在上图中我们能看到存在多条直线能够解决该分类问题。其中是否有一些直线比其他的更好呢?我们可以直观的定义一种标准来评估这些直线的价值:

    如果一条直线距离某些点太近,我们就说这条线不好,因为它会影响灵敏度,较难正确的形成。因此我们的目标应该是找到一条直线,尽可能的离所有点都最远。

    那么,SVM算法的操作是基于寻找那个离训练样本最短距离最大的超平面。而且,这个距离出现一个SVM理论中的重要名称“边缘”(margin)。因此,最优分割超平面最大化训练数据的边缘。

支持向量机介绍_第2张图片

3.最优超平面是怎么计算的?

    让我们介绍用来正式定义一个超平面的公式:

    其中,β是权向量,β0是偏差。

    最优超平面可以根据ββ0的不同,被用有限种方式描述。作为约定,在所有可能的超平面描述中,我们选择下面这个:

    这里,x代表离超平面最近的训练样本。一般情况下,离超平面最近的训练样本被称为“支持向量”。这一表示被称为“规范超平面”。(译者注:这里可能会有读者疑惑方程怎么来的,是什么意思?首先这里是一个约定,就将满足这样一个约束条件的超平面称为一个“规范超平面”;然后,其实一个方程直接加上一个常数,就是将这个方程在笛卡尔坐标系中所代表的超平面作上下平移,或者就说二维空间中的平面上下平移,这里将原方程加上绝对值符号后再等于1,实际上是在超平面方程上加上或者减去一个常数1,绝对值符号去掉就是等于正负1嘛。因此,规范超平面实际上是我们假象的超平面向上向下各平移1个单位所得到的两个新的超平面,即规范超平面)

   

    现在,我们通过几何学结论,点x到超平面(β, β0)的距离:

    特殊的,对于规范超平面,分子等于1,所以规范超平面到支持向量的距离为:

    回顾前面介绍的边缘的概念,表示为M,其距离是超平面到最近样本距离的2倍:

    最后,最大化M的问题等同于服从相同约束的方程L(β)的最小化问题。约束条件模拟超平面正确分类所有训练样本xi的必要条件。正式的,

    其中yi代表训练数据符号。

   

    这是拉格朗日算子优化问题,可以通过拉格朗日乘数获得最优超平面的权向量β和偏差β0解决该问题。

 

4.示例代码

#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/ml/ml.hpp>
 
usingnamespace cv;
 
intmain()
{
    // Data for visual representation
    int width = 512, height = 512;
    Mat image = Mat::zeros(height, width,CV_8UC3);
 
    // Set up training data
    float labels[4] = {1.0, -1.0, -1.0, -1.0};
    Mat labelsMat(4, 1, CV_32FC1, labels);//标记
 
    float trainingData[4][2] = { {501, 10},{255, 10}, {501, 255}, {10, 501} };
    Mat trainingDataMat(4, 2, CV_32FC1,trainingData);//数据
 
    // Set up SVM's parameters
    CvSVMParams params;
    params.svm_type    = CvSVM::C_SVC;
    params.kernel_type = CvSVM::LINEAR;
    params.term_crit   = cvTermCriteria(CV_TERMCRIT_ITER, 100,1e-6);
 
    // Train the SVM
    CvSVM SVM;
    SVM.train(trainingDataMat, labelsMat,Mat(), Mat(), params);
 
    Vec3b green(0,255,0), blue (255,0,0);
    //Show the decision regions given by the SVM
    for (int i = 0; i < image.rows; ++i)
        for (int j = 0; j < image.cols; ++j)
        {
            Mat sampleMat =(Mat_<float>(1,2) << j,i);
            float response =SVM.predict(sampleMat);
 
            if (response == 1)
                image.at<Vec3b>(i,j)  = green;
            else if (response == -1)
                image.at<Vec3b>(i,j)  =blue;
        }
 
    // Show the training data
    int thickness = -1;
    int lineType = 8;
    circle( image, Point(501,  10), 5, Scalar(  0,  0,   0), thickness, lineType);
    circle( image, Point(255,  10), 5, Scalar(255, 255, 255), thickness,lineType);
    circle( image, Point(501, 255), 5,Scalar(255, 255, 255), thickness, lineType);
    circle( image, Point( 10, 501), 5,Scalar(255, 255, 255), thickness, lineType);
 
    // Show support vectors
    thickness = 2;
    lineType = 8;
    int c    = SVM.get_support_vector_count();
 
    for (int i = 0; i < c; ++i)
    {
        const float* v =SVM.get_support_vector(i);
        circle( image,  Point( (int) v[0], (int) v[1]),   6, Scalar(128, 128, 128), thickness, lineType);
    }
 
    imwrite("result.png",image);        // save the image
 
    imshow("SVM Simple Example",image); // show it to the user
    waitKey(0);
 
}

 

5.代码解析

(1)创建训练数据

    此练习中训练数据由一组已标记的二维点组成,他们分别属于两个不同的类;其中一类有一个点组成,另一类由三个点组成。

floatlabels[4] = {1.0, -1.0, -1.0, -1.0};//对应数据点标记
floattrainingData[4][2] = {{501, 10}, {255, 10}, {501, 255}, {10, 501}};//数据

    函数CvSVM::train的调用将在需要的训练数据被保存成float型的Mat对象之后。因此,我们要通过上面定义的数组创建这些对象:

MattrainingDataMat(3, 2, CV_32FC1, trainingData);
MatlabelsMat      (3, 1, CV_32FC1, labels);

(2)创建SVM的参数

    本篇指导中,我们用最简单的例子介绍了SVM理论,其中训练样本可以被线性地区分为两类。但是,SVM能被用于各种问题(例如,数据的非线性分类问题,SVM使用一个核函数来提升样本维度等等)。基于这一结论,我们在训练SVM之前必须定义一些参数。这些参数被保存在CvSVMParams类的对象中。

CvSVMParamsparams;
params.svm_type    = CvSVM::C_SVC;
params.kernel_type= CvSVM::LINEAR;
params.term_crit   = cvTermCriteria(CV_TERMCRIT_ITER, 100,1e-6);

SVM的类型: 我们选择类型CvSVM::C_SVM,能被用来做n(n≥2)个类别分类。这个参数定义在属性CvSVMParams.svm_type中。

注意:SVM的类型的重要特性CvSVM::C_SVC用来处理类的不完全分离(例如,当训练数据是非线性分离的)。这个特性在这里并不重要,因为这里的数据是线性分割的,并且我们选择这个SVM类型只因为他最常用。

SVM核函数的类型:我们上面没有介绍核函数,是因为他们对我们处理的训练数据不感兴趣(译者注:此处是二维训练数据,核函数用来处理多维的情况)。然而,还是让我们来简单解释一下核函数背后的主要思想。它是对训练数据做映射,以提高数据与线性分类数据的相似性。映射包括提高数据维度,是通过核函数有效执行的。这里我们选择CvSVM::LINEAR,是指不做映射。这个参数定义在属性CvSVMParams.kernel_type。

算法的终止条件:实施SVM训练步骤,是通过迭代的方式来解决有约束的二次优化问题。这里我们指定一个迭代的最大数值,和一个容忍的误差值,因此我们让这个算法以最少计算步数终止,即便最优超平面还没有计算完成。这个参数定义在属性cvTermCriteria。

 

(3)训练SVM

    我们称这个方法为CvSVM::train来创建SVM模型。

CvSVMSVM;
SVM.train(trainingDataMat,labelsMat, Mat(), Mat(), params);

(4)使用SVM区域分类

    CvSVM::predict是通过以训练的SVM模型来分类输入样本的方法。本例中为了着色由SVM预测的空间我们就使用这个方法。也就是说,我们遍历一副图片,把它的像素当成笛卡尔平面的点。每一个点都根据SVM的分类预测染上了颜色;如果是标记为1的类别就涂成绿色的,如果是标记为-1的类,就涂成蓝色的。

Vec3bgreen(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_<float>(1,2)<< i,j);
    float response = SVM.predict(sampleMat);
 
    if (response == 1)
       image.at<Vec3b>(j, i)  = green;
    else
    if (response == -1)
       image.at<Vec3b>(j, i)  = blue;
    }
 

(5)支持向量

    这里我们使用很多方法来获得支持向量的信息。方法CvSVM::get_support_vector_count输出该问题中支持向量的总数,CvSVM::get_support_vector可通过索引获取每一个支持向量。这里,我们使用这种方法找到是支持向量的训练样本并把他们标记出来。

intc     = SVM.get_support_vector_count();
 
for(int i = 0; i < c; ++i)
{
constfloat* v = SVM.get_support_vector(i); // get and then highlight with grayscale
circle(   image, Point( (int) v[0], (int) v[1]),  6,  Scalar(128, 128, 128),thickness, lineType);
}
 

6.结果

l  此代码打开了一副图片,显示了两类中的训练样本。其中一类中的所有点表示成白色,另一类是黑色。

l  训练支持向量机,并用来分类图片中的所有像素。结果图片被分成蓝色区域和绿色区域。两块区域的边界位置极为最优分割超平面。

l  最后,训练样本中带有灰色环的就是支持向量。

支持向量机介绍_第3张图片

原文:http://docs.opencv.org/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html#source-code

你可能感兴趣的:(机器学习,SVM)