libSVM + VS2013 + C++使用介绍

				版权声明:本文为博主原创文章,未经博主允许不得转载。					https://blog.csdn.net/lhanchao/article/details/53367532				</div>
							            <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
					<div class="htmledit_views" id="content_views">

libSVM是一个非常有名的SVM开源库,最近我在做分类任务,最后需要用到SVM进行分类,可是网上对于libSVM的介绍大多是matlab的,还有就是使用DOS命令调用的,直接使用libSVM的函数进行编程的介绍非常少,我来大体介绍一下我使用的情况吧。

我对于libSVM的了解也不是很清楚,只是单纯的利用他做训练和识别而已。

一、环境搭建

我使用的VS2013 + C++作为开发的,首先下载libSVM最新的版本 http://www.csie.ntu.edu.tw/~cjlin/libsvm/,解压后如下图所示:

使用VS2013创建一个新的空工程,把上图目录中的svm.cpp和svm.h复制到工程目录下,并通过在工程中右键——Add——Exsiting Item把这两个文件添加到工程中去,如下图所示。

好了,到目前为止环境就搭建好了,简单明了~
注意:VS2013中使用fopen会出现一个错误,原因是VS2013自身兼容性不好,认为fopen不安全,可以通过 工程右键——Properties——C++——Preprocesser——Preprocesser Definitions中添加_CRT_SECURE_NO_WARNINGS解决该问题。
同时VS2013中编译会出现strdup函数编译不过去,同样根据提示,把该函数改为_strdup即可。

二、特征文件读取

我感觉网上对于libsvm有一种误导,就是你的特征文件必须要按照一定的格式来,才能够被读取训练,其实这只是对于使用dos命令行调用libsvm时的规定,因为libsvm自定义的特征文件格式是与其读取相匹配的。
如果我们使用自己的读取文件函数,则完全不用拘束于这种格式,只要我们在读取函数之中与我们自己的特征文件格式相匹配即可。
在libsvm中,与读取特征文件相关的类型为svm_problem。这个类中有三个元素,如下所示:

   
   
  1. struct svm_problem
  2. {
  3. int n; //记录样本总数
  4. double *y; //记录样本所属类别
  5. struct svm_node **x; //存储所有样本的特征,二维数组,一行存一个样本的所有特征
  6. };
其中svm_node类型的定义如下:

   
   
  1. struct svm_node //用来存储输入空间中的单个特征
  2. {
  3. int index; //该特征在特征空间中的维度编号
  4. double value; //该特征的值
  5. };
借用网上的一张图进行表示:
好了,知道在libsvm中特征是如何存储以后,就可以编写读取文件的函数了。

以我使用svm举例,我的特征文件如下图所示:

我使用SVM的类的头文件如下所示:

   
   
  1. #include "svm.h"
  2. #include <vector>
  3. #include <list>
  4. #include <iostream>
  5. #include <Windows.h>
  6. class ClassificationSVM
  7. {
  8. public:
  9. ClassificationSVM();
  10. ~ClassificationSVM();
  11. void train(const std::string& modelFileName);
  12. void predict(const std::string& featureaFileName, const std::string& modelFileName);
  13. private:
  14. void setParam();
  15. void readTrainData(const std::string& featureFileName);
  16. private:
  17. svm_parameter param;
  18. svm_problem prob; //all the data for train
  19. std:: list<svm_node*> dataList; //list of features of all the samples
  20. std:: list< double> typeList; //list of type of all the samples
  21. int sampleNum;
  22. //bool* judgeRight;
  23. };
其中 setParam函数设置如下所示:

   
   
  1. void ClassificationSVM::setParam()
  2. {
  3. param.svm_type = C_SVC;
  4. param.kernel_type = RBF;
  5. param.degree = 3;
  6. param.gamma = 0.5;
  7. param.coef0 = 0;
  8. param.nu = 0.5;
  9. param.cache_size = 40;
  10. param.C = 500;
  11. param.eps = 1e-3;
  12. param.p = 0.1;
  13. param.shrinking = 1;
  14. param.nr_weight = 0;
  15. param.weight = NULL;
  16. param.weight_label = NULL;
  17. }

