Machine Learning in Action(5) SVM算法

       做机器学习的一定对支持向量机(support vector machine-SVM)颇为熟悉,因为在深度学习出现之前,SVM一直霸占着机器学习老大哥的位子。他的理论很优美,各种变种改进版本也很多,比如latent-SVM, structural-SVM等。这节先来看看SVM的理论吧,在(图一)中A图表示有两类的数据集,图B,C,D都提供了一个线性分类器来对数据进行分类?但是哪个效果好一些?

 

(图一)

        可能对这个数据集来说,三个的分类器都一样足够好了吧,但是其实不然,这个只是训练集,现实测试的样本分布可能会比较散一些,各种可能都有,为了应对这种情况,我们要做的就是尽可能的使得线性分类器离两个数据集都尽可能的远,因为这样就会减少现实测试样本越过分类器的风险,提高检测精度。这种使得数据集到分类器之间的间距(margin)最大化的思想就是支持向量机的核心思想,而离分类器距离最近的样本成为支持向量。既然知道了我们的目标就是为了寻找最大边距,怎么寻找支持向量?如何实现?下面以(图二)来说明如何完成这些工作。

 

(图二)

假设(图二)中的直线表示一个超面,为了方面观看显示成一维直线,特征都是超面维度加一维度的,图中也可以看出,特征是二维,而分类器是一维的。如果特征是三维的,分类器就是一个平面。假设超面的解析式为,那么点A到超面的距离为,下面给出这个距离证明:

(图三)

在(图三)中,青色菱形表示超面,Xn为数据集中一点,W是超面权重,而且W是垂直于超面的。证明垂直很简单,假设X’和X’’都是超面上的一点,

 

,因此W垂直于超面。知道了W垂直于超面,那么Xn到超面的距离其实就是Xn和超面上任意一点x的连线在W上的投影,如(图四)所示:

(图四)

而(Xn-X)在W上的投影可通过(公式一)来计算,另外(公式一)也一并完成距离计算:

 

 

(公式一)

     注意最后使用了配项法并且用了超面解析式才得出了距离计算。有了距离就可以来推导我们刚开始的想法:使得分类器距所有样本距离最远,即最大化边距,但是最大化边距的前提是我们要找到支持向量,也就是离分类器最近的样本点,此时我们就要完成两个优化任务,找到离分类器最近的点(支持向量),然后最大化边距。如(公式二)所示:

 

(公式二)

        大括号里面表示找到距离分类超面最近的支持向量,大括号外面则是使得超面离支持向量的距离最远,要优化这个函数相当困难,目前没有太有效的优化方法。但是我们可以把问题转换一下,如果我们把大括号里面的优化问题固定住,然后来优化外面的就很容易了,可以用现在的优化方法来求解,因此我们做一个假设,假设大括号里的分子等于1,那么我们只剩下优化W咯,整个优化公式就可以写成(公式三)的形式:

 

(公式三)

        这下就简单了,有等式约束的优化,约束式子为,这个约束等式背后还有个小窍门,假设我们把样本Xn的标签设为1或者-1,当Xn在超面上面(或者右边)时,带入超面解析式得到大于0的值,乘上标签1仍然为本身,可以表示离超面的距离;当Xn在超面下面(或者左边)时,带入超面解析式得到小于0的值,乘上标签-1也是正值,仍然可以表示距离,因此我们把通常两类的标签01转换成-11就可以把标签信息完美的融进等式约束中,(公式三)最后一行也体现出来咯。下面继续说优化 求解(公式四)的方法,在最优化中,通常我们需要求解的最优化问题有如下几类:

       (i)无约束优化问题,可以写为:

              min f(x);  

       (ii)有等式约束的优化问题,可以写为:

                  min f(x), 

                   s.t. h_i(x) = 0; i =1, ..., n 

        (iii)有不等式约束的优化问题,可以写为:

                min f(x), 

                 s.t. g_i(x) <= 0; i =1, ..., n

                h_j(x) = 0; j =1,..., m

       对于第(i)类的优化问题,常常使用的方法就是Fermat定理,即使用求取f(x)的导数,然后令其为零,可以求得候选最优值,再在这些候选值中验证;如果是凸函数,可以保证是最优解。

       对于第(ii)类的优化问题,常常使用的方法就是拉格朗日乘子法(LagrangeMultiplier),即把等式约束h_i(x)用一个系数与f(x)写为一个式子,称为拉格朗日函数,而系数称为拉格朗日乘子。通过拉格朗日函数对各个变量求导,令其为零,可以求得候选值集合,然后验证求得最优值。

       对于第(iii)类的优化问题,常常使用的方法就是KKT条件。同样地,我们把所有的等式、不等式约束与f(x)写为一个式子,也叫拉格朗日函数,系数也称拉格朗日乘子,通过一些条件,可以求出最优值的必要条件,这个条件称为KKT条件。

       而(公式三)很明显符合第二类优化方法,因此可以使用拉格朗日乘子法来对其求解,在求解之前,我们先对(公式四)做个简单的变换。最大化||W||的导数可以最小化||W||或者W’W,如(公式四)所示:

 

