LibSVM學習(四)——逐步深刻LibSVM

其實,在以前上海交大模式分析與機器智能實驗室對2.6版本的svm.cpp作了部分註解,(在哪裏?google一下你就知道)。可是,這個註釋只是針對代碼而註釋,整篇看下來,你會發現除了理解幾個參數的含義,仍是會對libsvm一頭霧水。固然做爲理解程序的輔助材料,仍是有很大用處的。特別是,對幾個結構體的說明,比較清楚。可是要清楚程序具體作了什麼,仍是要追蹤程序中去。web

 

       因爲svm涉及的數學知識比較多,咱們這篇只是講一些基本的思路,因此就從最基本的C-SVCsvm,核函數採用經常使用的RBF函數。LibSVM就採用2.6版本的好了,由於後續的版本做者又加了不少內容,不易理解做者最初的思路。我是作模式識別,主要從分類的角度來解析函數的調用過程,咱們從svmtrain.c看起,其調用的函數過程以下:      4.1算法

   4.2

    上圖是整個C-SVC的計算過程,下面對一些重要的內容進行具體說明:編程

 

1. svm_group_class函數

      2.6版中沒有此函數的,其功能直接在svm_train實現,爲了加強可讀性,2.89版中設置了這個函數,其實所做的工做都是同樣的。須要說明的是其從新排列後perm中只存儲的是各個樣本在原始位置的序號,而非數據。這樣作的好處有兩個:google

       1)沒必要破壞原始數據(也就是讀進來的x的數據);spa

       2)檢索起來方便,只須要L維的數據檢索,獲得序號後,而後定位到原始數據中相應的位置就能夠。 orm

 4.3

       perm是中各種的排列順序是按照原始樣本中各種出現的前後順序排列的,不必定是按照你原始樣本的label序號排列,假如原始樣本的label{-101},而最早出現的label1的樣本,那麼perm中就把label1的做爲類0最早排列。而start中記錄的是各種的起始序號,而這個序號是在perm中的序號。內存

 

