Fork me on GitHub 返回顶部

使用支持向量机(SVM) 算法进行分类

1 支持向量机(SVM)的基本概念

  SVM是一种分类算法,其侧重于模式识别方面。使用SVM可以大大提高分类的准确性。

  分类相当于模式识别的子集合,模式识别重点在于对已知数据进行特征发现与提取。

  SVM重点在于解决线性可分的问题。但很多时候,实际的问题是线性不可分的。SVM的思想就是将线性不可分的问题转化线性可分的问题。那么如何来是实现呢?就是将空间映射到多维空间。如把二维空间映射到三维空间。以增加维数来减少方程的次数。

  比如,在二维空间中,不得不用 f(x)=ax2+b+c 这个曲线将两类样本分开,而不是用一条直线将它们分开,这样就是一个线性不可分的问题。为了解决这个问题,我们可以使用换元法将即 x^2=y1 , x=y2 , 这样一来替换为 z=a'y1+b'y2+c' 这个线性方程,从而转换到了高维空间,代价是维度的增加引入了更多的变量。这样我们就完成了从线性不可分问题到线性可分问题的转换。

  所谓支持向量,即在每一类样本集中,过离separating hyper plane 最近的样本做一个与separating hyper plane 平行的线/超平面。有几个分类,就有几个这种直线/超平面,这样的直线/超平面就叫做支持向量(Support Vector)。

2 核函数

  那么找到一个线/超平面来完成二分类成为问题的关键。这个线/超平面的函数被我们成为“核函数”。常见的核函数有:

  • 线性核函数————linear
  • 多项式核函数————poly
  • 径向基核函数————rbf(用得较多)
  • Sigmoid核函数————sigmoid

  那么如何来选择合适的核函数呢?答案就是比较不同核函数的分类的准确率。在实际处理分类问题时,分别计算几种核函数的分类性能,将准确率最高的核函数作为最终用于预测分类的核函数即可。在本文的例子中,我们使用画图的方式来判断分类性能的好坏。

  使用模型不同,转换的方式不同,在多维空间中的图形就不同,分类的效果就不同。

  网上关于SVM、线性可不可分、核函数的博文很多,如http://www.cnblogs.com/LeftNotEasy/archive/2011/05/02/basic-of-svm.html 。这里就不再赘述。

3 比较分类效果与子图的划分

  为了比较不同分类器的准确性,我们采用绘制散点图,然后人工观察来判断。我们将两个类的点在坐标系中用不同颜色表示出来,同时标出训练数据,看预测点与训练点的在图中的重叠情况。重叠越紧密,说明分类的效果越准确。

  在绘图的时候,我们希望在一个平面中同时展现多幅图片,这时使用matplotlib模块的子图(subplot)来展现。

subplot的参数

	subplot(横向划分个数,纵向划分个数,当前定位)

  第一个参数代表的是横向要划分的子图个数,第二个参数代表的是纵向要划分的子图的个数,第三个参数表示当前的定位。

使用示例一:

使用示例二:

使用示例三:

4 源码

应用场景

  假设在第一象限有10个点,它们分别是[0, 0], [1, 1], ... , [9, 9]。它们[0,0],[1,1],[2,2],[3,3]属于“0”这个类(类标签为0),另外6个点属于“1”这个类(类标签为1)。
  现在第一象限构造900*900个点,纵横坐标方向上相邻的点距离为0.01。

[ 0.00, 0.00],[ 0.01, 0.00], [ 0.02, 0.00],..., [ 8.97, 8.99], [ 8.98, 8.99],[ 8.99, 8.99]

  这样,布满区间的点作为预测数据,用各种分类模型进行二分类。最后通过绘图模块给不同类的点上色以判断分类性能。

import numpy as npy
import matplotlib.pyplot as plt
from sklearn import svm
x=[]#存储样本数据
y=[]#存储类标号
for i in range(0,10):#构造10个点作为训练数据
    if(i<=3):#if(i<=3 or i>=8):
        x.append([i,i])
        y.append(0)
    else:
        x.append([i,i])
        y.append(1)

train_x=npy.array(x)#转换为数组
train_y=npy.array(y)


