机器学习笔记——支持向量机SMO算法完整版代码分析

  • 代码大体分析
    • 外循环
      • 参数类
    • 内循环
      • KKT条件判断
      • eCache参数
  • 完整SMO代码
  • 添加核函数代码

代码参考书籍:《机器学习实战》

\qquad关于支持向量机的一些笔记整理可参考:机器学习笔记——支持向量机的一些整理

\qquad在前两次阅读SMO算法时,并未搞懂代码中的一些部分是什么作用,此处对书中的代码进行一个解读,这里以《机器学习实战》当中的SMO完整版为例。

代码大体分析

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

\qquad在进行代码前先对SMO算法进行大体的分析,要完成支持向量机对“最大间隔”的寻找及对参数的优化,那么总体可以分为两个部分:第一寻找出可以优化的点xxx(按照前面的介绍中一般为先寻找非边界样本进行优化,然后利用边界样本进行优化);第二利用“最大步长”找到xxx对应的优化点yyy进行优化。 不断重复上述的循环优化,直至精度达到一定程度,或者达到最大循环次数结束迭代。

\qquad那么就可以考虑两个循环:外循环寻找xxx点,同时进行非边界样本优化和边界样本优化的切换,同时控制整个算法程序的循环结束判断;内循环进行参数的优化。这里只对几个有疑问的点进行笔记,文章最后给出完整的代码。

外循环

\qquad首先进行外循环的分析,在迭代的最初,我们将所有的参数(bbb和α\alphaα)都初始化定义为0,那么也就是说在最初所有的点都在边界上,所以在一开始我们之间将其视为边界情况进行优化,进行整个样本数据的遍历。在第一次样本遍历后检测是否存在非边界可优化样本,然后进行非边界情况、边界情况的切换优化。伪代码如下:

初始化参数为0
未达到最大循环次数、参数仍可进行优化属于非边界情况参数优化属于边界情况参数优化

\qquad接着的问题是如何判断是否存在非边界情况,这里用的是numpy.nonzero方法,提取出非边界情况样本的index,然后进行遍历。

\qquad然后是如何进行非边界和边界情况的切换,看下面的外代码,alphaPairsChangedalphaPairsChangedalphaPairsChanged用于统计是否迭代对参数进行了优化,有优化则返回1,没有优化或不满足KKT条件则返回0。这里的方法是设置了一个entireSetentireSetentireSet参数,同时结合是否仍能优化来进行判断,可以看到一开始是遍历整个样本集的,因为所有的参数都在边界上,当第一次遍历完成后,entireSetentireSetentireSet变为FalseFalseFalse,那么第二次循环就会遍历非边界的点,而在非边界的点无法再进行优化时(alphaPairsChanged=0alphaPairsChanged=0alphaPairsChanged=0),entireSetentireSetentireSet被转换为TrueTrueTrue,于是下一次循环会遍历整个样本集,而此时样本集中的非边界情况对参数已经无法优化了,那么有优化的就是边界情况。

\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 += 1else:# 用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后则将其转换为Falseif entireSet:entireSet = False# 将非边界情况的迭代结束条件设置为不再有精度提升,这时要考虑边界情况,则再次将entireSet设置为True,利用这种方法进行边界、非边界情况的切换elif alphaPairsChanged == 0:entireSet = True

参数类

\qquad外循环当中使用了一个参数类Parameter,用于存储各种参数,在一开始并不明白eCacheeCacheeCache的作用,在后面的内循环中对其作用进行介绍。

class parameter:"""参数定义:toler为可容忍的误差或说精度;C为惩罚因子;eCache用于存储Ei,在选择最优j的时候要用到"""def __init__(self,dataMat,labelMat,C,toler):self.x = dataMatself.y = labelMatself.m = dataMat.shape[0]self.alpha = np.mat(np.zeros((self.m,1)))self.b = 0self.C = Cself.toler = toler self.eCache = np.mat(np.zeros((self.m,2)))

内循环

KKT条件判断

