打赏

OPENCV SVM介绍和自带例子

依据机器学习算法如何学习数据可分为3类:
有监督学习:从有标签的数据学习,得到模型参数,对测试数据正确分类;
无监督学习:没有标签,计算机自己寻找输入数据可能的模型;
强化学习(reinforcement learning):计算机与动态环境交互,学习错误反馈达到更优的目的。

依据机器学习期望结果来分类:
分类:输入被分为N个类别的一种;
回归:输出是连续值;如依据房子的大小,时间,位置来预测房子的价格;
聚类:使用无监督学习将输入聚为N类;
密度估计(density estimation):找到输入可能的分布;

以OpenCV的SVM为例:
支持向量机较其他传统机器学习算法的优点:
1、小样本,并不是说样本的绝对数量少(实际上,对任何算法来说,更多的样本几乎总是能带来更好的效果),而是说与问题的复杂度比起来,SVM算法要求的样本数是相对比较少的。SVM解决问题的时候,和样本的维数是无关的(甚至样本是上万维的都可以,这使得SVM很适合用来解决文本分类的问题,当然,有这样的能力也因为引入了核函数)。
2、结构风险最小。(对问题真实模型的逼近与问题真实解之间的误差,就叫做风险,更严格的说,误差的累积叫做风险)。
3、非线性,是指SVM擅长应付样本数据线性不可分的情况,主要通过松弛变量(也有人叫惩罚变量)和核函数技术来实现,这一部分是SVM的精髓。

SVM的精髓之一核函数:OpenCV提供了以下几种
线性核函数:SVM::LINEAR,线性内核,没有高维空间映射,速度快;
多项式核函数:SVM::POLY,gamma>0,coef(),degree;
径向基核函数: SVM::RBF,比较好的选择,gamma>0;
SIGMOD核函数:这个核让人想起神经网络和深度学习。。gamma,coef();

上面核中记录了SVM使用的参数;见SVMParams();
C++: CvSVMParams::CvSVMParams(int svm_type, int kernel_type, double degree, double gamma, double coef0, double Cvalue, double nu, double p, CvMat* class_weights, CvTermCriteria term_crit)

svm_type:SVM类型:
1、CvSVM::C_SVC : C类支撑向量分类机。 n类分组 (n≥2),容许用异常值处罚因子C进行不完全分类。
2、CvSVM::NU_SVC : 类支撑向量分类机。n类似然不完全分类的分类器。参数为gamma代替C。
3、CvSVM::ONE_CLASS : 单分类器,所有的练习数据提取自同一个类里,然后SVM建树了一个分界线以分别该类在特点空间中所占区域和其它类在特点空间中所占区域。
4、CvSVM::EPS_SVR : 用于回归。练习集中的特征向量和拟合出来的超平面的间隔须要小于p。异常值处罚因子C被采取。
5、CvSVM::NU_SVR : 回归机,gamma代替p
kernel_type:即上面所说的核函数类型;仔细看可以发现这两个参数类型都是int,所以除了填这些宏一样的名称外也可以直接填1,2,3.。。。
degree,gamma,coef0,都是特定核函数使用的参数,上面有总结;
Cvalue:SVM类型(C_SVC/ EPS_SVR/ NU_SVR)的参数C。
nu:SVM类型(NU_SVC/ ONE_CLASS/ NU_SVR)的参数 gamma;
p:SVM类型(EPS_SVR)的参数
class_weights:C_SVC中的可选权重,赋给指定的类,乘以C后变成 class_weights*C;
term_crit:SVM的迭代终止条件,可以指定的公差和最大迭代次数。
不设置时使用默认初始值初始化各参数。

SVM的训练函数有两个,训练的好坏直接影响学习结果,非常重要。
C++: bool CvSVM::train(const Mat& trainData, const Mat& responses, const Mat& varIdx=Mat(), const Mat& sampleIdx=Mat(), CvSVMParams params=CvSVMParams() )

C++: bool CvSVM::train_auto();
推荐第二个,因为能帮你优化参数啊!!!除非你自认调参能力出众可选第一个!

训练后就是预测了,SVM有以下三种形式:
C++: float CvSVM::predict(const Mat& sample, bool returnDFVal=false ) const
C++: float CvSVM::predict(const CvMat* sample, bool returnDFVal=false ) const
C++: float CvSVM::predict(const CvMat* samples, CvMat* results) const
sample: 须要预测的输入样本;samples: 须要预测的输入样本们,多个;
returnDFVal: 指定返回值类型。若值是true,则是一个2类分类问题;
results: 响应的样本输出猜测的响应;分类中返回的是标签类别号;

来看一下OPENCV文档的例子:

 1 void TestSVM1()
 2 {
 3      
 4     int width = 512, height = 512;
 5     Mat image = Mat::zeros(height, width, CV_8UC3);
 6 
 7     // set up training data
 8     float labels[4] = { 1.0, 1.0, -1.0, -1.0 };
 9     Mat labelsMat(4, 1, CV_32FC1, labels);
10 
11     float trainingData[4][2] = { { 501, 10 }, { 255, 10 }, { 501, 255 }, { 10, 501 } };
12     Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
13 
14     // set up SVM's parameters
15     CvSVMParams params;
16     params.svm_type = CvSVM::C_SVC;
17     params.kernel_type = CvSVM::LINEAR;
18     params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
19 
20     // train the svm
21     CvSVM SVM;
22     SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
23 
24     Vec3b green(0, 255, 0), blue(255, 0, 0);
25 
26     // show the decision region given by the SVM
27     for (int i = 0; i < image.rows; ++i)
28     {
29         for (int j = 0; j < image.cols; ++j)
30         {
31             Mat sampleMat = (Mat_<float>(1, 2) << i, j);
32 
33             // predict 函数使用训练好的SVM模型对一个输入的样本进行分类
34             float response = SVM.predict(sampleMat);
35 
36             if (response == 1)
37             {
38                 // 注意这里是(j,i),不是(i,j)
39                 image.at<Vec3b>(j, i) = green;
40             }
41             else
42             {
43                 // 同上
44                 image.at<Vec3b>(j, i) = blue;
45             }
46         }
47     }
48 
49     int thickness = -1;
50     int lineType = 8;
51 
52     circle(image, Point(501, 10), 5, Scalar(0, 0, 0), thickness, lineType);
53     circle(image, Point(255, 10), 5, Scalar(0, 0, 0), thickness, lineType);
54     circle(image, Point(501, 255), 5, Scalar(255, 255, 255), thickness, lineType);
55     circle(image, Point(10, 501), 5, Scalar(255, 255, 255), thickness, lineType);
56 
57     // show support vectors
58     thickness = 2;
59     lineType = 8;
60 
61     // 获得当前的支持向量的个数
62     int c = SVM.get_support_vector_count();
63 
64     for (int i = 0; i < c; ++i)
65     {
66         const float* v = SVM.get_support_vector(i);
67         circle(image, Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), thickness, lineType);
68     }
69 
70     //imwrite("result.png", image); // save the image
71 
72     imshow("SVM Simple Example", image); // show it to the user
73     waitKey(0);
74     return;
75 }

  看代码的label可以知道是一个二分类问题,输入为四个点,核函数也是线性核。

结果:

四个点每个类才两个点总觉得不够看啊,笔者增加了点数还是二分类,分界面看上去好多了。

  每个类6点,看上去分类更有规律了。

下次将介绍从文件训练模型,使用更复杂的SVM模型~~

posted @ 2017-10-13 17:40  巴尔扎克_S  阅读(16151)  评论(0编辑  收藏  举报