因为项目的需要,我们需要同时使用Libsvm与Liblinear,并将其封装起来做成统一调用形式,目前软件已经完成,名称为Tmsvm-基于SVM的文本挖掘系统
本文就来分析一下Libsvm与Liblinear的异同点以及封装这两个软件需要注意的事宜。
关于Libsvm的源码分析,上海交通大学模式识别实验室曾经有过这方面的工作,可以从网上下载。本文也结合他之前的工作,对libsvm的剖析做一些完善。目前关于Liblinear这方面的源码分析较少,因此我们就着手分析liblinear在具体实现上一些细节。
本文所分析所对应的libsvm的版本为3.0,liblinear的版本为1.8,开发语言为Java。对于Libsvm的Java版本,原作者用Java重新实现,而不是调用C++的动态链接库。而liblinear的Java版本因为原作者只提供了一个Jar包,并没有提供源代码,所以我们使用的是一个叫做Benedikt的人实现的,其源代码可在http://www.bwaldvogel.de/liblinear-java/ 进行下载。虽然是别人实现,但具体算法上和原作者基本相同。这也不影响本文对这两者进行一个综合的比较。
关于Libsvm,台湾大学林智仁这样描述它:"LIBSVM is an integrated software for support vector classification, (C-SVC, nu-SVC), regression (epsilon-SVR, nu-SVR) and distribution estimation (one-class SVM). It supports multi-class classification."即Libsvm是一个整合了支持向量机(C-SVC, nu-SVC)、回归、分布估计(one-class SVM)的软件。并且支持多类别的分类。而对于LIblinear,官网上是这样介绍的:”LIBLINEAR is a linear classifier for data with millions of instances and features“,即主要专门为百万级别的数据和特征实现的线性分类器。
他们两个都是用来做分类的,相对来说Libsvm应用的范围较广, 而Liblinear主要用于处理大数据量的训练过程。在什么样的情况下,该选择Liblinear而不是Libsvm呢?作者给出几点建议:
在这类情况下,建议你使用Liblinear,而不是libsvm。文本分类是最典型的例子,文本分类的样本量非常多,而且特征的维度也是很高,从几千-几百万的数量级,因此在做文本方面的分类时最好选择liblinear。作者给出一个例子,对比liblinear与libsvm训练效果与时间效率。数据总共包含20,242样本,每个样本都包含47,236 个特征。
% time libsvm-2.85/svm-train -c 4 -t 0 -e 0.1 -m 800 -v 5 rcv1_train.binary Cross Validation Accuracy = 96.8136% 345.569s % time liblinear-1.21/train -c 4 -e 0.1 -v 5 rcv1_train.binary Cross Validation Accuracy = 97.0161% 2.944s
因为不是同一个人缩写,所以在代码风格上可以看出,原作者充满了c的风味,因为连类的名字都是用 svm_model 这样的风格,liblinear的作者显然在这这些细节上上更像Java的风格。这里我们首先来剖析一下两者的代码风格:
下面要比较的两个类为特征向量中的节点类,Libsvm定义为svm_node,而Liblinear中定义为FeatureNode。即如果一个特征向量x={0.02,0.05,0,0.03},存储时就使用一个包含5 个svm_node 的数组来存储此4 维向量
1 | 2 | 3 | 4 | -1 |
0.02 | 0.05 | 0 | 0.03 | 空 |
如果有一个节点的value为0,程序中是不会存储他的,上面的这个表只是为了能清晰的表示。
public class svm_node implements java.io.Serializable { public int index; public double value; }
public class FeatureNode{ public final int index; public double value; public FeatureNode( final int index, final double value ) { if (index < 0) throw new IllegalArgumentException("index must be >= 0"); this.index = index; this.value = value; } /** * @since 1.9 */ public int getIndex() { return index; } /** * @since 1.9 */ public double getValue() { return value; } /** * @since 1.9 */ public void setValue(double value) { this.value = value; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + index; long temp; temp = Double.doubleToLongBits(value); result = prime * result + (int)(temp ^ (temp >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; FeatureNode other = (FeatureNode)obj; if (index != other.index) return false; if (Double.doubleToLongBits(value) != Double.doubleToLongBits(other.value)) return false; return true; } @Override public String toString() { return "FeatureNode(idx=" + index + ", value=" + value + ")"; } }
好了,上面这些只是开胃小菜,接下来我们将会深入的剖析Libsvm与Liblinear的异同,以及封装他们的细节。