(公式四)

套进拉格朗日乘子法公式得到如(公式五)所示的样子:

 

 

(公式五)

        在(公式五)中通过拉格朗日乘子法函数分别对W和b求导,为了得到极值点,令导数为0,得到

 

 ,然后把他们代入拉格朗日乘子法公式里得到(公式六)的形式:

 

(公式六)

     (公式六)后两行是目前我们要求解的优化函数,现在只需要做个二次规划即可求出alpha,二次规划优化求解如(公式七)所示:

 

 

(公式七)

       通过(公式七)求出alpha后,就可以用(公式六)中的第一行求出W。到此为止,SVM的公式推导基本完成了,可以看出数学理论很严密,很优美,尽管有些同行们认为看起枯燥,但是最好沉下心来从头看完,也不难,难的是优化。二次规划求解计算量很大,在实际应用中常用SMO(Sequential minimal optimization)算法,SMO算法打算放在下节结合代码来说。

      上面基本完成了SVM的理论推倒,寻找最大化间隔的目标最终转换成求解拉格朗日乘子变量alpha的求解问题,求出了alpha即可求解出SVM的权重W,有了权重也就有了最大间隔距离,但是其实上节我们有个假设:就是训练集是线性可分的,这样求出的alpha在[0,infinite]。但是如果数据不是线性可分的呢?此时我们就要允许部分的样本可以越过分类器,这样优化的目标函数就可以不变,只要引入松弛变量即可,它表示错分类样本点的代价,分类正确时它等于0,当分类错误时,其中Tn表示样本的真实标签-1或者1,回顾上节中,我们把支持向量到分类器的距离固定为1,因此两类的支持向量间的距离肯定大于1的,当分类错误时肯定也大于1,如(图五)所示(这里公式和图标序号都接上一节)。

 

(图五)

       这样有了错分类的代价,我们把上节(公式四)的目标函数上添加上这一项错分类代价,得到如(公式八)的形式:

 

(公式八)

        重复上节的拉格朗日乘子法步骤,得到(公式九):

 

(公式九)

         多了一个Un乘子,当然我们的工作就是继续求解此目标函数,继续重复上节的步骤,求导得到(公式十):

(公式十)

 

         又因为alpha大于0,而且Un大于0,所以0<alpha<C,为了解释的清晰一些,我们把(公式九)的KKT条件也发出来(上节中的第三类优化问题),注意Un是大于等于0

 

       推导到现在,优化函数的形式基本没变,只是多了一项错分类的价值,但是多了一个条件,0<alpha<C,C是一个常数,它的作用就是在允许有错误分类的情况下,控制最大化间距,它太大了会导致过拟合,太小了会导致欠拟合。接下来的步骤貌似大家都应该知道了,多了一个C常量的限制条件,然后继续用SMO算法优化求解二次规划,但是我想继续把核函数也一次说了,如果样本线性不可分,引入核函数后,把样本映射到高维空间就可以线性可分,如(图六)所示的线性不可分的样本:

 

(图六)

         在(图六)中,现有的样本是很明显线性不可分,但是加入我们利用现有的样本X之间作些不同的运算,如(图六)右边所示的样子,而让f作为新的样本(或者说新的特征)是不是更好些?现在把X已经投射到高维度上去了,但是f我们不知道,此时核函数就该上场了,以高斯核函数为例,在(图七)中选几个样本点作为基准点,来利用核函数计算f,如(图七)所示:

 

