OpenCV 2.4+ C++ SVM線性不可分處理

目的 html

  • 實際事物模型中,並不是全部東西都是線性可分的。
  • 須要尋找一種方法對線性不可分數據進行劃分。

 

原理 ios

上一篇文章,咱們推導出對於線性可分數據,最佳劃分超平面應知足: 函數

    \min_{\beta, \beta_{0}} L(\beta) = \frac{1}{2}||\beta||^{2} \text{ subject to } y_{i}(\beta^{T} x_{i} + \beta_{0}) \geq 1 \text{ } \forall i,

如今咱們想引入一些東西,來表示那些被錯分的數據點(好比噪點),對劃分的影響。 優化

如何來表示這些影響呢? ui

被錯分的點,離本身應當存在的區域越遠,就表明了,這個點「錯」得越嚴重。 spa

因此咱們引入\xi_{i},爲對應樣本離同類區域的距離。 .net

Samples misclassified and their distances to their correct regions

接下來的問題是,如何將這種錯的程度,轉換爲和原模型相同的度量呢? code

咱們再引入一個常量C,表示\xi_{i}和原模型度量的轉換關係,用C對\xi_{i}進行加權和,來表徵錯分點對原模型的影響,這樣咱們獲得新的最優化問題模型: htm

    \min_{\beta, \beta_{0}} L(\beta) = ||\beta||^{2} + C \sum_{i} {\xi_{i}} \text{ subject to } y_{i}(\beta^{T} x_{i} + \beta_{0}) \geq 1 - \xi_{i} \text{ and } \xi_{i} \geq 0 \text{ } \forall i

關於參數C的選擇, 明顯的取決於訓練樣本的分佈狀況。 儘管並不存在一個廣泛的答案,可是記住下面幾點規則仍是有用的: blog

  • C比較大時分類錯誤率較小,可是間隔也較小。 在這種情形下, 錯分類對模型函數產生較大的影響,既然優化的目的是爲了最小化這個模型函數,那麼錯分類的情形必然會受到抑制。
  • C比較小時間隔較大,可是分類錯誤率也較大。 在這種情形下,模型函數中錯分類之和這一項對優化過程的影響變小,優化過程將更加關注於尋找到一個能產生較大間隔的超平面。

 說白了,C的大小表徵了,錯分數據對原模型的影響程度。因而C越大,優化時越關注錯分問題。反之越關注可否產生一個較大間隔的超平面。

 

開始使用

