午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

【轉(zhuǎn)】 利用Open CV和SVM實(shí)現(xiàn)問題識(shí)別

 清風(fēng)明月0391 2013-12-24

【轉(zhuǎn)】 利用Open CV和SVM實(shí)現(xiàn)問題識(shí)別

轉(zhuǎn)自:http://www.cnblogs.com/justany/archive/2012/11/23/2784125.html

http://www.cnblogs.com/justany/archive/2012/11/26/2788509.html

http://www.cnblogs.com/justany/archive/2012/11/27/2789767.html

 

一、SVM介紹

分類器

分類器是一種計(jì)算機(jī)程序。

他的設(shè)計(jì)目標(biāo)是在通過學(xué)習(xí)后,可自動(dòng)將數(shù)據(jù)分到已知類別。

 

平面線性分類器

一個(gè)簡(jiǎn)單的分類問題,如圖有一些圓圈和一些正方形,如何找一條最優(yōu)的直線將他們分開?

A seperation example

我們可以找到很多種方法畫出這條直線,但怎樣的直線才是最優(yōu)的呢?

距離樣本太近的直線不是最優(yōu)的,因?yàn)檫@樣的直線對(duì)噪聲敏感度高,泛化性較差。 因此我們的目標(biāo)是找到一條直線,離最近的點(diǎn)距離最遠(yuǎn)。

怎么尋找距離最遠(yuǎn)的直線?枚舉所有直線,然后計(jì)算其樣本最小距離?這樣顯然不是一個(gè)好辦法,這將產(chǎn)生大量的計(jì)算開銷。

我們利用另一種方法,對(duì)直線的正負(fù)偏移量1,這樣就產(chǎn)生了一個(gè)區(qū)域(下圖的Maximum margin覆蓋的區(qū)域),區(qū)域邊界上的點(diǎn)到直線的距離是固定的,現(xiàn)在的問題是最近的點(diǎn)是否剛好在邊界上或者在邊界外。

The Optimal hyperplane

還記得點(diǎn)到線的公式么?

對(duì)于直線Ax+By+C=0,點(diǎn)(x0, y0)到直線的距離:

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

那么區(qū)域邊緣到直線的距離:

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

并需要滿足對(duì)于所有樣本類別y滿足:yi (Ax+By+C) > = 1,也就是所有樣本都不在該區(qū)域以內(nèi)。

于是我們可以找到適當(dāng)?shù)腁、B、C,從而得到:

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

 

超平面推廣

同理,我們將這一定理推廣到任意維度。其超平面表達(dá)式為:

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

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

其中 \beta 叫做 權(quán)重向量 ,  \beta_{0} 叫做 偏置向量。

用這種表達(dá)式來表達(dá)線Ax+By+C = 0的話,可以這么表示:

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

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

由于最優(yōu)超平面可以有很多種表達(dá)方式,我們定義:

    ββTx = 0,

為最優(yōu)超平面表達(dá)式。于是我們可以得到他的Maximum margin區(qū)域邊界表達(dá)式應(yīng)該為:

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

我們稱在這邊界上的點(diǎn)為:支持向量(Supper Vector)。

因?yàn)辄c(diǎn)到超平面距離公式為:

    \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求倒數(shù)1/M 則可將求最大轉(zhuǎn)換成求最小。于是有:

    \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} 表示樣本的類別標(biāo)記。

這是一個(gè)拉格朗日優(yōu)化問題,可以通過拉格朗日乘數(shù)法得到最優(yōu)超平面的權(quán)重向量 \beta 和偏置 \beta_{0} 。

 

什么是SVM

支持向量機(jī) (SVM) 是一個(gè)類分類器,正式的定義是一個(gè)能夠?qū)⒉煌悩颖驹跇颖究臻g分隔的超平面。 換句話說,給定一些標(biāo)記好的訓(xùn)練樣本 (監(jiān)督式學(xué)習(xí)),SVM算法輸出一個(gè)最優(yōu)化的分隔超平面。

1995年Cortes和Vapnik于首先提出SVM,它在解決小樣本、非線性及高維模式識(shí)別中表現(xiàn)出許多特有的優(yōu)勢(shì),并能夠推廣應(yīng)用到函數(shù)擬合等其他機(jī)器學(xué)習(xí)問題中。

 

使用SVM

復(fù)制代碼
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>

using namespace cv;

