机器学习实验—使用LibSVM

来源:互联网 发布:淘宝上怎么买到a货翡翠 编辑:程序博客网 时间:2024/05/22 14:04

一、实验目的和内容

        1、掌握数据预处理的方法,对训练集数据进行预处理;
        2、掌握分类算法的原理,基于有监督的机器学习方法,训练文本分类器;
        3、了解SVM机器学习方法,可以运用开源工具LibSVM完成文本分类过程并且根据测试文档已给出的Lable标签计算准确率。

二、实验过程

        1、下载并解压解压LibSVM开源工具
        Libsvm是一个开源的软件包,需要者都可以免费的从作者的个人主页http://www.csie.ntu.edu.tw/~cjlin/ 处获得。下载地址为:  

http://www.csie.ntu.edu.tw/~cjlin/libsvm/index.html


        解压后的工具包内容包含Python、Java、C++版本以及可以直接在cmd状态下运行的exe文件。


        2、在VS环境下使用LibSVM
        开源工具包里面已经包含了用于C++环境编译的源码,新建一个工程,将svm.h、svm.cpp、svm-train.cpp导入,然后新建一个C++文件svm-example.cpp,这个文件里面调用了svm.h、svm.cpp、svm-train.cpp的相关内容。svm-example.cpp源码如下:

#include "svm.h"#include <string.h>#include<iostream>using namespace std;svm_parameter param;void init_param(){param.svm_type = C_SVC;param.kernel_type = RBF;param.degree = 3;param.gamma = 0.0078125;param.coef0 = 0;param.nu = 0.5;param.cache_size = 1024;param.C = 2048;param.eps = 1e-5;param.shrinking = 1;param.probability = 0;param.nr_weight = 0;param.weight_label = NULL;param.weight = NULL;}int main(){init_param();svm_problem prob;prob.l = 8285; //样本数prob.y = new double[prob.l];double d;int probfeature = 11; //样本特征维数if (param.gamma == 0) param.gamma = 0.5;svm_node *x_space = new svm_node[(probfeature + 1)*prob.l];//样本特征存储空间prob.x = new svm_node *[prob.l]; //每一个X指向一个样本cout << "size: " << sizeof(x_space) << endl;///////////////////////////////////////////////////double *p = (double *)malloc(100000 * sizeof(double));double *q = (double *)malloc(100000 * sizeof(double));int k = 0, train_num = 0, test_num = 0;FILE *ftrain,*ftest;//文件指针ftrain = fopen("train.txt", "r");//以文本方式打开训练文件。ftest = fopen("test.txt", "r");//以文本方式打开测试文件。if (ftrain == NULL) //打开文件出错。return -1;if (ftest == NULL) //打开文件出错。return -1;while (fscanf(ftrain, "%lf", &p[k]) != EOF) //读取数据到数组,直到文件结尾(返回EOF){k++;}train_num = k / probfeature;//统计训练样本数量k = 0;while (fscanf(ftest, "%lf", &q[k]) != EOF) //读取数据到数组,直到文件结尾(返回EOF){k++;}test_num = k / probfeature; //统计测试样本数量/////////////////////////////////////////////////////int count = 0;double judge = 1;int i = 0,j=0;while (j<train_num){x_space[i].index = 1;x_space[i].value = p[i];x_space[i+1].index = 2;x_space[i+1].value = p[i+1];x_space[i+2].index = 3;x_space[i + 2].value = p[i + 2];x_space[i + 3].index = 4;x_space[i + 3].value = p[i + 3];x_space[i + 4].index = 5;x_space[i + 4].value = p[i + 4];x_space[i + 5].index = 6;x_space[i + 5].value = p[i + 5];x_space[i + 6].index = 7;x_space[i + 6].value = p[i + 6];x_space[i + 7].index = 8;x_space[i + 7].value = p[i + 7];x_space[i + 8].index = 9;x_space[i + 8].value = p[i + 8];x_space[i + 9].index = 10;x_space[i + 9].value = p[i + 9];x_space[i+10].index = -1;prob.x[j] = &x_space[i];prob.y[j] = p[i + 10];i += 11;j++;}svm_model *model = svm_train(&prob, ¶m);svm_node xnode[11];i = 0, j = 0;while (j<8285){xnode[0].index = 1;xnode[0].value = q[i];xnode[1].index = 2;xnode[1].value = q[i + 1];xnode[2].index = 3;xnode[2].value = q[i + 2];xnode[3].index = 4;xnode[3].value = q[i + 3];xnode[4].index = 5;xnode[4].value = q[i + 4];xnode[5].index = 6;xnode[5].value = q[i + 5];xnode[6].index = 7;xnode[6].value = q[i + 6];xnode[7].index = 8;xnode[7].value = q[i + 7];xnode[8].index = 9;xnode[8].value = q[i + 8];xnode[9].index = 10;xnode[9].value = q[i + 9];xnode[10].index = -1;d = svm_predict(model, xnode);cout << d << " " << endl;if (d == q[i + 10]){count++;}i += 11;j++;}cout << "用于训练模型的样本数量:" << train_num << endl;cout << "用于测试模型的样本数量:" << test_num << endl;cout << "测试准确的个数为:" << count << endl;double x = 0;x = (double)count / test_num;cout << "准确率为:"<<x << endl;delete[] x_space;delete[] prob.x;delete[] prob.y;}
        3、主要函数
        在源程序里面,主要由以下2个函数来实现:
        (1) struct svm_model *svm_train(const struct svm_problem *prob, const struct svm_parameter *param);
        该函数用来做训练,参数prob,是svm_problem类型数据,具体结构定义如下:

