SVM
SVM是一种监督学习方法算法,既可以用来分类Classification 也可以用来做回归Regression。SVM相比于其他的机器学习算法有较好的实效性。在分类问题没有很好的思路的时候一般优先选择SVM。
.C-SVC
首先,我们从二维平面思考问题,假设有两类点,分别如图所示。如果我们选一条直线将其分开,那么这条直线是不是有无数条呢?很显然,是的。我们现在要最优的把这条直线找到,是不是解就是唯一的呢?那什么 又是最优呢?如果有那么一条直线,它离两个类别最近的点的距离组大我们是不是就可以认为这是最优呢?那似乎对于二维平面来说这已经解决了,那高维度呢?原理是一样的,只不过那个时候分离的不再是直线,对于三维点是平面,再高维度,那就是超平面。那么文字上我们似乎已经表述清楚了,那就剩两个问题:
- 找到一个平面去分割这些类。
- 优化这个平面,找到最优解。
那这不就回到了最优问题了。对头,我们现在把文字描述变成优美的数学公式。
首先我们把正样本点标记位1,负样本点标记为-1.。对于图中的三个点我们称之为支持向量。经过途中三个点的直线的值分别是1,-1.那显然,必然有一条直线会使得:
成立。
我们现在可以定义,我们假设我们寻找的超平面的方程为:
对于一个新的样本,我们只需要带入即可,如果大于0则为1,小于0则为-1,数学公式表达为:
看到这里,怎么感觉和线性回归有点相似,是的。接下来就应该是根据样本怎么去求W和b了。
我们假设tn为样本标记点,如果正样本则标记为1,负样本为-1.则有:
而W对于超平面来说是法向量,一个平面的法向量有无数条,但是单位向量只有一个,所以我们除上二范数,得到以下公式:
我们先考察一下我们的目标,我们是需要寻找一个最大的距离,使得该平面和支持向量之间的距离最大。对于在二维平面中,距离的公式可以表示为:
这和上述公式是不是完美的契合。而分子刚好是恒大于0的。所以我们的目标就是下面的方程:
看到这里可能有疑惑,这也不对头啊。还有第一个问题呢?找一个平面去分离这些点。下面我们先考虑,假设我们已知一个w一个b我们是不是唯一确定了一个平面。那我们得去寻找这个平面对应的支持向量,这个支持向量是不是应该是所有点中值最小的那一个?(这里需要停下来思考一下)
对于直线1来说,它的经过支持向量的直线式红色的,而2的支持向量是蓝色的,如果这个直线继续平移这个值是不是会更大?因为负样本乘了-1,所以值也会变大。所以对于一个给定的w,b我们需要寻找最小的条件:
至于为什么加1/2,方便求导。为啥是平方,因为使得值恒大于0.看到这里,基本SVC的推导过程基本结束。下面就是这个有条件的最优化问题了,我们的拉格朗日就应该出场了。
即可求出w和b。
下面给一个简单例子:
有时候,不是所有的类别都能那么好的分出来,这个时候就需要引入松弛因子,这个时候目标函数为:
求解方法和上部分类似:
以上就是SVC得全部推导过程,后面还有SVO用来求解上述拉格朗日遗留得问题,将在下一篇给出。下面给出一个编程实例,。
opencv 实现SVM编程实例
#include<opencv.hpp>
#include<ml.hpp>
#include<iostream>
using namespace std;
using namespace cv;
using namespace ml;
void svm1() {
const int Kwidth = 512;
const int Kheight = 512;
//用于显示分类结果的图像
Mat image = Mat::zeros(Kheight, Kwidth, CV_8UC3);
int labels[20];
for (int i = 0; i < 10; i++)
labels[i] = 1;
for (int i = 10; i < 20; i++)
labels[i] = 2;
Mat labelsMat(20, 1, CV_32SC1, labels);
float trainDataArray[20][2];
RNG rng;
for (int i = 0; i < 10; i++)
{
trainDataArray[i][0] = 400 + static_cast<float>(rng.gaussian(30));
trainDataArray[i][1] = 400 + static_cast<float>(rng.gaussian(30));
}
for (int i = 10; i < 20; i++)
{
trainDataArray[i][0] = 30 + static_cast<float>(rng.gaussian(30));
trainDataArray[i][1] = 30 + static_cast<float>(rng.gaussian(30));
}
Mat trainMat(20, 2, CV_32FC1, trainDataArray);
Ptr<SVM> model = SVM::create();
//参数设置
model->setC(1);
model->setKernel(SVM::LINEAR);
model->setType(SVM::C_SVC);
model->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
//数据准备
Ptr<TrainData> data = TrainData::create(trainMat, ROW_SAMPLE, labelsMat);
model->train(data);
//对图像内所有512*512个背景点进行预测,不同的预测结果,图像背景区域显示不同的颜色
Vec3b red(0, 0, 255), green(0, 255, 0), blue(255, 0, 0);
Mat sampleMat;
for (int i = 0; i < image.rows; ++i) {
for (int j = 0; j < image.cols; ++j)
{
sampleMat = (Mat_<float>(1, 2) << j, i); //生成测试数据
int response = model->predict(sampleMat); //进行预测,返回1或-1
if (response == 1)
image.at<Vec3b>(i, j) = red;
else
image.at<Vec3b>(i, j) = green;
}
}
//把训练样本点,显示在图相框内
for (int i = 0; i < trainMat.rows; i++)
{
const float * v = trainMat.ptr<float>(i);
Point pt = Point((int)v[0], (int)v[1]);
if (labels[i] == 1) //不同的圆点,标记不同的颜色
circle(image, pt, 5, Scalar::all(255), -1, 8);
else if (labels[i] == 2)
circle(image, pt, 5, Scalar::all(128), -1, 8);
else
circle(image, pt, 5, Scalar::all(255), -1, 8);
}
//显示分类结果图像
imshow("SVM", image);
waitKey(0);
}
int main() {
svm1();
system("pause");
}
上述程序运行得结果为:
可以看出效果还是蛮好得。
小结
1.本章节主要叙述了SVM得基本原理,SVC得算法推导,实现了一个SVC得实例。在下一章节我们将介绍Opencv中有关SVM得所有参数得含义和测试,其实这和上述得推导过程是一样得,如果不清楚整个计算过程,自然参数设置得意义就没有了。所以说,算法得推导过程还是很重要得。
2.码字不易,欲知后事如何,请听下回分解。
3.如果说需要部署到嵌入式系统中,建议查看opencv2.x得源码,因为在opencv3以上得版本,opencv已经将所有得机器学习算法都继承于一个虚类,这对我们查看源码是不太友好的。但了解了架构问题也不大,可以去官网自行查看。
https://docs.opencv.org/master/d1/d2d/classcv_1_1ml_1_1SVM.html#details在这直接附上链接。