\qquad在外循环完成后,就要考虑如何对参数进行优化,这就涉及到:如何寻找与xxx对应的最优yyy,也就是寻找最大步长;如何进行参数的迭代优化,也就是文章SVM解释:五、SMO算法当中介绍的如何对参数进行迭代,这里对几个关键的点进行解释。

# 判断是否满足KKT条件,若不满足则进入优化
if ((oS.alpha[i,0] < oS.C) and (oS.y[i,0]*Ei < -oS.toler)) or ((oS.alpha[i,0] > 0) and (oS.y[i,0]*Ei > oS.toler)):

首先是对KKT条件的判断,这里的ififif判断条件来源于如下:
\qquad\qquad\qquad\qquad\qquad\qquad\qquad当αi≥0\alpha_i\ge 0αi​≥0时,yi⋅(w⋅xi+b)≤1y_i·(w·x_i+b)\le 1yi​⋅(w⋅xi​+b)≤1
上述条件等价于():
\qquad\qquad\qquad\qquad\qquad\qquad\qquad当αi≥0\alpha_i\ge 0αi​≥0时,Ei=yi⋅ui−yi2≤0E_i=y_i·u_i-y_i^2\le 0Ei​=yi​⋅ui​−yi2​≤0
即:
\qquad\qquad\qquad\qquad\qquad\qquad\qquad当αi≥0\alpha_i\ge 0αi​≥0时,Ei=yi⋅Ei≤0E_i=y_i·E_i\le 0Ei​=yi​⋅Ei​≤0
那么当其大于tolertolertoler时,就判定其不满足KKT条件。

eCache参数

\qquad在上面的讨论中提到了在参数类的定义时,存在一个eCacheeCacheeCache参数,其作用是用于存储有效的EiE_iEi​,那么如何理解呢?我们最初给α\alphaα等参数定义时,是全部初始化为0的,那么就会造成一个问题,在选择最大步长时,所涉及到的EjE_jEj​并不是其真实的值,所以这里定义一个eCacheeCacheeCache参数,在所有EjE_jEj​都不是有效时,随机选择jjj,在迭代过程中会逐渐出现有效的EjE_jEj​,当存在有效的EjE_jEj​时,就在这些有效的EjE_jEj​当中选取jjj。

完整SMO代码

