机器学习实战笔记:支持向量机

匿名 (未验证) 提交于 2019-12-03 00:22:01

SVM的一般流程:

  1. 收集数据;
  2. 准备数据:数值型
  3. 分析数据:有助于可视化分隔超平面
  4. 训练算法;
  5. 测试算法;
  6. 使用算法;

简化的SMO算法:

创建alpha向量并将其初始化为0向量 while 迭代次数<最大迭代次数:(外循环)     对数据集中每个数据向量(内循环):         if 该数据向量可以被优化:             随机选择另外一个数据向量             同时优化这两个向量             if 这两个向量都不能被优化:                 退出内循环 if 所有向量都没被优化:     增加迭代数目,继续下一次循环
def somSimple(dataMatIn,classLabels,C,toler,maxIter):   #5个输入参数:数据集,类别标签,常数C,容错率和最大循环次数     dataMatrix=mat(dataMatIn)     labelMat=mat(classLabels).transpose()     b=0     m,n=shape(dataMatrix)  #m是样本个数,n是特征数     alphas=mat(zeros((m,1)))  #alpha矩阵,alpha个数等于样本个数     iter=0  #用于存储在没有任何alpha改变的情况下遍历数据集的次数     while(iter<maxIter):         alphaPairsChanged=0   #记录alpha是否已经进行了优化         for i in range(m):             fXi=float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T))+b  #multiply是对应元素相乘,*就是矩阵乘法 fXi是预测的f(x),是预测类别             Ei=fXi-float(labelMat[i])  #预测类别和真实标签的差值即为误差             if((labelMat[i]*Ei<-toler)and(alphas[i]<C))or((labelMat[i]*Ei>toler)and(alphas[i]>0)): #如果i样本的预测误差很大且αi不等于0或C就可以进行优化                 j=selectJrand(i,m)  #随机选择一个αj                 fXj=float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T))+b  #计算αj的预测值                 Ej=fXj-float(labelMat[j])  #计算αj的误差                 alphaIold=alphas[i].copy()  #将现在的alpha[i]和alpha[j]相应的保存                 alphaJold=alphas[j].copy()                 #L和H用于将alpha[j]调整到0和C之间                 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:  #如果L==H就不做任何调整,直接做下一次的for循环                     print("L==H")                     continue                 #计算δ,δ是αj的最优修改量,如果δ>=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                 #用求出的L和H对αj进行调整                 alphas[j]-=labelMat[j]*(Ei-Ej)/eta                 alphas[j]=clipAlpha(alphas[j],H,L)                 if(abs(alphas[j]-alphaJold)<0.00001):  #如果αj调整过于轻微,则跳出当前的循环                     print("j not moving enough")                     continue                 alphas[i]+=labelMat[j]*labelMat[i]*(alphaJold-alphas[j])  #αi也做调整,大小同αj方向相反                 #为αi和αj设置一个常数项b                 #由f(x)=∑(m,i=1)αi*yi*xi.T*x+b,可以求得常数项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  #表示该alpha对进行了优化                 print("iter:{}".format(iter),"i:{}".format(i),",pairs changed{}".format(alphaPairsChanged))         if(alphaPairsChanged==0):   #遍历数据集结束后,没有一对alpha进行了优化,则要将遍历次数加一             iter+=1         else:             iter=0         print("iteration number:{}".format(iter))   #否则遍历结束     return b,alphas  #返回SMO求得的b和α值

  1. 在所有数据集上进行单遍扫描
  2. 在非边界(不等于边界0或C的alpha值)alpha中实现单遍扫描:需要先建立这些alpha值得列表,然后再对这个表进行遍历,同时会跳过那些已知的不会改变的alpha值。


"""建立一个类来保存所有数据的值""" 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))  #用来存储误差E,eCache的第一列给出eCache是否有效地标志位,第二列给出的实际的E值  """辅助函数用来计算误差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  """用于选择第二个alpha(内循环的alpha)""" """目标是选择合适的第二个alpha保证在每次优化中采用最大步长""" def selectJ(i,oS,Ei):     maxK=-1     maxDeltaE=0     Ej=0     oS.eCache[i]=[1,Ei]   #设置Ei有效(1)     validEcacheList=nonzero(oS.eCache[:,0].A)[0]   #构造一个非零列表".A"将矩阵转化为列表,非零E值对应的alpha值     if(len(validEcacheList))>1:         for k in validEcacheList:   #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:  #如果是第一次循环就随机选择一个alpha         j=selectJ(i,oS.m)         Ej=calcEk(oS,j)     return j,Ej  """误差值并存入缓存,在对alpha优化时会用到""" def updateEk(oS,k):     Ek=calcEk(oS,k)     oS.eCache[k]=[1,Ek]  """调整大于H或小于L的alpha值""" def clipAlpha(aj,H,L):     if aj>H:         aj=H     if L>aj:         aj=L     return aj  """完整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.alphas[i]):             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 mving 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]-alphaJold)*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]-alphaJold)*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  """完整的SMO外循环代码""" def smoP(dataMatIn,classLabels,C,toler,maxIter,kTup=('lin',0)):     oS=optStruct(mat(dataMatIn),mat(classLabels).transpose(),C,toler)     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:{}".format(iter),"i:{}".format(i),"pairs changed:{}".format(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:{}".format(iter),"i:{}".format(i),"pairs changed:{}".format(alphaPairsChanged))                 iter+=1         if entireSet:             entireSet=False         elif(alphaPairsChanged==0):             entireSet=True         print("iteration number:{}".format(iter))     return oS.b,oS.alphas 

以上我们就得到了SVM中的α,通过如下的函数,就可以求得w,并基于alpha获得分隔超平面。

"""求w""" 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
对于测试样本如果y=wx+b的结果大于0则属于正类,如果小于0则属于负类。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!