(7) 支持向量机(上)

news2024/11/25 21:36:37

文章目录

  • 1 概述
    • 1.1 支持向量机分类器是如何工作的
  • 2 sklearn.svm.SVC
    • 2.1 线性SVM决策过程的可视化
    • 2.2 重要参数kernel(核函数)
    • 2.3 探索核函数在不同数据集上的表现
    • 2.4 探索核函数的优势和缺陷
    • 2.5 选取与核函数相关的参数:degree & gamma & coef0
    • 2.6 硬间隔与软间隔:重要参数C
  • 3 附录
    • 3.1 SVC的参数列表
    • 3.2 SVC的属性列表
    • 3.3 SVC的接口列表

文章内容参考于该up主相关视频

1 概述

支持向量机(Support Vector Machine,SVM)是一种常用的监督学习算法,主要用于分类和回归问题。

在分类问题中,SVM试图找到一个超平面(在二维情况下就是一条直线)来分割两个不同类别的数据点,使得每个类别中的数据点都尽可能地靠近自己的一侧,同时尽可能地远离超平面。SVM所使用的超平面被称为“最大间隔超平面”,因为它使得两个不同类别的数据点之间的间隔最大化。SVM算法可以使用不同的核函数来处理非线性问题。

image-20230501152243980

在回归问题中,SVM也试图找到一个超平面来拟合数据点,但它的目标是使得数据点与超平面的距离最小化。与分类问题不同,SVM回归模型并不是寻找最大间隔,而是寻找在误差允许范围内最大化支持向量的数量。

SVM在许多方面都表现出色,包括:

  1. SVM在处理高维度、稀疏数据时效果显著,它能够从海量的特征中挑选出对分类最有帮助的特征进行学习,这使得SVM在文本分类、图像分类、生物信息学等领域表现优秀。
  2. SVM可以使用不同的核函数来处理非线性问题,如多项式核(poly)、径向基函数(RBF)核、Sigmoid核等。这使得SVM具有很强的通用性和灵活性,可以适用于许多不同类型的数据。
  3. SVM的目标是最大化间隔,因此具有很好的泛化性能,能够有效地避免过拟合的问题。
  4. 对于小样本数据集,SVM具有很好的表现,因为它可以利用少量的支持向量来表示整个数据集。

总的来说,SVM是一种强大的分类和回归算法,在处理高维度、稀疏数据和非线性问题时表现尤为突出

1.1 支持向量机分类器是如何工作的

image-20230501152450275

image-20230501152608086

2 sklearn.svm.SVC

class sklearn.svm.SVC (C=1.0, kernel=’rbf’, degree=3, gamma=’auto_deprecated’, coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape=’ovr’, random_state=None)

2.1 线性SVM决策过程的可视化

我们可以使用sklearn来为可视化决策边界,支持向量,以及决策边界平行的两个超平面

  1. 导入需要的模块
from sklearn.datasets import make_blobs
from sklearn.svm import SVC
import matplotlib.pyplot as plt
import numpy as np
  1. 可视化数据,观察大致分布
X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.6)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap="rainbow")  # c=y的意思是将y数组的取值作为散点图中每个数据点的颜色
plt.xticks([])
plt.yticks([])
plt.show()

output_4_0

  1. 画决策边界:理解等高线函数contour用法
    matplotlib.axes.Axes.contour([X, Y,] Z, [levels], **kwargs)

Contour是我们专门用来绘制等高线的函数。等高线,本质上是在二维图像上表现三维图像的一种形式,其中两维X和Y是两条坐标轴上的取值,而Z表示高度。Contour就是将由X和Y构成平面上的所有点中,高度一致的点连接成线段的函数,在同一条等高线上的点一定具有相同的Z值。我们可以利用这个性质来绘制我们的决策边界

image-20230501153201774

首先,我们需要获取样本构成的平面,作为一个对象。

# 首先要有散点图
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap="rainbow")
ax = plt.gca()  # 获取当前子图,如果不存在则创建新的子图

output_6_0

有了这个平面,我们需要在平面上制作一个足够细的网格,来代表我们“平面上的所有点”。

  1. 画决策边界:制作网格,理解函数meshgrid
#获取平面上两条坐标轴的最大值和最小值
xlim = ax.get_xlim()  # 元组(min,max)
ylim = ax.get_ylim()

#在最大值和最小值之间形成30个规律的数据
axisx = np.linspace(xlim[0], xlim[1], 30)
axisy = np.linspace(ylim[0], ylim[1], 30)

