• 支持向量机(SVM)----sklearn库的应用


    支持向量机(SVM)----sklearn库的应用

    本文主要实现sklearn库中的支持向量机SVM模型,希望能够帮助到大家。

    维度升级及切分图像

    线性模型在低维空间中可能非常受限,因为线和平面的灵活度十分有限。

    有一种方法可以让线性模型更加灵活,就是添加更多的特征,例如添加特征的交互项或者多项式,专业术语即维度升级

    先看一下如下的数据集

    from sklearn.datasets import make_blobs            #导入斑点(blobs)数据集
    import matplotlib.pyplot as plt
    import mglearn
    
    X, y = make_blobs(centers=4, random_state=8)          #生成4团斑点数据
    y = y%2                                               #通过取余操作,将原来的4个类别(0,1,2,3)转化为2个类别(0,1)
    
    mglearn.discrete_scatter(X[:,0], X[:,1], y)           #用画布的形式展示所有数据的位置信息,包含三个参数(特征1、2,标签)
    
    plt.xlabel('Feature 0')
    plt.ylabel('Feature 1')                               #特征名称
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    Text(0, 0.5, 'Feature 1')
    
    • 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AerG7y4I-1663125077906)(output_5_1.png)]

    用于分类的线性模型只能用一条直线来划分数据点,对整个数据集无法给出较好的结果,如下图

    from sklearn.svm import LinearSVC                      #引入线性分类支持向量机
    
    linear_svm = LinearSVC().fit(X, y)                      #训练数据
    
    mglearn.plots.plot_2d_separator(linear_svm, X)          #绘图画出模型给出的数据集切分线
    mglearn.discrete_scatter(X[:,0], X[:,1], y)             #绘图所有的数据
    
    plt.xlabel('Feature 0')
    plt.ylabel('Feature 1')                               #特征名称
    plt.legend()                                          #标出说明标签
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vHOGUHxn-1663125077909)(output_7_2.png)]

    可以看到,对于该数据集,无法使用往常的线性模型对数据集进行有效分类。

    现在对输入特征进行扩展,比如说添加第二个特征的平方 f e a t u r e 1 2 feature_1^2 feature12作为一个新特征。

    现将每个数据点表示为三维数据( f e a t u r e 0 , f e a t u r e 1 , f e a t u r e 1 2 feature_0,feature_1,feature_1^2 feature0,feature1feature12),而不是二维数据点( f e a t u r e 0 , f e a t u r e 1 feature_0, feature_1 feature0,feature1),这个新的表示可以化成下图:

    import numpy as np                          #导入numpy数组
    #导入3d图形的绘制工具
    from mpl_toolkits.mplot3d import Axes3D, axes3d
    import matplotlib.pyplot as  plt
    
    X_new = np.hstack([X, X[:,1:]**2])          #hstack合并数据,在X的数据集上再追加一列,为X[:,1:]的平方,此时数据为三维数据
    
    figure = plt.figure()                       #绘制面板对象
    ax = Axes3D(figure, elev=-152, azim=-26)    #3d可视化
    mask = y == 0                               #把数据集中所有 y = 0的点赋值给 mask变量,返回值是bool类型的列表
    
    ax.scatter(X_new[mask,0], X_new[mask,1], X_new[mask,2], c='b', cmap=mglearn.cm2, s = 60)                  #绘制所有类别为0的数据点
    ax.scatter(X_new[~mask,0], X_new[~mask,1], X_new[~mask,2], c='r', marker='^',cmap=mglearn.cm2, s = 60)   #绘制所有类别为1的数据点
    
    ax.set_xlabel('feature 0')
    ax.set_ylabel('feature 1')
    ax.set_zlabel('feature 1 ** 2')        #绘制坐标轴标签
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oThgaRkr-1663125077911)(output_10_2.png)]

    可以看到,我们已经将数据集中的二维数据成功转化为了三维数据。

    在数据的新表示中,现在可以用线性模型(三维空间中的平面)将这两个类别分开。我们可以用线性模型拟合扩展后的数据来验证这一点,如下

    linear_svm_3d = LinearSVC().fit(X_new, y)                                           #再次用线性分类支持向量机训练数据
    coef, intercept = linear_svm_3d.coef_.ravel(), linear_svm_3d.intercept_             #获取训练数据集后的参数
    
    figure = plt.figure()                                                   #定义显示面板
    ax = Axes3D(figure, elev=-152, azim=-26)                                #3d显示
    #numpy.linspace()为均分函数, 用于绘制超平面需要,即把n到m的距离均分s份 
    xx = np.linspace(X_new[:,0].min()-2, X_new[:,0].max()+2, 50)            #将第一个维度从最小值到最大值均分为50份   
    yy = np.linspace(X_new[:,1].min()-2, X_new[:,1].max()+2, 50)            #将第二个维度从最小值到最大值均分为50份   
    
    '''
    X, Y = np.meshgrid(x, y) 代表的是将x中每一个数据和y中每一个数据组合生成很多点,
    然后将这些点的x坐标放入到X中,y坐标放入Y中,并且相应位置是对应的
    '''
    XX, YY = np.meshgrid(xx, yy)                                            #生成了较大密度的XX, YY,分别代表数据集的第一纬度、第二维度数据
    ZZ = (coef[0] * XX + coef[1] * YY + intercept)/-coef[2]                #生成数据集的第三维度z的所有数据点
    ax.plot_surface(XX, YY, ZZ, rstride=8, cstride=8, alpha=0.3)           #根据所有数据集的数据的第一、第二、第三维度数据进行绘制决策超平面
    
    ax.scatter(X_new[mask,0], X_new[mask,1], X_new[mask,2], c='b', cmap=mglearn.cm2, s = 60)                  #绘制所有类别为0的数据点
    ax.scatter(X_new[~mask,0], X_new[~mask,1], X_new[~mask,2], c='r', marker='^',cmap=mglearn.cm2, s = 60)   #绘制所有类别为1的数据点
    
    ax.set_xlabel('feature 0')
    ax.set_ylabel('feature 1')
    ax.set_zlabel('feature 1 ** 2')        #绘制坐标轴标签
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-msQJ4ZKR-1663125077912)(output_12_2.png)]

    如果将线性SVM模型看作原始特征的函数,那么它实际上已经不是线性的了。

    它不是一条直线,而是一个椭圆,如下图

    ZZ = YY ** 2
    # ravel() 函数将数组多维度拉成一维数组
    dec = linear_svm_3d.decision_function(np.c_[XX.ravel(), YY.ravel(), ZZ.ravel()])       #决策函数
    
    #绘制三维等高线图,不同点在于contour() 是绘制轮廓线,contourf()会填充轮廓
    plt.contourf(XX, YY, dec.reshape(XX.shape), levels=[dec.min(), 0, dec.max()], cmap=mglearn.cm2, alpha=0.5)
    
    mglearn.discrete_scatter(X[:,0], X[:,1], y)         #绘制所有数据点
    plt.xlabel('Feature 0')                             #绘制坐标轴的特征标签
    plt.ylabel('Feature 1')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    Text(0, 0.5, 'Feature 1')
    
    • 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cGMW0ThX-1663125077913)(output_14_1.png)]

    支持向量的图像

    支持向量一般为距离决策超平面较近的一些数据点,如下图

    from sklearn.svm import SVC                          #导入支持向量机分类器
    
    X, y = mglearn.tools.make_handcrafted_dataset()       #导入数据集
    
    svm = SVC(kernel='rbf', C=10, gamma=0.1).fit(X, y)    #初始化支持向量机分类器,核函数为高斯核、C正则程度为10、gamma初始化0.1
    
    mglearn.plots.plot_2d_separator(svm, X, eps=.5)       #绘图分界线
    mglearn.discrete_scatter(X[:,0], X[:,1], y)           #绘制所有数据点
    
    sv = svm.support_vectors_     #画出支持向量
    
    #dual_coef_.ravel()内存放的是一个列表(lambda * y_i)
    sv_labels = svm.dual_coef_.ravel() > 0
    
    #绘制支持向量
    mglearn.discrete_scatter(sv[:,0], sv[:,1], sv_labels, s=15, markeredgewidth=3)
    
    plt.xlabel('Feature 0')                             #绘制坐标轴的特征标签
    plt.ylabel('Feature 1')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    Text(0, 0.5, 'Feature 1')
    
    • 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aNyuKARQ-1663125077915)(output_17_1.png)]

    不同参数下SVM模型划分的图像结果

    gamma 参数是控制高斯核宽度的参数(如有不解,请看本章)。它决定了点与点"靠近"是指多大的距离。

    C参数是正则化参数,与线性模型中用到的类似,它限制每个点的重要性。

    fig, axes = plt.subplots(3, 3, figsize=(15,10))
    
    for ax, C in zip(axes, [-1, 0, 3]):
        for a, gamma in zip(ax, range(-1, 2)):
            mglearn.plots.plot_svm(log_C=C, log_gamma=gamma, ax=a)
            
    axes[0,0].legend(['class0', 'class1', 'sv class0', 'sv class1'], ncol=4, loc=(.9, 1.2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    
    
    • 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eNwrxoBp-1663125077917)(output_20_1.png)]

    如上图,可以发现以下规律

    从左到右,参数gamma的值从0.1增加到10。gamma值较小,说明高斯核的半径较大,许多点都被看做比较靠近。这一点可以在图中看出,左侧的图决策边界非常平滑,越向右的点决策边界更关注单个点。小的gamma值表示决策边界变化很慢,生成的是复杂度较低的模型,而大的gamma值则会生成更为复杂的模型。

    从上到下,将参数C的值从0.1增加到1000。与线性模型相同,C值很小,说明模型非常受限,每个数据点的影响范围非常有限。而增大C之后这些点对模型的影响变大,使得决策边界发生弯曲来将这些点正确分类。

    调用支持向量机分类器SVC预测乳腺癌数据集

    初预测
    from sklearn.datasets import load_breast_cancer
    from sklearn.model_selection import train_test_split
    
    cancer = load_breast_cancer()                       #加载数据集
    X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state=0)
    
    svc = SVC()                                         #定义SVC支持向量机分类器
    svc.fit(X_train, y_train)                           #训练数据集
    
    print(svc.score(X_train, y_train))                  #输出预测的训练集与测试集评分
    print(svc.score(X_test, y_test))
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    0.903755868544601
    0.9370629370629371
    
    • 1
    • 2

    预测并不是特别完美。

    虽然SVM的表现通常很好,但它对参数的设定和数据的缩放非常敏感。特别地,它要求所有特征具有相似的变化范围。

    现在来看下每个特征的最小值与最大值,绘制在对数坐标上

    #加入axis参数,当axis=0时会分别取每一列的最大值或最小值,axis=1时,
    #会分别取每一行的最大值或最小值,且将所有取到的数据放在一个一维数组中。
    #X_train.min(axis=0)即找出每一列的最小值,X_train.min(axis=1)即找出每一行的最小值
    plt.plot(X_train.min(axis=0), 'o', label='min')
    plt.plot(X_train.min(axis=1), '^', label='max')
    plt.legend(loc=4)
    plt.xlabel('Feature index')
    plt.xlabel('Feature magnitude')
    plt.yscale('log')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c6fwWbh5-1663125077919)(output_27_0.png)]

    从这张图中可看出,特征的最小值具有完全不同的数量级,这对于其它模型来说可能是小问题,但对核SVM却有极大影响。

    现在对数据集进行预处理。

    数据预处理(缩放)

    解决这个问题的一种办法就是对每个特征就行缩放,使其大致位于同一范围。

    核SVM常用的缩放方法就是将所有特征缩放到0和1之间,现在来进行缩放

    min_on_training = X_train.min(axis=0)                               #计算训练集中每个特征的最小值
    
    range_on_training = (X_train - min_on_training).max(axis=0)           #计算训练集中每个特征的范围(最大值-最小值)
    
    #减去最小值,然后除以范围,这样数据集的每个特征都会在0~1之间
    X_train_scaled = (X_train - min_on_training)/range_on_training
    
    #输出所有进行缩放后的数据
    X_train_scaled
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    array([[0.23044157, 0.32157676, 0.21940433, ..., 0.31484671, 0.30277942,
            0.09858323],
           [0.20062473, 0.42116183, 0.19452699, ..., 0.06965208, 0.34042973,
            0.06677161],
           [0.62232003, 0.76929461, 0.60403566, ..., 0.56079917, 0.19850187,
            0.07431457],
           ...,
           [0.11619102, 0.35726141, 0.11077327, ..., 0.17402687, 0.17524147,
            0.17263545],
           [0.12963226, 0.35311203, 0.11706171, ..., 0.        , 0.06780997,
            0.06919848],
           [0.21434995, 0.59004149, 0.21235575, ..., 0.33251808, 0.10782574,
            0.21172767]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以看到,它们均位于0~1之间,再输出每个特征的最大值与最小值

    print('Minimum for each feature\n{}'.format(X_train_scaled.min(axis=0)))          #输出每个特征的最小值
    print('Maximum for each feature\n{}'.format(X_train_scaled.max(axis=0)))          #输出每个特征的最大值
    
    • 1
    • 2
    Minimum for each feature
    [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
     0. 0. 0. 0. 0. 0.]
    Maximum for each feature
    [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
     1. 1. 1. 1. 1. 1.]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    现在同样对测试集的数据进行缩放

    X_test_scaled = (X_test - min_on_training)/range_on_training                #利用训练集的最小值和范围对数据集做相同的变换
    
    • 1
    再预测
    svc = SVC()                                         #定义SVC支持向量机分类器
    svc.fit(X_train_scaled, y_train)                    #训练进行缩放后的数据集
    
    print(svc.score(X_train_scaled, y_train))                  #输出预测的训练集与测试集评分
    print(svc.score(X_test_scaled, y_test))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    0.9835680751173709
    0.972027972027972
    
    • 1
    • 2

    可以看到,无论是训练集还是测试集,预测的准确度有了明显的提高。

    但是训练集与测试集的预测结果较接近,可能出现了欠拟合的状态。

    参数调节

    现在尝试调节正则化参数C来拟合更为复杂的模型。

    svc = SVC(C=30)                                   #定义SVC支持向量机分类器,增大参数C,提高模型的复杂度(但要适度,过度增加会导致过拟合)
    svc.fit(X_train_scaled, y_train)                  #训练进行缩放后的数据集
    
    print(svc.score(X_train_scaled, y_train))         #输出预测的训练集与测试集评分
    print(svc.score(X_test_scaled, y_test))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    0.9906103286384976
    0.986013986013986
    
    • 1
    • 2

    可以发现,在本次测试中,增加参数C明显地提高了预测地准确度,得到了98.6%的准确度。

    优、缺点及总结

    核支持向量机是非常强大的模型,在各种数据集上的表现均较好。

    但对样本个数的缩放表现不好,当预测值较低时,可以尝试对数据进行缩放再进行预测,另外在调节参数C和gamma时也应谨慎小心。

  • 相关阅读:
    在Ubuntu或linux中为coreutils工具包的cp和mv命令添加进度条
    Android应用内组件通讯之EventBus源码分析之post流程(三)
    项目中常用的正则表达式
    pytorch深度学习实战lesson10
    worthington酶活性研究丨worthington pH值的影响
    280049flash guide中文
    环境温湿度在线监测如何实现?有何应用场景?
    Python教程之字典(Dictionary)操作详解
    2022祥云杯---Crypto
    用了那么久的 Lombok,你知道它的原理么?
  • 原文地址:https://blog.csdn.net/weixin_43479947/article/details/126848909