当前位置:   article > 正文

机器学习(3)-支持向量机(SVM)含python代码_python支持向量机给定确定的c和g

python支持向量机给定确定的c和g

                                          支持向量机(SVM)

定义:

支持向量机:英文名support vector machine,一般简称SVM。通俗来讲,它是一种二类分类模型,其基本模型定义为特征空间上的间隔最大的超平面(线性分类器),其学习策略便是间隔最大化,最终可转化为一个凸二次规划问题的求解。

超平面方程式 ,其中X为多维向量,W也为多维向量,b为实数。当f(x)=0时,该点位于超平面上,根据f(x)与0的比较,确定其所属标签。

求解过程:

1,找出支持向量(已知的含有标签数据集中,每个标签下的边界向量)

2,根据支持向量,求出最优超平面(最优的标准就是支持向量距离超平面间隔最大)

本文以实际应用为基础,对一维、二维、多维的线性数据集和非线性数据集进行求解 线性数据集:能用一个超平面进行数据划分的数据集,在一维中该超平面为一个点,二维中为一条直线,三维中为一平面 非线性数据集:无法用一个超平面进行数据划分的数据集,与线性数据集对应。

一维数据

有一维数据T( 1,5,6,10,8 ,20,30,50,60,75),其中A(1,5,6,9,8)的标签为1,B( 20,30,50,60,75 )的标签为-1,如下图: 

      

支持向量:A中边界点为:1,10。且分类器必然处于A与B之间,所以10为A的支持向量。同理可得20为B的支持变量 将10,20带入计算公式 :                         

可得:w为1维向量,设为[X]

[X] .T*[10] + b =-1

[X] .T*[20] + b=1

求得:X=0.2(既w=[0.2]),b=-3 同样根据f(x)=0时为分类器上的点,可知点[15]为分类器上的点 。

二维数据

有二维数据集T,其中A集合的标签为-1,B集合标签为1,如下图:

                            

支持向量:A的备选边界点有a1,a2,a3,B中备选边界点有b1,b2(边界点求法多维时介绍),对分别做成直线a1a2,a2a3,b1b2,分别求相反标签中距离该直线最近点到该直线的距离(几何间隔,既垂线距离),既b1到直线a2a3的距离dis1,b2到a1a2的距离dis2,a2到b1b2的距离dis3,比较可得dis3>dis2>dis1.既可以的得到A的支持向量为a2,B的支持向量为b1,b2。

将a2,b1,b2带入计算公式: ,w的转置为2维向量,设为[[X],[Y]]

[[X],[Y]].T*[[9],[4]] + b = 1

[[X],[Y]] .T*[[8],[1]] + b = 1

[[X],[Y]] .T*[[6],[5]] + b = -1

计算结果:X=0.6,Y=-0.2,b=-3.6, w=[0.6,-0.2]

同样根据f(x)=0时为分类器上的点,可知0.6X-0.2Y-3.6=0为分类直线

多维数据

有多维数据集T,其中A集合的标签为-1,B集合标签为1,由于无法表达多维数据,暂用二维数据图:

                           

多维数据求解步骤如下:

1,几何间隔:样本x到超平面的距离,公式为:

2,最大间隔分离器:既为我们所求的做好的分离器,其定义为离支持变量最远的超平面,当我们取支持变量值为1时,可以得到我们所求的问题转化为:  
也可以转换公式为: 

3,对偶算法:(1)引进拉格朗日乘子,定义拉格朗日函数

2)根据拉格朗日对偶性,原始问题的对偶问题是极大极小问题,先求对w,b的极小值.将L(w,b,a)分别对w,b求偏导数并令其等于0,带入公式。将求极大转换为求极小,最后转换公式为:

3)由KKT条件成立得到以下公式,其中j为使aj*>0的下标之一.所以问题就变为求对偶问题的解a*,再求得原始问题的解w*,b*,从而得分离超平面及分类决策函数可以看出w*和b*都只依赖训练数据中ai*>0的样本点(xi,yi),这些实例点xi被称为支持向量

                                                                                    

                                                        非线性数据

核函数:

支持向量机通过某非线性变换 φ( x) ,将输入空间映射到高维特征空间。特征空间的维数可能非常高。如果支持向量机的求解只用到内积运算,而在低维输入空间又存在某个函数 K(x, x′) ,它恰好等于在高维空间中这个内积,即K( x, x′) =<φ( x) ⋅φ( x′) > 。那么支持向量机就不用计算复杂的非线性变换,而由这个函数 K(x, x′) 直接得到非线性变换的内积,使大大简化了计算。这样的函数 K(x, x′) 称为核函数。