axisx, axisy = np.meshgrid(axisx, axisy)
#我们将使用这里形成的二维数组作为我们contour函数中的X和Y
#使用meshgrid函数将两个一维向量转换为特征矩阵
#核心是将两个特征向量广播,以便获取y.shape * x.shape这么多个坐标点的横坐标和纵坐标

xy = np.vstack([axisx.ravel(), axisy.ravel()]).T
#其中ravel()是降维函数,vstack能够将多个结构一致的一维数组按行堆叠起来
#xy就是已经形成的网格,它是遍布在整个画布上的密集的点

plt.scatter(xy[:, 0], xy[:, 1], s=1, cmap="rainbow")

output_9_1

有了网格后,我们需要计算网格所代表的“平面上所有的点”到我们的决策边界的距离。所以我们需要我们的模型和决策边界。

  1. 建模,计算决策边界并找出网格上每个点到决策边界的距离
#建模,通过fit计算出对应的决策边界
clf = SVC(kernel="linear").fit(X, y)  #计算出对应的决策边界
Z = clf.decision_function(xy).reshape(axisx.shape)
#重要接口decision_function,返回每个输入的样本所对应的到决策边界的距离
#然后再将这个距离转换为axisx的结构,这是由于画图的函数contour要求Z的结构必须与X和Y保持一致

#首先要有散点图
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap="rainbow")
ax = plt.gca()  #获取当前的子图,如果不存在,则创建新的子图
#画决策边界和平行于决策边界的超平面
ax.contour(axisx, axisy, Z
           , colors="k"
           , levels=[-1, 0, 1]  #画三条等高线,分别是Z为-1,Z为0和Z为1的三条线
           , alpha=0.5  #透明度
           , linestyles=["--", "-", "--"])

ax.set_xlim(xlim)  #设置x轴取值
ax.set_ylim(ylim)

output_12_1

  1. 将完整绘图过程包装成一个函数
def plot_svc_decision_function(model, ax=None):
    if ax is None:
        ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    x = np.linspace(xlim[0], xlim[1], 30)
    y = np.linspace(ylim[0], ylim[1], 30)
    Y, X = np.meshgrid(y, x)
    xy = np.vstack([X.ravel(), Y.ravel()]).T
    P = model.decision_function(xy).reshape(X.shape)

    ax.contour(X, Y, P, colors="k", levels=[-1, 0, 1], alpha=0.5, linestyles=["--", "-", "--"])
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)


#则整个绘图过程可以写成:
clf = SVC(kernel="linear").fit(X, y)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap="rainbow")
plot_svc_decision_function(clf)

output_14_0

  1. 探索建好的模型
clf.predict(X)
#根据决策边界,对X中的样本进行分类,返回的结构为n_samples

clf.score(X, y)
#返回给定测试数据和标签的平均准确度

clf.support_vectors_
#返回支持向量

clf.n_support_
#返回每个类中支持向量的个数
array([2, 1])
  1. 推广到非线性情况

我们之前所讲解的原理,以及绘图的过程,都是基于数据本身是线性可分的情况。如果把数据推广到非线性数据,比如说环形数据上呢?

# factor=0.1表示内外圆之间的距离因子,即内圆的半径是外圆半径的0.1倍;
# noise=0.1表示噪声的标准差,即样本点的位置会在水平和竖直方向上加上一定程度的随机扰动,增加了数据的随机性。
from sklearn.datasets import make_circles

X, y = make_circles(100, factor=0.1, noise=.1)

# X.shape (100,2)
# y.shape  (100,)

plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap="rainbow")
plt.show()

output_19_0

试试看用我们已经定义的函数来划分这个数据的决策边界:

clf = SVC(kernel="linear").fit(X, y)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap="rainbow")
plot_svc_decision_function(clf)

output_21_0

明显,现在线性SVM已经不适合于我们的状况了,我们无法找出一条直线来划分我们的数据集,让直线的两边分别是两种类别。这个时候,如果我们能够在原本的X和y的基础上,添加一个维度r,变成三维,我们可视化这个数据,来看看添加维度让我们的数据如何变化。

  1. 为非线性数据增加维度并绘制3D图像
#定义一个由x计算出来的新维度r
r = np.exp(-(X ** 2).sum(1))  # (100,)

rlim = np.linspace(min(r), max(r), 100)


#定义一个绘制三维图像的函数
#elev表示上下旋转的角度
#azim表示平行旋转的角度
def plot_3D(elev=30, azim=30, X=X, y=y):
    ax = plt.subplot(projection="3d")
    ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='rainbow')
    ax.view_init(elev=elev, azim=azim)  #设置视角
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.set_zlabel("r")
    plt.show()