int main()
{
    // 用于保存可視化數(shù)據(jù)的矩陣
    int width = 512, height = 512;
    Mat image = Mat::zeros(height, width, CV_8UC3);

    // 創(chuàng)建一些訓(xùn)練樣本
    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);

    // 設(shè)置SVM參數(shù)
    CvSVMParams params;
    params.svm_type    = CvSVM::C_SVC;
    params.kernel_type = CvSVM::LINEAR;
    params.term_crit   = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);

    // 對(duì)SVM進(jìn)行訓(xùn)練
    CvSVM SVM;
    SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
    
    Vec3b green(0,255,0), blue (255,0,0);
    // 將SVM斷定的分劃區(qū)域繪制出來
    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;
        }

    // 繪制訓(xùn)練數(shù)據(jù)點(diǎn)
    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("簡(jiǎn)單SVM分類", image); 
    waitKey(0);

}
復(fù)制代碼

 

建立訓(xùn)練樣本

這里通過Mat構(gòu)造函數(shù),建立了一個(gè)簡(jiǎn)單的訓(xùn)練樣本。

復(fù)制代碼
//建立一個(gè)標(biāo)簽矩陣
float labels[4] = {1.0, -1.0, -1.0, -1.0};
Mat labelsMat(3, 1, CV_32FC1, labels);

//建立一個(gè)訓(xùn)練樣本矩陣
float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
Mat trainingDataMat(3, 2, CV_32FC1, trainingData);
復(fù)制代碼

由于CvSVM::train 要求樣本數(shù)據(jù)存儲(chǔ)在float 類型的Mat中,所以建立了float類型的Mat樣本。

 

設(shè)置SVM參數(shù)

struct CvSVMParams

SVM 訓(xùn)練參數(shù)結(jié)構(gòu)。

該結(jié)構(gòu)必須被初始化后,傳給CvSVM。

CvSVMParams::CvSVMParams

構(gòu)造函數(shù)

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)
參數(shù)
  • svm_type –

    指定SVM的類型,下面是可能的取值:

    • CvSVM::C_SVC C類支持向量分類機(jī)。 n類分組  (n \geq 2),允許用異常值懲罰因子C進(jìn)行不完全分類。
    • CvSVM::NU_SVC \nu類支持向量分類機(jī)。n類似然不完全分類的分類器。參數(shù)為 \nu 取代C(其值在區(qū)間【0,1】中,nu越大,決策邊界越平滑)。
    • CvSVM::ONE_CLASS 單分類器,所有的訓(xùn)練數(shù)據(jù)提取自同一個(gè)類里,然后SVM建立了一個(gè)分界線以分割該類在特征空間中所占區(qū)域和其它類在特征空間中所占區(qū)域。
    • CvSVM::EPS_SVR \epsilon類支持向量回歸機(jī)。訓(xùn)練集中的特征向量和擬合出來的超平面的距離需要小于p。異常值懲罰因子C被采用。
    • CvSVM::NU_SVR \nu類支持向量回歸機(jī)。 \nu 代替了 p

    可從 [LibSVM] 獲取更多細(xì)節(jié)。

  • kernel_type –

    SVM的內(nèi)核類型,下面是可能的取值:

    • CvSVM::LINEAR 線性內(nèi)核。沒有任何向映射至高維空間,線性區(qū)分(或回歸)在原始特征空間中被完成,這是最快的選擇。K(x_i, x_j) = x_i^T x_j.
    • CvSVM::POLY 多項(xiàng)式內(nèi)核: K(x_i, x_j) = (\gamma x_i^T x_j + coef0)^{degree}, \gamma > 0.
    • CvSVM::RBF 基于徑向的函數(shù),對(duì)于大多數(shù)情況都是一個(gè)較好的選擇: K(x_i, x_j) = e^{-\gamma ||x_i - x_j||^2}, \gamma > 0.
    • CvSVM::SIGMOID Sigmoid函數(shù)內(nèi)核:K(x_i, x_j) = \tanh(\gamma x_i^T x_j + coef0).
  • degree – 內(nèi)核函數(shù)(POLY)的參數(shù)degree。
  • gamma – 內(nèi)核函數(shù)(POLY/ RBF/ SIGMOID)的參數(shù)\gamma
  • coef0 – 內(nèi)核函數(shù)(POLY/ SIGMOID)的參數(shù)coef0。
  • Cvalue – SVM類型(C_SVC/ EPS_SVR/ NU_SVR)的參數(shù)C
  • nu – SVM類型(NU_SVC/ ONE_CLASS/ NU_SVR)的參數(shù) \nu。
  • p – SVM類型(EPS_SVR)的參數(shù) \epsilon。
  • class_weights – C_SVC中的可選權(quán)重,賦給指定的類,乘以C以后變成 class\_weights_i * C。所以這些權(quán)重影響不同類別的錯(cuò)誤分類懲罰項(xiàng)。權(quán)重越大,某一類別的誤分類數(shù)據(jù)的懲罰項(xiàng)就越大。
  • term_crit – SVM的迭代訓(xùn)練過程的中止條件,解決部分受約束二次最優(yōu)問題。您可以指定的公差和/或最大迭代次數(shù)。

