逻辑回归的局限
逻辑回归找到的决策边界是任意一条能将数据分开的直线。问题是:这样的直线有无穷多条。
逻辑回归的可能决策边界:
特征₂
│ 所有虚线都能把 ○ 和 × 分开
│ ○ ○ ○ 但哪条最好?
│ ○ ╱ ╲
│ ○ ╱ ╲ ×
│ ╱ × ×
│ ╱ × ×
└──────────────────→ 特征₁
如果换一组训练数据(稍有不同的采样),逻辑回归可能选到完全不同的另一条线。这意味着模型的方差很大。
SVM 的核心思想:最大间隔
SVM(Support Vector Machine)引入了一个关键概念:间隔(Margin)。
间隔 = 决策边界到最近训练样本的距离。
SVM 的目标不是随便找一条分开的线,而是找到那一条让间隔最大的线。
SVM 的最大间隔决策边界:
特征₂
│
│ ○ ○ ○
│ ○ ╲
│ ○ ╲ × ×
│ ╱╲ ╲ ×
│ ╱ ╲ ×
│╱ ╲
└──────────────────→ 特征₁
↑ ↑
支持向量 ← 这些"最危险的"点决定了边界的位置
二、数学表达:从"间隔"到优化问题
决策边界的表示
延续逻辑回归的符号,决策边界仍然是:
w·x + b = 0
对于线性可分的二分类问题:
w·x + b > 0 → 预测为 +1 类
w·x + b < 0 → 预测为 -1 类
什么是"间隔"?
对于一个样本 xᵢ,它到决策边界的几何距离是:
dᵢ = yᵢ × (w·xᵢ + b) / ||w||
其中 yᵢ ∈ {+1, -1} 是类别标签
当预测正确时,yᵢ 和 (w·xᵢ + b) 同号,dᵢ > 0。
间隔 = 所有样本到决策边界的最短距离:
Margin = min(dᵢ) 对所有样本 i
SVM 的优化目标
SVM 要找的 w 和 b,就是在正确分类所有样本的前提下,最大化这个最小距离:
max Margin(w,b)
s.t. yᵢ × (w·xᵢ + b) / ||w|| ≥ Margin 对所有 i
经过数学变换(令 Margin × ||w|| = 1),可以重写为更简洁的形式:
min (1/2) × ||w||²
s.t. yᵢ × (w·xᵢ + b) ≥ 1 对所有 i
关键 insight:最大化间隔 ⇔ 最小化 ||w||²。
这和岭回归的 L2 正则化在形式上完全一致------SVM 的"最大间隔"本质上等价于在分类任务上做 L2 正则化!
三、支持向量(Support Vectors)
什么是支持向量?
支持向量是那些距离决策边界最近的样本------它们恰好落在间隔边界上:
特征₂
│
│ ○ ←─── 这个 ○ 是支持向量
│ ○ ╲
│ ○ ╲ × ←─── 这个 × 是支持向量
│━━━━━━━━━━━━━━━━ 决策边界
│ ╱ ╲ ×
│ ╱ × ×
└──────────────────→ 特征₁
为什么它们叫"支持"向量?
因为这些点是整个模型的"支柱"------只有它们决定了决策边界的位置。其他远离边界的样本,无论怎么移动,只要不越过间隔边界,都不影响决策边界。
支持向量的关键性质
# 训练 SVM 后,支持向量的索引
support_vector_indices = model.support_
print(f"支持向量个数: {len(support_vector_indices)}")
# 在 569 个样本中,通常只有 30-80 个是支持向量
# 查看哪些是支持向量
support_vectors = model.support_vectors_
| 性质 | 含义 |
|---|---|
| 稀疏性 | 只有少数样本(支持向量)决定模型 |
| 鲁棒性 | 远离边界的样本不影响决策边界 |
| 高效性 | 预测时只需比较新样本和支持向量 |
这与 KNN 形成鲜明对比------KNN 需要记住所有训练数据,SVM 只需要记住支持向量。
四、软间隔:处理不可分数据
现实世界没有完美线性可分的数据
真实数据通常不是完美线性可分的------总有一些点落在错误的一侧。如果强制要求所有点都分类正确,SVM 可能会找到一个极度扭曲的边界,导致过拟合。
Soft Margin SVM
引入松弛变量(slack variable)ξᵢ,允许一些样本被错误分类或被分到间隔内部:
min (1/2) × ||w||² + C × Σ(ξᵢ)
s.t. yᵢ × (w·xᵢ + b) ≥ 1 - ξᵢ
ξᵢ ≥ 0
C 是正则化参数,控制"对错误分类的容忍度":
| C 值 | 效果 | 类比 |
|---|---|---|
| C 很大(如 100) | 间隔窄,几乎不允许错误 | 严格的老师,训练准确率高,可能过拟合 |
| C 适中(如 1.0) | 间隔合理,允许少量错误 | 正常的老师,泛化能力好 |
| C 很小(如 0.01) | 间隔宽,容忍较多错误 | 宽松的老师,可能欠拟合 |
from sklearn.svm import SVC
# C 越小,正则化越强,决策边界越平滑
svm_strict = SVC(C=100, kernel='linear') # 严格,边界复杂
svm_normal = SVC(C=1.0, kernel='linear') # 适中(默认)
svm_loose = SVC(C=0.01, kernel='linear') # 宽松,边界平滑
C 的调参:和所有正则化参数一样,用交叉验证选择 C。
五、核技巧(Kernel Trick):SVM 最精妙的设计
线性不可分的问题
很多现实问题不是线性可分的------比如:
特征₂ 特征₂
│ │
│ ○ ○ │ ○ ○
│ ○ ○ │ ○ ○ ○ ○
│ ○ ○ │ ○ ○ ○
│ × × │ ×
│ × × │ × ×
│ × × │ × ×
└──────────→ 特征₁ └──────────────→ 特征₁
线性可分(SVM 直接处理) 线性不可分(需要核技巧)
核心思想:映射到高维空间
对于线性不可分的数据,一个经典思路是:把数据映射到更高维的空间,在高维空间中用线性边界分类。
二维空间中线性不可分:
x₁² + x₂² > r² → 圆内为 +1 类
无法用一条直线分隔
映射到三维空间:
令 z = x₁² + x₂²
在 (x₁, x₂, z) 三维空间中,数据变得线性可分
→ 用一个平面 z = r² 就可以分开
但直接做高维映射的问题:计算量爆炸。如果原始特征是 1000 维,映射到无穷维后根本无法计算。
核函数:隐式完成高维映射
核函数(Kernel Function)的精妙之处在于:不需要显式计算高维空间中的坐标,只需计算两个向量在高维空间中的内积。
原始空间中的距离:<xᵢ, xⱼ> ← 直接计算
高维空间中的内积:K(xᵢ, xⱼ) = <φ(xᵢ), φ(xⱼ)>
← 用核函数一步算出,不需要知道 φ 的具体形式
常用核函数
from sklearn.svm import SVC
# 1. 线性核 ------ 等价于不带核的线性 SVM(最快)
svm_linear = SVC(kernel='linear', C=1.0)
# K(x, y) = x · y
# 适用场景:数据本身线性可分,或特征维度很高
# 2. 多项式核 ------ 通过多项式组合特征
svm_poly = SVC(kernel='poly', degree=3, C=1.0)
# K(x, y) = (γ·x·y + coef0)^degree
# 适用场景:有交互特征的数据
# 3. RBF 核(径向基函数)------ 最常用,默认选择
svm_rbf = SVC(kernel='rbf', gamma='scale', C=1.0)
# K(x, y) = exp(-γ × ||x - y||²)
# γ 控制影响半径:γ 越大,每个点的影响范围越小
# 适用场景:大多数非线性问题
# 4. Sigmoid 核
svm_sigmoid = SVC(kernel='sigmoid', gamma='scale', C=1.0)
# K(x, y) = tanh(γ·x·y + coef0)
# 适用场景:类似神经网络的激活函数
核函数对比
| 核函数 | 参数 | 决策边界特点 | 适用场景 | 使用频率 |
|---|---|---|---|---|
| 线性核 | C | 直线/平面 | 高维数据、文本分类 | ⭐⭐⭐ |
| RBF 核 | C, γ | 任意光滑形状 | 大多数场景,默认首选 | ⭐⭐⭐⭐⭐ |
| 多项式核 | C, degree | 多项式曲线 | 有特征交互 | ⭐⭐ |
| Sigmoid 核 | C, γ | 类似神经网络 | 特定场景 | ⭐ |
RBF 核为什么是默认选择?
线性核是 RBF 的特例(γ 很小时)
多项式核是 RBF 的近似
→ RBF 覆盖了线性核和多项式核的能力
→ 只有一个额外参数 γ,调参简单
六、SVM 的两个关键参数:C 和 γ
C(正则化参数)
C ↑ → 对错误容忍度低 → 边界复杂、可能过拟合
C ↓ → 对错误容忍度高 → 边界平滑、可能欠拟合
调参范围:[0.001, 0.01, 0.1, 1, 10, 100]
默认值:1.0
γ(RBF 核参数)
γ 控制单个样本的影响半径:
γ ↑ → 每个样本影响范围小 → 决策边界复杂(关注局部细节)
γ ↓ → 每个样本影响范围大 → 决策边界平滑(全局视角)
调参范围:[0.0001, 0.001, 0.01, 0.1, 1, 10]
默认值:'scale' = 1 / (特征数 × X.var())
γ 的直观理解
γ = 0.01(小) γ = 1(适中) γ = 100(大)
┌──────┐ ┌──────┐ ┌──────┐
│ ╱╲ │ │ ╱╲ │ │╱╲╱╲╱│
│╱ ╲ │ │╱ ╲ │ │╱╲╱╲╱│
│ ╲ │ │ ╲ │ │╱╲╱╲╱│
│ ╲│ │ ╲ │ │╱╲╱╲╱│
└──────┘ └──────┘ └──────┘
平滑,可能欠拟合 正常,泛化好 复杂,可能过拟合
网格搜索调参
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
# SVM + 标准化(SVM 也是距离算法,必须做特征缩放)
pipeline = make_pipeline(
StandardScaler(),
SVC(kernel='rbf')
)
# 参数网格
param_grid = {
'svc__C': [0.1, 1, 10, 100],
'svc__gamma': [0.001, 0.01, 0.1, 1, 'scale', 'auto'],
}
# 网格搜索
grid = GridSearchCV(
pipeline,
param_grid,
cv=5,
scoring='accuracy',
n_jobs=-1,
verbose=1
)
grid.fit(X_train, y_train)
print(f"最佳参数: {grid.best_params_}")
print(f"最佳交叉验证准确率: {grid.best_score_:.3f}")
print(f"测试集准确率: {grid.score(X_test, y_test):.3f}")
七、SVM 完整实战:手写数字识别
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.metrics import classification_report, ConfusionMatrixDisplay
# ===== 1. 加载手写数字数据集 =====
# 8×8 像素的手写数字(0-9),1797 个样本
digits = load_digits()
X, y = digits.data, digits.target
print(f"样本数: {X.shape[0]}, 特征数: {X.shape[1]}")
print(f"类别: {digits.target_names}")
# 样本数: 1797, 特征数: 64
# ===== 2. 可视化样本 =====
fig, axes = plt.subplots(2, 5, figsize=(10, 4))
for i, ax in enumerate(axes.ravel()):
ax.imshow(digits.images[i], cmap='gray')
ax.set_title(f'数字: {digits.target[i]}')
ax.axis('off')
plt.tight_layout()
plt.show()
# ===== 3. 划分 =====
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# ===== 4. 构建 SVM 流水线 =====
pipeline = make_pipeline(
StandardScaler(),
SVC(kernel='rbf', class_weight='balanced')
)
# ===== 5. 网格搜索调参 =====
param_grid = {
'svc__C': [0.1, 1, 10, 100],
'svc__gamma': [0.001, 0.01, 0.1, 'scale'],
}
grid = GridSearchCV(
pipeline, param_grid, cv=5, scoring='accuracy', n_jobs=-1
)
grid.fit(X_train, y_train)
print(f"最佳参数: {grid.best_params_}")
print(f"最佳交叉验证准确率: {grid.best_score_:.3f}")
# ===== 6. 评估 =====
test_acc = grid.score(X_test, y_test)
y_pred = grid.predict(X_test)
print(f"\n测试集准确率: {test_acc:.3f}")
print("\n分类报告:")
print(classification_report(y_test, y_pred))
# ===== 7. 查看支持向量比例 =====
best_svm = grid.best_estimator_.named_steps['svc']
sv_ratio = len(best_svm.support_vectors_) / len(X_train)
print(f"支持向量: {len(best_svm.support_vectors_)} / {len(X_train)} ({sv_ratio:.1%})")
# 只有约 30-40% 的训练样本是支持向量
输出示例:
样本数: 1797, 特征数: 64
最佳参数: {'svc__C': 10, 'svc__gamma': 0.01}
最佳交叉验证准确率: 0.985
测试集准确率: 0.989
分类报告:
precision recall f1-score
0 1.00 1.00 1.00
1 0.97 1.00 0.99
2 1.00 1.00 1.00
3 1.00 0.97 0.99
4 1.00 0.97 0.99
5 0.97 0.97 0.97
6 1.00 1.00 1.00
7 1.00 1.00 1.00
8 0.97 0.97 0.97
9 0.97 1.00 0.99
支持向量: 514 / 1437 (35.8%)
八、SVM vs 逻辑回归 vs KNN
三类算法对比
| 对比维度 | 逻辑回归 | KNN | SVM(RBF 核) |
|---|---|---|---|
| 决策边界 | 线性 | 非线性(分段) | 非线性(核映射) |
| 训练速度 | 快 | 无训练 | 中等(O(N²~N³)) |
| 预测速度 | 最快 | 慢 | 快(仅支持向量) |
| 高维数据 | ✅ 好 | ❌ 差 | ✅ 好 |
| 可解释性 | ✅ 最好 | ❌ 差 | ❌ 差 |
| 数据量敏感 | 少 | 少 | 中 |
| 特征缩放 | 建议做 | 必须做 | 必须做 |
选型指南
特征数 >> 样本数(文本分类 等)
→ 线性 SVM 或 逻辑回归(线性核在特征多时表现好)
样本数 >> 特征数(一般结构化数据)
→ RBF 核 SVM 或 随机森林(V 形决策面效果好)
数据量大(>10万)
→ 逻辑回归(SVM 训练复杂度 O(N²~N³),大数据太慢)
需要可解释性
→ 逻辑回归(能告诉你每个特征的影响)
数据低维且小
→ KNN 或 RBF-SVM(都适合小数据)
九、SVR:SVM 做回归
SVM 不仅做分类,也可以做回归------称为 SVR(Support Vector Regression)。
SVR 的核心思想与 SVC 相反:
SVC(分类):让尽可能多的点在间隔之外(正确分类)
SVR(回归):让尽可能多的点在间隔内部(误差容忍)
┌─────────── ε-不敏感带 ───────────┐
│ 预测值 ↑ │
│ │ ● │
│ │ ● ● │
│ │ ●━━━━━━━━━━━● ● │
│ │━━━━━━━━━━━━━━━━━━━━ │ ← 预测线
│ │ ● ● ● ● ●━━━━● │
│ │● ● │
│ └────────────────────→ 真实值 │
└─────────────────────────────────────┘
在 ε-不敏感带内的点,误差为 0
带外的点,计算误差
from sklearn.svm import SVR
svr = SVR(kernel='rbf', C=1.0, epsilon=0.1)
# epsilon = ε,控制不敏感带的宽度
svr.fit(X_train, y_train)
y_pred = svr.predict(X_test)
十、SVM 的优缺点与适用场景
优点
- 最大间隔 = 内置正则化------泛化能力强
- 核技巧------可以用线性模型解决非线性问题
- 支持向量稀疏性------只依赖少数关键样本
- 高维数据表现好------文本分类等领域表现优异
- 理论完备------凸优化保证全局最优解
缺点
- 训练复杂度 O(N²~N³)------大数据集上极慢
- 没有概率输出------原生 SVM 只输出类别(Platt 缩放可扩展,但增加复杂度)
- 参数调优需要经验------C 和 γ 的组合搜索耗时
- 不直接支持多分类------需要用 OvR/OvO 策略
- 缺乏可解释性------RBF 核的决策边界难以理解
2026 年的 SVM
| 场景 | 地位 |
|---|---|
| 中小规模数据集 | ✅ 仍然是最强算法之一 |
| 文本分类 | ✅ 线性 SVM 仍是最常用 baseline |
| 大规模数据 | ❌ 被 XGBoost 和神经网络取代 |
| 图像 | ❌ 被深度 CNN/Transformer 彻底取代 |
| 需要概率输出 | ⚠️ 逻辑回归或神经网络更适合 |
SVM 的黄金时代(2000-2015 年)已经过去,但在中小规模和文本数据上,它仍然是极有价值的选择。
十一、总结
| 概念 | 一句话理解 |
|---|---|
| 最大间隔 | 不只要分开,还要让决策边界离两边越远越好 |
| 支持向量 | 决定边界的"关键少数"------其他的点不重要 |
| 软间隔 | 通过 C 参数允许一些点被分错,防止过拟合 |
| 核技巧 | 不用显式计算高维映射,直接算出高维内积 |
| RBF 核 | 默认核函数------γ 控制每个点的影响半径 |
核心三句话:
- SVM = 线性分类器 + 最大间隔 + 核技巧------三项叠加让它成为经典 ML 的巅峰
- 核技巧是高维映射的"物理学"------不实地测量也能算出宇宙的距离
- SVM 在小到中等数据上仍是最强选择之一------尤其文本和高维稀疏数据
从线性回归到 SVM,我们走过了经典 ML 中四个最重要的线性/非线性模型。下一篇文章将进入决策树------一个完全不同的世界:不是优化超平面,而是问一系列"是/否"问题。