为了账号安全,请及时绑定邮箱和手机立即绑定

支持向量机分类器详解:迷你二维数据集的可视化指南

分类算法 找到最佳的‘分界线’?当然啦。

⛳️ 更多分类算法介绍: · [Dummy Classifier](https://medium.com/dummy-classifier-explained-a-visual-guide-with-code-examples-for-beginners-009ff95fc86e) · [KNN分类器](https://medium.com/k-nearest-neighbor-classifier-explained-a-visual-guide-with-code-examples-for-beginners-a3d85cad00e1) · [Bernoulli Naive Bayes](https://medium.com/bernoulli-naive-bayes-explained-a-visual-guide-with-code-examples-for-beginners-aec39771ddd6) · [Gaussian Naive Bayes](https://medium.com/gaussian-naive-bayes-explained-a-visual-guide-with-code-examples-for-beginners-04949cef383c) · [Decision Tree Classifier](https://medium.com/decision-tree-classifier-explained-a-visual-guide-with-code-examples-for-beginners-7c863f06a71e) · [Logistic Regression](https://medium.com/logistic-regression-explained-a-visual-guide-with-code-examples-for-beginners-81baf5871505) ▶ [Support Vector Classifier](https://medium.com/support-vector-classifier-explained-a-visual-guide-with-mini-2d-dataset-62e831e7b9e9) · [Multilayer Perceptron](https://medium.com/multilayer-perceptron-explained-a-visual-guide-with-mini-2d-dataset-0ae8100c5d1c)

“支持向量机(SVM)在分类方面的基本原理是——它试图找到最佳分界线来将两类数据分开。”但如果我再听到这个过于简化的解释,我可能会对着枕头大叫。

虽然前提听起来很简单,但支持向量机(SVM)却是一个充满数学技巧的算法,让我花了很长时间才理解。为什么它被称为“机器”?为什么我们需要支持向量?为什么有些点突然就变得不重要了?为什么它必须是直线——哦,等等,是超平面?然后是那个据说非常棘手的优化公式,所以才有了“对偶形式”这一版本。但等等,我们现在还需要一个叫SMO的算法来解决这个问题?为什么会有这些对偶系数,而且scikit-learn会直接输出它们?如果这还不够,当直线不起作用时,我们突然使用了这些所谓的“核技巧”?我们为什么需要这些技巧?为什么这些教程从来不会展示实际的数字?

在这篇文章中,我试图停止支持向量的疯狂。在花了数小时理解这个算法后,我将尝试用实际的数字和它的可视化来解释到底发生了什么,不涉及复杂的数学,非常适合初学者入门。

A cartoon magician with a top hat performs a classic “sawing a person in half” trick. The “split” person below represents data being separated, while the magician’s triumphant pose symbolizes the complex “magic” behind SVM’s seemingly simple separation principle.

所有视觉内容均由作者使用Canva Pro制作。优化了移动端显示,但在桌面上可能会显得过大。

定义:

支持向量机是一种主要用于分类任务的监督学习模型,不过也可以用于回归任务。SVM的目标是找到一条最佳地分割数据集的直线,最大化这些类别的间隔(唉……)。

虽然支持向量机(SVM)虽然复杂,它仍然可以被视为机器学习中一个基本的算法。

“支持向量”是那些离超平面最近的数据点,它们实际上也能定义这条超平面。那么,“机器”这个词又是怎么回事呢?其他机器学习算法可能也叫“机器”,而SVM的名字可能部分反映了它开发时的历史背景。

📊 数据集

要理解SVM是如何工作的,从少量样本和低维度的数据集开始是个好主意。我们将用一个简单的二维数据集(受[1]启发)做例子。

列:温度(0-3),湿度(0-3),打高尔夫(是/否)。训练数据集有2维和8个样本。

我们不会详细解释训练过程的每一步,而是会逐个解析关键术语,来看看SVM实际上是如何运作的:

第一部分:基本部分
决策边界

在支持向量机中,决策边界是算法确定的最佳分隔不同类别的数据的线(或在高维情况下称为“超平面”)。

这一行会把大多数“YES”点放在一边,而把大多数“NO”点放在另一边。但对于那些无法线性区分的数据,这条边界就无法做到完美——有些点可能会错位到“错误”的一边。

一旦划出了这条线,任何新的数据点就可以根据它落在哪边来分类。

在我们的例子中,这将是一条试图将“是”(打高尔夫)的点与“否”点分开的线。支持向量机(SVM)会尝试定位这条线,即使无法用一条直线完美地分开它们。从我们的眼睛来看,这似乎是一条不错的分界线。

线性分离性

线性可分性指的是我们是否可以画一条直线完美地分开两类数据点。如果数据是线性可分的,SVM 可以找到两类之间清晰明确的分界线。然而,当数据不可线性分隔(就像我们的情况)时,SVM 需要采用更复杂的方法。

在训练集中,不管我们怎么画这条线,都无法把这两类分开。如果我们去掉索引 1 和 8,现在就可以做到了。

留白

支持向量机(SVM)中的间隔是指决策边界与每个类别中最近的数据点之间的距离。这些最近的数据点被称为支撑向量。

SVM旨在最大化这个间隔。较大的间隔通常带来更好的泛化性能——即正确分类新未见过的数据点的能力。

然而,由于数据通常并不完全可分离,SVM 可能会采用软间隔策略。这允许一些点位于间隔内甚至错误的一侧,通过牺牲完美的分割来提高整体分类器的稳健性。

SVM会尝试将决策边界定位在尽可能大的间隔上,同时尽可能地将正例和反例分开。

硬边距与软边距

硬边距支持向量机(Hard Margin SVM,简称为硬SVM)是在理想场景中,所有数据点都能被决策边界完美分开,没有误分类的情况。在这种情况下,‘硬边距’的特点是不允许任何数据点出现在边界之外或者位于边距之内。

软间隔支持向量机(SVM)则允许一定程度的灵活性。它允许某些数据点被误分类或位于间隔区。这使得SVM能够在准确性与复杂度之间找到良好的平衡:

  1. 最大化间距
  2. 最小化分类失误

在我们的案例中,由于数据不是线性可分的,因此无法采用硬边距。因此,在我们的情况下,软边距方法是必要的。在这种情况下,采用软边距支持向量机时,你可能允许像ID 1和ID 8这样的点出现在边界“错误”的一侧,如果这样做能提高整体分类器的表现的话。

距离计算

在支持向量机(SVM)里,距离计算在训练和分类过程中都起着关键的作用。点 x 到决策边界的距离计算如下:

|w · x + b | / ||w || (注:此表达式表示向量_w_和_x_的点积加上偏置_b_的绝对值除以向量_w_的范数)

其中 w 是垂直于超平面的权重向量 wb 是偏置项,||w|| 是 w 的欧氏范数。

这样一来,我们就能看出哪些点最近超平面,不用画出来。

支持向量注:此处“支持向量”指技术领域的特定概念。

支持向量是距离超平面最近的数据点。它们之所以重要,是因为它们“撑住”超平面,定义了它的位置。

支撑向量之所以特别是因为这些点才是确定决策边界的关键。所有其他点都可以移除,而不会影响边界的位置。这正是支持向量机的一个关键特点——它仅基于最关键的数据点来做决定,而不是基于所有数据点。

对于这个超平面,我们有这3个位于间隔边界上的支撑向量。在某些特定情况下,这些另外两个误分类的数据点也可以被视为支撑向量。

松量变量

松弛变量在软间隔支持向量机中被引入,用来量化每个数据点的分类错误或间隔违规的程度。它们被称作“松弛变量”,因为它们给模型提供了一定的灵活性,使其在适应数据时更宽松。

在支持向量机(SVM)中,松弛变量 ξᵢ 可以这样计算,如下所示:

误差 ξᵢ 定义为最大值,其值要么为0,要么为1减去标签 yᵢ 乘以特征向量 xᵢ 和权重 w 的点积加上偏置 b 的结果。其中,ξᵢ 表示误差,yᵢ 表示类别标签,wxᵢ 分别表示权重和特征向量,b 表示偏置。

其中
· w 是权重向量
· b 是偏置项
· xᵢ 是输入向量
· yᵢ 是对应的标签

此公式仅在类别标签 yᵢ 采用 {-1, +1} 格式时才适用。它以优雅的方式处理了这两种情况:
· 正确分类且远离间隔的点:ξᵢ = 0
· 错误分类或位于间隔内的点:ξᵢ > 0

使用{-1, +1}标签的方式能够保持SVM的数学对称性特点并简化优化步骤,而{0, 1}标签则需要为每个类分别处理。

在我们的数据集中,点 (3,3) — NO 却落在了边界线的“YES”一侧。我们会为此点分配一个松弛变量来衡量其偏离边界程度。同样地,如果 (2,0) — NO 虽被正确分类,但落在了边界内,也会为其分配一个松弛变量。

在我们的数据集中,点 (3,3) — NO 落在了“YES”这边的边界线。我们会为此点分配一个松弛变量来衡量它偏离边界线有多远。同样地,如果 (2,0) — NO 虽然被正确分类,但接近边界,也会给它分配一个松弛变量。

原始的硬边际形式

原始形式是指支持向量机(SVM)的原始优化问题表述。它直接描述了在特征空间中寻找最大间隔超平面。

简单来说,原始形态试图做到:

  1. 找到一个能够正确分类所有数据样本的超平面。
  2. 最大化这个超平面与各个类别最近数据样本之间的距离。

原始形式是。

最小化 : (1/2) ||w ||²
满足以下条件 : yᵢ(w · xᵢ + b) ≥ 1 对所有 i

其中
· w 是权重向量
· b 是偏置
· 这些 xᵢ 是输入向量
· yᵢ 是对应的标签(+1 或 -1)
· ||w ||² 是 w 的L2范数的平方

在省略索引1和8的时候,我们正在尝试找到具有更大间距的最佳线。

如果我们选择更小的间隔的超平面,目标函数值会更高,这可不是我们想要的结果。

软边界的原始形式

请记住,软间隔支持向量机(SVM)是对原始(硬间隔)SVM的一种扩展,允许一定程度的误分类。这种变化体现在原形式中。软间隔SVM的原形式如下:

最小化:(1/2) ||w| |² + C Σ ᵢ ξᵢ
满足条件yᵢ(w · xᵢ + b) ≥ 1 — ξᵢ 对于所有的 iξᵢ ≥ 0 对于所有的 i

其中
· C 是惩罚参数
· ξᵢ 是松弛变量
· 剩余所有变量与硬边距情况相同.

误分类数据点的惩罚会加入到目标函数中,作为需要最小化的附加项。

如果我们选择一个稍微更接近索引8的超平面呢?现在的目标值提高了。错分类样本离超平面的距离越均匀,总的惩罚值就越小。

双重形态

这里有糟糕的消息:原始形态可能既慢又难解,特别是在复杂数据的情况下。

双重形式提供了一种替代方案来解决SVM优化问题,通常带来计算上的优势。其形式如下所示:

最大化目标 : Σᵢ,ⱼ(αᵢyᵢ) - ½ΣᵢΣⱼ(αᵢαⱼyᵢyⱼ(xᵢ · _xⱼ))
约束条件为: 0 ≤ αᵢ ≤ C 对于所有的 i,Σ ᵢαᵢyᵢ = 0

位置:
· αᵢ 是拉格朗日乘子(对偶)
· yᵢ 是标签(+1 或 -1)
· xᵢ 是输入向量
· C 是正则化系数(αᵢ 的上限)
· (xᵢ · xⱼ) 指的是 xᵢxⱼ 的点积

除了训练数据本身之外,这种形式中的唯一其他部分是拉格朗日乘子(αᵢ)。

拉格朗日乘子法

在对偶形式中,我们注意到,拉格朗日乘子(αᵢ)出现,这是在把原问题转化为对偶形式的过程中(这也是为什么它们也被称为对偶系数)。你也许注意到了,权重和偏置项已经不复存在了!

训练集中的每个数据点都对应一个拉格朗日乘数。这样做更方便理解的是,拉格朗日乘数使事情变得简单明了。

  1. 解释说明
    - αᵢ = 0:该点被正确分类并且位于间隔区之外,此点不会影响决策边界。
    - 0 < αᵢ < C :该点位于间隔边界上,这些点被称为“自由”或“非边界”的支持向量。
    - αᵢ = C :该点位于间隔中或间隔内(包括那些被错误分类的点),这些点被称为“有界”的支持向量。
  2. 与决策边界的关系
    公式 w = Σ (αᵢ yᵢ xᵢ),
    其中 b = yᵢ — Σ (αᵢ yⱼ(xⱼ · xᵢ))
    其中 yᵢ 是任何(非边界)支持向量的标签。
    这意味着最终的决策边界仅由非零 αᵢ 的点决定!

算法最终决定我们的原始超平面是最优的,只需要把所有权重减半来加大间隔就行了。这样所有点都会变成支持向量,不过因为数据集本身不大,所以也没关系。😅

顺序最小化优化

我们还没有真正展示如何找到最优的拉格朗日乘子(αᵢ),对吧?其实,解决这个问题的方法叫做序列最小优化(SMO)。下面是一个简化的过程,说明我们如何得到这些值:

  1. 从所有 αᵢ 均初始化为零开始。
  2. 反复选择并调整两个 αᵢ,以优化解。
  3. 快速应用简单的数学公式更新这些配对。
  4. 确保每次更新都满足 SVM 的约束条件。
  5. 重复上述步骤,直到所有的 αᵢ 都达到“足够好”的程度。
  6. 当 αᵢ > 0 时,该点即为支持向量。

这种方法有效地解决了SVM优化,无需复杂的计算,使得它在大规模数据集上也能运行得很好。

做决定的函数

在通过使用对偶形式解决了SVM优化问题并获得了拉格朗日乘子之后,我们可以定义决策函数。这个函数用于决定新输入的数据点是如何被训练好的SVM模型分类的。

f(x) = Σ (αᵢyᵢ(xᵢ · x)) + b
在机器学习中,这个公式表示... (其中 f(x) 表示...)

在这里,αᵢ 是拉格朗日乘子,yᵢ 是类别标签 yᵢ(+1 或 -1),xᵢ 是支持向量,而 x 是待分类的输入数据。对于新的输入数据点 x,最终的分类结果取决于 f(x) 的正负值,结果要么为正,要么为负。

注意,这个决策函数仅使用非零_αᵢ_的那些数据点用于对新输入进行分类,这正是SVM算法的核心思想之一。

🌟 支持向量机分类器的代码

下面的代码可以得到这些结果:

    import numpy as np  
    import pandas as pd  
    from sklearn.svm import SVC  

    # 创建并拟合SVC模型如下  
    df = pd.DataFrame({  
        '🌞': [0, 1, 1, 2, 3, 3, 2, 3, 0, 0, 1, 2, 3],  
        '💧': [0, 0, 1, 0, 1, 2, 3, 3, 1, 2, 3, 2, 1],  
        'y': [1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1]  
    }, index=range(1, 14))  

    # 划分训练集和测试集如下  
    train_df, test_df = df.iloc[:8].copy(), df.iloc[8:].copy()  
    X_train, y_train = train_df[['🌞', '💧']], train_df['y']  
    X_test, y_test = test_df[['🌞', '💧']], test_df['y']  

    # 创建并拟合SVC模型如下  
    svc = SVC(kernel='linear', C=2)  
    svc.fit(X_train, y_train)  

    # 添加拉格朗日乘子和支持向量状态如下  
    train_df['α'] = 0.0  
    train_df.loc[svc.support_ + 1, 'α'] = np.abs(svc.dual_coef_[0])  
    train_df['是否SV'] = train_df.index.isin(svc.support_ + 1)  

    print("训练数据、拉格朗日乘子和支持向量如下:")  
    print(train_df)  

    # 打印模型参数如下  
    w, b = svc.coef_[0], svc.intercept_[0]  
    print(f"\n模型参数如下:")  
    print(f"权重 w: [{w[0]}, {w[1]}]")  
    print(f"偏置 b: {b}")  
    print(f"决策函数:f(🌞, 💧) = {w[0]}*🌞 + {w[1]}*💧 + {b}")  

    # 进行预测如下  
    test_df['ŷ'] = svc.predict(X_test)  

    print("\n测试数据和预测结果如下:")  
    print(test_df)

第二部分:核技巧(Kernel Trick)

如我们迄今为止所见,无论我们怎么设定超平面,都无法完美地将这两类分开……尽管通过一些“小技巧”可以让它们变得可分,虽然不再是线性可分的了。

输入域与特征空间

输入空间指的是你的数据特征的原始空间,也称为特征空间,这在数据科学领域更常见。在我们的高尔夫数据集中,输入空间是二维的,包含温度和湿度这两个特征。该空间中的每个数据点代表了某人在这种天气条件下是否决定去打高尔夫。

另一方面来说,特征空间是输入空间转换后的版本,在该空间中SVM实际上是在执行分类。有时,原先在输入空间中无法线性分割的数据在映射到更高维度的特征空间后,就可以进行线性分割了。

到目前为止,我们尝试过之后,无论我们选择什么样的超平面,都无法将这两个类别线性分离。与其仅仅使用 🌞 和 💧,特征空间可能包括像 🌞², 💧², 🌞×💧 这样的组合。这将把我们的输入空间转换为五维特征空间。如果你仔细观察,你现在可以找到一个能够完美分离这两个类别的超平面!

核心和隐式转换

核函数是一种用于计算两个数据点之间相似度的函数,隐式地将它们映射到更高维度的空间(即特征空间)。

有个函数 φ(x),它会把每个输入点 x 变换到更高维的向量空间里。比如:φ : ℝ² → ℝ³,φ(x, y) = (x, y, _x_² + _y_²)

常用的核函数及其隐含变换:
a. 线性核 : K(x, y) = x · y
- 变换为:
φ(x) = x (恒等变换)
- 这实际上并不改变空间,但对于线性可分的数据来说非常有用。

b. 多项式核函数K(x ,y) = (x · y + c)
- 对于 d = 2, c = 1 的变换(在二维实数空间 ℝ² 中):
φ(x ₁,x ₂) = (1, √2 x ₁, √2 x ₂, x ₁², √2 xx ₂, x ₂²)
- 这涵盖了所有最高次数为 d 的多项式项。

c. RBF核K(x,y) = exp(-γ ||x - y ||²)
- 转换(作为无穷级数):
φ(_x_₁,_x_₂)= exp(-γ ||x ||²) * (1, √(2 γ)_x_₁, √(2 γ)_x_₂, …, √(2 _γ_²/2!)_x_₁², √(2 _γ_²/2!)_x_₁ _x_₂, √(2 _γ_²/2!)_x_₂², …, √(2 γⁿ/ n!)_x_₁ , …)
- 可以认为这是一种相似度度量,距离越大,其值越小。

这是为了说明核是如何在输入空间上进行转换的。实际上,由于计算成本较高,并不会直接计算每个点,因此称之为隐式计算。

核心技巧

kernel trick的巧妙之处在于,我们可以通过核函数在这个更高维度的空间里进行操作,而无需显式计算这个φ(x)变换。

注意,在双形式或双重结构中,数据点仅以点积 xᵢ · xⱼ 的形式出现。这就是核技巧派上用场的地方。我们可以用核函数 K 来替换这个点积,将 xᵢ · xⱼ 替换成 K(xᵢ , xⱼ)。

如果我们只用原始形式,这一步骤就无法完成,这也是为什么我们更倾向于使用双形式的主要原因。这也说明了为什么双形式更受欢迎的主要原因之一。

这种替换方式隐含地将数据映射到“高维空间”,而无需显式计算该变换过程,

决策过程与核技巧小妙招

对于一个新的点 x ,我们得到的决策函数是:

f(x) = 符号(∑ _ᵢ_ _αᵢyᵢ核函数(xᵢ , x) + b)

其中求和是针对所有支持向量(即αᵢ > 0的点),而不必在句末加上“进行的”。

🌟 支持向量机分类器(利用核技巧)代码概览 🌟

以下是一个可以得到这些结果的代码:

    import numpy as np  
    import pandas as pd  
    from sklearn.svm import SVC  

    # 创建DataFrame  
    df = pd.DataFrame({  
        '🌞': [0, 1, 1, 2, 3, 3, 2, 3, 0, 0, 1, 2, 3],  
        '💧': [0, 0, 1, 0, 1, 2, 3, 3, 1, 2, 3, 2, 1],  
        'y': [1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1]  
    }, index=range(1, 14))  

    # 划分训练集和测试集  
    train_df, test_df = df.iloc[:8].copy(), df.iloc[8:].copy()  
    X_train, y_train = train_df[['🌞', '💧']], train_df['y']  
    X_test, y_test = test_df[['🌞', '💧']], test_df['y']  

    # 创建并拟合使用多项式核的SVC  
    svc = SVC(kernel='poly', degree=2, coef0=1, C=1)  
    svc.fit(X_train, y_train)  

    # 添加拉格朗日乘数和支持向量状态  
    train_df['α'] = 0.0  
    train_df.loc[svc.support_ + 1, 'α'] = np.abs(svc.dual_coef_[0])  
    train_df['是否为支持向量'] = train_df.index.isin(svc.support_ + 1)  

    print("训练数据、拉格朗日乘数和是否为支持向量:")  
    print(train_df)  

    # 进行预测  
    test_df['ŷ'] = svc.predict(X_test)  
    print("\n测试数据和预测结果:")  
    print(test_df)

注意:由于SVC中的一些数值不稳定性,我们无法让scikit-learn计算出的截距和手动计算的结果一致……因此我没有展示如何手动计算偏置(虽然理论上计算方法应该和线性核一样)。

要参数

注:此处若作为标题或文档中的部分,建议使用“重要参数”或“关键性参数”,并且可以考虑去掉“#”符号,除非该符号在文档结构中有特定用途。在中文中通常用加粗或调整字体大小来表示标题。

在支持向量机中,关键参数是C,它表示正则化参数。

  • 大C:努力正确分类所有训练点,可能会过拟合,
  • 小C:允许更多的错误分类,但目标是得到一个更简单、更通用的模型

当然,如果你使用非线性核,你还需要调整与该核相关的度数(和系数),以保持简洁并符合技术术语的常用表达。

最后的评论

我们已经讨论了很多关于支持向量机(SVM)的关键概念及其工作原理,但主要的想法是这样的:一切都关乎找到正确的平衡。你希望你的SVM能够学习到数据中的重要模式,不过于执着于将每个训练样本都精准地划分到正确的超平面一侧。如果太严格,可能会错过大局;如果太灵活,则可能会看到不存在的虚假模式。诀窍在于调整你的SVM,使其能够识别真实的趋势,同时保持足够的灵活性以应对新数据。把握好这个平衡,这样你就能得到一个能高效处理各种分类问题的利器。

推荐阅读

要详细了解支持向量机及其在scikit-learn中的实现方法,读者可以参考该官方文档[2],它提供关于其使用方法和参数设置的全面信息。

技术环境:

(注释:在特定情况下,如果"技术架构"更符合语境,也可以考虑使用"技术架构"。)

本文使用的是 Python 3.7 和 scikit-learn 1.5。虽然讨论的概念具有普遍适用性,具体的代码实现可能因不同版本而略有差异。

关于这些插图的信息如下:这里有一些关于插图的信息。

除非特别注明,所有图片均由作者制作,采用了 Canva Pro 授权的设计元素。

参考如下

[1] T. M. Mitchell, 《机器学习》(1997), 麦格劳-希尔科学/工程/数学, 页59.

[2] F. Pedregosa 等人,Scikit-learn: Python 中的机器学习,《机器学习研究期刊》,第 12 卷,第 2825 至 2830 页,2011 年。在线访问 https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html(访问日期:[具体日期])

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消