SVM 找的不是「能分开的线」,而是「离两边都最远的那条线」。 只有支持向量在决定这条线,其他点删掉线也不动。 C 控制容忍度:C 小边界宽、肯放过错点;C 大边界窄、硬要贴合。
前两篇是回归,预测一个数。这一篇开始是分类,预测一个类别。 数据用 make_blobs 造,种子写死,谁跑都是同一份。
一、它想干什么
两类点中间能画无穷多条线,选哪条? SVM 的答案:选那条离两边最近点都尽量远的。这条线叫最大间隔分界线。 离分界线最近的那几个点叫支持向量,它们决定了线画在哪。
其他点删掉,线一点不动。这就是它名字的来历,也是它的性格。
二、线性核:一条直线
八十个点分两团,训练 60、测试 20。 支持向量只有 3 个,占训练集的 5%。 训练准确率 1.0000,测试准确率也是 1.0000。
数据分得开的时候,SVM 又快又准,支持向量还特别少。
kernel 是「用什么形状的线」。linear 是直线,rbf 是弯曲的。
三、故意标错六个点
真实数据总有噪声,甚至还有录错的。这次把前六个点的标签写反。 这种点谁都分不对,问题是:模型该不该为了它们把边界拧歪? 放宽靠参数 C:C 大表示「一个都不许错」,C 小表示「允许几个错,换更宽的间隔」。
C 从 0.01 加到 100,支持向量从 22 个降到 16 个。 训练准确率一直是 0.9333——那六个点本来就该分不对。 但测试准确率从 0.9000 掉到 0.8500。训练分没涨,测试分却降了。
C 小时模型愿意放弃那六点,换一条更宽的边界;C 大了边界被它们拽歪,新数据就吃亏。
四、弯的边界:rbf 核
两个交错的半月形,一条直线怎么画都分不开。 线性核测试准确率 0.9600,rbf 核 1.0000。差的就是那条弯曲的本事。 rbf 把边界掰弯了,所以能分开。这是核函数的作用,不是参数调出来的。
rbf 有两个参数:C 和 gamma。gamma 控制「弯得多细」,越大越贴着点走。
五、用它之前先想两件事
第一,所有特征先标准化。SVM 靠距离算,量纲不一会让大数特征压倒一切。 第二,样本别太大。训练复杂度接近平方级,几万条就开始慢。 样本几十万、特征几百万的场子,是线性模型和树模型的天下。
标准化这一步很多人省掉,结果模型全靠一个特征在撑,还以为是 SVM 不行。
六、动手 Demo:支持向量机:画一条最宽的分界
先看一份干净数据的 SVM,再看故意标错六个点之后 C 的影响。 最后用两个半月形对比线性核和 rbf 核。
from sklearn.datasets import make_blobs, make_moons
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
X, y = make_blobs(n_samples=80, centers=2, cluster_std=1.0, random_state=42)
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=42)
sv = SVC(kernel="linear", C=1.0).fit(Xtr, ytr)
print("干净数据:支持向量 %d 个,训练 %.4f,测试 %.4f"
% (len(sv.support_vectors_), sv.score(Xtr, ytr), sv.score(Xte, yte)))
X2, y2 = make_blobs(n_samples=80, centers=2, cluster_std=2.0, random_state=42)
y2[0:6] = 1 - y2[0:6]
Xtr2, Xte2, ytr2, yte2 = train_test_split(X2, y2, test_size=0.25,
random_state=42)
for c in (0.01, 0.1, 1.0, 100.0):
mc = SVC(kernel="linear", C=c).fit(Xtr2, ytr2)
print("C=%-7g 支持向量 %2d 训练 %.4f 测试 %.4f"
% (c, len(mc.support_vectors_), mc.score(Xtr2, ytr2),
mc.score(Xte2, yte2)))
Xm, ym = make_moons(n_samples=100, noise=0.15, random_state=42)
Xtr3, Xte3, ytr3, yte3 = train_test_split(Xm, ym, test_size=0.25,
random_state=42)
print("半月形 线性核 %.4f,rbf 核 %.4f"
% (SVC(kernel="linear").fit(Xtr3, ytr3).score(Xte3, yte3),
SVC(kernel="rbf").fit(Xtr3, ytr3).score(Xte3, yte3)))
实跑输出:
--------------------------------------------------------------------
支持向量机:画一条最宽的分界
--------------------------------------------------------------------
① 它想干什么
--------------------------------------------------------------------
二分类问题,要在两类点中间画一条线。能画的线有无穷多条,选哪条?
SVM 的答案:选那条离两边最近点都尽量远的。这条线叫最大间隔分界线。
离分界线最近的那几个点叫支持向量,它们决定了线画在哪。
! 只有支持向量在起作用,其他点删掉,线也不动。这就是它名字的来历。
② 线性核:一条直线
--------------------------------------------------------------------
八十个点,两类,每类四十个。训练集 60,测试集 20。
支持向量 3 个,占训练集的 5.0%。
训练准确率 1.0000,测试准确率 1.0000。
! kernel 是「用什么形状的线」。linear 是直线,rbf 是弯曲的。
③ 分不开的时候
--------------------------------------------------------------------
真实数据里总有「你中有我」的点,甚至还有标错的。一条直线怎么画都分不干净。
两个办法:换个弯的核函数,或者放宽一点容忍几个错分的点。
放宽靠参数 C:C 大表示「一个都不许错」,C 小表示「允许几个错,换更宽的间隔」。
八十个点分两团,再故意把前六个点的标签写反,模拟记录错误。
这种点谁都分不对。问题是:模型该不该为了它们把边界拧歪?
| C | 支持向量个数 | 训练准确率 | 测试准确率 |
|---:|---:|---:|---:|
| 0.01 | 22 | 0.9333 | 0.9000 |
| 0.1 | 17 | 0.9333 | 0.9000 |
| 1 | 16 | 0.9333 | 0.8500 |
| 100 | 16 | 0.9333 | 0.8500 |
C 从 0.01 加到 100,支持向量从 22 个降到 16 个。
训练准确率一直是 0.9333——那六个错标点始终分不对,这本来就该分不对。
但测试准确率从 0.9000 掉到 0.8500。训练分没涨,测试分却降了。
C 小的时候,模型愿意放弃那六个点,换一条更宽的边界;C 大了,边界被它们拽歪,新数据就吃亏。
! 这就是过拟合的样子:不是训练分变高,而是测试分变低。
④ 弯的边界:rbf 核
--------------------------------------------------------------------
两个半月形,交错的,一条直线分不开。
线性核测试准确率 0.9600,rbf 核 1.0000。
差的就是那条弯曲的本事。rbf 能把边界掰弯,所以能分开。
! rbf 有两个参数:C 和 gamma。gamma 控制「弯得多细」,越大越贴着点走。
⑤ 用它之前先想两件事
--------------------------------------------------------------------
第一,所有特征先标准化。SVM 靠距离算,量纲不一样会让大数特征压倒一切。
第二,样本量别太大。SVM 训练复杂度接近平方级,几万条就开始慢。
样本几十万、特征几百万的场子,是线性模型和树模型的天下。
! 标准化这一步很多人省掉,结果模型全靠一个特征在撑,还以为是 SVM 不行。
y2[0:6] = 1 - y2[0:6] 是把前六个标签 0 变 1、1 变 0。这是「造噪声」的标准手法。
support_vectors_ 是支持向量本身的坐标,len(...) 才是个数。
盯着测试准确率看,别盯训练准确率。这次 C 变大时训练分没动,测试分掉了 5 个点。
30 秒实验
- 把翻转的六个点改成二十个,看 C 的影响还明显不明显。
- 在 rbf 核上加 gamma 参数,试 0.1、1、10,看测试准确率怎么变。
- 想一想:为什么 SVM 对量纲特别敏感?它算的到底是什么距离?
常见误区
- 只看训练准确率:C 大能把训练分刷上去,测试分反而掉。
- 不标准化就上 SVM:量纲大的特征会主导距离计算。
- 以为支持向量越多越好:越多说明边界越宽、容错越多,不一定是好事。
- 拿直线核硬啃弯形状:该换 rbf,不是调 C 能救的。
自测三问(点开对答案)
1. SVM 在众多分界线里选哪一条? —— 离两边最近点都最远的那条,也就是间隔最大的那条。 2. C 变大意味着什么? —— 对错分的容忍变小,边界变窄、贴着点走,容易过拟合。 3. 为什么 SVM 必须先标准化? —— 它靠距离计算,量纲大的特征会把距离主导掉,其他特征等于没参与。有标签的分类讲完了。下一篇反过来:没有标签,怎么把点分成几堆。
- ☐ (本篇完,下一篇 8.5 聚类:没有标签怎么分堆)