核技巧:基本思想是通过一个非线性变换将输入空间对应于一个特征空间,使得在输入空间中的超曲面模型对应于特征空间中的超平面模型(支持向量机).在学习和预测中只定义核函数K(x,z),而不显式地定义映射函数.对于给定的核K(x,z),特征空间和映射函数的取法并不唯一.注意到在线性支持向量机的对偶问题中,目标函数和决策函数都只涉及输入实例与实例之间的内积,xi`xj可以用核函数K(xi,xj)=Ф(xi)`Ф(xj)来代替.当映射函数是非线性函数时,学习到的含有核函数的支持向量机是非线性分类模型.在实际应用中,往往依赖领域知识直接选择核函数.

常见核函数

多项式核函数:

对应的支持向量机是一个p次多项式分类器,分类决策函数为:

高斯核函数:

对应的支持向量机是高斯径向基函数(RBF)分类器 分类决策函数为:

线性核函数:

此函数对应的为线性可分数据,这实际上就是原始空间中的内积,这个核存在的主要目的是使得“映射后空间中的问题”和“映射前空间中的问题”两者在形式上统一起来了(意思是说,咱们有的时候,写代码,或写公式的时候,只要写个模板或通用表达式,然后再代入不同的核,便可以了,于此,便在形式上统一了起来,不用再分别写一个线性的,和一个非线性的)

松弛变量

通过核函数把原始数据映射到高维空间之后,能够线性分隔的概率大大增加,但是对于某些情况还是很难处理,比如数据有噪音,对于这种偏离正常位置很远的数据点,我们称之为 outlier,如下图: 

                                                           

这些outlier意味着不能满足函数间隔大于等于1的约束条件,可以对每个样本点引进一个松弛变量,使函数间隔加上松弛变量大于等于1,约束条件变为:      

 同时对每个松弛变量,支付一个代价,目标函数变为;      ,其中C>0称为惩罚 

 参数,C值越大对误分类的惩罚也越大.新目标函数包含了两层含义:使间隔尽量大,同时使误分类点的个数尽量小.

根据前面的对偶性算法,最终所得公式为:

KKT条件成立可以得到,j是满足0<aj*<C的下标之一.问题就变为选择惩罚参数C>0,求得对偶问题(凸二次规划问题)的最优解a*,代入计算w*和b*,求得分离超平面和分类决策函数.因为b的解并不唯一,所以实际计算b*时可以取所有样本点上的平均值。

支持向量:在线性不可分的情况下,将对应与ai*>0的样本点(xi,yi)的实例点xi称为支持向量。

SMO算法

SMO(sequence minimum optimal 序列最小最优化):快速求解凸二次规划问题的算法.基本思路是:如果所有变量都满足此优化问题的KKT条件,那么解就得到了.否则,选择两个变量,固定其他变量,针对这两个变量构建一个二次规划问题.不断地将原问题分解为子问题并对子问题求解,就可以求解原问题.注意子问题两个变量中只有一个是自由变量,另一个由等式约束确定. 

两个变量二次规划的求解方法:       

假设选择的两个变量是a1,a2,其他变量是固定的,于是得到子问题:

ε是常数,目标函数式省略了不含a1,a2的常数项.考虑不等式约束和等式约束,要求的是目标函数在一条平行于对角线的线段上        的最优值

                                 

问题变为单变量的最优化问题.假设初始可行解为aold,最优解为anew,考虑沿着约束方向未经剪辑的最优解anew,unc(即未考虑不等式约束).对该问题求偏导数,并令导数为0,代入

 原式,

 经剪辑后a2的解是  L与H是a2new所在的对角线段端点的界.并解得:
变量的选择方法:在每个子问题中选择两个变量优化,其中至少一个变量是违反KKT条件的.第一个变量的选取标准是违反KKT条件最严重的样本点,第二个变量的选取标准是希望能使该变量有足够大的变化,一般可以选取使对应的|E1-E2|最大的点.在每次选取完点后,更新阈值b和差值Ei.

算法步骤:

1,循环所以训练样本,判断是否满足KKT条件,如不满足,则记录该点ai: KKT条件:

                                                                                                                                   

(2)不等式约束使得(αi,αj)在盒子[0, C]x[0, C]内,等式约束使得(αi, αj)在平行于盒子[0, C]x[0, C]的对角线的直线上。因此要求的是目标函数在一条平行于对角线的线段上的最优值。这使得两个变量的最优化问题成为实质的单变量的最优化问题。由图可以得到,αj的上下界可以通过下面的方法得到: 

                                                                   

