支持向量机(svm)介绍

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

⽀持向量机(svm)介绍
⽀持向量机(SVM)介绍
⽬标
本⽂档尝试解答如下问题:
如何使⽤OpenCV函数训练⼀个SVM分类器,以及⽤测试训练结果。

什么是⽀持向量机(SVM)?
⽀持向量机 (SVM) 是⼀个类分类器,正式的定义是⼀个能够将不同类样本在样本空间分隔的超平⾯。

换句话说,给定⼀些标记(label)好的训练样本 (监督式学习), SVM算法输出⼀个最优化的分隔超平⾯。

如何来界定⼀个超平⾯是不是最优的呢? 考虑如下问题:
假设给定⼀些分属于两类的2维点,这些点可以通过直线分割,我们要找到⼀条最优的分割线.
Note
在这个⽰例中,我们考虑卡迪尔平⾯内的点与线,⽽不是⾼维的向量与超平⾯。

这⼀简化是为了让我们以更加直觉的⽅式建⽴起对SVM概念的理解,但是其基本的原理同样适⽤于更⾼维的样本分类情形。

在上⾯的图中,你可以直觉的观察到有多种可能的直线可以将样本分开。

那是不是某条直线⽐其他的更加合适呢? 我们可以凭直觉来定义⼀条评价直线好坏的标准:
距离样本太近的直线不是最优的,因为这样的直线对噪声敏感度⾼,泛化性较差。

因此我们的⽬标是找到⼀条直线,离所有点的距离最远。

由此, SVM算法的实质是找出⼀个能够将某个值最⼤化的超平⾯,这个值就是超平⾯离所有训练样本的最⼩距离。

这个最⼩距离⽤SVM术语来说叫做间隔(margin) 。

概括⼀下,最优分割超平⾯最⼤化训练数据的间隔。

如何计算最优超平⾯?
下⾯的公式定义了超平⾯的表达式:
叫做权重向量,叫做偏置(bias)。

See also
关于超平⾯的更加详细的说明可以参考T. Hastie, R. Tibshirani 和 J. H. Friedman的书籍Elements of Statistical Learning, section 4.5 (Seperating Hyperplanes)。

最优超平⾯可以有⽆数种表达⽅式,即通过任意的缩放和。

习惯上我们使⽤以下⽅式来表达最优超平⾯
式中表⽰离超平⾯最近的那些点。

这些点被称为⽀持向量**。

该超平⾯也称为 **canonical 超平⾯.
通过⼏何学的知识,我们知道点到超平⾯的距离为:
特别的,对于 canonical 超平⾯, 表达式中的分⼦为1,因此⽀持向量到canonical 超平⾯的距离是
刚才我们介绍了间隔(margin),这⾥表⽰为, 它的取值是最近距离的2倍:
最后最⼤化转化为在附加限制条件下最⼩化函数。

限制条件隐含超平⾯将所有训练样本
正确分类的条件,
式中表⽰样本的类别标记。

这是⼀个拉格朗⽇优化问题,可以通过拉格朗⽇乘数法得到最优超平⾯的权重向量和偏置。

源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>
using namespace cv;
int main()
{
// 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(3, 1, CV_32FC1, labels);
float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
Mat trainingDataMat(3, 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) << 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;
}
// 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);
}
解释
1. 建⽴训练样本
本例中的训练样本由分属于两个类别的2维点组成,其中⼀类包含⼀个样本点,另⼀类包含三个点。

float labels[4] = {1.0, -1.0, -1.0, -1.0};
float trainingData[4][2] = {{501, 10}, {255, 10}, {501, 255}, {10, 501}};
函数要求训练数据储存于float类型的结构中,因此我们定义了以下矩阵:
Mat trainingDataMat(3, 2, CV_32FC1, trainingData);
Mat labelsMat (3, 1, CV_32FC1, labels);
2. 设置SVM参数
此教程中,我们以可线性分割的分属两类的训练样本简单讲解了SVM的基本原理。

然⽽,SVM的实际应⽤情形可能复杂得多 (⽐如⾮线性分割数据问题,SVM核函数的选择问题等等)。

总⽽⾔之,我们需要在训练之前对SVM做⼀些参数设定。

这些参数保存在类中。

CvSVMParams params;
params.svm_type = CvSVM::C_SVC;
params.kernel_type = CvSVM::LINEAR;
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
SVM类型. 这⾥我们选择了 CvSVM::C_SVC 类型,该类型可以⽤于n-类分类问题 (n 2)。

这个参数定义
在CvSVMParams.svm_type属性中.
Note
CvSVM::C_SVC 类型的重要特征是它可以处理⾮完美分类的问题 (及训练数据不可以完全的线性分割)。

在本例中这⼀特征的意义并不⼤,因为我们的数据是可以线性分割的,我们这⾥选择它是因为它是最常被使⽤的SVM类型。

SVM 核类型. 我们没有讨论核函数,因为对于本例的样本,核函数的讨论没有必要。

然⽽,有必要简单说⼀下核函数背后的主要思想,核函数的⽬的是为了将训练样本映射到更有利于可线性分割的样本集。

映射的结果是增加了样本向量的维度,这⼀过程通过核函数完成。

此处我们选择的核函数类型是 CvSVM::LINEAR 表⽰不需要进⾏映射。

该参数
由CvSVMParams.kernel_type属性定义。

算法终⽌条件. SVM训练的过程就是⼀个通过迭代⽅式解决约束条件下的⼆次优化问题,这⾥我们指定⼀个最⼤迭代次数和容许误差,以允许算法在适当的条件下停⽌计算。

该参数定义在结构中。

3. 训练⽀持向量机
调⽤函数来建⽴SVM模型。

CvSVM SVM;
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
4. SVM区域分割
函数通过重建训练完毕的⽀持向量机来将输⼊的样本分类。

本例中我们通过该函数给向量空间着⾊,及将图像中的每个像素当作卡迪尔平⾯上的⼀点,每⼀点的着⾊取决于SVM对该点的分类类别:绿⾊表⽰标记为1的点,蓝⾊表⽰标记为-1的点。

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) << 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. ⽀持向量
这⾥⽤了⼏个函数来获取⽀持向量的信息。

函数输出⽀持向量的数量,函数根据输⼊⽀持向量的索引来获取指定位置的⽀持向量。

通过这⼀⽅法我们找到训练样本的⽀持向量并突出显⽰它们。

int c = SVM.get_support_vector_count();
for (int i = 0; i < c; ++i)
{
const float* 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);
}
结果
程序创建了⼀张图像,在其中显⽰了训练样本,其中⼀个类显⽰为⽩⾊圆圈,另⼀个类显⽰为⿊⾊圆圈。

训练得到SVM,并将图像的每⼀个像素分类。

分类的结果将图像分为蓝绿两部分,中间线就是最优分割超平⾯。

最后⽀持向量通过灰⾊边框加重显⽰。

翻译者。

相关文档
最新文档