# -*- coding: utf-8 -*-
"""
Created on Sat Aug 10 19:46:57 2019@author:wangtao_zuelE-mail:wangtao_zuel@126.com支持向量机SMO算法完整版"""
import pandas as pd
import numpy as np
import matplotlib.pyplot as pltdef svmSmo(dataPath,outerPath,C=0.6,toler=0.001,maxIters=40):"""主函数"""# 读取数据,此处用的为xlsx类型数据,其他类型的数据进行相应的改动dataMat,labelMat = loadData(dataPath)# 将数据放在参数类中,并定义一些后面需要用到参数oS = parameter(dataMat,labelMat,C,toler)# 统计大循环次数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)print("fullSet. iters:%d. alphaPairsChanged:%d"%(iters,alphaPairsChanged))iters += 1else:# 用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)print("nonBond. iters:%d. alphaPairsChanged:%d"%(iters,alphaPairsChanged))iters += 1# 切换边界、非边界操作,同时结合着大循环的结束判断来理解# 若第一次循环,则为遍历整个数据集;第一次循环完成后则先遍历非边界情况,再遍历边界情况,所以第一次True后则将其转换为Falseif entireSet:entireSet = False# 将非边界情况的迭代结束条件设置为不再有精度提升,这时要考虑边界情况,则再次将entireSet设置为True,利用这种方法进行边界、非边界情况的切换elif alphaPairsChanged == 0:entireSet = Trueprint("迭代优化完成!")
#    print(oS.alpha)# 计算参数w = calcW(oS)# 可视化,只适用于二维数据
#    draw(oS,w)# 敏感性分析,只适用于二维数据
#    parameterAnalyze(oS,w)# 训练集外数据预测predict(oS,w,outerPath)
#    return w[0,0],w[1,0],oS.b[0,0]def loadData(datapath):"""数据读取"""data = pd.read_excel(datapath)# 将训练集数据的特征和分类分开features = np.mat(data.iloc[:,:-1])labels = np.mat(data.iloc[:,-1]).Treturn features,labelsclass parameter:"""参数定义:toler为可容忍的误差或说精度;C为惩罚因子;eCache用于存储Ei,在选择最优j的时候要用到"""def __init__(self,dataMat,labelMat,C,toler):self.x = dataMatself.y = labelMatself.m = dataMat.shape[0]self.alpha = np.mat(np.zeros((self.m,1)))self.b = 0self.C = Cself.toler = toler self.eCache = np.mat(np.zeros((self.m,2)))def innerL(oS,i):"""迭代优化部分,成功优化则返回1;满足KKT条件、无法优化返回0"""Ei = calcEi(oS,i)# 判断是否满足KKT条件,若不满足则进入优化if ((oS.alpha[i,0] < oS.C) and (oS.y[i,0]*Ei < -oS.toler)) or ((oS.alpha[i,0] > 0) and (oS.y[i,0]*Ei > oS.toler)):# 寻找最大步长的jj,Ej = selectJ(oS,i,Ei)# 保存一下上一步的alpha,在新alpha计算中需要用到alphaIOld = oS.alpha[i,0].copy()alphaJOld = oS.alpha[j,0].copy()# 判断alpha上下界if oS.y[i,0] != oS.y[j,0]:L = max(0,oS.alpha[j,0]-oS.alpha[i,0])H = min(oS.C,oS.C+oS.alpha[j,0]-oS.alpha[i,0])else:L = max(0,oS.alpha[j,0]+oS.alpha[i,0]-oS.C)H = min(oS.C,oS.alpha[j,0]+oS.alpha[i,0])# 若L=H,则alpha必定在边界上,没有优化的空间,可直接返回0值if L == H:return 0eta = oS.x[i,:]*oS.x[i,:].T + oS.x[j,:]*oS.x[j,:].T - 2*oS.x[i,:]*oS.x[j,:].T# 若eta为0,则返回0,因为分母不能为0,其实eta并不会为负数if eta == 0:return 0# 求新的参数,要注意符号问题,尤其是在结果中出现alpha全为0时,可能出现了符号问题oS.alpha[j,0] += oS.y[j,0]*(Ei-Ej)/eta# 将新参数与上下界进行比较oS.alpha[j,0] = clipAlpha(oS.alpha[j,0],H,L)# 更新eChacheupdateEi(oS,j)# 若优化精度提高较小,则返回0if abs(oS.alpha[j,0]-alphaJOld) < 0.00001:
#            print("优化提高不大,放弃此次优化!")return 0# 更新alpha_i,根据alpha_i*y_i和alpha_j*y_j的变动程度相同但方向相反来计算oS.alpha[i,0] += oS.y[i,0]*oS.y[j,0]*(alphaJOld-oS.alpha[j,0])updateEi(oS,i)# 更新参数bbi = oS.b - Ei - oS.y[i,0]*(oS.alpha[i,0]-alphaIOld)*oS.x[i,:]*oS.x[i,:].T - oS.y[j,0]*(oS.alpha[j,0]-alphaJOld)*oS.x[i,:]*oS.x[j,:].Tbj = oS.b - Ej - oS.y[i,0]*(oS.alpha[i,0]-alphaIOld)*oS.x[i,:]*oS.x[j,:].T - oS.y[j,0]*(oS.alpha[j,0]-alphaJOld)*oS.x[j,:]*oS.x[j,:].T# 判断b,且注意这里b值返回的不再是数值型数据if (oS.alpha[i,0] > 0) and (oS.alpha[i,0] < oS.C):oS.b = bielif (oS.alpha[j,0] > 0) and (oS.alpha[j,0] < oS.C):oS.b = bjelse:oS.b = (bi+bj)/2# 所有参数都更新了则返回1return 1# 若满足KKT条件,则返回0else:return 0def updateEi(oS,i):"""更新eChache"""Ei = calcEi(oS,i)oS.eCache[i,:] = [1,Ei]def clipAlpha(alpha,H,L):"""alpha与上下界比较"""if alpha < L:alpha = Lelif alpha > H:alpha = Hreturn alpha        def selectJ(oS,i,Ei):"""寻找和i对应最大步长的j"""maxJ = 0maxdeltaE = 0oS.eCache[i,:] = [1,Ei]validEcacheList = np.nonzero(oS.eCache[:,0].A)[0]# 在有效的j中寻找最大步长的jif len(validEcacheList) > 1:for j in validEcacheList:if j == i:continueEj = calcEi(oS,j)deltaE = abs(Ei-Ej)if deltaE > maxdeltaE:maxJ = jmaxdeltaE = deltaEbest_Ej = Ejreturn maxJ,best_Ej# 若不存在有效的j,则随机选取一个作为jelse:j = randomJ(i,oS.m)Ej = calcEi(oS,j)return j,Ejdef randomJ(i,m):"""随机选取j"""j = iwhile j == i:j = np.random.randint(0,m+1)return jdef calcEi(oS,i):"""计算Ei,根据最大步长来选择最优的j"""ui = float(np.multiply(oS.alpha,oS.y).T*(oS.x*oS.x[i,:].T)+oS.b)Ei = ui - float(oS.y[i,0])return Eidef calcW(oS):   """计算参数w"""w = oS.x.T*np.multiply(oS.alpha,oS.y)return wdef draw(oS,w):"""拟合结果可视化:注意这里只适用于两特征的二维情况"""x1 = []y1 = []x2 = []y2 = []for i in range(oS.m):if oS.y[i,0] == -1:x1.append(oS.x[i,0])y1.append(oS.x[i,1])else:x2.append(oS.x[i,0])y2.append(oS.x[i,1])fig = plt.figure()ax = fig.add_subplot(111)ax.scatter(x1,y1,marker='*')ax.scatter(x2,y2)x = np.arange(3,22,0.5)y = -(w[0,0]*x+oS.b[0,0])/w[1,0]ax.plot(x,y)plt.show()print(w)print(oS.b)def parameterAnalyze(k,b,c):"""参数分析:只适用于两特征二维样本"""fig = plt.figure()ax1 = fig.add_subplot(211)ax1.plot(c,k)ax2 = fig.add_subplot(212)ax2.plot(c,b)plt.tight_layout()plt.show()def predict(oS,w,outerPath):"""训练集外数据分类"""data = pd.read_excel(outerPath)# 转换为矩阵形式dataMat = np.mat(data)result = dataMat*w + oS.b[0,0]# 计算结果大于0则分类为1,小于0则分类为-1类result[result>0] = 1result[result<0] = -1# 数据保存data['classLabel'] = resultdata.to_excel(outerPath,index=False)print("分类完成!")

