matlab 基于 libsvm工具箱的svm分类遇到的问题与解决
最近在做基于无线感知的身份识别这个工作,在后期数据处理阶段,需要使用二分类的方法进行训练模型。本身使用matlab做,所以看了一下网上很多都是使用libsvm这个工具箱,就去下载了,既然用到了想着就把这个东西梳理一下,顺便记录一下过程中的遇到的问题。
1、 Libsvm下载与安装
Libsvm这个工具箱是台湾大学林智仁(Lin Chih-Jen)教授等开发的一套基于SVM的模式识别的软件包,网上也有详细的介绍,还有源代码,很方便学习。
下载:https://www.csie.ntu.edu.tw/~cjlin/libsvm/
我用的是现在比较新的一个版本:libsvm-3.23
安装:安装过程比较简单,下载好工具包之后傻瓜操作就可以了
A、解压到matlab安装根目录toolbox下(其他目录也行,最好放这儿,比较好找,其他地方哪天说不定删掉) D:\Program Files\MATLAB\R2015b\toolbox\
B、 打开matlab之后设置好当前路径:D:\Program Files\MATLAB\R2015b\toolbox\libsvm-3.23\matlab,在命令行输入mex –setup,选择编译环境。(编译环境比较重要,需要将这个工具箱中的源码使用C编译器编译,不然无法使用)
C、 在命令行输入 make 进行编译,在D:\Program Files\MATLAB\R2015b\toolbox\libsvm-3.23\matlab 生成svmtrain.mexw64、svmpredict.mexw64、libsvmread.mexw64,
libsvmwrite.mexw64这四个文件,仔细查看,如果没有编译成功也无法使用。
D、到这一步其实应该是没啥问题了,可以在命令行输入 which svm,会输出你之前工具箱所在的那个路径。如果都没啥问题,就可以使用了,不放心还可以使用它自带的数据进行测试。
2、 svm 的使用
svm的使用也比较简单,不过也根据需求而定,可以做one-class、two-class,当然也可以实现多分类。这都取决于svm模型的参数设定,关于参数详细可以doc svmtrain在matlab帮助文档中查看。
1 训练:model = svmtrain(trainlabel,traindata,PARAMETERS); 2 3 预测:[predictlabel,predictacc] =svmpredict(testlabel,testdata,model);
PARAMETERS一般是用键值对的方式,如‘-s 0 -c 100 -g 0.8 -t 2 -b 1’ 可以没有,没有就是默认参数。一般来说为了达到好的结果都会进行调参
简单列一下主要参数:(红色的是我实验中用到的参数)
-s svm类型:SVM设置类型(默认0)
0 -- C-SVC:C-支持向量分类机; C代表惩罚系数,C越大表示对错误分类的惩罚越大,通过自己实验发现这个C确实对准确率影响很大,C的选择很关键。
1 --v-SVC:v-支持向量分类机;
2 – 一类SVM:单类别-支持向量机,不需要类标号,用于支持向量的密度估计和聚类。这种思路可能更适合对于那种异常检测的应用,这个单类模型需要有很高的区分度。
3 -- e -SVR:ε-支持向量回归机。
4 -- v-SVR:n-支持向量回归机。
3、4对应回归问题,用到可以试一下。
-t 核函数类型:核函数设置类型(默认2)
0 – 线性核:u'v
1 – 多项式核:(r*u'v + coef0)^degree
2 – RBF核:exp(-gamma|u-v|^2)
3 – sigmoid:tanh(r*u'v + coef0)
-d degree : 用于多项式核函数,默认值是3
-g gamma :多项式核函数、RBF核函数、sigmoid核函数。默认是1/k(num_features)
-r coef0 :多项式核函数、sigmoid核函数。默认是0
-c cost : C-SVC使用的惩罚因子C,epsilon-SVR, and nu-SVR中也使用 ,默认值为1
-n nu : nu-SVC, one-class SVM, and nu-SVR使用的参数nu。默认值为0.5
-p epsilon : epsilon-SVR的损失函数所用参数,默认0.1
-m cachesize :设置缓存大小,默认100M
-e epsilon :终止标准公差,默认0.001
-h shrinking :是否使用缩减的启发式
-b probability_estimates :是否训练SVC或SVR模型进行概率估计,0或1(默认为0)
-wi weight : C-SVC
-v n: n-折交叉验证,必须大于等于2
-q : quiet mode (no outputs)
%关于网格搜索调参:网格搜索其实就是穷举给定范围内c和g的值来选择模型,c和g给定的范围不要太大,不然穷举次数太多导致程序跑起来太慢。 %寻找最优c和g % c 的变化范围是 2^(-2),2^(-1.5),...,2^(10), g 的变化范围是 2^(-4),2^(-3.5),...,2^(4) [bestacc,bestc,bestg] = SVMcgForClass(trainlabelk,traindatak,-2,10,-4,4,3,0.5,0.5,0.9); %将得到的最高的参数bestc和bestg用来训练模型 cmd = ['-c ',num2str(bestc),' -g ',num2str(bestg),' -t ',num2str(2),' -b ',num2str(1)]; model = svmtrain(trainlabel,traindatak,cmd);
3、 遇到的问题
A 分类模型
多分类(单个模型):
我要做的是不同人的身份识别,比如我现在有20个人的数据,我需要识别出这20个人,这样一看好像就是个多分类问题,一个多分类模型就可以搞定,我也用了一些matlab中自带的一个应用于多分类的svm(这个在2014版以后的matlab中有):
Mdl = fitcecoc(traindata,trainlabel);
[preb] = predict(Mdl,testdata);
但是后来一想,这个模型根本没法扩展,人多了就没法用了,实际根本不可行。这样根本没法扩展,更多的人来怎么办?
基于异常检测的多分类(one class SVM):这个实现方法比较多(有一对一,一对多等),可以根据不同的数据集以及应用场景确定
直接用每个人的数据给自己训练一个模型,这样得到N个不同的模型,用不同的测试数据测试,显然结果不是很好,这个对训练数据的要求很高,如果做人脸识别或者指纹识别这种,可能更适合这种方式。
前面两种都试了,效果不理想,所以只能用基于二分类的多分类,给每个人建立模型的时候,将其他所有不是这个人的数据作为负样本进行训练,这样虽然说扩展性也不是很好,但还是有进行扩展的空间。
B、可能性评估出现Line search fails in two-class probability estimates
因为每个人只有50条左右的数据,所以在给每个人训练样本时,正负样本的比例接近几十倍。当然这个问题确实会有,但是为了模型的准确性也不可避免这样做。
开始训练模型和测试,使用70%训练集,30测试集的原则进行数据集划分,在人数少时识别效果非常好,在人数多时出现了很奇怪的问题:结果接近两个极端,一个很好,一个很差,而且重复很多次实验,仍然是这样,没有别的结果出现,差的结果占百分之七八十。反复检查之后发现坏结果发生时,命令行会输出这样一句话:Line search fails in two-class probability estimates
百度之后发现这个问题也确实有人遇到过但不是很多,本人也是菜鸟,所以还真不知道哪里出了问题,参考网上的说法(https://stackoverflow.com/questions/16709053/libsvm-outputs-line-search-fails-in-two-class-probability-estimates),我也试了,还是没有找到端倪:
有回答说这个是单纯效果不好的原因,我有点怀疑,但是为什么只有两个极端呢,这个结果不好应该是个随机的,至少结果分布应该正常的,这完全就不是我想看到的结果。所以我打开了svm的源码,找到输出那条语句的地方:
果然,在可能性计算的时候,就没有进去这个循环,而是直接跳出了,突然想到可能模型并没有训练,去看了一下训练好的模型,结果是这样:
里面全是空的,支持向量都没有,ok那就是说,一开始由于样本的不均衡导致svm觉得这两类训练数据完全分不开就直接罢工不干了…好吧,就是数据集惹的祸。
后面改了训练样本中正负样本的比例,就不会出现这个问题了。
参考链接:
https://www.cnblogs.com/LuffySir/p/5961219.html
https://blog.csdn.net/s9434/article/details/75386112
https://stackoverflow.com/questions/36686672/why-do-i-get-the-message-line-search-fails-in-two-class-probability-estimates
https://blog.csdn.net/ture_dream/article/details/52788745