libsvm最新源代码(版本3.21)理解解析(一)

来源:互联网 发布:csoldjb9.0最终优化 编辑:程序博客网 时间:2024/06/06 22:41

一、导读

        SVM的理论通过吴恩达(Andrew NG)的机器学习斯坦福公开课(http://open.163.com/special/opencourse/machinelearning.html)以及一些博文都可以很好的理解了,但理解归理解,如何实现却是另一回事。LIBSVM(http://www.csie.ntu.edu.tw/~cjlin/libsvm/)是一款是应用最为广泛的SVM库之一,它集合了C-SVC,V-SVC以及多分类等各种SVM的变形库。最为重要的是,LIBSVM是一个开源库,开发者和学习者都可以自由下载进行源代码的学习。经过一个月的学习,本人对LIBSVM的源代码稍微研究了一下,特此记录一下自己的理解。当然,现在的理解还不够透彻,以后还会不断补充加强并进行记录。

       当大家对SVM的解决过程理解了之后查看LIBSVM源代码之后却发现有很大的不同,这是因为,林智仁等人并不是根据吴恩达等人所讲解的原版算法进行编写的,而是采用的改进版本,比如SMO的改进版、更新算子的启发式选法,都与网上流传的不太一致,因此,想要弄明白源代码的完全内容需要花费大量时间阅读论文和资料,直到现在,本人仍然存在着大量不理解的地方,本文只是对整个算法框架进行一个大致的理解记录。

       本文撰写的基础是参照林智仁以及学生关于LIBSVM所写的指导论文进行理解而来。具体的论文名如下:

       (1)LIBSVM: A Library for Support Vector Machines

         http://www.csie.ntu.edu.tw/~cjlin/papers/libsvm.pdf

       (2)Working Set Selection Using Second Order Information for Training Support Vector Machines

         http://www.csie.ntu.edu.tw/~cjlin/papers/quadworkset.pdf

二、SVM基本概念

       SVM的详细原理问题就不介绍了,很多公开课和博文都有介绍,这里强调一下SVM的目标公式。SVM的基本形式是解决二分类问题,它的目的是寻找一个最大间隔超平面,使其能最佳分离两类的内容。SVM寻找超平面的公式为:


       其中是超平面,C为惩罚因子,松弛变量,i为样本编号,x向量是样本的特征向量,y是其所对应的分类值(标签值),取+1或-1,样本数量为l。经过对偶变换以及KKT等转换,由(1)式可得:


       其中α为拉格朗日乘子向量,Q是一个l X l的矩阵,是所选取的核函数,是数值全为1的行向量。可能有的同学对该式有点陌生,该公式实际上就是:


          max变成min,减号前后换一下位置即可,其它的也都是经过转换可得,拿支笔在草稿纸上稍微写写就能得到(2)式,至于为什么要采用(2)式而不采用上式,这是因为LIBSVM的论文中以及算法中使用了(2)式。

          当然,这只是LIBSVM中的C-SVC的目标公式,为了更加广泛性(适应),LIBSVM采用的是更加通用的如下目标公式:


        但是有一点需要注意,如果正样本(y值为+1)与负样本(y值为-1)的样本数量差距很大时(称为不平衡数据:unbalanced data),惩罚因子C的选取会严重影响SVM的精度。基于此,一些研究者在1997年和1998年提出了一种针对正负样本采取不同惩罚因子C的目标公式:


        经过转换,可以得到如下公式:


        当然,为了更通用可以将e换成p,0换成,上式也是LIBSVM源代码最终采用的目标公式。

        这个公式在LIBSVM(3.21)源代码中svm.cpp的第375行左右进行了注释:


        这里,对于正样本采用惩罚因子Cp(positive),对于负样本采用惩罚因子Cn(negative)。

三、SMO算法

我们都知道,要解决上述目标公式求最小值,需要存储大的核函数矩阵K(i,j),即K矩阵为l X l大小,当样本数量非常庞大时,当前我们的计算机内存是无法进行存储的。因此,为了解决这个问题,微软的研究者们提出每次只更新两个α,而不是更新所有的α,这就是所谓的SMO算法。然而,在LIBSVM中,所采用的SMO算法与原版相比已经改动非常大,他们采用的是Fan等人于2005年提出的SMO改进版算法,当然,他们采用的算法也并不完全是该算法,仍然进行了不少的改动。

下面稍微介绍一下源代码文件,看过源代码的可能都懂:

(1)svm.h:接口文件,包含众多的函数声明,用于其它调用svm的函数进行调用,其实现都在svm.cpp中。

(2)svm.cpp:算法实现文件,这是最重要的一个文件,所有的算法基本都在该文件中实现,包括这里介绍的SMO算法。

(3)svm-train.c:我们使用LIBSVM库时直接使用的文件,是训练算法的入口,不是本文的重要。

(4)还包含svm-predict.c和svm-scale.c,光看名字就知道干啥的源文件,不是本文的介绍重点。

(5)其它语言的libsvm实现:包含众多的其它语言的实现。

 本文只对(2)中的svm.cpp的SMO实现算法进行介绍,如日后有时间,可能会对其它算法和其它文件再进行博客分析。

 现在,我们可以想一想,如果没看libsvm的SMO实现,我们会怎么做呢?首先,肯定是读取参数(样本值,标签值、惩罚因子值等等),初始化拉格朗日因子α数组和其它值;接着,我们会根据启发式原则选取两个最不符合KKT条件的α值,然后更新这两个值,直到所有的α值都符合条件,也就是到达了停止条件;最后,更新b值( ρ值),有了所有的α值和b值,那么超平面就得到了,最后的分类决策函数也得到了。

其实,LIBSVM中的实现和上述过程基本差不多,只是选取的启发式规则比较复杂,更新α值也与其它地方讲的不一样。为了介绍这两个过程,这里先根据论文的结构介绍理论,然后结合源代码分析。

LIBSVM论文中的SMO过程,即Fan等人提出的SMO算法,也是源代码中基本采用的SMO算法,如下(至于为什么要这么做,本人未来得及看Fan等人提出的原论文,不懂):

(1)初始化α数组,即α[0]...a[l]=0,将k(迭代计数器)设置为1,并将α数组标记为,这时为

(2)若已经收敛到达停止条件(KKT),那么跳出循环,停止算法,返回结果;否则,找出一个两元的工作组,另外定义N=,即从1...l中去除i和j。另外定义对应B中的α,对应N中的α,k为本轮迭代计数值。上述过程可以简单的理解为就是启发式的选择两个α,B是选择的α的下标标号,N是所有的α的下标标号去掉B中的标号的集合,而就是选择需要更新的两个α[i]和α[j],而则是剩下的所有α。

(3)计算,若>0,那么解决如下问题:



<=0,则解决下面的问题:


这个步骤实际上就是计算一个值,这个值由三个核函数确定,若>0,那么采用一种更新α[i]和α[j]的方法,若<=0,则采用另外一种更新α[i]和α[j]的方法。

(4)将迭代计数器k++,同时用α[i]和α[j]替换原来α中的旧的α[i]和α[j]值,跳到第(2)步,开始循环。

这里存在三个难点,其中一个就是如何选择B集合,即i和j,和对应的α[i]和α[j],这就是所谓的启发式规则,工作集的选择;第二个如何判断α收敛,即什么时候停止循环;第三个就是如何更新α[i]和α[j],由于篇幅关系,这三部分内容将在下一篇博文中进行介绍。

四、总结

本文介绍了LIBSVM所要实现的目标函数以及SMO算法的大体步骤,至于详细实现步骤将在下篇进行介绍。





0 0
原创粉丝点击