我们优化的时候,αj必须要满足上面这个约束。也就是说上面是αj的可行域。然后我们开始寻找αj,使得目标函数最大化。通过推导得到αj的更新公式如下 :

                                       

对应参数值:

                              

这里Ek可以看做对第k个样本,SVM的输出与期待输出,也就是样本标签的误差。 而η实际上是度量两个样本i和j的相似性的。在计算η的时候,我们需要使用核函数,那么就可以用核函数来取代上面的内积。

(3)进行不等式剪裁:

                                     

(4)计算αi:

                                        

4,计算阈值b

优化αi和αj后,我们就可以更新阈值b,使得对两个样本i和j都满足KKT条件。如果优化后αi不在边界上(也就是满足0<αi<C,这时候根据KKT条件,可以得到yigi(xi)=1,这样我们才可以计算b),那下面的阈值b1是有效的,因为当输入xi时它迫使SVM输出yi。

          

同样      

如果0<αi<C和0<αj<C都满足,那么b1和b2都有效,而且他们是相等的。如果他们两个都处于边界上(也就是αi=0或者αi=C,同时αj=0或者αj=C),那么在b1和b2之间的阈值都满足KKT条件,一般我们取他们的平均值b=(b1+b2)/2。所以,总的来说对b的更新如下:

                   

5,凸优化问题终止条件:

SMO算法的基本思路是:如果说有变量的解都满足此最优化问题的KKT条件,那么这个最优化问题的解就得到了。因为KKT条件是该最优化问题的充分必要条件(证明请参考文献)。所以我们可以监视原问题的KKT条件,所以所有的样本都满足KKT条件,那么就表示迭代结束了。但是由于KKT条件本身是比较苛刻的,所以也需要设定一个容忍值,即所有样本在容忍值范围内满足KKT条件则认为训练可以结束;当然了,对于对偶问题的凸优化还有其他终止条件,可以参考文献。

