Liblinear是一个简单的解决大规模线性化分类和回归问题的软件包。它目前支持:
-L2正则化逻辑回归/L2损失支持向量分类/L1损失支持向量分类法
-L1正则化L2损失支持向量分类/L1正则化逻辑回归
-L2正则化L2损失支持向量回归/L1损失支持向量回归。
这篇文献介绍了Liblinear的用法。
首先,请先阅读“快速入门”部分。
对于开发人员,请检查“库的使用”部分学习如何在你的软件中集成Liblinear。
目录
=================
-何时使用Liblinear而不是使用LibSVM
-快速入门
-安装
-训练的使用方法
-预测的使用方法
-实例
-库的使用方法
-编译Windows二进制文件
-附加信息
-MATLAB/OCTAVE接口
-Python接口
何时使用Liblinear而不是使用LibSVM
====================================
有一些数据量很大的情况在使用或不用非线性映射得到的结果相似的;
不适用核方法,能够训练一个更大的数据集的分类或回归问题;
这些数据通常有很多特征。
文档分类就是一个例子。
警告:虽然一般来说Liblinear是非常快的,它默认的求解器在某些情况下可能较慢(例如,数据没有缩放或者C的值很大)。
关于如何处理这样的问题,请参考SVM指南的附录B。
http://www.csie.ntu.edu.tw/~cjlin/papers/guide/guide.pdf
警告:如果你是初学者,你的数据集不是很大,你应该首先考虑LIbSVM。
LIBSVM 网址: http://www.csie.ntu.edu.tw/~cjlin/libsvm
快速入门
===========
参考安装的章节来安装Liblinear
安装完毕之后,训练和测试分别有对应的程序train.exe和predict.exe。
关于数据格式,请检查LIbSVM的README文件。注意,特征索引必须从1开始(而不是0)。
`heart_scale'是这个软件包中的一个数据分类实例。
在命令行键入“train.exe heart_scale heart_scale.model”,程序将读取训练文件heart_scale并将模型文件输出为 heart_scale.model。
如果你有一个测试文件heart_scale.t,就在命令行键入“predict.exe heart_scale.t heart_scale.model output”来检验预测的精度,文件output中保存了预测的分类的标签值。
有关训练和预测的更多信息,请参考章节“训练的使用方法”和章节“预测的使用方法”。
为了获得良好的性能,有时需要对数据进行缩放。
请参考LibSVM的程序“svm-scale.exe”。对于大数据和稀疏数据,使用‘-l 0’参数来保持其稀疏性。
安装
============
在UNIX系统上,键入“make”来编译“train”和“predict”程序。
在没有参数的的情况下运行它们来显示用法。
在其他系统上,构建makefile来编译他们(例如,参考文献中的“在Windows上编译二进制文件”部分),
或者使用预编译的二进制文件(Windows二进制文件都在目录“windows”中)。
这个软件使用了一个一级目录的BLAS子程序。需要的函数都包含在这个包里。如果你的电脑上有一个BLAS库,
可以通过修改makefile文件使用它:注释下面的行
#LIBS ?= -lblas
并修改为
LIBS ?= blas/blas.a
训练的使用方法
=============
使用方法:train.exe 参数选项 训练集文件 模型文件
参数选项:
-s 类型:设置求解器的类型(默认值是1)
对于多分类问题
0—L2正则逻辑回归(原始的)
1—L2正则化L2损失支持向量分类(对偶的)
2—L2正则化L2损失支持向量分类(原始的)
3—L2正则化L1损失支持向量分类(对偶的)
4-Crammer and Singer的支持向量分类
5-L1正则化L2损失支持向量分类
6-L1正则逻辑回归
7-L2正则逻辑回归(对偶的)
对于回归问题
11-L2正则化L2损失支持向量回归(原始的)
12-L2正则化L2损失支持向量回归(对偶的)
13—L2正则化L1损失支持向量回归(对偶的)
-c 代价:设置参数C(默认值1)
-p 精度:设置损失函数的精度(默认值0.1)
-e 精度:设置终止迭代条件的容差(默认值0.1)
-s 0 和 2
|f'(w)|_2 <= eps*min(pos,neg)/l*|f'(w0)|_2,
这里f原函数且pos/neg是正/负数据(默认值0.01)
-s 11
|f'(w)|_2 <= eps*|f'(w0)|_2 (默认值 0.001)
-s 1, 3, 4 and 7
Dual maximal violation <= eps; 与libsvm相似 (默认值 0.1)
-s 5 和 6
|f'(w)|_1 <= eps*min(pos,neg)/l*|f'(w0)|_1,
这里f是原函数(默认值0.01)
-s 12 和 13
|f'(alpha)|_1 <= eps |f'(alpha0)|,
这里f是对偶函数(默认值0.1)
-B 偏置:如果偏置 >= 0,实例x变为[x,偏置]。如果偏置 < 0,就不增加偏置这个项(默认值-1)。
-wi 权重:调整不同类的参数C(详情请参阅README)
-v n:n-折交叉验证模式
-C : 寻找参数 C (仅针对 -s 0 和 2)
-q :静默模式(没有输出)
选项-v将数据随机分割成n个部分,并计算它们的交叉验证精度。
选项-C在不同的c值下进行交叉验证,并找到最优的c值。该选项仅支持-s 0 和 -s 2,如果没有指定求解器,则默认使用-s 2。
公式:
对于L2正则化逻辑回归(-s 0),我们求解
min_w w^Tw/2 + C \sum log(1 + exp(-y_i w^Tx_i))
对于L2正则化L2损失SVC对偶(-S 1)问题,我们求解
min_alpha 0.5(alpha^T (Q + I/2/C) alpha) - e^T alpha
s.t. 0 <= alpha_i,
对于L2正则化L2损失SVC对偶(-S 2)问题,我们求解
min_w w^Tw/2 + C \sum max(0, 1- y_i w^Tx_i)^2
对于L2正则化L1损失SVC对偶(-S 3)问题,我们求解
min_alpha 0.5(alpha^T Q alpha) - e^T alpha
s.t. 0 <= alpha_i <= C,
对于L1正则化L2损失SVC(-S 5),我们求解
min_w \sum |w_j| + C \sum max(0, 1- y_i w^Tx_i)^2
对于L1正则化Logistic回归(-S 6),我们求解
min_w \sum |w_j| + C \sum log(1 + exp(-y_i w^Tx_i))
对于L2正则化Logistic回归(-S 7),我们求解
min_alpha 0.5(alpha^T Q alpha) + \sum alpha_i*log(alpha_i) + \sum (C-alpha_i)*log(C-alpha_i) - a constant
s.t. 0 <= alpha_i <= C,
这里:
Q 是一个矩阵 Q_ij = y_i y_j x_i^T x_j.
对于L2正则化L2损失SVR(-S 11),我们求解
min_w w^Tw/2 + C \sum max(0, |y_i-w^Tx_i|-epsilon)^2
对于L2正则化L2损失SVR对偶(-S 12)问题,我们求解
min_beta 0.5(beta^T (Q + lambda I/2/C) beta) - y^T beta + \sum |beta_i|
对于L2正则化L1损失SVR对偶(-S 13),我们求解
min_beta 0.5(beta^T Q beta) - y^T beta + \sum |beta_i|
s.t. -C <= beta_i <= C,
这里:
Q是一个矩阵 Q_ij = x_i^T x_j.
如果bias >= 0, w 变为 [w; w_{n+1}] and x 变为 [x; bias].
原始-对偶关系意味着-s 1和 -s 2得到的结果相同,-s 0和 -s 7得到的结果相同,-s 11和 -s 12得到的结果相同。
我们实现了一对多的多类别分类方法。
在训练第i类 vs 非第i类时,相应的每个类别的c参数分别为c 和 wi * c。
如果只有2个类别,我们只训练一个模型。即weight1 * c VS weight2 * c。
我们还实现了 Crammer 和 Singer的多类别SVM分类(-s 4)
min_{w_m, \xi_i} 0.5 \sum_m ||w_m||^2 + C \sum_i \xi_i
s.t. w^T_{y_i} x_i - w^T_m x_i >= \e^m_i - \xi_i \forall m,i
这里 e^m_i = 0 如果 y_i = m,
e^m_i = 1 如果 y_i != m,
这里我们求解对偶问题
min_{\alpha} 0.5 \sum_m ||w_m(\alpha)||^2 + \sum_i \sum_m e^m_i alpha^m_i
s.t. \alpha^m_i <= C^m_i \forall m,i , \sum_m \alpha^m_i=0 \forall i
这里 w_m(\alpha) = \sum_i \alpha^m_i x_i,
并且 C^m_i = C if m = y_i,
C^m_i = 0 if m != y_i.
预测的使用方法
===============
使用方法:predict.exe 参数选项 测试集文件 模型文件 输出结果文件
选项:
-b 概率估计:是否输出概率估计,0或1(默认值0),目前仅使用于逻辑回归。
-q 静默模式(没有输出)
注意 -b 仅仅在预测阶段使用,这里与libSVM不同。
实例
========
> train.exe data_file
使用L2损失函数训练线性的支持向量机。
> train -v 5 -e 0.001 data_file
使用L2损失函数做5折SVM交叉验证。使用比默认值更小的容差值以获得更高的精度。
> train -C data_file
用L2损失函数进行多次SVM交叉验证,找到最佳交叉的参数C,验证准确性。
> train -C -s 0 -v 3 -c 0.5 -e 0.0001 data_file
对于参数-C的选择,用户可以使用不同数量的交叉验证。此外,用户可以使用-c选项指定搜索范围的最小C值。
当用户希望在制定C值的不同的设置下运行参数选择程序时,这个设置是有用的。例如,使用一个更为严格的停止迭代的容差-e 0.0001。
> train -c 10 -w1 2 -w2 5 -w3 2 four_class_data_file
训练4个分类器:
正样本类别 负样本类别 Cp(c*w) Cn
class 1 class 2,3,4. 20 10
class 2 class 1,3,4. 50 10
class 3 class 1,2,4. 20 10
class 4 class 1,2,3. 10 10
Cp是正样本的c值 wi * c, Cn是负样本的c值即c。
> train -c 10 -w3 1 -w2 5 two_class_data_file
如果只有两个类,我们就训练一个模型。两个类的C值分别为10和50。
> predict -b 1 test_file data_file.model output_file
输出概率估计(仅适用于Logistic回归)。
库的使用
=============
这些函数和结构体声明在头文件“linear.h”中。你可以查看train.c和predict.c中的例子看如何使用这些函数。
我们定义了 LIBLINEAR_VERSION 并声明 `extern int liblinear_version; '在linear.h中,这样你可以查看版本号。
函数:model* train(const struct problem *prob,
const struct parameter *param);
这个函数根据给定的训练数据和参数构建和返回一个分类或者回归的模型。
结构体problem描述了要求解的问题
struct problem
{
int l, n;
int *y;
struct feature_node **x;
double bias;
};
这里l是训练样本的个数。
如果bias >= 0,我们假设在每个样本实例的末尾添加一个附加的偏置特征。
n是每个样本包含的特征数量(包括bias >= 0时的偏置特征)
y是一个包含目标值的数组(对于分类则是整理的类别标签,对于回归则是小数的预测概率)。
x是一个指针数组,每个指针指向一个训练向量的稀疏表示(特征节点的数组,即一个样本的特征值数组)。
例如,如果我们有如下训练数据:
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
且bias = 1, 则问题的组成部分是:
l = 5
n = 6
y -> 1 2 1 2 3
x -> [ ] -> (2,0.1) (3,0.2) (6,1) (-1,?)
[ ] -> (2,0.1) (3,0.3) (4,-1.2) (6,1) (-1,?)
[ ] -> (1,0.4) (6,1) (-1,?)
[ ] -> (2,0.1) (4,1.4) (5,0.5) (6,1) (-1,?)
[ ] -> (1,-0.1) (2,-0.2) (3,0.1) (4,1.1) (5,0.1) (6,1) (-1,?)
结构体parameter描述了一个线性分类或逻辑回归模型:
struct parameter
{
int solver_type;
/* these are for training only */
double eps; /* stopping criteria */
double C;
int nr_weight;
int *weight_label;
double* weight;
double p;
};
求解器可以是这些中的一种,L2R_LR, L2R_L2LOSS_SVC_DUAL, L2R_L2LOSS_SVC, L2R_L1LOSS_SVC_DUAL, MCSVM_CS, L1R_L2LOSS_SVC, L1R_LR, L2R_LR_DUAL, L2R_L2LOSS_SVR, L2R_L2LOSS_SVR_DUAL, L2R_L1LOSS_SVR_DUAL.
对于分类问题
L2R_LR L2正则化逻辑回归(原始的)
L2R_L2LOSS_SVC_DUAL L2正则化L2损失支持向量分类(对偶的)
L2R_L2LOSS_SVC L2正则化L2损失支持向量分类(原始的)
L2R_L1LOSS_SVC_DUAL L2正则化L1损失支持向量分类(对偶的)
MCSVM_CS Crammer 和 Singer的支持向量分类
L1R_L2LOSS_SVC L1正则化L2损失支持向量分类
L1R_LR L1正则化逻辑回归
L2R_LR_DUAL L2正则化逻辑回归(对偶的)
对于回归问题
L2R_L2LOSS_SVR L2正则化L2损失支持向量回归(原始的)
L2R_L2LOSS_SVR_DUAL L2正则化L2损失支持向量回归(对偶的)
L2R_L1LOSS_SVR_DUAL L2正则化L1损失支持向量回归(对偶的)
C是违法约束后的代价。
p是支持向量回归损失的灵敏度。
eps是停止迭代的标准(精度)。
nr_weight, weight_label, 和 weight是用于改变某些类别的惩罚(如果一个类别的weight没有改变就被设置为1)。
这个在训练不平衡的输入数据或者包含非对称的误分类代价的时候很有用。
如何你不想改变任何一类的惩罚,只要把nr_weight设置为0。
注意:为了避免错误的参数,check_parameter()函数应该在调用train()函数之前先调用。
结构体model保存着从训练过程中得到的模型
struct model
{
struct parameter param;
int nr_class; /* number of classes */
int nr_feature;
double *w;
int *label; /* label of each class */
double bias;
};
param描述了用于获取model的parameters。
nr_class是类别的数量。
nr_feature是特征的数量。
nr_class是类别的数量。 nr_class = 2 用于回归。
w存储特征值的权重的数组。对于分类,该数组的大小是nr_feature*nr_class。对于回归,如果nr_class = 2,则该数组的大小是nr_feature。
我们使用一对多的多类别分类,因此每个特征索引对应着该类别的权重值。权重值的组织方式如下
+------------------+------------------+------------+
| nr_class weights | nr_class weights | ...
| for 1st feature | for 2nd feature |
+------------------+------------------+------------+
如果bias >= 0则,x变为 [x; bias]。特征的数量增加了一个,则w为一个(nr_feature+1)*nr_class个元素的数组。bias的值存储在变量值double bias中。
数组label存储着类别标签值。
函数:void cross_validation(const problem *prob,
const parameter *param,
int nr_fold, double *target);
这个函数执行交叉验证。数据被分割成nr_fold个部分。在给定的参数下,每个部分都是用剩下部分训练出来的模型来验证的。交叉验证过程中预测的标签值存储在数组target中。
prob的格式和train()函数中的一样。
函数: void find_parameter_C(const struct problem *prob,
const struct parameter *param,
int nr_fold,
double start_C,
double max_C,
double *best_C,
double *best_rate);
这个函数有点类似cross_validation()函数。然而,它不像cross_validation()函数那样指定c值,
它执行多次交叉验证使用C = start_C,2*start_C, 4*start_C, 8*start_C, ..., 最终返回一个最高交叉验证精度的最优c值。
如果start_C <= 0,则这个函数计算出一个足够小的c值作为start_C。当所有的部分训练出来的模型都趋向于稳定或者达到max_C,这个过程就停止。
相应的最优c值和精度则存储在*best_C 和 *best_rate中。
函数: double predict(const model *model_,
const feature_node *x);
对于一个分类的模型,返回特征节点x预测得到的类别标签。
对于一个回归的模型,返回使用这个模型计算特征节点后输出来的函数值。
函数: double predict_values(const struct model *model_,
const struct feature_node *x,
double* dec_values);
这个函数计算nr_w决策值存储到dec_values数组中。
如果是应用于回归或者类别数为2,则nr_w = 1.
一个例外就是Crammer 和 Singer的多类别SVM(-s 4),如果只有2个类别,则这里的nr_w = 2。
对于剩下的其他情况,nr_w就是类别数。
我们实现了一对多的多类别SVM分类方法(-s 0,1,2,3,5,6,7) 和 Crammer 与 Singer的多类别SVM分类方法(-s 4) 。
返回具有最高决策值的类(的标签)。
函数: double predict_probability(const struct model *model_,
const struct feature_node *x,
double* prob_estimates);
这个函数计算出nr_class的概率估计并存储到prob_estimates中。
nr_class能够从函数get_nr_class()获得。
返回具有最高概率的类别。
目前,我们仅仅支持输出逻辑回归的概率估计。
函数: int get_nr_feature(const model *model_);
这个函数返回模型中的特征数量。
函数: int get_nr_class(const model *model_);
这个函数返回模型中的类别数量。
对于一个回归模型,则返回2。
函数: void get_labels(const model *model_, int* label);
这个函数输出类别的标签值,赋值给数组label。
对于一个回归模型,标签值是不变的。
函数: double get_decfun_coef(const struct model *model_,
int feat_idx,
int label_idx);
这个函数给出满足条件feature index = feat_idx 且 label index = label_idx的特征和类别的相关系数。
注意:feat_idx的起始值从1开始,而label_idx的起始值从0开始。如果feat_idx不在有效的范围内(1到nr_feature),则返回一个0值。
对于分类模型,如果label_idx的值不在有效范围(0到nr_class-1),则返回一个0值。
对于回归模型,忽略label_idx。
函数: double get_decfun_bias(const struct model *model_,
int label_idx);
这个函数返回与label_idx对应的偏置bias。
对于分类的模型,如果label_idx的值不在有效范围(0到nr_class-1),则返回一个0值。
对于回归模型,忽略label_idx。
函数: const char *check_parameter(const struct problem *prob,
const struct parameter *param);
这个函数检测问题的参数是否在可行饭范围内。它应该在调用train()函数和cross_validation()函数之前先调用。
如果参数是可行的,则返回NULL,否则返回错误信息。
函数: int check_probability_model(const struct model *model);
如果模型支持概率估计输出,则这个函数返回1,否则返回0。
函数: int check_regression_model(const struct model *model);
如果模型是回归模型,则这个函数返回1,否则返回0.
函数: int save_model(const char *model_file_name,
const struct model *model_);
这个函数将模型写入文件,如果写入成功则返回0,如果写入出现错误,则返回-1。
函数: struct model *load_model(const char *model_file_name);
这个函数返回一个指向从模型文件中读出来的model的指针,如果模型文件加载失败则返回一个NULL指针。
函数: void free_model_content(struct model *model_ptr);
这个函数释放模型结构中的内容所占用的内存。
函数: void free_and_destroy_model(struct model **model_ptr_ptr);
这个函数释放模型所使用的内存并销毁model结构体。
函数: void destroy_param(struct parameter *param);
这个函数释放参数集所占用的内存。
函数: void set_print_string_function(void (*print_func)(const char *));
用户能够通过这个函数指定输出格式。
对于默认的输出到stdout,使用set_print_string_function(NULL);
编译Windows二进制文件
=========================
Windows二进制文件在文件夹`windows'中。
要重新通过Visual C++编译,则通过如下步骤:
1、打开命令行并切换文件目录到liblinear文件夹中。如果没有设置VC++环境变量,在命令行键入""C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\amd64\vcvars64.bat""
你可能需要根据你自己电脑上VC++的版本和安装位置修改上面的命令。
2、命令行键入nmake -f Makefile.win clean all
3、(可选的)编译liblinear动态库 libsvm.dll,在命令行键入nmake -f Makefile.win lib
4、(可选的)要编译32位的Windows二进制文件,你必须
(1)启动"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat" 而不是 vcvars64.bat
(2)修改CFLAGS,即修改Makefile.win中的/D _WIN64 为 /D _WIN32
MATLAB/OCTAVE 接口
=======================
请参考`matlab'文件夹中的READNE文件。
PYTHON 接口
================
请参考`python'文件夹中的README文件。
附加信息
======================
如果你发现liblinear对你有用,请这样引用它
R.-E. Fan, K.-W. Chang, C.-J. Hsieh, X.-R. Wang, and C.-J. Lin.
LIBLINEAR: A Library for Large Linear Classification, Journal of
Machine Learning Research 9(2008), 1871-1874. Software available at
http://www.csie.ntu.edu.tw/~cjlin/liblinear
如有任何问题或意见,请将您的电子邮件发送到[email protected]