[参考] 机器学习实战(Machine Learning in Action)
支持向量机(Support Vector Machine)是最好的现成的分类器,“现成”指的是分类器不加修改即可直接使用。基本形式的SVM分类器就可得到低错误率的结果。SVM有很多实现,文中采用最流行的一种实现,即序列最小优化(Sequential Minimal Optimization,一种求解支持向量机二次规划的算法)算法。还会介绍如何使用一种称为核函数(kernel)的方式将SVM扩展到更多的数据集上。
优点:泛化错误率低,计算开销不大,结果易理解。
这些优点是其十分流行,有人认为他是监督学习中最好的定式算法。与下章中AdaBoost是最好的监督学习的方法相对应。
缺点:对参数调节和核函数的选择敏感,原始分类器不加修改仅适用于二类问题。
使用数据类型:数值型和标称型数据。
希望找到离分隔超平面最近的点,确保它们离分隔面的距离尽可能远。这里点到分隔面的距离被称为间隔(margin)。支持向量(support vector)就是离分隔超平面最近的那些点。
分隔超平面的形式可以写成 wT+b ,要计算点A到分隔超平面的距离,就必须给出点到分隔面的法线或者垂线的长度,该值为 |wTA+b|/∥w∥ 。常数 b 类似于Logistic回归中的截距 w0 。向量 w 和常数 b 一起描述所给数据的分割线或超平面。
其他概念:线性可分(linearly separable),分隔超平面(separating hyperplane),间隔(margin)
SVM使用类似海维赛德阶跃函数(即单位阶跃函数)的函数对 wT+b 作用得到 f(wT+b) ,其中当 u<0 时 f(u)=−1 ,反之则输出+1。不像Logistic回归,采用0和1,是因为-1和+1仅仅相差一个符号,方便数学上的处理。可以通过一个统一公式来表示间隔或者数据点到分隔超平面的距离,同时不必担心数据到底是属于-1还是+1类。
间隔通过 label∗(wTx+b) 来表示计算,目标就是找出分类器定义中的 w 和 b ,需要找到具有最小间隔的点,这些点就是支持向量。然后,对间隔进行最大化。可以写作:
上面是对乘积进行优化的事情,需要固定其中一个因子而最大化其他因子。若令所有支持向量的 label∗(wTx+b) 都为1,那么可以求 ∥w∥−1 的最大值来得到最终解。但是并非所有数据点的 label∗(wTx+b) 都等1,只有那些离分隔超平面最近的点得到的值才为1。而离超平面越远的数据点,其 label∗(wTx+b) 的值也就越大。
上述优化问题给定了一些约束条件然后求最优值,该问题是一个带约束条件的优化问题。这里的优化条件是 label∗(wTx+b)≥1.0 。此类问题的一个著名的求解方法是拉格朗日乘子法。通过引入拉格朗日乘子,可基于约束条件表达原来的问题。由于这里的约束条件是基于数据点的,因此就可以将超平面写成数据点的形式。优化目标函数最后可以写成:
其中尖括号表示 xi,xj 两个向量的内积,整个目标函数的约束条件为:
因为并非所有数据都100%线性可分,所以需要引入松弛变量(slack variable),来允许有些数据点可处于分隔面的错误一侧。这样优化目标可保持不变,但此时约束条件则变为:
常数 C 用于控制“最大化间隔”和“保证大部分点的间隔小于1.0”这两个目标的权重。在优化算法的实现代码中, C 是一个参数,可通过调节该参数得到不同的结果。一旦求出所有的alpha,那么分隔超平面可通过这些alpha来表达。SVM的主要工作就是求解这些alpha。
其中(1)是最小化目标函数,(2)是优化过程中必须遵循的约束条件。以前,采用二次规划求解工具(quadratic solver)求解上述最优化问题。
SMO(Sequential Minimal Optimization)是一个强大的算法,表示序列最小优化。SMO将大优化问题分解成多个小优化问题来求解。此算法目标是求出一系列alpha和b,一旦求出这些alpha,就容易计算出权重向量 w 。
SMO算法的工作原理:每次循环中选择两个alpha进行优化处理。一旦找到一对“合适”的alpha,那么就增大其中一个同时减小另一个。“合适”指两个alpha必须要符合一定条件,条件之一就是这两个alpha必须要在间隔边界之外,而其第二个条件则是这两个alpha还没有进行过区间化处理或者不在边界上。
利用核函数将数据映射到高维空间
特征空间转换,称为从一个特征空间到另一个特征空间的映射。一般,这种空间映射会将低维特征空间映射到高维空间。这种映射可通过核函数(kernel)来实现。可以将核函数想象成一个包装器(wrapper)或者接口(interface)。经过空间转换后,可在高维空间中解决线性问题,这等价于在低维空间中解决非线性问题。
SVM优化中,所有运算都可以写成内积(inner product,也成为点积)的形式。向量的内积指的是两个向量相乘,之后得到单个标量或者数值。可将内积运算替换成核函数,而不必做简化处理。将内积替换成核函数的方式成为核技巧(kernel trick)或者核“变电”(kernel substation)
径向基核函数(radial basis function),是一个采用向量作为自变量的函数,能够基于向量距离运算输出一个标量。这个距离可以是从<0,0>向量或者其他向量开始计算的距离。可使用径向基核函数的高斯版本,具体公式:
其中, σ 是用户定义的用于确定到达率(reach)或者函数值跌落到0的速度参数。高斯核函数可将数据映射到一个无穷维的空间。
函数 | 功能 |
---|---|
str.strip(rm) | 删除str字符串中开头、结尾处,位于rm删除序列的字符,当rm为空时,默认删除空白符(含'\n','\r','\t',' ' ) |
str.lstrip(rm) | 删除str字符串中开头处,位于rm删除序列的字符 |
str.rstrip(rm) | 删除str字符串中结尾处,位于rm删除序列的字符 |
random.uniform(x, y) | 在[x,y]范围内,随机生成一个实数 |
np.multiply(a, b) | 对应下标元素相乘 |
np.multiply.outer(a, b) | 列表a中元素分别和b相乘,得到一个二维数组 |
list.copy() | 列表的浅复制 |
alphas[alphas>0] | 数组过滤,返回alphas中大于0的元素 |
mat.A | 将matrix数据转换成array |
mat[ind] | 获取ind列表中数字对应的mat矩阵的行 |
listdir(dirName) | from os import listdir,获取给定文件夹下的文件名列表,不含文件路径 |
# coding=utf-8
from numpy import *
def loadDataSet(fileName) :
dataMat = []; labelMat = []
fr = open(fileName)
for line in fr.readlines() :
lineArr = line.strip().split('\t')
dataMat.append([float(lineArr[0]), float(lineArr[1])])
labelMat.append(float(lineArr[2]))
return dataMat, labelMat
def selectJrand(i,m) :
j=i
while(j==i) :
j = int(random.uniform(0,m))
return j
def clipAlpha(aj, H, L) :
if aj > H :
aj = H
if L > aj :
aj = L
return aj
# dataMatIn: 数据集
# classLabels: 类别标签
# C: 常数C
# toler: 容错率
# maxIter: 退出前最的循环次数
def smoSimple(dataMatIn, classLabels, C, toler, maxIter) :
dataMatrix = mat(dataMatIn)
labelMat = mat(classLabels).transpose()
b = 0;
m,n = shape(dataMatrix)
alphas = mat(zeros((m,1)))
iter = 0
while (iter < maxIter) :
# alphaPairsChanged记录alpha是否已经进行优化
alphaPairsChanged = 0
for i in range(m) :
# fXi预测的类别
fXi = float(multiply(alphas, labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
# 预测结果与真实结果比对,计算误差Ei
Ei = fXi - float(labelMat[i])
# 如果误差很大,那么可对该数据实例所对应的alpha值进行优化,分别对正间隔和负间隔做了测试,
# 并且检查了alpha值,保证其不能等于0或者C,由于后面alpha小于0或者大于C时将被调整为0或C,
# 所以一旦该if语句中它们等于这两个值得话,那么它们就已经在“边界”上了,因而不再能够减小或增大,
# 因此也就不值得对它们进行优化
if((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)) :
# 利用辅助函数,随机选择第二个alpha值
j = selectJrand(i,m)
fXj = float(multiply(alphas, labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b
Ej = fXj - float(labelMat[j])
alphaIold = alphas[i].copy()
alphaJold = alphas[j].copy()
# L和H用于将alphas[j]调整到0-C之间。如果L==H,就不做任何改变,直接执行continue语句
if labelMat[i] != labelMat[j] :
L = max(0, alphas[j] - alphas[i])
H = min(C, C + alphas[j] - alphas[i])
else :
L = max(0, alphas[j] + alphas[i] -C)
H = min(C, alphas[j] + alphas[i])
if L==H : print 'L==H'; continue
# eta是alphas[j]的最优修改量,如果eta==0,需要退出for循环的当前迭代过程
eta = 2.0 * dataMatrix[i, :] * dataMatrix[j, :].T - dataMatrix[i, :]*dataMatrix[i, :].T \
- dataMatrix[j, :]*dataMatrix[j, :].T
if eta >= 0 : print 'eta>=0' ; continue
# 计算出一个新的alphas[j]值
alphas[j] -= labelMat[j]*(Ei - Ej)/eta
# 并使用辅助函数,以及L和H对其进行调整
alphas[j] = clipAlpha(alphas[j], H, L)
# 检查alphas[j]是否有轻微改变,如果是的话,退出for循环
if( abs(alphas[j] - alphaJold) < 0.00001) : print 'j not moving enough'; continue
# 然后alphas[i]和alphas[j]同样进行改变,虽然改变的大小一样,但是改变的方向正好相反
alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])
# 在对alphas[i]和alphas[j]进行优化后,给这两个alpha值设置一个常数项b
b1 = b - Ei - labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i, :]*dataMatrix[i, :].T\
- labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[i, :]*dataMatrix[j, :].T
b2 = b - Ej - labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i, :]*dataMatrix[j, :].T\
- labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[j, :]*dataMatrix[j, :].T
if (0 < alphas[i]) and (C > alphas[i]) : b = b1
elif (0 < alphas[j]) and (C > alphas[j]) : b = b2
else: b = (b1 + b2)/2.0
alphaPairsChanged += 1
print 'iter: %d i:%d, pairs changed %d' % (iter, i, alphaPairsChanged)
if alphaPairsChanged == 0 : iter += 1
else: iter = 0
print 'iteration number: %d' % iter
return b, alphas
# 建立一个数据结构来保存所有重要值
''' class optStruct: def __init__(self, dataMatIn, classLabels, C, toler) : self.X = dataMatIn self.labelMat = classLabels self.C = C 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))) '''
# 使用Kernel函数的class optStruct
class optStruct :
def __init__(self, dataMatIn, classLabels, C, toler, kTup) :
self.X = dataMatIn
self.labelMat = classLabels
self.C = C
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)
''' # 对于给定的alpha值,calcEk()能够计算E值并返回 def calcEk(oS, k) : fXk = float(multiply(oS.alphas, oS.labelMat).T*(oS.X*oS.X[k,:].T)) + oS.b Ek = fXk - float(oS.labelMat[k]) return Ek '''
# 使用Kernel函数的calcEk
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
# 内循环中的启发式方法,用于选择第二个alpha或者内循环的alpha值
# 目标:选择合适的第二个alpha值以保证在每次优化中采用最大步长
# 该函数的误差值与第一个alpha值Ei和下标i有关
def selectJ(i, oS, Ei) :
maxK = -1; maxDeltaE = 0; Ej = 0
# 将输入值Ei在缓存中设置成为有效的
oS.eCache[i] = [1, Ei]
# nonzero()返回一个列表,列表包含以输入列表为目录的列表值,这里的值并非零
# 返回的非零E值对应的alpha值,而不是E值本身
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)
# 选择具有最大步长的j
if deltaE > maxDeltaE :
maxK = k; maxDeltaE = deltaE; Ej = Ek
return maxK, Ej
else :# 第一次循环,随机选择一个alpha值
j = selectJrand(i, oS.m)
Ej = calcEk(oS, j)
return j, Ej
# 计算误差值并存入缓存当中
def updateEk(oS, k) :
Ek = calcEk(oS, k)
oS.eCache[k] = [1, Ek]
# 完整的Platt SMO算法中的优化例程
''' def innerL(i, oS) : Ei = calcEk(oS, i) 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.X[i,:]*oS.X[j,:].T - oS.X[i,:]*oS.X[i,:].T - oS.X[j,:]*oS.X[j,:].T 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) 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.X[i,:]*oS.X[i,:].T -\ oS.labelMat[j]*(oS.alphas[j] - alphaJold)*oS.X[i,:]*oS.X[j,:].T b2 = 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 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 '''
# 使用Kernel函数的innerL
def innerL(i, oS) :
Ei = calcEk(oS, i)
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.X[i,:]*oS.X[j,:].T - oS.X[i,:]*oS.X[i,:].T - oS.X[j,:]*oS.X[j,:].T
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)
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.X[i,:]*oS.X[i,:].T -\ oS.labelMat[j]*(oS.alphas[j] - alphaJold)*oS.X[i,:]*oS.X[j,:].T b2 = 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 '''
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
# 完整版Platt 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
# 退出循环条件:1、迭代次数超过指定最大值;2、遍历整个集合都未对任意alpha对进行修改。
while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)) :
alphaPairsChanged = 0
if entireSet :
# 在数据集上遍历任意可能的alpha,使用innerL()来选择第二个alpha,并在可能时对其进行优化
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]
# 遍历所有非边界alpha值,也就是不在边界0或C上的值
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
# 利用alpha值,进行分类
def calcWs(alphas, dataArr, classLabels) :
X = mat(dataArr)
labelMat = mat(classLabels).transpose()
m,n = shape(X)
w = zeros((n,1))
for i in range(m) :
w += multiply(alphas[i]*labelMat[i], X[i,:].T)
return w
# 核转换函数
# kTup: 元组,核函数的信息,元组的一个参数是描述所用核函数类型的一个字符串,另外2个参数则都是核函数可能需要的可选参数
# 一个调用实例:kernelTrans(sVs, dataMat[i,:], ('rbf', k1))
# 其中k1是径向基核函数高斯版本中的sigma
def kernelTrans(X, A, kTup) :
m,n = shape(X)
# 构建一个列向量
K = mat(zeros((m, 1)))
# 检查元组以确定核函数的类型
if kTup[0] == 'lin' : K = X * A.T
# 在径向基核函数的情况下
elif kTup[0] == 'rbf' :
# for循环中对于矩阵的每个元素计算高斯函数的值
for j in range(m) :
deltaRow = X[j, :] - A
K[j] = deltaRow*deltaRow.T
# 将计算过程应用到整个向量,元素间的除法
K = exp(K/(-1*kTup[1]**2))
else : raise NameError('Houston We Have a Problem -- That Kernel is not recognized')
return K
# 利用核函数进行分类的径向基测试函数
# k1: 高斯径向基函数中一个用户定义变量
# 此函数从文件中读取数据集,然后在该数据集上运行Platt SMO算法,其中核函数的类型是'rbf'
def testRbf(k1 = 1.3) :
dataArr, labelArr = loadDataSet('C:\python27\ml\\testSetRBF.txt')
b, alphas = smoP(dataArr, labelArr, 200, 0.0001, 10000, ('rbf', k1))
dataMat = mat(dataArr); labelMat = mat(labelArr).transpose()
svInd = nonzero(alphas.A>0)[0]
sVs = dataMat[svInd]
labelSV = labelMat[svInd]
print "there are %d Support Vectors" % shape(sVs)[0]
m,n = shape(dataMat)
errorCount = 0
for i in range(m) :
# for循环中前两行,给出了如何利用核函数进行分类
kernelEval = kernelTrans(sVs, dataMat[i,:], ('rbf', k1))
predict = kernelEval.T * multiply(labelSV, alphas[svInd]) + b
if sign(predict) != sign(labelArr[i]) : errorCount += 1
print "the training error rate is: %f" % (float(errorCount)/m)
dataArr, labelArr = loadDataSet('C:\python27\ml\\testSetRBF2.txt')
errorCount = 0
dataMat = mat(dataArr); labelMat = mat(labelArr).transpose()
m,n = shape(dataMat)
for i in range(m) :
kernelEval = kernelTrans(sVs, dataMat[i,:], ('rbf', k1))
predict = kernelEval.T * multiply(labelSV, alphas[svInd]) + b
if sign(predict) != sign(labelArr[i]) : errorCount += 1
print "the test error rate is: %f" % (float(errorCount)/m)
# 基于SVM的手写数字识别
def loadImages(dirName) :
from os import listdir
hwLabels = []
trainingFileList = listdir(dirName)
m = len(trainingFileList)
trainingMat = zeros((m, 1024))
for i in range(m) :
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
if classNumStr == 9 : hwLabels.append(-1)
else : hwLabels.append(1)
trainingMat[i, :] = img2vector('%s/%s' % (dirName, fileNameStr))
return trainingMat, hwLabels
def testDigits(kTup = ('rbf', 10)) :
dataArr, labelArr = loadImages('c:\python27\ml\\trainingDigits')
b, alphas = smoP(dataArr, labelArr, 200, 0.0001, 10000, kTup)
dataMat = mat(dataArr)
labelMat = mat(labelArr).transpose()
svInd = nonzero(alphas.A > 0)[0]
sVs = dataMat[svInd]
labelSV = labelMat[svInd]
print "there are %d Support Vectors" % shape(sVs)[0]
m,n = shape(dataMat)
errorCount = 0
for i in range(m) :
kernelEval = kernelTrans(sVs, dataMat[i, :], kTup)
predict = kernelEval.T * multiply(labelSV, alphas[svInd]) + b
if sign(predict) != sign(labelArr[i]) : errorCount += 1
print "the training error rate is: %f" % (float(errorCount)/m)
dataArr, labelArr = loadImages('c:\python27\ml\\testDigits')
errorCount = 0
dataMat = mat(dataArr)
labelArr = mat(labelArr).transpose()
m,n = shape(dataMat)
for i in range(m) :
kernelEval = kernelTrans(sVs, dataMat[i, :], kTup)
predict = kernelEval.T * multiply(labelSV, alphas[svInd]) + b
if sign(predict) != sign(labelArr[i]) : errorCount += 1
print "the test error rate is: %f" % (float(errorCount)/m)
在命令行中执行:
# 加载数据
>>> import ml.svmML as svmML
>>> dataArr, labelArr = svmML.loadDataSet('c:\python27\ml\\testSet.txt')
>>> labelArr
[-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0,
-1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1
.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0
, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0
, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0,
1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0,
-1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]
# 执行简化版SMO算法
>>> b, alphas = svmML.smoSimple(dataArr, labelArr, 0.6, 0.001, 40)
......
iteration number: 23
j not moving enough
j not moving enough
j not moving enough
iteration number: 24
iter: 24 i:29, pairs changed 1
j not moving enough
j not moving enough
iteration number: 0
j not moving enough
j not moving enough
j not moving enough
iteration number: 1
......
>>> b # 因为SMO算法的随机性,每次运行的结果不一样
matrix([[-3.84267434]])
>>> alphas[alphas>0]
matrix([[ 0.08891067, 0.27233877, 0.03016793, 0.33108151]])
>>> import numpy as np
>>> np.shape(alphas[alphas>0]) # 得到支持向量的个数
(1, 4)
>>> for i in range(100): # 得到哪些点是支持向量
... if alphas[i]>0.0: print dataArr[i], labelArr[i]
...
[4.658191, 3.507396] -1.0
[3.457096, -0.082216] -1.0
[5.286862, -2.358286] 1.0
[6.080573, 0.418886] 1.0
# 完整版Platt SMO
>>> reload(svmML)
<module 'ml.svmML' from 'C:\Python27\ml\svmML.py'>
>>> dataArr, labelArr = svmML.loadDataSet('c:\python27\ml\\testSet.txt')
>>> b, alphas = svmML.smoP(dataArr, labelArr, 0.6, 0.001, 40)
......
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
j not moving enough
j not moving enough
j not moving enough
j not moving enough
j not moving enough
j not moving enough
L==H
j not moving enough
j not moving enough
L==H
j not moving enough
j not moving enough
L==H
L==H
j not moving enough
fullSet, iter: 2 i: 99, pairs changed 0
iteration number: 3
>>> alphas[alphas>0]
matrix([[ 0.06961952, 0.0169055 , 0.0169055 , 0.0272699 , 0.04522972, 0.0272699 , 0.0243898 , 0.06140181, 0.06140181]])
# 进行数据点分类
>>> ws = svmML.calcWs(alphas, dataArr, labelArr)
>>> ws
array([[ 0.65307162], [-0.17196128]])
>>> import numpy as np
>>> dataMat = np.mat(dataArr)
>>> dataMat[0]
matrix([[ 3.542485, 1.977398]])
>>> b
matrix([[-2.89901748]])
>>> dataMat[0]*np.mat(ws) + b
matrix([[-0.92555695]])
>>> labelArr[0]
-1.0
>>> dataMat[2]*np.mat(ws) + b
matrix([[ 2.30436336]])
>>> labelArr[2]
1.0
>>> dataMat[1]*np.mat(ws) + b
matrix([[-1.36706674]])
>>> labelArr[1]
-1.0
# 在测试中使用核函数
>>> reload(svmML)
<module 'ml.svmML' from 'C:\Python27\ml\svmML.pyc'>
>>> svmML.testRbf()
......
j not moving enough
L==H
L==H
L==H
L==H
fullSet, iter: 6 i: 99, pairs changed 0
iteration number: 7
there are 27 Support Vectors
the training error rate is: 0.030000
the test error rate is: 0.040000
# 基于SVM的手写数字识别
>>> reload(svmML)
<module 'ml.svmML' from 'C:\Python27\ml\svmML.py'>
>>> svmML.testDigits(('rbf', 20))
......
j not moving enough
j not moving enough
j not moving enough
fullSet, iter: 7 i: 1933, pairs changed 0
iteration number: 8
there are 157 Support Vectors
the training error rate is: 0.000000
the test error rate is: 0.012685