'''
创建svm分类器的格式:
svm.SVC(kernel=某个核函数).fit(训练样本,类标签)
'''
#linear
linear_svc=svm.SVC(kernel="linear").fit(train_x,train_y)

#poly 要定义维度,degree决定了多项式的最高次幂.关于SVC参数的意义请参见文章后头的内容。
poly_svc=svm.SVC(kernel="poly",degree=4).fit(train_x,train_y)

#径向基核函数(这时SVC默认的核函数)
rbf_svc=svm.SVC().fit(train_x,y)

#Sigmoid
sigmoid_svc=svm.SVC(kernel="sigmoid").fit(train_x,train_y)

#下面就可以进行预测了
x1,x2=npy.meshgrid(npy.arange(train_x[:,0].min(),train_x[:,0].max(),0.01),npy.arange(train_x[:,1].min(),train_x[:,1].max(),0.01))

#先生成各个点。定义最小值和最大值后,定义隔多少值建立一个点。
#npy.arange(train_x[:,1].min(),train_x[:,1].max(),0.01))返回的是900个元素的数组
#meshgrid函数用来产生矩阵。上面的语句也是就是numpy.meshgrid(numpy.arange(0,9,0.01),numpy.arange(0,9,0.01))

'''
x1是矩阵 
      [[ 0.  ,  0.01,  0.02, ...,  8.97,  8.98,  8.99],
       [ 0.  ,  0.01,  0.02, ...,  8.97,  8.98,  8.99],
       [ 0.  ,  0.01,  0.02, ...,  8.97,  8.98,  8.99],
       ..., 
       [ 0.  ,  0.01,  0.02, ...,  8.97,  8.98,  8.99],
       [ 0.  ,  0.01,  0.02, ...,  8.97,  8.98,  8.99],
       [ 0.  ,  0.01,  0.02, ...,  8.97,  8.98,  8.99]]

x2是矩阵
      [[ 0.  ,  0.  ,  0.  , ...,  0.  ,  0.  ,  0.  ],
       [ 0.01,  0.01,  0.01, ...,  0.01,  0.01,  0.01],
       [ 0.02,  0.02,  0.02, ...,  0.02,  0.02,  0.02],
       ..., 
       [ 8.97,  8.97,  8.97, ...,  8.97,  8.97,  8.97],
       [ 8.98,  8.98,  8.98, ...,  8.98,  8.98,  8.98],
       [ 8.99,  8.99,  8.99, ...,  8.99,  8.99,  8.99]]
       
'''
splocation=1
for i in [linear_svc,poly_svc,rbf_svc,sigmoid_svc]:#遍历各个模型以便绘图,以看哪个核函数的准确率更高
    rst = i.predict(npy.c_[x1.ravel(),x2.ravel()])#横坐标和纵坐标的组合。x1.ravel()和x2.ravel()都是长度为810000的数组。c_[]用来将前后两个数组串联成一个810000行、2列的矩阵。

    #因为上面用到了四种分类模型,那么一个2×2的图就能够显示完全了。
    plt.subplot(2,2,splocation)#第一个参数代表的是横向要划分的子图个数,第二个参数代表的是纵向要划分的子图的个数,第三个参数表示当前的定位
    plt.contourf(x1,x2,rst.reshape(x1.shape))#contourf用来填充颜色。(当前横坐标,当前纵坐标,预测的分类结果(转为x1的规模维数))

    #训练数据的点也绘制出来
    for j in range(0,len(y)):
        if(int(y[j])==0):
            plt.plot(train_x[j:j+1,0],train_x[j:j+1],"yo")#y代表黄色,o代表散点图
        else:
            plt.plot(train_x[j:j+1,0],train_x[j:j+1],"ko")#类别为1填充为黑色。k代表黑色
    splocation+=1
plt.show()

运行结果:

  可见,在比较这种分布下,径向基核函数表现出更好的分类性能。

5 SVC的参数

  具体参数的意义与使用请参见链接:http://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html

  • C:目标函数的惩罚系数C,用来平衡分类间隔margin和错分样本的,default C = 1.0;

