【LIBSVM】使用C++和LIBSVM实现机器学习+样本分类

一、了解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中的多类分类就是根据这个方法实现的


发布了25 篇原创文章 · 获赞 63 · 访问量 16万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章