默認(rèn)的構(gòu)造函數(shù)初始化有以下值:

復(fù)制代碼
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 );
}
復(fù)制代碼

 

OpenCV的SVM 

class CvSVM

向量支持機(jī)

CvSVM::CvSVM

訓(xùn)練構(gòu)造函數(shù)。

C++: CvSVM::CvSVM()
C++: CvSVM::CvSVM(const Mat& trainData, const Mat& responses, const Mat& varIdx=Mat(), const Mat& sampleIdx=Mat(), CvSVMParamsparams=CvSVMParams() )
C++: CvSVM::CvSVM(const CvMat* trainData, const CvMat* responses, const CvMat* varIdx=0, const CvMat* sampleIdx=0, CvSVMParamsparams=CvSVMParams() )
參數(shù)
  • trainData — 訓(xùn)練數(shù)據(jù),必須是CV_32FC1 (32位浮點(diǎn)類型,單通道)。數(shù)據(jù)必須是CV_ROW_SAMPLE的,即特征向量以行來存儲(chǔ)。
  • responses — 響應(yīng)數(shù)據(jù),通常是1D向量存儲(chǔ)在CV_32SC1 (僅僅用在分類問題上)或者CV_32FC1格式。
  • varIdx — 指定感興趣的特征??梢允钦麛?shù)(32sC1)向量,例如以0為開始的索引,或者8位(8uC1)的使用的特征或者樣本的掩碼。用戶也可以傳入NULL指針,用來表示訓(xùn)練中使用所有變量/樣本。
  • sampleIdx — 指定感興趣的樣本。描述同上。
  • params — SVM參數(shù)。

CvSVM::train

訓(xùn)練一個(gè)SVM。

C++: bool CvSVM::train(const Mat& trainData, const Mat& responses, const Mat& varIdx=Mat(), const Mat& sampleIdx=Mat(), CvSVMParamsparams=CvSVMParams() )
C++: bool CvSVM::train(const CvMat* trainData, const CvMat* responses, const CvMat* varIdx=0, const CvMat* sampleIdx=0, CvSVMParamsparams=CvSVMParams() )

參數(shù)參考構(gòu)造函數(shù)。

CvSVM::train_auto

根據(jù)可選參數(shù)訓(xùn)練一個(gè)SVM。

C++: bool CvSVM::train_auto(const Mat& trainData, const Mat& responses, const Mat& varIdx, const Mat& sampleIdx, CvSVMParamsparams, int k_fold=10, CvParamGrid Cgrid=CvSVM::get_default_grid(CvSVM::C), CvParamGridgammaGrid=CvSVM::get_default_grid(CvSVM::GAMMA), CvParamGrid pGrid=CvSVM::get_default_grid(CvSVM::P), CvParamGridnuGrid=CvSVM::get_default_grid(CvSVM::NU), CvParamGrid coeffGrid=CvSVM::get_default_grid(CvSVM::COEF), CvParamGriddegreeGrid=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), CvParamGridcoeffGrid=get_default_grid(CvSVM::COEF), CvParamGrid degreeGrid=get_default_grid(CvSVM::DEGREE), bool balanced=false )
參數(shù)
  • k_fold – 交叉驗(yàn)證參數(shù)。訓(xùn)練集被分成k_fold的自子集。其中一個(gè)子集是用來測(cè)試模型,其他子集則成為訓(xùn)練集。所以,SVM算法復(fù)雜度是執(zhí)行k_fold的次數(shù)。
  • *Grid – 對(duì)應(yīng)的SVM迭代網(wǎng)格參數(shù)。
  • balanced – 如果是true則這是一個(gè)2類分類問題。這將會(huì)創(chuàng)建更多的平衡交叉驗(yàn)證子集。

這個(gè)方法根據(jù)CvSVMParams中的最佳參數(shù)Cgammapnucoef0degree自動(dòng)訓(xùn)練SVM模型。參數(shù)被認(rèn)為是最佳的交叉驗(yàn)證,其測(cè)試集預(yù)估錯(cuò)誤最小。

