OpenCV 2.4+ C++ SVM介紹

分類器 html

分類器是一種計算機程序。 算法

他的設計目標是在經過學習後,可自動將數據分到已知類別。 安全

 

平面線性分類器 機器學習

一個簡單的分類問題,如圖有一些圓圈和一些正方形,如何找一條最優的直線將他們分開? 函數

A seperation example

咱們能夠找到不少種方法畫出這條直線,但怎樣的直線纔是最優的呢? 學習

距離樣本太近的直線不是最優的,由於這樣的直線對噪聲敏感度高,泛化性較差。 所以咱們的目標是找到一條直線,離最近的點距離最遠。 測試

怎麼尋找距離最遠的直線?枚舉全部直線,而後計算其樣本最小距離?這樣顯然不是一個好辦法,這將產生大量的計算開銷。 優化

咱們利用另外一種方法,對直線的正負偏移量1,這樣就產生了一個區域(下圖的Maximum margin覆蓋的區域),區域邊界上的點到直線的距離是固定的,如今的問題是最近的點是否恰好在邊界上或者在邊界外。 ui

The Optimal hyperplane

還記得點到線的公式麼? spa

對於直線Ax+By+C=0,點(x0, y0)到直線的距離:

  distance = |Ax0+By0+C| / (A2 + B2)1/2

那麼區域邊緣到直線的距離:

  distance = (|Ax+By+C| + 1)/ (A2 + B2)1/2 = 1/ (A2 + B2)1/2

並須要知足對於全部樣本類別y知足:yi (Ax+By+C) > = 1,也就是全部樣本都不在該區域之內。

因而咱們能夠找到適當的A、B、C,從而獲得:

  Maximum margin = 2/ (A2 + B2)1/2

 

超平面推廣

同理,咱們將這必定理推廣到任意維度。其超平面表達式爲:

    f(x) = \beta_{0} + \beta^{T} x,

一維是線、二維是面、三維是體……四維呢?五維呢?好吧統稱超平面吧……

其中 \beta 叫作 權重向量 ,  \beta_{0} 叫作 偏置向量。

用這種表達式來表達線Ax+By+C = 0的話,能夠這麼表示:

    f(x) = (C, 0) + (A, B)T (x, y);

其中(C, 0) 是偏置向量 \beta_{0},(A, B)是權重向量 \beta

因爲最優超平面能夠有不少種表達方式,咱們定義:

    ββTx = 0,

爲最優超平面表達式。因而咱們能夠獲得他的Maximum margin區域邊界表達式應該爲:

    |\beta_{0} + \beta^{T} x| = 1

咱們稱在這邊界上的點爲:支持向量(Supper Vector)。

由於點到超平面距離公式爲:

    \mathrm{distance} = \frac{|\beta_{0} + \beta^{T} x|}{||\beta||}.

在邊界上,即支持向量到超平面距離:

    \mathrm{distance}_{\text{ support vectors}} = \frac{|\beta_{0} + \beta^{T} x|}{||\beta||} = \frac{1}{||\beta||}.

因此Maximum margin爲兩倍距離,即:

    M = \frac{2}{||\beta||}

M求倒數1/M 則可將求最大轉換成求最小。因而有:

    \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,

其中 y_{i} 表示樣本的類別標記。

這是一個拉格朗日優化問題,能夠經過拉格朗日乘數法獲得最優超平面的權重向量 \beta 和偏置 \beta_{0} 。

 

什麼是SVM

支持向量機 (SVM) 是一個類分類器,正式的定義是一個可以將不一樣類樣本在樣本空間分隔的超平面。 換句話說,給定一些標記好的訓練樣本 (監督式學習),SVM算法輸出一個最優化的分隔超平面。

1995年Cortes和Vapnik於首先提出SVM,它在解決小樣本、非線性及高維模式識別中表現出許多特有的優點,並可以推廣應用到函數擬合等其餘機器學習問題中。

 

使用SVM

複製代碼
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/ml/ml.hpp> using namespace cv; int main() { // 用於保存可視化數據的矩陣 int width = 512, height = 512; Mat image = Mat::zeros(height, width, CV_8UC3); // 建立一些訓練樣本 float 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); // 設置SVM參數 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 SVM; SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params); Vec3b green(0,255,0), blue (255,0,0); // 將SVM判定的分劃區域繪製出來 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; else if (response == -1) image.at<Vec3b>(j, i)  = blue; } // 繪製訓練數據點 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); // 繪製支持向量 thickness = 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);  imshow("簡單SVM分類", image); 
    waitKey(0); }
複製代碼

 

創建訓練樣本

這裏經過Mat構造函數,創建了一個簡單的訓練樣本。

//創建一個標籤矩陣 float 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);

因爲CvSVM::train 要求樣本數據存儲在float 類型的Mat中,因此創建了float類型的Mat樣本。

 

設置SVM參數

struct CvSVMParams

SVM 訓練參數結構。

該結構必須被初始化後,傳給CvSVM。

CvSVMParams::CvSVMParams

構造函數

