SVM --从“原理”到实现

来源:互联网 发布:压缩感知重构算法 编辑:程序博客网 时间:2024/05/29 15:18

零.

        本文所有代码均能在我 github上的 DML 找到,顺便求点Star

一.引入

        从一开始接触机器学习,就感觉SVM(支持向量机 Support Vector Machine)就是高端大气上档次的代名词啊,在深度学习出来之前一直都力压ANN一头,是应用得最好的算法了,本文借着实现DML的机会实现一下。


二.原理

       SVM的文章先不说论文和书啦,博文也是不少,所以我觉得实在不用在这里赘述这些了,这是标题里原理打引号的原因……

       现推荐这些博客里讲的SVM,我认为写得是极好的:

                     JerryLead 的五篇  很好理解

                     july的博客 ,还是那啥的风格,大长篇……事实上我没看完这个…只是觉得挺全的


       除此之外还可以去看看《统计学习方法》的内容


三.过程.

       接下来讲一下我选择实现的SOM的基本过程吧:

       SMO是(Sequential minimal optimization),其优化过程就是每次选取两个优化变量α(i)和α(j),然后更新α,直到满足停机条件为止:

       我基本上是按照《统计学习方法》来实现的,所以也就直接贴一下这上面的过程:

      

         至于alpha的选择,第一个变量的选择是要选择违反KKT条件的:

         

          代码的这里不是直接这样实现的,因为用了Ei,为了方便形式有所改变,但你可以推一下是没问题的:

         第二个变量的选择是选择使|Ei-Ej|最大的,然后按照一定的规则计算和更新就行了


四.实现与测试

        这是DML/SVM/svm.py