plot_3D()

output_24_0

可以看见,此时此刻我们的数据明显是线性可分的了:我们可以使用一个平面来将数据完全分开,并使平面的上方的所有数据点为一类,平面下方的所有数据点为另一类。
此时我们的数据在三维空间中,我们的超平面就是一个二维平面,就是我们的决策边界。我们刚才做的,计算r,并将r作为数据的第三维度来将数据升维的过程,被称为“核变换”,就是将数据投影到高维空间中,以寻找能够将数据完美分割的超平面,就是说寻找能够让数据线性可分的高维空间。为了详细解释这个过程,我们需要引入SVM中的核心概念:核函数

2.2 重要参数kernel(核函数)

在支持向量机(SVM)中,核函数是一种用于将输入数据映射到高维特征空间的技术。核函数可以将线性不可分的数据转化为线性可分的数据,从而使得SVM可以更好地处理非线性问题

核函数的本质是在高维空间中计算点之间的相似度。常用的核函数有以下几种:

image-20230501154159873

其中,相关参数需要根据具体情况进行调整,以获得最佳的分类效果。

可以看出,除了选项"linear"之外,其他核函数都可以处理非线性问题。多项式核函数有次数d,当d为1的时候它就是在处理线性问题,当d为更高次项的时候它就是在处理非线性问题。我们之前画图时使用的是选项“linear",自然不能处理环形数据这样非线性的状况。而刚才我们使用的计算r的方法,其实是高斯径向基核函数所对应的功能,在参数”kernel“中输入”rbf“就可以使用这种核函数。我们来看看模型找出的决策边界时什么样:

clf = SVC(kernel="rbf").fit(X, y)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap="rainbow")
plot_svc_decision_function(clf)

output_27_0

可以看到,决策边界被完美地找了出来。

2.3 探索核函数在不同数据集上的表现

除了"linear"以外的核函数都能够处理非线性情况,那究竟什么时候选择哪一个核函数呢?遗憾的是,关于核函数在不同数据集上的研究甚少,谷歌学术上的论文中也没有几篇是研究核函数在SVM中的运用的,更多的是关于核函数在深度学习,神经网络中如何使用。在sklearn中,也没有提供任何关于如何选取核函数的信息。

但无论如何,我们还是可以通过在不同的核函数中循环去找寻最佳的核函数来对核函数进行一个选取。接下来我们就通过一个例子,来探索一下不同数据集上在不同核函数的表现。

我们现在有一系列线性或非线性可分的数据,我们希望通过绘制SVC在不同核函数下的决策边界并计算SVC在不同核函数下分类准确率来观察核函数的效用。

  1. 导入所需要的库和模块
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import svm
from sklearn.datasets import make_circles, make_moons, make_blobs, make_classification
  1. 创建数据集,定义核函数的选择
#样本数量
n_samples = 100

#创建4个数据集
datasets = [
    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),  #factor表示内圆是外圆半径的多少倍
    make_blobs(n_samples=n_samples, centers=2, random_state=5),  #centers表示中心点的个数
    #n_features表示特征数,n_informative表示有用的特征数,n_redundant表示冗余的特征数
    make_classification(n_samples=n_samples, n_features=2, n_informative=2, n_redundant=0, random_state=5)
]

# 四个核函数
Kernel = ["linear", "poly", "rbf", "sigmoid"]

# 四个数据集分别是什么样子呢?
for X, Y in datasets:
    plt.figure(figsize=(5, 4))
    plt.scatter(X[:, 0], X[:, 1], c=Y, s=50, cmap="rainbow")

output_33_0
output_33_1
output_33_2
output_33_3

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

  1. 构建子图,循环绘图

以下代码中相关函数及参数解释:
zorder=10: 控制绘制顺序,具有较高zorder值的元素将绘制在具有较低zorder值的元素之上。这里设置为10,以确保散点图在其他可能的图形元素之上。
cmap=plt.cm.Paired: 使用Paired颜色映射。这是一个离散的颜色映射,适用于表示具有不同类别的数据点。plt.cm是matplotlib的内置颜色映射模块。
edgecolors='k': 数据点边缘的颜色。这里设置为’k’,表示黑色。这有助于更清楚地区分相邻的数据点。
facecolors='none': 数据点内部的颜色。这里设置为’none’,表示无颜色填充。这有助于更清楚地区分相邻的数据点。
np.mgrid:合并了之前使用的np.linspace和np.meshgrid的用法.一次性使用最大值和最小值来生成网格 表示为[起始值:结束值:步长] 如果步长是复数,则其整数部分就是起始值和结束值之间创建的点的数量,并且结束值被包含在内。
np.c_:在列方向上连接两个向量,生成两列。