如果沒有需要優(yōu)化的參數(shù),相應(yīng)的網(wǎng)格步驟應(yīng)該被設(shè)置為小于或等于1的值。例如,為了避免gamma的優(yōu)化,設(shè)置gamma_grid.step = 0,gamma_grid.min_val, gamma_grid.max_val 為任意數(shù)值。所以params.gamma 由gamma得出。

最后,如果參數(shù)優(yōu)化是必需的,但是相應(yīng)的網(wǎng)格卻不確定,你可能需要調(diào)用函數(shù)CvSVM::get_default_grid(),創(chuàng)建一個(gè)網(wǎng)格。例如,對(duì)于gamma,調(diào)用CvSVM::get_default_grid(CvSVM::GAMMA)。

該函數(shù)為分類運(yùn)行 (params.svm_type=CvSVM::C_SVC 或者 params.svm_type=CvSVM::NU_SVC) 和為回歸運(yùn)行 (params.svm_type=CvSVM::EPS_SVR 或者 params.svm_type=CvSVM::NU_SVR)效果一樣好。如果params.svm_type=CvSVM::ONE_CLASS,沒有優(yōu)化,并指定執(zhí)行一般的SVM。

CvSVM::predict

預(yù)測(cè)樣本的相應(yīng)數(shù)據(jù)。

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
參數(shù)
  • sample – 需要預(yù)測(cè)的輸入樣本。
  • samples – 需要預(yù)測(cè)的輸入樣本們。
  • returnDFVal – 指定返回值類型。如果值是true,則是一個(gè)2類分類問題,該方法返回的決策函數(shù)值是邊緣的符號(hào)距離。
  • results – 相應(yīng)的樣本輸出預(yù)測(cè)的響應(yīng)。

這個(gè)函數(shù)用來預(yù)測(cè)一個(gè)新樣本的響應(yīng)數(shù)據(jù)(response)。在分類問題中,這個(gè)函數(shù)返回類別編號(hào);在回歸問題中,返回函數(shù)值。輸入的樣本必須與傳給trainData的訓(xùn)練樣本同樣大小。如果訓(xùn)練中使用了varIdx參數(shù),一定記住在predict函數(shù)中使用跟訓(xùn)練特征一致的特征。

后綴const是說預(yù)測(cè)不會(huì)影響模型的內(nèi)部狀態(tài),所以這個(gè)函數(shù)可以很安全地從不同的線程調(diào)用。

CvSVM::get_default_grid

生成一個(gè)SVM網(wǎng)格參數(shù)。

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

    SVM參數(shù)的IDs必須是下列中的一個(gè):

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

    網(wǎng)格參數(shù)將根據(jù)這個(gè)ID生成。

CvSVM::get_params

返回當(dāng)前SVM的參數(shù)。

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

這個(gè)函數(shù)主要是在使用CvSVM::train_auto()時(shí)去獲得最佳參數(shù)。

CvSVM::get_support_vector

檢索一定數(shù)量的支持向量和特定的向量。

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

該方法可以用于檢索一組支持向量。

CvSVM::get_var_count

返回變量的個(gè)數(shù)。

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

 

分割結(jié)果

  • 程序創(chuàng)建了一張圖像,在其中顯示了訓(xùn)練樣本,其中一個(gè)類顯示為白色圓圈,另一個(gè)類顯示為黑色圓圈。
  • 訓(xùn)練得到SVM,并將圖像的每一個(gè)像素分類。 分類的結(jié)果將圖像分為藍(lán)綠兩部分,中間線就是最優(yōu)分割超平面。
  • 最后支持向量通過灰色邊框加重顯示。

The seperated planes

OpenCV的SVM是基于臺(tái)灣大學(xué)林智仁開發(fā)的LIBSVM開發(fā)包的。如果你還不過癮可以看看下面林智仁的演示程序(需要JAVA支持):

   http://www.csie./~cjlin/libsvm/

在這個(gè)實(shí)驗(yàn)中,我們成功讓機(jī)器找到了區(qū)分樣品的線性劃分,并將其支持向量顯示出來。

 

被山寨的原文

Introduction to Support Vector Machines . OpenCV.org

Support Vector Machines API . OpenCV.org

 

 

 

二、SVM線性不可分問題

目的

  • 實(shí)際事物模型中,并非所有東西都是線性可分的。
  • 需要尋找一種方法對(duì)線性不可分?jǐn)?shù)據(jù)進(jìn)行劃分。

 

