SVM:從理論到OpenCV實踐

(轉載請註明出處:http://blog.csdn.net/zhazhiqiang/ 未經容許請勿用於商業用途)html

 
1、理論
參考網友的博客:
 
總結:
一、SVM是一個分類器(Classifier) ,也能夠作迴歸 (Regression) 。
二、 SVM的主要思想能夠歸納爲兩點
(1)它是針對線性可分狀況進行分析,對於線性不可分的狀況,經過使用非線性映射算法將低維輸入空間線性不可分的樣本轉化爲高維特徵空間使其線性可分,從而 使得高維特徵空間採用線性算法對樣本的非線性特徵進行線性分析成爲可能;
(2)它基於結構風險最小化理論之上在特徵空間中建構最優分割超平面,使得學習器獲得全局最優化,而且在整個樣本空間的指望風險以某個機率知足必定上界。
三、 最優 超平面:使得每一類數據與超平面距離最近的向量與超平面之間的距離最大的這樣的平面。
四、 支持向量:那些在間隔區邊緣的訓練樣本點。
五、 核函數:SVM的關鍵在於核函數。簡單說就是將低維空間線性不可分的樣本轉化爲高維空間線性可分的。低維空間向量集一般難於劃分,解決的方法是將它們映射到高維空間。但這個辦法帶來的困難就是計算複雜度的增長,而核函數正好巧妙地解決了這個問題。也就是說,只要選用適當的核函數,就能夠獲得高維空間的分類函數。在SVM理論中,採用不一樣的核函數將致使不一樣的SVM算法。在肯定了核函數以後,因爲肯定核函數的已知數據也存在必定的偏差,考慮到推廣性問題,所以引入了 鬆弛係數以及 懲罰係數兩個參變量來加以校訂。在肯定了核函數基礎上,再通過大量對比實驗等將這兩個係數取定,該項研究就基本完成,適合相關學科或業務內應用,且有必定能力的推廣性。固然偏差是絕對的,不一樣學科、不一樣專業的要求不一。
經常使用的核函數有如下4種:
線性核函數
 
多項式核函數
徑向基(RBF)核函數(高斯核函數)
 
Sigmoid核函數(二層神經網絡核函數)
<5-1> 徑向基(RBF)核函數(高斯核函數) 的說明

    這個核函數能夠將原始空間映射到無窮維空間。對於參數σ,若是選的很大,高次特徵上的權重實際上衰減得很是快,因此實際上(數值上近似一下)至關於一個低維的子空間;反過來,若是選得很小,則能夠將任意的數據映射爲線性可分——固然,這並不必定是好事,由於隨之而來的多是很是嚴重的過擬合問題。不過,總的來講,經過調控參數,高斯覈實際上具備至關高的靈活性,也是使用最普遍的核函數之一。算法

<5-2> 徑向基(RBF)核函數的參數選取

    徑向基(RBF)核函數主要肯定懲罰因子C參數σ。其中C控制着使間隔margin最大且錯誤率最小的折中,就是在肯定的特徵空間中調節學習機器的置信範圍和經驗風險的比例;而σ2RBF核函數參數,主要影響樣本數據在高維特徵空間中分佈的複雜程度。所以分類器的好壞取決於參數C、σ的肯定。參數選擇的好壞直接影響到分類器性能的好壞,但這方面目前缺少理論指導,沒有合適的方法,傳統的參數選取都是經過反覆的試驗,人工選取使人滿意的解。這種方法須要人的經驗指導,而且須要付出較高的時間代價。經常使用的參數選擇方法有:安全

I、網格法【OpenCV中SVM用到】網絡

    選取UCV個σ2,就會有的組合狀態,每種組合狀態對應一種SVM分類器,經過測試對比,找出推廣識別率最高的C和σ2組合。通常取U=V=15C取值分別爲取值分別爲255C、σ2組合。網格法實質上是一種窮舉法,隨着排列組合的可能狀況越多,其運算量將急劇增長。函數

II、雙線性法性能

利用RBFSVM的性能,首先對線性SVM求解最佳參數,使之爲參數的線性SVM推廣識別率最高,稱爲;而後固定,對知足學習

                                                       

,訓練SVM,根據對其推廣識別率的估算,獲得最優參數。雖然這種方法對σ2有很是明確的公式,但首先要求解C,而很難肯定最優的C測試

III、梯度降低搜索法優化

設泛化偏差爲url

                                                                  

核函數爲是待定的核參數,基本過程爲:

將θ置一個初始值

用一個標準的SVM解法(如SMO),求出SVM的解——Lagrange乘子

                                                                                               

跳轉到b直至T最小

    其中是足夠小且最終收斂到零的數列。步驟c是一個標準的梯度降低算法。由分類函數公式能夠求解的求解較麻煩,須要經過求解一個二次規劃問題獲得。

IV、遺傳算法

基本步驟爲:

       a t=0

       b 隨機選擇初始種羣P(t)

       c 計算個體適應度函數值F(t)

       d 若種羣中最優個體所對應的適應度函數值足夠大或者算法已經連續運行多代,且個體的最佳適應度無明顯改進則轉到第h

       e t=t+1

       f 應用選擇算子法從P(t-1)中選擇P(t)

       g P(t)進行交叉、變異操做,轉到第c

       h 給出最佳的核函數參合和懲罰因子C,並用其訓練數據集以得到全局最優分類面。

       遺傳算法的缺點是收斂很慢,容易受局部極小值干擾。

<5-3>驗證核函數性能的方法(3種)(衡量泛化能力)

I、單一驗證估計

    將大數量的樣本分爲兩部分:訓練樣本和測試樣本,此時測試集的錯誤率爲:

    

式中,p爲樣本數,爲樣本實際所屬的類別,爲對訓練樣本預測出的類別。這種方法直觀簡單。能夠經過理論證實,當樣本數量趨近於無窮大時,該估計爲無偏估計,但現實中處理的老是數量有限的樣本問題,因此此方法的應用範圍在必定程度上受到了限制。

II、K折交叉驗證【OpenCV中SVM用到】

    K折交叉驗證是一種迭代方式,一共迭代K次,每次將全部訓練樣本分爲K份相等的子集樣本,訓練樣本是選擇其中K-1份樣本,測試樣本是剩餘的一個樣本。經過K次迭代後,能夠利用平均值來評估指望泛化偏差,根據指望泛化偏差選擇一組性能最佳的參數。K折交叉驗證由K折交叉驗證偏差決定,K折交叉驗證偏差是算法錯誤率的估計,其計算方式爲:假設爲錯分類的樣本個數,通過K次迭代後,獲得,那麼算法的錯誤率能夠近似爲錯誤分類數和總樣本點數之比。該方法具備操做簡單的優勢,成爲目前應用最普遍的方法,可是這種方法容易受樣本劃分方式的影響。

III、留一法

    留一法是K折交叉驗證的特例,其基本思想是當可用樣本數爲N時,訓練集由其中N-1個樣本構成,測試樣本爲剩餘的一個樣本,經N次重複,使全部的樣本都參加過測試。經過理論證實,這種估計是無偏估計。所以,從實現原理來講,留一法的效果是最佳的;可是,在參數固定的狀況下,肯定其錯誤率對樣本要訓練N-1次,運算量很大。爲了解決留一法計算量大的缺陷,目前該方法肯定核函數及其參數的經常使用方法是估計經驗風險的上界,只要上界小,分類器的推廣能力就強。

 

2、OpenCV中SVM的參數和函數說明
一、訓練參數結構體 CvSVMParams(可參考 【OpenCV2.4】SVM的參數和函數介紹)
(1)注意:該結構必須被初始化後,傳給CvSVM
(2)構造函數的 原型

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

        )