nrows = len(datasets)
ncols = len(Kernel) + 1
# 创建画布,共20个子图,共四行五列,第一列为原始数据分布,后面四列为不同核函数下的分类结果
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(20, 16))

#第一层循环:在不同的数据集中循环
for ds_index, (X, Y) in enumerate(datasets):

    #在图像中的第一列,放置原数据的分布
    ax = axes[ds_index, 0]
    if ds_index == 0:  # 设置第一列图的标题
        ax.set_title("Input data")
    ax.scatter(X[:, 0], X[:, 1], c=Y, zorder=10, cmap=plt.cm.Paired, edgecolors='k')
    ax.set_xticks([])  # 坐标轴不显示刻度
    ax.set_yticks([])

    #第二层循环:在不同的核函数中循环
    #从图像的第二列开始,一个个填充分类结果
    for kn_index, ker in enumerate(Kernel):
        ax = axes[ds_index, kn_index + 1]  #定义子图位置
        clf = svm.SVC(kernel=ker, 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')
        # 绘制支持向量,支持向量在sklearn中的属性:support_vectors_
        ax.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=50, facecolors='none', zorder=10,
                   edgecolors='k')
        # 绘制决策边界
        x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
        y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
        XX, YY = np.mgrid[x_min:x_max:200j, y_min:y_max:200j]  #一个函数直接生成网格采样点
        Z = clf.decision_function(np.c_[XX.ravel(), YY.ravel()]).reshape(XX.shape)
        ax.pcolormesh(XX, YY, Z > 0, cmap=plt.cm.Paired, shading='auto')  #填充等高线不同区域的颜色
        ax.contour(XX, YY, Z, colors=['k', 'k', 'k'], linestyles=['--', '-', '--'], levels=[-1, 0, 1])
        ax.set_xticks([])
        ax.set_yticks([])
        if ds_index == 0:  #设置第一行标题
            ax.set_title(ker)
        ax.text(0.95, 0.06, ('%.2f' % score).lstrip('0')  # lstrip() 方法用于截掉字符串左边的空格或指定字符
                , size=15
                , bbox=dict(boxstyle='round', alpha=0.8, facecolor='white')  #设置文本框区域,白色底色
                , transform=ax.transAxes  #设置文本框相对于坐标轴的位置,就是ax子图的坐标轴本身
                , horizontalalignment='right')  #设置文本框中文字的位置,右对齐
plt.tight_layout()  #自动调整子图参数,使之填充整个图像区域
plt.show()

output_36_1

可以观察到,线性核函数和多项式核函数在非线性数据上表现会浮动,如果数据相对线性可分,则表现不错,如果是像环形数据那样彻底不可分的,则表现糟糕。在线性数据集上,线性核函数和多项式核函数即便有扰动项也可以表现不错,可见多项式核函数是虽然也可以处理非线性情况,但更偏向于线性的功能

Sigmoid核函数就比较尴尬了,它在非线性数据上强于两个线性核函数,但效果明显不如rbf,它在线性数据上完全比不上线性的核函数们,对扰动项的抵抗也比较弱,所以它功能比较弱小,很少被用到。

rbf,高斯径向基核函数基本在任何数据集上都表现不错,属于比较万能的核函数。个人的经验是,无论如何先试试看高斯径向基核函数,它适用于核转换到很高的空间的情况,在各种情况下往往效果都很不错,如果rbf效果不好,那我们再试试看其他的核函数。另外,多项式核函数多被用于图像处理之中。

2.4 探索核函数的优势和缺陷

看起来,除了Sigmoid核函数,其他核函数效果都还不错。但其实rbf和poly都有自己的弊端,我们使用乳腺癌数据集作为例子来展示一下:

from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
from time import time
import datetime

