libsvm很早之前就用了,現在封裝一下方便自己使用,也方便大家更快的使用這個庫,這個庫一個挺有用的特性就是對測試樣本的概率估計。源碼在隨筆的最后。liblinear的版本也是類似移植,主要是處理好數據的傳入即可。
1.源碼
碼農最喜歡的稻草了,封裝的項目源碼,請看附件:CxLibSVM.zip
libsvm源碼:libsvm
2.封裝的類CxLibSVM
基於libsvm封裝的類,如下:
#pragma once #include <string> #include <vector> #include <iostream> #include "./libsvm/svm.h" using namespace std; //內存分配 #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) /************************************************************************/ /* 封裝svm */ /************************************************************************/ class CxLibSVM { private: struct svm_model* model_; struct svm_parameter param; struct svm_problem prob; struct svm_node * x_space; public: //************************************ // 描 述: 構造函數 // 方 法: CxLibSVM // 文 件 名: CxLibSVM::CxLibSVM // 訪問權限: public // 返 回 值: // 限 定 符: //************************************ CxLibSVM() { model_ = NULL; } //************************************ // 描 述: 析構函數 // 方 法: ~CxLibSVM // 文 件 名: CxLibSVM::~CxLibSVM // 訪問權限: public // 返 回 值: // 限 定 符: //************************************ ~CxLibSVM() { free_model(); } //************************************ // 描 述: 訓練模型 // 方 法: train // 文 件 名: CxLibSVM::train // 訪問權限: public // 參 數: const vector<vector<double>> & x // 參 數: const vector<double> & y // 參 數: const int & alg_type // 返 回 值: void // 限 定 符: //************************************ void train(const vector<vector<double>>& x, const vector<double>& y, const struct svm_parameter& param) { if (x.size() == 0)return; //釋放先前的模型 free_model(); /*初始化*/ long len = x.size(); long dim = x[0].size(); long elements = len*dim; //參數初始化,參數調整部分在這里修改即可 // 默認參數 //param.svm_type = C_SVC; //算法類型 //param.kernel_type = LINEAR; //核函數類型 //param.degree = 3; //多項式核函數的參數degree //param.coef0 = 0; //多項式核函數的參數coef0 //param.gamma = 0.5; //1/num_features,rbf核函數參數 //param.nu = 0.5; //nu-svc的參數 //param.C = 10; //正則項的懲罰系數 //param.eps = 1e-3; //收斂精度 //param.cache_size = 100; //求解的內存緩沖 100MB //param.p = 0.1; //param.shrinking = 1; //param.probability = 1; //1表示訓練時生成概率模型,0表示訓練時不生成概率模型,用於預測樣本的所屬類別的概率 //param.nr_weight = 0; //類別權重 //param.weight = NULL; //樣本權重 //param.weight_label = NULL; //類別權重 //轉換數據為libsvm格式 prob.l = len; prob.y = Malloc(double, prob.l); prob.x = Malloc(struct svm_node *, prob.l); x_space = Malloc(struct svm_node, elements+len); int j = 0; for (int l = 0; l < len; l++) { prob.x[l] = &x_space[j]; for (int d = 0; d < dim; d++) { x_space[j].index = d+1; x_space[j].value = x[l][d]; j++; } x_space[j++].index = -1; prob.y[l] = y[l]; } /*訓練*/ model_ = svm_train(&prob, ¶m); } //************************************ // 描 述: 預測測試樣本所屬類別和概率 // 方 法: predict // 文 件 名: CxLibSVM::predict // 訪問權限: public // 參 數: const vector<double> & x 樣本 // 參 數: double & prob_est 類別估計的概率 // 返 回 值: double 預測的類別 // 限 定 符: //************************************ int predict(const vector<double>& x,double& prob_est) { //數據轉換 svm_node* x_test = Malloc(struct svm_node, x.size()+1); for (unsigned int i=0; i<x.size(); i++) { x_test[i].index = i; x_test[i].value = x[i]; } x_test[x.size()].index = -1; double *probs = new double[model_->nr_class];//存儲了所有類別的概率 //預測類別和概率 int value = (int)svm_predict_probability(model_, x_test, probs); for (int k = 0; k < model_->nr_class;k++) {//查找類別相對應的概率 if (model_->label[k] == value) { prob_est = probs[k]; break; } } delete[] probs; return value; } void do_cross_validation(const vector<vector<double>>& x, const vector<double>& y, const struct svm_parameter& param, const int & nr_fold) { if (x.size() == 0)return; /*初始化*/ long len = x.size(); long dim = x[0].size(); long elements = len*dim; //轉換數據為libsvm格式 prob.l = len; prob.y = Malloc(double, prob.l); prob.x = Malloc(struct svm_node *, prob.l); x_space = Malloc(struct svm_node, elements + len); int j = 0; for (int l = 0; l < len; l++) { prob.x[l] = &x_space[j]; for (int d = 0; d < dim; d++) { x_space[j].index = d + 1; x_space[j].value = x[l][d]; j++; } x_space[j++].index = -1; prob.y[l] = y[l]; } int i; int total_correct = 0; double total_error = 0; double sumv = 0, sumy = 0, sumvv = 0, sumyy = 0, sumvy = 0; double *target = Malloc(double, prob.l); svm_cross_validation(&prob, ¶m, nr_fold, target); if (param.svm_type == EPSILON_SVR || param.svm_type == NU_SVR) { for (i = 0; i < prob.l; i++) { double y = prob.y[i]; double v = target[i]; total_error += (v - y)*(v - y); sumv += v; sumy += y; sumvv += v*v; sumyy += y*y; sumvy += v*y; } printf("Cross Validation Mean squared error = %g\n", total_error / prob.l); printf("Cross Validation Squared correlation coefficient = %g\n", ((prob.l*sumvy - sumv*sumy)*(prob.l*sumvy - sumv*sumy)) / ((prob.l*sumvv - sumv*sumv)*(prob.l*sumyy - sumy*sumy)) ); } else { for (i = 0; i < prob.l; i++) if (target[i] == prob.y[i]) ++total_correct; printf("Cross Validation Accuracy = %g%%\n", 100.0*total_correct / prob.l); } free(target); } //************************************ // 描 述: 導入svm模型 // 方 法: load_model // 文 件 名: CxLibSVM::load_model // 訪問權限: public // 參 數: string model_path 模型路徑 // 返 回 值: int 0表示成功;-1表示失敗 // 限 定 符: //************************************ int load_model(string model_path) { //釋放原來的模型 free_model(); //導入模型 model_ = svm_load_model(model_path.c_str()); if (model_ == NULL)return -1; return 0; } //************************************ // 描 述: 保存模型 // 方 法: save_model // 文 件 名: CxLibSVM::save_model // 訪問權限: public // 參 數: string model_path 模型路徑 // 返 回 值: int 0表示成功,-1表示失敗 // 限 定 符: //************************************ int save_model(string model_path) { int flag = svm_save_model(model_path.c_str(), model_); return flag; } private: //************************************ // 描 述: 釋放svm模型內存 // 方 法: free_model // 文 件 名: CxLibSVM::free_model // 訪問權限: private // 返 回 值: void // 限 定 符: //************************************ void free_model() { if (model_ != NULL) { svm_free_and_destroy_model(&model_); svm_destroy_param(¶m); free(prob.y); free(prob.x); free(x_space); } } };
3.調用封裝的類CxLibSVM
如何調用該類請看如下代碼:
#include "cxlibsvm.hpp" #include <time.h> #include <iostream> using namespace std; void init_svm_param(struct svm_parameter& param) { //參數初始化,參數調整部分在這里修改即可 // 默認參數 param.svm_type = C_SVC; //算法類型 param.kernel_type = LINEAR; //核函數類型 param.degree = 3; //多項式核函數的參數degree param.coef0 = 0; //多項式核函數的參數coef0 param.gamma = 0.5; //1/num_features,rbf核函數參數 param.nu = 0.5; //nu-svc的參數 param.C = 10; //正則項的懲罰系數 param.eps = 1e-3; //收斂精度 param.cache_size = 100; //求解的內存緩沖 100MB param.p = 0.1; param.shrinking = 1; param.probability = 1; //1表示訓練時生成概率模型,0表示訓練時不生成概率模型,用於預測樣本的所屬類別的概率 param.nr_weight = 0; //類別權重 param.weight = NULL; //樣本權重 param.weight_label = NULL; //類別權重 } void gen_train_sample(vector<vector<double>>& x, vector<double>& y, long sample_num = 200, long dim = 10, double scale = 1) { //long sample_num = 200; //樣本數 //long dim = 10; //樣本特征維度 //double scale = 1; //數據縮放尺度 srand((unsigned)time(NULL));//隨機數 //生成隨機的正類樣本 for (int i = 0; i < sample_num; i++) { vector<double> rx; for (int j = 0; j < dim; j++) { rx.push_back(scale*(rand() % 10)); } x.push_back(rx); y.push_back(1); } //生成隨機的負類樣本 for (int i = 0; i < sample_num; i++) { vector<double> rx; for (int j = 0; j < dim; j++) { rx.push_back(-scale*(rand() % 10)); } x.push_back(rx); y.push_back(2); } } void gen_test_sample(vector<double>& x, long sample_num = 200, long dim = 10, double scale = 1) { //long sample_num = 200; //樣本數 //long dim = 10; //樣本特征維度 //double scale = 1; //數據縮放尺度 srand((unsigned)time(NULL));//隨機數 //生成隨機的正類樣本 for (int j = 0; j < dim; j++) { x.push_back(-scale*(rand() % 10)); } } void main() { //初始化libsvm CxLibSVM svm; //初始化參數 struct svm_parameter param; init_svm_param(param); /*1、准備訓練數據*/ vector<vector<double>> x; //樣本集 vector<double> y; //樣本類別集 gen_train_sample(x, y, 200, 10, 1); /*1、交叉驗證*/ int fold = 10; param.C = 100; param.svm_type = LINEAR; svm.do_cross_validation(x, y, param, fold); /*2、訓練*/ svm.train(x, y, param); /*3、保存模型*/ string model_path = ".\\svm_model.txt"; svm.save_model(model_path); /*4、導入模型*/ string model_path_p = ".\\svm_model.txt"; svm.load_model(model_path_p); /*5、預測*/ //生成隨機測試數據 vector<double> x_test; gen_test_sample(x_test, 200, 10, 1); double prob_est; //預測 double value = svm.predict(x_test, prob_est); //打印預測類別和概率 printf("label:%f,prob:%f", value, prob_est); }
4.測試模型
模型如下:
svm_type c_svc kernel_type linear nr_class 2 total_sv 8 rho -0.0379061 label 1 2 probA -3.05015 probB 0.103192 nr_sv 2 6 SV 0.002455897026356498 1:5 2:0 3:0 4:2 5:4 6:0 7:1 8:4 9:5 10:3 0.007680247728335155 1:2 2:3 3:6 4:0 5:3 6:0 7:1 8:0 9:2 10:1 -0.000110773050020484 1:-1 2:-3 3:-1 4:-2 5:-5 6:-1 7:-2 8:-4 9:-0 10:-6 -0.002310331085133643 1:-1 2:-0 3:-1 4:-2 5:-3 6:-5 7:-5 8:-5 9:-0 10:-4 -0.001462570160622233 1:-5 2:-6 3:-2 4:-9 5:-0 6:-0 7:-2 8:-0 9:-1 10:-0 -0.002824751492599935 1:-0 2:-2 3:-5 4:-0 5:-0 6:-0 7:-3 8:-8 9:-1 10:-0 -0.003207598246179264 1:-2 2:-4 3:-3 4:-1 5:-1 6:-7 7:-1 8:-2 9:-1 10:-1 -0.0002201207201360932 1:-4 2:-2 3:-1 4:-7 5:-0 6:-2 7:-0 8:-4 9:-1 10:-6
測試樣本的類別如下:
label:1.000000,prob:0.994105