(图七)

       这样就有了f,而核函数此时相当于对样本的X和基准点一个度量,做权重衰减,形成依赖于x的新的特征f,把f放在上面说的SVM中继续求解alpha,然后得出权重就行了,原理很简单吧,为了显得有点学术味道,把核函数也做个样子加入目标函数中去吧,如(公式十一)所示:

 

(公式十一)

 

 

        其中K(Xn,Xm)是核函数,和上面目标函数比没有多大的变化,用SMO优化求解就行了,代码如下:

 1 def smoPK(dataMatIn, classLabels, C, toler, maxIter):    #full Platt SMO
 2     oS = optStruct(mat(dataMatIn),mat(classLabels).transpose(),C,toler)
 3     iter = 0
 4     entireSet = True; alphaPairsChanged = 0
 5     while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):
 6         alphaPairsChanged = 0
 7         if entireSet:   #go over all
 8             for i in range(oS.m):        
 9                 alphaPairsChanged += innerL(i,oS)
10                 print "fullSet, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)
11             iter += 1
12         else:#go over non-bound (railed) alphas
13             nonBoundIs = nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]
14             for i in nonBoundIs:
15                 alphaPairsChanged += innerL(i,oS)
16                 print "non-bound, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)
17             iter += 1
18         if entireSet: entireSet = False #toggle entire set loop
19         elif (alphaPairsChanged == 0): entireSet = True  
20         print "iteration number: %d" % iter
21     return oS.b,oS.alphas

 

       下面演示一个小例子,手写识别。

      (1)收集数据:提供文本文件

      (2)准备数据:基于二值图像构造向量

      (3)分析数据:对图像向量进行目测

      (4)训练算法:采用两种不同的核函数,并对径向基函数采用不同的设置来运行SMO算法。

       (5)测试算法:编写一个函数来测试不同的核函数,并计算错误率

       (6)使用算法:一个图像识别的完整应用还需要一些图像处理的只是,此demo略。

      完整代码如下:

  1 from numpy import *
  2 from time import sleep
  3 
  4 def loadDataSet(fileName):
  5     dataMat = []; labelMat = []
  6     fr = open(fileName)
  7     for line in fr.readlines():
  8         lineArr = line.strip().split('\t')
  9         dataMat.append([float(lineArr[0]), float(lineArr[1])])
 10         labelMat.append(float(lineArr[2]))
 11     return dataMat,labelMat
 12 
 13 def selectJrand(i,m):
 14     j=i #we want to select any J not equal to i
 15     while (j==i):
 16         j = int(random.uniform(0,m))
 17     return j
 18 
 19 def clipAlpha(aj,H,L):
 20     if aj > H: 
 21         aj = H
 22     if L > aj:
 23         aj = L
 24     return aj
 25 
 26 def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
 27     dataMatrix = mat(dataMatIn); labelMat = mat(classLabels).transpose()
 28     b = 0; m,n = shape(dataMatrix)
 29     alphas = mat(zeros((m,1)))
 30     iter = 0
 31     while (iter < maxIter):
 32         alphaPairsChanged = 0
 33         for i in range(m):
 34             fXi = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
 35             Ei = fXi - float(labelMat[i])#if checks if an example violates KKT conditions
 36             if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
 37                 j = selectJrand(i,m)
 38                 fXj = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b
 39                 Ej = fXj - float(labelMat[j])
 40                 alphaIold = alphas[i].copy(); alphaJold = alphas[j].copy();
 41                 if (labelMat[i] != labelMat[j]):
 42                     L = max(0, alphas[j] - alphas[i])
 43                     H = min(C, C + alphas[j] - alphas[i])
 44                 else:
 45                     L = max(0, alphas[j] + alphas[i] - C)
 46                     H = min(C, alphas[j] + alphas[i])
 47                 if L==H: print "L==H"; continue
 48                 eta = 2.0 * dataMatrix[i,:]*dataMatrix[j,:].T - dataMatrix[i,:]*dataMatrix[i,:].T - dataMatrix[j,:]*dataMatrix[j,:].T
 49                 if eta >= 0: print "eta>=0"; continue
 50                 alphas[j] -= labelMat[j]*(Ei - Ej)/eta
 51                 alphas[j] = clipAlpha(alphas[j],H,L)
 52                 if (abs(alphas[j] - alphaJold) < 0.00001): print "j not moving enough"; continue
 53                 alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])#update i by the same amount as j
 54                                                                         #the update is in the oppostie direction
 55                 b1 = b - Ei- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i,:]*dataMatrix[i,:].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[i,:]*dataMatrix[j,:].T
 56                 b2 = b - Ej- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i,:]*dataMatrix[j,:].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[j,:]*dataMatrix[j,:].T
 57                 if (0 < alphas[i]) and (C > alphas[i]): b = b1
 58                 elif (0 < alphas[j]) and (C > alphas[j]): b = b2
 59                 else: b = (b1 + b2)/2.0
 60                 alphaPairsChanged += 1
 61                 print "iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)
 62         if (alphaPairsChanged == 0): iter += 1
 63         else: iter = 0
 64         print "iteration number: %d" % iter
 65     return b,alphas
 66 
 67 def kernelTrans(X, A, kTup): #calc the kernel or transform data to a higher dimensional space
 68     m,n = shape(X)
 69     K = mat(zeros((m,1)))
 70     if kTup[0]=='lin': K = X * A.T   #linear kernel
 71     elif kTup[0]=='rbf':
 72         for j in range(m):
 73             deltaRow = X[j,:] - A
 74             K[j] = deltaRow*deltaRow.T
 75         K = exp(K/(-1*kTup[1]**2)) #divide in NumPy is element-wise not matrix like Matlab
 76     else: raise NameError('Houston We Have a Problem -- \
 77     That Kernel is not recognized')
 78     return K
 79 
 80 class optStruct:
 81     def __init__(self,dataMatIn, classLabels, C, toler, kTup):  # Initialize the structure with the parameters 
 82         self.X = dataMatIn
 83         self.labelMat = classLabels
 84         self.C = C
 85         self.tol = toler
 86         self.m = shape(dataMatIn)[0]
 87         self.alphas = mat(zeros((self.m,1)))
 88         self.b = 0
 89         self.eCache = mat(zeros((self.m,2))) #first column is valid flag
 90         self.K = mat(zeros((self.m,self.m)))
 91         for i in range(self.m):
 92             self.K[:,i] = kernelTrans(self.X, self.X[i,:], kTup)
 93         
 94 def calcEk(oS, k):
 95     fXk = float(multiply(oS.alphas,oS.labelMat).T*oS.K[:,k] + oS.b)
 96     Ek = fXk - float(oS.labelMat[k])
 97     return Ek
 98         
 99 def selectJ(i, oS, Ei):         #this is the second choice -heurstic, and calcs Ej