data = load_breast_cancer()
X = data.data  #(569, 30)
y = data.target  # 0 1
# np.unique(y)
# plt.scatter(X[:, 0], X[:, 1], c=y)
# plt.show()

Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, y, test_size=0.3, random_state=420)
# Kernel = ["linear", "poly", "rbf", "sigmoid"]
# 如果使用上面一行的核函数,会发现怎么跑都跑不出来。模型一直停留在线性核函数之后,就没有再打印结果了。
# 这证明,多项式核函数此时此刻要消耗大量的时间,运算非常的缓慢。让我们在循环中去掉多项式核函数,使用下面一行的核函数,再试试看能否跑出结果:
# Kernel = ["linear", "rbf", "sigmoid"]
# 我们可以有两个发现。首先,乳腺癌数据集是一个线性数据集,线性核函数跑出来的效果很好。rbf和sigmoid两个
# 擅长非线性的数据从效果上来看完全不可用。其次,线性核函数的运行速度远远不如非线性的两个核函数。
# 如果数据是线性的,那我们取消degree参数注释,调整为1,多项式核函数应该也可以得到不错的结果:
Kernel = ["linear", "poly", "rbf", "sigmoid"]

for kernel in Kernel:
    time0 = time()
    clf = SVC(kernel=kernel, gamma="auto"
              , degree=1
              , cache_size=5000  #使用计算机内存作为缓存(MB),加快运算速度
              ).fit(Xtrain, Ytrain)
    print("The accuracy under kernel %s is %f" % (kernel, clf.score(Xtest, Ytest)))
    print(datetime.datetime.fromtimestamp(time() - time0).strftime("%M:%S:%f"))  # 分钟:秒:微秒
The accuracy under kernel linear is 0.929825
00:00:500026
The accuracy under kernel poly is 0.923977
00:00:093756
The accuracy under kernel rbf is 0.596491
00:00:062517
The accuracy under kernel sigmoid is 0.596491
00:00:000000

一个简单函数示例:

from time import time
import datetime

# time()函数用于获取当前时间戳
now = time()
datetime.datetime.fromtimestamp(now).strftime("%Y-%m-%d %H:%M:%S")  # 格式化日期
'2023-05-01 14:40:02'

多项式核函数的运行速度立刻加快了,并且精度也提升到了接近线性核函数的水平,可喜可贺。但是,我们之前的实验中,我们了解说,rbf在线性数据上也可以表现得非常好,那在这里,为什么跑出来的结果如此糟糕呢?

其实,这里真正的问题是数据的量纲问题。回忆一下我们如何求解决策边界,如何判断点是否在决策边界的一边?是靠计算”距离“,虽然我们不能说SVM是完全的距离类模型,但是它严重受到数据量纲的影响。让我们来探索一下乳腺癌数据集的量纲:

import pandas as pd

data = pd.DataFrame(X)
data.describe([0.01, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99]).T

一眼望去,果然数据存在严重的量纲不一的问题。我们来使用数据预处理中的标准化的类,对数据进行标准化:

from sklearn.preprocessing import StandardScaler

X = StandardScaler().fit_transform(X)  #标准化数据,保证每个维度的特征数据方差为1,均值为0
data = pd.DataFrame(X)
data.describe([0.01, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99]).T

标准化完毕后,再次让SVC在核函数中遍历,此时我们把degree的数值设定为1,观察各个核函数在去量纲后的数据上的表现:

Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, y, test_size=0.3, random_state=420)
Kernel = ["linear", "poly", "rbf", "sigmoid"]

for kernel in Kernel:
    time0 = time()
    clf = SVC(kernel=kernel, gamma="auto", degree=1, cache_size=5000).fit(Xtrain, Ytrain)
    print("The accuracy under kernel %s is %f" % (kernel, clf.score(Xtest, Ytest)))
    print(datetime.datetime.fromtimestamp(time() - time0).strftime("%M:%S:%f"))  # 分钟:秒:微秒
The accuracy under kernel linear is 0.976608
00:00:015632
The accuracy under kernel poly is 0.964912
00:00:000000
The accuracy under kernel rbf is 0.970760
00:00:000000
The accuracy under kernel sigmoid is 0.953216
00:00:015620

量纲统一之后,可以观察到,所有核函数的运算时间都大大地减少了,尤其是对于线性核来说,而多项式核函数居然变成了计算最快的。其次,rbf表现出了非常优秀的结果。经过我们的探索,我们可以得到的结论是:

  1. 线性核,尤其是多项式核函数在高次项时计算非常缓慢
  2. rbf和多项式核函数都不擅长处理量纲不统一的数据集

幸运的是,这两个缺点都可以由数据无量纲化来解决。因此,SVM执行之前,非常推荐先进行数据的无量纲化!到了这一步,我们是否已经完成建模了呢?虽然线性核函数的效果是最好的,但它是没有核函数相关参数可以调整的,rbf和多项式却还有着可以调整的相关参数,接下来我们就来看看这些参数。

2.5 选取与核函数相关的参数:degree & gamma & coef0

image-20230501155651793

