写点什么

支持向量机 - 探索核函数在不同数据集上的表现

  • 2022-11-24
    山东
  • 本文字数:3461 字

    阅读完需:约 11 分钟

关于核函数在不同数据集上的研究甚少,谷歌学术上的论文中也没有几篇是研究核函数在 SVM 中的运用的,更多的是关于核函数在深度学习,神经网络中如何使用。在 sklearn 中,也没有提供任何关于如何选取核函数的信息。接下来我们就通过一个例子,来探索一下不同数据集上核函数的表现。我们现在有一系列线性或非线性可分的数据,我们希望通过绘制 SVC 在不同核函数下的决策边界并计算 SVC 在不同核函数下分类准确率来观察核函数的效用。


import numpy as npimport matplotlib.pyplot as pltfrom matplotlib.colors import ListedColormapfrom sklearn.svm import SVCfrom sklearn.datasets import make_circles, make_moons, make_blobs, make_classification
n_samples = 100dataset = [ make_moons(n_samples=n_samples, noise=0.2, random_state=0), make_circles(n_samples=n_samples, noise=0.2, factor=0.5, random_state=1), make_blobs(n_samples=n_samples, centers=2, random_state=5), make_classification(n_samples=n_samples, n_features=2, n_informative=2, n_redundant=0, random_state=5)]
Kernel = ["linear", 'poly', 'sigmoid', 'rbf']for X,Y in dataset: plt.figure(figsize=(5,4)) plt.scatter(X[:,0],X[:,1],c=Y,s=50,cmap="rainbow")
复制代码






我们总共有四个数据集,四种核函数,我们希望观察每种数据集下每个核函数的表现。以核函数为列,以图像分布为行,我们总共需要 16 个子图来展示分类结果。而同时,我们还希望观察图像本身的状况,所以我们总共需要 20 个子图,其中第一列是原始图像分布,后面四列分别是这种分布下不同核函数的表现。


nrows = len(dataset)ncols = len(Kernel) + 1
fig, axes = plt.subplots(nrows,ncols,figsize=(20,16))
复制代码



[*enumerate(dataset)]# 结果是[(索引,(样本特征集,样本标签集))]# 因为很长这里就不展示详细什么样子了
[*enumerate(Kernel)]---[(0, 'linear'), (1, 'poly'), (2, 'sigmoid'), (3, 'rbf')]
fig, axes = plt.subplots(nrows,ncols,figsize=(20,16))# 第一层循环:在不同的数据集中循环# 我们不仅想取到元素,还想取到序号# [*惰性对象] 能打开的还有map,zip也都是惰性对象# list(enumerate(dataset))==[*enumerate]for ds_cnt, (X, Y) in enumerate(dataset): # 在图像中的第一列,放置原数据的分布 ax = axes[ds_cnt, 0] if ds_cnt == 0: ax.set_title("Input data") # 第一列第一行的图的标题,表示这一整列的标题 ax.scatter(X[:,0],X[:,1],c=Y ,zorder=10,cmap=plt.cm.Paired,edgecolors="k" ) # zorder:指定图像图层。数字越大图层越靠上 # edgecolors:点边缘的颜色 ax.set_xticks([]) ax.set_yticks([]) # 第二层循环:在不同的核函数中循环 # 从图像的第二列开始,一个个填充分列结果 for est_idx, kernel in enumerate(Kernel): # 定义子图位置 ax = axes[ds_cnt, est_idx + 1] # 行索引是不变的 # 建模 clf = SVC(kernel=kernel, gamma=2).fit(X,Y) score = clf.score(X,Y) # 绘制图像本身的散点图 ax.scatter(X[:,0],X[:,1],c=Y ,zorder=10,cmap=plt.cm.Paired,edgecolors="k" ) # 绘制支持向量 ax.scatter(clf.support_vectors_[:,0],clf.support_vectors_[:,1],s=50 ,facecolors="none",zorder=10,edgecolors='k') # facecolors:none值即点为透明的 # 因为我们这里想要画一个圈在支持向量上来表示支持向量的位置,而不是盖住支持向量 # 绘制决策边界 x_min, x_max = X[:,0].min() - 0.5, X[:,0].max() + 0.5 # 在原本区间的基础上最大最小各增大0.5 y_min, y_max = X[:,1].min() - 0.5, X[:,1].max() + 0.5 # np.mgrid,合并了之前使用的np.linspace和np.meshgrid的用法 # 一次性使用最大值和最小值来生成网格 # 表示为[起始值:结束值:步长],注意中间是分号,整体是方括号而不是小括号 # 如果步长是复数,则其整数部分就是起始值和结束值之间创建的点的数量,并且结束值被包含在内 XX, YY = np.mgrid[x_min:x_max:200j, y_min:y_max:200j] # 200j就是复数,这里用于指定200个点 # 生成的XX也就是np.mgrid返回的第一个元素每一行是同一个数,YY每一列是同一个数 Z = clf.decision_function(np.c_[XX.ravel(), YY.ravel()]).reshape(XX.shape) # np.c_类似np.vstack.T # 需要reshape是因为返回的是一维的array,而contour需要的是与XX,YY维度对应的矩阵 # 填充等高线不同区域的颜色 ax.pcolormesh(XX,YY,Z > 0, cmap=plt.cm.Paired) # 实际上就是区分决策边界的两侧 # 绘制等高线 ax.contour(XX,YY,Z,colors=['k','k','k'],linestyles=['--','-','--'] ,levels=[-1,0,1]) ax.set_xticks([]) ax.set_yticks([]) # 将标题放在第一行的顶上 if ds_cnt == 0: ax.set_title(kernel) # 为每张图添加分类的分数 ax.text(0.95, 0.06 ,('%.2f' %score).lstrip('0') ,size=15 ,bbox=dict(boxstyle='round',alpha=0.8,facecolor='white') ,transform=ax.transAxes ,horizontalalignment='right') # 0.95,0.06:文字所在x,y轴的位置。这里是添加在右下角 # 第三个参数,要添加的文字 # %.2f保留两位小数,结果是str,而不是float # lstrip('0')不要显示0.xx,显示.xx,前面的对象要是str,而不是float # bbox:添加格子,后面的字典指定盒子的样式 # boxstyle:格子角是圆的 # facecolor:将分数添加一个白色的格子作为底色 # transform=ax.transAxes:确定文字所对应的坐标轴,就是ax子图的坐标轴本身 # horizontalalignment='right':位于坐标轴的右侧方向 plt.tight_layout()# 图像之间空格尽量小,紧缩plt.show()# 会报一个warning# UserWarning: No contour levels were found within the data range.warnings.warn("No contour levels were found"# 猜测应该是应该是第三行第四列的图,因为完全没有画决策边界(sigmoid:“开摆”)# 对于第三行混杂的数据可以考虑决策树
复制代码



