Opencv SVM demo
轉載自http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html
支持向量機 (SVM) 是一個類分類器,正式的定義是一個能夠將不同類樣本在樣本空間分隔的超平面。 換句話說,給定一些標記(label)好的訓練樣本 (監督式學習), SVM算法輸出一個最優化的分隔超平面。
如何來界定一個超平面是不是最優的呢? 考慮如下問題:
假設給定一些分屬于兩類的2維點,這些點可以通過直線分割, 我們要找到一條最優的分割線.Note
在這個示例中,我們考慮卡迪爾平面內的點與線,而不是高維的向量與超平面。 這一簡化是為了讓我們以更加直覺的方式建立起對SVM概念的理解, 但是其基本的原理同樣適用于更高維的樣本分類情形。
在上面的圖中, 你可以直覺的觀察到有多種可能的直線可以將樣本分開。 那是不是某條直線比其他的更加合適呢? 我們可以憑直覺來定義一條評價直線好壞的標準:
距離樣本太近的直線不是最優的,因為這樣的直線對噪聲敏感度高,泛化性較差。 因此我們的目標是找到一條直線,離所有點的距離最遠。由此, SVM算法的實質是找出一個能夠將某個值最大化的超平面,這個值就是超平面離所有訓練樣本的最小距離。這個最小距離用SVM術語來說叫做 間隔(margin) 。 概括一下,最優分割超平面 最大化 訓練數據的間隔。
如何計算最優超平面??
下面的公式定義了超平面的表達式:
叫做 權重向量 , 叫做 偏置(bias) 。
See also
關于超平面的更加詳細的說明可以參考T. Hastie, R. Tibshirani 和 J. H. Friedman的書籍 Elements of Statistical Learning , section 4.5 (Seperating Hyperplanes)。
最優超平面可以有無數種表達方式,即通過任意的縮放 和 。 習慣上我們使用以下方式來表達最優超平面
式中 表示離超平面最近的那些點。 這些點被稱為 支持向量**。 該超平面也稱為 **canonical 超平面.
通過幾何學的知識,我們知道點 到超平面 的距離為:
特別的,對于 canonical 超平面, 表達式中的分子為1,因此支持向量到canonical 超平面的距離是
剛才我們介紹了間隔(margin),這里表示為 , 它的取值是最近距離的2倍:
最后最大化 轉化為在附加限制條件下最小化函數 。 限制條件隱含超平面將所有訓練樣本 正確分類的條件,
式中 表示樣本的類別標記。
這是一個拉格朗日優化問題,可以通過拉格朗日乘數法得到最優超平面的權重向量 和偏置 。
源碼?
| 123456789 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/ml/ml.hpp>using namespace cv;int main() {// Data for visual representationint width = 512, height = 512;Mat image = Mat::zeros(height, width, CV_8UC3);// Set up training datafloat labels[4] = {1.0, -1.0, -1.0, -1.0};Mat labelsMat(3, 1, CV_32FC1, labels);float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };Mat trainingDataMat(3, 2, CV_32FC1, trainingData);// Set up SVM's parametersCvSVMParams params;params.svm_type = CvSVM::C_SVC;params.kernel_type = CvSVM::LINEAR;params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);// Train the SVMCvSVM SVM;SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);Vec3b green(0,255,0), blue (255,0,0);// Show the decision regions given by the SVMfor (int i = 0; i < image.rows; ++i)for (int j = 0; j < image.cols; ++j){Mat sampleMat = (Mat_<float>(1,2) << i,j);float response = SVM.predict(sampleMat);if (response == 1)image.at<Vec3b>(j, i) = green;else if (response == -1) image.at<Vec3b>(j, i) = blue;}// Show the training dataint thickness = -1;int lineType = 8;circle( image, Point(501, 10), 5, Scalar( 0, 0, 0), thickness, lineType);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);// Show support vectorsthickness = 2;lineType = 8;int c = SVM.get_support_vector_count();for (int i = 0; i < c; ++i){const float* v = SVM.get_support_vector(i);circle( image, Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thickness, lineType);}imwrite("result.png", image); // save the image imshow("SVM Simple Example", image); // show it to the userwaitKey(0);} |
解釋?
本例中的訓練樣本由分屬于兩個類別的2維點組成, 其中一類包含一個樣本點,另一類包含三個點。
float labels[4] = {1.0, -1.0, -1.0, -1.0}; float trainingData[4][2] = {{501, 10}, {255, 10}, {501, 255}, {10, 501}};函數 CvSVM::train 要求訓練數據儲存于float類型的 Mat 結構中, 因此我們定義了以下矩陣:
Mat trainingDataMat(3, 2, CV_32FC1, trainingData); Mat labelsMat (3, 1, CV_32FC1, labels);設置SVM參數
此教程中,我們以可線性分割的分屬兩類的訓練樣本簡單講解了SVM的基本原理。 然而,SVM的實際應用情形可能復雜得多 (比如非線性分割數據問題,SVM核函數的選擇問題等等)。 總而言之,我們需要在訓練之前對SVM做一些參數設定。 這些參數保存在類 CvSVMParams 中。
CvSVMParams params; params.svm_type = CvSVM::C_SVC; params.kernel_type = CvSVM::LINEAR; params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);-
SVM類型. 這里我們選擇了 CvSVM::C_SVC 類型,該類型可以用于n-類分類問題 (n 2)。 這個參數定義在 CvSVMParams.svm_type 屬性中.
Note
CvSVM::C_SVC 類型的重要特征是它可以處理非完美分類的問題 (及訓練數據不可以完全的線性分割)。在本例中這一特征的意義并不大,因為我們的數據是可以線性分割的,我們這里選擇它是因為它是最常被使用的SVM類型。
-
SVM 核類型. 我們沒有討論核函數,因為對于本例的樣本,核函數的討論沒有必要。然而,有必要簡單說一下核函數背后的主要思想, 核函數的目的是為了將訓練樣本映射到更有利于可線性分割的樣本集。 映射的結果是增加了樣本向量的維度,這一過程通過核函數完成。 此處我們選擇的核函數類型是 CvSVM::LINEAR 表示不需要進行映射。 該參數由 CvSVMParams.kernel_type 屬性定義。
-
算法終止條件. SVM訓練的過程就是一個通過 迭代 方式解決約束條件下的二次優化問題,這里我們指定一個最大迭代次數和容許誤差,以允許算法在適當的條件下停止計算。 該參數定義在 cvTermCriteria 結構中。
訓練支持向量機
調用函數 CvSVM::train 來建立SVM模型。
CvSVM SVM; SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);SVM區域分割
函數 CvSVM::predict 通過重建訓練完畢的支持向量機來將輸入的樣本分類。 本例中我們通過該函數給向量空間著色, 及將圖像中的每個像素當作卡迪爾平面上的一點,每一點的著色取決于SVM對該點的分類類別:綠色表示標記為1的點,藍色表示標記為-1的點。
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) << i,j);float response = SVM.predict(sampleMat);if (response == 1)image.at<Vec3b>(j, i) = green;elseif (response == -1)image.at<Vec3b>(j, i) = blue;}支持向量
這里用了幾個函數來獲取支持向量的信息。 函數 CvSVM::get_support_vector_count 輸出支持向量的數量,函數 CvSVM::get_support_vector 根據輸入支持向量的索引來獲取指定位置的支持向量。 通過這一方法我們找到訓練樣本的支持向量并突出顯示它們。
int c = SVM.get_support_vector_count();for (int i = 0; i < c; ++i) { const float* v = SVM.get_support_vector(i); // get and then highlight with grayscale circle( image, Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thickness, lineType); }結果?
- 程序創建了一張圖像,在其中顯示了訓練樣本,其中一個類顯示為白色圓圈,另一個類顯示為黑色圓圈。
- 訓練得到SVM,并將圖像的每一個像素分類。 分類的結果將圖像分為藍綠兩部分,中間線就是最優分割超平面。
- 最后支持向量通過灰色邊框加重顯示。
總結
以上是生活随笔為你收集整理的Opencv SVM demo的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CMake编译opencv
- 下一篇: 华为hilink怎么用