支持向量机(SVM)算法详解与代码实例

本文系统讲解支持向量机(SVM)的核心原理,包括超平面、几何间隔、支持向量等几何概念;推导硬间隔与软间隔优化模型及其对偶形式;深入解析核技巧与常用核函数(如RBF);介绍SMO求解算法;并通过scikit-learn完整代码实例展示标准化、网格搜索、多分类与参数调优等工程要点,强调C和γ的物理意义及常见问题排查。

1. 引言:为什么需要 SVM

在分类算法中,逻辑回归、决策树、KNN 都能给出一个"能分开"的边界,但它们往往只关心"分对",而不关心"分得稳不稳"。支持向量机(Support Vector Machine,SVM)的核心思想恰恰相反:在所有能正确划分样本的超平面中,选择那个离两类样本都最远的。

这个"最远"的直觉带来了两个直接好处:

  • 泛化能力强:间隔越大,模型对未见样本的容错空间越大;
  • 理论完备:SVM 有扎实的统计学习理论(结构风险最小化)支撑,不是纯经验调参。

本文从几何直觉出发,推导硬间隔、软间隔与核技巧,最后给出基于 scikit-learn 的完整代码实例,并讨论工程实践中的调参要点。

2. 核心概念:超平面、间隔与支持向量

2.1 超平面与决策函数

在 nnn 维空间中,线性分类器由超平面定义:

wTx+b=0 w^T x + b = 0 wTx+b=0

其中 www 是法向量,决定超平面的方向;bbb 是偏置,决定超平面的位置。分类决策为:

f(x)=sign(wTx+b) f(x) = \text{sign}(w^T x + b) f(x)=sign(wTx+b)

样本到超平面的几何距离为:

∣wTx+b∣∥w∥ \frac{|w^T x + b|}{\|w\|} ∥w∥∣wTx+b∣​

2.2 函数间隔与几何间隔

对样本 (xi,yi)(x_i, y_i)(xi​,yi​),yi∈{−1,+1}y_i \in \{-1, +1\}yi​∈{−1,+1},定义:

  • 函数间隔:γ^i=yi(wTxi+b)\hat{\gamma}_i = y_i(w^T x_i + b)γ^​i​=yi​(wTxi​+b)
  • 几何间隔:γi=yi(wTxi+b)∥w∥\gamma_i = \dfrac{y_i(w^T x_i + b)}{\|w\|}γi​=∥w∥yi​(wTxi​+b)​

函数间隔会随 w,bw, bw,b 等比例缩放而改变,因此不能直接作为优化目标;几何间隔才是真正的"物理距离"。

2.3 支持向量

距离超平面最近、且恰好落在间隔边界上的样本,称为支持向量(Support Vector)。它们满足:

yi(wTxi+b)=1 y_i(w^T x_i + b) = 1 yi​(wTxi​+b)=1

关键结论:最终的分类超平面只由支持向量决定,其余样本删掉也不影响结果。这也是 SVM 对噪声相对鲁棒、模型可解释性较好的原因。

3. 硬间隔 SVM:线性可分情形

3.1 优化问题的建立

假设数据线性可分。我们希望所有样本都满足 yi(wTxi+b)≥1y_i(w^T x_i + b) \ge 1yi​(wTxi​+b)≥1,同时最大化间隔 2∥w∥\dfrac{2}{\|w\|}∥w∥2​。等价于最小化 12∥w∥2\dfrac{1}{2}\|w\|^221​∥w∥2:

min⁡w,b12∥w∥2s.t.yi(wTxi+b)≥1,i=1,…,m \begin{aligned} \min_{w,b} \quad & \frac{1}{2}\|w\|^2 \\ \text{s.t.} \quad & y_i(w^T x_i + b) \ge 1, \quad i = 1, \dots, m \end{aligned} w,bmin​s.t.​21​∥w∥2yi​(wTxi​+b)≥1,i=1,…,m​

这是一个凸二次规划问题,存在全局最优解。

3.2 拉格朗日对偶

引入拉格朗日乘子 αi≥0\alpha_i \ge 0αi​≥0,构造拉格朗日函数:

L(w,b,α)=12∥w∥2−∑i=1mαi[yi(wTxi+b)−1] L(w, b, \alpha) = \frac{1}{2}\|w\|^2 - \sum_{i=1}^{m}\alpha_i\left[y_i(w^T x_i + b) - 1\right] L(w,b,α)=21​∥w∥2−i=1∑m​αi​[yi​(wTxi​+b)−1]

对 www 和 bbb 求偏导并令其为零,得到:

