文档章节

libsvm代码阅读(1):基础准备与svm.h头文件

y
 yunpiao
发布于 2014/12/26 13:28
字数 3248
阅读 70
收藏 0

libsvm是国立台湾大学Chih-Jen Lin开发的一个SVM的函数库,是当前应用最广泛的svm函数库,从2000年到2010年,该函数库的下载量达到250000之多。它的最新版本是version 3.17,主要是对是svm_group_classes做了修改。

主页:LIBSVM -- A Library for Support Vector Machines   

下载地址:zip.file ortar.gz 

我下载后的解压文件如下所示:


libsvm函数包的组织结构如下

1、主文件路径:包含了核心的C/C++程序和例子数据。其中svm.cpp是svm的核心程序,它实现了svm的训练和测试算法。

2、tool子文件路径:包含了一些检验数据格式以及选择svm参数的tool。

3、其他子文件路径:主要包含pre-built 二值文件和相关语言的接口。

关于libsvm的官方实现文档LIBSVM: A Library for Support Vector Machines,本相关博文也是以这篇文档为基础的,同时本相关博文还参考了上海交大模式分析与机器智能实验室的libsvm2.6代码注释

关于svm的理论知识,本文不想多说,如果你还不是很懂,可以参考我前面的两篇文章:

支持向量机学习笔记:数学过程及经典Tutorial

支持向量机学习笔记:LIBSVM应用(人脸识别)

=======================================================

现在开始看一看libsvm的头文件svm.h

svm.h中主要是定义了4个结构体,分别是svm_node、svm_problem、svm_parameter、svm_model,然后就是19个函数的声明,函数的声明我就不先讲了,等到在svm.cpp中碰到后再细说。下面来看一看这几个结构体:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_1 height=18 name=ZeroClipboardMovie_1 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=1&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. struct svm_node  
  2. {  
  3.     int index;  
  4.     double value;  
  5. };  

这个结构体用于存储单一向量中的单个特征。例如:向量x1={0.002,0.345,4.000,5.677},则用svm_node来存储就是使用一个包含5个svm_node的数组来存储这个4维向量,内存中的表示如下:



[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_2 height=18 name=ZeroClipboardMovie_2 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=2&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. struct svm_problem  
  2. {  
  3.     int l; //记录样本总数  
  4.     double *y;//指向样本所属类别的数组  
  5.     struct svm_node **x;//指向一个存储内容为指针的数组  
  6. };  

这个结构体用于存储本次参加运算的所有样本及其所属类别,一个示意图如下(其中最右边的4个长方格如同上图中的表格):



[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_3 height=18 name=ZeroClipboardMovie_3 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=3&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. struct svm_parameter  
  2. {  
  3.     int svm_type;//SVM类型,见前enum  
  4.     int kernel_type;//核函数  
  5.     int degree; /* for poly */  
  6.     double gamma;   /* for poly/rbf/sigmoid */  
  7.     double coef0;   /* for poly/sigmoid */  
  8.   
  9.     /* these are for training only */  
  10.     double cache_size; /* in MB */  
  11.     double eps; /* stopping criteria */  
  12.     double C;   /* for C_SVC, EPSILON_SVR and NU_SVR */  
  13.     int nr_weight;      /* for C_SVC */  
  14.     int *weight_label;  /* for C_SVC */  
  15.     double* weight;     /* for C_SVC */  
  16.     double nu;  /* for NU_SVC, ONE_CLASS, and NU_SVR */  
  17.     double p;   /* for EPSILON_SVR */  
  18.     int shrinking;  /* use the shrinking heuristics */  
  19.     int probability; /* do probability estimates */  
  20. };  

这个结构体用于存储svm的各个参数,知道就行。


[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_4 height=18 name=ZeroClipboardMovie_4 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=4&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. struct svm_model  
  2. {  
  3.     struct svm_parameter param; /* parameter */  
  4.     int nr_class;       /* number of classes, = 2 in regression/one class svm */  
  5.     int l;          /* total #SV */  
  6.     struct svm_node **SV;       /* SVs (SV[l]) */  
  7.     double **sv_coef;   /* coefficients for SVs in decision functions (sv_coef[k-1][l]) */  
  8.     double *rho;        /* constants in decision functions (rho[k*(k-1)/2]) */  
  9.     double *probA;      /* pariwise probability information */  
  10.     double *probB;  
  11.     int *sv_indices;        /* sv_indices[0,...,nSV-1] are values in [1,...,num_traning_data] to indicate SVs in the training set */  
  12.   
  13.     /* for classification only */  
  14.   
  15.     int *label;     /* label of each class (label[k]) */  
  16.     int *nSV;       /* number of SVs for each class (nSV[k]) */  
  17.                 /* nSV[0] + nSV[1] + ... + nSV[k-1] = l */  
  18.     /* XXX */  
  19.     int free_sv;        /* 1 if svm_model is created by svm_load_model*/  
  20.                 /* 0 if svm_model is created by svm_train */  
  21. };  

这个函数体用于保存训练后的训练模型model,在predict中要用到。
=======================================

关于extern “C”的解析

在阅读svm.h时发现了如下一段代码:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_5 height=18 name=ZeroClipboardMovie_5 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=5&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. #ifndef _LIBSVM_H  
  2. #define _LIBSVM_H  
  3. #define LIBSVM_VERSION 317  
  4.   
  5. #ifdef __cplusplus  
  6. extern "C" {  
  7. #endif  

显然,前3行代码是为了防止头文件被重复引用,那么5-6行是什么意思呢?


某企业曾经给出如下的一道面试题:为什么标准头文件都有类似以下的结构?

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_6 height=18 name=ZeroClipboardMovie_6 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=6&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. #ifndef __INCvxWorksh  
  2. #define __INCvxWorksh   
  3. #ifdef __cplusplus  
  4. extern "C" {  
  5. #endif   
  6. /*...*/   
  7. #ifdef __cplusplus  
  8. }  
  9. #endif   
  10. #endif /* __INCvxWorksh */  

分析
显然,头文件中的编译宏“#ifndef __INCvxWorksh、#define __INCvxWorksh、#endif” 的作用是防止该头文件被重复引用。

那么

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_7 height=18 name=ZeroClipboardMovie_7 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=7&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. #ifdef __cplusplus  
  2. extern "C" {  
  3. #endif   
  4. #ifdef __cplusplus  
  5. }  
  6. #endif  

的作用又是什么呢?

extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。

被extern "C"限定的函数或变量是extern类型的;extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。记住,下列语句:extern int a;仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。

与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。

被extern "C"修饰的变量和函数是按照C语言方式编译和连接的;

未加extern “C”声明时的编译方式

首先看看C++中对类似C的函数是怎样编译的。作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_8 height=18 name=ZeroClipboardMovie_8 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=8&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. void foo( int x, int y );  

该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。

未加extern "C"声明时的连接方式

假设在C++中,模块A的头文件如下:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_9 height=18 name=ZeroClipboardMovie_9 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=9&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. // 模块A头文件 moduleA.h  
  2. #ifndef MODULE_A_H  
  3. #define MODULE_A_H  
  4. int foo( int x, int y );  
  5. #endif  

在模块B中引用该函数:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_10 height=18 name=ZeroClipboardMovie_10 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=10&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. // 模块B实现文件 moduleB.cpp  
  2. #include "moduleA.h"  
  3. foo(2,3);  

实际上,在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!

加extern "C"声明后的编译和连接方式

加extern "C"声明后,模块A的头文件变为:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_11 height=18 name=ZeroClipboardMovie_11 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=11&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. // 模块A头文件 moduleA.h  
  2. #ifndef MODULE_A_H  
  3. #define MODULE_A_H  
  4. extern "C" int foo( int x, int y );  
  5. #endif  

在模块B的实现文件中仍然调用foo( 2,3 ),其结果是:

(1)模块A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;

(2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。

所以,可以用一句话概括extern “C”这个声明的真实目的:实现C++与C及其它语言的混合编程
  
extern "C"的惯用法

(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_12 height=18 name=ZeroClipboardMovie_12 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=12&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. extern "C"  
  2. {  
  3. #include "cExample.h"  
  4. }  

而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。笔者编写的C++引用C函数例子工程中包含的三个文件的源代码如下:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_13 height=18 name=ZeroClipboardMovie_13 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=13&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. /* c语言头文件:cExample.h */  
  2. #ifndef C_EXAMPLE_H  
  3. #define C_EXAMPLE_H  
  4. extern int add(int x,int y);  
  5. #endif  
  6. /* c语言实现文件:cExample.c */  
  7. #include "cExample.h"  
  8. int add( int x, int y )  
  9. {  
  10.   return x + y;  
  11. }  
  12. // c++实现文件,调用add:cppFile.cpp  
  13. extern "C"   
  14. {  
  15. #include "cExample.h"  
  16. }  
  17. int main(int argc, char* argv[])  
  18. {  
  19.   add(2,3);   
  20.   return 0;  
  21. }  

如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { }。

(2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。
笔者编写的C引用C++函数例子工程中包含的三个文件的源代码如下:

[cpp]   view plain copy 在CODE上查看代码片 派生到我的代码片
<EMBED id=ZeroClipboardMovie_14 height=18 name=ZeroClipboardMovie_14 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=14&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. //C++头文件 cppExample.h  
  2. #ifndef CPP_EXAMPLE_H  
  3. #define CPP_EXAMPLE_H  
  4. extern "C" int add( int x, int y );  
  5. #endif  
  6. //C++实现文件 cppExample.cpp  
  7. #include "cppExample.h"  
  8. int add( int x, int y )  
  9. {  
  10.    return x + y;  
  11. }  
  12. /* C实现文件 cFile.c 
  13. /* 这样会编译出错:#include "cExample.h" */  
  14. extern int add( int x, int y );  
  15. int main( int argc, char* argv[] )  
  16. {  
  17.   add( 2, 3 );   
  18.   return 0;  
  19. }  

上面关于extern C的解析转载自:C++中extern “C”含义深层探索

其他关于extern C 的知识可参见博文:

C++项目中的extern "C" {}

面试之C++:extern及extern “C”用法

本文转载自:http://blog.csdn.net/linj_m/article/details/19498747

共有 人打赏支持
y
粉丝 2
博文 41
码字总数 71902
作品 0
海淀
私信 提问
LibSvm使用说明和LibSvm源码解析

kernel_type rbf //训练采用的核函数类型,此处为RBF核gamma 0.0769231 //RBF核的参数γnr_class 2 //类别数,此处为两分类问题total_sv 132 //支持向量总个数rho 0.424462 //判决函数的偏置...

haoji007
2018/05/13
0
0
MATLAB安装libsvm工具箱的方法

支持向量机(support vector machine,SVM)是机器学习中一种流行的学习算法,在分类与回归分析中发挥着重要作用。基于SVM算法开发的工具箱有很多种,下面我们要安装的是十分受欢迎的libsvm工...

东聃
2018/08/12
0
0
python下使用libsvm:计算点到超平面的距离

最近在看的资料里涉及到计算 点到支持向量机分类超平面的距离 这一点内容,我使用的svm是libsvm。 由于是新手,虽然看了一些资料,但中英转换误差等等原因导致经常出现理解错误,因此对libsv...

小梳子一直走
2014/03/17
0
0
【毕设进行时-工业大数据,数据挖掘】LIBSVM 初步测试

正文之前 打摆子的日子很快就要一去不复返了。想想有点悲伤。今天做了下LibSVM的初步运用,也写了个从数据库读取数据,然后改造成LibSVM需要的数据格式的类,需要的自取。 正文 这是个从前面...

HustWolf
2018/04/23
0
0
sparkmlib的sample_binary_classification_data.txt 和sample_libsvm_data.txt内容怎么换成实际项目内容

sample_binary_classification_data.txt 和sample_libsvm_data.txt的内容怎么理解和使用 sparkmlib的sample_binary_classification_data.txt 和sample_libsvm_data.txt内容怎么换成实际项目内......

知行合一1
2017/09/30
20
0

没有更多内容

加载失败,请刷新页面

加载更多

jenkins安装

https://my.oschina.net/u/593517/blog/1797968 jenkins 安装 https://my.oschina.net/u/593517/blog/3028175 GIT 安装 https://my.oschina.net/u/593517/blog/3028179 maven 安装 插件安装 ......

Gm_ning
9分钟前
1
0
小言服务端解决方案-监控

框架保证方向,整体包容细节 为保证服务端运行平稳正常,owner应使得系统应保有相应的监控:系统监控,业务监控。而服务运行的平稳高效是否有保障跟监控粒度又成直接的正比关系。本文仅针对开...

重城重楼
21分钟前
0
0
搜索引擎(Elasticsearch搜索详解)

学完本课题,你应达成如下目标: 掌握ES搜索API的规则、用法。 掌握各种查询用法 搜索API 搜索API 端点地址 GET /twitter/_search?q=user:kimchy GET /twitter/tweet,user/_search?q=user:...

这很耳东先生
45分钟前
6
0
浅谈如何减少GC的次数

GC会stop the world。会暂停程序的执行,带来延迟的代价。所以在开发中,我们不希望GC的次数过多。 本文将讨论如何在开发中改善各种细节,从而减少GC的次数。 (1)对象不用时最好显式置为 Nu...

浮躁的码农
46分钟前
1
0
jpa 自定义返回对象

任何ORM框架都少不了开放自定义sql的问题。jpa自然也不例外,很多场景需要写复杂sql的。 首先定义一个方法签名,然后打上@Query注解。像下面这样,需要注意nativeQuery,这个表示query中的字...

朝如青丝暮成雪
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部