代码参考书籍:《机器学习实战》
\qquad 关于支持向量机的一些笔记整理可参考:机器学习笔记——支持向量机的一些整理
\qquad 在前两次阅读SMO算法时,并未搞懂代码中的一些部分是什么作用,此处对书中的代码进行一个解读,这里以《机器学习实战》当中的SMO完整版为例。
代码大体分析
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
\qquad 在进行代码前先对SMO算法进行大体的分析,要完成支持向量机对“最大间隔”的寻找及对参数的优化,那么总体可以分为两个部分:第一寻找出可以优化的点 x x x(按照前面的介绍中一般为先寻找非边界样本进行优化,然后利用边界样本进行优化);第二利用“最大步长”找到 x x x对应的优化点 y y y进行优化。 不断重复上述的循环优化,直至精度达到一定程度,或者达到最大循环次数结束迭代。
\qquad 那么就可以考虑两个循环:外循环寻找 x x x点,同时进行非边界样本优化和边界样本优化的切换,同时控制整个算法程序的循环结束判断;内循环进行参数的优化。这里只对几个有疑问的点进行笔记,文章最后给出完整的代码。
外循环
\qquad 首先进行外循环的分析,在迭代的最初,我们将所有的参数( b b b和 α \alpha α)都初始化定义为0,那么也就是说在最初所有的点都在边界上,所以在一开始我们之间将其视为边界情况进行优化,进行整个样本数据的遍历。在第一次样本遍历后检测是否存在非边界可优化样本,然后进行非边界情况、边界情况的切换优化。伪代码如下:
初始化参数为0
未达到最大循环次数、参数仍可进行优化
属于非边界情况
参数优化
属于边界情况
参数优化
\qquad 接着的问题是如何判断是否存在非边界情况,这里用的是numpy.nonzero方法,提取出非边界情况样本的index,然后进行遍历。
\qquad 然后是如何进行非边界和边界情况的切换,看下面的外代码, a l p h a P a i r s C h a n g e d alphaPairsChanged alphaPairsChanged用于统计是否迭代对参数进行了优化,有优化则返回1,没有优化或不满足KKT条件则返回0。这里的方法是设置了一个 e n t i r e S e t entireSet entireSet参数,同时结合是否仍能优化来进行判断,可以看到一开始是遍历整个样本集的,因为所有的参数都在边界上,当第一次遍历完成后, e n t i r e S e t entireSet entireSet变为 F a l s e False False,那么第二次循环就会遍历非边界的点,而在非边界的点无法再进行优化时( a l p h a P a i r s C h a n g e d = 0 alphaPairsChanged=0 alphaPairsChanged=0), e n t i r e S e t entireSet entireSet被转换为 T r u e True True,于是下一次循环会遍历整个样本集,而此时样本集中的非边界情况对参数已经无法优化了,那么有优化的就是边界情况。
\qquad 通过这种方式达到了非边界、边界情况优化的切换,当两者都无法进行优化同时达到最大循环次数就结束迭代。
def svmSmo(datapath,C,toler,maxIters=40):
"""
主函数、外循环
"""
# 统计大循环次数
iters = 0
# 用于切换边界、非边界情况
entireSet = True
# 统计在边界、非边界情况下是否进行了优化,若当前没有不再有优化则进行切换
alphaPairsChanged =0
# 循环结束条件:达到最大迭代次数或迭代无法提高精度(非边界、边界情况下都无法再进行优化)
while (iters < maxIters) and ((alphaPairsChanged > 0) or (entireSet)):
# 每次循环重新统计
alphaPairsChanged =0
# 最初将所有的alpha都定义为0,所以先遍历整个训练集
if entireSet:
for i in range(oS.m):
alphaPairsChanged += innerL(oS,i)
iters += 1
else:
# 用nonzero方法筛选出非边界情况:即alpha!=0oralpha!=C的情况
nonBond = np.nonzero((oS.alpha.A > 0)*(oS.alpha.A < oS.C))[0]
for i in nonBond:
alphaPairsChanged += innerL(oS,i)
iters += 1
# 切换边界、非边界操作,同时结合着大循环的结束判断来理解
# 若第一次循环,则为遍历整个数据集;第一次循环完成后则先遍历非边界情况,再遍历边界情况,所以第一次True后则将其转换为False
if entireSet:
entireSet = False
# 将非边界情况的迭代结束条件设置为不再有精度提升,这时要考虑边界情况,则再次将entireSet设置为True,利用这种方法进行边界、非边界情况的切换
elif alphaPairsChanged == 0:
entireSet = True
参数类
\qquad 外循环当中使用了一个参数类Parameter,用于存储各种参数,在一开始并不明白 e C a c h e eCache eCache的作用,在后面的内循环中对其作用进行介绍。
class parameter:
"""
参数定义:toler为可容忍的误差或说精度;C为惩罚因子;eCache用于存储Ei,在选择最优j的时候要用到
"""
def __init__(self,dataMat,labelMat