转载自:http://blog.csdn.net/ajianyingxiaoqinghan/article/details/73480476
本文摘自《libsvm》的GitHub中的README文档。
前言
LibSVM库的函数和结构体都在头文件 svm.h 中声明,我们需要在 C/C++ 文件中添加语句 #include “svm.h” ,然后就可以将我们的程序链接到 svm.cpp 文件上。我们可以以 svm-train.c 和 svm-predict.c 作为例程,了解它们是如何使用LibSVM库函数的。我们定义了 LIBSVM_VERSION 并在 svm.h 中声明了 extern int libsvm_version,所以我们可以检查库函数版本号。
在我们分类测试数据之前,我们需要使用训练数据构建一个 SVM 模型(即 svm_model)。一个模型也可以被存储到文件中,以便以后的运用。一旦一个 SVM 模型是可用的,你可以用它分类新数据。
LibSVM函数与结构体
svm_train
- struct svm_model* svm_train(const struct svm_problem* prob, const struct svm_parameter* param);
该函数根据给定的训练数据和参数,构建并返回了一个 SVM 模型。
svm_problem
svm_problem 结构体描述了问题:
struct svm_problem
{
int l;
double *y;
struct svm_node **x;
};
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
上述结构体的说明如下:
- l:训练数据的数量;
- y:包含目标数据的数组;
- x:数组指针,每一个指针指向一个训练向量,训练向量是一个 svm_node 的数组;
例如,如果我们有下列的训练数据:
LABEL ATTR1 ATTR2 ATTR3 ATTR4 ATTR5
----- ----- ----- ----- ----- -----
1 0 0.1 0.2 0 0
2 0 0.1 0.3 -1.2 0
1 0.4 0 0 0 0
2 0 0.1 0 1.4 0.5
3 -0.1 -0.2 0.1 1.1 0.1
那么此时 svm_problem 的组成就是:
l = 5
y -> 1 2 1 2 3
x -> [ ] -> (2,0.1) (3,0.2) (-1,?)
[ ] -> (2,0.1) (3,0.3) (4,-1.2) (-1,?)
[ ] -> (1,0.4) (-1,?)
[ ] -> (2,0.1) (4,1.4) (5,0.5) (-1,?)
[ ] -> (1,-0.1) (2,-0.2) (3,0.1) (4,1.1) (5,0.1) (-1,?)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
svm_node
svm_node 结构体中存储的是(index, value):
struct svm_node
{
int index;
double value;
};
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
index = -1 表明了一个向量的结尾。
注:标签号(index)的排列必须是升序排序。
svm_parameter
结构体 svm_parameter 描述了 SVM 模型的参数:
struct svm_parameter
{
int svm_type;
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 */
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 */
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
参数说明如下:
- svm_type:可以是C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR其中之一;
- C_SVC:C-SVM 分类器;
- NU_SVC:NU-SVM 分类器;
- ONE_CLASS:one-class SVM;
- EPSILON_SVR:epsilon-SVM 回归;
- NU_SVR:nu-SVM 回归;
- kernel_type:可以是 LINEAR, POLY, RBF, SIGMOID 其中之一;
- LINEAR: u T v
- POLY: (gammau T v+coef0) degree
- RBF: exp(−gamma×|u−v| 2 )
- SIGMOID: tanh(gamma×u T v+coef0)
- PRECOMPUTED:在训练集文件中的存放核函数值;
- cache_size:是核缓存的大小,单位是MBytes;
- eps:停止标准(我们在 NU-SVC 中使用的标准是0.00001,在其他分类器中使用 0.001);
- nu:用于 nu-SVM, nu-SVR, one-class-SVM 的参数;
- p:epsilon-SVM 回归中损失函数的小量,损失函数对小量不敏感;
- shrinking:
- shrinking = 1,则收缩是进行的;
- shrinking = 0,则收缩无效;
- probability:
- probability = 1,模型中包含概率信息;
- probability = 0,模型中不包含概率信息;
其它的,nr_weight, weight_label 和 weight 用来为某些类改变惩罚因子(如果一个类的 weight 值没有改变,则被设置为1).这将有助于训练分类器使用不平衡输入数据,或者有利于降低不对称错误分类的代价。
nr_weight 是在 weight_label 数组和 weight 数组的元素个数。每一个 weight[i] 对应了一个 weight_label[i],意味着类 weight_label[i] 的惩罚,就是与 weight[i] 相乘的结果。
如果我们不想改变任何分类的惩罚因子,就把 nr_weight 设置为0;。
注:
1. 因为 svm_model 包含了 svm_problem 的指针,所以如果我们仍然需要使用由 svm_train() 函数生成的 svm_model ,我们不能释放包含了 svm_problem 的内存空间。
2. 为了避免错误参数,函数 svm_check_parameter() 应该在 svm_train() 函数之前调用。
svm_model
结构体 svm_model 存储了从训练过程中得到的模型。这里并不推荐直接访问该结构体中的值,编程者应该使用接口函数获得其中的值。
struct svm_model
{
struct svm_parameter param; /* parameter */
int nr_class; /* number of classes, = 2 in regression/one class svm */
int l; /* total #SV */
struct svm_node **SV; /* SVs (SV[l]) */
double **sv_coef; /* coefficients for SVs in decision functions (sv_coef[k-1][l]) */
double *rho; /* constants in decision functions (rho[k*(k-1)/2]) */
double *probA; /* pairwise probability information */
double *probB;
int *sv_indices; /* sv_indices[0,...,nSV-1] are values in [1,...,num_traning_data] to indicate SVs in the training set */
/* for classification only */
int *label; /* label of each class (label[k]) */
int *nSV; /* number of SVs for each class (nSV[k]) */
/* nSV[0] + nSV[1] + ... + nSV[k-1] = l */
/* XXX */
int free_sv; /* 1 if svm_model is created by svm_load_model*/
/* 0 if svm_model is created by svm_train */
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
参数解释如下:
- param:描述了在该模型中使用的参数;
- nr_class:分类数量。对于回归问题和 one-class SVM,其值为2;
- l:支持向量的个数;
- SV, sv_coef:分别代表支持向量和其相应系数。
假设有 k 种分类。对于数据中的分类 j,相应的 sv_coef 包含了 (k - 1) y * alpha 个向量,alpha的向量是一系列二分类问题的解决方案:1 vs j, 2 vs j, …, j-1 vs j, j vs j+1, j vs j+2, …, j vs k。并且 y=1 对应前面 j-1 个向量,y=-1 代表后面剩余的 k-j 个向量。
举例说明,如果这里有 4 个分类,那么 sv_coef 和 SV 如下所示:
+-+-+-+--------------------+
|1|1|1| |
|v|v|v| SVs from class 1 |
|2|3|4| |
+-+-+-+--------------------+
|1|2|2| |
|v|v|v| SVs from class 2 |
|2|3|4| |
+-+-+-+--------------------+
|1|2|3| |
|v|v|v| SVs from class 3 |
|3|3|4| |
+-+-+-+--------------------+
|1|2|3| |
|v|v|v| SVs from class 4 |
|4|4|4| |
+-+-+-+--------------------+
可以以函数 svm_train() 为例,观察其中如何将值分配给 sv_coef。
- rho:偏差值 (-b);
- probA, probB:在概率输出中使用的参数。如果有 k 个类别,则会存在 k×(k-1)/2 个二值问题,以及 rho, probA, probB 值,它们按照二值问题的顺序对齐:1 vs 2, 1 vs 3, …, 1 vs k, 2 vs 3, …, 2 vs k, …, k-1 vs k
- sv_indices[0, …, nSV-1]:在 [1, …, num_training_data]中,标示训练集里面支持向量的值;
- label:包含训练数据的标签;
- nSV:每个类中的支持向量的数量;
- free_sv:标志位,用来决定 SV 的空间是否应该被函数 free_model_content(struct svm_model*) 和 free_and_destroy_model(struct svm_model**) 释放。如果模型是由函数 svm_train() 生成的,那么 SV 在 svm_problem 中指向的数据不应该被移除。例如,如果 svm_model 不是由 svm_train 函数生成,那么free_sv值为0;但是如果是由 svm_load_model 函数生成,则 free_sv 值为1;
svm_predict
- double svm_predict(const struct svm_model* model, const struct svm_node* x);
该函数以模型 model 为基础,对测试向量 x 进行分类或回归预测。
- 对于分类模型,将会返回 x 的预测分类;
- 对于回归模型,将会返回使用模型计算得到的 x 值;
- 对于 one-class 模型,返回 +1 或 -1;
svm_cross_validation
- void svm_cross_validation(const struct svm_problem* prob, const struct svm_parameter* param, int nr_fold, double* target);
该函数用来进行交叉验证。数据被分离成 nr_fold 组,在给定的参数下,按照顺序的每一组用由剩余数据训练得到的模型进行验证。验证过程中的预测标签(所有概率情况)被存到目标数组中。
svm_prob 格式与 svm_train() 相同。
svm_get_svm_type
- int svm_get_svm_type(const struct svm_model* model);
该函数返回模型的 svm_type。svm_type 可能存在的值在文件 svm.h 中已经定义。
svm_get_nr_class
- int svm_get_nr_class(const svm_model* model);
对于分类模型,函数返回分类的数量;
对于回归模型或 one-class 模型,返回值为 2 ;
svm_get_labels
- void svm_get_labels(const svm_model* model, int* label);
对于分类模型,该函数将标签名称输出到标签数组。
对于回归模型和 one-class 模型,标签不变。
svm_get_sv_indices
- void svm_get_sv_indices(const struct svm_model* model, int* sv_indices);
该函数将支持向量的编号输出到 sv_indices 数组。
sv_indices 的大小是支持向量的大小,该值可以通过调用 svm_get_nr_sv 函数得到。
每个 sv_indices[i] 的范围都在 [1, …, num_training_data] 之中。
svm_get_nr_sv
- int svm_get_nr_sv(const struct svm_model* model);
该函数返回所有支持向量的个数。
svm_get_svr_probability
- double svm_get_svr_probability(const struct svm_model* model);
对于带有概率信息的回归模型,该函数输出一个大于 0 的 sigma 值。对于测试数据,我们认为概率模型为:目标值 = 预测值 + z。
其中,z 是拉普拉斯变换:
exp(−|z|/sigma)2×sigma
如果模型不是 svr ,或者不包含需要的信息,返回 0 值。
svm_predict_values
- double svm_predict_values(const svm_model* model, const svm_node* x, double* dec_values);
该函数通过一个模型 model 计算向量 x ,得到预测决策值,并返回其预测标签(分类问题)或函数值(回归问题)。
对于 nr_class 个分类的分类模型,该函数在数组 dec_values 中给出 nr_class * (nr_class - 1) / 2 个决策值,其中 nr_class 的值可以通过函数 svm_get_nr_class 得到,其顺序是:
label[0] vs. label[1],
...,
label[0] vs. label[nr_class-1],
...,
label[nr_class-2] vs. label[nr_class-1]
标签 label 可以由函数 svm_get_labels 得到。
该函数返回值是 x 的预测分类结果。注:当 nr_class = 1 时,该函数不会给出任何决策值。
对于回归问题,dec_values[0] 和返回值都是通过模型计算 x 得到的预测值。
对于 one-class 模型,dec_values[0] 是 x 的决策结果,返回值是 +1 或 -1。
svm_predict_probability
- double svm_predict_probability(const struct svm_model* model, const struct svm_node* x, double* prob_estimates);
该函数对一个带有概率信息的模型,对测试向量 x 进行分类或回归操作。
对于带有概率信息的分类模型,该函数在数组 prob_estimates 中给出了 nr_class 种概率估计(nr_class 值可以从函数 svm_get_nr_class 得到)。结果将返回高概率的分类。
对于回归问题或 one-class 问题,数组 prob_estimates 不变,返回值与函数 svm_predict 相同。
svm_check_parameter
- const char* svm_check_parameter(const struct svm_problem* prob, const struct svm_parameter* param);
该函数检测参数是否在该问题的可行范围之中。该函数应该在调用 svm_train() 和 svm_cross_validation() 之前调用。如果参数是可行的,则返回 NULL 值;否则返回一个错误信息。
svm_check_probability_model
- int svm_check_probability_model(const struct svm_model* model);
该函数检测模型是否包含概率估计的所需信息。如果是,将返回 +1;否则,返回 0。
该函数应该在调用 svm_get_svr_probability 和 svm_predict_probability 函数之前进行调用。
svm_save_model
- int svm_save_model(const char* model_file_name, const struct svm_model* model);
该函数将模型存到一个文件中。
存储成功,返回 0;存储出现错误,返回 -1。
svm_load_model
- struct svm_model* svm_load_model(const char* model_file_name);
该函数从一个文件中读取模型,返回一个模型的指针。
如果模型不能被读取,则返回一个空指针。
svm_free_model_content
- void svm_free_model_content(struct svm_model* model_ptr);
该函数释放被模型结构体占用的内存空间。
svm_free_and_destroy_model
- void svm_free_and_destroy_model(struct svm_model** model_ptr_ptr);
该函数用来释放被模型占用的内存空间,并销毁模型结构体。
该函数与函数 svm_destroy_model 相同,但函数 svm_destroy_model 在版本 3.0 之后被弃用。
svm_destroy_param
- void svm_destroy_param(struct svm_parameter* param);
该函数释放被参数集合占用的内存空间。
svm_set_print_string_function
- void svm_set_print_string_function(void (* print_func)(const char *));
用户可以指定一个函数的输出格式。例如:
svm_set_print_string_function(NULL);
- 1
- 1
可以使用默认输出到stdout。