在知道如何选取核函数后,我们还要观察一下除了kernel之外的核函数相关的参数。对于线性核函数,"kernel"是唯一能够影响它的参数,但是对于其他三种非线性核函数,他们还受到参数gamma,degree以及coef0的影响。参数gamma就是表达式中的 γ \gamma γ,degree就是多项式核函数的次数,参数coef0就是常数项。其中,高斯径向基核函数受到gamma的影响,而多项式核函数受到全部三个参数的影响。

但从核函数的公式来看,我们其实很难去界定具体每个参数如何影响了SVM的表现。当gamma的符号变化,或者degree的大小变化时,核函数本身甚至都不是永远单调的。所以如果我们想要彻底地理解这三个参数,我们要先推导出它们如何影响核函数地变化,再找出核函数的变化如何影响了我们的预测函数(可能改变我们的核变化所在的维度),再判断出决策边界随着预测函数的改变发生了怎样的变化。无论是从数学的角度来说还是从实践的角度来说,这个过程太复杂也太低效。所以,我们往往避免去真正探究这些参数如何影响了我们的核函数,而直接使用学习曲线或者网格搜索来帮助我们查找最佳的参数组合

对于高斯径向基核函数,调整gamma的方式其实比较容易,那就是画学习曲线。我们来试试看高斯径向基核函数rbf的参数gamma在乳腺癌数据集上的表现:

score = []
gamma_range = np.logspace(-10, 1, 50)  # 生成对数空间(默认以10为底)中等间隔的数值序列
for i in gamma_range:
    clf = SVC(kernel="rbf", gamma=i, cache_size=5000).fit(Xtrain, Ytrain)
    score.append(clf.score(Xtest, Ytest))

print(max(score), gamma_range[score.index(max(score))])
plt.plot(gamma_range, score)
plt.show()
0.9766081871345029 0.012067926406393264

output_53_1

通过学习曲线,很容就找出了rbf的最佳gamma值。但我们观察到,这其实与线性核函数的准确率一模一样之前的准确率。我们可以多次调整gamma_range来观察结果,可以发现97.6608%应该是rbf核函数的极限了。

但对于多项式核函数来说,一切就没有那么容易了,因为三个参数共同作用在一个数学公式上影响它的效果,因此我们往往使用网格搜索来共同调整三个对多项式核函数有影响的参数。依然使用乳腺癌数据集。

from sklearn.model_selection import GridSearchCV  #网格搜索
from sklearn.model_selection import StratifiedShuffleSplit
from time import time
import datetime

time0 = time()
gamma_range = np.logspace(-10, 1, 20)
coef0_range = np.linspace(0, 5, 10)

param_grid = dict(gamma=gamma_range, coef0=coef0_range)  # 参数字典
cv = StratifiedShuffleSplit(n_splits=5, test_size=0.3, random_state=420)  # 划分5次,每次都使用全部样本
grid = GridSearchCV(SVC(kernel="poly", degree=1, cache_size=5000), param_grid=param_grid, cv=cv)  #创建需要调优的模型对象
grid.fit(X, y)  #训练模型,找到最佳参数

print("The best parameters are %s with a score of %0.5f" % (grid.best_params_, grid.best_score_))
print(datetime.datetime.fromtimestamp(time() - time0).strftime("%M:%S:%f"))  # 分钟:秒:微秒
The best parameters are {'coef0': 0.0, 'gamma': 0.18329807108324375} with a score of 0.96959
00:06:543634

可以发现,网格搜索为我们返回了参数coef0=0,gamma=0.18329807108324375,但整体的分数是0.96959,虽然比调参前略有提高,但依然没有超过线性核函数核和rbf的结果。可见,如果最初选择核函数的时候,你就发现多项式的结果不如rbf和线性核函数,那就不要挣扎了,试试看调整rbf或者直接使用线性

2.6 硬间隔与软间隔:重要参数C

image-20230501160936771

在支持向量机(SVM)中,惩罚项C是一个用于控制模型复杂度和对训练误差的惩罚力度的超参数。它在SVM中的作用类似于正则化项在线性回归中的作用

C的取值越大,意味着对于误分类样本的惩罚越严厉,模型的复杂度越高,更多的样本会被归为支持向量。相反,当C的取值较小时,模型的复杂度降低,对误分类的惩罚也相应减轻,更多的样本会被归为非支持向量。

当C取值过大时,模型可能会出现过拟合的情况;当C取值过小时,模型可能会出现欠拟合的情况。因此,需要在训练过程中对C进行调参,以获得最佳的分类效果。