複製代碼
#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/ml/ml.hpp> #define NTRAINING_SAMPLES   100 // 每類訓練樣本的數量 #define FRAC_LINEAR_SEP     0.9f // 線性可分部分的樣本組成比例 using namespace cv; using namespace std; int main(){ // 用於顯示的數據 const int WIDTH = 512, HEIGHT = 512; Mat I = Mat::zeros(HEIGHT, WIDTH, CV_8UC3); /* 1. 隨即產生訓練數據 */ Mat trainData(2*NTRAINING_SAMPLES, 2, CV_32FC1); Mat labels (2*NTRAINING_SAMPLES, 1, CV_32FC1); RNG rng(100); // 生成隨即數 // 設置線性可分的訓練數據 int nLinearSamples = (int) (FRAC_LINEAR_SEP * NTRAINING_SAMPLES); // 生成分類1的隨機點 Mat trainClass = trainData.rowRange(0, nLinearSamples); // 點的x座標在[0, 0.4)之間 Mat c = trainClass.colRange(0, 1); rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(0.4 * WIDTH)); // 點的y座標在[0, 1)之間 c = trainClass.colRange(1,2); rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); // 生成分類2的隨機點 trainClass = trainData.rowRange(2*NTRAINING_SAMPLES-nLinearSamples, 2*NTRAINING_SAMPLES); // 點的x座標在[0.6, 1]之間 c = trainClass.colRange(0 , 1); rng.fill(c, RNG::UNIFORM, Scalar(0.6*WIDTH), Scalar(WIDTH)); // 點的y座標在[0, 1)之間 c = trainClass.colRange(1,2); rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); /* 設置非線性可分的訓練數據 */ // 生成分類1和分類2的隨機點 trainClass = trainData.rowRange(  nLinearSamples, 2*NTRAINING_SAMPLES-nLinearSamples); // 點的x座標在[0.4, 0.6)之間 c = trainClass.colRange(0,1); rng.fill(c, RNG::UNIFORM, Scalar(0.4*WIDTH), Scalar(0.6*WIDTH)); // 點的y座標在[0, 1)之間 c = trainClass.colRange(1,2); rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); /* 設置分類標籤 */ labels.rowRange( 0,   NTRAINING_SAMPLES).setTo(1); // Class 1 labels.rowRange(NTRAINING_SAMPLES, 2*NTRAINING_SAMPLES).setTo(2); // Class 2 /* 設置支持向量機參數 */ CvSVMParams params; params.svm_type    = SVM::C_SVC; params.C           = 0.1; params.kernel_type = SVM::LINEAR; params.term_crit   = TermCriteria(CV_TERMCRIT_ITER, (int)1e7, 1e-6); /* 3. 訓練支持向量機 */ cout << "Starting training process" << endl; CvSVM svm; svm.train(trainData, labels, Mat(), Mat(), params); cout << "Finished training process" << endl; /* 4. 顯示劃分區域 */ Vec3b green(0,100,0), blue (100,0,0); for (int i = 0; i < I.rows; ++i) for (int j = 0; j < I.cols; ++j){ Mat sampleMat = (Mat_<float>(1,2) << i, j); float response = svm.predict(sampleMat); if (response == 1)    I.at<Vec3b>(j, i)  = green; else if (response == 2)    I.at<Vec3b>(j, i)  = blue; } /* 5. 顯示訓練數據 */ int thick = -1; int lineType = 8; float px, py; // 分類1 for (int i = 0; i < NTRAINING_SAMPLES; ++i){ px = trainData.at<float>(i,0); py = trainData.at<float>(i,1); circle(I, Point( (int) px,  (int) py ), 3, Scalar(0, 255, 0), thick, lineType); } // 分類2 for (int i = NTRAINING_SAMPLES; i <2*NTRAINING_SAMPLES; ++i){ px = trainData.at<float>(i,0); py = trainData.at<float>(i,1); circle(I, Point( (int) px, (int) py ), 3, Scalar(255, 0, 0), thick, lineType); } /* 6. 顯示支持向量 */ thick = 2; lineType = 8; int x     = svm.get_support_vector_count(); for (int i = 0; i < x; ++i) { const float* v = svm.get_support_vector(i); circle( I, Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thick, lineType); } imwrite("result.png", I); // 保存圖片 imshow("SVM線性不可分數據劃分", I); // 顯示給用戶 waitKey(0); }
複製代碼

 

設置SVM參數

這裏的參數設置能夠參考一下上一篇文章的API。

CvSVMParams params; params.svm_type = SVM::C_SVC; params.C = 0.1; params.kernel_type = SVM::LINEAR; params.term_crit = TermCriteria(CV_TERMCRIT_ITER, (int)1e7, 1e-6);

 能夠看到,此次使用的是C類支持向量分類機。其參數C的值爲0.1。

 

 結果

  • 程序建立了一張圖像,在其中顯示了訓練樣本,其中一個類顯示爲淺綠色圓圈,另外一個類顯示爲淺藍色圓圈。
  • 訓練獲得SVM,並將圖像的每個像素分類。 分類的結果將圖像分爲藍綠兩部分,中間線就是最優分割超平面。因爲樣本非線性可分, 天然就有一些被錯分類的樣本。 一些綠色點被劃分到藍色區域, 一些藍色點被劃分到綠色區域。
  • 最後支持向量經過灰色邊框加劇顯示。

Training data and decision regions given by the SVM

 

被山寨的原文

Support Vector Machines for Non-Linearly Separable Data . OpenCV.org

相關文章
相關標籤/搜索