使用libsvm对MNIST数据集进行实验---浅显易懂!

原文:http://blog.csdn.net/arthur503/article/details/19974057

在学SVM中的实验环节,老师介绍了libsvm的使用。当时看完之后感觉简单的说不出话来。

1. libsvm介绍

虽然原理要求很高的数学知识等,但是libsvm中,完全就是一个工具包,拿来就能用。当时问了好几遍老师,公司里做svm就是这么简单的?敲几个命令行就可以了。。。貌似是这样的。当然,在大数据化的背景下,还会有比如:并行SVM、多核函数SVM等情况的研究和应用。

实验环节老师给的数据很简单,也就1000个数据点,使用svm进行分类。没有太多好说的。于是自己想要试试做手写字体分类检测,类似于行车违章拍照后的车牌识别。从网上搜到了数据集为MNIST数据集,是一个入门的基本数据集。

关于libsvm的介绍和使用参考:libSVM介绍。不过,svm-toy是最多支持三分类的,而不是只是二分类。

使用windows文件夹下的svm-train.exe,svm-predict.exe命令可以来进行建模和预测,具体参数看文档。

svm-train的主要可选参数有:

-s 选择SVM类型。一般选的是C-SVM

-c 选择松弛变量的权重系数。C越大,对松弛变量的惩罚越大,两个支持向量直线之间的间隔就越小,模型就越精确苛刻,对噪声数据点容忍小,越容易过拟合;C越小,两个支持向量直线之间的距离越大,对噪声的容忍能力就越大,最终效果越好。但是,模型分错的数据点就越多,容易欠拟合。

-t 选择核函数。一般是线性和RBF做对比,线性速度更快,但要求数据可分;RBF更通用,默认选择RBF。

-g garma系数。是exp(-gamma*|u-v|^2),相当于gamma=1/(2τ^2)。τ表示高斯函数中的宽度,g与τ成反比。g越大,τ越小,则高斯函数越窄,覆盖面积小,这样需要的支持向量越多,模型越复杂,容易过拟合。

-wi 对样本分类的权重分配。因为,在分类中,某些分类可能更加重要。

-v 交叉验证的参数。用来做交叉检验。

svm-predict只有一个可选参数,一般也不用。

2. 数据处理

从MNIST官网下载,解压,按照其中的数据格式对byte数据进行读取,提取到了train和test的图片灰度数据。图片均为28*28像素。其中,train数据为60000张,test数据10000张。
先使用svm测试了一下1000个数据,结果发现效果很差!只有11%左右的正确率。经过检查和实验,发现是没有对原始数据进行scale,可能导致数据差距过大,从而对结果产生影响。 
实验记录如下:
使用SVM在MNIST的十分类,在不对图像灰度数据进行scale的情况下,即:直接使用图像的像素值进行建模,最终得到只有11%左右的正确率,相当于十分之一。检查predict结果验证,发现predict都预测为1(这样差不多正好是十分之一的正确率)。因此,猜测数据若相差过大的情况下,不进行scale会严重影响SVM的性能。
阅读libsvm的文档后,将图像灰度数据scale到[0,1]之间,之后再使用小数据集测试得到80%+的正确率。
使用c=2,其他参数默认的情况下,对train_60k_scale.txt数据集进行建模,对test_10k_scale.txt测试数据集进行验证,得到95.02%的正确率。
使用./tools/grid.py方法(需修改内容参数,参看:libsvm 使用介绍),使用文档中的方法对1k的测试数据,对c和g都以(-10,10,1)为参数来寻找最优参数(实际上即是grid.py使用交叉验证法来寻找),最终得到最优参数为:c=4.0 g=0.015625 rate=91.1。按照该参数进行设定、使用train_60k_scale.txt数据集训练SVM模型,并对test_10k_scale.txt测试数据集进行验证,最终得到98.46%的正确率!
最终训练出来的SVM模型参数如下:
svm_type c_svc
kernel_type rbf
gamma 0.015625
nr_class 10
total_sv 12110
rho -0.409632 -0.529655 -0.842478 -0.567781 -0.125654 -0.34742 -0.696415 -0.191642 -1.4011 -0.0458988 -0.303381 0.0614391 0.420461 0.266255 -0.0264913 0.0878689 0.0784119 0.167691 0.0910791 0.577181 0.395401 0.0896789 0.381334 0.134266 -0.0137303 0.902749 0.779241 0.120543 0.203025 -0.523485 0.3886 0.468605 -0.14921 1.10158 -0.320523 -0.120132 -0.656063 -0.44432 -0.925911 -0.421136 -0.176363 -1.16086 0.0610109 0.0764374 -0.192982
label 5 0 4 1 9 2 3 6 7 8
nr_sv 1466 843 1229 516 1531 1419 1373 948 1101 1684
可以看出,在这60000个训练模型样本中,最终使用的支持向量有12110个。