需要注意的是,不同的C值对模型的预测性能有着不同的影响,需要根据具体问题进行选择和调整。

image-20230501161031074

# 调线性核函数
score = []
C_range = np.linspace(0.01, 30, 50)
for i in C_range:
    clf = SVC(kernel="linear", C=i, cache_size=5000).fit(Xtrain, Ytrain)
    score.append(clf.score(Xtest, Ytest))
print(max(score), C_range[score.index(max(score))])
plt.plot(C_range, score)
plt.show()
0.9766081871345029 1.2340816326530613

output_58_1

# 调rbf核函数
score = []
C_range = np.linspace(0.01, 30, 50)
for i in C_range:
    clf = SVC(kernel="rbf", C=i, gamma=0.012067926406393264, cache_size=5000).fit(Xtrain, Ytrain)
    score.append(clf.score(Xtest, Ytest))
print(max(score), C_range[score.index(max(score))])
plt.plot(C_range, score)
plt.show()
0.9824561403508771 6.7424489795918365

output_59_1

# 进一步细化rbf
score = []
C_range = np.linspace(5, 7, 1000)
for i in C_range:
    clf = SVC(kernel="rbf", C=i, gamma=0.012067926406393264, cache_size=5000).fit(Xtrain, Ytrain)
    score.append(clf.score(Xtest, Ytest))
print(max(score), C_range[score.index(max(score))])
plt.plot(C_range, score)
plt.show()
0.9824561403508771 6.211211211211211

output_60_1

此时,我们找到了乳腺癌数据集上的最优解:rbf核函数下的98.246%的准确率。当然,我们还可以使用交叉验证来改进我们的模型,获得不同测试集和训练集上的交叉验证结果。但上述过程,为大家展现了如何选择正确的核函数,以及如何调整核函数的参数。

3 附录

3.1 SVC的参数列表

image-20230501161357270

image-20230501161436668

image-20230501161506355

3.2 SVC的属性列表

image-20230501161620255

3.3 SVC的接口列表

image-20230501161701663

image-20230501161723865

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/480369.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java笔试强训 27】

🎉🎉🎉点进来你就是我的人了博主主页:🙈🙈🙈戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔🤺🤺🤺 目录 一、选择题 二、编程题 🔥 不用加…

VSCode下载、安装和简单配置

之前提到Python IDE的选择时,VSCode以其轻便、简洁、高效、专业等优点成为最适合做Python工程项目开发的IDE,本期就来详细讲解一下VSCode的一个下载、安装以及Python开发环境的配置。 一、下载 直接进入VSCode官网,选择对应系统版本的VSCod…

切片、索引和排序

关于使用Series切片带尾片的疑惑。 切片是数字的时候不带尾片 切片非数字时带尾片 索引 可以使用loc()和iloc()选择数据。轴标签(loc()),整数标签(iloc())。 # 第一行列名为’A‘,’B‘的行。 print( df.loc[1, [A, B]])# [0, 1)的列为 [B(1), A(0…

默认成员函数:详解类的隐式操作

目录 一.类的默认成员函数二.构造函数三.析构函数。四.拷贝构造函数五.赋值运算符重载 一.类的默认成员函数 类的默认成员函数就是定义一个类后,类会自动生成的成员函数,若我们显示定义则类不会自动生成。 二.构造函数 在数据结构学习阶段我们手撕过栈…

VC++ | MFC应用程序设计:框架搭建

VC | MFC应用程序设计:框架搭建 时间:2023-05-01 文章目录 VC | MFC应用程序设计:框架搭建1.启动程序2.新建项目2-1.新建项目2-2.应用程序类型2-3.文档模板属性2-4.用户界面功能2-5.高级功能选项2-6.生成的类2-7.解决方案资源管理器 3.工程文…

如何在外远程控制我的世界服务器 - MCSM面板【端口映射】

文章目录 概述1.MCSManager 安装2.内网穿透2.1 安装cpolar内网穿透 3. 访问公网地址4.固定公网地址4.1 保留一个二级子域名4.2 配置固定二级域名4.3 访问固定公网地址 5. 设置节点公网地址6. 固定节点公网地址6.1 保留一个固定tcp地址6.2 配置固定TCP地址 转载自远程穿透文章&a…

【Latex】有关于Latex tabularray的一些很不错的教程、模板

1. 简介: 除了大家熟知的tabular,Latex在2021年出了一个table排版的新包:tabularray。 笔者这几天初步体验了一下tabularray,个人觉得tabularray明显比tabular的使用体感好不少。 不管是从排版的效果、便捷程度,还是…

