標籤:
原文:http://blog.csdn.net/u014691453/article/details/40393137
軟體版本:
Visual Studio版本:VS2012
(註:使用方法在 VS2010 上面親測同樣可用,只是可能會出現的問題是: VS2010 和 VS2012 之間有些函數寫法不同,需在編譯前做改動,譬如 VS2010 的 scanf 對比 VS2012 的 scanf_s )
libsvm版本:libsvm-3.18
聲明:
如果你找到了我這篇標題的文章,那很大程度上意味著你一定是在 VS 裡或者 MFC 裡使用 libsvm 工具箱出現了問題。那你也一定知道了,libsvm 在VS工程裡的使用與它在 matlab 或者是 python 裡面的使用,是多麼的不同。
致謝:
如果沒有 gg 百分之百的協助,就憑我在 VS 裡編程的菜鳥能力,是絕對不可能1天就搞好的,我絕對會弄半年不止所以非常感謝非常感謝,今後我一定認真學習編程。
本文:
分三部分:第一部分:先把 VS 工程的架構建好
第二部分:工程裡包含主函數的 cpp 檔案要怎麼寫
第三部分:把 libsvm 工具箱移植到 MFC 中進行使用
第四部分:在本文代碼中能夠正確啟動並執行資料格式
資源地址:如何在Visual Studio(VS)裡使用libsvm工具箱(與博文相關的程式碼封裝)
第一部分:先把 VS 工程的架構建好之前一直都是在 matlab 裡面使用這個 libsvm 工具箱,也曾經有一段時間在 python 裡面使用過,剛接觸的時候還在 cmd 控制台上面使用過,libsvm 的編寫者真的很贊,開啟此工具箱我們看到的情景是這樣的:
對於java、matlab、python、windows 都有相對應的檔案夾,檔案夾裡面的內容和中的這個 readme 就可以協助你在以上四種情況下使用了。
那在 VS 裡該怎麼使用呢?
首先,我們先建立一個Win32控制台項目,起名為 MM ,如下步驟:
然後,把 libsvm 檔案夾裡的這幾個檔案(如),都拷貝到 MM 項目的檔案夾(如下)裡去,以備後續使用。
然後,在 MM 項目裡,我們把 svm.h 和 svm.cpp 分別添加到標頭檔和源檔案裡面去,如:
然後,在源檔案裡,右鍵——建立項:MM.cpp
接下來是第二部分
第二部分:工程裡包含主函數的 cpp 檔案要怎麼寫MM.cpp也就是上面說的包含主函數的 cpp 檔案,我們最終要執行的就是它。首先,我們先給 MM.cpp 裡寫好需要用到的標頭檔,如下:
[cpp] view plaincopy
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #include <list>
- #include <fstream>
- #include <iostream>
- #include "svm.h"
- using namespace std;
接下來的內容大致分五部分:第一:讀入訓練資料和測試資料第二:構建參數 param 的結構體(主要目的就是讓VS知道你要用的參數都是什麼)第三:構建分類問題 prob 的結構體(主要就是把第一步讀入的資料傳遞給 svm_train 和 svm_predict)第四:主函數如下第五:結果展示 第一:讀入訓練資料和測試資料首先,在讀資料之前,先定義一些變數(整個程式還是主要參考的 libsvm/svm_toy/qt/svm_toy.cpp 這個代碼,由於目前我編程能力實在是差,所以一些冗餘的代碼我都沒有刪改,僅僅只實現了能夠使用libsvm的目的。)
[cpp] view plaincopy
- #define XLEN 10 //生產測試資料,這個感覺就是無用的地方
- #define YLEN 10
-
-
- ofstream outdata; //需要準備的所有資料的標籤label和特徵feature都是分開的,而且特徵feature前面不需要加序號冒號這種東西,只需用空格隔開就好
- ifstream indata; //所以這裡indata只包含特徵,indata_label只包含label,每一行是一個樣本的。
- ofstream outdata_lable;
- ifstream indata_lable;
-
- int NUM = 1440; //由於我的樣本特徵是 1440 維,所以讀資料的時候,我定義了個數,你可以根據自己特徵的維度對 NUM 變更。
-
- //char default_param[] = "-t 2 -c 100"; // 這個是 svm_toy.cpp 裡對參數的選擇
- char default_param[] = "-t 2 -c 4 -g 32"; // 這個是匹配我資料的參數
- struct point { //這個是後面 svm_train 拿來資料做訓練時候的資料傳入方式
- double *feature;
- //signed char value;
- int value;
- };
-
- list<point> point_list; // 通過 indata 讀進來的資料就放在 point_list 裡面
- int current_value = 1; // 路人甲變數
-
- void clear_all() // 清空鏈表
- {
- point_list.clear();
- }
然後,我們先讀入訓練資料,用來訓練模型
[cpp] view plaincopy
- void readFile1(char *file,char *file_lable) //定義讀入訓練資料的函數叫做 readFile1
- {
-
- indata.open(file,ios::in); //讀入特徵
- indata_lable.open(file_lable,ios::in); // 讀入標籤
- cout <<"read data begin"<<endl; // 屏顯
-
- clear_all();
- while(!indata.eof()) // 如果沒有讀到檔案結束,就繼續讀,知道讀完整個 traindata 檔案
- {
-
- double *line = new double[NUM];
- for(int i =0;i<NUM;i++)
- {
- indata >> line[i]; //把特徵存起來
- }
- point p;
- indata_lable >>p.value; //把label存起來
-
- p.feature = line;
- point_list.push_back(p);
- }
-
- point_list.pop_back(); //如果你的 traindata.txt 檔案資料的最後有一個空格的話,需要加上這句話,否則預測會有錯如果沒有空格,這句話就不需要了
- indata.close();
- indata_lable.close();
- cout <<"read data end"<<endl;
- }
接下來,讀入測試資料:
[cpp] view plaincopy
- void readFile2(char *file,char *file_lable) // 讀入測試檔案的函數叫做 readFile2
- {
-
- indata.open(file,ios::in); //接下來代碼和上面讀訓練資料是一個樣子的
- indata_lable.open(file_lable,ios::in); // <span style="font-family: Arial, Helvetica, sans-serif;">由於讀訓練資料之後就會建立model,然後才會讀測試資料,所以不用擔心會有資料覆蓋的問題</span>
- cout <<"read test data begin"<<endl;
- clear_all();
- while(!indata.eof())
- {
-
- double *line = new double[NUM];
- for(int i =0;i<NUM;i++)
- {
- indata >> line[i];
- }
- point p;
- indata_lable >>p.value;
-
- p.feature = line;
- point_list.push_back(p);
- }
- point_list.pop_back();
- cout <<"read test data end size = "<<point_list.size()<<endl;
- indata.close();
- indata_lable.close();
- }
第二:構建參數 param 的結構體
然後,訓練模型!:包括了構建參數param結構和構建 prob結構。由於太長了,我上傳到 CSDN 下載頁面吧,免積分下載。這段代碼主要就是參考的 svm_toy.cpp 檔案,同學們好好研讀。 資源地址:如何在Visual Studio(VS)裡使用libsvm工具箱(與博文相關的程式碼封裝)第三:構建分類問題 prob 的結構體 同第二,代碼是一起的,我會把整體代碼,包括以上寫的讀入feature和label的代碼,發到下載頁面 第四:主函數如下
[cpp] view plaincopy
- int main()
- {
- int choice;
- cout<<"1 train model\n2 test1\n3 test2\n"<<endl;
- cin >>choice;
- switch(choice)
- {
- case 1:
- {
- readFile1("traindata.txt","trainlabel.txt"); //選擇1 是訓練模型,模型會儲存為“model.txt”
- run();
- break;
- }
- case 2:
- {
- readFile2("1.txt","1_label.txt"); // 選擇2 是用第一份測試資料進行測試
- testData();
- break;
- }
- case 3:
- {
- readFile2("11.txt","11_label.txt"); // 選擇3 是用第二份測試資料進行測試
- testData();
- break;
- }
-
- }
- system("pause");
- return 0;
- }
第五:結果展示等啊等~ 訓練結束啦~接下來就是測試了: 第三部分:把 libsvm 工具箱移植到 MFC 中進行使用 其實在 MFC 中的使用,和libsvm在普通VS 項目中的使用是一樣的。如果你不想在MFC 裡訓練 model,那麼完全可以把在第二部分中訓練出來的 model 拷貝到 MFC 的這個項目裡面,然後在 測試資料之前,把 model load進去就好了,具體load方式參照My Code裡
test()函數裡寫的內容。那麼在 MFC 的代碼裡,你就可以把第二部分代碼中的
void run() ,即訓練模型部分去掉了
註:我就是在 MFC 裡使用 libsvm 的時候,發現了 VS2010 和 VS2012 的不同,svm.cpp 和 svm.h 這兩個檔案裡的一些函數,可以在 VS2010 的 MFC 工程裡跑的很好,可是到了 VS2012 裡就會提示說 “你把 XX 換成 XX_s 會更安全”,是錯誤而不是警告,所以如果在 VS2012 裡面用libsvm的話,需要在svm.cpp 和 svm.h 這兩個檔案裡修改部分函數的表達形式,這個在網上都有解答,可以自行查詢。 第四部分:在本文代碼中能夠正確啟動並執行資料格式在第二部分給出的資源連結裡,我上傳的資料裡包括我的測試資料,1.txt和1_label.txt 就是test1測試的內容,11.txt 和 11_label.txt 就是test2 測試的內容,如果你要測試你自己的資料,記得在
test()代碼裡更改成你自己資料的名稱。 可以使用的資料格式: 大家可以參照我上傳的 1.txt,大致如以下: 0.564 0.436 0.675 0.453 0.000 0.345 0.354 0.345 0.456 0.000 0.346 0.645 0.678 0.678 0.866 0.757 0.575 0.867 0.866 0.865 0.856 0.867 0.557 0.754 我的1_label.txt,大致如以下: 1122 如以上格式,因為我的資料是之前通過 matlab 處理過的,所以空格位置用的是 ‘\t’ 。
如果你的資料格式和我不一樣,只有兩條路: 1:改成我這樣的資料格式 2:更改代碼裡讀資料這部分的程式,保證讀進去的資料是對的就好
或者還有一條路,就是自己按照你的需要重新寫代碼。 利用了寶貴的一個下午來整理這些東西,希望能夠真的幫到像之前的我一樣有需要的人。 感覺該說的應該都說完了,如果有解釋不正確或者漏掉的地方,也請大家告知我,我會再修改
如何在Visual Studio(VS)2012裡使用libsvm工具箱