1. 程式人生 > >SVM(支援向量機)原理

SVM(支援向量機)原理

SVM原理

SVM主要就是找出一個能夠將某個值最大化的超平面,這個值就是超平面離所有訓練樣本的最小距離。

這個超平面可以用f(x)來定義:

f(x)=β0+βTx,

β 叫做 權重向量,β0叫做 偏置(bias) 。(所以就是他的權重向量的轉置乘以xx是離超平面最近的那些點)

最優超平面可以有無數種表達方式,即通過任意的縮放 ββ0。 習慣上我們使用以下方式來表達最優超平面

|β0+ βTx| = 1

通過幾何學的知識,我們知道點 x 到超平面(β,β0) 的距離為:
distance=|β0+βTx|/|β||. (我們的目的就是讓這個distance最大化)

在distance最大化之前我們還要做一些變形,對於超平面, 表示式中的分子為1,因此支援向量到canonical 超平面的距離是:
distance support vectors=|β0+βTx|/|β||=1/|β||.

(變成了一個只有β的表示式)

間隔(margin),這裡表示為 M, 它的取值是最近距離的2倍:
M=2/|β||(最後最大化 M 轉化為在附加限制條件下最小化函式 L(β) )

限制條件隱含超平面將所有訓練樣本 xi 正確分類的條件如下:
minβ,β0L(β)=12||β||2 subject to yi(βTxi+β0)≥1 ∀i,
式中 yi表示樣本的類別標記。
這是一個拉格朗日優化問題,可以通過拉格朗日乘數法得到最優超平面的權重向量 β 和偏置 β0

程式碼解析

以下是opencv官網提供的原始碼,我們對他進行一下解析:

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include "opencv2/imgcodecs.hpp"
#include <opencv2/highgui.hpp> #include <opencv2/ml.hpp> using namespace cv; using namespace cv::ml; int main(int, char**) { // Data for visual representation int width = 512, height = 512; Mat image = Mat::zeros(height, width, CV_8UC3); // Set up training data int labels[4
] = {1, -1, -1, -1};//設定四個訓練資料的標籤 float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };//設定四個訓練資料的座標 Mat trainingDataMat(4, 2, CV_32FC1, trainingData);//利用Mat矩陣儲存訓練座標 Mat labelsMat(4, 1, CV_32SC1, labels);//利用Mat矩陣儲存標籤,4行1列 // Train the SVM Ptr<SVM> svm = SVM::create(); svm->setType(SVM::C_SVC);//SVM引數配置 後面會有介紹 svm->setKernel(SVM::LINEAR); svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6)); svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);對資料進行分類 // Show the decision regions given by the SVM 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) << j,i); float response = svm->predict(sampleMat);//去識別Mat屬於哪個標籤 if (response == 1)//標籤在1的範圍內為綠色 image.at<Vec3b>(i,j) = green; else if (response == -1)//標籤是別為-1為藍色 image.at<Vec3b>(i,j) = blue; } // Show the training data int 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 vectors thickness = 2; lineType = 8; Mat sv = svm->getUncompressedSupportVectors(); for (int i = 0; i < sv.rows; ++i) { const float* v = sv.ptr<float>(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 user waitKey(0); }

SVM引數的設定:

    Ptr<SVM> svm = SVM::create();
    svm->setType(SVM::C_SVC);
    svm->setKernel(SVM::LINEAR);
    svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));

SVM型別: 這裡我們選擇了 SVM::C_SVC 型別,該型別可以用於n-類分類問題 (n \geq 2)。該型別的重要特徵是它可以處理非完美分類的問題 (及訓練資料不可以完全的線性分割)。在本例中這一特徵的意義並不大,因為我們的資料是可以線性分割的,我們這裡選擇它是因為它是最常被使用的SVM型別。
SVM 核型別: 核函式的目的是為了將訓練樣本對映到更有利於可線性分割的樣本集。 對映的結果是增加了樣本向量的維度,這一過程通過核函式完成。 此處我們選擇的核函式型別是SVM::LINEAR 表示不需要進行對映。
演算法終止條件: SVM訓練的過程就是一個通過 迭代 方式解決約束條件下的二次優化問題,這裡我們指定一個最大迭代次數和容許誤差,以允許演算法在適當的條件下停止計算。
以下是引數配置的參考連結:
https://docs.opencv.org/trunk/d1/d2d/classcv_1_1ml_1_1SVM.html#ab4b93a4c42bbe213ffd9fb3832c6c44f

大致對SVM的原理就梳理到這裡,後續還會跟著自己的學習不斷的去完善。