開源SVM庫libSVM介紹

注:本文非筆者原創,原文轉載自:

http://blog.csdn.net/carson2005/article/details/6527055

http://blog.csdn.net/carson2005/article/details/6539192

libSVM是臺灣大學林智仁教授等研究人員開發的一個用於支持向量機分類,迴歸分析及分佈估計的c/c++開源庫。另外,它也可以用於解決多類分類問題。 libSVM最新的版本是20114月發佈的3.1版。林智仁教授設計開發該SVM庫的目的是爲了讓其它非專業人士可以更加方便快捷的使用SVM這個統計學習工具。libSVM提供了一些簡單易用的接口,從而使得用戶可以方便的使用,而不必關心其內部複雜的數學模型和運算過程。libSVM的主要特點有:

(1)各種SVM的表達公式;

(2)有效的多類分類能力;

(3)交叉驗證功能;

(4)各種核函數,包括預先計算得到的核矩陣;

(5)用於非平衡數據的加權svm

(6)提供c++java源代碼;

(7)用於演示SVM分類與迴歸能力的GUI界面;

.....

很多初學者往往按照以下的步驟使用libSVM

(1)將數據轉換到libSVM指定的格式;

(2)隨機選擇一個核函數和一些參數;

(3)測試;

這種方法雖然可行,但卻不一定能很快達到好的效果。爲此,林智仁教授推薦按照以下的步驟來使用libSVM

(1)將數據轉換到libSVM指定的格式;

(2)對數據進行尺度操作(一般指數據的歸一化);

(3)考慮RBF(徑向基)核函數;

(4)利用交叉驗證來得到最好的參數Cr;

(5)用最好的Cr來訓練所有訓練集合;

(6)測試;

之所以推薦首選徑向基核函數,是由於該核可以將數據非線性地映射到高維空間,而且,它還能處理那種特徵(數據)及其屬性之間呈現非線性關係的情況,而線性核函數只是徑向基核函數的一個特例。另外,相比而言,多項式核函數在高維空間有着更多的參數,從而使得模型更加複雜。同時,需要提醒的是,徑向基核函數並非萬能的,尤其當特徵數據的數值本身比較大的時候,線性核函數要更實用一些。

任何人可以在http://www.csie.ntu.edu.tw/~cjlin/libsvm 來下載libSVM開源庫。不過,按照開發者的要求,在使用之前,請務必閱讀其copyright,並按照其要求進行相應的引用和說明。另外,在使用之前,強烈推薦大家閱讀libSVM.zip裏面的readme文件。該文件詳細描述了libSVM的使用方法及注意事項。


鑑於libSVM中的readme文件有點長,而且,都是採用英文書寫,這裏,我把其中重要的內容提煉出來,並給出相應的例子來說明其用法,大家可以直接參考我的代碼來調用libSVM庫。

第一部分,利用libSVM自帶的簡易工具來演示SVM的兩類分類過程。(以下內容只是利用libSVM自帶的一個簡易的工具供大家更好的理解SVM,如果你對SVM已經有了一定的瞭解,可以直接跳過這部分內容)

首先,你要了解的是libSVM只是衆多SVM實現版本中的其中之一。而SVM是一種進行兩類分類的分類器,在libSVM最新版(libSVM3.1)裏面,已經自帶了簡單的工具,可以對二分類進行演示。以windows平臺爲例,將libSVM.zip解壓之後,有一個名爲windows的子文件夾,裏面有一個名爲svm-toy.exe的可執行文件。直接雙擊,運行該可執行文件,顯示如下的界面

點擊第二個按鈕“Run”,然後,在左上部分,用鼠標左鍵隨機點幾下,代表你選擇的第一類模式的數據分佈,下圖是我隨即點了幾下的結果:

之後,點擊“Change”,接着,用鼠標左鍵在窗口右下方隨便點擊幾下,代表你選擇的第二類模式的數據分佈,如下圖所示:

接着,點擊“Run”,libSVM就幫你把這兩類模式分開了,並用兩種不同的顏色區域來代表兩類不同的模式,如下圖所示:

圖中左上方紫色的區域,是第一類模式所在的區域,右下方的藍色區域,是你選擇的第二類模式所在的的區域,而兩者的分界面,也就是SVM的最優分類面。當然,SVM是通過核函數將原始數據映射到高維空間,在高維空間進行線性分類。換句話說,在高維空間,這兩類數據應該是線性可分的,即:最優分類面應該是一條直線,而這裏看到的,是將高維空間分類的結果又映射回原始空間所呈現的分類結果,即:非線性的分類面。細心的朋友可能已經發現,在上述界面的右下角,有一個編輯框,裏面寫着“-t 2 -c 100”,顯然,這是libSVM的一些參數,你也可以試着更改這些參數,來選擇不同的核函數、不同的SVM類型等來達到最好的分類效果。

 