原理

上一篇文章,我們推導(dǎo)出對(duì)于線性可分?jǐn)?shù)據(jù),最佳劃分超平面應(yīng)滿足:

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

現(xiàn)在我們想引入一些東西,來表示那些被錯(cuò)分的數(shù)據(jù)點(diǎn)(比如噪點(diǎn)),對(duì)劃分的影響。

如何來表示這些影響呢?

被錯(cuò)分的點(diǎn),離自己應(yīng)當(dāng)存在的區(qū)域越遠(yuǎn),就代表了,這個(gè)點(diǎn)“錯(cuò)”得越嚴(yán)重。

所以我們引入\xi_{i},為對(duì)應(yīng)樣本離同類區(qū)域的距離。

Samples misclassified and their distances to their correct regions

接下來的問題是,如何將這種錯(cuò)的程度,轉(zhuǎn)換為和原模型相同的度量呢?

我們?cè)僖胍粋€(gè)常量C,表示\xi_{i}和原模型度量的轉(zhuǎn)換關(guān)系,用C對(duì)\xi_{i}進(jìn)行加權(quán)和,來表征錯(cuò)分點(diǎn)對(duì)原模型的影響,這樣我們得到新的最優(yōu)化問題模型:

    \min_{\beta, \beta_{0}} L(\beta) = ||\beta||^{2} + C \sum_{i} {\xi_{i}} \text{ subject to } y_{i}(\beta^{T} x_{i} + \beta_{0}) \geq 1 - \xi_{i} \text{ and } \xi_{i} \geq 0 \text{ } \forall i

關(guān)于參數(shù)C的選擇, 明顯的取決于訓(xùn)練樣本的分布情況。 盡管并不存在一個(gè)普遍的答案,但是記住下面幾點(diǎn)規(guī)則還是有用的:

  • C比較大時(shí)分類錯(cuò)誤率較小,但是間隔也較小。 在這種情形下, 錯(cuò)分類對(duì)模型函數(shù)產(chǎn)生較大的影響,既然優(yōu)化的目的是為了最小化這個(gè)模型函數(shù),那么錯(cuò)分類的情形必然會(huì)受到抑制。
  • C比較小時(shí)間隔較大,但是分類錯(cuò)誤率也較大。 在這種情形下,模型函數(shù)中錯(cuò)分類之和這一項(xiàng)對(duì)優(yōu)化過程的影響變小,優(yōu)化過程將更加關(guān)注于尋找到一個(gè)能產(chǎn)生較大間隔的超平面。

 說白了,C的大小表征了,錯(cuò)分?jǐn)?shù)據(jù)對(duì)原模型的影響程度。于是C越大,優(yōu)化時(shí)越關(guān)注錯(cuò)分問題。反之越關(guān)注能否產(chǎn)生一個(gè)較大間隔的超平面。

 

開始使用

復(fù)制代碼
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>

#define NTRAINING_SAMPLES   100         // 每類訓(xùn)練樣本的數(shù)量
#define FRAC_LINEAR_SEP     0.9f        // 線性可分部分的樣本組成比例

using namespace cv;
using namespace std;