(3)註釋
A. 默認的構造函數初始化有如下值:  
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 );
}
 
  
B. 構造函數的參數(一共10個):
<1> svm_type:指定SVM的類型(5種):
  • CvSVM::C_SVC : C類支持向量分類機。 n類分組  (n≥2),容許用異常值懲罰因子C進行不徹底分類。
  • CvSVM::NU_SVC : 類支持向量分類機。n相似然不徹底分類的分類器。參數爲取代C(其值在區間【0,1】中,nu越大,決策邊界越平滑)。
  • CvSVM::ONE_CLASS : 單分類器,全部的訓練數據提取自同一個類裏,而後SVM創建了一個分界線以分割該類在特徵空間中所佔區域和其它類在特徵空間中所佔區域。
  • CvSVM::EPS_SVR : 類支持向量迴歸機。訓練集中的特徵向量和擬合出來的超平面的距離須要小於p。異常值懲罰因子C被採用。
  • CvSVM::NU_SVR : 類支持向量迴歸機。 代替了 p。 
 
  

<2>kernel_type:SVM的內核類型(4種):

  • CvSVM::LINEAR : 線性內核,沒有任何向映射至高維空間,線性區分(或迴歸)在原始特徵空間中被完成,這是最快的選擇。
             .
  • CvSVM::POLY : 多項式內核:
              .
  • CvSVM::RBF : 基於徑向的函數,對於大多數狀況都是一個較好的選擇:
              .
  • CvSVM::SIGMOID : Sigmoid函數內核:
             .