w=∑i=1mαiyixi,∑i=1mαiyi=0 w = \sum_{i=1}^{m}\alpha_i y_i x_i, \qquad \sum_{i=1}^{m}\alpha_i y_i = 0 w=i=1∑m​αi​yi​xi​,i=1∑m​αi​yi​=0

代回后得到对偶问题:

max⁡α∑i=1mαi−12∑i=1m∑j=1mαiαjyiyjxiTxjs.t.∑i=1mαiyi=0,αi≥0 \begin{aligned} \max_{\alpha} \quad & \sum_{i=1}^{m}\alpha_i - \frac{1}{2}\sum_{i=1}^{m}\sum_{j=1}^{m}\alpha_i\alpha_j y_i y_j x_i^T x_j \\ \text{s.t.} \quad & \sum_{i=1}^{m}\alpha_i y_i = 0, \quad \alpha_i \ge 0 \end{aligned} αmax​s.t.​i=1∑m​αi​−21​i=1∑m​j=1∑m​αi​αj​yi​yj​xiT​xj​i=1∑m​αi​yi​=0,αi​≥0​

3.3 KKT 条件与稀疏性

KKT 互补松弛条件要求:

αi[yi(wTxi+b)−1]=0 \alpha_i\left[y_i(w^T x_i + b) - 1\right] = 0 αi​[yi​(wTxi​+b)−1]=0

这意味着:只有落在间隔边界上的样本(支持向量)才有 αi>0\alpha_i > 0αi​>0,其余样本 αi=0\alpha_i = 0αi​=0。对偶问题的解天然稀疏,这是 SVM 计算与存储上的重要优势。

4. 软间隔 SVM:处理噪声与线性不可分

现实数据几乎不可能完全线性可分。硬间隔会为了几个异常点把间隔压得极窄,导致过拟合。软间隔引入松弛变量 ξi≥0\xi_i \ge 0ξi​≥0:

min⁡w,b,ξ12∥w∥2+C∑i=1mξis.t.yi(wTxi+b)≥1−ξi,ξi≥0 \begin{aligned} \min_{w,b,\xi} \quad & \frac{1}{2}\|w\|^2 + C\sum_{i=1}^{m}\xi_i \\ \text{s.t.} \quad & y_i(w^T x_i + b) \ge 1 - \xi_i, \quad \xi_i \ge 0 \end{aligned} w,b,ξmin​s.t.​21​∥w∥2+Ci=1∑m​ξi​yi​(wTxi​+b)≥1−ξi​,ξi​≥0​

对应的对偶问题只多了一个上界约束:

0≤αi≤C 0 \le \alpha_i \le C 0≤αi​≤C

4.1 惩罚参数 C 的含义

  • CCC 越大:对误分类惩罚越重,间隔越窄,越容易过拟合;
  • CCC 越小:允许更多样本落在间隔内甚至被误分,间隔更宽,正则化更强。

CCC 是 SVM 最重要的超参数,通常在对数尺度上搜索(如 10−310^{-3}10−3 到 10310^{3}103)。

4.2 合页损失视角

软间隔 SVM 等价于最小化**合页损失(Hinge Loss)**加 L2 正则:

min⁡w,b∑i=1mmax⁡(0,1−yi(wTxi+b))+12C∥w∥2 \min_{w,b} \sum_{i=1}^{m}\max\left(0, 1 - y_i(w^T x_i + b)\right) + \frac{1}{2C}\|w\|^2 w,bmin​i=1∑m​max(0,1−yi​(wTxi​+b))+2C1​∥w∥2

合页损失在 yif(xi)≥1y_i f(x_i) \ge 1yi​f(xi​)≥1 时为零,否则线性增长——这正是"间隔内才惩罚"的数学表达。

5. 核技巧:从线性到非线性

5.1 核函数的思想

观察对偶问题,样本只以内积 xiTxjx_i^T x_jxiT​xj​ 的形式出现。如果用一个映射 ϕ(⋅)\phi(\cdot)ϕ(⋅) 把样本升到高维空间,只需把内积替换为:

K(xi,xj)=ϕ(xi)Tϕ(xj) K(x_i, x_j) = \phi(x_i)^T \phi(x_j) K(xi​,xj​)=ϕ(xi​)Tϕ(xj​)

这就是核技巧:无需显式计算高维映射 ϕ\phiϕ,只要计算核函数值,就能在高维空间中做线性分类,等价于在原空间做非线性分类。

5.2 常用核函数

