OpenCV 之 支持向量機 (一)


  機器學習是由 模型 + 策略 + 算法 構成的,構建一種機器學習方法 (例如,支持向量機),就是具體去確定這三個要素。

1  支持向量機

  支持向量機,簡稱 SVM (Support Vector Machine),是一種二分分類模型。

1) 模型 (model)

    定義在特征空間上的,一種間隔 (margin) 最大的,線性分類器 (linear classifier)

2) 策略 (strategy)

    使間隔最大化,可轉化為求解凸二次規划的問題。

3) 算法 (algorithm)

    求解凸二次規划的最優化算法。

  供訓練的樣本數據可分為三類:第一類是線性可分的,第二類是近似線性可分的,第三類是線性不可分的。

  三種樣本數據對應的 SVM 分別為:線性可分 (硬間隔最大化),線性 (軟間隔最大化),非線性 (核技巧 + 軟間隔最大化)。

  為了方便起見,下文提到的向量機 或 SVM,都是指線性可分支持向量機

 

2  基本概念

2.1  超平面 (hyperplane)

  n 維歐式空間中,余維度等於 1 (也即 n-1 維) 的線性子空間,稱為超平面。

  超平面在二維空間中是直線,在三維空間中是平面,可用來分隔數據。如下圖所示,超平面 (直線) 能將兩類不同的數據 (圓點和方點) 分隔開來。

  如果將數據點記為 x (n 維向量),則超平面的方程為 $\ f(x) = \beta_{0} + \beta^{T} x = 0\; $,其中,$\beta $ 為權重向量 (有的書稱為 “法向量”)

                   

  解釋:右圖中 $\beta^{*}$ 為超平面 (綠色直線) 的單位法向量 $\ \beta^{*} = \dfrac{\beta}{||\beta||}$,平面中任意點 x 到超平面的距離為 $\ r = \dfrac{|\beta_{0} + \beta^{T} x|} {||\beta||}$

  又附: 平面坐標中,一個點 $\;(x_{0}, y_{0})\;$到直線$\;(Ax + By + C = 0)\;$ 的距離為 $\; d = \dfrac{Ax_{0} + By_{0} + C}{\sqrt{A^{2} + B^{2}}} $

2.2  支持向量 (support vector)

  如果取輸出 y 分別為 +1 和 -1,代表兩種不同類別,則對於 x,其對應的 f(x) 有三種可能取值:

  1) 當位於超平面上時 (也即圖中的直線上),$ f(x) = \beta_{0} + \beta^{T} x = 0 $

  2) 當位於超平面左邊時, $f(x) = \beta_{0} + \beta^{T} x \leq -1$

  3) 當位於超平面右邊時, $f(x) = \beta_{0} + \beta^{T} x \geq +1$

  假設存在一個超平面,能將 n 個樣本數據正確的分類,則對於任意一個樣本數據$\;(x_{i}, y_{i})$,滿足如下約束條件

  $\quad y_{i}(\beta^{T} x_{i} + \beta_{0}) \geq 1 , i = 1, 2, ..., n $

        

  如上圖所示,距離超平面最近的三個樣本點,使得 2) 和 3) 中的等號成立,它們稱為 “支持向量”

2.3  幾何間隔 (geometric margin)

  因為支持向量使得 2) 和 3) 的等號成立,所以它們到超平面的距離:

$\quad r = \dfrac{|\beta_{0} + \beta^{T} x|} {||\beta||} = \dfrac{1}{||\beta||}$

  兩個不同種類的支持向量 (分別取值為 +1 和 -1),到超平面的距離之和為:

$\quad r^{'} = \dfrac{2}{||\beta||}\;$,$r^{'}\;$稱為 “幾何間隔” (geometric margin)

  一個點距離超平面的遠近,可用來表示分類結果的正確性和確信度。

  直觀上看,超平面越是靠近兩類樣本數據的正中間 (也即兩類數據點到超平面的距離越遠),則分類結果的正確性和確信度就越高。

   

