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

来源:互联网 发布:英格拉姆体测数据 编辑:程序博客网 时间:2024/06/07 21:43

简介

因为项目的需要,我们需要同时使用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 个特征。

[plain] view plaincopy
  1. % time libsvm-2.85/svm-train -c 4 -t 0 -e 0.1 -m 800 -v 5 rcv1_train.binary  
  2. Cross Validation Accuracy = 96.8136%  
  3. 345.569s  
  4. % time liblinear-1.21/train -c 4 -e 0.1 -v 5 rcv1_train.binary  
  5. Cross Validation Accuracy = 97.0161%  
  6. 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

[java] view plaincopy
  1. public class svm_node implements java.io.Serializable  
  2. {  
  3.     public int index;  
  4.     public double value;  
  5. }  

Liblinear

[java] view plaincopy
  1. public class FeatureNode{  
  2.   
  3.     public final int index;  
  4.     public double    value;  
  5.   
  6.     public FeatureNode( final int index, final double value ) {  
  7.         if (index < 0throw new IllegalArgumentException("index must be >= 0");  
  8.         this.index = index;  
  9.         this.value = value;  
  10.     }  
  11.   
  12.     /** 
  13.      * @since 1.9 
  14.      */  
  15.     public int getIndex() {  
  16.         return index;  
  17.     }  
  18.   
  19.     /** 
  20.      * @since 1.9 
  21.      */  
  22.     public double getValue() {  
  23.         return value;  
  24.     }  
  25.   
  26.     /** 
  27.      * @since 1.9 
  28.      */  
  29.     public void setValue(double value) {  
  30.         this.value = value;  
  31.     }  
  32.   
  33.     @Override  
  34.     public int hashCode() {  
  35.         final int prime = 31;  
  36.         int result = 1;  
  37.         result = prime * result + index;  
  38.         long temp;  
  39.         temp = Double.doubleToLongBits(value);  
  40.         result = prime * result + (int)(temp ^ (temp >>> 32));  
  41.         return result;  
  42.     }  
  43.   
  44.     @Override  
  45.     public boolean equals(Object obj) {  
  46.         if (this == obj) return true;  
  47.         if (obj == nullreturn false;  
  48.         if (getClass() != obj.getClass()) return false;  
  49.         FeatureNode other = (FeatureNode)obj;  
  50.         if (index != other.index) return false;  
  51.         if (Double.doubleToLongBits(value) != Double.doubleToLongBits(other.value)) return false;  
  52.         return true;  
  53.     }  
  54.   
  55.     @Override  
  56.     public String toString() {  
  57.         return "FeatureNode(idx=" + index + ", value=" + value + ")";  
  58.     }  
  59. }  

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

  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的异同,以及封装他们的细节。

0 0
原创粉丝点击