100     maxK = -1; maxDeltaE = 0; Ej = 0
101     oS.eCache[i] = [1,Ei]  #set valid #choose the alpha that gives the maximum delta E
102     validEcacheList = nonzero(oS.eCache[:,0].A)[0]
103     if (len(validEcacheList)) > 1:
104         for k in validEcacheList:   #loop through valid Ecache values and find the one that maximizes delta E
105             if k == i: continue #don't calc for i, waste of time
106             Ek = calcEk(oS, k)
107             deltaE = abs(Ei - Ek)
108             if (deltaE > maxDeltaE):
109                 maxK = k; maxDeltaE = deltaE; Ej = Ek
110         return maxK, Ej
111     else:   #in this case (first time around) we don't have any valid eCache values
112         j = selectJrand(i, oS.m)
113         Ej = calcEk(oS, j)
114     return j, Ej
115 
116 def updateEk(oS, k):#after any alpha has changed update the new value in the cache
117     Ek = calcEk(oS, k)
118     oS.eCache[k] = [1,Ek]
119         
120 def innerL(i, oS):
121     Ei = calcEk(oS, i)
122     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)):
123         j,Ej = selectJ(i, oS, Ei) #this has been changed from selectJrand
124         alphaIold = oS.alphas[i].copy(); alphaJold = oS.alphas[j].copy();
125         if (oS.labelMat[i] != oS.labelMat[j]):
126             L = max(0, oS.alphas[j] - oS.alphas[i])
127             H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
128         else:
129             L = max(0, oS.alphas[j] + oS.alphas[i] - oS.C)
130             H = min(oS.C, oS.alphas[j] + oS.alphas[i])
131         if L==H: print "L==H"; return 0
132         eta = 2.0 * oS.K[i,j] - oS.K[i,i] - oS.K[j,j] #changed for kernel
133         if eta >= 0: print "eta>=0"; return 0
134         oS.alphas[j] -= oS.labelMat[j]*(Ei - Ej)/eta
135         oS.alphas[j] = clipAlpha(oS.alphas[j],H,L)
136         updateEk(oS, j) #added this for the Ecache
137         if (abs(oS.alphas[j] - alphaJold) < 0.00001): print "j not moving enough"; return 0
138         oS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j])#update i by the same amount as j
139         updateEk(oS, i) #added this for the Ecache                    #the update is in the oppostie direction
140         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]
141         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]
142         if (0 < oS.alphas[i]) and (oS.C > oS.alphas[i]): oS.b = b1
143         elif (0 < oS.alphas[j]) and (oS.C > oS.alphas[j]): oS.b = b2
144         else: oS.b = (b1 + b2)/2.0
145         return 1
146     else: return 0
147 
148 def smoP(dataMatIn, classLabels, C, toler, maxIter,kTup=('lin', 0)):    #full Platt SMO
149     oS = optStruct(mat(dataMatIn),mat(classLabels).transpose(),C,toler, kTup)
150     iter = 0
151     entireSet = True; alphaPairsChanged = 0
152     while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):
153         alphaPairsChanged = 0
154         if entireSet:   #go over all
155             for i in range(oS.m):        
156                 alphaPairsChanged += innerL(i,oS)
157                 print "fullSet, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)
158             iter += 1
159         else:#go over non-bound (railed) alphas
160             nonBoundIs = nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]
161             for i in nonBoundIs:
162                 alphaPairsChanged += innerL(i,oS)
163                 print "non-bound, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)
164             iter += 1
165         if entireSet: entireSet = False #toggle entire set loop
166         elif (alphaPairsChanged == 0): entireSet = True  
167         print "iteration number: %d" % iter
168     return oS.b,oS.alphas
169 
170 def calcWs(alphas,dataArr,classLabels):
171     X = mat(dataArr); labelMat = mat(classLabels).transpose()
172     m,n = shape(X)
173     w = zeros((n,1))
174     for i in range(m):
175         w += multiply(alphas[i]*labelMat[i],X[i,:].T)
176     return w
177 
178 def testRbf(k1=1.3):
179     dataArr,labelArr = loadDataSet('testSetRBF.txt')
180     b,alphas = smoP(dataArr, labelArr, 200, 0.0001, 10000, ('rbf', k1)) #C=200 important
181     datMat=mat(dataArr); labelMat = mat(labelArr).transpose()
182     svInd=nonzero(alphas.A>0)[0]
183     sVs=datMat[svInd] #get matrix of only support vectors
184     labelSV = labelMat[svInd];
185     print "there are %d Support Vectors" % shape(sVs)[0]
186     m,n = shape(datMat)
187     errorCount = 0
188     for i in range(m):
189         kernelEval = kernelTrans(sVs,datMat[i,:],('rbf', k1))
190         predict=kernelEval.T * multiply(labelSV,alphas[svInd]) + b
191         if sign(predict)!=sign(labelArr[i]): errorCount += 1
192     print "the training error rate is: %f" % (float(errorCount)/m)
193     dataArr,labelArr = loadDataSet('testSetRBF2.txt')
194     errorCount = 0
195     datMat=mat(dataArr); labelMat = mat(labelArr).transpose()
196     m,n = shape(datMat)
197     for i in range(m):
198         kernelEval = kernelTrans(sVs,datMat[i,:],('rbf', k1))
199         predict=kernelEval.T * multiply(labelSV,alphas[svInd]) + b
200         if sign(predict)!=sign(labelArr[i]): errorCount += 1    
201     print "the test error rate is: %f" % (float(errorCount)/m)    
202     
203 def img2vector(filename):
204     returnVect = zeros((1,1024))
205     fr = open(filename)
206     for i in range(32):
207         lineStr = fr.readline()
208         for j in range(32):
209             returnVect[0,32*i+j] = int(lineStr[j])
210     return returnVect
211 
212 def loadImages(dirName):
213     from os import listdir
214     hwLabels = []
215     trainingFileList = listdir(dirName)           #load the training set
216     m = len(trainingFileList)
217     trainingMat = zeros((m,1024))
218     for i in range(m):
219         fileNameStr = trainingFileList[i]
220         fileStr = fileNameStr.split('.')[0]     #take off .txt
221         classNumStr = int(fileStr.split('_')[0])
222         if classNumStr == 9: hwLabels.append(-1)
223         else: hwLabels.append(1)
224         trainingMat[i,:] = img2vector('%s/%s' % (dirName, fileNameStr))
225     return trainingMat, hwLabels    
226 
227 def testDigits(kTup=('rbf', 10)):
228     dataArr,labelArr = loadImages('trainingDigits')
229     b,alphas = smoP(dataArr, labelArr, 200, 0.0001, 10000, kTup)
230     datMat=mat(dataArr); labelMat = mat(labelArr).transpose()
231     svInd=nonzero(alphas.A>0)[0]
232     sVs=datMat[svInd] 
233     labelSV = labelMat[svInd];
234     print "there are %d Support Vectors" % shape(sVs)[0]
235     m,n = shape(datMat)
236     errorCount = 0
237     for i in range(m):
238         kernelEval = kernelTrans(sVs,datMat[i,:],kTup)
239         predict=kernelEval.T * multiply(labelSV,alphas[svInd]) + b
240         if sign(predict)!=sign(labelArr[i]): errorCount += 1
241     print "the training error rate is: %f" % (float(errorCount)/m)
242     dataArr,labelArr = loadImages('testDigits')
243     errorCount = 0
244     datMat=mat(dataArr); labelMat = mat(labelArr).transpose()
245     m,n = shape(datMat)
246     for i in range(m):
247         kernelEval = kernelTrans(sVs,datMat[i,:],kTup)
248         predict=kernelEval.T * multiply(labelSV,alphas[svInd]) + b
249         if sign(predict)!=sign(labelArr[i]): errorCount += 1    
250     print "the test error rate is: %f" % (float(errorCount)/m) 
251 
252 
253 '''#######********************************
254 Non-Kernel VErsions below
255 '''#######********************************
256 
257 class optStructK:
258     def __init__(self,dataMatIn, classLabels, C, toler):  # Initialize the structure with the parameters 
259         self.X = dataMatIn
260         self.labelMat = classLabels
261         self.C = C
262         self.tol = toler
263         self.m = shape(dataMatIn)[0]
264         self.alphas = mat(zeros((self.m,1)))
265         self.b = 0
266         self.eCache = mat(zeros((self.m,2))) #first column is valid flag
267         
268 def calcEkK(oS, k):
269     fXk = float(multiply(oS.alphas,oS.labelMat).T*(oS.X*oS.X[k,:].T)) + oS.b
270     Ek = fXk - float(oS.labelMat[k])
271     return Ek
272         
273 def selectJK(i, oS, Ei):         #this is the second choice -heurstic, and calcs Ej
274     maxK = -1; maxDeltaE = 0; Ej = 0
275     oS.eCache[i] = [1,Ei]  #set valid #choose the alpha that gives the maximum delta E
276     validEcacheList = nonzero(oS.eCache[:,0].A)[0]
277     if (len(validEcacheList)) > 1:
278         for k in validEcacheList:   #loop through valid Ecache values and find the one that maximizes delta E
279             if k == i: continue #don't calc for i, waste of time
280             Ek = calcEk(oS, k)
281             deltaE = abs(Ei - Ek)
282             if (deltaE > maxDeltaE):
283                 maxK = k; maxDeltaE = deltaE; Ej = Ek
284         return maxK, Ej
285     else:   #in this case (first time around) we don't have any valid eCache values
286         j = selectJrand(i, oS.m)
287         Ej = calcEk(oS, j)
288     return j, Ej
289 
290 def updateEkK(oS, k):#after any alpha has changed update the new value in the cache
291     Ek = calcEk(oS, k)
292     oS.eCache[k] = [1,Ek]
293         
294 def innerLK(i, oS):
295     Ei = calcEk(oS, i)
296     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)):
297         j,Ej = selectJ(i, oS, Ei) #this has been changed from selectJrand
298         alphaIold = oS.alphas[i].copy(); alphaJold = oS.alphas[j].copy();
299         if (oS.labelMat[i] != oS.labelMat[j]):
300             L = max(0, oS.alphas[j] - oS.alphas[i])
301             H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
302         else:
303             L = max(0, oS.alphas[j] + oS.alphas[i] - oS.C)
304             H = min(oS.C, oS.alphas[j] + oS.alphas[i])
305         if L==H: print "L==H"; return 0
306         eta = 2.0 * oS.X[i,:]*oS.X[j,:].T - oS.X[i,:]*oS.X[i,:].T - oS.X[j,:]*oS.X[j,:].T
307         if eta >= 0: print "eta>=0"; return 0
308         oS.alphas[j] -= oS.labelMat[j]*(Ei - Ej)/eta
309         oS.alphas[j] = clipAlpha(oS.alphas[j],H,L)
310         updateEk(oS, j) #added this for the Ecache
311         if (abs(oS.alphas[j] - alphaJold) < 0.00001): print "j not moving enough"; return 0
312         oS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j])#update i by the same amount as j
313         updateEk(oS, i) #added this for the Ecache                    #the update is in the oppostie direction
314         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
315         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
316         if (0 < oS.alphas[i]) and (oS.C > oS.alphas[i]): oS.b = b1
317         elif (0 < oS.alphas[j]) and (oS.C > oS.alphas[j]): oS.b = b2
318         else: oS.b = (b1 + b2)/2.0
319         return 1
320     else: return 0
321 
322 def smoPK(dataMatIn, classLabels, C, toler, maxIter):    #full Platt SMO
323     oS = optStruct(mat(dataMatIn),mat(classLabels).transpose(),C,toler)
324     iter = 0
325     entireSet = True; alphaPairsChanged = 0
326     while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):
327         alphaPairsChanged = 0
328         if entireSet:   #go over all
329             for i in range(oS.m):        
330                 alphaPairsChanged += innerL(i,oS)
331                 print "fullSet, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)
332             iter += 1
333         else:#go over non-bound (railed) alphas
334             nonBoundIs = nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]
335             for i in nonBoundIs:
336                 alphaPairsChanged += innerL(i,oS)
337                 print "non-bound, iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged)
338             iter += 1
339         if entireSet: entireSet = False #toggle entire set loop
340         elif (alphaPairsChanged == 0): entireSet = True  
341         print "iteration number: %d" % iter
342     return oS.b,oS.alphas