第二部分:libSVM中的小工具

libSVM中包含以下可執行程序文件(小工具):

(1)svm-scale:一個用於對輸入數據進行歸一化的簡易工具

(2)svm-toy:一個帶有圖形界面的交互式SVM二分類功能演示小工具;

(3)svm-train:對用戶輸入的數據進行SVM訓練。其中,訓練數據是按照以下格式輸入的:

<類別號> <索引1><特徵值1> <索引2><特徵值2>...

(4)svm-predict:根據SVM訓練得到的模型,對輸入數據進行預測,即分類。

 

第三部分:libSVM用法介紹:`

      libSVM的所有函數申明及結構體定義均包含在libSVM.h文件當中,在使用過程中,你必須要包含該頭文件,並且,對libSVM.cpp進行相應的鏈接。在對libSVM中的函數用法進行詳細介紹之前,我們不妨先簡單瞭解一下libSVM.h中一些結構體的含義。

struct svm_node

{

int index;

double value;

};

該結構體,定義了一個“SVM節點”,即:索引i及其所對應的第i個特徵值。這樣n個相同類別號的SVM節點,就構成了一個SVM輸入向量。即:一個SVM輸入向量可以表示爲如下的形式:

類別標籤 索引1:特徵值索引2:特徵值索引3:特徵值3...

我們可以將若干個這樣的輸入向量輸入到libSVM進行訓練,或者,輸入一個類別標籤未知的向量對其進行預測。

struct svm_problem

{

int l;

double *y;

struct svm_node **x;

};

該結構體中的l代表訓練樣本的個數;double型指針y代表l個訓練樣本中每個訓練樣本的類別號,也就是我們常說的“標籤”;而"SVM節點"x,則是一個指針的指針(如果你對指針的指針不熟悉,完全可以把x理解爲一個矩陣),x所指向的內容就是所有訓練樣本所有的特徵值數據。

假如我們有下面的訓練樣本數據:

類別標籤   特徵值 特徵值2 特徵值3 特徵值4 特徵值5

   1       0     0.1     0.2      0       0

   2      0     0.1     0.3     -1.2       0

   1        0.4      0      0      0       0

   2      0     0.1       0      1.4      0.5

  1    -0.1    -0.2       0.1      1.1      0.1

那麼,svm_problem結構體中的l=5(共有5個訓練樣本),y=[1,2,1,2,1];指針x所指向的內容可以視爲5個行向量,每個行向量有5列,即:x指代一個5*5的矩陣,其值爲:

(1,0)(2,0.1)(3,0.2)(4,0)(5,0)(-1,?)

(1,0)(2,0.1)(3,0.3)(4,-1.2)(5,0)(-1,?)

(1,0.4)(2,0)(3,0)(4,0)(5,0)(-1,?)

(1,0)(2,0.1)(3,0)(4,1.4)(5,0.5)(-1,?)

(1,-0.1)(2,-0.2)(3,0.1)(4,1.1)(5,0.1)(-1,?) 

需要提醒的是,這裏,每一行最後一列都是以“-1”開頭,這是libSVM規定的特徵值向量的結束標識;此外,索引應該按照升序方式進行排列。

       

enum { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR };//libSVM規定的SVM類型

 

enum { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED };//libSVM規定的核函數的類型

 

struct svm_parameter

{

int svm_type;//取值爲前面提到的枚舉類型中的值

int kernel_type;//取值爲前面提到的枚舉類型中的值

int degree; //用於多項式核函數/

double gamma;//用於多項式、徑向基、S型核函數

   double coef0;//用於多項式和S型核函數

 

/* 以下參數僅僅用於訓練階段 */

double cache_size; //核緩存大小,以MB爲單位

double eps; //誤差精度小於eps時,停止訓練

double C; //用於C_SVC,EPSILON_SVR,NU_SVR

int nr_weight; //用於C_SVC

int *weight_label;//用於C_SVC

double* weight;//用於C_SVC

double nu;//用於NU_SVC,ONE_CLASS,NU_SVR

double p;//用於EPSILON_SVR

int shrinking; //等於1代表執行啓發式收縮

int probability;//等於1代表模型的分佈概率已知

};

該結構體定義了libSVM中的用到的SVM參數。其中svm_type可以是C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR中的任意一種,代表着SVM的類型;

C_SVC: C-SVM classification

    NU_SVC: nu-SVM classification

    ONE_CLASS: one-class-SVM

    EPSILON_SVR: epsilon-SVM regression

    NU_SVR: nu-SVM regression

kernel_type可以是LINEAR, POLY, RBF, SIGMOID中的一種,代表着核函數的類型;

LINEAR: u'*v,線性核函數;

    POLY: (gamma*u'*v + coef0)^degree,多項式核函數;

    RBF: exp(-gamma*|u-v|^2),徑向基核函數;

    SIGMOID: tanh(gamma*u'*v + coef0)S型核函數;

PRECOMPUTED: kernel values in training_set_file,自定義的核函數;

nr_weight, weight_label, and weight這三個參數用於改變某些類的懲罰因子。當輸入數據不平衡,或者誤分類的風險代價不對稱的時候,這三個參數將會對樣本訓練起到非常重要的調節作用。

nr_weightweight_labelweight的元素個數,或者稱之爲維數。Weight[i]weight_label[i]之間是一一對應的,weight[i]代表着類別weight_label[i]的懲罰因子的係數是weight[i]。如果你不想設置懲罰因子,直接把nr_weight設置爲0即可。

爲了防止錯誤的參數設置,你還可以調用libSVM提供的接口函數svm_check_parameter()來對輸入參數進行檢查。

 

    在使用libSVM進行分類之前,你需要通過樣本學習,構建一個SVM分類模型。該分類模型也可以理解爲生成一些用於分類的“數據”。當然,構建的分類模型需要保存爲文件,以便後續使用。用於libSVM訓練的函數,其申明如下所示:

struct svm_model *svm_train(const struct svm_problem *prob, const struct svm_parameter *param);

顯然,該函數的輸入,就是svm_problem結構體的prob指針所指向的內容。該結構體在前面已經介紹過,其內部,不僅包含了訓練樣本的個數,還包含每個訓練樣本的“標籤”及該訓練樣本對應的特徵數據。而svm_parameter類型的param指針則指定了libSVM所用到的諸如SVM類型,核函數類型,懲罰因子之類的參數。另外,該函數的返回值是一個svm_model結構體,該結構體的定義,在libSVM.cpp當中:

struct svm_model

{

svm_parameter param; //SVM參數設置

int nr_class; //類別數量,對於regression和ne-class SVM這兩種情況,該值爲2

int l; //支持向量的個數

svm_node **SV; //支持向量

double **sv_coef; //用於決策函數的支持向量係數

double *rho; //決策函數中的常數項

double *probA; // pariwise probability information

double *probB;

 

// for classification only

 

int *label; // 每個類類別標籤

int *nSV; //每個類的支持向量個數

int free_sv; //如果svm_model已經通過svm_load_model創建,則該值爲1;如果svm_model是通過svm_train創建的,該值爲0

};

需要提醒的是,libSVM支持多類分類問題,當有k個待分類問題時,libSVM構建k*(k-1)/2種分類模型來進行分類,即:libSVM採用一對一的方式來構建多類分類器,如下所示:

1 vs 2, 1 vs 3, ..., 1 vs k, 2 vs 3, ..., 2 vs k, ..., k-1 vs k。

用戶在得到SVM分類模型之後,需要將其進行保存。在這裏,libSVM已經提供了相應的函數接口:

int svm_save_model(const char *model_file_name, const struct svm_model *model);

在調用訓練函數之後,只需要指定保存位置,直接調用該函數,就可以進行相應的保存。

在對樣本進行訓練得到分類模型之後,就可以利用該分類模型對未知輸入數據進行類別判斷了,也就是我們常說的“預測”。用於libSVM預測的函數,其申明如下所示:

double svm_predict(const struct svm_model *model, const struct svm_node *x);

該函數的第一個參數就是利用樣本訓練得到的SVM分類模型,第二個參數,是輸入的未知模式的特徵數據,即:得到了表徵某一類別的特徵數據,根據這些數據,來判斷它所對應的類別標籤。而SVM分類模型,可以由libSVM定義的下面這個接口函數來進行加載:

struct svm_model *svm_load_model(const char *model_file_name);

此外,在使用上述函數過程中,需要對svm_model及svm_parameter申請內存,而不使用它們的時候,用戶需要調用以下兩個函數進行內存釋放:

void svm_destroy_model(struct svm_model *model);

void svm_destroy_param(struct svm_parameter *param);


發佈了1 篇原創文章 · 獲贊 9 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章