int main(){
    // 用于顯示的數(shù)據(jù)
    const int WIDTH = 512, HEIGHT = 512;
    Mat I = Mat::zeros(HEIGHT, WIDTH, CV_8UC3);

    /* 1. 隨即產(chǎn)生訓(xùn)練數(shù)據(jù) */
    Mat trainData(2*NTRAINING_SAMPLES, 2, CV_32FC1);
    Mat labels   (2*NTRAINING_SAMPLES, 1, CV_32FC1);
    
    RNG rng(100); // 生成隨即數(shù)

    // 設(shè)置線性可分的訓(xùn)練數(shù)據(jù)
    int nLinearSamples = (int) (FRAC_LINEAR_SEP * NTRAINING_SAMPLES);

    // 生成分類1的隨機(jī)點(diǎn)
    Mat trainClass = trainData.rowRange(0, nLinearSamples);
    // 點(diǎn)的x坐標(biāo)在[0, 0.4)之間
    Mat c = trainClass.colRange(0, 1);
    rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(0.4 * WIDTH));
    // 點(diǎn)的y坐標(biāo)在[0, 1)之間
    c = trainClass.colRange(1,2);
    rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT));

    // 生成分類2的隨機(jī)點(diǎn)
    trainClass = trainData.rowRange(2*NTRAINING_SAMPLES-nLinearSamples, 2*NTRAINING_SAMPLES);
    // 點(diǎn)的x坐標(biāo)在[0.6, 1]之間
    c = trainClass.colRange(0 , 1); 
    rng.fill(c, RNG::UNIFORM, Scalar(0.6*WIDTH), Scalar(WIDTH));
    // 點(diǎn)的y坐標(biāo)在[0, 1)之間
    c = trainClass.colRange(1,2);
    rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT));

    /* 設(shè)置非線性可分的訓(xùn)練數(shù)據(jù) */

    // 生成分類1和分類2的隨機(jī)點(diǎn)
    trainClass = trainData.rowRange(  nLinearSamples, 2*NTRAINING_SAMPLES-nLinearSamples);
    // 點(diǎn)的x坐標(biāo)在[0.4, 0.6)之間
    c = trainClass.colRange(0,1);
    rng.fill(c, RNG::UNIFORM, Scalar(0.4*WIDTH), Scalar(0.6*WIDTH)); 
    // 點(diǎn)的y坐標(biāo)在[0, 1)之間
    c = trainClass.colRange(1,2);
    rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT));
    
    /* 設(shè)置分類標(biāo)簽 */
    labels.rowRange(                0,   NTRAINING_SAMPLES).setTo(1);  // Class 1
    labels.rowRange(NTRAINING_SAMPLES, 2*NTRAINING_SAMPLES).setTo(2);  // Class 2

    /* 設(shè)置支持向量機(jī)參數(shù) */
    CvSVMParams params;
    params.svm_type    = SVM::C_SVC;
    params.C           = 0.1;
    params.kernel_type = SVM::LINEAR;
    params.term_crit   = TermCriteria(CV_TERMCRIT_ITER, (int)1e7, 1e-6);

    /* 3. 訓(xùn)練支持向量機(jī) */
    cout << "Starting training process" << endl;
    CvSVM svm;
    svm.train(trainData, labels, Mat(), Mat(), params);
    cout << "Finished training process" << endl;
    
    /* 4. 顯示劃分區(qū)域 */
    Vec3b green(0,100,0), blue (100,0,0);
    for (int i = 0; i < I.rows; ++i)
        for (int j = 0; j < I.cols; ++j){
            Mat sampleMat = (Mat_<float>(1,2) << i, j);
            float response = svm.predict(sampleMat);

            if      (response == 1)    I.at<Vec3b>(j, i)  = green;
            else if (response == 2)    I.at<Vec3b>(j, i)  = blue;
        }

    /* 5. 顯示訓(xùn)練數(shù)據(jù) */
    int thick = -1;
    int lineType = 8;
    float px, py;
    // 分類1
    for (int i = 0; i < NTRAINING_SAMPLES; ++i){
        px = trainData.at<float>(i,0);
        py = trainData.at<float>(i,1);
        circle(I, Point( (int) px,  (int) py ), 3, Scalar(0, 255, 0), thick, lineType);
    }
    // 分類2
    for (int i = NTRAINING_SAMPLES; i <2*NTRAINING_SAMPLES; ++i){
        px = trainData.at<float>(i,0);
        py = trainData.at<float>(i,1);
        circle(I, Point( (int) px, (int) py ), 3, Scalar(255, 0, 0), thick, lineType);
    }

    /* 6. 顯示支持向量 */
    thick = 2;
    lineType  = 8;
    int x     = svm.get_support_vector_count();

    for (int i = 0; i < x; ++i)
    {
        const float* v = svm.get_support_vector(i);
        circle( I,  Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thick, lineType);
    }

    imwrite("result.png", I);                      // 保存圖片
    imshow("SVM線性不可分?jǐn)?shù)據(jù)劃分", I); // 顯示給用戶
    waitKey(0);
}
復(fù)制代碼

 

設(shè)置SVM參數(shù)

這里的參數(shù)設(shè)置可以參考一下上一篇文章的API。

CvSVMParams params;
params.svm_type    = SVM::C_SVC;
params.C              = 0.1;
params.kernel_type = SVM::LINEAR;
params.term_crit   = TermCriteria(CV_TERMCRIT_ITER, (int)1e7, 1e-6);

 可以看到,這次使用的是C類支持向量分類機(jī)。其參數(shù)C的值為0.1。

 

 結(jié)果

  • 程序創(chuàng)建了一張圖像,在其中顯示了訓(xùn)練樣本,其中一個(gè)類顯示為淺綠色圓圈,另一個(gè)類顯示為淺藍(lán)色圓圈。
  • 訓(xùn)練得到SVM,并將圖像的每一個(gè)像素分類。 分類的結(jié)果將圖像分為藍(lán)綠兩部分,中間線就是最優(yōu)分割超平面。由于樣本非線性可分, 自然就有一些被錯(cuò)分類的樣本。 一些綠色點(diǎn)被劃分到藍(lán)色區(qū)域, 一些藍(lán)色點(diǎn)被劃分到綠色區(qū)域。
  • 最后支持向量通過灰色邊框加重顯示。