C++: CvSVMParams::CvSVMParams() 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的類型,下面是可能的取值:

    • CvSVM::C_SVC C類支持向量分類機。n類分組  (n \geq 2),容許用異常值懲罰因子C進行不徹底分類。
    • CvSVM::NU_SVC \nu類支持向量分類機。n相似然不徹底分類的分類器。參數爲 \nu 取代C(其值在區間【0,1】中,nu越大,決策邊界越平滑)。
    • CvSVM::ONE_CLASS 單分類器,全部的訓練數據提取自同一個類裏,而後SVM創建了一個分界線以分割該類在特徵空間中所佔區域和其它類在特徵空間中所佔區域。
    • CvSVM::EPS_SVR \epsilon類支持向量迴歸機。訓練集中的特徵向量和擬合出來的超平面的距離須要小於p。異常值懲罰因子C被採用。
    • CvSVM::NU_SVR \nu類支持向量迴歸機。 \nu 代替了 p。

    可從 [LibSVM] 獲取更多細節。

  • kernel_type –

    SVM的內核類型,下面是可能的取值:

    • CvSVM::LINEAR 線性內核。沒有任何向映射至高維空間,線性區分(或迴歸)在原始特徵空間中被完成,這是最快的選擇。K(x_i, x_j) = x_i^T x_j.
    • CvSVM::POLY 多項式內核: K(x_i, x_j) = (\gamma x_i^T x_j + coef0)^{degree}, \gamma > 0.
    • CvSVM::RBF 基於徑向的函數,對於大多數狀況都是一個較好的選擇: K(x_i, x_j) = e^{-\gamma ||x_i - x_j||^2}, \gamma > 0.
    • CvSVM::SIGMOID Sigmoid函數內核:K(x_i, x_j) = \tanh(\gamma x_i^T x_j + coef0).
  • degree – 內核函數(POLY)的參數degree。
  • gamma – 內核函數(POLY/ RBF/ SIGMOID)的參數\gamma
  • coef0 – 內核函數(POLY/ SIGMOID)的參數coef0。
  • Cvalue – SVM類型(C_SVC/ EPS_SVR/ NU_SVR)的參數C。
  • nu – SVM類型(NU_SVC/ ONE_CLASS/ NU_SVR)的參數 \nu
  • p – SVM類型(EPS_SVR)的參數 \epsilon
  • class_weights – C_SVC中的可選權重,賦給指定的類,乘以C之後變成 class\_weights_i * C。因此這些權重影響不一樣類別的錯誤分類懲罰項。權重越大,某一類別的誤分類數據的懲罰項就越大。
  • term_crit – SVM的迭代訓練過程的停止條件,解決部分受約束二次最優問題。您能夠指定的公差和/或最大迭代次數。

默認的構造函數初始化有如下值:

CvSVMParams::CvSVMParams() : svm_type(CvSVM::C_SVC), kernel_type(CvSVM::RBF), degree(0), gamma(1), coef0(0), C(1), nu(0), p(0), class_weights(0) { term_crit = cvTermCriteria( CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000, FLT_EPSILON ); }

 

OpenCV的SVM 

class CvSVM

向量支持機

CvSVM::CvSVM

訓練構造函數。

C++: CvSVM::CvSVM() C++: CvSVM::CvSVM(const Mat&  trainData, const Mat&  responses, const Mat&  varIdx=Mat(), const Mat&  sampleIdx=Mat(), CvSVMParams params=CvSVMParams() ) C++: CvSVM::CvSVM(const CvMat*  trainData, const CvMat*  responses, const CvMat*  varIdx=0, const CvMat*  sampleIdx=0, CvSVMParams params=CvSVMParams() )
參數
  • trainData — 訓練數據,必須是CV_32FC1 (32位浮點類型,單通道)。數據必須是CV_ROW_SAMPLE的,即特徵向量以行來存儲。
  • responses — 響應數據,一般是1D向量存儲在CV_32SC1 (僅僅用在分類問題上)或者CV_32FC1格式。
  • varIdx — 指定感興趣的特徵。能夠是整數(32sC1)向量,例如以0爲開始的索引,或者8位(8uC1)的使用的特徵或者樣本的掩碼。用戶也能夠傳入NULL指針,用來表示訓練中使用全部變量/樣本。
  • sampleIdx — 指定感興趣的樣本。描述同上。
  • params — SVM參數。

CvSVM::train

訓練一個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(const CvMat*  trainData, const CvMat*  responses, const CvMat*  varIdx=0, const CvMat*  sampleIdx=0, CvSVMParams params=CvSVMParams() )

參數參考構造函數。

CvSVM::train_auto

根據可選參數訓練一個SVM。

