【機器學習】用libsvm C++訓練SVM模型
阿新 • • 發佈:2019-01-06
前言:本文大水文一篇,大神請繞道。在正文之前,首先假設讀者都已經瞭解SVM(即支援向量機)模型。
1. introduction
libsvm是臺灣大學林智仁(Chih-Jen Lin)教授於2001年開發的一套支援向量機的工具包,可以很方便地對資料進行分類或者回歸分析。使用時,只需要把訓練資料按照它的格式打包,然後直接喂進去訓練即可。我這裡的資料是儲存在mat檔案的,資料怎麼匯入這裡略去不說(以下內容提及的特徵向量和一個樣本是一回事)。
2. prepare
幾個重要的資料結構
2.1
struct svm_problem
{
int l; // 記錄樣本的總數
double *y; // 樣本所屬的標籤(+1, -1)
struct svm_node **x; // 指向樣本資料的二維陣列(即一個矩陣,行數是樣本數,列數是特徵向量維度)
};
2.2
struct svm_node
{
int index;
double value;
};
svm_node是用來儲存單個樣本資料的,打個比方說,svm_problem是一群羊,那麼svm_node就是這一群羊中的一隻。需要注意的是,svm_node的儲存空間應該比特徵數大一位,最後一位index值必須以-1結束。比如:
svm_node* node = new svm_node[1 + feature_size];
for (int j = 0; j < feature_size; j++)
{
node[j].index = j + 1;
node[j].value = xdata[j];
}
node[feature_size].index = -1;
return node;
2.3
struct svm_parameter
{
int svm_type;// SVM的型別
int kernel_type;// 核函式
double degree;// 多項式引數
double gamma;// 核函式為poly/rbf/sigmoid的引數
double coef0;// 核函式為poly/sigmoid的引數
//下面是訓練所需的引數
double cache_size;// 訓練所需的記憶體MB為單位
double eps;// 訓練停止的標準(誤差小於eps停止)
double C;// 懲罰因子,越大訓練時間越長
int nr_weight;// 權重的數目,目前只有兩個值,預設為0
int *weight_label;// 權重,元素個數由nr_weight決定
double* weight;// C_SVC權重
double nu;
double p;
int shrinking;// 訓練過程是否使用壓縮
int probability;// 是否做概率估計
};
3. 訓練你的模型
在vs建立一個工程,把libsvm裡的svm.h和svm.cpp匯入你的專案中。
3.1 準備訓練資料
svm_problem prob;
svm_parameter param;
/*train_x,train_y是我已經匯入的資料,分別是樣本及其對應的類別標籤*/
void init_svm_problem()
{
prob.l = train_size; // 訓練樣本數
prob.y = new double[train_size];
prob.x = new svm_node*[train_size];
svm_node* node = new svm_node[train_size*(1 + feature_size)];
prob.y = vec2arr(train_y);
// 按照格式打包
for (int i = 0; i < train_size; i++)
{
for (int j = 0; j < feature_size; j++)
{ // 看不懂指標就得複習C語言了,類比成二維陣列的操作
node[(feature_size + 1) * i + j].index = j + 1;
node[(feature_size + 1) * i + j].value = train_x[i][j];
}
node[(feature_size + 1) * i + feature_size].index = -1;
prob.x[i] = &node[(feature_size + 1) * i];
}
}
3.2 設定訓練引數
void init_svm_parameter()
{
param.svm_type = C_SVC; // 即普通的二類分類
param.kernel_type = RBF; // 徑向基核函式
param.degree = 3;
param.gamma = 0.01;
param.coef0 = 0;
param.nu = 0.5;
param.cache_size = 1000;
param.C = 0.09;
param.eps = 1e-5;
param.p = 0.1;
param.shrinking = 1;
param.probability = 0;
param.weight_label = NULL;
param.weight = NULL;
}
上面的C和gamma是我進行調優過的,用於你的資料時應該重新調整。
3.3 進行訓練
int main()
{
load_data(); // 匯入訓練及測試資料
init_svm_problem(); // 打包訓練樣本
init_svm_parameter(); // 初始化訓練引數
svm_model* model = svm_train(&prob, ¶m);
svm_save_model("model", model); // 儲存訓練好的模型,下次使用時就可直接匯入
int acc_num = 0; // 分類正確數
//svm_model* model = svm_load_model("model");
for (int i = 0; i < test_size; i++)
{
svm_node* node = init_test_data(vec2arr(xdata[i]));
double pred = svm_predict(model, node);
if (pred == label[i])
acc_num++;
}
cout << "accuracy: " << acc_num*100.0 / test_size << "%" << endl;
cout << "classification: " << acc_num << " / " << test_size << endl;
return 0;
}
3.4 訓練及測試結果
正確率幾乎達到100%,可見分類效果是非常好的(這也是我一直鍾情於SVM的原因)!
訓練你的模型吧!