Press "Enter" to skip to content

应用sklearn库的支持向量机模型(SVM),并进行可视化绘图,参数调节与测试

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

文章目录

支持向量机(SVM)—-sklearn库的应用
不同参数下SVM模型划分的图像结果
调用支持向量机分类器SVC预测乳腺癌数据集

支持向量机(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')                               #特征名称

 

Text(0, 0.5, 'Feature 1')

 

 

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

 

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()                                          #标出说明标签

 

 

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

 

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

 

现将每个数据点表示为三维数据( 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 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 ​ ),而不是二维数据点( f e a t u r e 0 , f e a t u r e 1 feature_0, feature_1 f e a t u r e 0 ​ , f e a t u r e 1 ​ ),这个新的表示可以化成下图:

 

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')        #绘制坐标轴标签

 

 

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

 

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

 

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')        #绘制坐标轴标签

 

 

如果将线性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')

 

Text(0, 0.5, 'Feature 1')

 

 

支持向量的图像

 

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

 

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')

 

Text(0, 0.5, 'Feature 1')

 

 

不同参数下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))

 

<matplotlib.legend.Legend at 0x196ce93ffd0>

 

 

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

 

从左到右, 参数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))

 

0.903755868544601
0.9370629370629371

 

预测并不是特别完美。

 

虽然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')

 

 

从这张图中可看出,特征的最小值具有完全不同的数量级,这对于其它模型来说可能是小问题,但对核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

 

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]])

 

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

 

print('Minimum for each feature
{}'.format(X_train_scaled.min(axis=0)))          #输出每个特征的最小值
print('Maximum for each feature
{}'.format(X_train_scaled.max(axis=0)))          #输出每个特征的最大值

 

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.]

 

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

 

X_test_scaled = (X_test - min_on_training)/range_on_training                #利用训练集的最小值和范围对数据集做相同的变换

 

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))

 

0.9835680751173709
0.972027972027972

 

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

 

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

 

参数调节

 

现在尝试调节正则化参数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))

 

0.9906103286384976
0.986013986013986

 

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

 

优、缺点及总结

 

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

 

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

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。