基于NumPy构建LSTM模块并进行实例应用(附代码)

文章目录 0. 前言0.1 读本文前的必备知识 1. LSTM架构2. LSTM正向传播代码实现2.1 隐藏层正向传播2.2 输出层正向传播 3. LSTM反向传播代码实现3.1 输出层反向传播3.2 隐藏层反向传播 4. 实例应用说明5. 运行结果6. 后记6 完整代码 0. 前言 按照国际惯例,首先声明&a…

目标跟踪--卡尔曼滤波 与 匈牙利算法

目前主流的目标跟踪算法都是基于Tracking-by-Detecton策略,即基于目标检测的结果来进行目标跟踪。 跟踪结果中,每个bbox左上角的数字是用来标识某个人的唯一ID号。那么问题就来了,视频中不同时刻的同一个人,位置发生了变化&#x…

西瓜书读书笔记整理(三)—— 第二章 模型评估与选择

第二章 模型评估与选择 第 2 章 模型评估与选择2.1 经验误差与过拟合1. 错误率 / 精度 / 误差2. 训练误差 / 经验误差 / 泛化误差3. 过拟合 / 欠拟合4. 学习能力5. 模型选择 2.2 评估方法1. 评估方法概述2. 留出法3. 交叉验证法4. 自助法5. 调参 / 最终模型 2.3 性能度量1. 回归…

【JavaEE】UDP数据报套接字—实现回显服务器(网络编程)

博主简介:想进大厂的打工人博主主页:xyk:所属专栏: JavaEE初阶 本篇文章将带你了解什么是网络编程? 网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输&am…

中断-STM32

中断-STM32 中断:在主程序运行过程中,出现了特定的中断触发条件 (中断源),使得CPU暂停当前正在运行的程序转而去处理中断程序处理完成后又返回原来被暂停的位置继续运行。 中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓…

Java程序猿搬砖笔记(十一)

文章目录 Hexo博客 Next主题图片防盗链问题Springboot Druid数据库密码加密配置步骤Java统计字符串出现的次数Java获取某个字符在字符串中出现第N次的位置Maven激活指定profileMaven中resources标签的用法详解MySQL 字符集不一致报错EasyExcel日期格式化Configuration、Compone…

gradle Task 详解

Task定义和配置 查看工程下所有的task,使用如下命令 gradle tasks 定义一个task task创建的源码 参数分别是 task 名称,和一个 closure。groovy语法的closure可以写在小括号外面,小括号可以省略 task的源码 public interface Task extends…

【Java笔试强训 25】

🎉🎉🎉点进来你就是我的人了博主主页:🙈🙈🙈戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔🤺🤺🤺 目录 一、选择题 二、编程题 🔥星际密码…

RabbitMQ死信队列延迟交换机

RabbitMQ死信队列&延迟交换机 1.什么是死信 死信&死信队列 死信队列的应用: 基于死信队列在队列消息已满的情况下,消息也不会丢失实现延迟消费的效果。比如:下订单时,有15分钟的付款时间 2. 实现死信队列 2.1 准备E…

网络编程代码实例:IO复用版

文章目录 前言代码仓库内容代码(有详细注释)server.cclient_select.cclient_poll.cclient_epoll.c 结果总结参考资料作者的话 前言 网络编程代码实例:IO复用版。 代码仓库 yezhening/Environment-and-network-programming-examples: 环境和…

[Linux]网络连接、资源共享

​⭐作者介绍:大二本科网络工程专业在读,持续学习Java,输出优质文章 ⭐作者主页:逐梦苍穹 ⭐所属专栏:Linux基础操作。本文主要是分享一些Linux系统常用操作,内容主要来源是学校作业,分享出来的…

详解c++---vector模拟实现

目录标题 准备工作构造函数迭代器的完善性质相关的函数实现reservepush_back[ ]emptyresizeinserteraseerase后迭代器失效问题swapclear~vector老式拷贝构造迭代器构造新式拷贝构造老式赋值重载新式赋值重载N个数据的构造vector的浅拷贝问题 准备工作 首先我们知道vector是一个…

HTB靶机06-Beep-WP

beep 靶机IP:10.10.10.7 攻击机IP:10.10.14.6 web RCE漏洞利用、nmap提权 扫描 nmap 常规扫描: ┌──(xavier㉿xavier)-[~/HTB/005-Beep] └─$ sudo nmap -sSV -sC 10.10.10.7 -oN nmap1.out Starting Nmap 7.91 ( https://nmap.org …