libsvm在vc下的使用

LIBSVM软件包是台湾大学林智仁(Chih-Jen Lin)博士等用C++实现的SVM库,并且拥有matlab,perl等工具箱或者代码,移植和使用都比较方便.它可以解决分类问题(包括C-SVC、n-SVC)、回归问题(包括e-SVR、n-SVR)以及分布估计(one-class-SVM )等问题,提供了线性、多项式、径向基和S形函数四种常用的核函数供选择,可以有效地解决多类问题、交叉验证选择参数、对不平衡样本加权、多类问题的概率估计等。
    在Windows环境下,此软件包只提供DOS工具集(主要包括:训练工具svmtrain.exe,预测工具svmpredict.exe,缩放数据工具svmscale.exe和二维演示工具svmtoy.exe.
如果没有工具箱,可以从这里下载,
文件:libsvm-2.89.rar
大小:515KB
下载:下载
或者从林智仁的主页上下载:http://www.csie.ntu.edu.tw/~cjlin/
这里是2.89版本的,在他的windows下有几个工具,主要使用的是svm-scale.exe用来实现数据缩放归一化,svm-train.exe用来训练数据,生成向量机的model模型,svm-predict.exe用来预测.
具体的使用方法已经在以前的文章中说明过了,现在主要说明下在vc下怎么实现使用libsvm.
 
在源程序里面,主要由以下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;               //输入空间序号,假设输入空间数为m
       double value;        //该输入空间的值
};
所以,prob也可以说是问题的指针,它指向样本数据的类别和输入向量,在内存中的具体结构图如下:
 
图1.1LIBSVM训练时,样本数据在内存中的存放结构
只需在内存中申请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值就可以知道预测数据的类别了。
上面函数的解释表明LIBSVM做分类的具体内部实现过程。那么,就可以不用DOS工具,直接通过调用LIBSVM函数来实现分类,也就可以直接让LIBSVM嵌入到原有程序中。下面是LIBSVM2.83移植到本文程序中做两分类的步骤:
(1) 拷贝svm.h和svm.cpp文件到源工程目录下,并添加到工程中;
(2) 用既定格式保存样本点信息到内存中,并设置好训练参数;
(3) 调用训练函数,训练得到支持向量器并以文件形式保存到磁盘;
(4) 导入训练好的支持向量器文件,调用预测函数对血细胞图像中的每一个点进行预测,并根据其返回结果进行分类。
其实,我们完全不必要理会他的源代码,可以通过vc调用dos中的exe程序来实现调用他所提供的windows下的几个程序,他所提供的Windows下的工具的源代码包含在所提供的源代码中.
比如在vc下调用svm-scale.exe程序,该程序的参数是-s savefilename inputfilename>outfilename
其中>是重定向符,意思是dos命令行执行该命令的时候,将屏幕输出重新定向到outfilename中.
因为在vc中使用的时候,我们需要这些屏幕输出,因此有两种方法
1)采用vc中的管道等方式来截获命令行程序输出,重定向到我们的保存文件
2)修改svm-scale.exe源代码,使他能够在没有重定向的情况下可以自己重定向或者输出到指定文件
管道的方式,有点复杂,初学vc并不是很明白,现在说下第二种方法.
查看svm-scale源代码,可以找到输出部分为下图所示部分:
 

void output(int index, double value)
{
    /* skip single-valued attribute */
    if(feature_max[index] == feature_min[index])
        return;

    if(value<=feature_min[index])
        value = lower;
    else if(value>=feature_max[index])
        value = upper;
    else
        value = lower + (upper-lower) *
            (value-feature_min[index])/
            (feature_max[index]-feature_min[index]);

    if(value != 0)
    {
        printf("%d:%g ",index, value);
        fprintf(f_out,"%d:%g ",index,value);
        new_num_nonzeros++;
    }

}

代码中红色加粗的部分就是添加的输出到文件的命令.另外还有label值的输出,换行符的输出,均采取相同的方式,在其向屏幕输出的同时向文件输出,或者干脆不让他向屏幕输出直接写文件就可以了.

这样就可以解决获得命令行输出的问题了.

现在是vc中使用ShellExecute函数来调用外部exe程序了.格式为:

 

::ShellExecute(NULL,"open","svm-train.exe",s_train_in,".\\svm\\",SW_HIDE);

其中,svm-train.exe为运行程序,s_train_in为传输给该程序的参数字符串,".\\svm\\"为程序存在的默认目录,SW_HIDE表示不显示该命令解释窗口,否则为SW_SHOW显示的时候会看到dos窗口闪一下就没有了.

ShellExecute函数的参数说明可以随意查到,不在详述.

这样我们就可以实现在vc下的libsvm的调用,并且又不需要对该算法有详细的了解.需要做的就是知道这些程序需要哪些参数.

获得参数列表可以不带参数在dos行下执行这些命令,或者查看源代码就会知道.

转载于

http://blog.chinaunix.net/uid-9634119-id-1999599.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值