OPENCV SVM介紹和自帶例子

weixin_30314631發表於2017-10-13

依據機器學習演算法如何學習資料可分為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模型~~

轉載於:https://www.cnblogs.com/chenzhefan/p/7662315.html

相關文章