可以观察到,线性核函数和多项式核函数在非线性数据上表现会浮动,如果数据相对线性可分,则表现不错,如果是像环形数据那样彻底不可分的,则表现糟糕。在线性数据集上,线性核函数和多项式核函数即便有扰动项也可以表现不错,可见多项式核函数是虽然也可以处理非线性情况,但更偏向于线性的功能。另外,多项式核函数多被用于图像处理之中。Sigmoid 核函数就比较尴尬了,它在非线性数据上强于两个线性核函数,但效果明显不如 rbf,它在线性数据上完全比不上线性的核函数们,对扰动项的抵抗也比较弱,所以它功能比较弱小,很少被用到。rbf,高斯径向基核函数基本在任何数据集上都表现不错,属于比较万能的核函数。我个人的经验是,无论如何先试试看高斯径向基核函数,它适用于核转换到很高的空间的情况,在各种情况下往往效果都很不错,如果 rbf 效果不好,那我们再试试看其他的核函数


几个函数单独拎出来大概展示一下是干啥的 np.mgrid

a, b = np.mgrid[1:3:3j, 2:4:3j]
print(a)
print(b)
---
[[1. 1. 1.]
[2. 2. 2.]
[3. 3. 3.]]
[[2. 3. 4.]
[2. 3. 4.]
[2. 3. 4.]]

np.c_[]和 np.vstack

np.c_[[1,2,3],[2,3,4]] # 逐行连接,矩阵增长,并且转置
---
array([[1, 2],
    [2, 3],
    [3, 4]])

np.vstack([[1,2,3],[2,3,4]]) # 逐行连接,矩阵增长
---
array([[1, 2, 3],
    [2, 3, 4]])

(np.c_[[1,2,3],[2,3,4]] == np.vstack([[1,2,3],[2,3,4]]).T).all() # np.c_[]等价于np.vstack([]).T
---
True

格式化字符串

a = 0.01
('%.2f' %a).lstrip('0') # 去掉0.前面的0
---
'.01'

type('%.2f' %a)
---
str


视频作者:菜菜TsaiTsai链接:【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili


发布于: 刚刚阅读数: 3
用户头像

跃入人海 2022-09-14 加入

还未添加个人简介

评论

发布
暂无评论
支持向量机-探索核函数在不同数据集上的表现_Python_烧灯续昼2002_InfoQ写作社区