使用不同 SVM 核函数绘制分类边界#

本示例展示了 SVC(支持向量分类器)中不同核函数如何影响二元二维分类问题中的分类边界。

SVC 旨在通过最大化每个类最外层数据点之间的间隔,找到一个有效分离训练数据中类别的超平面。这通过找到定义决策边界超平面并最小化错误分类样本的合页损失(通过 hinge_loss 函数衡量)总和的最佳权重向量 \(w\) 来实现。默认情况下,正则化通过参数 C=1 应用,这允许一定程度的错误分类容忍度。

如果数据在原始特征空间中不是线性可分的,可以设置非线性核参数。根据核函数,该过程涉及添加新特征或转换现有特征,以丰富数据并可能增加数据的意义。当设置除 "linear"` 之外的核函数时,SVC 会应用核技巧,它使用核函数计算数据点对之间的相似性,而无需显式地转换整个数据集。核技巧通过仅考虑所有数据点对之间的关系,避免了对整个数据集进行矩阵转换的必要性。核函数将两个向量(每对观测值)通过它们的点积映射到它们的相似性。

然后可以使用核函数计算超平面,就像数据集在更高维空间中表示一样。使用核函数而不是显式矩阵转换可以提高性能,因为核函数的时间复杂度为 \(O({n}^2)\),而矩阵转换的复杂度取决于所应用的具体转换。

在本示例中,我们比较了支持向量机最常见的核函数类型:线性核("linear")、多项式核("poly")、径向基函数核("rbf")和 sigmoid 核("sigmoid")。

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

创建数据集#

我们创建一个包含 16 个样本和两个类别的二维分类数据集。我们用与各自目标匹配的颜色绘制样本。

import matplotlib.pyplot as plt
import numpy as np

X = np.array(
    [
        [0.4, -0.7],
        [-1.5, -1.0],
        [-1.4, -0.9],
        [-1.3, -1.2],
        [-1.1, -0.2],
        [-1.2, -0.4],
        [-0.5, 1.2],
        [-1.5, 2.1],
        [1.0, 1.0],
        [1.3, 0.8],
        [1.2, 0.5],
        [0.2, -2.0],
        [0.5, -2.4],
        [0.2, -2.3],
        [0.0, -2.7],
        [1.3, 2.1],
    ]
)

y = np.array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1])

# Plotting settings
fig, ax = plt.subplots(figsize=(4, 3))
x_min, x_max, y_min, y_max = -3, 3, -3, 3
ax.set(xlim=(x_min, x_max), ylim=(y_min, y_max))

# Plot samples by color and add legend
scatter = ax.scatter(X[:, 0], X[:, 1], s=150, c=y, label=y, edgecolors="k")
ax.legend(*scatter.legend_elements(), loc="upper right", title="Classes")
ax.set_title("Samples in two-dimensional feature space")
_ = plt.show()
Samples in two-dimensional feature space

我们可以看到,样本不能被一条直线清晰地分离。

训练 SVC 模型并绘制决策边界#

我们定义一个函数,该函数拟合一个 SVC 分类器,允许将 kernel 参数作为输入,然后使用 DecisionBoundaryDisplay 绘制模型学习到的决策边界。

请注意,为简单起见,在本示例中,C 参数设置为其默认值(C=1),并且所有核函数的 gamma 参数都设置为 gamma=2,尽管对于线性核函数它会自动被忽略。在实际分类任务中,如果性能很重要,强烈建议进行参数调优(例如使用 GridSearchCV),以捕获数据中不同的结构。

DecisionBoundaryDisplay 中设置 response_method="predict" 会根据预测类别对区域进行着色。使用 response_method="decision_function" 允许我们同时绘制决策边界及其两侧的间隔。最后,通过训练后的 SVC 的 support_vectors_ 属性识别训练期间使用的支持向量(它们始终位于间隔上),并将其绘制出来。

from sklearn import svm
from sklearn.inspection import DecisionBoundaryDisplay


def plot_training_data_with_decision_boundary(
    kernel, ax=None, long_title=True, support_vectors=True
):
    # Train the SVC
    clf = svm.SVC(kernel=kernel, gamma=2).fit(X, y)

    # Settings for plotting
    if ax is None:
        _, ax = plt.subplots(figsize=(4, 3))
    x_min, x_max, y_min, y_max = -3, 3, -3, 3
    ax.set(xlim=(x_min, x_max), ylim=(y_min, y_max))

    # Plot decision boundary and margins
    common_params = {"estimator": clf, "X": X, "ax": ax}
    DecisionBoundaryDisplay.from_estimator(
        **common_params,
        response_method="predict",
        plot_method="pcolormesh",
        alpha=0.3,
    )
    DecisionBoundaryDisplay.from_estimator(
        **common_params,
        response_method="decision_function",
        plot_method="contour",
        levels=[-1, 0, 1],
        colors=["k", "k", "k"],
        linestyles=["--", "-", "--"],
    )

    if support_vectors:
        # Plot bigger circles around samples that serve as support vectors
        ax.scatter(
            clf.support_vectors_[:, 0],
            clf.support_vectors_[:, 1],
            s=150,
            facecolors="none",
            edgecolors="k",
        )

    # Plot samples by color and add legend
    ax.scatter(X[:, 0], X[:, 1], c=y, s=30, edgecolors="k")
    ax.legend(*scatter.legend_elements(), loc="upper right", title="Classes")
    if long_title:
        ax.set_title(f" Decision boundaries of {kernel} kernel in SVC")
    else:
        ax.set_title(kernel)

    if ax is None:
        plt.show()

线性核#

线性核是输入样本的点积

\[K(\mathbf{x}_1, \mathbf{x}_2) = \mathbf{x}_1^\top \mathbf{x}_2\]

然后它应用于数据集中任意两个数据点(样本)的组合。这两个点的点积决定了它们之间的 cosine_similarity(余弦相似度)。值越高,点越相似。

plot_training_data_with_decision_boundary("linear")
Decision boundaries of linear kernel in SVC

在线性核函数上训练 SVC 会产生一个未转换的特征空间,其中超平面和间隔是直线。由于线性核函数的表达能力不足,训练出的类别无法完美地捕获训练数据。

多项式核#

多项式核改变了相似性的概念。核函数定义为

\[K(\mathbf{x}_1, \mathbf{x}_2) = (\gamma \cdot \ \mathbf{x}_1^\top\mathbf{x}_2 + r)^d\]

其中 \({d}\) 是多项式的次数(degree),\({\gamma}\)gamma)控制每个独立训练样本对决策边界的影响,而 \({r}\) 是偏差项(coef0),用于向上或向下平移数据。在这里,我们使用核函数中多项式次数的默认值(degree=3)。当 coef0=0(默认值)时,数据仅被转换,但没有添加额外的维度。使用多项式核函数等同于创建 PolynomialFeatures,然后在转换后的数据上拟合一个带有线性核函数的 SVC,尽管对于大多数数据集来说,这种替代方法计算成本会很高。

plot_training_data_with_decision_boundary("poly")
Decision boundaries of poly kernel in SVC

带有 gamma=2 的多项式核函数能很好地适应训练数据,使超平面两侧的间隔相应地弯曲。

RBF 核#

径向基函数(RBF)核,也称为高斯核,是 scikit-learn 中支持向量机的默认核函数。它在无限维度中衡量两个数据点之间的相似性,然后通过多数投票进行分类。核函数定义为

\[K(\mathbf{x}_1, \mathbf{x}_2) = \exp\left(-\gamma \cdot {\|\mathbf{x}_1 - \mathbf{x}_2\|^2}\right)\]

其中 \({\gamma}\)gamma)控制每个独立训练样本对决策边界的影响。

两个点 \(\|\mathbf{x}_1 - \mathbf{x}_2\|^2\) 之间的欧氏距离越大,核函数越接近零。这意味着距离较远的两个点更可能不相似。

plot_training_data_with_decision_boundary("rbf")
Decision boundaries of rbf kernel in SVC

在图中我们可以看到决策边界如何趋向于围绕彼此靠近的数据点收缩。

Sigmoid 核#

Sigmoid 核函数定义为

\[K(\mathbf{x}_1, \mathbf{x}_2) = \tanh(\gamma \cdot \mathbf{x}_1^\top\mathbf{x}_2 + r)\]

其中核系数 \({\gamma}\)gamma)控制每个独立训练样本对决策边界的影响,而 \({r}\) 是偏差项(coef0),用于向上或向下平移数据。

在 sigmoid 核中,两个数据点之间的相似性是使用双曲正切函数(\(\tanh\))计算的。核函数对两个点(\(\mathbf{x}_1\)\(\mathbf{x}_2\))的点积进行缩放并可能进行平移。

plot_training_data_with_decision_boundary("sigmoid")
Decision boundaries of sigmoid kernel in SVC

我们可以看到,使用 sigmoid 核函数获得的决策边界呈现弯曲和不规则的形状。决策边界试图通过拟合一个 sigmoid 形的曲线来分离类别,导致一个复杂的边界,这可能对未见过的数据泛化能力不佳。从这个例子中,显而易见,sigmoid 核函数在处理呈现 sigmoid 形状的数据时具有非常特定的用例。在此示例中,仔细的微调可能会找到更具泛化性的决策边界。由于其特殊性,与其它核函数相比,sigmoid 核函数在实践中不那么常用。

结论#

在本示例中,我们可视化了使用所提供数据集训练的决策边界。这些图表直观地展示了不同核函数如何利用训练数据来确定分类边界。

超平面和间隔虽然是间接计算的,但可以想象为转换后特征空间中的平面。然而,在图中,它们是相对于原始特征空间表示的,因此多项式、RBF 和 sigmoid 核函数会产生弯曲的决策边界。

请注意,这些图表不评估单个核函数的准确性或质量。它们旨在提供对不同核函数如何使用训练数据的视觉理解。

为了进行全面评估,建议使用 SVC 参数进行微调,例如使用 GridSearchCV 等技术来捕获数据中的底层结构。

XOR 数据集#

一个经典的非线性可分数据集示例是 XOR 模式。这里我们演示了不同核函数在此类数据集上的表现。

xx, yy = np.meshgrid(np.linspace(-3, 3, 500), np.linspace(-3, 3, 500))
np.random.seed(0)
X = np.random.randn(300, 2)
y = np.logical_xor(X[:, 0] > 0, X[:, 1] > 0)

_, ax = plt.subplots(2, 2, figsize=(8, 8))
args = dict(long_title=False, support_vectors=False)
plot_training_data_with_decision_boundary("linear", ax[0, 0], **args)
plot_training_data_with_decision_boundary("poly", ax[0, 1], **args)
plot_training_data_with_decision_boundary("rbf", ax[1, 0], **args)
plot_training_data_with_decision_boundary("sigmoid", ax[1, 1], **args)
plt.show()
linear, poly, rbf, sigmoid

从上面的图中可以看出,只有 rbf 核函数能为上述数据集找到一个合理的决策边界。

脚本总运行时间: (0 分 1.350 秒)

相关示例

RBF SVM 参数

RBF SVM 参数

在鸢尾花数据集上绘制不同 SVM 分类器

在鸢尾花数据集上绘制不同 SVM 分类器

鸢尾花数据集上半监督分类器与 SVM 的决策边界

鸢尾花数据集上半监督分类器与 SVM 的决策边界

SVM:用于不平衡类别的分离超平面

SVM:用于不平衡类别的分离超平面

由 Sphinx-Gallery 生成的画廊