Training data and decision regions given by the SVM

 

被山寨的原文

Support Vector Machines for Non-Linearly Separable Data . OpenCV.org

 

 

 

三、SVM文字識(shí)別

預(yù)備知識(shí)

下面兩個(gè)都不是必備知識(shí),但是如果你想了解更多內(nèi)容,可參考這兩篇文章。

OpenCV 2.4+ C++ SVM介紹

OpenCV 2.4+ C++ SVM線性不可分處理

 

SVM劃分的意義

到此,我們已經(jīng)對(duì)SVM有一定的了解了??墒沁@有什么用呢?回到上一篇文章結(jié)果圖:

Training data and decision regions given by the SVM

這個(gè)結(jié)果圖的意義在于,他成功從二維劃分了分類的區(qū)域。于是如果以后,有一個(gè)新的樣本在綠色區(qū)域,那么我們就可以把他當(dāng)成是綠色的點(diǎn)。

由于這可以像更高維度推廣,所以如果我們能對(duì)樣品映射成高維度空間的點(diǎn),當(dāng)有足夠多的樣品時(shí),我們同樣可以找到一個(gè)高維度的超平面劃分,使得同一類樣品的映射點(diǎn)在同一區(qū)域,于是當(dāng)有新樣品落在這些區(qū)域是,我們可以把它當(dāng)成是這一類型的樣本。

 

通俗一點(diǎn)

可能我們能更加通俗一點(diǎn)。比如我們來識(shí)別男性和女性。

我們發(fā)現(xiàn)男性和女性可能頭發(fā)長(zhǎng)度不一樣,可能胸圍不一樣,于是我們對(duì)樣本個(gè)體產(chǎn)生這樣的一種映射:

    人 —> (頭發(fā)長(zhǎng)度, 胸圍)

于是我們將每個(gè)樣品映射到二維平面,其中“頭發(fā)的長(zhǎng)度”和“胸圍的長(zhǎng)度”分別是x軸和y軸。我們把這些樣品丟給SVM學(xué)習(xí),則他會(huì)尋找出一個(gè)合理的x和y的區(qū)域來劃分男性和女性。

當(dāng)然,也有可能有些男的頭發(fā)比女的還長(zhǎng),有的男性的胸圍比女性還大,這些就是錯(cuò)分點(diǎn),它們也影響著劃分。

最后,當(dāng)我們把一個(gè)人映射到這個(gè)二維空間時(shí),SVM就可以根據(jù)以往的學(xué)習(xí),猜一猜這個(gè)人到底是什么性別。

我們學(xué)到了什么呢?

好吧,特征要找準(zhǔn)一點(diǎn),否則可能遇到下面的悲劇……

如果這是老板,你就可死翹翹了……

 

簡(jiǎn)單的文字識(shí)別

當(dāng)然計(jì)算機(jī)沒那么厲害能看出你的胸圍或者頭發(fā)長(zhǎng)短。他需要一些他能讀懂的東西,特別計(jì)算機(jī)通?!翱吹健钡氖窍旅娴倪@種東東……

A matrix of the mirror of a car

我們需要對(duì)文字找到他的特征,來映射到高維空間。

還記得小學(xué)時(shí)候練字的米字格么?這似乎暗示了我們,雖然每個(gè)人寫的字千差萬(wàn)別,但是他們卻具有一定的特點(diǎn)。

我們嘗試這樣做,取一個(gè)字,選取一個(gè)包含該字的正方形區(qū)域,將這個(gè)正方形區(qū)域分割成8*8個(gè)小格,統(tǒng)計(jì)每個(gè)小格中像素的數(shù)量,以這些數(shù)量為維度進(jìn)行映射。

OK,明白了原理讓我們開始吧。

 

樣本獲取

由于通常文字樣本都是白底黑字的,而手寫也可以直接獲取寫入的數(shù)據(jù)而無視背景,所以我們并不需要對(duì)樣本進(jìn)行提取,但我們需要對(duì)他定位,并弄成合適的大小。