核函数表达式适用场景
线性核K(xi,xj)=xiTxjK(x_i, x_j) = x_i^T x_jK(xi​,xj​)=xiT​xj​特征维度高、样本量大(如文本)
多项式核K=(γxiTxj+r)dK = (\gamma x_i^T x_j + r)^dK=(γxiT​xj​+r)d特征交互明显的中小规模数据
RBF / 高斯核K=exp⁡(−γ∣xi−xj∣2)K = \exp(-\gamma |x_i - x_j|^2)K=exp(−γ∣xi​−xj​∣2)通用默认选择,适合大多数非线性问题
Sigmoid 核K=tanh⁡(γxiTxj+r)K = \tanh(\gamma x_i^T x_j + r)K=tanh(γxiT​xj​+r)类似神经网络激活,较少使用

RBF 核的 γ\gammaγ 控制单个样本的影响半径:γ\gammaγ 越大,影响范围越小,决策边界越弯曲,越容易过拟合。

5.3 核函数成立的条件

并非任意函数都能作为核函数。根据 Mercer 定理,一个对称函数是合法核函数,当且仅当其对应的核矩阵(Gram 矩阵)对任意样本集都半正定。

6. 求解算法:SMO

对偶问题是一个带约束的二次规划问题,样本量大时通用 QP 求解器效率很低。SMO(Sequential Minimal Optimization) 的思路是:每次只优化两个乘子 αi,αj\alpha_i, \alpha_jαi​,αj​,固定其余所有变量。

由于约束 ∑iαiyi=0\sum_i \alpha_i y_i = 0∑i​αi​yi​=0,单独改变一个 αi\alpha_iαi​ 会破坏约束,所以必须成对更新。两个变量的子问题可以解析求解,无需迭代数值优化,因此 SMO 每步都很快。

是

否

初始化 alpha = 0

是否满足 KKT 条件

输出 alpha 与 b

启发式选择违反 KKT 最严重的 alpha_i

选择使步长最大的 alpha_j

解析求解两变量子问题并更新

工程实现中(如 LIBSVM)还会加入工作集缓存、核矩阵缓存、收缩(shrinking)等优化,使 SVM 能处理数万级样本。

7. 代码实例:基于 scikit-learn 的完整实践

7.1 环境准备

pip install numpy scikit-learn matplotlib

7.2 线性可分数据:硬间隔与软间隔对比

import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.datasets import make_blobs

# 构造两类线性可分数据
X, y = make_blobs(n_samples=60, centers=2, random_state=6, cluster_std=0.60)
y = np.where(y == 0, -1, 1)

def plot_boundary(clf, X, y, title):
    plt.figure(figsize=(6, 4))
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.coolwarm, s=30)

    ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    xx = np.linspace(xlim[0], xlim[1], 200)
    yy = np.linspace(ylim[0], ylim[1], 200)
    YY, XX = np.meshgrid(yy, xx)
    xy = np.vstack([XX.ravel(), YY.ravel()]).T
    Z = clf.decision_function(xy).reshape(XX.shape)

    # 决策边界与间隔边界
    ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1],
               alpha=0.7, linestyles=['--', '-', '--'])
    # 圈出支持向量
    ax.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
               s=120, linewidth=1.2, facecolors='none', edgecolors='k')
    plt.title(title)
    plt.show()

# C 很大 -> 接近硬间隔
clf_hard = svm.SVC(kernel='linear', C=1e6)
clf_hard.fit(X, y)
plot_boundary(clf_hard, X, y, 'C = 1e6 (近似硬间隔)')

# C 较小 -> 软间隔,间隔更宽
clf_soft = svm.SVC(kernel='linear', C=0.1)
clf_soft.fit(X, y)
plot_boundary(clf_soft, X, y, 'C = 0.1 (软间隔)')

print('支持向量个数:', len(clf_soft.support_))
print('权重 w:', clf_soft.coef_)
print('偏置 b:', clf_soft.intercept_)

7.3 非线性数据:RBF 核与参数影响

from sklearn.datasets import make_circles

# 构造环形分布的非线性数据
X, y = make_circles(n_samples=200, factor=0.4, noise=0.08, random_state=0)
y = np.where(y == 0, -1, 1)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, (C, gamma) in zip(axes, [(1, 0.5), (1, 20), (100, 20)]):
    clf = svm.SVC(kernel='rbf', C=C, gamma=gamma)
    clf.fit(X, y)

    xx = np.linspace(X[:, 0].min() - 0.3, X[:, 0].max() + 0.3, 300)
    yy = np.linspace(X[:, 1].min() - 0.3, X[:, 1].max() + 0.3, 300)
    YY, XX = np.meshgrid(yy, xx)
    Z = clf.decision_function(np.c_[XX.ravel(), YY.ravel()]).reshape(XX.shape)

    ax.contourf(XX, YY, Z, levels=20, cmap='RdBu_r', alpha=0.6)
    ax.contour(XX, YY, Z, levels=[-1, 0, 1], colors='k',
               linestyles=['--', '-', '--'])
    ax.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.coolwarm,
               edgecolors='k', s=20)
    ax.set_title(f'C={C}, gamma={gamma}')

