- 转载请注明作者和出处:http://blog.csdn.net/u011475210
- 代码地址:https://github.com/WordZzzz/ML/tree/master/Ch06
- 操作系统:WINDOWS 10
- 软件版本:python-3.6.2-amd64
- 编 者:WordZzzz
前言
在小规模数据集上,上一篇文章中的简化版SMO是没有问题的,但是在更大的数据集上,运行速度就会变慢。
完整版SMO和简化版SMO,实现alpha的更改个代数运算的优化环节一模一样。在优化过程中,唯一的不同就是选择alpha的方式。完整版的SMO算法应用了一些能够提速的启发方法。
Platt SMO算法通过一个外循环来选择第一个alpha,并且其选择过程会在两种方式之间进行切换:一种是在所有数据集上进行单遍扫描,另一种则是在非边界alpha(不等于边界0或C的alpha值)中实现单遍扫描。对整个数据集的扫描很容易,前面已经实现了,而实现非边界alpha值的扫描时,需要建立这些alpha值得列表,然后再对这个表进行遍历。同时,该步骤会跳过那些已知的不会改变的alpha值。
在选择第一个alpha值之后,算法会通过一个内循环来选择第二个alpha。在优化过程中,会通过最大化步长的方式来获得第二个alpha值。在简化版SMO算法中,我们会在选择j之后计算错误率Ej。但在这里,我们会建立一个全局的缓存用于保存误差值,并从中选择使得步长或者Ei-Ej最大的alpha值。
支持函数
和简化版一样,完整版也需要一些支持函数。
- 首要的事情就是建立一个数据结构来保存所有的重要值,而这个过程可以通过一个对象来完成;
- 对于给定的alpha值,第一个辅助函数calcEk()能够计算E值并返回(因为调用频繁,所以必须要单独拎出来);
- selectJ()用于选择第二个alpha或者说内循环的alpha值,选择合适的值以保证在每次优化中采用最大步长;
- updateEk()用于计算误差值并将其存入缓存中。
1 | '''#######******************************** |
优化例程
接下来简单介绍一下用于寻找决策边界的优化例程。
大部分代码和之前的smoSimple()是一样的,区别在于:
- 使用了自己的数据结构,该结构在oS中传递;
- 使用selectJ()而不是selectJrand()来选择第二个alpha的值;
- 在alpha值改变时更新Ecache。
1 | def innerL(i, oS): |
外循环代码
外循环代码的输入和函数smoSimple()完全一样。整个代码的主体是while循环,终止条件:当迭代次数超过指定的最大值,或者遍历整个集合都未对任意alpha对进行修改时,就退出循环。while循环内部与smoSimple()中有所不同,一开始的for循环在数据集上遍历任意可能的alpha。通过innerL()来选择第二个alpha,并在可能时对其进行优化处理。如果有任意一对alpha值发生改变,就会返回1.第二个for循环遍历所有的非边界alpha值,也就是不在边界0或C上的值。
1 | def smoP(dataMatIn, classLabels, C, toler, maxIter): |
测试代码,大家有兴趣的话可以多次运行计算一下运行时间的平均值,看看和简化版相比快了多少。
1 | reload(svmMLiA) |
像之前一样,打印b和alpha,得出的数据用来画图。
1 | b |
常数C一方面要保障所有样例的间隔不小于1.0,另一方面又要使得分类间隔要尽可能大,并且要在这两方面之间平衡。如果C很大,那么分类器就会将力图通过分隔超平面对多有的样例都正确分类。这种优化结果如下图,很明显,支持向量变多了。如果数据集非线性可分,就会发现支持向量会在超平面附近聚集成团。
分类测试
好了,终于可以拿我们计算出来的alpha值进行分类了。首先必须基于alpha值得到超平面,这也包括了w的计算。
1 | def calcWs(alphas, dataArr, classLabels): |
上述代码中最重要的就是for循环,实现多个数的乘积。虽然for循环遍历了数据集中的所有数据,但是最终起作用的只有支持向量。
1 | reload(svmMLiA) |
上面的测试中,计算值大于0属于1类,小于0属于-1类。
至此,线性分类器介绍完了,如果数据集非线性可分,那么我们就需要引入核函数的概念了,下一篇将进行介绍。
系列教程持续发布中,欢迎订阅、关注、收藏、评论、点赞哦~~( ̄▽ ̄~)~
完的汪(∪。∪)。。。zzz