比如,你沒法避免有人這么寫字……

坑爹啊,好好的那么大地方你就躲在左上角……

 

開始定位

復(fù)制代碼
void getROI(Mat& src, Mat& dst){
    int left, right, top, bottom;
    left = src.cols;
    right = 0;
    top = src.rows;
    bottom = 0;

    //得到區(qū)域
    for(int i=0; i<src.rows; i++)
    {
        for(int j=0; j<src.cols; j++)
        {
            if(src.at<uchar>(i, j) > 0)
            {
                if(j<left) left = j;
                if(j>right) right = j;
                if(i<top) top = i;
                if(i>bottom) bottom = i;
            }
        }
    }

    int width = right - left;
    int height = bottom - top;

    //創(chuàng)建存儲(chǔ)矩陣
    dst = Mat::zeros(width, height, CV_8UC1);

    Rect dstRect(left, top, width, height);
    dst(dstRect);
}
復(fù)制代碼

這段代碼通過遍歷所有圖像矩陣的元素,來獲取該樣本的定位和大小。并把樣本提取出來。

 

重新縮放

Mat dst = Mat::zeros(8, 8, CV_8UC1);
resize(src, dst, dst.size());

進(jìn)行縮放,把所有樣本變成8*8的大小。為了簡(jiǎn)便,我們把像素多少變成了像素的灰度值。

resize的API:

調(diào)整圖片大小

C++: void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )
參數(shù)
  • src – 輸入圖像。
  • dst – 輸出圖像;它有一個(gè)dsize (當(dāng)其不為0時(shí)) 或者這個(gè)size由 src.size(),fxfy算出。dst的類型和src相同。
  • dsize –

    輸出圖像的大小,如果取值為0,則:

    \texttt{dsize = Size(round(fx*src.cols), round(fy*src.rows))}

    dsize或者fxfy必須有一種大小決定方法不為0。

  • fx –

    水平軸縮放因子,當(dāng)取值為0時(shí),則為:

    \texttt{(double)dsize.width/src.cols}

  • fy –

    垂直軸縮放因子,當(dāng)取值為0時(shí),則為:

    \texttt{(double)dsize.height/src.rows}

  • interpolation –

    插值方法

    • INTER_NEAREST - 最近鄰值插入方法。
    • INTER_LINEAR - 雙線性插值(默認(rèn)方式)。
    • INTER_AREA - 使用象素關(guān)系重采樣。當(dāng)圖像縮小時(shí)候,該方法可以避免波紋出現(xiàn)。當(dāng)圖像放大時(shí),類似于 CV_INTER_NN 方法。
    • INTER_CUBIC - 立方插值。
    • INTER_LANCZOS4 - 8x8的Lanczos插入方法。

 

準(zhǔn)備樣本數(shù)據(jù)

復(fù)制代碼
Mat data = Mat::zeros(total, 64, CV_32FC1);    //樣本數(shù)據(jù)矩陣  
Mat res = Mat::zeros(total, 1, CV_32SC1);    //樣本標(biāo)簽矩陣

res.at<double>(k, 1) = label;    //對(duì)第k個(gè)樣本添加分類標(biāo)簽

//對(duì)第k個(gè)樣本添加數(shù)據(jù)
for(int i = 0; i<8; i++)  {  
    for(int j = 0; j<8; j++)  {  
        res.at<double>(k, i * 8 + j) = dst.at<double>(i, j);  
    }  
}
復(fù)制代碼

將剛剛的結(jié)果,輸入樣本,并加上標(biāo)簽。

 

訓(xùn)練

復(fù)制代碼
CvSVM svm = CvSVM();   
CvSVMParams param;   
CvTermCriteria criteria;  
  
criteria= cvTermCriteria(CV_TERMCRIT_EPS, 1000,  FLT_EPSILON);   
param= CvSVMParams(CvSVM::C_SVC, CvSVM::RBF, 10.0, 8.0, 1.0, 10.0, 0.5, 0.1, NULL, criteria);   
  
svm.train(data, res, Mat(), Mat(), param);  
svm.save( "SVM_DATA.xml" );  
復(fù)制代碼

開始訓(xùn)練并保存訓(xùn)練數(shù)據(jù)。

 

使用

CvSVM svm = CvSVM();   
svm.load( "SVM_DATA.xml" );
svm.predict(m);        //對(duì)樣本向量m檢測(cè)

 

 參考資料

 使用OPENCV訓(xùn)練手寫數(shù)字識(shí)別分類器 . firefight . 2011-05-28

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多