struct svm_problem //存储本次参加运算的所有样本(数据集),及其所属类别。{int n; //记录样本总数double *y; //指向样本所属类别的数组struct svm_node **x; //指向一个存储内容为指针的数组};其中svm_node的结构体定义如下:struct svm_node //用来存储输入空间中的单个特征{int index; //输入空间序号,假设输入空间数为mdouble value; //该输入空间的值};
        所以,prob也可以说是问题的指针,它指向样本数据的类别和输入向量,在内存中的具体结构图如下:

        只需在内存中申请n*(m+1)*sizeof(struct svm_node)大小的空间,并在里面填入每个样本的每个输入空间的值,即可在程序中完成prob参数的设置。
        参数param,是svm_parameter数据结构,具体结构定义如下:

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 */

        只需申请一个svm_parameter结构体,并按实际需要设定SVM类型、核函数和各种参数的值即可完成参数param的设置。
        设定完这两个参数,就可以直接在程序中调用训练函数进行训练了,该其函数返回一个struct svm_model *SVM模型的指针,可以使用svm_save_model(const char *model_file_name, const struct svm_model *model)函数,把这个模型保存在磁盘中。至此,训练函数的移植已经完成。
        (2) double svm_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结构体的指针,本意是一个输入空间的指针,但实际上,该函数执行的时候,是从参数x处计算输入空间,直到遇到单个样本数据结束标记-1才结束,也就是说,该函数运算了单个样本中的所有输入空间数据。因此,在调用此函数时,必须先把预测样本的数据按图3.4中的固定格式写入内存中。另外,该函数只能预测一个样本的值,本文需要对图像中的所有像数点预测,就要使用for循环反复调用。
        该函数返回一个double类型,指明被预测数据属于哪个类。面对两分类问题的时候,通常使用+1代表正样本,即类1;-1代表负样本,即类2。最后根据返回的double值就可以知道预测数据的类别了。

        4、导入数据,编译运行,查看结果
        该实验提供了训练数据train.data和test.data,在使用前,将其转换为txt格式的文本。

三、实验结果

        以下是实验结果:
        训练的模型参数:

        使用模型分类的效果:



        其中有属于1类别的,也有属于-1类别的。
        实验过程中还统计了对训练、测试样本数量的统计以及计算了分类的转确率。该准确率为0.94305.


四、总结

        此次试验得以顺利完成,也得益于一个博客中对于LibSVM分类的示例,他的例子是将4个样本作为样例训练模型,维度为2,样本内容为男女生的身高体重,然后用了2个测试样本进行测试,依葫芦画瓢,跟着这个示例的思路,如何将八千多的训练样本以及两千多的测试样本使用起来就值得思考了,而且维度是11。通过这个实验,我掌握了工具的流程:按照Libsvm软件包所要求的格式准备数据集,要求格式为:<label> <index1>:<value1> <index2>:<value2> ,对于index,我直接赋值,lable是训练样本文件的每一个样本的最后一维数据;对数据进行简单的缩放操作;选用RBF ,param.kernel_type = RBF; 采用交叉验证选择最佳参数C与g;采用最佳参数C与g对整个训练集进行训练获取支持向量机模型;利用获取的模型进行测试与预测。
        本实验还存在的问题是,实验初期对于测试样本测试的结果全属于-1类,观察训练数据发现:8285个样本中有7819个数据属于-1类别,也就是说-1类别占比大约94.4%,所以也可以解释为什么测试结果全属于-1,从这里可以看出训练数据质量并不好,不能训练处良好的SVM模型。为了能够做出改进,那么就应该考虑到对LibSVM参数调优,比较重要的参数是 gamma (-g) 跟 cost (-c) ,而cross validation (-v)的参数常用5。
        参数调优的方法:使用libsvm 的 python 子目录下面的 grid.py。此时。其中安装python2.5需要(一般默认安装到c:/python25下),将gnuplot解压。安装解压完毕后,进入/libsvm/tools目录下,用文本编辑器(记事本,edit都可以)修改grid.py文件,找到其中关于gnuplot路径的那项(其默认路径为
gnuplot_exe=r"c:/tmp/gnuplot/bin/pgnuplot.exe"),根据实际路径进行修改,并保存。然后,将grid.py和C:/Python25目录下的python.exe文件拷贝到libsvm/windows目录下,键入以下命令:$ python grid.py train.1.scale 执行后,即可得到最优参数c和g。(该方法来源于博客http://blog.csdn.net/chl033/article/details/4645544)。
根据以上方法调出的参数为gamma = 0.0078125; C = 2048;再次测试会发现测试结果既有-1也有1,但是准确率94.3050%,调整前的准确率为94.3533%。


        注:实验示例代码下载地址http://download.csdn.net/detail/dmxexcalibur/9837291

0 0