2. 多類判別的one-against-oneget

      svm作判別是用的分界線(),兩類之間只有一個分界線(),所以分類器也只有1種,要麼是1類要麼是2類。可是對於多類,分類方式就有多種。目前,存在的方法主要有:數學

       11-V-R方式

       對於k類問題把其中某一類的n個訓練樣本視爲一類,全部其餘類別歸爲另外一類,所以共有k個分類器。最後預測時,判別式使用競爭方式,也就是哪一個類得票多就屬於那個類。

       21-V-1方式

       也就是咱們所說的one-against-one方式。這種方法把其中的任意兩類構造一個分類器,共有(k-1)×k/2個分類器。最後預測也採用競爭方式。

       3)有向無環圖(DAG-SVM

       該方法在訓練階段採用1-V-1方式,而判別階段採用一種兩向有向無環圖的方式。

       LibSVM採用的是1-V-1方式,由於這種方式思路簡單,而且許多實踐證明效果比1-V-R方式要好。  

4.4 

       上圖是一個51-V-1組合的示意圖,紅色是0類和其餘類的組合,紫色是1類和剩餘類的組合,綠色是2類與右端兩類的組合,藍色只有34的組合。所以,對於nr_class個類的組合方式爲:

                                                        for(i = 0; i < nr_class; i ++)

                                                        {

                                                               for(j = i+1; i < nr_class; j ++)     

                                                               { 類 i –V – 類 j }

                                                        }

 

3. hessian矩陣的內存處理        

      由於svm是基於結構風險最小的,所以在分類識別方式具備較傳統的基於經驗風險最小的方式有優點。可是svm也有一個致命的缺陷,由於要計算hessian矩陣Qij所耗的內存巨大,不利於實踐中應用。目前,怎麼減少內存的使用依舊是SVM的研究的課題。LibSVMhessian矩陣處理的策略是定義了一個內存處理類Cache類,預先認爲分配必定的內存,存儲計算好的Qij,其序號的檢索採用雙向鏈表的方式,加快了檢索速度。其最重要的函數爲: 

       int Cache::get_data(const int index, Qfloat **data, int len)

       //len  data 的長度,data爲返回的內存首地址,indexQij的行。

       每次都要查找鏈表中行爲indexQi,假如已經計算過了,就返回計算過的內存地址,並把儲存首地址的鏈表節點插入到鏈表尾部。假如沒計算過,就分配內存並進行計算,當剩餘的內存不夠時,就要回收鏈表頭指向的內存。這裏,可能有人會問,難道之前計算的就沒有用了嗎??其實,是由於Qij是稀疏矩陣,在訓練過程當中只要其對應的alpha[i]再也不變更(這時alpha[i]=0或者alpha[i]=C),其對應的Qi就不會被選到來訓練,所以原來計算的Qi就沒有用了。其實,鏈表的順序表明了別選到的頻率,最頭部的是最不可能被選到,由於這時alpha[i]=0或者alpha[i]=C,而最尾部的最容易被選到。

 

4. 數據選擇select_working_set(i,j)     

     對於樣本數量比較多的時候(幾千個),SVM所須要的內存是計算機所不能承受的。目前,對於這個問題的解決方法主要有兩種:塊算法和分解算法。這裏,libSVM採用的是分解算法中的SMO(串行最小化)方法,其每次訓練都只選擇兩個樣本。咱們不對SMO作具體的討論,要想深刻了解能夠查閱相關的資料,這裏只談談和程序有關的知識。

      通常SVM的對偶問題爲:                      

 4.5                                                              

 S.t.                                                                                                         (4.1)

   4.6                                                                                           

     SVM收斂的充分必要條件是KKT條件,其表現爲:

   4.7                                        (4.2) 

     4.1式求導可得:4.8

                                           (4.3) 

     進一步推導可知:

    4.9                                              (4.4)

    也就是說,只要全部的樣本都知足4.4式,那麼獲得解就是最優值。所以,在每輪訓練中,每次只要選擇兩個樣本(序號爲ij),是最違反KKT條件(也就是4.4式)的樣本,就能保證其餘樣本也知足KKT條件。序號ij的選擇方式以下: 

 4.10                         (4.5)

 

5. 中止準則

    LibSVM程序中,中止準則蘊含在了函數select_working_set(i,j)返回值中。也就是,當找不到符合4.5式的樣本時,那麼理論上就達到了最優解。可是,實際編程時,因爲KKT條件仍是蠻苛刻的,要進行適當的放鬆。令: 

 4.11                                                         (4.6)

     由4.4式可知,當全部樣本都知足KKT條件時,gi ≤ -gj

       加一個適當的寬鬆範圍ε,也就是程序中的eps,默認爲0.001,那麼最終的中止準則爲:

gi ≤ -g+ε  →    gi g≤ε                                    (4.7)

 

6. 因子α的更新

 

    因爲SMO每次都只選擇2個樣本,那麼4.1式的等式約束能夠轉化爲直線約束: 

4.12                                             (4.8)

   轉化爲圖形表示爲: 

4.13                             

      4.8式中α1α2 表示,即:4.14,結合上圖由解析幾何可得α2的取值範圍: 

  4.15                                  (4.9)

     通過一系列變換,能夠獲得的α2更新值α2new

   4.16                                               (4.10)

    結合4.94.10式獲得α2new最終表達式:

4.17                                                                     (4.11)

      獲得α2new後,就能夠由4.8式求α1new

      這裏,具體操做的時候,把選擇後的序號ij代替這裏的12就能夠了。固然,編程時,這些公式仍是太抽象。對於4.9式,還須要具體細分。好比,對於y1 y2 = -1時的L = max(0,α2 - α1),是0大還α2 - α1是大的問題。總共須要分8種狀況。

 

7. 數據縮放do_shrinking()

     上面說到SVM用到的內存巨大,另外一個缺陷就是計算速度,由於數據大了,計算量也就大,很顯然計算速度就會降低。所以,一個好的方式就是在計算過程當中逐步去掉不參與計算的數據。由於,實踐證實,在訓練過程當中,alpha[i]一旦達到邊界(alpha[i]=0或者alpha[i]=C),alpha[i]值就不會變,隨着訓練的進行,參與運算的樣本會愈來愈少,SVM最終結果的支持向量(0<alpha[i]<C)每每佔不多部分。

       LibSVM採用的策略是在計算過程當中,檢測active_size中的alpha[i]值,若是alpha[i]到了邊界,那麼就應該把相應的樣本去掉(變成inactived),並放到棧的尾部,從而逐步縮小active_size的大小。

 

8. 截距b的計算

    b計算的基本公式爲:

   4.18                                                                       (4.12)

     理論上,b的值是不定的。當程序達到最優後,只要用任意一個標準支持向量機(0<alpha[i]<C)的樣本帶入4.12式,獲得的b值都是能夠的。目前,求b的方法也有不少種。在libSVM中,分別對y=+1y=-1的兩類全部支持向量求b,而後取平均值:

 4.19                                                                     (4.13)

 

    至此,libSVM的整個思路咱們簡單的過了一遍,裏面涉及到很到理論知識,許多細節須要查看相關的SVM的書籍。說實話,筆者也是新手,有些理論也沒弄很清楚,我只能把我知道的儘可能的講出來。但願對一些想要了解SVM的有所幫助。 

相關文章
相關標籤/搜索