机器学习--支持向量机实战(三)完整版SMO算法实现
完整版和简化版的smo不通之处在于alpha的选择方式上,alpha的更改和代数运算和简化版本,完整版的alpha的选择方式采用了启发式进行选择,前面文章已经详解了,这里再简单的叙述一下:
Platt SMO算法是通过一个外循环来选择第一个alpha值的,并且其选择过程会在两种方式之间进行交替即:一种方式是在所有数据集上进行单遍扫描,另一种方式则是在非边界alpha中实现单遍扫描,而所谓非边界alpha指的就是那些不等于边界0或者C的alpha值。对整个数据集扫描相当容易,而实现非边界alpha值的扫描时,首先需要建立这些alpha值的列表,然后在对这个表进行遍历,同时该步骤跳过那些已知不会改变的alpha值。
在选择第一个alpha值后,算法会通过一个内循环来选择第二个alpha值,在优化过程中,会通过最大步长的方式来获的第二个alpha值,在简易的smo中,我会在选择j后计算误差率E,但这里会建立一个全局的缓存用于保存误差值,并从中选择使得步长或者说Ei-Ej最大的alpha值,为什么是这样呢?看下式:
从上式可以清楚的看到的步长取决于和,而 又无法人为改变,所以能改变的只有,使其差值越大,说明前进步长越大,下面给出代码,代码均已详细注释:
# 完整版smo算法实现
# 下面的函数就是参数方面的存储定义
# 简化版的smo都是在函数体内定义,这里单独拿出来,不同的是处理E值,增加了ecache项
class optStruct:def __init__(self, dataMatIn, classLabels, C, toler):# dataMatIn、classLabels为输入数据和标签,C为松弛因子,toler为容忍度,简单来说就是alpha多大才认为不在变化了# kTup为核函数的信息self.X = dataMatInself.labelMat = classLabelsself.C = Cself.tol = tolerself.m = np.shape(dataMatIn)[0]self.alphas = np.mat(np.zeros((self.m, 1))) # 给alpha占位self.b = 0self.eCache = np.mat(np.zeros((self.m, 2)))# 首先这是一个矩阵,两列,第一列是表面E是否有效的标志位,第二列给出的是实际的E值# self.K = np.mat(np.zeros((self.m, self.m))) # 核函数方面的参数#for i in range(self.m):# self.K[:, i] = kernelTrans(self.X, self.X[i, :], kTup)# 其实就是f(x) = ∑alpha_i*y_i*<x_i,x> + b,前面理论也详细讲了这一点,就是预测x的类别,只是单独拿出来了
# 然后计算 差值 Ekdef calcEk(oS, k):fXk = float(np.multiply(oS.alphas,oS.labelMat).T*(oS.X*oS.X[k,:].T)) + oS.bEk = fXk - float(oS.labelMat[k])return Ek# 用来选择第二个alpha参数,或者说内循环的alpha值,其目标是选择合适的alpha值使其步长最大
def selectJ(i, oS, Ei): # this is the second choice -heurstic, and calcs EjmaxK = -1 # 定义使的计算E的差值最大maxDeltaE = 0 # E值的最大变化量Ej = 0 # 使的差值最大的那个EjoS.eCache[i] = [1, Ei] # 这是把计算出来的差值记录并标为有效validEcacheList = np.nonzero(oS.eCache[:, 0].A)[0] # 刚开始有点不理解.A是什么意思,查了一下,解释一下# 情况下面的示例,其中.A的意思是把矩阵转换成数组,因为oS.eCache[:, 0].A,取的是一列的数据E值有效位,转换后还是一列# 而nonzero(oS.eCache[:, 0].A)[0]返回的是非零的目录列表值并取出赋给validEcacheList,共下面处理if (len(validEcacheList)) > 1:for k in validEcacheList: # 索引validEcacheList的标号,求最大差值if k == i: continue # 如果相等,说明选取两个alpha一样,直接跳出本次循环,进行下一次循环Ek = calcEk(oS, k) # 计算EkdeltaE = abs(Ei - Ek) # 求两个Ek的差值,选择最大的步长if (deltaE > maxDeltaE): # 比较,选择最大的差值maxK = k;maxDeltaE = deltaE;Ej = Ekreturn maxK, Ejelse:# 这个是为第一次准备的,不满足上面的条件即(len(validEcacheList)) > 1,因为第一次的E都为0,# 所以长度都为0,不满足条件,来到这里,进行随机抽取alpha 的 ijj = selectJrand(i, oS.m)Ej = calcEk(oS, j)return j, Ej
'''
In [12]: a = [[0,0,3],[0,5,0],[7,0,9]]
In [13]: b = np.mat(a)
In [14]: c = b.A
In [15]: print('type(a): ',type(a),'type(b): ',type(b),'type(c): ',type(c))
type(a): <class 'list'> type(b): <class 'numpy.matrixlib.defmatrix.matrix'> type(c): <class 'numpy.ndarray'>
In [16]: b
Out[16]:
matrix([[0, 0, 3],[0, 5, 0],[7, 0, 9]])
In [17]: c
Out[17]:
array([[0, 0, 3],[0, 5, 0],[7, 0, 9]])
In [18]: v =np.nonzero(c)[0]
In [19]: v
Out[19]: array([0, 1, 2, 2], dtype=int64)
'''# 每一次循环计算的E都会缓存,即用于更新
def updateEk(oS, k): # after any alpha has changed update the new value in the cacheEk = calcEk(oS, k)oS.eCache[k] = [1, Ek]# 完整版的SMO优化例程,和前面的简化版类似
def innerL(i, oS):Ei = calcEk(oS, i) # 计算第一个alpha的差值# 寻找非边界的alpha,为寻找第二个alpha做准备if ((oS.labelMat[i]*Ei < -oS.tol) and (oS.alphas[i] < oS.C)) or ((oS.labelMat[i]*Ei > oS.tol) and (oS.alphas[i] > 0)):j,Ej = selectJ(i, oS, Ei) # 挑选第二个alpha,并得到Ej# 保存旧的alpha,为后面比较使用alphaIold = oS.alphas[i].copy()alphaJold = oS.alphas[j].copy()# 加入约束条件if (oS.labelMat[i] != oS.labelMat[j]):L = max(0, oS.alphas[j] - oS.alphas[i])H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])else:L = max(0, oS.alphas[j] + oS.alphas[i] - oS.C)H = min(oS.C, oS.alphas[j] + oS.alphas[i])if L==H:print("L==H")return 0# eta, 大家还记得上篇的参数更新里的: K_11 + K_22 - 2K_12吗,其实就是这个eta,有点不同,# 是因为假设条件不一样,但是原理相同eta = 2.0 * oS.X[i,:]*oS.X[j,:].T - oS.X[i,:]*oS.X[i,:].T - oS.X[j,:]*oS.X[j,:].Tif eta >= 0:print("eta>=0")return 0# 更新alphaoS.alphas[j] -= oS.labelMat[j]*(Ei - Ej)/etaoS.alphas[j] = clipAlpha(oS.alphas[j],H,L)# 把选出的第二个alpha对应的E添加到EcacheupdateEk(oS, j) #added this for the Ecache# 比较第二alpha的值变化是否在容忍度以内,如果在这属于不在变化的alpha了,则返回,反之继续执行if (abs(oS.alphas[j] - alphaJold) < 0.00001):print("j not moving enough")return 0# 更新另一个alpha,即第一个alphaoS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j])#update i by the same amount as j# 同样添加到缓存区updateEk(oS, i) #added this for the Ecache #the update is in the oppostie direction# 计算b值b1 = oS.b - Ei- oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.X[i,:]*oS.X[i,:].T - oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.X[i,:]*oS.X[j,:].Tb2 = oS.b - Ej- oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.X[i,:]*oS.X[j,:].T - oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.X[j,:]*oS.X[j,:].T# 根据条件更新bif (0 < oS.alphas[i]) and (oS.C > oS.alphas[i]): oS.b = b1elif (0 < oS.alphas[j]) and (oS.C > oS.alphas[j]): oS.b = b2else: oS.b = (b1 + b2)/2.0return 1else:return 0# 外循环即第一个alpha的选择
def smoPK(dataMatIn, classLabels, C, toler, maxIter): #full Platt SMO# 把数据传入数据结构中oS = optStruct(np.mat(dataMatIn), np.mat(classLabels).transpose(), C, toler)iter = 0entireSet = TruealphaPairsChanged = 0# 执行while的条件是1.迭代次数达到最大,2.alpha值存在变化,或者整个开始启动即entireSet = Truewhile (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):alphaPairsChanged = 0if entireSet: #go over allfor i in range(oS.m): # 遍历所有的alpha值alphaPairsChanged += innerL(i,oS) # 从上面我们知道进入innerL是第一个alpha选取就为i# 第二个跟剧最大的E差值进行选择也是在innerl完成选择# 如果寻找满足条件则返回1,反之返回0,所以alphaPairsChanged是记录变化的alpha的变化次数print("fullSet, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged))iter += 1 # 整个遍历完以后alpha更新结束为一次迭代else:# 遍历非边界的alpha值,也就是不在0或者C上的值nonBoundIs = np.nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]for i in nonBoundIs:alphaPairsChanged += innerL(i,oS)print("non-bound, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged))iter += 1if entireSet:entireSet = False # 开始时先遍历整个alpha,然后迭代完成一次以后,开始转向非边界进行遍历elif (alphaPairsChanged == 0):entireSet = True # 如果遍历完所有的非边界都没有可更新的alpha,则继续转为全局遍历print("iteration number: %d" % iter)return oS.b,oS.alphas# 计算w,大家还记的求w的式子吗?
# W = ∑alpha_i*y_i*x_i
def calcWs(alphas, dataArr, classLabels):X = np.mat(dataArr);labelMat = np.mat(classLabels).transpose()m, n = np.shape(X)w = np.zeros((n, 1))for i in range(m):w += np.multiply(alphas[i] * labelMat[i], X[i, :].T)# 虽然该过程遍历所有的样本点,但是通过前面的原理详解可知,大部分的alpha值为0,只有支持向量的值不为零# 所以这个计算还是很简单的return w
下面给出测试代码:
b, alphas = SMO_simplify.smoPK(dataarr, labelarr, 0.6, 0.001, 40)print('b: ',b)
print('alphas: ',alphas[alphas>0])ws = SMO_simplify.calcWs(alphas, dataarr, labelarr)
print('ws: ',ws)# 对数据进行预测
datmat = np.mat(dataarr)
print('datmat[0]*np.mat(ws) + b = ', datmat[0]*np.mat(ws) + b)
print('labelarr[0]',labelarr[0])
err=0
# 计算全体数据的错误率
for i in range(len(dataarr)):y = datmat[i]*np.mat(ws) + bif y > 0:y = 1else:y = -1if y != labelarr[i]:err += 1;print('err: ', err)
print('错误率为: ', err/len(dataarr))
测试 结果:
non-bound, iter: 1 i:46, pairs changed 0
non-bound, iter: 1 i:55, pairs changed 0
non-bound, iter: 1 i:94, pairs changed 0
iteration number: 2
j not moving enough
fullSet, iter: 2 i:0, pairs changed 0
fullSet, iter: 2 i:1, pairs changed 0
fullSet, iter: 2 i:2, pairs changed 0
j not moving enoughb: [[-2.89901748]]
alphas: [[0.06961952 0.0169055 0.0169055 0.0272699 0.04522972 0.02726990.0243898 0.06140181 0.06140181]]
ws: [[ 0.65307162][-0.17196128]]
datmat[0]*np.mat(ws) + b = [[-0.92555695]]
labelarr[0] -1.0
err: 0
错误率为: 0.0
简单测试100%正确,这是线性分类,如果是非线性呢?下一节将详细讨论核函数实战代码
机器学习--支持向量机实战(三)完整版SMO算法实现相关推荐
- svd降维 python案例_菜菜的机器学习sklearn实战-----sklearn中的降维算法PCA和SVD
菜菜的机器学习sklearn实战-----sklearn中的降维算法PCA和SVD 概述 从什么叫维度说开来 简单讲,shape中返回了几个数字就是几维. 一张表最多就是一维 当一个数组中存在2张3行 ...
- seo实战密码完整版_SEO的完整形式是什么?
seo实战密码完整版 SEO:搜索引擎优化 (SEO: Search Engine Optimization) SEO is an abbreviation of Search Engine Opti ...
- 机器学习--支持向量机实战(二)简易SMO算法实现
该简易算法其实完成的任务很简单,就是违反KKT条件的alpha进行符合KKT条件,然后通过不在边界的点进行迭代,然后使其alpha稳定下来下面的代码基本每一句都进行了详细的解释,具体看代码,但是建议没 ...
- QT教程,QT从入门到实战教程完整版
Qt是一个跨平台开发框架,可以使用C++和Qml开发,同时它又不仅仅只是开发框架,它也是一种技术策略,可以让你快速.高效地设计.开发.部署和维护软件,同时在所有设备上提供无缝的用户体验.因而,对于C/ ...
- 机器学习(第三章)—— 经典算法
目录 一.支持向量机 二.逻辑回归 三.决策树 注: 关于SVM的理论知识可移步(四)支持向量机(SVM) 关于逻辑回归的知识可移步(二)逻辑回归 关于决策树的知识可移步(五)决策树 逻辑回归相比线性 ...
- 机器学习--支持向量机(三)对偶问题、松弛变量详解
上篇讲到了对偶的式子即: 但是什么是对偶?从哪里能体现到对偶的特性?上面的式子为什么是对求最大值?之前不是求最小问题吗?下面给大家讲解一下原因. 先给出最初的求最小值的表达式,存在约束条件: 受限于 ...
- 支持向量机SVM(五)SMO算法
11 SMO优化算法(Sequential minimal optimization) SMO算法由Microsoft Research的John C. Platt在1998年提出,并成为最快的二次规 ...
- 算法笔记上机训练实战指南 完整版 高清带书签pdf
下载地址:网盘下载 算法笔记上机训练实战指南是<算法笔记>的配套习题集,内容按照<算法笔记>的章节顺序进行编排,其中整理归类了PAT甲级.乙级共150多道题的详细题解,大部分题 ...
- 机器学习之支持向量机SVM(完整版)
目录 1 支持向量机简介 2 线性可分支持向量机 2.1 什么是线性可分 2.2 什么是几何间隔
最新文章
- 插值算法C实现(二元全区间)
- 【Python】大神教你五分钟搞清楚Python函数的参数!
- 使用ldirectord实现后端RS健康状态监测及LVS调度功能
- boost.asio系列——io_service
- BioPython-1
- Redis 在Golang中使用遇到的坑
- 网络空间安全导论期末复习资料
- Taskctl是什么软件,有什么用?
- 曲面映射的算法理论基础
- 求素数平均值c语言,C 输入10个正整数到a数组中,求a数组中素数的平均值.
- sql md5或shal加密
- 去丹麦学计算机,哥本哈根大学计算机硕士经历
- GATT协议学习笔记
- Hashmap底层源码
- 胡凡算法笔记第二章摘录
- 别忽略国美之争的真正遗产
- ES6 Proxy和Reflect
- 华峰氨纶,昨日黄花?
- 【观点】996.ICU 会带来实质性的改变吗?
- 低市值高业绩的TCL,能否借“元宇宙”风口说新故事?
热门文章
- Q109:用PBRT渲染Blender导出的模型 (2)
- 第六章 应用层[练习题+课后习题]
- 【web组件库系列】封装自己的字体图标库
- android tv webview,Android TV开发---WebView焦点处理
- idea中build project不能用_Java语言编程第40讲——如何在一个项目中组织多个SpringBoot服务
- L2-018 多项式A除以B(模拟)
- 永城职业学院计算机专业分类,计算机专业师资队伍
- mac可以写linux的进程,macOS系统上读写Linux的ext4分区方法
- Python实现主成分分析(PCA)降维:原理及实例分析
- Struts2 文件上传 文件类型 大小过滤