C越大,相当于惩罚松弛变量,希望松弛变量接近0,即对误分类的惩罚增大,趋向于对训练集全分对的情况,这样对训练集测试时准确率很高,但泛化能力弱。C值小,对误分类的惩罚减小,允许容错,将他们当成噪声点,泛化能力较强。

  • kernel :核函数,默认是rbf,可以是‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’

  • degree :多项式poly函数的维度,默认是3,选择其他核函数时会被忽略。

  • gamma : ‘rbf’,‘poly’ 和‘sigmoid’的核函数参数。默认是’auto’,则gamma=1/n_features

  • coef0 :核函数的常数项。对于‘poly’和 ‘sigmoid’有用。

  • probability :是否采用概率估计。默认为False。要采用的话必须先于调用fit,这个过程会增加用时。

  • shrinking :是否采用shrinking heuristic方法,默认为true

  • tol :停止训练的误差值大小,默认为1e-3

  • cache_size :核函数cache缓存大小,默认为200

  • class_weight :类别的权重,字典形式传递。设置第几类的参数C为weight*C(C-SVC中的C)

  • verbose :允许冗余输出。跟多线程有关系。默认为False。

  • max_iter :最大迭代次数。-1为无限制。

  • decision_function_shape :是否返回模型中每一个类别的样本的ovr决策函数,或者ovo决策函数。 默认为None

  • random_state :数据洗牌时的种子值,int值

主要调节的参数有:C、kernel、degree、gamma、coef0。

6 推广

  官方文档是学习的绝佳利器。传送门:http://scikit-learn.org/dev/modules/svm.html

6.1 多分类

  SVM算法最初是为二值分类问题设计的,当处理多类问题时,就需要构造合适的多类分类器。目前,构造SVM多类分类器的方法主要有两类:一类是直接法,直接在目标函数上进行修改,将多个分类面的参数求解合并到一个最优化问题中,通过求解该最优化问题“一次性”实现多类分类。这种方法看似简单,但其计算复杂度比较高,实现起来比较困难,只适合用于小型问题中;另一类是间接法,主要是通过组合多个二分类器来实现多分类器的构造,常见的方法有one-against-one和one-against-all两种。

  1. 一对多法(one-versus-rest,简称1-v-r SVMs)。训练时依次把某个类别的样本归为一类,其他剩余的样本归为另一类,这样k个类别的样本就构造出了k个SVM。分类时将未知样本分类为具有最大分类函数值的那类。
  2. 一对一法(one-versus-one,简称1-v-1 SVMs)。其做法是在任意两类样本之间设计一个SVM,因此k个类别的样本就需要设计k(k-1)/2个SVM。当对一个未知样本进行分类时,最后得票最多的类别即为该未知样本的类别。Libsvm中的多类分类就是根据这个方法实现的。
  3. 层次支持向量机(H-SVMs)。层次分类法首先将所有类别分成两个子类,再将子类进一步划分成两个次级子类,如此循环,直到得到一个单独的类别为止。 对c和d两种方法的详细说明可以参考论文《支持向量机在多类分类问题中的推广》(计算机工程与应用。2004)
  4. 其他多类分类方法。除了以上几种方法外,还有有向无环图SVM(Directed Acyclic Graph SVMs,简称DAG-SVMs)和对类别进行二进制编码的纠错编码SVMs。

6.2 回归

  支持分类的支持向量机可以推广到解决回归问题,这种方法称为支持向量回归。

  支持向量分类所产生的模型仅仅依赖于训练数据的一个子集,因为构建模型的成本函数不关心在超出边界范围的点,类似的,通过支持向量回归产生的模型依赖于训练数据的一个子集,因为构建模型的函数忽略了靠近预测模型的数据集。

  有三种不同的实现方式:支持向量回归SVR,nusvr和linearsvr。linearsvr提供了比SVR更快实施但只考虑线性核函数,而nusvr实现比SVR和linearsvr略有不同。
作为分类类别,训练函数将X,y作为向量,在这种情况下y是浮点数

posted @ 2017-05-29 19:28  d0main  阅读(15535)  评论(0编辑  收藏  举报