机器学习实战【7】(SMO算法实现)
来源:互联网 发布:乌玛·瑟曼 知乎 编辑:程序博客网 时间:2024/05/23 18:15
本博客记录《机器学习实战》(MachineLearningInAction)的学习过程,包括算法介绍和python实现。
SMO算法
前两篇文章介绍了SVM的原理,经过一番推导,原始问题转化为:
SMO算法就是用来解决这个问题,求解出这些
二元优化
SMO算法的核心在于,它把原本n个参数的优化问题拆分为很多个小的子问题,每次只优化其中的两个参数而固定其它参数,两个参数的优化是很快的,从而使得最终算法的效率非常高。
假设在n个参数
其中:
由等式约束条件得出:
以此消去上式中的
上式求导:
带入
其中,E表示误差:
解的修剪
上式中计算出的
依此对
b的更新
因为在每次迭代的过程中需要计算当前模型的预测函数值
若
同样,若
若两者都不满足,则取
参数的启发式选择
第一个参数选择那些违反kkt条件的参数,第二个参数选择那些使得更新跨度更大的参数。
第一个参数的确定(外循环):
kkt条件:
在外循环中,遍历一次整个样本集,遇到违反kkt条件的
第二个参数的确定(内循环):
第二个参数依据更新跨度大小来选择,而由于
第二个参数的选择首先在非边界样本集上进行,若无法找到具有足够更新跨度的参数,则在整体样本集上找,若还是找不到则重新选择第一个参数。
python实现
# 定义结构用于存放参数和中间变量class optStruct: def __init__(self,dataMatIn, classLabels, C, toler, kTup): # 输入数据集 self.X = dataMatIn # 输入类标签 self.labelMat = classLabels # 参数C,用于调整松弛变量比重 self.C = C # 作为kkt条件判断时的阈值 self.tol = toler # 样本数 self.m = shape(dataMatIn)[0] # 拉格朗日乘子 self.alphas = mat(zeros((self.m,1))) self.b = 0 # 误差缓存,第一列为是否可用的标志位 self.eCache = mat(zeros((self.m,2))) # 核函数计算结果矩阵 self.K = mat(zeros((self.m,self.m))) for i in range(self.m): self.K[:,i] = kernelTrans(self.X, self.X[i,:], kTup)# 计算误差def calcEk(oS, k): fXk = float(multiply(oS.alphas,oS.labelMat).T*oS.K[:,k] + oS.b) Ek = fXk - float(oS.labelMat[k]) return Ek# 内循环,寻找最优的第二个参数def selectJ(i, oS, Ei): maxK = -1; maxDeltaE = 0; Ej = 0 oS.eCache[i] = [1,Ei] # 误差计算之后会进行缓存,这里先在缓存中遍历误差,选择最优的 validEcacheList = nonzero(oS.eCache[:,0].A)[0] if len(validEcacheList) > 1: for k in validEcacheList: if k == i: continue Ek = calcEk(oS, k) deltaE = abs(Ei - Ek) if (deltaE > maxDeltaE): maxK = k maxDeltaE = deltaE Ej = Ek return maxK, Ej else: j = selectJrand(i, oS.m) Ej = calcEk(oS, j) return j, Ej# 更新参数,返回参数是否被更新def innerL(i, oS): Ei = calcEk(oS, i) # 此处违反kkt条件的判断采用阈值的方式,在边界附近某个小范围内的值都视作在边界上 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) 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 = 2.0 * oS.K[i,j] - oS.K[i,i] - oS.K[j,j] if eta >= 0: print("eta>=0") return 0 # 更新第一个参数 oS.alphas[j] -= oS.labelMat[j]*(Ei - Ej)/eta oS.alphas[j] = clipAlpha(oS.alphas[j],H,L) updateEk(oS, j) #added this for the Ecache if abs(oS.alphas[j] - alphaJold) < 0.00001: print("j not moving enough") return 0 # 更新第二个参数 oS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j]) updateEk(oS, i) b1 = oS.b - Ei- oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.K[i,i] - oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.K[i,j] b2 = oS.b - Ej- oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.K[i,j]- oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.K[j,j] if (0 < oS.alphas[i]) and (oS.C > oS.alphas[i]): oS.b = b1 elif (0 < oS.alphas[j]) and (oS.C > oS.alphas[j]): oS.b = b2 else: oS.b = (b1 + b2)/2.0 return 1 else: return 0# smo算法,部分细节与理论不太相同,但影响不大def smoP(dataMatIn, classLabels, C, toler, maxIter,kTup = ('lin', 0)): oS = optStruct(mat(dataMatIn),mat(classLabels).transpose(),C,toler, kTup) iter = 0 # 当前正在进行的遍历是否是整个数据集 entireSet = True alphaPairsChanged = 0 # 外循环 while (iter < maxIter) and (alphaPairsChanged > 0 or entireSet): alphaPairsChanged = 0 # 遍历全体数据集 if entireSet: for i in range(oS.m): alphaPairsChanged += innerL(i,oS) print("fullSet, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)) iter += 1 # 遍历非边界数据集 else: nonBoundIs = 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 += 1 # 交替遍历全体数据集与非边界数据集 if entireSet: entireSet = False elif alphaPairsChanged == 0: entireSet = True print("iteration number: %d" % iter) return oS.b,oS.alphas
总结
smo算法一次选择两个参数进行更新,将复杂的优化问题分解为一个个很好解决的小问题进行化解。机器学习实战书中的实现代码相比paper中的理论进行了一些简化,但对整体速度影响不大,但是其中的误差缓存方式貌似有些问题,在更新了参数之后只对对应的数据误差进行了更新,但此时其它的误差应该是失效了,却没有重新计算,可能是因为其它的误差变化不大,所以不会有很大的影响吧。
- 机器学习实战【7】(SMO算法实现)
- 机器学习实战SMO算法源码解析
- 《机器学习实战》(六)—— SVM(SMO算法)
- Andrew Ng机器学习笔记+Weka相关算法实现(六)SMO/LibSVM/SVM参数
- py2.7 : 《机器学习实战》 SVM支持向量机:1.26号 6-1 SMO算法简化版
- 机器学习入门笔记:(4.3)SMO算法
- 【读书笔记】机器学习实战 6.3节 SVM-简化版SMO算法
- python机器学习之SMO算法
- 【机器学习详解】SMO算法剖析
- 【机器学习详解】SMO算法剖析
- 【机器学习详解】SMO算法剖析
- 【机器学习详解】SMO算法剖析
- 机器学习二一:SMO算法
- 【机器学习详解】SMO算法剖析
- 【机器学习算法-python实现】svm支持向量机(2)—简化版SMO算法
- 《机器学习实战》学习笔记-[6]-支持向量机SMO
- 机器学习实战(6):SVM-SMO-核函数 手写识别
- Python3:《机器学习实战》之支持向量机(2)简化版SMO
- PC制做gif动图超简单教程
- 二叉树的遍历(递归、非递归)
- Linux中的crontab详解
- dhtmlxGrid Cell.getValue()获取带&字符数据问题
- [BZOJ1003][ZJOI2006]物流运输(最短路+dp)
- 机器学习实战【7】(SMO算法实现)
- hihocoder #1584 : Bounce(规律)
- set、env、export——Linux中的环境变量命令
- Spring学习笔记(五) --- 装配Bean之导入和混合配置
- Minimum (ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛 题9)
- Oracle 小记
- Java Calendar 类的时间操作
- 2.最短路径的性质
- 局部线性嵌入(LLE)