Libsvm与Liblinear源码剖析与封装(一)

来源:互联网 发布:mac chown 777 编辑:程序博客网 时间:2024/05/22 08:04

简介

因为项目的需要,我们需要同时使用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呢?作者给出几点建议:

  1.  当你面对海量的数据时,这里的海量通常是百万级别以上。海量数据分为两个层次:样本数量和特征的数量。
  2. 使用线性和非线性映射训练模型得到相近的效果。
  3. 对模型训练的时间效率要求较高。

在这类情况下,建议你使用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.binaryCross Validation Accuracy = 96.8136%345.569s% time liblinear-1.21/train -c 4 -e 0.1 -v 5 rcv1_train.binaryCross 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 维向量1234-10.020.0500.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;    }    /**     * @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 + ")";    }}

从上述代码,我们理解可以看出若干端倪:

  1. 在类命名上,Libsvm的作者沿袭了C的风格,开头小写,两个单词之间用”_“连接。而Liblinear在继承了Java命名的一贯风格,开头大写,第二个单词也大写。
  2. 在类的定义上,Libsvm更像是C中的Struct,没有构造函数,只给出了成员变量,而且都是public型,也没有相应的Getter和Setter。而liblinear则遵循了Java类封装的原则。
  3. Liblinear除了定义构造函数、Getter、Setter外,还定义了hashCode()和equals()。如果将FeatureNode作为HashMap的Key,定义这两个函数至关重要。

好了,上面这些只是开胃小菜,接下来我们将会深入的剖析Libsvm与Liblinear的异同,以及封装他们的细节。