目的
- 實際事物模型中,並非所有東西都是線性可分的。
- 需要尋找一種方法對線性不可分數據進行划分。
原理
上一篇文章,我們推導出對於線性可分數據,最佳划分超平面應滿足:
現在我們想引入一些東西,來表示那些被錯分的數據點(比如噪點),對划分的影響。
如何來表示這些影響呢?
被錯分的點,離自己應當存在的區域越遠,就代表了,這個點“錯”得越嚴重。
所以我們引入,為對應樣本離同類區域的距離。
接下來的問題是,如何將這種錯的程度,轉換為和原模型相同的度量呢?
我們再引入一個常量C,表示和原模型度量的轉換關系,用C對
進行加權和,來表征錯分點對原模型的影響,這樣我們得到新的最優化問題模型:
關於參數C的選擇, 明顯的取決於訓練樣本的分布情況。 盡管並不存在一個普遍的答案,但是記住下面幾點規則還是有用的:
- 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,並將圖像的每一個像素分類。 分類的結果將圖像分為藍綠兩部分,中間線就是最優分割超平面。由於樣本非線性可分, 自然就有一些被錯分類的樣本。 一些綠色點被划分到藍色區域, 一些藍色點被划分到綠色區域。
- 最后支持向量通過灰色邊框加重顯示。
被山寨的原文
Support Vector Machines for Non-Linearly Separable Data . OpenCV.org