ToB企服应用市场:ToB评测及商务社交产业平台

标题: 机器学习实战-支持向量机 [打印本页]

作者: 嚴華    时间: 2022-10-17 23:19
标题: 机器学习实战-支持向量机
1.支持向量机简介





2.线性可分支持向量机

如下图,要找到离黑色实线最近的样本点,使得该点到黑线的距离达到最大,该距离可以用我们以前学过的点到直线的距离公式来求

最大间隔分离超平面

3.SVM目标函数的求解


由目标函数构造拉格朗日函数,下图的变量写错了应该是L(w,b,a)

然后对拉格朗日求解

3.1对alpha求最大值


3.2举例求解


求解步骤如下图:

4.线性支持向量机

4.1概念

有些样本点无论怎么分,都不能用一条直线将样本点完全分开,但是对于绝大多数样本点还是可以通过超平面给分隔开。
4.2目标函数的优化



由上图可知得到的结果与线性可分向量机得到的结果可以说是完全一样,唯一的区别就是约束条件不太一样

5.非线性支持向量机


由上图可知在原空间中,用一个椭圆曲线将圆点和xx分开,总所周知,椭圆的方程为x^2 / a^2 + y^2 / b^2,为了使得其与机器学习相关,可以再左右添加一个参数c,然后用w1表示1/c* a^2 , w2表示1 / c*b^2, b表示-c,则椭圆方程可以简化为w1 * x1^2 + w2 * x2^2 + b=0,坐在新空间中存在一种对应关系,使得z1= x1^2 ,z2= x2^2 ,则椭圆方程可以进一步简化为w1 * Z1+ w2 * Z2+b=0,这个过程也被称为核技巧,也就是将元空间的曲线方程,转化为新空间的线性可分的直线方程
由下图可知,当o(x)取不同维数的函数,经过映射之后得到的核函数都是一样的。所以核函数的原理就是通过在低维空间的计算,而去完成在高维空间所能完成的事情


5.SMO算法推导结果

如果不知道推导的过程其实也不影响后面的学习,知道每个公式在代码里面怎么用就可以了
5.1SMO算法简易版代码实现
  1. from numpy import *
  2. import matplotlib as mpl
  3. import matplotlib.pyplot as plt
  4. from matplotlib.patches import Circle
  5. def loadDataSet(fileName):
  6.     dataMat=[];labelMat=[]#数据集以及标签集分开存储
  7.     fr=open(fileName)
  8.     for line in fr.readlines():#读取文件的每一行数据
  9.         lineArr=line.strip().split('\t')#每个数据以空格分开
  10.         dataMat.append([float(lineArr[0]),float(lineArr[1])])#将每一行的第一个数据和第二个数据存放在数据集中
  11.         labelMat.append(float(lineArr[2]))#每一行的第三个数据存放在标签集中
  12.     return dataMat,labelMat
  13. #alpha的选取,随机选择一个不等于i值得j
  14. def selectJrand(i,m):#i的值就是当前选定的alpha的值
  15.     j=i
  16.     while(j==i):
  17.         j=int(random.uniform(0,m))
  18.     return j
  19. #进行剪辑
  20. def clipAlpha(aj,H,L):
  21.     if aj>H:
  22.         aj=H
  23.     if L>aj:
  24.         aj=L
  25.     return aj
  26. #dataMatIn就是之前讲的公式里的x,classLabels就是之前公式里的y
  27. #toler误差值达到多少时可以停止,maxIter迭代次数达到多少是可以停止
  28. def smoSimple(dataMatIn,classLabels,C,toler,maxIter):
  29.     dataMatrix=mat(dataMatIn);labelMat=mat(classLabels).transpose()
  30.     #初始化b为0
  31.     b=0
  32.     #获取数据维度
  33.     m,n=shape(dataMatrix)
  34.     #初始化所有alpha为0
  35.     alphas=mat(zeros((m,1)))
  36.     iter=0
  37.     #迭代求解
  38.     while(iter<maxIter):
  39.         alphaPairsChanged=0
  40.         for i in range(m):
  41.             #计算g(xi)
  42.             gXi= float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T))+b
  43.             #计算Ei
  44.             Ei=gXi-float(labelMat[i])
  45.             if((labelMat[i]*Ei<-toler) and (alphas[i]<C)) or ((labelMat[i]*Ei>toler) and (alphas[i]>0)):
  46.                 #随机选择一个待优化的alpha(先随机出alpha下标)
  47.                 j=selectJrand(i,m)
  48.                 #计算g(xj)
  49.                 gXj = float(multiply(alphas, labelMat).T * (dataMatrix * dataMatrix[j, :].T)) + b
  50.                 #计算Ej
  51.                 Ej = gXj - float(labelMat[j])
  52.                 #把原来的恶alpha的值复制一份,作为old的值
  53.                 alphaIold=alphas[i].copy();alpaJold=alphas[j].copy()
  54.                 #计算上下界
  55.                 if(labelMat[i]!=labelMat[j]):
  56.                     L=max(0,alphas[j]-alphas[i])
  57.                     H=min(C,C+alphas[j]-alphas[i])
  58.                 else:
  59.                     L=max(0,alphas[j]+alphas[i]-C)
  60.                     H=min(C,alphas[j]-alphas[i])
  61.                 if L==H:
  62.                     print("L==H")
  63.                     continue
  64.                 #计算eta:在公式里就是计算K11+K22-2K12,但是这里算的负的eta
  65.                 eta=2*dataMatrix[i,:]*dataMatrix[j,:].T-dataMatrix[i,:]*dataMatrix[i,:].T-dataMatrix[j,:]*dataMatrix[j,:].T
  66.                 if eta>=0:
  67.                     print("eta>=0")
  68.                     continue
  69.                 #计算alpha[j],为了和公式对应把j看出2
  70.                 alphas[j]-=labelMat[j]*(Ei-Ej)/eta
  71.                 #剪辑alphas[j],为了和公式对应把j看成2
  72.                 alphas[j]=clipAlpha(alphas[j],H,L)
  73.                 if(abs(alphas[j]-alpaJold)<0.00001):
  74.                     print("j not moving enough")
  75.                     continue
  76.                 #计算alphas[i],为了和公式对应把i看成1
  77.                 alphas[i] += labelMat[i]*labelMat[j]*(alpaJold-alphas[j])
  78.                 #计算b1
  79.                 b1=-Ei-labelMat[i]*(dataMatrix[i,:]*dataMatrix[i,:].T)*(alphas[i]-alphaIold)-labelMat[j]*(dataMatrix[j,:]*dataMatrix[i,:].T)*(alphas[j]-alpaJold)+b
  80.                 #计算b2
  81.                 b2=-Ej-labelMat[i]*(dataMatrix[i,:]*dataMatrix[j,:].T)*(alphas[i]-alphaIold)-labelMat[j]*(dataMatrix[j,:]*dataMatrix[j,:].T)*(alphas[j]-alpaJold)+b
  82.                 #求解b
  83.                 if(0<alphas[i]) and (C>alphas[j]):
  84.                     b = b1
  85.                 elif (0<alphas[j]) and (C>alphas[j]):
  86.                     b = b2
  87.                 else:
  88.                     b=(b1+b2)/2.0
  89.                 alphaPairsChanged+=1
  90.                 print("iter:%d i:%d,pairs changed %d" %(iter,i,alphaPairsChanged))
  91.         if(alphaPairsChanged==0):
  92.             iter+=1
  93.         else:
  94.             iter=0
  95.             print("iteration number:%d" %iter)
  96.     return b,alphas
  97. #计算w的值
  98. def calcWs(dataMat, labelMat, alphas):
  99.     X=mat(dataMat);labelMat=mat(labelMat).transpose()
  100.     m,n=shape(X)
  101.     #初始化w都为1
  102.     w=zeros((n,1))
  103.     #循环计算
  104.     for i in range(m):
  105.         w+=multiply(alphas[i]*labelMat[i],X[i,:].T)
  106.     return w
  107. #画图
  108. def showClassifer(dataMat, labelMat, b,alphas,w):
  109.     fig=plt.figure()
  110.     ax=fig.add_subplot(111)
  111.     cm_dark=mpl.colors.ListedColormap(['g','r'])
  112.     ax.scatter(array(dataMat)[:,0],array(dataMat)[:,1],c=array(labelMat).squeeze(),cmap=cm_dark,s=30)
  113.     #画决策平面
  114.     x=arange(-2.0,12.0,0.1)
  115.     y=(-w[0]*x-b)/w[1]
  116.     ax.plot(x,y.reshape(-1,1))
  117.     ax.axis([-2,12,-8,6])
  118.     #画支持向量
  119.     alphas_non_zeros_index=where(alphas>0)
  120.     for i in alphas_non_zeros_index[0]:
  121.         circle= Circle((dataMat[i][0],dataMat[i][1]),0.2,facecolor='none',edgecolor=(0,0.8,0.8),linewidth=3,alpha=0.5)
  122.         ax.add_patch(circle)
  123.     plt.show()
  124. if __name__ == '__main__':
  125.     dataMat, labelMat = loadDataSet('testSet.txt')
  126.     b,alphas=smoSimple(dataMat,labelMat,0.6,0.001,40)
  127.     w = calcWs(dataMat, labelMat, alphas)
  128.     showClassifer(dataMat, labelMat, b,alphas,w)
复制代码
画决策平面
  1. x=arange(-2.0,12.0,0.1)
  2. y=(-w[0]*x-b)/w[1]
  3. ax.plot(x,y.reshape(-1,1))
  4. ax.axis([-2,12,-8,6])
  5. plt.show()
复制代码

画支持向量
  1. alphas_non_zeros_index=where(alphas>0)
  2. for i in alphas_non_zeros_index[0]:
  3.     circle= Circle((dataMat[i][0],dataMat[i][1]),0.2,facecolor='none',edgecolor=(0,0.8,0.8),linewidth=3,alpha=0.5)
  4.     ax.add_patch(circle)
  5. plt.show()
复制代码

5.2引入核函数
  1. from numpy import *
  2. import matplotlib as mpl
  3. import matplotlib.pyplot as plt
  4. from matplotlib.patches import Circle
  5. class optStruct:
  6.     """
  7.     数据结构,维护所有需要操作的值
  8.     Parameters:
  9.         dataMatIn - 数据矩阵
  10.         classLabels - 数据标签
  11.         C - 松弛变量
  12.         toler - 容错率
  13.     """
  14.     def __init__(self, dataMatIn, classLabels, C, toler, kTup):
  15.         self.X = dataMatIn                                #数据矩阵
  16.         self.labelMat = classLabels                        #数据标签
  17.         self.C = C                                         #松弛变量
  18.         self.tol = toler                                 #容错率
  19.         self.m = shape(dataMatIn)[0]                 #数据矩阵行数
  20.         self.alphas = mat(zeros((self.m,1)))         #根据矩阵行数初始化alpha参数为0
  21.         self.b = 0                                         #初始化b参数为0
  22.         self.eCache = mat(zeros((self.m,2)))         #根据矩阵行数初始化虎误差缓存,第一列为是否有效的标志位,第二列为实际的误差E的值。
  23.         self.K = mat(zeros((self.m, self.m)))  # 初始化核K
  24.         for i in range(self.m):  # 计算所有数据的核K
  25.             self.K[:, i] = kernelTrans(self.X, self.X[i, :], kTup)
  26. def loadDataSet(fileName):
  27.     dataMat=[];labelMat=[]#数据集以及标签集分开存储
  28.     fr=open(fileName)
  29.     for line in fr.readlines():#读取文件的每一行数据
  30.         lineArr=line.strip().split('\t')#每个数据以空格分开
  31.         dataMat.append([float(lineArr[0]),float(lineArr[1])])#将每一行的第一个数据和第二个数据存放在数据集中
  32.         labelMat.append(float(lineArr[2]))#每一行的第三个数据存放在标签集中
  33.     return dataMat,labelMat
  34. def calcEk(oS, k):
  35.     """
  36.     计算误差
  37.     Parameters:
  38.         oS - 数据结构
  39.         k - 标号为k的数据
  40.     Returns:
  41.         Ek - 标号为k的数据误差
  42.     """
  43.     fXk = float(multiply(oS.alphas, oS.labelMat).T * oS.K[:, k] + oS.b)
  44.     Ek = fXk - float(oS.labelMat[k])
  45.     return Ek
  46. def selectJ(i, oS, Ei):
  47.     """
  48.     内循环启发方式2
  49.     Parameters:
  50.         i - 标号为i的数据的索引值
  51.         oS - 数据结构
  52.         Ei - 标号为i的数据误差
  53.     Returns:
  54.         j, maxK - 标号为j或maxK的数据的索引值
  55.         Ej - 标号为j的数据误差
  56.     """
  57.     maxK = -1; maxDeltaE = 0; Ej = 0    #初始化
  58.     oS.eCache[i] = [1,Ei]     #设为有效                                 #根据Ei更新误差缓存
  59.     validEcacheList = nonzero(oS.eCache[:,0].A)[0] #返回误差不为0的数据的索引值
  60.     if (len(validEcacheList)) > 1:  #有不为0的误差
  61.         for k in validEcacheList:  #迭代所有有效的缓存,找到误差最大的E
  62.             if k == i: continue   #不计算i,浪费时间
  63.             Ek = calcEk(oS, k)    #计算Ek
  64.             deltaE = abs(Ei - Ek)    #计算|Ei-Ek|
  65.             if (deltaE > maxDeltaE):    #找到maxDeltaE
  66.                 maxK = k; maxDeltaE = deltaE; Ej = Ek
  67.         return maxK, Ej     #返回maxK,Ej
  68.     else:          #没有不为0的误差
  69.         j = selectJrand(i, oS.m)   #随机选择alpha_j的索引值
  70.         Ej = calcEk(oS, j)  #计算Ej
  71.     return j, Ej
  72. #跟新缓存
  73. def updateEk(oS, k):
  74.     """
  75.     计算Ek,并更新误差缓存
  76.     Parameters:
  77.         oS - 数据结构
  78.         k - 标号为k的数据的索引值
  79.     Returns:
  80.         无
  81.     """
  82.     Ek = calcEk(oS, k)                                        #计算Ek
  83.     oS.eCache[k] = [1,Ek]                                    #更新误差缓存
  84. def innerL(i,oS):
  85.     """
  86.         优化的SMO算法
  87.         Parameters:
  88.             i - 标号为i的数据的索引值
  89.             oS - 数据结构
  90.         Returns:
  91.             1 - 有任意一对alpha值发生变化
  92.             0 - 没有任意一对alpha值发生变化或变化太小
  93.         """
  94.     # 步骤1:计算误差Ei
  95.     Ei = calcEk(oS, i)
  96.     # 优化alpha,设定一定的容错率。
  97.     if ((oS.labelMat[i] * Ei < -oS.tol) and (oS.alphas[i] < oS.C)) or (
  98.             (oS.labelMat[i] * Ei > oS.tol) and (oS.alphas[i] > 0)):
  99.         # 使用内循环启发方式2选择alpha_j,并计算Ej
  100.         j, Ej = selectJ(i, oS, Ei)
  101.         # 保存更新前的aplpha值,使用深拷贝
  102.         alphaIold = oS.alphas[i].copy()
  103.         alphaJold = oS.alphas[j].copy()
  104.         # 步骤2:计算上下界L和H
  105.         if (oS.labelMat[i] != oS.labelMat[j]):
  106.             L = max(0, oS.alphas[j] - oS.alphas[i])
  107.             H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
  108.         else:
  109.             L = max(0, oS.alphas[j] + oS.alphas[i] - oS.C)
  110.             H = min(oS.C, oS.alphas[j] + oS.alphas[i])
  111.         if L == H:
  112.             print("L==H")
  113.             return 0
  114.         # 步骤3:计算eta
  115.         eta = 2.0 * oS.K[i, j] - oS.K[i, i] - oS.K[j, j]
  116.         if eta >= 0:
  117.             print("eta>=0")
  118.             return 0
  119.         # 步骤4:更新alpha_j
  120.         oS.alphas[j] -= oS.labelMat[j] * (Ei - Ej) / eta
  121.         # 步骤5:修剪alpha_j
  122.         oS.alphas[j] = clipAlpha(oS.alphas[j], H, L)
  123.         # 更新Ej至误差缓存
  124.         updateEk(oS, j)
  125.         if (abs(oS.alphas[j] - alphaJold) < 0.00001):
  126.             print("alpha_j变化太小")
  127.             return 0
  128.         # 步骤6:更新alpha_i
  129.         oS.alphas[i] += oS.labelMat[j] * oS.labelMat[i] * (alphaJold - oS.alphas[j])
  130.         # 更新Ei至误差缓存
  131.         updateEk(oS, i)
  132.         # 步骤7:更新b_1和b_2
  133.         b1 = oS.b - Ei - oS.labelMat[i] * (oS.alphas[i] - alphaIold) * oS.K[i, i] - oS.labelMat[j] * (
  134.                     oS.alphas[j] - alphaJold) * oS.K[i, j]
  135.         b2 = oS.b - Ej - oS.labelMat[i] * (oS.alphas[i] - alphaIold) * oS.K[i, j] - oS.labelMat[j] * (
  136.                     oS.alphas[j] - alphaJold) * oS.K[j, j]
  137.         # 步骤8:根据b_1和b_2更新b
  138.         if (0 < oS.alphas[i]) and (oS.C > oS.alphas[i]):
  139.             oS.b = b1
  140.         elif (0 < oS.alphas[j]) and (oS.C > oS.alphas[j]):
  141.             oS.b = b2
  142.         else:
  143.             oS.b = (b1 + b2) / 2.0
  144.         return 1
  145.     else:
  146.         return 0
  147. #alpha的选取,随机选择一个不等于i值得j
  148. def selectJrand(i,m):#i的值就是当前选定的alpha的值
  149.     j=i
  150.     while(j==i):
  151.         j=int(random.uniform(0,m))
  152.     return j
  153. #进行剪辑
  154. # aj - alpha值
  155. # H - alpha上限
  156. # L - alpha下限
  157. def clipAlpha(aj,H,L):
  158.     if aj>H:
  159.         aj=H
  160.     if L>aj:
  161.         aj=L
  162.     return aj
  163. #dataMatIn就是之前讲的公式里的x,classLabels就是之前公式里的y
  164. #toler误差值达到多少时可以停止,maxIter迭代次数达到多少是可以停止
  165. def smoSimple(dataMatIn,classLabels,C,toler,maxIter,kTup = ('lin',0)):
  166.     """
  167.         完整的线性SMO算法
  168.         Parameters:
  169.             dataMatIn - 数据矩阵
  170.             classLabels - 数据标签
  171.             C - 松弛变量
  172.             toler - 容错率
  173.             maxIter - 最大迭代次数
  174.             kTup - 包含核函数信息的元组
  175.         Returns:
  176.             oS.b - SMO算法计算的b
  177.             oS.alphas - SMO算法计算的alphas
  178.         """
  179.     oS = optStruct(mat(dataMatIn), mat(classLabels).transpose(), C, toler, kTup)                #初始化数据结构iter = 0  # 初始化当前迭代次数
  180.     iter=0
  181.     entireSet = True
  182.     alphaPairsChanged = 0
  183.     while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):  # 遍历整个数据集都alpha也没有更新或者超过最大迭代次数,则退出循环
  184.         alphaPairsChanged = 0
  185.         if entireSet:  # 遍历整个数据集
  186.             for i in range(oS.m):
  187.                 alphaPairsChanged += innerL(i, oS)  # 使用优化的SMO算法
  188.                 print("全样本遍历:第%d次迭代 样本:%d, alpha优化次数:%d" % (iter, i, alphaPairsChanged))
  189.             iter += 1
  190.         else:  # 遍历非边界值
  191.             nonBoundIs = nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]  # 遍历不在边界0和C的alpha
  192.             for i in nonBoundIs:
  193.                 alphaPairsChanged += innerL(i, oS)
  194.                 print("非边界遍历:第%d次迭代 样本:%d, alpha优化次数:%d" % (iter, i, alphaPairsChanged))
  195.             iter += 1
  196.         if entireSet:  # 遍历一次后改为非边界遍历
  197.             entireSet = False
  198.         elif (alphaPairsChanged == 0):  # 如果alpha没有更新,计算全样本遍历
  199.             entireSet = True
  200.         print("迭代次数: %d" % iter)
  201.     return oS.b, oS.alphas
  202. #计算w的值
  203. def calcWs(dataMat, labelMat, alphas):
  204.     X=mat(dataMat);labelMat=mat(labelMat).transpose()
  205.     m,n=shape(X)
  206.     #初始化w都为1
  207.     w=zeros((n,1))
  208.     #循环计算
  209.     for i in range(m):
  210.         w+=multiply(alphas[i]*labelMat[i],X[i,:].T)
  211.     return w
  212. #核函数
  213. def kernelTrans(X,A,kTup):
  214.     m,n=shape(X)
  215.     K=mat(zeros((m,1)))
  216.     if kTup[0]=='lin':#线性核
  217.         K=X*A.T
  218.     elif kTup[0] == 'rbf':#高斯核
  219.         for j in range(m):
  220.             deltaRow = X[j,:]-A
  221.             K[j]=deltaRow*deltaRow.T
  222.         K = exp(K/(-2*kTup[1]**2))
  223.     else:
  224.         raise NameError("Houston we Have a Problem--\ That Kernel is not recognized")
  225.     return K
  226. #画图
  227. def showClassifer(dataMat, labelMat, b,alphas,w):
  228.     fig=plt.figure()
  229.     ax=fig.add_subplot(111)
  230.     cm_dark=mpl.colors.ListedColormap(['g','r'])
  231.     ax.scatter(array(dataMat)[:,0],array(dataMat)[:,1],c=array(labelMat).squeeze(),cmap=cm_dark,s=30)
  232.     #画决策平面
  233.     # x=arange(-2.0,12.0,0.1)
  234.     # y=(-w[0]*x-b)/w[1]
  235.     # ax.plot(x,y.reshape(-1,1))
  236.     # ax.axis([-2,12,-8,6])
  237.     #画支持向量
  238.     alphas_non_zeros_index=where(alphas>0)
  239.     for i in alphas_non_zeros_index[0]:
  240.         circle= Circle((dataMat[i][0],dataMat[i][1]),0.03,facecolor='none',edgecolor=(0,0.8,0.8),linewidth=3,alpha=0.5)
  241.         ax.add_patch(circle)
  242.     plt.show()
  243. if __name__ == '__main__':
  244.     dataMat, labelMat = loadDataSet('testSetRBF.txt')
  245.     b,alphas=smoSimple(dataMat,labelMat,0.6,0.001,40)
  246.     w = calcWs(dataMat, labelMat, alphas)
  247.     showClassifer(dataMat, labelMat, b,alphas,w)
复制代码
运行结果


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4