C++: bool CvSVM::train_auto(const Mat&  trainData, const Mat&  responses, const Mat&  varIdx, const Mat&  sampleIdx, CvSVMParams params, int  k_fold=10, CvParamGrid  Cgrid=CvSVM::get_default_grid(CvSVM::C), CvParamGrid gammaGrid=CvSVM::get_default_grid(CvSVM::GAMMA), CvParamGrid  pGrid=CvSVM::get_default_grid(CvSVM::P), CvParamGrid nuGrid=CvSVM::get_default_grid(CvSVM::NU), CvParamGrid  coeffGrid=CvSVM::get_default_grid(CvSVM::COEF), CvParamGrid degreeGrid=CvSVM::get_default_grid(CvSVM::DEGREE), bool  balanced=false) C++: bool CvSVM::train_auto(const CvMat*  trainData, const CvMat*  responses, const CvMat*  varIdx, const CvMat*  sampleIdx, CvSVMParams  params, int  kfold=10, CvParamGrid  Cgrid=get_default_grid(CvSVM::C), CvParamGrid  gammaGrid=get_default_grid(CvSVM::GAMMA), CvParamGrid  pGrid=get_default_grid(CvSVM::P), CvParamGrid  nuGrid=get_default_grid(CvSVM::NU), CvParamGrid coeffGrid=get_default_grid(CvSVM::COEF), CvParamGrid  degreeGrid=get_default_grid(CvSVM::DEGREE), bool  balanced=false )
參數
  • k_fold – 交叉驗證參數。訓練集被分紅k_fold的自子集。其中一個子集是用來測試模型,其餘子集則成爲訓練集。因此,SVM算法複雜度是執行k_fold的次數。
  • *Grid – 對應的SVM迭代網格參數。
  • balanced – 若是是true則這是一個2類分類問題。這將會建立更多的平衡交叉驗證子集。

這個方法根據CvSVMParams中的最佳參數C, gamma, p, nu, coef0, degree自動訓練SVM模型。參數被認爲是最佳的交叉驗證,其測試集預估錯誤最小。

若是沒有須要優化的參數,相應的網格步驟應該被設置爲小於或等於1的值。例如,爲了不gamma的優化,設置gamma_grid.step = 0,gamma_grid.min_val, gamma_grid.max_val 爲任意數值。因此params.gamma 由gamma得出。

最後,若是參數優化是必需的,可是相應的網格卻不肯定,你可能須要調用函數CvSVM::get_default_grid(),建立一個網格。例如,對於gamma,調用CvSVM::get_default_grid(CvSVM::GAMMA)。

該函數爲分類運行 (params.svm_type=CvSVM::C_SVC 或者 params.svm_type=CvSVM::NU_SVC) 和爲迴歸運行 (params.svm_type=CvSVM::EPS_SVR 或者 params.svm_type=CvSVM::NU_SVR)效果同樣好。若是params.svm_type=CvSVM::ONE_CLASS,沒有優化,並指定執行通常的SVM。

CvSVM::predict

預測樣本的相應數據。

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 – 相應的樣本輸出預測的響應。

這個函數用來預測一個新樣本的響應數據(response)。在分類問題中,這個函數返回類別編號;在迴歸問題中,返回函數值。輸入的樣本必須與傳給trainData的訓練樣本一樣大小。若是訓練中使用了varIdx參數,必定記住在predict函數中使用跟訓練特徵一致的特徵。

後綴const是說預測不會影響模型的內部狀態,因此這個函數能夠很安全地從不一樣的線程調用。

CvSVM::get_default_grid

生成一個SVM網格參數。

C++: CvParamGrid CvSVM::get_default_grid(int  param_id)
參數
  • param_id –

    SVM參數的IDs必須是下列中的一個:

    • CvSVM::C
    • CvSVM::GAMMA
    • CvSVM::P
    • CvSVM::NU
    • CvSVM::COEF
    • CvSVM::DEGREE

    網格參數將根據這個ID生成。

CvSVM::get_params

返回當前SVM的參數。

C++: CvSVMParams CvSVM::get_params() const

這個函數主要是在使用CvSVM::train_auto()時去得到最佳參數。

CvSVM::get_support_vector

檢索必定數量的支持向量和特定的向量。

C++: int CvSVM::get_support_vector_count() const C++: const float* CvSVM::get_support_vector(int  i) const
參數 i – 指定支持向量的索引。

該方法能夠用於檢索一組支持向量。

CvSVM::get_var_count

返回變量的個數。

C++: int CvSVM::get_var_count() const

 

分割結果

  • 程序建立了一張圖像,在其中顯示了訓練樣本,其中一個類顯示爲白色圓圈,另外一個類顯示爲黑色圓圈。
  • 訓練獲得SVM,並將圖像的每個像素分類。 分類的結果將圖像分爲藍綠兩部分,中間線就是最優分割超平面。
  • 最後支持向量經過灰色邊框加劇顯示。

The seperated planes

OpenCV的SVM是基於臺灣大學林智仁開發的LIBSVM開發包的。若是你還不過癮能夠看看下面林智仁的演示程序(須要JAVA支持):

   http://www.csie.ntu.edu.tw/~cjlin/libsvm/

在這個實驗中,咱們成功讓機器找到了區分樣品的線性劃分,並將其支持向量顯示出來。

 

被山寨的原文

Introduction to Support Vector Machines . OpenCV.org

Support Vector Machines API . OpenCV.org

相關文章
相關標籤/搜索