Python 实战:掌握 SVM 机器学习算法
1. 引言
支持向量机(Support Vector Machine, SVM)是一种基于统计学习理论的监督学习算法,因其优越的分类性能和理论严谨性,在以下领域广泛应用:
- 文本分类(垃圾邮件过滤、新闻分类)
- 图像识别(人脸检测、手写数字识别)
- 异常检测(信用卡欺诈检测)
- 回归问题(SVR)
SVM 的核心思想:
- 找到能够最大化分类间隔的超平面
- 利用支持向量定义决策边界
- 对于线性不可分问题,通过核函数映射到高维空间
2. 数学原理深度解析
2.1 最大间隔超平面
给定训练数据集:
$$ D = \{ (x_i, y_i) | x_i \in \mathbb{R}^n, y_i \in \{-1, 1\} \} $$
SVM 目标是找到一个超平面:
$$ w \cdot x + b = 0 $$
使得两类样本满足:
$$ y_i (w \cdot x_i + b) \ge 1 $$
且最大化分类间隔 $\frac{2}{||w||}$,等价于优化问题:
$$ \min_{w,b} \frac{1}{2} ||w||^2 $$
$$ s.t. \quad y_i (w \cdot x_i + b) \ge 1 $$
2.2 拉格朗日对偶问题
利用拉格朗日乘子法构建目标函数:
$$ L(w, b, \alpha) = \frac{1}{2} ||w||^2 - \sum_{i=1}^{N} \alpha_i [ y_i (w \cdot x_i + b) - 1] $$
对 $w$ 和 $b$ 求偏导并令其为 0,可得到对偶问题:
$$ \max_{\alpha} \sum_{i=1}^N \alpha_i - \frac{1}{2}\sum_{i,j=1}^{N} \alpha_i \alpha_j y_i y_j (x_i \cdot x_j) $$
$$ s.t. \quad \sum_{i=1}^N \alpha_i y_i = 0, \quad \alpha_i \ge 0 $$
2.3 KKT 条件
支持向量满足:
- $\alpha_i [y_i(w \cdot x_i + b) - 1] = 0$
- $\alpha_i > 0 \Rightarrow x_i$ 在间隔边界上
最终分类器为:
$$ f(x) = sign\Big( \sum_{i=1}^{N} \alpha_i y_i (x_i \cdot x) + b \Big) $$
2.4 核技巧(Kernel Trick)
对于线性不可分问题,通过核函数 $\phi(x)$ 将数据映射到高维空间:
$$ K(x_i, x_j) = \phi(x_i) \cdot \phi(x_j) $$
常见核函数:
- 线性核:
K(x, x') = x·x'
- RBF 核:
K(x, x') = exp(-γ||x-x'||²)
- 多项式核:
K(x, x') = (x·x' + c)^d
3. Python 实战
3.1 数据准备与可视化
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
# 生成非线性可分数据(双月形)
X, y = datasets.make_moons(n_samples=200, noise=0.2, random_state=42)
y = np.where(y==0, -1, 1) # SVM 使用 -1 和 1 标签
plt.scatter(X[:,0], X[:,1], c=y)
plt.title("Non-linear data for SVM")
plt.show()
3.2 Sklearn 快速实现 SVM
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
# 使用 RBF 核
clf = SVC(kernel='rbf', C=1.0, gamma=0.5)
clf.fit(X_train, y_train)
print("支持向量数量:", len(clf.support_))
print("测试集准确率:", clf.score(X_test, y_test))
3.3 可视化决策边界
def plot_decision_boundary(clf, X, y):
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 300),
np.linspace(y_min, y_max, 300))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.3)
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k')
plt.scatter(clf.support_vectors_[:,0],
clf.support_vectors_[:,1],
s=100, facecolors='none', edgecolors='r')
plt.title("SVM Decision Boundary")
plt.show()
plot_decision_boundary(clf, X, y)
3.4 手写简化版 SVM(SMO思想)
class SimpleSVM:
def __init__(self, C=1.0, tol=1e-3, max_iter=1000):
self.C = C
self.tol = tol
self.max_iter = max_iter
def fit(self, X, y):
n_samples, n_features = X.shape
self.alpha = np.zeros(n_samples)
self.b = 0
self.X = X
self.y = y
for _ in range(self.max_iter):
alpha_prev = np.copy(self.alpha)
for i in range(n_samples):
# 简化 SMO:只更新一个 alpha
j = np.random.randint(0, n_samples)
if i == j:
continue
xi, xj, yi, yj = X[i], X[j], y[i], y[j]
eta = 2 * xi.dot(xj) - xi.dot(xi) - xj.dot(xj)
if eta >= 0:
continue
# 计算误差
Ei = self.predict(xi) - yi
Ej = self.predict(xj) - yj
alpha_i_old, alpha_j_old = self.alpha[i], self.alpha[j]
# 更新 alpha
self.alpha[j] -= yj * (Ei - Ej) / eta
self.alpha[j] = np.clip(self.alpha[j], 0, self.C)
self.alpha[i] += yi * yj * (alpha_j_old - self.alpha[j])
# 更新 b
self.b = np.mean(y - self.predict(X))
if np.linalg.norm(self.alpha - alpha_prev) < self.tol:
break
def predict(self, X):
return np.sign((X @ (self.alpha * self.y @ self.X)) + self.b)
# 使用手写SVM
svm_model = SimpleSVM(C=1.0)
svm_model.fit(X, y)
4. SVM 的优缺点总结
优点
- 在高维空间有效
- 适合小样本数据集
- 使用核函数可解决非线性问题
缺点
- 对大规模数据训练速度慢(O(n²\~n³))
- 对参数敏感(C、gamma)
- 对噪声敏感
5. 实战经验与调优策略
数据预处理
- 特征标准化非常重要
调参技巧
GridSearchCV
搜索最佳C
和gamma
核函数选择
- 线性问题用
linear
,非线性问题用rbf
- 线性问题用
可视化支持向量
- 便于分析模型决策边界
6. 总结
本文从数学原理 → 对偶问题 → 核函数 → Python 实战 → 手写 SVM,完整解析了 SVM 的底层逻辑和实现方式:
- 掌握了支持向量机的核心思想:最大间隔分类
- 理解了拉格朗日对偶与 KKT 条件
- 学会了使用
sklearn
和手写代码实现 SVM - 掌握了可视化和参数调优技巧
评论已关闭