Day 85:【99天精通Python】机器学习进阶 – 支持向量机 (SVM) – 寻找最宽的街道
前言
欢迎来到第85天!
在前面的课程中,我们学习了逻辑回归(找一条线划分)和决策树(用 if/else 切分)。
今天,我们要学习一个在深度学习流行之前,统治了机器学习领域近 20 年的算法——支持向量机 (Support Vector Machine, SVM)。
SVM 的核心思想是:找到一个能将两类数据分得"最开"的决策边界(超平面)。这个边界就像一条马路,我们希望马路越宽越好,这样容错率更高。
本节内容:
- SVM 原理:最大间隔与支持向量
- 核技巧 (Kernel Trick):处理非线性数据
- Sklearn 中的 SVC 与 SVR
- 超参数调优 (C, gamma)
- 实战练习:手写数字识别 (再战 MNIST)
一、SVM 原理
1.1 最大间隔 (Maximal Margin)
想象一下,在两堆点之间画一条线,可以有无数种画法。SVM 认为,最好的那条线,是离两边最近的点最远的那条。
这条"最宽的马路"的宽度,就叫间隔 (Margin)。
1.2 支持向量 (Support Vectors)
决定这条"马路"边界的那些点,就叫支持向量。SVM 只关心这些离边界最近的点,其他点对模型没有影响。
[外链图片转存中…(img-wdUVZQ58-1768773765029)]
1.3 核技巧 (Kernel Trick) – 精髓
如果数据是线性不可分的(比如一个圈套着另一个圈),怎么办?
SVM 的天才之处在于核技巧。它通过一个核函数,将数据从低维空间映射到高维空间,让它们变得线性可分。
- 线性核 (linear): 不做映射。
- 多项式核 (poly): 映射到多项式空间。
- 高斯径向基核 (RBF): 最常用,能处理各种复杂形状。
二、Sklearn 中的 SVM
2.1 分类 (SVC) 与 回归 (SVR)
- SVC: Support Vector Classifier
- SVR: Support Vector Regressor
2.2 超参数
- C (惩罚系数):
- C 越大,越追求"纯洁"(所有点都分对),容错率低,容易过拟合。
- C 越小,允许犯一些错误,容错率高,泛化能力可能更好。
- kernel: 'linear', 'poly', 'rbf' (默认)。
- gamma (RBF核专用):
- gamma 越大,影响范围越小,边界越弯曲,容易过拟合。
- gamma 越小,影响范围越大,边界越平滑。
三、实战:再战 MNIST 手写数字识别
我们用 SVM 来解决 Day 79 的手写数字识别问题,看看和神经网络比效果如何。
3.1 数据准备
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
import pandas as pd
# 1. 加载数据 (如果慢,可以只取一小部分)
# as_frame=False 返回 NumPy 数组
X, y = fetch_openml('mnist_784', version=1, return_X_y=True, as_frame=False)
# 数据量太大,只取前 10000 个样本用于演示
X = X[:10000]
y = y[:10000]
# 2. 标准化 (SVM对尺度敏感,必须标准化)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 3. 划分数据集
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y, test_size=0.3, random_state=42
)
3.2 训练与评估
# 1. 创建模型 (RBF 核是默认的)
# C 和 gamma 是需要调优的超参数
model = SVC(kernel='rbf', C=5, gamma=0.05, random_state=42)
# 2. 训练
print("开始训练 SVM (这可能会很慢)…")
model.fit(X_train, y_train)
# 3. 预测与评估
print("训练完成,开始评估…")
y_pred = model.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print(f"SVM 在 MNIST 上的准确率: {acc:.3f}")
# 结果通常在 0.95 以上,非常强大!
四、非线性分类可视化
让我们看看 SVM 如何处理线性不可分的数据。
from sklearn.datasets import make_circles
import numpy as np
import matplotlib.pyplot as plt
# 1. 创建环形数据
X, y = make_circles(n_samples=100, factor=0.3, noise=0.1)
# 2. 训练 SVM (使用 rbf 核)
svm = SVC(kernel='rbf').fit(X, y)
# 3. 绘制决策边界 (辅助函数)
def plot_decision_boundary(model, X, y):
h = .02
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.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
Z = model.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')
# 4. 可视化
plt.figure(figsize=(8, 6))
plot_decision_boundary(svm, X, y)
plt.title("SVM with RBF Kernel")
# plt.show()
五、常见问题
Q1:SVM 训练太慢怎么办?
SVM 的计算复杂度较高,尤其是样本量很大时。
- 数据降维:使用 PCA 等技术减少特征数量。
- 使用 LinearSVC:如果你的数据是线性可分的,或者样本量巨大,LinearSVC 专门为大规模线性问题做了优化,比 SVC(kernel='linear') 快得多。
- 随机抽样:用一部分数据训练。
Q2:C 和 gamma 怎么选?
这就是所谓的超参数调优 (Hyperparameter Tuning)。
通常使用 GridSearchCV (网格搜索) 来自动寻找最佳组合。
六、小结
#mermaid-svg-azOHmwQeax7Os7m8{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-azOHmwQeax7Os7m8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-azOHmwQeax7Os7m8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-azOHmwQeax7Os7m8 .error-icon{fill:#552222;}#mermaid-svg-azOHmwQeax7Os7m8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-azOHmwQeax7Os7m8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-azOHmwQeax7Os7m8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-azOHmwQeax7Os7m8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-azOHmwQeax7Os7m8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-azOHmwQeax7Os7m8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-azOHmwQeax7Os7m8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-azOHmwQeax7Os7m8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-azOHmwQeax7Os7m8 .marker.cross{stroke:#333333;}#mermaid-svg-azOHmwQeax7Os7m8 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-azOHmwQeax7Os7m8 p{margin:0;}#mermaid-svg-azOHmwQeax7Os7m8 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-azOHmwQeax7Os7m8 .cluster-label text{fill:#333;}#mermaid-svg-azOHmwQeax7Os7m8 .cluster-label span{color:#333;}#mermaid-svg-azOHmwQeax7Os7m8 .cluster-label span p{background-color:transparent;}#mermaid-svg-azOHmwQeax7Os7m8 .label text,#mermaid-svg-azOHmwQeax7Os7m8 span{fill:#333;color:#333;}#mermaid-svg-azOHmwQeax7Os7m8 .node rect,#mermaid-svg-azOHmwQeax7Os7m8 .node circle,#mermaid-svg-azOHmwQeax7Os7m8 .node ellipse,#mermaid-svg-azOHmwQeax7Os7m8 .node polygon,#mermaid-svg-azOHmwQeax7Os7m8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-azOHmwQeax7Os7m8 .rough-node .label text,#mermaid-svg-azOHmwQeax7Os7m8 .node .label text,#mermaid-svg-azOHmwQeax7Os7m8 .image-shape .label,#mermaid-svg-azOHmwQeax7Os7m8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-azOHmwQeax7Os7m8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-azOHmwQeax7Os7m8 .rough-node .label,#mermaid-svg-azOHmwQeax7Os7m8 .node .label,#mermaid-svg-azOHmwQeax7Os7m8 .image-shape .label,#mermaid-svg-azOHmwQeax7Os7m8 .icon-shape .label{text-align:center;}#mermaid-svg-azOHmwQeax7Os7m8 .node.clickable{cursor:pointer;}#mermaid-svg-azOHmwQeax7Os7m8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-azOHmwQeax7Os7m8 .arrowheadPath{fill:#333333;}#mermaid-svg-azOHmwQeax7Os7m8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-azOHmwQeax7Os7m8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-azOHmwQeax7Os7m8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-azOHmwQeax7Os7m8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-azOHmwQeax7Os7m8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-azOHmwQeax7Os7m8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-azOHmwQeax7Os7m8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-azOHmwQeax7Os7m8 .cluster text{fill:#333;}#mermaid-svg-azOHmwQeax7Os7m8 .cluster span{color:#333;}#mermaid-svg-azOHmwQeax7Os7m8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-azOHmwQeax7Os7m8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-azOHmwQeax7Os7m8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-azOHmwQeax7Os7m8 .icon-shape,#mermaid-svg-azOHmwQeax7Os7m8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-azOHmwQeax7Os7m8 .icon-shape p,#mermaid-svg-azOHmwQeax7Os7m8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-azOHmwQeax7Os7m8 .icon-shape rect,#mermaid-svg-azOHmwQeax7Os7m8 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-azOHmwQeax7Os7m8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-azOHmwQeax7Os7m8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-azOHmwQeax7Os7m8 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
SVM
核心思想
核技巧
超参数
最大化间隔 (Margin)
支持向量 (Support Vectors)
linear (线性)
poly (多项式)
rbf (高斯核 – 推荐)
C (惩罚)
gamma (RBF半径)
关键要点:
七、课后作业
下节预告
Day 86:机器学习进阶 – K-Means 聚类 – 如果数据没有标签怎么办?明天我们学习无监督学习,让机器自己从数据中找出"物以类聚"的规律。
系列导航:
- 上一篇:Day 84 – 决策树与随机森林
- 下一篇:Day 86 – K-Means聚类(待更新)