代码:

  1. from numpy import *
  2. import matplotlib.pyplot as plt
  3. MOSHI = 'mul'
  4. GAOSI_C = 3
  5. def loadDataSet(filename): #读取数据
  6. dataMat=[]
  7. labelMat=[]
  8. fr=open(filename)
  9. for line in fr.readlines():
  10. lineArr=line.strip().split('###')
  11. dataMat.append([float(lineArr[0]),float(lineArr[1])])
  12. labelMat.append(float(lineArr[2]))
  13. return dataMat,labelMat #返回数据特征和数据类别
  14. def selectJrand(i,m): #在0-m中随机选择一个不是i的整数
  15. j=i
  16. while (j==i):
  17. j=int(random.uniform(0,m))
  18. return j
  19. def clipAlpha(aj,H,L): #保证a在L和H范围内(L <= a <= H)
  20. if aj>H:
  21. aj=H
  22. if L>aj:
  23. aj=L
  24. return aj
  25. def kernelTrans(X, A, kTup): #核函数,输入参数,X:支持向量的特征树;A:某一行特征数据;kTup:('lin',k1)核函数的类型和参数
  26. m,n = shape(X)
  27. K = mat(zeros((m,1)))
  28. if kTup[0]=='lin': #线性函数
  29. K = X * A.T
  30. elif kTup[0]=='rbf': # 径向基函数(radial bias function)gaosi
  31. for j in range(m):
  32. deltaRow = X[j,:] - A
  33. K[j] = deltaRow*deltaRow.T
  34. K = exp(K/(-1*kTup[1]**2)) #返回生成的结果
  35. elif kTup[0]=='mul': # duo xiang shi
  36. for j in range(m):
  37. deltaRow = X[j,:]*A.T
  38. K[j] = (deltaRow + 1)**kTup[1]
  39. else:
  40. raise NameError('Houston We Have a Problem -- That Kernel is not recognized')
  41. return K
  42. #定义类,方便存储数据
  43. class optStruct:
  44. def __init__(self,dataMatIn, classLabels, C, toler, kTup): # 存储各类参数
  45. self.X = dataMatIn #数据特征
  46. self.labelMat = classLabels #数据类别
  47. self.C = C #软间隔参数C,参数越大,非线性拟合能力越强
  48. self.tol = toler #停止阀值
  49. self.m = shape(dataMatIn)[0] #数据行数
  50. self.alphas = mat(zeros((self.m,1)))
  51. self.b = 0 #初始设为0
  52. self.eCache = mat(zeros((self.m,2))) #缓存
  53. self.K = mat(zeros((self.m,self.m))) #核函数的计算结果
  54. for i in range(self.m):
  55. self.K[:,i] = kernelTrans(self.X, self.X[i,:], kTup)
  56. def calcEk(oS, k): #计算Ek(参考《统计学习方法》p127公式7.105)
  57. fXk = float(multiply(oS.alphas,oS.labelMat).T*oS.K[:,k] + oS.b)
  58. Ek = fXk - float(oS.labelMat[k])
  59. return Ek
  60. #随机选取aj,并返回其E值
  61. def selectJ(i, oS, Ei):
  62. maxK = -1
  63. maxDeltaE = 0
  64. Ej = 0
  65. oS.eCache[i] = [1,Ei]
  66. validEcacheList = nonzero(oS.eCache[:,0].A)[0] #返回矩阵中的非零位置的行数
  67. if (len(validEcacheList)) > 1:
  68. for k in validEcacheList:
  69. if k == i:
  70. continue
  71. Ek = calcEk(oS, k)
  72. deltaE = abs(Ei - Ek)
  73. if (deltaE > maxDeltaE): #返回步长最大的aj
  74. maxK = k
  75. maxDeltaE = deltaE
  76. Ej = Ek
  77. return maxK, Ej
  78. else:
  79. j = selectJrand(i, oS.m)
  80. Ej = calcEk(oS, j)
  81. return j, Ej
  82. def updateEk(oS, k): #更新os数据
  83. Ek = calcEk(oS, k)
  84. oS.eCache[k] = [1,Ek]
  85. #首先检验ai是否满足KKT条件,如果不满足,随机选择aj进行优化,更新ai,aj,b值
  86. def innerL(i, oS): #输入参数i和所有参数数据
  87. Ei = calcEk(oS, i) #计算E值
  88. 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)): #检验这行数据是否符合KKT条件 参考《统计学习方法》p128公式7.111-113
  89. j,Ej = selectJ(i, oS, Ei) #随机选取aj,并返回其E值
  90. alphaIold = oS.alphas[i].copy()
  91. alphaJold = oS.alphas[j].copy()
  92. if (oS.labelMat[i] != oS.labelMat[j]): #以下代码的公式参考《统计学习方法》p126
  93. L = max(0, oS.alphas[j] - oS.alphas[i])
  94. H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
  95. else:
  96. L = max(0, oS.alphas[j] + oS.alphas[i] - oS.C)
  97. H = min(oS.C, oS.alphas[j] + oS.alphas[i])
  98. if L==H:
  99. print("L==H")
  100. return 0
  101. eta = 2.0 * oS.K[i,j] - oS.K[i,i] - oS.K[j,j] #参考《统计学习方法》p127公式7.107 (x-y)**2
  102. if eta >= 0:
  103. print("eta>=0")
  104. return 0
  105. oS.alphas[j] -= oS.labelMat[j]*(Ei - Ej)/eta #参考《统计学习方法》p127公式7.106
  106. oS.alphas[j] = clipAlpha(oS.alphas[j],H,L) #参考《统计学习方法》p127公式7.108
  107. updateEk(oS, j)
  108. if (abs(oS.alphas[j] - alphaJold) < oS.tol): #alpha变化大小阀值(自己设定)
  109. print("j not moving enough")
  110. return 0
  111. oS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j])#参考《统计学习方法》p127公式7.109
  112. updateEk(oS, i) #更新数据
  113. #以下求解b的过程,参考《统计学习方法》p129公式7.114-7.116
  114. 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]
  115. 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]
  116. if (0 < oS.alphas[i]<oS.C):
  117. oS.b = b1
  118. elif (0 < oS.alphas[j]<oS.C):
  119. oS.b = b2
  120. else:
  121. oS.b = (b1 + b2)/2.0
  122. return 1
  123. else:
  124. return 0
  125. #SMO函数,用于快速求解出alpha
  126. def smoP(dataMatIn, classLabels, C, toler, maxIter,kTup=('lin', 0)): #输入参数:数据特征,数据类别,参数C,阀值toler,最大迭代次数,核函数(默认线性核)
  127. oS = optStruct(mat(dataMatIn),mat(classLabels).transpose(),C,toler, kTup)
  128. iter = 0
  129. entireSet = True
  130. alphaPairsChanged = 0
  131. while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):
  132. alphaPairsChanged = 0
  133. if entireSet:
  134. for i in range(oS.m): #遍历所有数据
  135. alphaPairsChanged += innerL(i,oS)
  136. print("fullSet, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)) #显示第多少次迭代,那行特征数据使alpha发生了改变,这次改变了多少次alpha
  137. else:
  138. nonBoundIs = nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]
  139. for i in nonBoundIs: #遍历非边界的数据
  140. alphaPairsChanged += innerL(i,oS)
  141. print("non-bound, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged))
  142. iter += 1
  143. if entireSet:
  144. entireSet = False
  145. elif (alphaPairsChanged == 0):
  146. entireSet = True
  147. print("iteration number: %d" % iter)
  148. return oS.b,oS.alphas
  149. def showdata(dataArr,labelArr, ax):
  150. datMat = array(dataArr)
  151. labelMat = array(labelArr)
  152. labelMat_i = argwhere(labelMat == 1)
  153. labelMat_j = argwhere(labelMat == -1)
  154. datMat_i = datMat[labelMat_i].T
  155. datMat_j = datMat[labelMat_j].T
  156. plt.sca(ax)
  157. plt.scatter([datMat_i[1]], [datMat_i[0]], c='r', marker='x')
  158. plt.scatter([datMat_j[1]], [datMat_j[0]], c='g', marker='o')
  159. def showdata_i(dataArr,nums, ax):
  160. datMat = array(dataArr)
  161. datMat_i = datMat[nums].T
  162. plt.sca(ax)
  163. plt.scatter([datMat_i[1]], [datMat_i[0]], c='b', marker='v')
  164. def showdata_sv(dataArr,nums, ax):
  165. datMat = array(dataArr)
  166. datMat_i = datMat[nums].T
  167. plt.sca(ax)
  168. plt.scatter([datMat_i[1]], [datMat_i[0]], c='b', marker='P')
  169. def testRbf(data_train,data_test):
  170. plt.figure(1, figsize=(16, 8))
  171. dataArr,labelArr = loadDataSet(data_train) #读取训练数据
  172. ax1 = plt.subplot(1, 2, 1)
  173. showdata(dataArr,labelArr, ax1)
  174. b,alphas = smoP(dataArr, labelArr, 200, 0.00001, 10000, (MOSHI, GAOSI_C)) #通过SMO算法得到b和alpha
  175. datMat=mat(dataArr)
  176. labelMat = mat(labelArr).transpose()
  177. svInd=nonzero(alphas)[0] #选取不为0数据的行数(也就是支持向量)
  178. showdata_sv(dataArr, svInd, ax1)
  179. sVs=datMat[svInd] #支持向量的特征数据
  180. labelSV = labelMat[svInd] #支持向量的类别(1或-1)00001
  181. print("there are %d Support Vectors" % shape(sVs)[0]) #打印出共有多少的支持向量
  182. m,n = shape(datMat) #训练数据的行列数
  183. errorCount = 0
  184. wT = multiply(labelSV, alphas[svInd]).T
  185. nums = []
  186. for i in range(m):
  187. kernelEval = kernelTrans(sVs,datMat[i,:],(MOSHI, GAOSI_C)) #将支持向量转化为核函数
  188. predict=wT * kernelEval + b #这一行的预测结果(代码来源于《统计学习方法》p133里面最后用于预测的公式)注意最后确定的分离平面只有那些支持向量决定。
  189. if sign(predict)!=sign(labelArr[i]): #sign函数 -1 if x < 0, 0 if x==0, 1 if x > 0
  190. errorCount += 1
  191. nums.append(i)
  192. showdata_i(dataArr, nums, ax1)
  193. print("the training error rate is: %f" % (float(errorCount)/m)) #打印出错误率
  194. dataArr_test,labelArr_test = loadDataSet(data_test) #读取测试数据
  195. ax2 = plt.subplot(1, 2, 2)
  196. showdata(dataArr_test, labelArr_test, ax2)
  197. errorCount_test = 0
  198. datMat_test=mat(dataArr_test)
  199. labelMat = mat(labelArr_test).transpose()
  200. m,n = shape(datMat_test)
  201. nums_test = []
  202. for i in range(m): #在测试数据上检验错误率
  203. kernelEval = kernelTrans(sVs,datMat_test[i,:],(MOSHI, GAOSI_C))
  204. predict=kernelEval.T * multiply(labelSV,alphas[svInd]) + b
  205. if sign(predict)!=sign(labelArr_test[i]):
  206. errorCount_test += 1
  207. nums_test.append(i)
  208. print("the test error rate is: %f" % (float(errorCount_test)/m))
  209. showdata_i(dataArr_test, nums_test, ax2)
  210. plt.show()
  211. #主程序
  212. def main():
  213. filename_traindata='train_data2.TXT'
  214. filename_testdata='test_data1.TXT'
  215. testRbf(filename_traindata,filename_testdata)
  216. if __name__=='__main__':
  217. main()

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/177271
推荐阅读
相关标签
  

闽ICP备14008679号