发布时间:2024-08-28 09:01
话不多说,直接上代码:
#include
#include
#include
using namespace std;
using namespace cv;
using namespace cv::ml;
int main(int, char**)
{
//视觉表示的数据
int width = 512, height = 512;
Mat image = Mat::zeros(height, width, CV_8UC3);
//设置训练数据
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);
//训练SVM
Ptr<SVM> svm = SVM::create();//创建一个svm对象
svm->setType(SVM::C_SVC); //设置SVM公式类型
svm->setKernel(SVM::LINEAR);//设置SVM核函数类型
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));//设置SVM训练时迭代终止条件
svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);//训练数据
//显示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;
}
//显示训练数据
int thickness = -1;//-1表示实心
int lineType = 8;
circle(image, Point(501, 10), 5, Scalar(0, 0, 0), thickness, lineType);//半径为5
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);
//显示支持向量
thickness = 2;
lineType = 8;
Mat sv = svm->getUncompressedSupportVectors();
//cout << sv << endl;//输出结果:[501,10; 255,10; 501,255]为什么???
for (int i = 0; i < sv.rows; ++i)
{
const float* v = sv.ptr<float>(i);//指向矩阵sv的第i行
circle(image, Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), thickness, lineType);//灰色,半径为6
}
imwrite(\"result.png\", image); //保存图像
imshow(\"SVM Simple Example\", image); //显示图像
waitKey(0);
}
支持向量机SVM的训练过程主要包括以下四个方面:
本案例中的训练数据如下:
//设置训练数据
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);
这里主要设置了四个坐标点,其中第一个坐标点的标签为1,后三个坐标点的标签为-1。最后将其转化为SVM能识别的所需的类型。
【注】:
①样本数据必须是CV_32FC1类型。这是由opencv3版本决定的;
②样本标签必须是CV_32SC1,opencv3后从int数组转换为CV_32SC1类型,而opencv2是从float数据转换。
初始化参数程序如下:
Ptr<SVM> svm = SVM::create();//创建一个svm对象
svm->setType(SVM::C_SVC); //设置SVM公式类型
svm->setKernel(SVM::LINEAR);//设置SVM核函数类型
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));//设置SVM训练时迭代终止条件
SVM::create() ——创建一个SVM对象
svm->setType(SVM::C_SVC); ——设置SVM公式类型,包括C_SVC、NU_SVC、ONE_CLASS、EPS_SVR、NU_SVR,用于指定分类、回归等,默认为C_SVC;
svm->setKernel(SVM::LINEAR); ——设置SVM核函数类型,包括CUSTOM、LINEAR、POLY、RBF、SIGMOID、CHI2、INTER,默认值为RBF;
非线性分类中常见的核函数包括:齐次多项式、非齐次多项式、双曲正切、高斯核(Gaussiankernel)、线性核、径向基函数(radialbasis function, RBF)核和、Sigmoid核。
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6)); ——设置SVM训练时迭代终止条件,默认值是cv::TermCriteria(cv::TermCriteria::MAX_ITER + TermCriteria::EPS,1000, FLT_EPSILON);
程序如下:
svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);//训练数据
当遇到较大数据量时,一般需要将数据保存为xml文件,以便在其他程序中得到调用。程序如下:
//保存模型
svm->save(\"svm.xml\");
当需要加载2.4中保存的xml文件时,代码为:
Ptr<SVM> svm = SVM::load(\"svm.xml\");//加载svm对象
测试数据代码如下:
int response = (int)svm->predict(p);
其中,p为CV_32FC1类型数据,response输出SVM预测的数据类型。
本例中,由于数据量较小,因此我们可以显示SVM的决策区域,以便更好地了解SVM算法的实现过程。主要步骤如下:
//视觉表示的数据
int width = 512, height = 512;
Mat image = Mat::zeros(height, width, CV_8UC3);
将图像显示区域设置为512*512像素大小。
//显示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;
}
遍历图像上所有点,如果该点预测为1,则赋值为绿色;否则如果预测为-1,则赋值为蓝色。
//显示训练数据
int thickness = -1;//-1表示实心
int lineType = 8;
circle(image, Point(501, 10), 5, Scalar(0, 0, 0), thickness, lineType);//半径为5
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);
将训练数据标签为1的坐标点显示为半径为5的黑色实心圆圈,标签为-1的坐标点显示为半径为5的白色实心圆圈。
那什么是支持向量呢?看一幅图:
样本中距离超平面最近的一些点,这些点叫做支持向量。
代码如下:
//显示支持向量
thickness = 2;
lineType = 8;
Mat sv = svm->getUncompressedSupportVectors();
//cout << sv << endl;//输出结果:[501,10; 255,10; 501,255]为什么???
for (int i = 0; i < sv.rows; ++i)
{
const float* v = sv.ptr<float>(i);//指向矩阵sv的第i行
circle(image, Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), thickness, lineType);//灰色,半径为6
}
将所有训练得到的支持向量绘制为半径为6的空心灰色圆圈。
输出图像为:
在上图中可以发现,被灰色圆圈包围的三个数据才决定了最终的决策边界,所以这三个数据被称之为支持向量。
ok,以上便是全部内容了,如果对你有所帮助,记得点个赞呦~
参考:
如果还想进一步加深对支持向量机SVM的了解,可以参考博客:基于C++,OpenCV3以及SVM的手写数字识别系统的设计(从手写数字识别设计中认识SVM)
如果想了解SVM的推导过程,可以参考视频:
1.林轩田机器学习技法(Machine Learning Techniques)
2.机器学习-白板推导系列(六)-支持向量机SVM(Support Vector Machine)
或
3.机器学习实战教程(八):支持向量机原理篇之手撕线性SVM
4.机器学习实战教程(九):支持向量机实战篇之再撕非线性SVM