3. 模型解释

对于支持向量模型中的参数解释,使用二分类的结果比较好解释,如下:
svm_type c_svc
kernel_type linear 使用线性分类器
nr_class 2 二分类
total_sv 15 支持向量个数
rho 0.307309
label 1 -1
nr_sv 8 7 正负类的支持向量(SV)个数
SV
1 1:7.213038 2:0.198066 
1 1:-4.405302 2:0.414567 
1 1:8.380911 2:0.210671 
1 1:3.491775 2:0.275496 
1 1:-0.926625 2:0.220477 
1 1:-2.220649 2:0.406389 
0.4752011717540238 1:1.408517 2:0.377613 
0.4510429211309505 1:-8.633542 2:0.546162 
-1 1:8.869004 2:-0.343454 
-1 1:7.263065 2:-0.239257 
-1 1:-4.2467 2:0.057275 
-0.9262440928849748 1:0.755912 2:-0.225401 
-1 1:-9.495737 2:-0.027652 
-1 1:9.100554 2:-0.297695 
-1 1:-3.93666 2:-0.047634 
支持向量分三种:对于正类数据:C(也就是参数-c:C设置的值)表示边界内的支持向量、0<x<C表示边界上的支持向量(即:在wx+b=±1和wx+b=0之间的支持向量)。对于负类数据也同理。支持向量机就主要是根据这两类支持向量来建立模型的。对于第三类数据,也就是错分数据,他们的位置是在支持向量的平面之外,也就是在另一类的区域,并且|wx+b|>1。这一类的点,在训练数据时并不存在,因此,不会出现在支持向量SV中。
 

 [整理]Libsvm官方FAQ翻译

quote献给使用libsvm做毕业论文的朋友们—— By 
faruto

 

 

 

问: 我在那里能够找到libsvm的文件 ?

软件包中有一个 README 文件,里面详细说明了所有参数选项、数据格式以及库函数的调用。在python目录下,模型选择工具和python界面的libsvm各有一个README文件。 初学者可以通过A practical guide to support vector classification 了解如何训练和检验数据.论文LIBSVM : a library for support vector machines详细讨论了libsvm的使用. 

 

 

 

以前版本的libsvm都有什么变化?
详见变化日志你可以到这里下载以前版本的libsvm.

 

 

 

 

问:为什么有时我在training/model 文件中看不到所有的数据属性呢?W

libsvm
应用了所谓的”稀疏“格式,这样零值就不用存储了。例如,有下面属性的数据

1 0 2 0

将被替换为: 1:1 3:2

 

 

 

:如果我的数据是非数值型的,可以用libsvm吗?

目前libsvm只支持数值型的数据。因此,你必须将非数值型的转为数值型的数据。比如,你可以用二进制属性来替代原来的类别属性。

 

 

 

 

:为什么要采用稀疏格式呢密集数据在训练时候会不会很慢?
这是个具有争议的话题。将系数向量赋值给核函数是比较慢的,因此总的训练时间至少是采用密集格式的2倍或3倍。但是,我们不支持密集格式的数据,因为我们不能够处理极度稀疏的数据。代码的简洁也是我们考虑的一个因素。目前我们决定只支持稀疏格式的数据。

 

 

 

 

问:怎样选择核函数?
通常我们建议你首先采用RBF核函数。Keerthi 和 Lin 的最近的研究(
下载论文
) 表明如果模型选择RBF的话,
就没有必要再考虑线性核函数了。采用sigmoid核函数的矩阵不一定会正有界,而且通常它的准确性也不如RBF(可参见Lin和Lin的论文
此处下载
). 多项式核函数还不错,但是如果度数很高的话,数值困难就会发生
(考虑到(<1)的d次幂会趋向0,(>1)的d次幂会趋向无穷)

 

 

 

 