plt.tight_layout()
plt.show()

可以看到:γ\gammaγ 过大会让决策边界紧贴每个样本,形成"孤岛",是典型的过拟合;γ\gammaγ 过小则边界过于平滑,欠拟合。

7.4 完整流程:标准化 + 网格搜索 + 评估

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report, confusion_matrix

# 1. 加载数据
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
    data.data, data.target, test_size=0.25, random_state=42, stratify=data.target
)

# 2. 构建 Pipeline:标准化 + SVM
pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('svm', svm.SVC(kernel='rbf'))
])

# 3. 网格搜索
param_grid = {
    'svm__C': [0.1, 1, 10, 100],
    'svm__gamma': ['scale', 0.01, 0.1, 1],
}
grid = GridSearchCV(pipe, param_grid, cv=5, scoring='f1', n_jobs=-1)
grid.fit(X_train, y_train)

print('最佳参数:', grid.best_params_)
print('交叉验证最佳 F1:', round(grid.best_score_, 4))

# 4. 测试集评估
y_pred = grid.predict(X_test)
print('\n混淆矩阵:')
print(confusion_matrix(y_test, y_pred))
print('\n分类报告:')
print(classification_report(y_test, y_pred, target_names=data.target_names))

7.5 多分类与概率输出

SVM 原生只处理二分类,多分类通过 OvO(一对一) 或 OvR(一对多) 扩展,scikit-learn 默认使用 OvO。若需要概率输出,可设置 probability=True(内部用 Platt 缩放,训练会变慢):

from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=0, stratify=y
)

clf = Pipeline([
    ('scaler', StandardScaler()),
    ('svm', svm.SVC(kernel='rbf', C=10, gamma='scale',
                    probability=True, random_state=0))
])
clf.fit(X_train, y_train)

proba = clf.predict_proba(X_test[:3])
print('预测概率:\n', np.round(proba, 3))
print('预测类别:', clf.predict(X_test[:3]))

8. 工程实践要点

8.1 必须做特征标准化

SVM 基于距离与内积,特征量纲差异会直接扭曲间隔。使用 RBF 核或线性核前,务必先做 StandardScaler 或 MinMaxScaler,这是新手最常踩的坑。

8.2 参数搜索策略

  • 先固定 kernel='rbf',在 C 与 gamma 的二维网格上做对数搜索;
  • 数据量大(> 10 万)时优先考虑线性核 + LinearSVC,或改用 SGDClassifier;
  • 类别不平衡时设置 class_weight='balanced'。

8.3 计算复杂度与替代方案

场景建议
样本量 < 1 万,特征维度中等SVC(kernel='rbf'),效果通常很好
样本量大、特征维度高(文本)LinearSVC 或 SGDClassifier(loss='hinge')
需要概率输出SVC(probability=True) 或 CalibratedClassifierCV
需要增量学习SGDClassifier(SVM 不支持在线学习)

SVM 训练复杂度约为 O(m2)O(m^2)O(m2) 到 O(m3)O(m^3)O(m3)(mmm 为样本数),因此不适合超大规模数据,这是它相比梯度提升树的主要劣势。

8.4 常见问题排查

  • 准确率异常低:先检查是否漏了标准化;
  • 训练极慢:检查 C 是否过大、是否误用了 probability=True;
  • 过拟合:降低 C 或减小 gamma;
  • 欠拟合:增大 C 或增大 gamma,或换用 RBF 核。

9. 总结

SVM 的完整逻辑链条可以概括为:

  1. 几何直觉:最大化间隔 → 得到唯一且稳健的决策边界;
  2. 对偶转化:把带约束的凸二次规划转成只依赖内积的形式,带来稀疏性;
  3. 软间隔:引入松弛变量与惩罚参数 CCC,平衡间隔与误分类;
  4. 核技巧:用核函数隐式升维,把线性分类器变成非线性分类器;
  5. SMO:成对更新乘子,让大规模求解变得可行。

在深度学习盛行的今天,SVM 依然是中小规模、高维稀疏数据(如文本分类、生物信息学)上的强基线,也是理解"间隔"“正则化”"核方法"这些机器学习核心概念的最佳入口。掌握它,比记住它的公式更有价值。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值