添加核函数代码

机器学习笔记——支持向量机SMO算法完整版代码(核函数)

机器学习笔记——支持向量机SMO算法完整版代码分析相关推荐

  1. 700 页的机器学习笔记火了!完整版开放下载

    点上方蓝字计算机视觉联盟获取更多干货 在右上方 ··· 设为星标 ★,与你不见不散 作者       梁劲(Jim Liang),来自SAP(全球第一大商业软件公司). 书籍特点       条理清晰 ...

  2. 700页的机器学习笔记火了!完整版开放下载

    作者:梁劲(Jim Liang),来自SAP(全球第一大商业软件公司). 书籍特点:条理清晰,含图像化表示更加易懂,对公式有详细的注解等. 内容概要:主要分为基本概念.常用算法和其他三部分. 正是对机 ...

  3. 机器学习之支持向量机SVM(完整版)

    目录 1   支持向量机简介 2   线性可分支持向量机 2.1   什么是线性可分 2.2   什么是几何间隔

  4. C4.5决策树生成算法完整版(Python),连续属性的离散化, 缺失样本的添加权重处理, 算法缺陷的修正, 代码等

    C4.5决策树生成算法完整版(Python) 转载请注明出处:©️ Sylvan Ding ID3算法实验 决策树从一组无次序.无规则的事例中推理出决策树表示的分类规则,采用自顶向下的递归方式,在决策 ...

  5. html-css练习题(天天生鲜静态网页制作)文末有完整版代码地址链接

    一.前言: 前端学习经典练手网页,重新整理网页版代码,如果你是初学者,请试着做一下这个网页 素材:文末完整版代码中......     二.效果图: 三.主要需求: 1.login最外侧盒子设定高29 ...

  6. 机器学习笔记之EM算法(二)EM算法公式推导过程

    机器学习笔记之EM算法--EM算法公式推导过程 引言 回顾:EM算法公式 推导过程 引言 上一节介绍了隐变量和EM算法,以及 以EM算法公式为条件,证明了随着EM算法迭代步骤的增加,每次迭代得到新的模 ...

  7. 机器学习笔记之EM算法(一)隐变量与EM算法公式的收敛性

    机器学习笔记之EM算法--隐变量与EM算法公式的收敛性 引言 隐变量 示例1 示例2 EM算法 包含隐变量的混合概率模型 EM算法的表达形式 EM算法的收敛性 EM算法的收敛性证明的条件与目标 EM算 ...

  8. python实战一个完整的项目-Python项目开发实战(第2版)高清晰PDF完整版+代码

    会写代码≠能做好项目! 1.建立有序生产环境 2.迅速融入开发团队 3.高效处理项目问题 网罗Python项目开发中的流程,让你的编程事半功倍 Python项目与封装/团队开发环境/问题驱动开发/源码 ...

  9. 动图图解C语言选择排序算法,含代码分析

    C语言文章更新目录 C语言学习资源汇总,史上最全面总结,没有之一 C/C++学习资源(百度云盘链接) 计算机二级资料(过级专用) C语言学习路线(从入门到实战) 编写C语言程序的7个步骤和编程机制 C ...