[python] view plaincopy在CODE上查看代码片派生到我的代码片
  1. from __future__ import division  
  2. import numpy as np  
  3. import scipy as sp  
  4. from dml.tool import sign  
  5. import matplotlib.pyplot as plt  
  6. from numpy.random import random  
  7. import random as rd  
  8. class SVMC:  
  9.     def Gauss_kernel(x,z,sigma=2):  
  10.         return np.exp(-np.sum((x-z)**2)/(2*sigma**2))  
  11.     def Linear_kernel(x,z):  
  12.         return np.sum(x*z)  
  13.     def __init__(self,X,y,C=10,tol=0.01,kernel=Linear_kernel):  
  14.         ''''' 
  15.             X is n N*M matrix where N is #features and M is #train_case 
  16.         '''  
  17.         self.X=np.array(X)  
  18.         self.y=np.array(y).flatten(1)  
  19.         self.tol=tol  
  20.         self.N,self.M=self.X.shape  
  21.         self.C=C  
  22.         self.kernel=kernel  
  23.         self.alpha=np.zeros((1,self.M)).flatten(1)  
  24.         self.supportVec=[]  
  25.         self.b=0  
  26.         self.E=np.zeros((1,self.M)).flatten(1)  
  27.     def fitKKT(self,i):  
  28.         if ((self.y[i]*self.E[i]<-self.tol) and (self.alpha[i]<self.C)) or \  
  29.         (((self.y[i]*self.E[i]>self.tol)) and (self.alpha[i]>0)):  
  30.             return False  
  31.         return True   
  32.   
  33.           
  34.     def select(self,i):  
  35.         pp=np.nonzero((self.alpha>0))[0]  
  36.         if (pp.size>0):  
  37.             j=self.findMax(i,pp)  
  38.         else:  
  39.             j=self.findMax(i,range(self.M))  
  40.         return j  
  41.   
  42.     def randJ(self,i):  
  43.         j=rd.sample(range(self.M),1)  
  44.         while j==i:  
  45.             j=rd.sample(range(self.M),1)  
  46.         return j[0]  
  47.     def findMax(self,i,ls):  
  48.         ansj=-1  
  49.         maxx=-1  
  50.         self.updateE(i)  
  51.         for j in ls:  
  52.             if i==j:continue  
  53.             self.updateE(j)  
  54.             deltaE=np.abs(self.E[i]-self.E[j])  
  55.             if deltaE>maxx:  
  56.                 maxx=deltaE  
  57.                 ansj=j  
  58.         if ansj==-1:  
  59.             return self.randJ(i)  
  60.         return ansj  
  61.   
  62.     def InerLoop(self,i,threshold):  
  63.         j=self.select(i)  
  64.         #print i,j,self.y[i]==self.y[j],self.alpha[i],self.alpha[j],self.C  
  65.         #print self.y[i],self.y[j]  
  66.         self.updateE(j)  
  67.         self.updateE(i)  
  68.         if (self.y[i]==self.y[j]):  
  69.             L=max(0,self.alpha[i]+self.alpha[j]-self.C)  
  70.             H=min(self.C,self.alpha[i]+self.alpha[j])  
  71.         else:  
  72.             L=max(0,self.alpha[j]-self.alpha[i])  
  73.             H=min(self.C,self.C+self.alpha[j]-self.alpha[i])  
  74.         #print L,H  
  75.   
  76.         a2_old=self.alpha[j]  
  77.         a1_old=self.alpha[i]  
  78.         #print i,j  
  79.         #if L==H:  
  80.         #   return True  
  81.           
  82.         K11=self.kernel(self.X[:,i],self.X[:,i])  
  83.         K22=self.kernel(self.X[:,j],self.X[:,j])  
  84.         K12=self.kernel(self.X[:,i],self.X[:,j])  
  85.         eta=K11+K22-2*K12  
  86.         if eta==0:  
  87.             return True  
  88.           
  89.         self.alpha[j]=self.alpha[j]+self.y[j]*(self.E[i]-self.E[j])/eta  
  90.           
  91.         if self.alpha[j]>H:  
  92.             self.alpha[j]=H  
  93.         elif self.alpha[j]<L:  
  94.             self.alpha[j]=L  
  95.   
  96.         if np.abs(self.alpha[j]-a2_old)<threshold:  
  97.             #print np.abs(a2_new-self.alpha[j])  
  98.             return True  
  99.         #print np.abs(a2_new-self.alpha[j]),"improve"  
  100.         self.alpha[i]=self.alpha[i]+self.y[i]*self.y[j]*(a2_old-self.alpha[j])  
  101.         b1_new=self.b-self.E[i]-self.y[i]*K11*(self.alpha[i]-a1_old)-self.y[j]*K12*\  
  102.                 (self.alpha[j]-a2_old)  
  103.         b2_new=self.b-self.E[j]-self.y[i]*K12*(self.alpha[i]-a1_old)-self.y[j]*K22*\  
  104.                 (self.alpha[j]-a2_old)  
  105.         #print a1_new,"a1 new"  
  106.         #print a2_new,"a2 new"  
  107.         if self.alpha[i]>0 and self.alpha[i]<self.C:self.b=b1_new  
  108.         elif self.alpha[j]>0 and self.alpha[j]<self.C:self.b=b2_new  
  109.         else:   
  110.             self.b=(b1_new+b2_new)/2  
  111.   
  112.         #self.alpha[i]=a1_new  
  113.         #self.alpha[j]=a2_new  
  114.         self.updateE(j)  
  115.         self.updateE(i)  
  116.         return False  
  117.         pass  
  118.   
  119.     def updateE(self,i):  
  120.         #self.supportVec=np.nonzero((self.alpha>0))[0]  
  121.         self.E[i]=0  
  122.         for t in range(self.M):  
  123.         #for t in range(self.M):  
  124.             self.E[i]+=self.alpha[t]*self.y[t]*self.kernel(self.X[:,i],self.X[:,t])  
  125.         self.E[i]+=self.b-self.y[i]  
  126.   
  127.     def train(self,maxiter=100,threshold=0.000001):  
  128.         iters=0  
  129.         flag=False  
  130.         for i in range(self.M):  
  131.             self.updateE(i)  
  132.         while (iters<maxiter) and (not flag):  
  133.             flag=True  
  134.             temp_supportVec=np.nonzero((self.alpha>0))[0]  
  135.             iters+=1  
  136.             for i in temp_supportVec:  
  137.                 self.updateE(i)  
  138.                 if (not self.fitKKT(i)):  
  139.                     flag=flag and self.InerLoop(i,threshold)  
  140.                     #if not flag:break  
  141.             if (flag):  
  142.                 for i in range(self.M):  
  143.                     self.updateE(i)  
  144.                     if (not self.fitKKT(i)):  
  145.                         flag= flag and self.InerLoop(i,threshold)  
  146.                         #if not flag:break        
  147.               
  148.             print "the %d-th iter is running" % iters  
  149.         self.supportVec=np.nonzero((self.alpha>0))[0]  
  150.     def predict(self,x):  
  151.         w=0  
  152.         for t in self.supportVec:  
  153.             w+=self.alpha[t]*self.y[t]*self.kernel(self.X[:,t],x).flatten(1)  
  154.         w+=self.b  
  155.         return sign(w)  
  156.     def pred(self,X):  
  157.         test_X=np.array(X)  
  158.         y=[]  
  159.         for i in range(test_X.shape[1]):  
  160.             y.append(self.predict(test_X[:,i]))  
  161.         return y  
  162.     def error(self,X,y):  
  163.         py=np.array(self.pred(np.array(X))).flatten(1)  
  164.         #print y,py  
  165.         print "the #error_case is  ",np.sum(py!=np.array(y))  
  166.   
  167.     def prints_test_linear(self):  
  168.         w=0  
  169.         for t in self.supportVec:  
  170.             w+=self.alpha[t]*self.y[t]*self.X[:,t].flatten(1)  
  171.         w=w.reshape(1,w.size)  
  172.         print np.sum(sign(np.dot(w,self.X)+self.b).flatten(1)!=self.y),"errrr"  
  173.         #print w,self.b  
  174.         x1=0  
  175.         y1=-self.b/w[0][1]  
  176.         y2=0  
  177.         x2=-self.b/w[0][0]  
  178.         plt.plot([x1+x1-x2,x2],[y1+y1-y2,y2])  
  179.         #plt.plot([x1+x1-x2,x2],[y1+y1-y2-1,y2-1])  
  180.         plt.axis([0,30,0,30])  
  181.   
  182.         for i in range(self.M):  
  183.             if  self.y[i]==-1:  
  184.                 plt.plot(self.X[0,i],self.X[1,i],'or')  
  185.             elif  self.y[i]==1:  
  186.                 plt.plot(self.X[0,i],self.X[1,i],'ob')  
  187.         for i in self.supportVec:  
  188.             plt.plot(self.X[0,i],self.X[1,i],'oy')  
  189.         plt.show()  

试验一下结果:

      使用类似如下的代码:

[python] view plaincopy在CODE上查看代码片派生到我的代码片
  1. svms=SVMC(X,y,kernel=Gauss_kernel)  
  2. svms.train()  
  3. print len(svms.supportVec),"SupportVectors:"  
  4.   
  5. for i in range(len(svms.supportVec)):  
  6.     t=svms.supportVec[i]  
  7.     print svms.X[:,t]  
  8. svms.error(X,y)  
      完整的测试代码参见:DML/test/svm_test

     线性的:

     

     高斯的:你要自己写个高斯核

      

[python] view plaincopy在CODE上查看代码片派生到我的代码片
  1. def Gauss_kernel(x,z,sigma=1):  
  2.         return np.exp(-np.sum((x-z)**2)/(2*sigma**2))  
  3. svms=SVMC(X,y,kernel=Gauss_kernel)  
使用的测试数据:     

结果:


          

五.参考

      【1】《统计学习方法》

      【2】 JerryLead 的五篇

      【3】一个Java的实现 :点击打开链接

0 0