OpenCV 之 支持向量機 (一)

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

1  支持向量機

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

1) 模型 (model)

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

2) 策略 (strategy)

    使間隔最大化,可轉化爲求解凸二次規劃的問題。函數

3) 算法 (algorithm)

    求解凸二次規劃的最優化算法。學習

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

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

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

 

2  基本概念

2.1  超平面 (hyperplane)

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

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

  若是將數據點記爲 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」

相關文章
相關標籤/搜索