运行结果如(图八)所示:

(图八)

上面代码有兴趣的可以读读,用的话,建议使用libsvm。

上面内容转载至朋友博客:http://blog.csdn.net/marvin521/article/details/9305497

Ps:正如文章开头所说的SVM算法以其优异的性能,曾常年霸占机器学习兵器排行榜头名,也是当年导师叫我学习的第一个算法,真怀念当时满怀热情熬夜通宵Libsvm的时光。推荐SVM入门神马八股介绍http://www.blogjava.net/zhenandaci/archive/2009/02/13/254519.html和大牛的burges 的 tutorial http://research.microsoft.com/pubs/67119/svmtutorial.pdf ,SVM好是好,但是有几个纠结的点1,参数调节比较蛋疼,对于数据量大,采用网格搜寻得花好几天的时间。2,万恶的kernel函数,线性核还好,神马RBF, polynomial是坑人的节奏,不知道后面出的tree,string的kernel咋样。前段时间百度深度学习研究院副院长余凯在微博上抛出来一行代码写出SVM的言论引来各路神牛激烈讨论,有1行Python的,Matlab,R的,各种奇葩答案,针对SVM的问题好多学者搞起了large scale 的SVM,从优化算法到代码实现,推荐http://leon.bottou.org/projects/sgd 作者理论跟工程代码 都很赞。关于并行的Mahout里面有实现,本书的第十五章就有基于MrJob 的并行SVM算法的实现。GPU方面也有http://patternsonascreen.net/cuSVM.htmlhttp://code.google.com/p/multisvm/,这两个都是CUDA架构下的实现,记得当时在学校上届师兄就用OpenCL把SVM应用到Ranking里面而获得当年AMD高性能比赛的第一名。啊,闲话扯远了,最后引用下微博达人夏粉_百度说的一句话:我分享常问的问题,请介绍一下SVM,Boosting,LR中任何一个最熟悉的算法的目标函数、优化过程、并行实现、算法收敛性、样本复杂度、适用场景、调参经验。

 

