current position:Home>Support Vector Machines - Exploring the performance of kernel functions on different data sets

Support Vector Machines - Exploring the performance of kernel functions on different data sets

2022-11-24 23:08:54InfoQ

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

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.svm import SVC
from sklearn.datasets import make_circles, make_moons, make_blobs, make_classification

n_samples = 100
dataset = [
 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")

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

nrows = len(dataset)
ncols = len(Kernel) + 1

fig, axes = plt.subplots(nrows,ncols,figsize=(20,16))

null
[*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:“开摆”)
# 对于第三行混杂的数据可以考虑决策树

null
可以观察到,线性核函数和多项式核函数在非线性数据上表现会浮动,如果数据相对线性可分,则表现不错,如果是像环形数据那样彻底不可分的,则表现糟糕.在线性数据集上,线性核函数和多项式核函数即便有扰动项也可以表现不错,可见多项式核函数是虽然也可以处理非线性情况,但更偏向于线性的功能.另外,多项式核函数多被用于图像处理之中.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

copyright notice
author[InfoQ],Please bring the original link to reprint, thank you.
https://en.chowdera.com/2022/328/202211242306463148.html

Random recommended