前言
支持向量机(SVM)是一个功能强大并且全面的机器学习模型,它能够执行线性或者非线性分类、回归,同时它特别适合用于中小型复杂数据集的分类,这也是本期介绍其的主要原因之一。
线性SVM分类
我们先看一下SVM的定义:通过最大化样本间隔确定最优分类边界,并利用核技巧处理非线性问题,以结构风险最小化实现高效泛化
其核心思想是要在类之间拟合可以的最宽的分界线,我们可以看到下图,左边显示了三种可能的线性分类器的决策边界,其中虚线根本无法对其分类,而其他两个模型可以分类,不过它们的决策边界对新实例时,表现得可以不会太好,而右边得分界线,不仅分离了两个类,同时尽可能地远离了最近的训练实例,我们将其称为大间隔分类,也是我们需要找的分界线。
同时SVM的理论基础是结构风险最小化原则,不仅最小化训练误差,同时最大化间隔来控制模型复杂度,从本质上平衡模型的拟合能力和泛化能力。
注意:我们发现决策边界完全由类的边缘实例所决定,我们称这些实例为支持向量。
接下来我们具体看一下公式:
1 在线性SVM分类器,我们通过计算决策函数来预测新实例的分类,我们定义决策函数(超平面)为:
如果结果为正,则预测类别是1,否则预测其为-1。
如果是二维中,点到直线的距离可以写成:
又由于标签属于{-1,1},所以:
所以距离可以写成几何间隔:
2 假设离决策函数最近的样本点到其的几何间隔为d,则所有样本点的几何间隔至少大于d
我们将函数间隔定义为:
为了简化计算,我们归一化超平面,让支持向量到决策函数的函数间隔为1,即令 d = 1 ∥ w ∥ d = \frac{1}{\|w\|} d=∥w∥1,让所有样本满足:
支持向量取等号,两类支持向量之间的几何间隔为 2 ∥ w ∥ \frac{2}{\|w\|} ∥w∥2,现在我们的目标是最小化它:
3 为了将带约束的优化问题转化为无约束的问题,引入拉格朗日乘子 α i > = 0 \alpha_{i}>= 0 αi>=0(每个样本对于一个乘子)构成拉格朗日函数:
4 根据KKT条件,对w和b求偏导数为0:
5 将w代入拉格朗日函数,消去w和b,得到对偶问题,只包含 α \alpha α:
约束条件:
我们发现对偶问题中出现了样本的内积即 x i ⋅ x j x_i ⋅ x_j xi⋅xj,这为后续引入核函数处理非线性问题奠定了基础。
6 引入核函数:假设数据在原始空间中无法用直线(超平面)分开,但可以通过一个映射 ϕ ( x ) \phi(x) ϕ(x) 将样本点映射到更高维空间(例如二维数据映射到三维,让数据在三维空间中线性可分)。
我们将对偶问题中样本的内积替换成核函数(接下来会介绍),约束条件不变:
7 通过求解对偶问题,得到每个样本的 α i \alpha_i αi,根据KKT条件,若样本是非支持向量,则 α = 0 \alpha = 0 α=0 ,若为支持向量, α > 0 \alpha > 0 α>0
8 最后计算超平面参数w和b
1 由于只有支持向量的 α > 0 \alpha > 0 α>0,故:
2 对于任何一个支持向量都有 y j ( w ⋅ x j + b ) = 1 y_j (w \cdot x_j + b) = 1 yj(w⋅xj+b)=1,解得
9 决策函数,对于新样本x,计算它到超平面的距离。
注意:SVM对特征的大小特别敏感,故在使用前我们需要进行特征缩放。
软间隔分类
之前我们讲的都是让所有实例都位于正确的一边,我们称为硬间隔分类,它对于异常值非常敏感,我们一般在数据是线性可分离的时候使用它,然而有些情况下,我们找不到硬间隔,为了解决这个问题,我们引入软间隔分类,在使用Scikit-Learn创建SVM模型时,我们可以指定超参数C,C如果设置的较低,我们得到左边的分类,如果设置的较高,我们得到右边的模型。
如果SVM模型过拟合,我们可以尝试降低C来对其进行正则化。下面举一个实例:以Scikit-Learn代码可加载鸢尾花数据集,缩放特征,然后训练线性SVM模型(使用C=1的LinearSVC类和稍后描述的hinge损失函数)来检测维吉尼亚鸢尾花:
import numpy as np # 导入数值计算库
from sklearn import datasets # 导入数据集模块
from sklearn.pipeline import Pipeline # 导入流水线工具,用于组合数据处理和模型
from sklearn.preprocessing import StandardScaler # 导入特征标准化工具
from sklearn.svm import LinearSVC # 导入线性支持向量机分类器
iris = datasets.load_iris() # 加载鸢尾花数据集(包含3类鸢尾花数据)
# 选择特征:使用第3、4个特征(花瓣长度、花瓣宽度),将数据降为2维便于后续可视化和理解
X = iris["data"][:, (2, 3)]
# 构建二分类标签:判断是否为第3类鸢尾花(Iris virginica,原标签为2)
# 将布尔值(True/False)转换为数值型(1.0/0.0),满足SVM对标签的输入要求
y = (iris["target"] == 2).astype(np.float64)
svm_clf = Pipeline([
# 第一步:特征标准化(核心预处理步骤)
("scaler", StandardScaler()), # 对特征进行Z-score标准化(均值为0,标准差为1)SVM对特征尺度敏感,标准化可避免尺度差异影响超平面优化
# 第二步:线性支持向量机分类器
("linear_svc", LinearSVC( # 使用线性SVM(适用于线性可分或近似线性可分数据)
C=1, # 正则化参数:控制对分类错误的惩罚力度
# C越大,对错误的容忍度越低,模型越严格(防止欠拟合)
# C越小,允许更多样本违反间隔约束,模型更简单(防止过拟合)
loss="hinge" # 使用hinge损失函数,SVM的核心损失函数
# 仅当样本违反间隔约束时(y*(w·x+b) < 1),损失非零
)),
])
svm_clf.fit(X, y) # 在预处理后的特征X和标签y上训练模型
svm_clf.predict([[5.5, 1.7]]) # 进行预测
#array([1.])
非线性SVM分类
在很多情况下,线性SVM分类器是有效的,但是有些数据集远不是线性可分离的,处理非线性数据集的方法之一就是添加更多特征。下面当只有一个特征的时候不可分,但是我们添加一个特征,生成的2D数据集则是完全线性可分离的。
为了使用Scikit-Learn来实现这个想法,创建一个包含PolynomialFeatures转换器的Pipeline,然后是StandardScaler和LinearSVC。让我们在卫星数据集上进行测试:这是一个用于二元分类的小数据集,其中数据点的形状为两个交织的半圆。你可以使用make_moons()函数生成此数据集:
from sklearn.datasets import make_moons
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
X, y = make_moons(n_samples=100, noise=0.15)
polynomial_svm_clf = Pipeline([
#生成多项式特征(核心:将非线性数据映射到高维空间)
("poly_features", PolynomialFeatures(degree=3)),
("scaler", StandardScaler()),
("svm_clf", LinearSVC(C=10, loss="hinge"))
])
polynomial_svm_clf.fit(X, y)
对于 d 维特征,生成 n 次多项式特征后,新特征的维度为:C(d+n,n),上面代码原数据集X是特征矩阵(2维的)就是d = 2 ,而degree=3,就是n = 3,也就是C(5,3) = 10,生成的新特征如下:
核函数
添加多项式特征实现起来非常简单,并且对所有的机器学习算法(不只是SVM)都非常有效。但问题是,如果多项式太低阶,则处理不了非常复杂的数据集。而高阶则会创造出大量的特征,导致模型变得太慢。不过SVM还有一个核技巧(也就是我们公式推导过程所所使用的核函数),它产生的结果就跟添加了许多多项式特征(甚至是非常高阶的多项式特征)一样,但实际上并不需要真的添加。核函数定义为高维空间中内积的“低维替代”:即核函数 k ( x i , x j ) k(x_i, x_j) k(xi,xj) 的值等于原始样本在高维空间中的内积。通过核函数,无需显式定义 ϕ \phi ϕ,只需计算低维空间中的 k ( x i , x j ) k(x_i, x_j) k(xi,xj),即可隐性完成高维内积运算,这称为核技巧(Kernel Trick)。这里具体的推导过程不是重点,我们需要知道如何使用就行。
常见的核函数公式包括: γ \gamma γ:控制距离衰减速度,r为常数项。
多项式内核
from sklearn.svm import SVC
poly_kernel_svm_clf = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="poly", degree=3, coef0=1, C=5))
])
poly_kernel_svm_clf.fit(X, y)
这段代码使用了一个3阶多项式内核训练SVM分类器。如左图所示。而右图是另一个使用了10阶多项式核的SVM分类器。显然,如果模型过拟合,你应该降低多项式阶数;反过来,如果欠拟合,则可以尝试使之提升。超参数coef0控制的是模型受高阶多项式还是低阶多项式影响的程度。coef0越大,模型越偏向于低次项(如线性项、常数项),决策边界更简单;coef0 越小,高次项的影响越大,边界更复杂(可能过拟合)。
损失函数
hinge 损失的意义:仅对违反间隔的样本施加惩罚,确保高维空间中的间隔最大化。
当满足:
即我们定义hinge损失函数为:
- 当样本被正确分类且在间隔外( y i ( w T x i + b ) ≥ 1 y_i (w^T x_i + b) \geq 1 yi(wTxi+b)≥1):损失为 0(不惩罚);
- 当样本在间隔内或分类错误( y i ( w T x i + b ) < 1 y_i (w^T x_i + b) < 1 yi(wTxi+b)<1):损失随误差线性增长。
总结
本期介绍了SVM进行线性与非线性分类的任务,包含公式的推导、核技巧,以及代码的实现,实际上SVM还能处理回归任务。
写在文末
有疑问的友友,欢迎在评论区交流,笔者看到会及时回复。
请大家一定一定要关注!!!
请大家一定一定要关注!!!
请大家一定一定要关注!!!
友友们,你们的支持是我持续更新的动力~