Python 实战:掌握 SVM 机器学习算法

1. 引言

支持向量机(Support Vector Machine, SVM)是一种基于统计学习理论的监督学习算法,因其优越的分类性能和理论严谨性,在以下领域广泛应用:

  • 文本分类(垃圾邮件过滤、新闻分类)
  • 图像识别(人脸检测、手写数字识别)
  • 异常检测(信用卡欺诈检测)
  • 回归问题(SVR)

SVM 的核心思想:

  1. 找到能够最大化分类间隔的超平面
  2. 利用支持向量定义决策边界
  3. 对于线性不可分问题,通过核函数映射到高维空间

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 条件

支持向量满足:

  1. $\alpha_i [y_i(w \cdot x_i + b) - 1] = 0$
  2. $\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) $$

常见核函数:

  1. 线性核:K(x, x') = x·x'
  2. RBF 核:K(x, x') = exp(-γ||x-x'||²)
  3. 多项式核: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. 实战经验与调优策略

  1. 数据预处理

    • 特征标准化非常重要
  2. 调参技巧

    • GridSearchCV 搜索最佳 Cgamma
  3. 核函数选择

    • 线性问题用 linear,非线性问题用 rbf
  4. 可视化支持向量

    • 便于分析模型决策边界

6. 总结

本文从数学原理 → 对偶问题 → 核函数 → Python 实战 → 手写 SVM,完整解析了 SVM 的底层逻辑和实现方式:

  1. 掌握了支持向量机的核心思想:最大间隔分类
  2. 理解了拉格朗日对偶与 KKT 条件
  3. 学会了使用 sklearn 和手写代码实现 SVM
  4. 掌握了可视化和参数调优技巧

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日