一、瞭解SVM:
支持向量機,因其英文名爲support vector machine,故一般簡稱SVM,通俗來講,它是一種二類分類模型,其基本模型定義爲特徵空間上的間隔最大的線性分類器,其學習策略便是間隔最大化,最終可轉化爲一個凸二次規劃問題的求解。
關於SVM的具體理論解釋可以參考博文: 支持向量機通俗導論(理解SVM的三層境界)
(因爲ddl比較緊張,所以我就不求甚解地看了一下SVM原理,然後研究了一下如何實現SVM;建議至少要理解懲罰參數C是怎麼回事兒)
二、瞭解LIBSVM——方便快捷的SVM工具包:
LIBSVM是臺灣大學林智仁(Lin Chih-Jen)教授等2001年開發設計的一個簡單, 易於使用和快速有效的SVM模式識別與迴歸的軟件包, 他不但提供了編譯好的可在Windows系列系統的執行文件, 還提供了源代碼, 方便改進, 修改以及在其它操作系統上應用; 該軟件對SVM所涉及的參數調節相對比較少, 提供了很多的默認參數, 利用這些默認參數可以解決很多問題; 並提供了交互檢驗(Cross Validation)的功能. 該軟件包可在http://www.csie.ntu.edu.tw/~cjlin/免費獲得. 該軟件可以解決C-SVM, ν-SVM, ε-SVR和ν-SVR等問題, 包括基於一對一算法的多類模式識別問題.
LIBSVM在Windows下提供了直接可以在DOS下使用的exe文件,但是因爲大作業的環境需要完全用C++實現,所以需要把LIBSVM的源代碼揪出來,然後在它的基礎上進行編程。
三、調用方法和參數介紹
(參數不知道怎麼調節沒有關係,暫時使用默認值就可以)
svm_model 爲模型類,通過訓練或加載訓練好的模型文件獲得
svm_parameter 爲參數類,主要爲支持向量機設定參數,具體參數如下:
svm_parameter.svm_type
svm類型:SVM設置類型(默認svm_parameter.C_SVC)
svm_parameter.C_SVC -- C-SVC
svm_parameter.NU_SVC -- ν-SVC
svm_parameter.ONE_CLASS – 一類SVM
svm_parameter.EPSILON_SVR -- ε -SVR
svm_parameter.NU_SVR -- ν-SVR
svm_parameter.kernel_type
核函數類型:核函數設置類型(svm_parameter.LINEAR)
svm_parameter.LINEAR – 線性:u'×v
svm_parameter.POLY– 多項式:(γ×u'×v + coef0)^degree
svm_parameter.RBF – RBF函數:exp(-γ×|u-v|^2)
svm_parameter.SIGMOID – sigmoid:tanh(γ×u'×v + coef0)
svm_parameter.degree
核函數中的degree設置(默認3)
svm_parameter.coef0
核函數中的coef0設置(默認0)
svm_parameter.shrinking
是否使用啓發式,0或1(默認1)
svm_parameter.nu
設置ν-SVC,一類SVM和ν- SVR的參數(默認0.5)
svm_parameter.C
設置C-SVC,ε -SVR和ν-SVR的參數(默認1)
svm_parameter.cache_size
設置cache內存大小,以MB爲單位(默認40)
svm_problem 相當於訓練集合,可講需要訓練的數據加入該類傳遞給訓練器
四、說明
我的大作業工程還沒完工,先以YHBcctv同學的實驗來做說明吧(感謝YHBcctv同學的實驗報告和代碼)
完整的工程代碼+實驗報告 下載(免積分):http://download.csdn.net/detail/jsgaobiao/9332813
SVM的實現主要由訓練樣本和預測樣本兩部分構成,他們分別由函數svm_train和svm_predict實現。
(1) struct svm_model*svm_train(const struct svm_problem *prob, const struct svm_parameter *param);
其中,prob存儲的是參加訓練的樣本集,param存儲的是訓練的各種參數。他們定義如下:
struct svm_problem //存儲本次參加運算的所有樣本(數據集),及其所屬類別。
{
int l; //記錄樣本總數
double *y; //指向樣本所屬類別的數組
struct svm_node **x; //指向一個存儲內容爲指針的數組
};
struct svm_parameter// 訓練參數
{
int svm_type; //SVM類型,
int kernel_type; //核函數類型
int degree; /* for poly */
double gamma; /* for poly/rbf/sigmoid */
double coef0; /* for poly/sigmoid */
/* these are for training only */
double cache_size; /* in MB 制定訓練所需要的內存*/
double eps; /* stopping criteria */
double C; /* for C_SVC, EPSILON_SVR and NU_SVR ,懲罰因子*/
int nr_weight; /* for C_SVC 權重的數目*/
int *weight_label; /* for C_SVC 權重,元素個數由nr_weight 決定*/
double* weight; /* for C_SVC */
double nu; /* for NU_SVC, ONE_CLASS, and NU_SVR */
double p; /* for EPSILON_SVR */
int shrinking; /* use the shrinking heuristics 指明訓練過程是否使用啓發式*/
int probability; /* do probability estimates 指明是否要做概率估計*/
}
其中,SVM類型和核函數類型如下:
enum { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR }; /* svm_type */
enum { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED }; /* kernel_type */
(2) doublesvm_predict(const struct svm_model *model, const struct svm_node *x);
參數model,是一個SVM模型的指針,可以使用函數struct svm_model*svm_load_model(const char *model_file_name),導入訓練時保存好的SVM模型,此函數返回一個SVM模型的指針,可以直接賦值給變量model。
參數x,是const struct svm_node結構體的指針,指向的是需要預測的單個樣本。
部分核心代碼如下:
訓練樣本
void train(char *filePath)
{
FILE *fp;
int k;
int line=0;
int temp;
if((fp=fopen(filePath,"rt"))==NULL) return;
while(1)
{
svm_node* features = new svm_node[85+1];
//讀入一個訓練樣本(每個樣本是一個長度86的序列,其中最後一個變量是該樣本的歸類)
for(k=0;k<85;k++)
{
fscanf(fp,"%d",&temp);
features[k].index = k + 1;
features[k].value = temp/(MAX*1.0) ;
//對樣本每一維的值進行歸一化,目的是:
//A、避免一些特徵值範圍過大而另一些特徵值範圍過小;
//B、避免在訓練時爲了計算核函數而計算內積的時候引起數值計算的困難。
}
features[85].index = -1;
fscanf(fp,"%d",&temp);
xList.push_back(features);
yList.push_back(temp);
line++;
trainNum=line;
if(feof(fp))
break;
}
setParam();
prob.l=line;
prob.x=new svm_node *[prob.l]; //對應的特徵向量
prob.y = new double[prob.l]; //放的是值
int index=0;
while(!xList.empty())
{
prob.x[index]=xList.front();
prob.y[index]=yList.front();
xList.pop_front();
yList.pop_front();
index++;
}
svmModel=svm_train(&prob, ?m); //調用svm_train訓練出model
//保存model
svm_save_model("model.txt",svmModel);
//釋放空間
delete prob.y;
delete [] prob.x;
svm_free_and_destroy_model(&svmModel);
}
預測樣本
void predict(char *filePath)
{
svm_model *svmModel = svm_load_model("model.txt"); //載入之前訓練好的model
FILE *fp;
int line=0;
int temp;
if((fp=fopen(filePath,"rt"))==NULL) return;
while(1)
{
svm_node* input = new svm_node[85+1]; //讀入每一個預測樣本分別預測結果(也就是第86個數值)
for(int k=0;k<85;k++)
{
fscanf(fp,"%d",&temp);
input[k].index = k + 1;
input[k].value = temp/(MAX*1.0); //歸一化
}
input[85].index = -1; //待預測的數值標記爲-1
int predictValue=svm_predict(svmModel, input); //調用svm_predict進行預測
predictvalue.push_back(predictValue);
cout<<predictValue<<endl; //輸出預測的分類結果(0或1)
if(feof(fp))
break;
}
}
完整的工程代碼+實驗報告 下載(免積分):http://download.csdn.net/detail/jsgaobiao/9332813
五、用SVM實現多分類
SVM是一個二分類器,當遇到多類別的時候,一般採取如下兩種策略。
a.一對多法(one-versus-rest,簡稱1-v-r SVMs)。訓練時依次把某個類別的樣本歸爲一類,其他剩餘的樣本歸爲另一類,這樣k個類別的樣本就構造出了k個SVM。分類時將未知樣本分類爲具有最大分類函數值的那類。
b.一對一法(one-versus-one,簡稱1-v-1 SVMs)。其做法是在任意兩類樣本之間設計一個SVM,因此k個類別的樣本就需要設計k(k-1)/2個SVM。當對一個未知樣本進行分類時,最後得 票最多的類別即爲該未知樣本的類別。Libsvm中的多類分類就是根據這個方法實現的