: libsvm是否可以用来专门处理线性SVMD
不是的,目前libsvm用同样的方法处理线性/非线性SVMs. 注意:如果采用线性核函数,一些技巧可能会节省训练/检验的
时间。 因此libsvm对线性SVM并不时特别有效,尤其是采用很大的C的问题,这些问题数据的数值比其属性值要大得多。
你可以: 
仅用很大的C.下面的论文表明了:当C大于一个确定的阀值以后,判决函数是相同的。

S.S. Keerthi and C.-J. LinAsymptoticbehaviors of support vector machines with Gaussian kernel.NeuralComputation, 15(2003), 1667-1689.
尝试用bsvm,它有个对解决线性SVMs很有效的方法.你可以在下面的研究中找到更详细的内容: 
K.-M. Chung, W.-C. Kao, T. Sun, and C.-J. Lin. Decomposition Methods for Linear Support Vector Machines. NeuralComputation, 16(2004), 1689-1704.
另外,你并没必要一定要解决线性SVMs.你可以参考前面有关如何选取核函数的问题。

 

 

 

 

:将属性值限制到[0,1],是否比限制到 [-1,1]有很大的不同?
对于线性规划方法,如果采用可RBF核函数并进行了参数的选择,两者是没什么不同的。假设Mi和mi分别代表第i个属性的
最大值和最小值.规划到[0,1]即: 

x'=(x-mi)/(Mi-mi)对于[-1 1]: 

x''=2(x-mi)/(Mi-mi)-1.在RBF核函数中: 

x'-y'=(x-y)/(Mi-mi), x''-y''=2(x-y)/(Mi-mi).因此在[0,1]数据规划中用(C,g),和[-1 1]数据规划中用(C,g/2)是一样的。 
尽管性能相同,但是计算机时间可能不同。对于有许多零入口的数据, [0,1]规划保持了输入数据的稀疏特性,因此可能
会节省计算时间。

 

 

 

 

我的数据是不平衡的,libsvm能解决这样的问题吗?
可以。libsvm有一个-wi选项。例如,你用: 
svm-train -s 0 -c 10 -w1 1 -w-1 5 data_file 则对类别“-1”的惩罚就较大。注意-w选项仅用在C-SVC中。

 

 

 

 

: nu-SVCC-SVC有什么不同之处?
除了参数不同外,两者基本是一样的。C-SVC中,C的范围是0到无穷,nu-SVC中C的范围是[0 1]。 nu一个很好的
特性是:它与支持向量的比率和训练误差的比率相关。

 

 

 

 

:对于多分类SVMlibsvm采用的是什么方法 ? 为什么不用"1-against-the rest"  ?
对于多分类,我们采用的是1against 1法.我们的选择建立在以下对比的基础上: C.-W. Hsu and C.-J. Lin. A comparison of methods for multi-class support vector machines IEEE Transactions on Neural Networks, 13(2002), 415-425. "1agains1the rest"是个很好的方法,而且分类效果
和"1-against-1."可以相比。但是我们采用后者,因为它训练的时间更短。

 

 

 

 

:如果我想解决L2-svm 问题(即二次方误差项). 我应该怎样修改代码 ?
这十分简单. 以c-svc为例, 在svm.cpp中只要修改两个地方即可. 第一, 将solve_c_svc中的: 

s.Solve(l, SVC_Q(*prob,*param,y), minus_ones, y,alpha, Cp, Cn, param->eps, si, param->shrinking);修改为: 

s.Solve(l, SVC_Q(*prob,*param,y), minus_ones, y,alpha, INF, INF, param->eps, si, param->shrinking);第二:在SVC_Q类中, 声明C为私有变量: 

double C;在构造(constructor)中,将它赋给param.C: 
this->C = param.C;在子程序get_Q中, 在for循环之后,添加: 

if(i >= start && i < len)data += 1/C;对于一分类svm,以上修改完全一样。对于SVR,上面的if语句就没有必要了,你只要用一个简单的赋值语句即可: 

data[real_i] += 1/C;
原文地址:https://www.cnblogs.com/zhizhan/p/4430279.html