最新文章

  1. 神经网络感知器算法调整原理是什么
  2. 我不懂,数学家为啥老跟驴过不去?
  3. 设置 Confluence 6 日志
  4. C语言eigen存为txt文件,如何为使用Eigen(C线性代数的模板库)的C项目编写一个makefile?...
  5. 好用的浏览器_“遇见”一个好用的浏览器,功能非常强大到无法想象
  6. SpringBoot 模版渲染
  7. 想用最新追剧的影视图片作为手机壁纸桌面就到高图网
  8. el-jy-ii计算机组成原理实验报告,EL-JY-II型计算机组成原理实验系统
  9. GBase数据库系统操作
  10. Matlab 三角函数输入
  11. CentOS7恢复rm -rf 误删的xfs系统
  12. 千年服务器GM命令及使用详解
  13. 对抗雾霾的健康饮食注意
  14. 帮蔡徐坤刷出一亿转发,APP开发者一审获刑
  15. ac6005直连ap 如何配置_邀请实测搭档 完成AC+瘦AP经典组网_华为 AC6005_网络设备-中关村在线...
  16. java安装的时候无效参数,java.sql.SQLException: 调用中无效的参数
  17. P1024 [NOIP2001 提高组] 一元三次方程求解 /1238:一元三次方程求解
  18. 图像饱和度(Saturation)是什么?(颜色的鲜艳程度)
  19. 基于快照实现远程数据只读复制
  20. 【软考备战·希赛网每日一练】2023年4月13日

热门文章

  1. 【bzoj1502】 NOI2005—月下柠檬树
  2. Apple M1芯片版Mac系统重装教程
  3. 3036: 绿豆蛙的归宿
  4. excel数据透视表_Excel数据透视表排序问题
  5. miniblink获取html文档,五、【miniblink】直接注册js可以调用的原生函数
  6. Eclipse 断点不生效原因之一 Tigger Point 断点右下角出现类似箭头
  7. 2021信创“大比武”正式启动!金山办公助力信创人才培养和生态成熟
  8. ADF——增广迪基—福勒检验 和 自相关
  9. 从专业角度说说修仙游戏应该怎么做
  10. ccna学习指南中文第5版(PDF版)