LibSVM28程序代码注释Word格式文档下载.docx
- 文档编号:17118390
- 上传时间:2022-11-28
- 格式:DOCX
- 页数:37
- 大小:655.38KB
LibSVM28程序代码注释Word格式文档下载.docx
《LibSVM28程序代码注释Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《LibSVM28程序代码注释Word格式文档下载.docx(37页珍藏版)》请在冰豆网上搜索。
那么用structsvm_node来存储时就使用一个包含5个svm_node的数组来存储此4维向量,内存映象如下:
1
2
3
4
-1
0.002
0.345
4.000
5.677
空
其中如果value为0.00,该特征将不会被存储,其中(特征3)被跳过:
5
0.00不保留的好处在于,做点乘
的时候,可以加快计算速度,对于稀疏矩阵,更能充分体现这种数据结构的优势。
但做归一化时,操作就比较麻烦了。
(类型转换不再说明)
2.2structsvm_problem
structsvm_problem
intl;
double*y;
structsvm_node**x;
structsvm_problem存储本次参加运算的所有样本(数据集),及其所属类别。
在某些数据挖掘实现中,常用DataSet来实现。
intl;
记录样本总数
double*y;
指向样本所属类别或者回归值的数组。
在多类问题中,因为使用了one-agianst-one方法,可能原始样本中y[i]的内容是1.0,2.0,3.0,…,也就是属于类别1,2,3,但但参与多类计算时,参加分类的两类所对应的y[i]内容是+1,和-1。
Structsvm_node**x;
指向一个存储内容为指针的数组;
如下图,最右边的四个长条格同上表,存储三维数据。
(黑边框的是最主要的部分)
这样的数据结构有一个直接的好处,可以用x[i][j]来访问其中的某一元素(如果value为0.00的也全部保留的话)。
私下认为其中有一个败笔,就是把svm_node*x_space放到结构外面去了。
2.3enums
enum{C_SVC,NU_SVC,ONE_CLASS,EPSILON_SVR,NU_SVR};
/*svm_type*/
enum{LINEAR,POLY,RBF,SIGMOID};
/*kernel_type*/
支持向量机类型以及若干文献:
C-SVC:
C.J.C.Burges.Atutorialonsupportvectormachinesforpatternrecognition.DataMiningandKnowledgeDiscovery,2
(2):
955-974,1998.
NU_SVC:
(待补充)
2.4structsvm_parameter
structsvm_parameter
intsvm_type;
//SVM类型,见前enum
intkernel_type;
//核函数
doubledegree;
/*forpoly*/
doublegamma;
/*forpoly/rbf/sigmoid*/
doublecoef0;
/*forpoly/sigmoid*/
/*thesearefortrainingonly*/
doublecache_size;
/*inMB*/
doubleeps;
/*stoppingcriteria*/
doubleC;
/*forC_SVC,EPSILON_SVRandNU_SVR*/
intnr_weight;
/*forC_SVC*/
int*weight_label;
/*forC_SVC*/
double*weight;
doublenu;
/*forNU_SVC,ONE_CLASS,andNU_SVR*/
doublep;
/*forEPSILON_SVR*/
intshrinking;
/*usetheshrinkingheuristics*/
intprobability;
/*doprobabilityestimates*/
部分参数解释,(附核函数)
1、
2、
3、
4、
doubledegree;
//就是2式中的d
doublegamma;
//就是2,3,4式中的gamma
doublecoef0;
//就是2,4式中的r
doublecache_size;
/*inMB*/制定训练所需要的内存,默认是40M,LibSVM2.5中是4M,所以自己做开发选LibSVM2.5还是不错的!
doubleeps;
见参考文献[1]中式3.13
doubleC;
//没什么好说的,惩罚因子,越大训练的模型越那个…,当然耗的时间越多
intnr_weight;
//权重的数目,目前在实例代码中只有两个值,一个是默认0,另外一个是svm_binary_svc_probability函数中使用数值2。
int*weight_label;
//权重,元素个数由nr_weight决定
doublenu;
没什么好说的,看代码中的注释,too
doublep;
没什么好说的,看代码中的注释,three
intshrinking;
指明训练过程是否使用缩减
intprobability;
指明训练过程是否需要预报概率。
//
2.5structstructsvm_model
structsvm_model
svm_parameterparam;
//parameter
intnr_class;
//numberofclasses,=2inregression/oneclasssvm
//total#SV
svm_node**SV;
//SVs(SV[l])
double**sv_coef;
//coefficientsforSVsindecisionfunctions(sv_coef[n-1][l])
double*rho;
//constantsindecisionfunctions(rho[n*(n-1)/2])
double*probA;
//pariwiseprobabilityinformation
double*probB;
//forclassificationonly
int*label;
//labelofeachclass(label[n])
int*nSV;
//numberofSVsforeachclass(nSV[n])
//nSV[0]+nSV[1]+...+nSV[n-1]=l
//XXX
intfree_sv;
//1ifsvm_modeliscreatedbysvm_load_model
//0ifsvm_modeliscreatedbysvm_train
结构体svm_model用于保存训练后的训练模型,当然原来的训练参数也必须保留。
本来这个结构体应该是在svm.cpp中,为统一起见,一起描述。
svm_parameterparam;
训练参数,这里使用的是svm_param的实例,而不是指针。
这样训练完成后,原来参数被完全保留,再去预报时,就不用担心下次训练会把参数冲掉
intnr_class;
类别数
支持向量数
svm_node**SV;
保存支持向量的指针,至于支持向量的内容,如果是从文件中读取,内容会额外保留;
如果是直接训练得来,则保留在原来的训练集中。
如果训练完成后需要预报,原来的训练集内存不可以释放。
double**sv_coef;
相当于判别函数中的alpha
double*rho;
相当于判别函数中的b
double*probA;
pariwiseprobabilityinformation
double*probB;
int*label;
labelofeachclass(label[n])
int*nSV;
numberofSVsforeachclass(nSV[n])
intfree_sv;
1ifsvm_modeliscreatedbysvm_load_model;
0ifsvm_modeliscreatedbysvm_train
2.6接口函数
//以下接口函数设计得非常合理,最后一节详细说明
//最主要的驱动函数,训练数据
structsvm_model*svm_train(conststructsvm_problem*prob,conststructsvm_parameter*param);
//用SVM做交叉验证
voidsvm_cross_validation(conststructsvm_problem*prob,conststructsvm_parameter*param,intnr_fold,double*target);
//保存训练好的模型到文件
intsvm_save_model(constchar*model_file_name,conststructsvm_model*model);
//从文件中把训练好的模型读到内存中
structsvm_model*svm_load_model(constchar*model_file_name);
//得到数据集的SVM类型(必须经过训练得到模型后才可以用)
intsvm_get_svm_type(conststructsvm_model*model);
//得到数据集的类别数(必须经过训练得到模型后才可以用)
intsvm_get_nr_class(conststructsvm_model*model);
//得到数据集的类别标号(必须经过训练得到模型后才可以用)
voidsvm_get_labels(conststructsvm_model*model,int*label);
//LibSvm2.6新增函数
doublesvm_get_svr_probability(conststructsvm_model*model);
//用训练好的模型预报样本的值,输出结果保留到数组中。
(并非接口函数)
voidsvm_predict_values(conststructsvm_model*model,conststructsvm_node*x,double*dec_values);
//预报某一样本的值
doublesvm_predict(conststructsvm_model*model,conststructsvm_node*x);
//LibSvm2.6新增函数
doublesvm_predict_probability(conststructsvm_model*model,conststructsvm_node*x,double*prob_estimates);
//消除训练的模型,释放资源
voidsvm_destroy_model(structsvm_model*model);
voidsvm_destroy_param(structsvm_parameter*param);
//检查输入的参数,保证后面的训练能正常进行。
constchar*svm_check_parameter(conststructsvm_problem*prob,conststructsvm_parameter*param);
intsvm_check_probability_model(conststructsvm_model*model);
第三章:
SVM.cpp文件
3.1宏
.头文件:
从整个.cpp文件来看,感觉有些头文件是多余的,不知何故,反正多包含头文件不会犯错。
后面的typedef,特别是typedeffloatQfloat,是为了方便控制内存存储的精度。
#include<
math.h>
stdio.h>
stdlib.h>
ctype.h>
float.h>
string.h>
stdarg.h>
#include"
svm.h"
typedeffloatQfloat;
typedefsignedcharschar;
//.以下是定义的几个主要的模板,主要是为了比较大小,交换数据和完全复制数据。
Min()和Max()在<
中提供了相应的函数,这里的处理是为了兼容MicrosoftVC++编译器。
因为VC对标准模板库(STL)支持得不是很好。
下面对min,max的处理方法非常经典。
#ifndefmin
template<
classT>
inlineTmin(Tx,Ty){return(x<
y)?
x:
y;
}
#endif
#ifndefmax
inlineTmax(Tx,Ty){return(x>
//这里的克隆函数是完全克隆,不同于一般的复制。
操作结束后,内部的所有数据和指针完全一样。
inlinevoidswap(T&
x,T&
y){Tt=x;
x=y;
y=t;
classS,classT>
inlinevoidclone(T*&
dst,S*src,intn)
dst=newT[n];
memcpy((void*)dst,(void*)src,sizeof(T)*n);
}
//这里使用了define,非内联函数
#defineMalloc(type,n)(type*)malloc((n)*sizeof(type))
一般来说,在cpp中使用delete比较正规,在c中使用malloc比较正规。
LibSVM比较好地贯彻了这点,malloc用在接口函数中。
//以下的函数用作调试。
跳过~
#if1
voidinfo(char*fmt,...)
va_listap;
va_start(ap,fmt);
vprintf(fmt,ap);
va_end(ap);
voidinfo_flush()
fflush(stdout);
#elsevoidinfo(char*fmt,...){}
voidinfo_flush(){}
//以下部分为svm.cpp中的类继承和组合图:
(实线表示继承关系,虚线表示组合关系),更正规的图例见第一章。
3.2类Cache
本类主要负责运算所涉及的内存的管理,包括申请、释放等。
本类对理解核心代码关系不大,如果弄不清楚影响也不大。
可以跳过。
类定义:
classCache
public:
Cache(intl,intsize);
~Cache();
intget_data(constintindex,Qfloat**data,intlen);
voidswap_index(inti,intj);
//future_option
private:
intsize;
structhead_t
{
head_t*prev,*next;
//acicularlist
Qfloat*data;
intlen;
//data[0,len)iscachedinthisentry
};
head_t*head;
head_tlru_head;
voidlru_delete(head_t*h);
voidlru_insert(head_t*h);
成员变量:
head_t*head;
//变量指针,该指针用来记录程序所申请的内存,单块申请到的内存用structhead_t来记录所申请内存的指针,并记录长度。
而且通过双向的指针,形成链表,增加寻址的速度。
记录所有申请到的内存,一方面便于释放内存,另外方便在内存不够时适当释放一部分已经申请到的内存。
head_tlru_head;
//双向链表的头。
//样本总数。
intsize;
//所指定的全部内存,据说用Mb做单位。
成员函数:
voidlru_delete(head_t*h);
//从双向环形链表中删除某个元素的链接,不删除、不释放该元素所涉及的内存。
一般是删除当前所指向的元素。
voidlru_insert(head_t*h);
//在链表后面插入一个新的链接;
其实就是在lru_head后添加。
(环形的嘛)
Cache(intl,intsize);
构造函数。
该函数根据样本数L,申请L个head_t的空间。
根据说明,该区域会初始化为0。
Lru_head因为尚没有head_t中申请到内存,故双向链表指向自己。
至于size的处理,先将原来的byte数目转化为float的数目,然后扣除L个head_t的内存数目。
size为程序指定的内存大小4M/40M。
size不要设得太小。
intget_data(constintindex,Qfloat**data,intlen);
该函数保证head_t[index]中至少有len个float的内存,并且将可以使用的内存块的指针放在data指针中。
返回值为申请到的内存。
函数首先将head_t[index]从链表中断开,如果head_t[index]原来没有分配内存,则跳过断开这步。
计算当前head_t[index]已经申请到的内存,如果不够,释放部分内存(怀疑这样做的动机:
老数据为什么就可以释放,而不真的另外申请一块?
老数据没用了?
),等内存足够后,重新分配内存。
重新使head_t[index]进入双向链表。
并返回申请到的内存的长度。
//返回值不为申请到的内存的长度,为head_t[index]原来的数据长度h->
len。
调用该函数后,程序会计算
的值,并将其填入data所指向的内存区域,如果下次index不变,正常情况下,不用重新计算该区域的值。
若index不变,则get_data()返回值len与本次传入的len一致,从Kernel:
:
get_Q()中可以看到,程序不会重新计算。
从而提高运算速度。
While循环内的部分基本上难得用到一次。
voidswap_index(inti,intj);
交换head_t[i]和head_t[j]的内容,先从双向链表中断开,交换后重新进入双向链表中。
对后面的处理不理解,可能是防止中head_t[i]和head_t[j]可能有一方并未申请内存。
但h->
len>
i和h->
j无法解释。
for(head_t*h=lru_head.next;
h!
=&
lru_head;
h=h->
next)
if(h->
i)
{
if(h->
j)
swap(h->
data[i],h->
data[j]);
else
{
//giveup
lru_delete(h);
free(h->
data);
size+=h->
len;
h->
data=0;
len=0;
}
}
后注:
这部分内容我几次读过,都不太清楚,如果有谁弄懂了,请告知。
3.3类QMatrix
classQMatrix{
virtualQfloat*get_Q(intcolumn,intlen)const=0;
virtualQfloat*get_QD()const=0;
virtualvoidswap_index(inti,intj)const=0;
纯虚类。
理论上用户只要能在需要的时候,提供必要的数据,就可以实现其它的变形支持向量机。
用户可以自己实现数据矩阵(很大的哦),然后直接提供给Solver。
3.4类Kernel
classKernel{
Kernel(intl,svm_node*const*x,constsvm_paramete
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- LibSVM28 程序代码 注释