<3> degree:內核函數(POLY)的參數degree。
<4> gamma:內核函數(POLY/ RBF/ SIGMOID)的參數
<5> coef0:內核函數(POLY/ SIGMOID)的參數coef0。
<6> Cvalue:SVM類型(C_SVC/ EPS_SVR/ NU_SVR)的參數C。
<7> nu:SVM類型(NU_SVC/ ONE_CLASS/ NU_SVR)的參數 
<8> p:SVM類型(EPS_SVR)的參數
<9> class_weights:C_SVC中的可選權重,賦給指定的類,乘以C之後變成  。因此這些權重影響不一樣類別的錯誤分類懲罰項。權重越大,某一類別的誤分類數據的懲罰項就越大。
<10> term_crit:SVM的迭代訓練過程的停止條件,解決部分受約束二次最優問題。您能夠指定的公差和/或最大迭代次數。
 
二、支持向量機CvSVM類(8個函數)
(1)構造函數
<1>構造函數的 原型

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()

             )

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

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() 

)

<3>訓練函數的參數註釋(5個)
        和構造函數的參數是同樣的,請參考構造函數的參數註釋。
 
(3) 自動 訓練函數
<1>做用:根據可選參數訓練一個SVM。
<2>自動訓練函數 原型

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

)

<3>自動訓練函數的參數註釋(13個)
  • 前5個參數參考構造函數的參數註釋。
  • k_fold: 交叉驗證參數。訓練集被分紅k_fold的自子集。其中一個子集是用來測試模型,其餘子集則成爲訓練集。因此,SVM算法複雜度是執行k_fold的次數。
  • *Grid: (6個)對應的SVM迭代網格參數。
  • balanced: 若是是true則這是一個2類分類問題。這將會建立更多的平衡交叉驗證子集。
<4>自動訓練函數的使用說明
  • 這個方法根據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。
<5>網格搜索法+K交叉驗證
    上述使用說明是OpenCV使用文檔中的,這裏再加其餘一些補充:
B、優化參數的方式通常是用網格搜索法取值,而後對這組參數進行K交叉驗證,計算精確值(交叉驗證的準確率等於可以被正確分類的數量百分比),尋求最優參數。
 
(4)預測函數
<1>做用:對輸入樣本作預測響應。  
<2>預測函數的函數 原型

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

<3>預測函數的參數註釋

  • sample: 須要預測的輸入樣本。
  • samples: 須要預測的輸入樣本們。
  • returnDFVal: 指定返回值類型。若是值是true,則是一個2類分類問題,該方法返回的決策函數值是邊緣的符號距離。
  • results: 相應的樣本輸出預測的響應。
<4>預測函數的使用說明
  • 這個函數用來預測一個新樣本的響應數據(response)。
  • 在分類問題中,這個函數返回類別編號;在迴歸問題中,返回函數值。
  • 輸入的樣本必須與傳給trainData的訓練樣本一樣大小。
  • 若是訓練中使用了varIdx參數,必定記住在predict函數中使用跟訓練特徵一致的特徵。
  • 後綴const是說預測不會影響模型的內部狀態,因此這個函數能夠很安全地從不一樣的線程調用。
(5)生成SVM網格參數的函數
<1>做用: 生成一個SVM網格參數。 
<2>函數原型
C++:  CvParamGrid CvSVM:: get_default_grid(int  param_id)
 
<3>函數的參數註釋
  • param_id: SVM參數的IDs必須是下列中的一個:(網格參數將根據這個ID生成 )
    • CvSVM::C
    • CvSVM::GAMMA
    • CvSVM::P
    • CvSVM::NU
    • CvSVM::COEF
    • CvSVM::DEGREE
<4>函數的使用說明
       該函數生成一個指定的SVM網格參數,主要用於傳遞給自動訓練函數 CvSVM::train_auto()。
 
(6)獲取當前SVM參數的函數
<1>做用: 獲取當前SVM參數
<2>函數原型:
C++:  CvSVMParams CvSVM:: get_params() const
<3>函數的使用說明
        這個函數主要是在使用CvSVM::train_auto()時去得到最佳參數。
 
(7)獲取支持向量及其數量的函數
<1>做用:獲取支持向量及其數量
<2>函數原型

C++: int CvSVM::get_support_vector_count() const  //獲取支持向量的數量

C++: const float* CvSVM::get_support_vector(int i) const    //獲取支持向量

        參數:i – 指定支持向量的索引。

 

(8)獲取所用特徵的數量的函數

<1>做用:獲取所用特徵的數量

<2>函數原型:

C++:  int CvSVM:: get_var_count() const
 
3、OpenCV的簡單的程序例子
    上述講述了處理一個 線性可分狀況的例子,包含了SVM使用的幾個步驟:
(1)準備訓練樣本及其類別標籤(trainingDataMat,labelsMat )
(2)設置訓練參數( CvSVMParams )
(3)對SVM進行訓練(CvSVM:: train )
(4)對新的輸入樣本進行預測(CvSVM:: predict )
(5)獲取支持向量(CvSVM:: get_support_vector_count,CvSVM:: get_support_vector  )
 
    上述講述了處理一個 線性不可分狀況的例子,着重講述了 懲罰因子C的做用:
  • C比較大時:分類錯誤率較小,可是間隔也較小。 在這種情形下, 錯分類對模型函數產生較大的影響,既然優化的目的是爲了最小化這個模型函數,那麼錯分類的情形必然會受到抑制。
  • C比較小時:間隔較大,可是分類錯誤率也較大。 在這種情形下,模型函數中錯分類之和這一項對優化過程的影響變小,優化過程將更加關注於尋找到一個能產生較大間隔的超平面。
     換而言之,C越大,優化時越關注錯分問題;C越小,越關注可否產生一個較大間隔的超平面。
    因爲樣本非線性可分, 天然就有一些被錯分類的樣本。
 
三、多分類的簡單例子(可參考 利用SVM解決2維空間向量的3級分類問題)
    上述講述了一個三分類的例子,核函數用了RBF,並用到了其參數gamma,以及懲罰因子C,訓練與預測和二分類同樣,只要對樣本賦予第三類的類別標籤。
 
    訓練與預測的使用方法和上述同樣,主要看下對圖像數據的處理(簡單的特徵提取)。
 
    訓練與預測的使用方法仍是和上述同樣,主要看下Hog特徵的使用( HOGDescriptor::compute)。
 

4、SVM處理流程總結:

一、 收集數據相關性分析(好比p卡方檢驗), 特徵選擇(好比主成份分析PCA)。
二、歸一化數據:就是根據實際要求,將數據的取值範圍轉化爲統一的區間如[a,b],a,b爲整數。(參考 縮放訓練和測試數據時的常見錯誤[附錄B])
三、分訓練集和測試集:利用抽樣技術將數據集分爲訓練集和測試集。抽樣技術有分層抽樣,簡單抽樣(等機率抽樣)。 通常訓練集數量大於測試集數量,就是要保證足夠的訓練樣例。
四、將數據轉化爲軟件(接口)所支持的格式
五、選擇核函數,能夠優先考慮RBF。
六、使用交叉驗證(cross-validation)尋找最佳參數C和Υ:對訓練集利用交叉驗證法選擇最好的參數C和r(西格瑪)(RBF核函數中的參數gama)。能夠經過網格法尋找出最優的參數,注意一次交叉驗證獲得一個參數對所對應的模型精度,網格法目的就是找到使得模型精度達到對高的參數對(這裏的參數對可能不止兩個,有可能也有其餘的),可使用一些啓發式的搜索來下降複雜度,雖然這個方法笨了點,可是它能獲得很穩定的搜索結果。須要提到的這裏在對訓練集進行分割的時候涉及到抽樣,一個較好的方法就是分層抽樣。從這步能夠看出其實 Cross-Validation是一種評估算法的方法。

    a. 訓練的目的獲得參數和支持向量(存儲在xml文件中),獲得參數就能獲得支持向量,帶進算式計算SVM分類的準確度,以準確度最高的一組參數做爲最終的結果,沒有絕對線性可分的,都有一個偏差,參數就是把那個偏差降到最低。

    b. 這裏的準確性是指將訓練集的每一個樣本的向量與支持向量作運算,將運算結果與標記值比較,判斷是否屬於這個類,統計這個類的正確的樣本數,最高的那一組參數準確性最高。

    c. 最終訓練獲得分類器。SVM只能分兩類,因此這裏的分類器是兩個類組成一個分類器,若是有K類,就有k(k-1)/2個分類器。

七、使用最佳參數C和Υ來訓練整個訓練集:用6中獲得的參數對在整個訓練集合上進行訓練,從而得出模型。
八、測試:利用測試集測試模型,獲得精度。這個精度能夠認爲是模型最終的精度。固然有人會擔憂3步中抽樣會有必定的偏差,致使8獲得的精度不必定是最好的,所以能夠重複3-8獲得多個模型的精度,而後選擇最好的一個精度最爲模型的精度(或者求全部精度的均值作爲模型精度)。(須要屢次選擇訓練集和測試集,而後每一次獲得一個精度的模型,選擇最好的一個精度做爲模型,也就是咱們項目裏面要屢次訓練的緣由)。
9. 識別分類:兩個類超平面的造成,意味着目標函數的造成,而後代入待識別樣本,識別時對應的組代入對應的參數,得出結果進行投票,斷定屬於那個類。
相關文章
相關標籤/搜索