codecamp

SVM简介

目标

在本教程中,您将学习如何:

什么是SVM?

Support Vector Machines(SVM)是由分离超平面正式定义的鉴别分类器。换句话说,给定的训练数据(监督学习),算法输出了一个最优的超平面,对新的例子进行了分类。

在哪个意义上是超平面获得最优?让我们考虑以下简单问题:

对于属于两类之一的线性可分离的2D点组,找到分离直线。

separating-lines

注意
在这个例子中,我们处理笛卡尔平面中的线和点,而不是在高维空间中的超平面和向量。这是一个简化的问题。重要的是要明白,这只是因为我们的直觉更好地从容易想象的例子构建。然而,相同的概念适用于要分类的示例位于维度高于2的空间中的任务。

在上图中,您可以看到有多条线路可以解决问题。他们中的任何一个比别人好吗?我们可以直观地定义一个标准来估计线条的价值:如果线路通过太靠近点,则它是坏的,因为它将会对噪声敏感,并且不会正确地推广。所以我们的目标应该是尽可能地找出所有的路线。

然后,SVM算法的操作基于找到给出训练样本最大最小距离的超平面。两次,这个距离在SVM的理论中得到了边缘的重要名称。因此,最优分离超平面使训练数据的余量达到最大。

SVM简介

如何计算最佳超平面?

我们来介绍用于定义超平面的符号:

SVM简介

其中β被称为权重向量,而β0称为偏差

也可以看看
这和超平面更深入的描述,你可以在4.5节(发现分隔条件超平面书的):统计学习的要素通过 T. Hastie, R. Tibshirani和JH Friedman([172])。

通过缩放β和可以以无限数量的不同方式表示最优超平面β0。作为惯例,在超平面的所有可能的表示中,选择的是

SVM

其中X表示最接近超平面的训练样本。通常,最接近超平面的训练样本称为支持向量。这种表示被称为规范超平面。

现在,我们使用几何的结果给出点X和超平面之间的距离(β,β0):

SVM

特别地,对于规范超平面,分子等于1,并且到支持向量的距离为

SVM

回想一下,上一节中介绍的the margin(这里表示为M)是距离最接近的例子的两倍:

SVM简介

最后,m最大化问题等价于一个函数L(β)在某些约束条件下的极小化问题。约束模型对超平面的要求正确分类所有训练样本。从形式上看,

SVM简介

其中yi 表示训练样本的每个标签。

这是拉格朗日优化的问题,可以使用拉格朗日乘数来求解最优超平面的权重向量β和偏置β0。

源代码

注意
以下代码已经通过OpenCV 3.0类和函数实现。可以在此页面中找到使用OpenCV 2.4的代码的等效版本
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include "opencv2/imgcodecs.hpp"
#include <opencv2/highgui.hpp>
#include <opencv2/ml.hpp>
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
    int labels[4] = {1, -1, -1, -1};
    float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
    Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
    Mat labelsMat(4, 1, CV_32SC1, labels);
    // Train the SVM
    Ptr<SVM> svm = SVM::create();
    svm->setType(SVM::C_SVC);
    svm->setKernel(SVM::LINEAR);
    svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
    svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);
    // Show the decision regions given by the SVM
    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_<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;
    Mat sv = svm->getUncompressedSupportVectors();
    for (int i = 0; i < sv.rows; ++i)
    {
        const float* v = sv.ptr<float>(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);
}

说明

  • 设置训练数据该练习的训练数据由属于两个不同类之一的一组标记的2D点形成; 其中一个课程包括一个点和另一个三点。
    int labels [4] = {1,-1,-1,-1};
    float trainingData [4] [2] = {{501,10},{255,10},{501,255},{10,501}};

将要使用的函数cv :: ml :: SVM :: train需要将训练数据存储为cv :: Mat对象的浮点数。因此,我们从上面定义的数组创建这些对象:

    Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
    Mat labelsMat(4, 1, CV_32SC1, labels);
  • 设置SVM的参数

在本教程中,我们在最简单的情况下介绍了SVM的理论,当训练示例分为两类可以线性分离时。然而,SVM可以用于各种各样的问题(例如,非线性可分离数据的问题,使用内核函数的SVM来提高示例的维度等)。因此,我们必须在训练SVM之前定义一些参数。这些参数存储在类cv :: ml :: SVM的对象中。

    ptr <SVM> svm = SVM :: create();
    svm->的setType(SVM :: C_SVC);
    svm-> setKernel(SVM :: LINEAR);
    svm-> setTermCriteria(TermCriteria(TermCriteria :: MAX_ITER,100,1e-6));

这里:

  1. SVM类型。我们在这里选择可用于n类分类的类型C_SVC(n 2)。这种类型的重要特征是处理类的不完全分离(即训练数据是非线性分离的)。这个功能在这里并不重要,因为数据是线性可分的,我们选择这个SVM类型是最常用的。≥
  2. SVM内核的类型。我们没有谈到内核函数,因为它们对我们正在处理的培训数据不是有趣的。不过,现在让我们简单介绍一下内核函数的主要思路。它是对训练数据进行的映射,以改进其与线性可分离数据集的相似度。该映射包括增加数据的维度,并使用内核函数高效地完成。我们在这里选择类型LINEAR,这意味着没有映射完成。此参数使用cv :: ml :: SVM :: setKernel定义
  3. 算法的终止标准。SVM训练程序以迭代方式解决约束二次优化问题。这里我们指定最大迭代次数和公差误差,因此即使最佳超平面尚未计算,我们允许算法以较少的步数完成。此参数在结构cv :: TermCriteria中定义。

    svm-> train(trainingDataMat,ROW_SAMPLE,labelsMat);

  • 由SVM分类的区域

方法cv :: ml :: SVM :: predict用于使用经过训练的SVM对输入样本进行分类。在这个例子中,我们使用这种方法来根据SVM所做的预测来对空间进行着色。换句话说,遍历图像将其像素解释为笛卡尔平面的点。每个点根据SVM预测的类别着色; 如果是带有标签1的类,则为绿色,如果为带有标签-1的类,则为蓝色

支持向量

我们在这里使用几种方法来获取有关支持向量的信息。方法cv :: ml :: SVM :: getSupportVectors获取所有的支持向量。我们在这里使用这种方法来找到支持向量的训练示例并突出显示。

    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_<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;
        }

  • 支持向量

我们在这里使用几种方法来获取有关支持向量的信息。方法cv :: ml :: SVM :: getSupportVectors获取所有的支持向量。我们在这里使用这种方法来找到支持向量的训练示例并突出显示。

    thickness = 2;
    lineType  = 8;
    Mat sv = svm->getUncompressedSupportVectors();
    for (int i = 0; i < sv.rows; ++i)
    {
        const float* v = sv.ptr<float>(i);
        circle( image,  Point( (int) v[0], (int) v[1]),   6,  Scalar(128, 128, 128), thickness, lineType);
    }

结果

  1. 该代码打开一个图像并显示两个类的训练示例。一个等级的点用白色圆圈表示,黑色的点用于另一个类。
  2. SVM被训练并用于对图像的所有像素进行分类。这导致在蓝色区域和绿色区域中的图像的划分。两个区域之间的边界是最优分离超平面。
  3. 最后,训练示例周围使用灰色戒指显示支持向量。

SVM简介

如何为Halide后端安排网络
非线性可分离数据的SVM
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

OpenCV教程

OpenCV高级GUI和媒体(highgui模块)

OpenCV图像输入和输出(imgcodecs模块)

对象检测(objdetect模块)

计算摄影(照片模块)

图像拼接(拼接模块)

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }