《机器学习实战》6.支持向量机(SVM)

目录

1 基于最大间隔分隔数据

2 寻找最大间隔

2.1 分类器求解的优化问题

2.2 SVM应用的一般流程

3 SMO高效优化算法

3.1 Platt的SMO算法

3.2应用简化版SMO算法处理小规模数据集

4 利用完整Platt SMO算法加速优化

 5 在复杂数据上应用核函数

5.1 利用核函数将数据映射到高维空间

5.2径向基核函数

5.3 在测试中用到核函数

 6 手写识别问题回顾

 7 本章小结


本章涉及到的向相关代码和数据

SVM有很多种实现方法,但是这里学习最流行的一种,即序列最小化(SMO)算法。在此之后,将介绍如何使用一种称为核函数的方式奖SVM拓展到更多数据集上

1 基于最大间隔分隔数据

优点:泛化错误率低,计算开销不大,结果易解释  

缺点:对参数调节喝核函数的选择敏感,原始分类器不加修饰仅适用于处理二类问题  

适用数据类型:数值型数据与标称型数据  

支持向量就是离分割超平面最近的那些点,然后找到最大化支持向量到分割面的距离,需要找到此问题的优化求解方法

2 寻找最大间隔

2.1 分类器求解的优化问题

使用海维赛德阶跃函数来进行分类的计算

为了找到分割最好的那一条线,我们在这里选择得到最大化支持向量的最大化间隔

2.2 SVM应用的一般流程

①收集数据:可以使用任意方法  

②准备数据:需要数值型数据  

③分析数据:有助于可视化分隔超平面  

④训练算法:SVM的大部分时间都源于训练,该过程主要实现两个参数的调优  

⑤测试算法:十分简单的计算过程就可以实现  

⑥使用算法:几乎所有的分类问题都可以使用SVM,值得一提的是,SVM本身是一个二类分类器,对多类问题应用SVM需要对代码做出一些修改  

3 SMO高效优化算法

①最小化的目标函数

②在优化过程中必须遵循约束条件

3.1 Platt的SMO算法

SMO表示序列最小化。PLatt的SMO算法是将大优化问题分解为多个小优化问题来求解。这些小优化问题往往很容易求解,并且对他们进行顺序求解的结果与将他们整体来求解的结果是完全一致的。在结果完全相同的条件下,SMO算法的求解时间就会短很多。  

SMO算法的目标是求出一系列alpha和b,一旦求出了这些alpha,就很容易计算出权重向量w,并得到分割超平面。  

SMO算法的工作原理为:每次循环中选择两个alpha进行优化处理。一旦找到一对合适的alpha,那么就增大其中一个同时减小另一个。这里所谓的合适就是指两个alpha必须要符合一定的条件,条件之一就是这两个alpha必须要在间隔边界之外,而这第二个条件则是这两个alpha还没有进行区间化处理或者不在边界上。

3.2应用简化版SMO算法处理小规模数据集

platt SMO的实现需要大量的代码。因此我们对算法进行简化处理,以便大致了解算法的基本工作思路,之后再基于简化版给出完整版。简化版虽然量少但执行速度慢。

# SMO算法中的辅助函数

# 打开文件并对其进行逐行分析,从而得到每行的类标签和整个数据矩阵
def loadDataSet(fileName):
    dataMat=[]
    labelMat=[]
    fr=open(fileName)
    # 读取文件
    # 按行读取
    for line in fr.readlines():
        # 将每一行的数据进行分割
        lineArr=line.strip().split('\t')
        # 前两列为x,y值
        dataMat.append([float(lineArr[0]),float(lineArr[1])])
        # 第三列为标签值
        labelMat.append(float(lineArr[2]))
    # 返回数据集和标签集
    return dataMat,labelMat

# 只要函数值不等于输入值i,函数就会进行随机选择
import random
#         i输入下标,m为总数
def selectJrand(i,m):
    j=i
    while(j==i):
        j=int(random.uniform(0,m))
    return j

# 用于调整大于H或是小于L的alpha的值
def clipAlpha(aj,H,L):
    # 大于H的值取H
    if aj>H:
        aj=H
    # 小于L的值取L
    if L>aj:
        aj=L
    return aj

运行上面函数读取数据

dataArr,labelArr=loadDataSet('testSet.txt')
# 可以看出这里的分类标签是1和-1,而不是1和0
labelArr

运行得到的结果为:

 先写出数据集中的二维点的分布函数

# 画出待分类数据的二维分布
import numpy as np
def plotBestFit(dataMat,labelMat):
    import matplotlib.pyplot as plt
    # getA()函数将矩阵转化为数组
    # weights=wei.getA()
    # 加载数据
    dataArr=np.array(dataMat)

    n=np.shape(dataArr)[0]
    xcord1=[]
    ycord1=[]
    xcord2=[]
    ycord2=[]
    # print(labelMat)
    for i in range(n):
        if int(labelMat[i])==1:
            xcord1.append(dataArr[i][0])
            ycord1.append(dataArr[i][1])
        else:
            xcord2.append(dataArr[i][0])
            ycord2.append(dataArr[i][1])
    fig=plt.figure()
    ax=fig.add_subplot(111)
    # print(xcord1)
    # print(ycord1)
    ax.scatter(xcord1,ycord1,s=30,c='red',marker='s')
    ax.scatter(xcord2,ycord2,s=30,c='green')
    # arange()函数用于创造等差数组
            # 起始点 终止点 步长
    # x=np.arange(-3.0,3.0,0.1)
    # y=(-weights[0]-weights[1]*x)/weights[2]
    # ax.plot(x,y)
    # 添加xy轴标签
    plt.xlabel('X1')
    plt.ylabel('X2')
    # 显示图像
    plt.show()

调用上述函数

plotBestFit(dataArr,labelArr)

得到二维点的分布结果:

接下来写SMO函数的具体实现,他的伪代码大致如下:

 创建一个alpha向量并将其初始化为0向量  

当迭代次数小于最大迭代次数时:(外循环)  

 对数据集中的每个数据向量:(内循环)  

  如果该数据向量可以被优化:  

    随机选择另外一个数据向量  

    同时优化这两个向量  

    如果两个向量都不能被优化,推出内循环  

 如果所有向量都没有被优化,增加迭代次数,继续下一次循环  

 简化版的代码如下:

# 简化版的smo算法
from numpy import *
# 构建函数时,采用通用的接口,这样就可以对算法和数据源进行组合和配对处理
#    输入参数:数据集,类别标签,常数C,容错率,取消最大的循环次数
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
    # 将数列转化为矩阵
    dataMatrix = mat(dataMatIn)
    # .transpose()函数映射坐标轴
    labelMat = mat(classLabels).transpose()
    b = 0; 
    # 得到矩阵的长和宽
    m,n = shape(dataMatrix)
    # 得到一个m行1列全部是0的矩阵
    alphas = mat(zeros((m,1)))
    iter = 0
    while (iter < maxIter):
        alphaPairsChanged = 0
        for i in range(m):
            fXi = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
            Ei = fXi - float(labelMat[i])#if checks if an example violates KKT conditions
            # 如果alpha可以更改则进入优化过程
            if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
                j = selectJrand(i,m)
                fXj = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b
                Ej = fXj - float(labelMat[j])
                alphaIold = alphas[i].copy()
                alphaJold = alphas[j].copy()
                # 保证alpha在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: print ("L==H"); continue
                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
                alphas[j] -= labelMat[j]*(Ei - Ej)/eta
                alphas[j] = clipAlpha(alphas[j],H,L)
                if (abs(alphas[j] - alphaJold) < 0.00001): print ("j not moving enough"); continue
                alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])#update i by the same amount as j
                                                                        #the update is in the oppostie direction
                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
                print ("iter: %d i:%d, pairs changed %d" % (iter,i,alphaPairsChanged))
        if (alphaPairsChanged == 0): 
            iter += 1
        else: 
            iter = 0
        print ("iteration number: %d" % iter)
    return b,alphas

调用简化版的代码:

b,alphas=smoSimple(dataArr,labelArr,0.6,0.001,40)

运行得到的结果为:

 分别查看b和支持向量的alpha的值如下

 并且写代码筛选出得到的支持向量:

shape(alphas[alphas>0])
for i in range(100):
    if alphas[i]>0.0:
        print(dataArr[i],labelArr[i])

运行得到的支持向量为5个支持向量

4 利用完整Platt SMO算法加速优化

在几百个点组成的小规模数据集上,简化版SMO算法的运行是么样问题的,但是在更大的数据集上的运行速度就会变慢。在完整版中,实现alpha的更改和代数运算的优化环节一模一样。在优化过程中唯一不同的就是选择alpha的方式。完整版的Platt SMO算法应用了一些那个提速的启发方法。  

Platt SMO算法时通过一个外循环来选择第一个alpha的值,并且其选择过程会在两种方式之间交替:一种方式是在所有数据集上进行单遍扫描,另一种方式则是在非边界alpha中实现单遍扫描。而那些所谓非边界alpha指的就是那些不等于边界0或C的alpha值。对整个数据集的扫描相当容易,而实现非边界alpha值的扫描时,首先需要建立这些alpha值的列表,然后子啊对这个表进行遍历。同时该步骤会跳过那些一职的不会改变的alpha值。  

在选择第一个alpha值的时后,算法会通过一个内循环来选择第二个alpha的值,在优化过程中,会通过最大化步长的方式来获得第二个alpha值。再简化版SMO算法中,我们会选择j之后计算错误率Ej。但在这里,我们会建立一个全局的缓存用于保存误差值,并从中选择使得步长或者说是Ei-Ej最大的alpha值  

# 建立一个数据结构来保存所有重要的数据
class optStruct:
    def __init__(self,dataMatIn,classLabels,C,toler):
        self.X=dataMatIn                      #数据集
        self.labelMat=classLabels             #标签集
        self.C=C                              #常数C
        self.tol=toler                        #容错率
        self.m=shape(dataMatIn)[0]            #数据集的列
        self.alphas=mat(zeros((self.m,1)))    #alpha矩阵
        self.b=0                              #参数b
        self.eCache=mat(zeros((self.m,2)))

# 对于给定的alpha值,计算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的值
def selectJ(i,oS,Ei):
    maxK=-1
    maxDeltaE=0
    Ej=0
    oS.eCache[i]=[1,Ei]
    validEcacheList=nonzero(oS.eCache[:,0].A)[0]
    if(len(validEcacheList))>1:
        for k in 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:
        j=selectJrand(i,oS.m)
        Ej=calcEk(oS,j)
    return j,Ej

# 计算误差值并存入缓存
def updateEk(oS,k):
    Ek=calcEk(oS,k)
    oS.eCache[k]=[1,Ek]


# 用于寻找决策边界的优化例程
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.labelMat[j]):
            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) #added this for the Ecache
        if (abs(oS.alphas[j] - alphaJold) < 0.00001): 
            print ("j not moving enough")
            return 0
        oS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j])#update i by the same amount as j
        updateEk(oS, i) #added this for the Ecache                    #the update is in the oppostie direction
        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
        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
        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

# 外循环代码
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
    # 进入循环
    # 如果循环次数没有达到最大上限  and 
    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:%d i:%d,pairs changed %d" %(iter,i,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:%d i:%d,pairs changed %d" %(iter,i,alphaPairsChanged))
            iter+=1
        # 使下次能够进入循环
        if entireSet:
            entireSet=False
        elif(alphaPairsChanged==0):
            entireSet=True
        print("iteration number:%d"%iter)
    # 返回训练得到的b和alpha矩阵
    return oS.b,oS.alphas
            

利用上述代码进行SMO的计算

# 打开文件,读取数据
dataArr,labelArr=loadDataSet('testSet.txt')
# 将数据进行支持向量机训练
b,alphas=smoP(dataArr,labelArr,0.6,0.001,40)

运行得到的结果为:

 再次查看b和支持向量的值:

计算最终起作用的(斜率)?

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

 调用上述函数并使用一个数据进行检验

ws=calcWs(alphas,dataArr,labelArr)
datMat=mat(dataArr)
datMat[0]*mat(ws)+b

得到的结果为

结果是小于0 的

因此查看该数据的分类:

是-1,因此测试的结果正确。 

 5 在复杂数据上应用核函数

前面我们用的数据就是讨论线性可分的情况。接下来,我们就要使用一种称为核函数的工具将数据转换为已于分类器理解的形式

5.1 利用核函数将数据映射到高维空间

我们将数据从一个特征空间转换为另一个特征空间。再新空间下,我们可以很容易利用已有的工具对数据进行处理。在通常情况下,这种映射将为从低维特征空间映射到高维空间。  

这种从某个特征空间到另一个特征空间的映射是通过核函数来实现的。他能够报数据从某个很难处理的形式转换为另一个较容易处理的形式。经过空间转换之后,我们可以在高维空间中解决线性问题,这也就等价于在地位空间中解决非线性问题。  

SVM优化中一个特别好的地方就是,所有的运算都可以写成内积的形式,向量的内积指的是两个向量相乘,之后得到的单个标量或是数值。把内积替换成核函数,而不做简化处理,这种方式被称为核技巧。  

核函数不仅仅应用于向量机,很多其他的机器学习算法也都有用到核函数。

5.2径向基核函数

径向基函数是SVM中常用到的一种核函数,他是一个采用向量作为自变量的函数,能够基于向量距离运算输出一个标量。  

这里 我们应用径向基函数的高斯版本,将数据从特征空间映射到更高维的空间,具体来说是映射到一个更高维的空间

# 转换核函数
def kernelTrans(X,A,kTup):
    m,n=shape(X)
    K=mat(zeros((m,1)))
    if kTup[0]=='lin':
        K=X*A.T
    elif kTup[0]=='rbf':
        for j in range(m):
            deltaRow=X[j,:]-A
            K[j]=deltaRow*deltaRow.T
        K=exp(K/(-1*kTup[1]**2))
    else:
        raise NameError('Houston We Have a Problem--That Kernel is not recognized')
    return K

# 定义一个新的结构体,加入核函数
class optStruct:
    def __init__(self,dataMatIn,classLabels,C,toler,kTup):
        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)))
        self.K=mat(zeros((self.m,self.m)))
        # 计算核函数
        # 引入一个新变量kTup
        for i in range(self.m):
            # 计算高斯函数的值
            self.K[:,i]=kernelTrans(self.X,self.X[i,:],kTup)

# 重新更改上文中提到的innerL()和calcLEk()函数的部分代码
# 用于寻找决策边界的优化例程
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.labelMat[j]):
            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.K[i,j]-oS.K[i,i]-oS.K[j,j]
        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) #added this for the Ecache
        if (abs(oS.alphas[j] - alphaJold) < 0.00001): 
            print ("j not moving enough")
            return 0
        oS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j])#update i by the same amount as j
        updateEk(oS, i) #added this for the Ecache                    #the update is in the oppostie direction
        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]
        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]
        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

# 外循环代码
def smoP(dataMatIn,classLabels,C,toler,maxIter,kTup=('lin',0)):
    # 初始化一个结构体
    oS=optStruct(mat(dataMatIn),mat(classLabels).transpose(),C,toler,kTup)
    iter=0     #记录循环次数
    entireSet=True
    alphaPairsChanged=0
    # 进入循环
    # 如果循环次数没有达到最大上限  and 
    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:%d i:%d,pairs changed %d" %(iter,i,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:%d i:%d,pairs changed %d" %(iter,i,alphaPairsChanged))
            iter+=1
        # 使下次能够进入循环
        if entireSet:
            entireSet=False
        elif(alphaPairsChanged==0):
            entireSet=True
        print("iteration number:%d"%iter)
    # 返回训练得到的b和alpha矩阵
    return oS.b,oS.alphas

def calcEk(oS,k):
    fXk=float(multiply(oS.alphas,oS.labelMat).T*oS.K[:,k]+oS.b)
    Ek=fXk-float(oS.labelMat[k])
    return Ek

5.3 在测试中用到核函数

def testRbf(k1=1.3):
    dataArr,labelArr=loadDataSet('testSetRBF.txt')
    plotBestFit(dataArr,labelArr)
    b,alphas=smoP(dataArr,labelArr,200,0.0001,10000,('rbf',k1))
    datMat=mat(dataArr)
    labelMat=mat(labelArr).transpose()
    svInd=nonzero(alphas.A>0)[0]
    sVs=datMat[svInd]
    labelSV=labelMat[svInd]
    print('there are %d Support Vectors' % shape(sVs)[0])
    m,n=shape(datMat)
    errorCount=0
    for i in range(m):
        kernelEval=kernelTrans(sVs,datMat[i,:],('rbf',k1))
        predict=kernelEval.T*multiply(labelSV,alphas[svInd])+b
        if sign(predict)!=sign(labelArr[i]):
            errorCount+=1
        print('the training error rate is :%f' % (float(errorCount)/m))
        dataArr,labelArr=loadDataSet('testSetRBF2.txt')
        errorCount=0
        datMat=mat(dataArr)
        labelMat=mat(labelArr).transpose()
        m,n=shape(datMat)
        for i in range(m):
            kernelEval=kernelTrans(sVs,datMat[i,:],('rbf',k1))
            predict=kernelEval.T*multiply(labelSV,alphas[svInd])+b
            if sign(predict)!=sign(labelArr[i]):
                errorCount+=1
        print("the test error rate is :%f" %(float(errorCount)/m))
    

调用上述函数:

testRbf()

得到的输出结果为:

 可以看出数据测试的错误率为0.27

 6 手写识别问题回顾

基于SVM的数字识别  

①收集数据:提供的文本文件  

②准备数据:基于二值图像构建向量  

③分析数据:对图像向量进行目测  

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

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

⑥使用算法:一个图像识别的完整应用还需要一些图像处理的知识  

from random import triangular


def img2vector(filename):
    returnVect=zeros((1,1024))
    fr=open(filename)
    # 按行读取矩阵
    for i in range(32):
        lineStr=fr.readline()
        for j in range(32):
            returnVect[0,32*i+j]=int (lineStr[j])
    return returnVect


def loadImages(dirName):
    from os import listdir
    hwLabels = []
    trainingFileList = listdir(dirName)           #load the training set
    m = len(trainingFileList)
    trainingMat = zeros((m,1024))
    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]     #take off .txt
        classNumStr = int(fileStr.split('_')[0])
        if classNumStr == 9: hwLabels.append(-1)
        else: hwLabels.append(1)
        trainingMat[i,:] = img2vector('%s/%s' % (dirName, fileNameStr))
    return trainingMat, hwLabels 

def testDigits(kTup=('rbf',10)):
    dataArr,labelArr=loadImages('digits/trainingDigits')
    b,alphas=smoP(dataArr,labelArr,100,0.001,10000,kTup)
    datMat=mat(dataArr)
    labelMat=mat(labelArr).transpose()
    svInd=nonzero(alphas.A>0)[0]
    sVs=datMat[svInd]
    labelSV=labelMat[svInd]
    print("there are %d Support Vectors"%shape(sVs)[0])
    m,n=shape(datMat)
    errorCount=0
    for i in range(m):
        kernelEval=kernelTrans(sVs,datMat[i,:],kTup)
        predict=kernelEval.T*multiply(labelSV,alphas[svInd])+b
        if sign(predict)!=sign(labelArr[i]):
            errorCount+=1
    print("the training error rate is:%f" % (float(errorCount)/m))
    dataArr,labelArr=loadImages('digits/testDigits')
    errorCount=0
    datMat=mat(dataArr)
    labelMat=mat(labelArr).transpose()
    m,n=shape(datMat)
    for i in range(m):
        kernelEval=kernelTrans(sVs,datMat[i,:],kTup)
        predict=kernelEval.T*multiply(labelSV,alphas[svInd])+b
        if sign(predict)!=sign(labelArr[i]):
            errorCount+=1
    print("the test error rate is :%f" % (float(errorCount)/m))

调用上述函数:

testDigits(('rbf',20))

得到的结果为为:

因此一旦碰到数字9,则输出类别标签-1,否则输出+1。本质上,支持向量机是-个二类分类器,其分类结果不是+1就是-1。由于这里只做二类分类,因此除了1和9之外的数字都被去掉了。

 7 本章小结

支持向量机是一种分类器。之所以称为“机”,是因为它会产生一个二值决策的结果,即它是一种决策机。支持向量机的泛化错误率较低,也就是说他具有良好的学习能力,并且学到的结果具有良好的推广性。  

支持向量机试图通过求解一个二次元问题来最大化分类间隔,再过去,训练支持向量机常采用非常复杂并且低效的二次规划问题,john Platt引入了SMO算法,此算法可以通过每次只优化2个alpha值来加快SVM的训练速度。  

核方法或者说是核技巧会将数据从一个低维空间映射到一个高维空间,可以将一个在低维空间中的非线性问题转换为高维空间的一个线性问题来研究

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆豆豆豆芽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值