我的读取文件的函数如下所示:

   
   
  1. void ClassificationSVM::readTrainData( const string& featureFileName)
  2. {
  3. FILE *fp = fopen(featureFileName.c_str(), "r");
  4. if (fp == NULL)
  5. {
  6. cout << "open feature file error!" << endl;
  7. return;
  8. }
  9. fseek(fp, 0L, SEEK_END);
  10. long end = ftell(fp);
  11. fseek(fp, 0L, SEEK_SET);
  12. long start = ftell(fp);
  13. //读取文件,直到文件末尾
  14. while (start != end)
  15. {
  16. //FEATUREDIM是自定义变量,表示特征的维度
  17. svm_node* features = new svm_node[FEATUREDIM + 1]; //因为需要结束标记,因此申请空间时特征维度+1
  18. for ( int k = 0; k < FEATUREDIM; k++)
  19. {
  20. double value = 0;
  21. fscanf(fp, "%lf", &value);
  22. features[k].index = k + 1; //特征标号,从1开始
  23. features[k].value = value; //特征值
  24. }
  25. features[FEATUREDIM].index = -1; //结束标记
  26. char c;
  27. fscanf(fp, "\n", &c);
  28. char name[ 100];
  29. fgets(name, 100, fp);
  30. name[ strlen(name) - 1] = '\0';
  31. //negative sample type is 0
  32. int type = 0;
  33. //positive sample type is 1
  34. if (featureFileName == "PositiveFeatures.txt")
  35. type = 1;
  36. dataList.push_back(features);
  37. typeList.push_back(type);
  38. sampleNum++;
  39. start = ftell(fp);
  40. }
  41. fclose(fp);
  42. }
其中dataList和typeList分别存放特征值和该特征样本对应的标号(正或负)。
**修改**
dataList和typeList的声明如下:
std::list<svm_node*> dataList;//list of features of all the samples 
std::list<double>  typeList;//list of type of all the samples

三、svm训练和识别

训练时的代码如下图所示:

   
   
  1. void ClassificationSVM::train( const string& modelFileName)
  2. {
  3. cout << "reading positivie features..." << endl;
  4. readTrainData( "PositiveFeatures.txt");
  5. cout << "reading negative features..." << endl;
  6. readTrainData( "NegativeFeatures.txt");
  7. cout << sampleNum << endl;
  8. prob.l = sampleNum; //number of training samples
  9. prob.x = new svm_node *[prob.l]; //features of all the training samples
  10. prob.y = new double[prob.l]; //type of all the training samples
  11. int index = 0;
  12. while (!dataList.empty())
  13. {
  14. prob.x[index] = dataList.front();
  15. prob.y[index] = typeList.front();
  16. dataList.pop_front();
  17. typeList.pop_front();
  18. index++;
  19. }
  20. cout << "start training" << endl;
  21. svm_model *svmModel = svm_train(&prob, ¶m);
  22. cout << "save model" << endl;
  23. svm_save_model(modelFileName.c_str(), svmModel);
  24. cout << "done!" << endl;
  25. }
prob是svm_problem类型的对象,就是把之前读取的特征全部放入svm_problem的对象中。
svm_train和svm_save_model函数都是libsvm中自带的,在svm.h中定义。

分类时的代码如下所示:

   
   
  1. void ClassificationSVM::predict( const string& featureFileName, const string& modelFileName)
  2. {
  3. std:: vector< bool> judgeRight;
  4. svm_model *svmModel = svm_load_model(modelFileName.c_str());
  5. FILE *fp;
  6. if ((fp = fopen(featureFileName.c_str(), "rt")) == NULL)
  7. return;
  8. fseek(fp, 0L, SEEK_END);
  9. long end = ftell(fp);
  10. fseek(fp, 0L, SEEK_SET);
  11. long start = ftell(fp);
  12. while (start != end)
  13. {
  14. svm_node* input = new svm_node[FEATUREDIM + 1];
  15. for ( int k = 0; k<FEATUREDIM; k++)
  16. {
  17. double value = 0;
  18. fscanf(fp, "%lf", &value);
  19. input[k].index = k + 1;
  20. input[k].value = value;
  21. }
  22. char c;
  23. fscanf(fp, "\n", &c);
  24. char name[ 100];
  25. fgets(name, 100, fp);
  26. name[ strlen(name) - 1] = '\0';
  27. input[FEATUREDIM].index = -1;
  28. int predictValue = svm_predict(svmModel, input);
  29. if (featureFileName == "positive_test.txt")
  30. {
  31. if (predictValue == 0)
  32. judgeRight.push_back( false);
  33. else
  34. judgeRight.push_back( true);
  35. }
  36. else if (featureFileName == "negative_test.txt")
  37. {
  38. if (predictValue == 1)
  39. judgeRight.push_back( false);
  40. else
  41. judgeRight.push_back( true);
  42. }
  43. start = ftell(fp);
  44. }
  45. fclose(fp);
  46. int correctNum = 0;
  47. int totalNum = judgeRight.size();
  48. for ( int i = 0; i < totalNum; i++)
  49. {
  50. if (judgeRight[i] == true)
  51. correctNum++;
  52. }
  53. double precent = 1.0 * correctNum / totalNum;
  54. cout << precent << endl;
  55. }
分类时的代码与之前的代码非常类似,不多做赘述。

最后注意:标准的svm只支持二分类问题,对于多分类问题,需要进行其他的处理操作。




				版权声明:本文为博主原创文章,未经博主允许不得转载。					https://blog.csdn.net/lhanchao/article/details/53367532				</div>
							            <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
					<div class="htmledit_views" id="content_views">

猜你喜欢

转载自blog.csdn.net/monk1992/article/details/88554742