2.4  學習算法

  SVM 的學習算法 (或稱最大間隔法),就是基於所給的樣本數據,去尋找到具有 “最大間隔” 的超平面,將不同種類的樣本分隔開來。

  也即,在滿足 “約束條件” 的前提下,使得 $r^{'}$ 的值最大:

  $\quad \max \limits_{\beta,\; \beta_{0}} \dfrac{2}{||\beta||} \quad subject\;to \quad y_{i}(\beta^{T} x_{i} + \beta_{0}) \geq 1 , i = 1, 2, ..., n $

  再或者,最大化 $r^{'}$,等價於最小化 $||\beta||^{2}$,如下所示:

  $\quad \min \limits_{\beta,\;\beta_{0}} \dfrac{1}{2} ||\beta||^{2} \quad subject \; to \quad y_{i} (\beta^{T} x_{i} + \beta_{0}) \geq 1 , i = 1, 2, ..., n $

 

3  OpenCV 函數

  OpenCV 中 SVM 的實現是基於 libsvm 的,其基本的過程為:創建 SVM 模型 --> 設置相關參數 --> 樣本數據訓練 --> 預測

1) 創建模型

static Ptr<SVM> cv::ml::SVM::create ( );  // 創建一個空模型

 2) 設置參數

virtual void cv::ml::SVM::setType (int val);  // 設置 SVM 的類型,默認為 SVM::C_SVC 
virtual void cv::ml::SVM::setKernel (int kernelType); // 設置核函數類型,本文為線性核函數,設為 SVM::LINEAR

virtual void cv::ml::SVM::setTermCriteria (const cv::TermCriteria & val); // 設置迭代終止准則

// type,准則類型; maxCount,最大迭代次數;epsilo,目標精度
cv::TermCriteria::TermCriteria(int type, int maxCount, double epsilon);

3) 訓練 (train)

virtual bool cv::ml::StatModel::train (
   InputArray  samples,   // 訓練樣本
    int        layout,   // 訓練樣本為 “行樣本” ROW_SAMPLE 或 “列樣本” COL_SAMPLE
    InputArray    responses // 對應樣本數據的分類結果
)     

4) 預測 (predict)

  用來預測一個新樣本的響應,各個參數如下:

// samples,輸入的樣本書數據;results,輸出矩陣,默認不輸出;flags,標識,默認為 0

virtual float cv::ml::StatModel::predict(InputArray samples, OutputArray results=noArray(),int flags=0) const;  

 

4  代碼示例

  下面是 OpenCV 3.2 中的官方例程,更改了訓練樣本數據

#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() { // 512 x 512 零矩陣 int width = 512, height = 512; Mat image = Mat::zeros(height, width, CV_8UC3); // 訓練樣本 float trainingData[6][2] = { { 500, 60 },{ 245, 40 },{ 480, 250 },{ 160, 380 },{400, 25},{55, 400} }; int labels[6] = {-1, 1, 1, 1,-1,1}; // 每個樣本數據對應的輸出,因為是二分模型,所以輸出為 +1 或者 -1 Mat trainingDataMat(6, 2, CV_32FC1, trainingData); Mat labelsMat(6, 1, CV_32SC1, labels); // 訓練 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->train(trainingDataMat, ROW_SAMPLE, labelsMat); // 顯示二分分類的結果 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); if (response == 1) image.at<Vec3b>(i, j) = blue; else if (response == -1) image.at<Vec3b>(i, j) = green; }
// 畫出訓練樣本數據 int thickness = -1; int lineType = 8; circle(image, Point(500, 60), 5, Scalar(0, 0, 0), thickness, lineType); circle(image, Point(245, 40), 5, Scalar(255, 255, 255), thickness, lineType); circle(image, Point(480, 250), 5, Scalar(255, 255, 255), thickness, lineType); circle(image, Point(160, 380), 5, Scalar(0, 0, 255), thickness, lineType); circle(image, Point(400, 25), 5, Scalar(255, 255, 255), thickness, lineType); circle(image, Point(55, 400), 5, Scalar(0, 0, 255), thickness, lineType);
    // 顯示出支持向量
    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); // 保存訓練的結果 imshow("SVM Simple Example", image); waitKey(0); }

  OpenCV 3.2 版本中使用了一個新的函數,來獲取支持向量,即 getUncompressedSupportVectors()

  而 OpenCV 3.0 中,獲取支持向量的函數為 getSupportVectors(),但當內核設為 SVM::LINEAR 時,該函數並不能得到支持向量,這是 3.0 版本的缺陷。

  運行結果如下圖所示,超平面附近的三個灰色匡白色圓點,便是所謂的 “支持向量”。

     

   

參考資料:

  <機器學習> 周志軍  第6章

  <統計學習方法> 李航  第7章

  <The Elements of Statistical Learning_2nd>  ch 4.5 , ch 12

  "支持向量機系列“  pluskid

  OpenCV 3.2  Tutorials -- Machine Learning (ml module)  -- Introduction to Support Vector Machines

  “LIBSVM -- A Library for Support Vector Machines”

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM