关于liblinear

20140906 配置和使用liblinear

今天主要是要把liblinear的样例过一遍。
liblinear的网址:http://www.csie.ntu.edu.tw/~cjlin/liblinear/
参考的文档在这个pdf的24页开始(这个文档的其他部分还介绍了SVM的优化方法等,值得一看):http://www.csie.ntu.edu.tw/~cjlin/papers/liblinear.pdf
news20的data从这里挂下来:http://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass.html#news20

准备工作

  • 下载liblinear并解压
  • 下载news20的数据,解压并放在liblinear的windows文件夹下
  • windows的话,不用配置了,所有需要的东西都已经编译好了

数据预处理

好了,开始按照文档一步步来:

  • time ./train -v 5 news20
    • -v 5 表示是5倍的交叉验证
    • 结果截图,有warning
  • time ./train -v 5 news20.scale
    • 如果对数据做了归一化,这里的归一化是所有非零特征赋值为1/sqrt(n),其中n为非零特征个数。(文本特征才能这样搞?)
    • 结果截图,可以看出快了很多,准确率也升了4个点。

如何选择Solver

考虑选择优化的损失函数,这里提供了很多选择:

对比了1和2,就是原问题和对偶问题的解法,对结果是没有影响的——Objective value都是576
./train -s 1 -e 0.0001 news20.scale

./train -s 2 -e 0.0001 news20.scale

虽然结果一样,但是对偶形式可能比较慢,作者的建议是:

  • 先尝试用对偶形式解决
  • 如果对偶形式太慢,那么才用回原始形式来求解

如何选择参数

也就是惩罚系数C,有两点:

  • C的大小对performance的影响不大
  • C过大的话,影响的是训练速度

样例1,C设置为1和100。时间上差的比较远。(注意这里使用的是默认的对偶形式求解)
time ./train -c 1 news20.scale

time ./train -c 1 news20.scale

样例2,如果使用原始形式来求解,那么用的是牛顿法(二次梯度),这时候时间上的差别会没有那么明显
time ./train -c 1 -s 2 news20.scale

time ./train -c 100 -s 2 news20.scale

所以作者的建议是:

  • 先试试C=1,看看效果
  • 慢慢提升C,如果带来的performance没有太大的提高,那么就没有必要调整了。(这一步可以用libsvm的grid.py来进行搜索)

附上了C(准确来说是logC)和准确率之间的影响的曲线,可以看出在logC=1附近,曲线基本达到了最大值,无需继续调整。

20140906(2) L1 regulared vs L2 regulared?

最常规的SVM其实是L2 regulared L1-loss:

这里L2指的是第一项w*w是二范的,L1-loss指的是hinge loss。
所以,L2 regulared L2-loss就是指带square hinge loss的SVM了:

而L1 regulared L2-loss SVM是:

这里用L1是为了让w呈现稀疏性(不过目前来说,其实我不懂稀疏性是用来干嘛的。。。)

20140906(3) liblinear in RCNN

RCNN最后训练SVM的部分调用的是liblinear,对应的代码是这个:https://github.com/rbgirshick/rcnn/blob/master/rcnn_train.m
对应的训练SVM代码是:

1
2
3
4
5
6
7
8
9
10
11
ll_opts = sprintf('-w1 %.5f -c %.5f -s %d -B %.5f', ...
opts.pos_loss_weight, opts.svm_C, ...
liblinear_type, opts.bias_mult);
fprintf('liblinear opts: %s\n', ll_opts);
X = sparse(size(cache.X_pos,2), num_pos+num_neg);
X(:,1:num_pos) = cache.X_pos(pos_inds,:)';
X(:,num_pos+1:end) = cache.X_neg(neg_inds,:)';
y = cat(1, ones(num_pos,1), -ones(num_neg,1));
llm = liblinear_train(y, X, ll_opts, 'col');
w = single(llm.w(1:end-1)');
b = single(llm.w(end)*opts.bias_mult);

其中ll_opts的参数包括了:

1
2
3
4
-w1 2          # 设置正样本的惩罚系数是2倍(错分了正样本的话,惩罚加倍)
-c 10^-3 # 设置惩罚系数C为10^-3(感觉好小,应该是为了保证训练的速度很快)
-s 3 # 设置使用的损失函数是标准的L2 regulared L1 loss SVM的对偶形式
-B 10 # 设置样本的bias项为10(if bias >= 0, instance x becomes [x; bias]; if < 0, no bias term added (default -1))

  • 其中设置-B为10的作用没有理解。
  • 得到的模型llm后,
    • 取前end-1项作为W
    • 最后一项*bias(也就是上面设置的-B 10)作为B(这一步别忘了)
      • 注意到这里其实是rcnn的处理逻辑,所以确实有点怪。
      • linlinear里面带bias的“正常”的predict流程是:
        • 在样本x后增加bias,得到新的样本是x_plus=[x, bias]
        • 点乘得到结果y=w·x_plus,这里的w已经隐含了bias对应的权值了
      • rcnn的predict流程是
        • 将w的最后一项乘以bias后设置为B,除最后一项的其他设置为W
        • 样本x不作改变
        • 点乘得到结果y=W·x+B
      • 实际上面两种方法是等价的,主要是实现的逻辑不一样。

20140906(4) Matlab运行样例

在matlab文件夹的Readme有说:

1
2
3
4
5
tic;
[heart_scale_label, heart_scale_inst] = libsvmread('../heart_scale');
model = train(heart_scale_label, heart_scale_inst, '-c 1 -B 1'); %这里我改了,加了-B 1 参数,准确率略有提高
[predict_label, accuracy, dec_values] = predict(heart_scale_label, heart_scale_inst, model); % test the training data(用训练数据继续测试。。囧)
toc;

出来的结果是:

1
2
3
4
5
6
.......*
optimization finished, #iter = 75
Objective value = -115.101147
nSV = 184
Accuracy = 84.8148% (229/270)
Elapsed time is 0.006317 seconds. #速度快得飞起来了。。。

20140907 Linux下的配置和使用

配置也很简单,cd到该目录下,直接make即可,会生成两个可执行文件train和predict。
做个简单的测试,跟上文的matlab的测试样例差不多:

1
2
./train heart_scale
./predict heart_scale heart_scale.model heart_scale.result

出来的结果如图:

20140907(1) C\C++调用liblinear

其实也不算调用了,因为本来liblinear就是用C来写的:
https://github.com/zhangliliang/liblinear/blob/master/linear.cpp
没有在网上找到样例,那么如果要参考,就参考文件夹里面的train.c和predict.c吧:
https://github.com/zhangliliang/liblinear/blob/master/train.c
https://github.com/zhangliliang/liblinear/blob/master/predict.c

其他可参考资料

zouxy09写的资料,比较接近文档的中文翻译:
LibLinear(SVM包)使用说明之(一)README
LibLinear(SVM包)使用说明之(二)MATLAB接口
LibLinear(SVM包)使用说明之(三)实践

很久没有更新网站,发现多了不少评论和问题,无法一一回复,如果现在仍有问题请再次留言 :) 2016.03.29