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
11ll_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的作用没有理解。
- 参考了网页http://stackoverflow.com/questions/16242377/how-to-understand-bias-parameter-in-liblinear
- 解释是bias不为1时候,分界面才不会强制地要通过原点,而bias的大小是通过交叉验证来设置的
然后放置到正负样本X和y,就可以开始解svm了:llm = liblinear_train(y, X, ll_opts, ‘col’);
- 得到的模型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
5tic;
[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包)使用说明之(三)实践