支持向量机(Support Vector Machine, SVM)作为机器学习领域的经典算法,其核心思想源于统计学习理论和结构风险最小化原则。本章将从几何直观和数学定义两个维度,深入剖析SVM的基本原理。
在二元分类问题中,感知机算法仅追求找到一个能将训练样本正确分类的分离超平面。然而,当数据线性可分时,这样的超平面有无限多个。图1展示了二维空间中三个不同的分离超平面,它们都能完美分类训练数据。
python复制import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
# 生成线性可分数据
X, y = make_blobs(n_samples=50, centers=2, random_state=42)
# 绘制数据点
plt.scatter(X[:,0], X[:,1], c=y, cmap='autumn')
# 绘制三个不同的分离超平面
x_vals = np.linspace(min(X[:,0]), max(X[:,0]), 100)
for w, b in [(1, -4), (1.2, -5), (0.8, -3.5)]:
y_vals = (-w*x_vals - b)/1
plt.plot(x_vals, y_vals, 'b-', alpha=0.3)
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.title('Multiple Separating Hyperplanes')
plt.grid(True)
plt.show()
SVM的创新之处在于提出了"最大间隔"原则:不仅要正确分类,还要使两类样本到超平面的最小距离最大化。这个最小距离被称为几何间隔(geometric margin),而对应的超平面称为最优分离超平面。
在n维空间中,超平面可以表示为:
$$ w^T x + b = 0 $$
其中w是法向量,决定了超平面的方向;b是位移项,决定了超平面与原点的距离。
对于训练样本$(x_i, y_i)$,其中$y_i \in {-1, +1}$,我们定义:
函数间隔(functional margin):
$$\hat{\gamma}_i = y_i(w^T x_i + b)$$
几何间隔(geometric margin):
$$\gamma_i = \frac{y_i(w^T x_i + b)}{||w||}$$
几何间隔实际上就是样本点到超平面的欧氏距离。SVM的目标是最大化所有样本中最小几何间隔:
$$\max_{w,b} \min_i \gamma_i$$
支持向量是距离分离超平面最近的样本点,它们"支撑"着最大间隔边界。数学上,支持向量满足:
$$ y_i(w^T x_i + b) = 1 $$
这些点对最终决策函数起决定性作用,而非支持向量的样本点对模型没有影响。这一特性使得SVM具有天然的稀疏性,模型仅依赖于少数关键样本。
表1展示了支持向量与非支持向量的对比特性:
| 特性 | 支持向量 | 非支持向量 |
|---|---|---|
| 几何位置 | 在间隔边界上或内部 | 在间隔边界外 |
| 对模型影响 | 决定分离超平面位置 | 不影响模型 |
| 数学条件 | $y_i(w^T x_i + b) \leq 1$ | $y_i(w^T x_i + b) > 1$ |
| 在解中的表现 | $\alpha_i > 0$ | $\alpha_i = 0$ |
现实中的数据往往不是严格线性可分的。Vapnik提出了软间隔SVM,通过引入松弛变量$\xi_i$允许部分样本分类错误:
原始优化问题变为:
$$
\begin{aligned}
\min_{w,b,\xi} \quad & \frac{1}{2}||w||^2 + C\sum_{i=1}^n \xi_i \
\text{s.t.} \quad & y_i(w^T x_i + b) \geq 1 - \xi_i, \quad \forall i \
& \xi_i \geq 0, \quad \forall i
\end{aligned}
$$
其中C是惩罚参数,控制对误分类的容忍度:
图2展示了不同C值对决策边界的影响:
python复制from sklearn.svm import SVC
# 生成带有噪声的线性可分数据
X, y = make_blobs(n_samples=100, centers=2, cluster_std=1.5, random_state=42)
# 训练不同C值的SVM
Cs = [0.1, 1, 10]
plt.figure(figsize=(15,5))
for i, C in enumerate(Cs):
svm = SVC(kernel='linear', C=C)
svm.fit(X, y)
# 绘制决策边界
plt.subplot(1, 3, i+1)
plt.scatter(X[:,0], X[:,1], c=y, cmap='autumn')
# 获取超平面参数
w = svm.coef_[0]
b = svm.intercept_[0]
x_vals = np.linspace(min(X[:,0]), max(X[:,0]), 100)
y_vals = (-w[0]*x_vals - b)/w[1]
plt.plot(x_vals, y_vals, 'b-')
# 绘制支持向量
plt.scatter(svm.support_vectors_[:,0], svm.support_vectors_[:,1],
s=100, facecolors='none', edgecolors='k')
plt.title(f'C={C}, SV={len(svm.support_vectors_)}')
plt.grid(True)
plt.tight_layout()
plt.show()
硬间隔SVM的原始优化问题可以表述为:
$$
\begin{aligned}
\min_{w,b} \quad & \frac{1}{2}||w||^2 \
\text{s.t.} \quad & y_i(w^T x_i + b) \geq 1, \quad \forall i
\end{aligned}
$$
这是一个凸二次规划问题,目标函数$\frac{1}{2}||w||^2$是严格凸的,约束条件是线性的,因此存在唯一全局最优解。
引入拉格朗日乘子$\alpha_i \geq 0$,构建拉格朗日函数:
$$
\mathcal{L}(w,b,\alpha) = \frac{1}{2}||w||^2 - \sum_{i=1}^n \alpha_i[y_i(w^T x_i + b) - 1]
$$
根据KKT条件,在最优解处:
$$
\frac{\partial \mathcal{L}}{\partial w} = 0 \Rightarrow w = \sum_{i=1}^n \alpha_i y_i x_i \
\frac{\partial \mathcal{L}}{\partial b} = 0 \Rightarrow \sum_{i=1}^n \alpha_i y_i = 0
$$
将这两个关系代入拉格朗日函数,得到对偶问题:
$$
\begin{aligned}
\max_{\alpha} \quad & \sum_{i=1}^n \alpha_i - \frac{1}{2}\sum_{i,j=1}^n \alpha_i \alpha_j y_i y_j x_i^T x_j \
\text{s.t.} \quad & \alpha_i \geq 0, \quad \forall i \
& \sum_{i=1}^n \alpha_i y_i = 0
\end{aligned}
$$
KKT条件提供了原始问题与对偶问题最优解之间的关系。对于SVM,关键的条件是互补松弛条件:
$$
\alpha_i[y_i(w^T x_i + b) - 1] = 0, \quad \forall i
$$
这意味着:
求解对偶问题得到$\alpha^$后,决策函数可以表示为:
$$
f(x) = \text{sign}\left( \sum_{i=1}^n \alpha_i^ y_i x_i^T x + b^* \right)
$$
其中$b^$可以通过任意支持向量计算:
$$
b^ = y_i - \sum_{j=1}^n \alpha_j^* y_j x_j^T x_i
$$
实践中通常取所有支持向量的平均值以提高数值稳定性。
序列最小优化(Sequential Minimal Optimization, SMO)算法是专门为SVM设计的高效训练算法。其核心思想是:
假设选择$\alpha_1$和$\alpha_2$进行优化,固定其他$\alpha_i$。根据约束条件:
$$
\alpha_1 y_1 + \alpha_2 y_2 = -\sum_{i=3}^n \alpha_i y_i = \zeta \quad (\text{常数})
$$
可以表示为:
$$
\alpha_1 = (\zeta - \alpha_2 y_2) y_1
$$
将目标函数表示为$\alpha_2$的函数,求导并考虑边界约束,得到$\alpha_2$的更新公式:
$$
\alpha_2^{new} = \alpha_2^{old} + \frac{y_2(E_1 - E_2)}{\eta}
$$
其中$E_i = f(x_i) - y_i$是预测误差,$\eta = K(x_1,x_1)+K(x_2,x_2)-2K(x_1,x_2)$。
SMO采用启发式策略选择优化变量:
外层循环:
内层循环:
选择使$|E_1 - E_2|$最大的$\alpha_2$,以最大化目标函数下降幅度。
每次更新$\alpha_1$和$\alpha_2$后,需要重新计算b:
如果$0 < \alpha_1^{new} < C$:
$$
b_1^{new} = -E_1 - y_1 K_{11}(\alpha_1^{new} - \alpha_1^{old}) - y_2 K_{21}(\alpha_2^{new} - \alpha_2^{old}) + b^{old}
$$
类似地计算$b_2^{new}$,最终取:
$$
b^{new} = \begin{cases}
b_1^{new} & \text{如果 } 0 < \alpha_1^{new} < C \
b_2^{new} & \text{如果 } 0 < \alpha_2^{new} < C \
\frac{b_1^{new} + b_2^{new}}{2} & \text{否则}
\end{cases}
$$
在实际实现SMO算法时,需要注意以下关键点:
表2总结了SMO算法与通用QP求解器的对比:
| 特性 | SMO算法 | 通用QP求解器 |
|---|---|---|
| 内存需求 | O(n) | O(n²) |
| 计算复杂度 | 实际O(n²) | 理论O(n³) |
| 适用规模 | 万级样本 | 千级样本 |
| 实现难度 | 较高 | 较低 |
| 稀疏性利用 | 天然支持 | 依赖实现 |
许多现实问题在原始特征空间中线性不可分。核方法的核心思想是通过非线性映射$\phi$将数据映射到高维特征空间,使其在新空间中线性可分。
例如,对于二维数据$x=(x_1,x_2)$,考虑映射:
$$
\phi(x) = (x_1, x_2, x_1^2 + x_2^2)
$$
这样原始空间中的圆边界在新空间中变为线性可分的。
核函数$K(x,z)=\phi(x)^T\phi(z)$允许我们在不显式计算$\phi(x)$的情况下,计算高维空间的内积。常见的核函数包括:
不同核函数适用于不同场景:
参数选择对模型性能至关重要:
RBF核的$\gamma$:控制单个样本的影响范围
多项式核的$d$:控制多项式次数
核矩阵$K$(Gram矩阵)定义为$K_{ij}=K(x_i,x_j)$,必须满足:
Mercer定理给出了函数$K$成为有效核的充要条件,确保对应的优化问题是凸的。
SVM本质上是二分类器,扩展到多分类的常用方法:
一对一(OvO):
一对多(OvR):
表3对比了两种策略:
| 特性 | OvO | OvR |
|---|---|---|
| 分类器数量 | $k(k-1)/2$ | $k$ |
| 训练数据量 | 每对类别的样本 | 全部样本 |
| 训练时间 | 较长 | 较短 |
| 预测速度 | 较慢 | 较快 |
| 适用场景 | 类别少、平衡数据 | 类别多、不平衡数据 |
SVR通过$\epsilon$-不敏感损失函数将SVM思想扩展到回归问题:
$$
L_\epsilon(y,f(x)) = \begin{cases}
0 & \text{如果 } |y-f(x)| \leq \epsilon \
|y-f(x)|-\epsilon & \text{否则}
\end{cases}
$$
优化问题为:
$$
\begin{aligned}
\min_{w,b,\xi,\xi^} \quad & \frac{1}{2}||w||^2 + C\sum_{i=1}^n (\xi_i + \xi_i^) \
\text{s.t.} \quad & y_i - w^T \phi(x_i) - b \leq \epsilon + \xi_i \
& w^T \phi(x_i) + b - y_i \leq \epsilon + \xi_i^* \
& \xi_i, \xi_i^* \geq 0
\end{aligned}
$$
参数搜索策略:
交叉验证:
核函数选择流程:
使用MNIST数据集演示SVM在图像分类中的应用:
python复制from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import classification_report
from sklearn.preprocessing import StandardScaler
# 加载数据
digits = load_digits()
X, y = digits.data, digits.target
# 数据预处理
X = X / 16.0 # 归一化到[0,1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练SVM
svm = SVC(kernel='rbf', C=10, gamma=0.001)
svm.fit(X_train, y_train)
# 评估
y_pred = svm.predict(X_test)
print(classification_report(y_test, y_pred))
# 可视化支持向量
print(f"支持向量数量: {len(svm.support_vectors_)}")
print(f"每类支持向量数: {svm.n_support_}")
当数据量较大时,可采用以下优化策略:
训练时间过长:
预测性能差:
内存不足:
表4比较了SVM与几种常见分类算法:
| 算法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| SVM | 高维有效、核方法灵活、泛化能力强 | 大规模数据效率低、参数敏感 | 中小规模、高维数据 |
| 逻辑回归 | 概率输出、训练快 | 线性边界、需特征工程 | 线性可分、概率预测 |
| 决策树 | 可解释性强、无需特征缩放 | 容易过拟合、不稳定 | 结构化数据、需要解释 |
| 神经网络 | 高度非线性、特征学习 | 需要大量数据、调参复杂 | 大规模复杂模式 |
在实际项目中,我经常发现SVM在特征维度较高但样本量中等的场景表现优异。特别是在文本分类、生物信息学等领域,SVM往往能取得state-of-the-art的结果。一个实用的建议是:当面对新问题时,可以先尝试RBF核SVM作为基准模型,它能提供一个不错的性能下限。