http://blog.csdn.net/zhzhl202/article/details/7438160
简介
因为项目的需要,我们需要同时使用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,程序中是不会存储他的,上面的这个表只是为了能清晰的表示。
Libsvm
- public class svm_node implements java.io.Serializable
- {
- public int index;
- public double value;
- }
Liblinear
- 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;
- }
-
-
-
-
- public int getIndex() {
- return index;
- }
-
-
-
-
- public double getValue() {
- return value;
- }
-
-
-
-
- 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的作者沿袭了C的风格,开头小写,两个单词之间用”_“连接。而Liblinear在继承了Java命名的一贯风格,开头大写,第二个单词也大写。
- 在类的定义上,Libsvm更像是C中的Struct,没有构造函数,只给出了成员变量,而且都是public型,也没有相应的Getter和Setter。而liblinear则遵循了Java类封装的原则。
- Liblinear除了定义构造函数、Getter、Setter外,还定义了hashCode()和equals()。如果将FeatureNode作为HashMap的Key,定义这两个函数至关重要。
好了,上面这些只是开胃小菜,接下来我们将会深入的剖析Libsvm与Liblinear的异同,以及封装他们的细节。