PS:附上博客达人july整理的关于SVM的材料,他的博客内容也非常赞,适合入门者http://blog.csdn.net/v_july_v/article/details/7624837

参考文献及推荐阅读

  1. 支持向量机导论》,[美] Nello Cristianini / John Shawe-Taylor 著
  2. 支持向量机导论一书的支持网站:http://www.support-vector.net/
  3. 《数据挖掘导论》,[美] Pang-Ning Tan / Michael Steinbach / Vipin Kumar 著
  4. 《数据挖掘:概念与技术》,(加)Jiawei Han;Micheline Kamber 著;
  5. 《数据挖掘中的新方法:支持向量机》,邓乃扬 田英杰 著;
  6. 支持向量机--理论、算法和扩展》,邓乃扬 田英杰 著;
  7. 支持向量机系列pluskidhttp://blog.pluskid.org/?page_id=683
  8. http://www.360doc.com/content/07/0716/23/11966_615252.shtml
  9. 数据挖掘十大经典算法初探
  10. 《模式识别支持向量机指南》,C.J.C Burges 著;
  11. 统计学习方法》,李航著(第7章有不少内容参考自支持向量机导论一书,不过,可以翻翻看看);
  12. 《统计自然语言处理》,宗成庆编著,第十二章、文本分类;
  13. SVM入门系列,Jasper:http://www.blogjava.net/zhenandaci/category/31868.html
  14. 最近邻决策和SVM数字识别的实现和比较,作者不详;
  15. 斯坦福大学机器学习课程原始讲义:http://www.cnblogs.com/jerrylead/archive/2012/05/08/2489725.html
  16. 斯坦福机器学习课程笔记:http://www.cnblogs.com/jerrylead/tag/Machine%20Learning/
  17. http://www.cnblogs.com/jerrylead/archive/2011/03/13/1982639.html
  18. http://www.cnblogs.com/jerrylead/archive/2011/03/18/1988419.html
  19. 数据挖掘掘中所需的概率论与数理统计知识、上
  20. 关于机器学习方面的文章,可以读读:http://www.cnblogs.com/vivounicorn/category/289453.html
  21. 数学系教材推荐:http://blog.sina.com.cn/s/blog_5e638d950100dswh.html
  22. 《神经网络与机器学习(原书第三版)》,[加] Simon Haykin 著;
  23. 正态分布的前世今生:http://t.cn/zlH3Ygc
  24. 数理统计学简史》,陈希孺院士著;
  25. 《最优化理论与算法(第2版)》,陈宝林编著;
  26. A Gentle Introduction to Support Vector Machines in Biomedicinehttp://www.nyuinformatics.org/downloads/supplements/SVM_Tutorial_2010/Final_WB.pdf,此PPT很赞,除了对引入拉格朗日对偶变量后的凸二次规划问题的深入度不够之外,其它都挺好,配图很精彩,本文有几张图便引自此PPT中
  27. 来自卡内基梅隆大学carnegie mellon university(CMU)的讲解SVM的PPT:http://www.autonlab.org/tutorials/svm15.pdf
  28. 发明libsvm的台湾林智仁教授06年的机器学习讲义SVM:http://wenku.baidu.com/link?url=PWTGMYNb4HGUrUQUZwTH2B4r8pIMgLMiWIK1ymVORrds_11VOkHwp-JWab7IALDiors64JW_6mD93dtuWHwFWxsAk6p0rzchR8Qh5_4jWHC
  29. http://staff.ustc.edu.cn/~ketang/PPT/PRLec5.pdf
  30. Introduction to Support Vector Machines (SVM),By Debprakash Patnai M.E (SSA),https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CCwQFjAA&url=http%3a%2f%2fwww%2epws%2estu%2eedu%2etw%2fccfang%2findex%2efiles%2fAI%2fAI%26ML-Support%2520Vector%2520Machine-1%2eppt&ei=JRR6UqT5C-iyiQfWyIDgCg&usg=AFQjCNGw1fTbpH4ltQjjmx1d25ZqbCN9nA
  31. 多人推荐过的libsvm:http://www.csie.ntu.edu.tw/~cjlin/libsvm/
  32. 《machine learning in action》,中文版为《机器学习实战》;
posted @ 2013-09-27 11:14  kobeshow  阅读(1589)  评论(0编辑  收藏  举报