25 个像素里,真正管用的可能只有两三个方向。降维要干的事,就是把这几个方向找出来, 其余的扔掉。 最经典的两个办法:主成分分析只看数据散得开不开,线性判别分析还要看标签。 一个不看答案,一个看答案——差别有多大,这一节用判别比量给你看。
这一节接 10.1 的最后一句话。上一节说「维数一高距离就失灵」,这里给出解法。 它也是 7.1 无监督学习的第二次出现:主成分分析是最典型的无监督方法,因为它一个标签都不用。
一、25 个像素里,有几个是废的
造一批小图:三个 5 乘 5 的模板(十字、斜叉、方框),每类 40 张训练、40 张测试, 每张按 12% 的概率翻转像素。 把 25 个像素原样当特征,最近邻分类的测试准确率是 0.9917。 看着挺好,可这 25 个数里有大量冗余:四个角基本恒为 0, 中间那个像素每张都不一样——它们对分类毫无贡献,却各自占着一个维度。 这正是 10.1 讲的那个陷阱的温和版。25 维还不至于让距离失灵, 但每多一个废维度,距离里就多一份纯噪声。
二、主成分分析:顺着散得最开的方向看
主成分分析的想法一句话说完:找数据散得最开的那个方向,把数据投影上去; 再找第二个方向(必须跟第一个垂直),继续投影。 为什么「散得最开」等于「信息最多」? 因为如果一个方向上所有点都挤在一起,那这个方向就分不出谁是谁;散得开,才有区分度。 对训练集算 25 乘 25 的协方差矩阵,做特征分解。前三个主成分画回 5 乘 5,是这样:
| 主成分 | 占方差 | 形状 |
|---|---|---|
| 第 1 个 | 31.0% | 一个十字(横竖各一道) |
| 第 2 个 | 27.4% | 一个斜叉(两条对角线) |
| 第 3 个 | 3.7% | 边角的零碎 |
| 这两个方向正好对应「十字」和「斜叉」两个模板。 | ||
| 没人告诉算法哪个模板长什么样,它自己从协方差里看出来的。 | ||
| 累计解释方差: |
| 主成分个数 | 1 | 2 | 3 | 5 | 8 | 12 | 25 |
|---|---|---|---|---|---|---|---|
| 累计方差 | 0.3095 | 0.5839 | 0.6206 | 0.6870 | 0.7652 | 0.8469 | 1.0000 |
| 前 2 个主成分只留下 58.4% 的方差,可分类准确率是 1.0000——比用全部 25 维的 0.9917 还高一点。 | |||||||
| 这件事值得停下来想一秒:丢掉 41.6% 的方差,结果反而更好。 | |||||||
| 因为丢掉的方差里,绝大部分是噪声。 | |||||||
| 降维不是压缩,是筛掉噪声——这是它跟「文件压缩」最大的区别。 |
三、一个必须提的坑:不标准化
主成分有个软肋:它只看方差大小,不问这个方差有没有意义。 做个实验:在 25 个像素后面再挂一个特征——「墨水总量」乘 40 再加噪声。 这个数纯粹是噪声,可它数值大,方差也就大。
| 做法 | 第 1 主成分占方差 | 第 26 维吃掉的载荷 | 前 2 维准确率 |
|---|---|---|---|
| 不标准化 | 0.9997 | 100.0% | 0.9167 |
| 先标准化 | 0.3167 | 8.1% | 0.9917 |
| 不标准化的时候,第 1 主成分 100.0% 的载荷都压在那一维噪声上,25 个像素一点没进。 | |||
| 换句话说,算法把整张图忽略了,只盯着那个数。 | |||
| 准确率立刻从 0.9917 掉到 0.9167。这是「降维降错了方向,信息真丢了」。 | |||
| 标准化之后,第 26 维只占 8.1%,主成分才回到图形结构上。 | |||
| 所以「先标准化再降维」不是讲究,是这一步的前提条件。 |
四、线性判别分析:让标签说话
主成分从头到尾不看标签。可如果标签本来就有,为什么不利用? 线性判别分析的想法:找一个方向,让「类间散得开、类内挤得紧」。 这个目标跟主成分完全不一样——主成分只问「整体散不散」,它问「类跟类分不分得开」。 量这个的指标叫判别比:类间方差除以类内方差,越大越分得开。同样把 25 维压到 1 维:
| 取 1 个方向 | 判别比 | 测试准确率 |
|---|---|---|
| 主成分(不看标签) | 14.1566 | 0.9417 |
| 判别方向(看标签) | 22.8599 | 0.9667 |
| 判别方向赢了,而且这是它该赢的——它优化的正是这个指标。 | ||
| 这张表要看的不是谁赢,是「有标签」这件事值多少钱: | ||
| 同样的数据、同样降到 1 维,只换个方向,判别比从 14.1566 变成 22.8599。 | ||
| 但判别分析有个死穴。把训练样本一路减下去: |
| 每类训练张数 | 判别比 | 测试准确率 |
|---|---|---|
| 9 | 712.8476 | 0.7300 |
| 15 | 73.5109 | 0.8100 |
| 30 | 28.1910 | 0.8500 |
| 40 | 23.0695 | 0.8717 |
| 每类只有 9 张的时候,判别比高得离谱(712 对 23),测试准确率却最低(0.7300 对 0.8717)。 | ||
| 原因很具体:25 维里凑 27 个样本,类内散度矩阵几乎是奇异的。 | ||
| 它求逆的时候会把「噪声的方向」也算成「可分的方向」,判别比就被刷上去了。 | ||
| 这是小样本下判别分析的典型翻车——指标漂亮,效果拉胯。 |
五、降维不是压缩,是取舍
把三种做法摆在一起:主成分、标准化后再主成分、线性判别分析。
| 做法 | 要不要标签 | 擅长什么 | 怕什么 |
|---|---|---|---|
| 主成分 | 不要 | 去冗余、可视化 | 被量纲拐跑 |
| 标准化+主成分 | 不要 | 同上,且公平对待每一维 | 把有用的小方差也压平 |
| 线性判别分析 | 要 | 分类前降维,判别比直接优化 | 样本少时矩阵奇异 |
| 三者没有谁更好,只有用在哪。 | |||
| 没有标签(或标签不可信)就主成分;要分类、标签可靠、样本够多就判别分析。 | |||
| 而两个方法都在做同一件事:把「维度」换成「方向」。 | |||
| 方向的好处是它可以挑——挑那些真带信息的,扔掉剩下的。 | |||
| 这一节的所有算法都有一个共同的软肋:方向和目标都是人设计的。 | |||
| 主成分挑「散得开」,判别分析挑「分得开」,可到底哪个方向真的有用,最终还是得靠人猜。 | |||
| 9.4 讲的卷积核,就是把这个「猜」交给数据去做。 |
六、动手 Demo:从 25 个像素里,抽出两三个真正有用的数
五段:原始像素的冗余、主成分长什么样、标准化的坑、判别分析、三者怎么选。 第 2 段和第 4 段是主角,一个是无监督的极限,一个是有监督的起点。 协方差分解、广义特征值、判别比,全部自己写。 主成分的图是「把方向画回 5 乘 5」,这一步特别值得盯着看:数学算出来的东西,居然长得像笔画。
def pca(X, k):
M = X.mean(0)
Z = X - M # 先减均值,再做分解
C = Z.T @ Z / max(1, len(X) - 1) # 协方差矩阵
v, V = np.linalg.eigh(C) # 对称矩阵用 eigh,一定出实数
o = np.argsort(v)[::-1] # 方差从大到小排
v, V = v[o], V[:, o]
return M, V[:, :k].T, v / v.sum() # 方向、解释方差比
def lda(X, y, k=1):
Sw = np.zeros((dim, dim)) # 类内散度:同一类离自己中心多远
Sb = np.zeros((dim, dim)) # 类间散度:各类中心离总中心多远
for c in range(NC):
Xc = X[y == c]
mc = Xc.mean(0)
Sw += (Xc - mc).T @ (Xc - mc)
d = (mc - M)[:, None]
Sb += len(Xc) * (d @ d.T)
W, V = np.linalg.eig(np.linalg.pinv(Sw) @ Sb) # 广义特征值问题
return V.real[:, np.argsort(W.real)[::-1]][:, :k].T
# 标准化的作用:不除以标准差,主成分就被数值最大的那一维拐跑
Z = (X - X.mean(0)) / X.std(0)
实跑输出:
--------------------------------------------------------------------
从 25 个像素里,抽出两三个真正有用的数
--------------------------------------------------------------------
==============================================================
[1] 直接用像素当特征,问题出在哪
三个 5 乘 5 的模板,每类加 12% 的像素翻转噪声,各造 40 张训练、40 张测试:
十字 ..#.. ..#.. ##### ..#.. ..#..
斜叉 #...# .#.#. ..#.. .#.#. #...#
方框 ##### #...# #...# #...# #####
每张图都是 25 个数,取值为 0 或 1。
拿 25 个像素原样当特征,最近邻分类:
25 维原始像素 测试准确率 0.9917
看着还行——因为噪声只有 12%,形状还在。
可这 25 个数里有大量冗余:中间那个像素几乎每张图都不一样,
而四个角基本恒为 0,它们对分类一点贡献都没有,却一样占着一个维度。
[2] 主成分分析:把 25 维压成 2 维
对训练集算协方差矩阵(25 乘 25),做特征分解,
取方差最大的几个方向。前三个主成分长这样:
第 1 主成分(占方差 31.0%)
+
.#..
+###:
.#.
+
第 2 主成分(占方差 27.4%)
:. +
#.#
.+:
#.#
+. +
第 3 主成分(占方差 3.7%)
:.:
:..
++ .
. :++
:#::+
累计解释方差:
前 1 个主成分 累计 0.3095
前 2 个主成分 累计 0.5839
前 3 个主成分 累计 0.6206
前 5 个主成分 累计 0.6870
前 8 个主成分 累计 0.7652
前 12 个主成分 累计 0.8469
前 25 个主成分 累计 1.0000
只用前 2 个主成分(25 维降到 2 维,只留下 58.4% 的方差),再分类:
25 维原始像素 测试准确率 0.9917
2 维主成分 测试准确率 1.0000
**维度砍掉九成,准确率不但没掉,还高了一点——说明那 23 个维度里本来就没信息,**
**这就是特征提取的意思:不是压缩,是「把没用的先扔掉」。**
[3] 一个坑:不标准化,主成分会被「量纲大」的那一维拐跑
在 25 个像素后面再挂一个特征:一张图的「墨水总量」乘 40,
这个数纯粹是噪声,但它数值大。看看主成分往哪边倒:
不标准化:第 1 主成分占方差 0.9997,其中第 26 维吃掉载荷能量的 100.0%
不标准化:前 2 维分类准确率 0.9167
标准化后:第 1 主成分占方差 0.3167,其中第 26 维只占载荷能量的 8.1%
标准化后:前 2 维分类准确率 0.9917
**两个后果都出来了:**
**第一,不标准化时主成分几乎整条都趴在那个噪声维上,25 个像素一点没进;**
**第二,分类准确率从 0.9917 掉到 0.9167——降维降错了方向,信息真丢了。**
**标准化之后第 26 维只占一小撮载荷,主成分才回到图形结构上。**
**所以「先标准化再降维」不是讲究,是这一步的前提。**
[4] 线性判别分析:让标签告诉你「哪个方向分得开」
主成分只问「哪个方向数据散得开」,从不看标签。
线性判别分析反过来:找「类间散得开、类内挤得紧」的方向。
只取 1 个方向 判别比 测试准确率
主成分(不看标签) 14.1566 0.9417
判别方向(看标签) 22.8599 0.9667
判别比是「类间方差除以类内方差」,越大说明三类在那个方向上越分得开。
**判别方向是按定义就该赢的——它优化的正是这个指标,**
**主成分完全没理由赢。这张表要看的不是谁赢,**
**而是「有标签」这件事本身值多少钱:同样的 25 维数据、同样降到 1 维,**
**换个方向,判别比就从 14.1566 变成 22.8599。**
但判别分析有个死穴,演示一下它的代价:
每类 9 张训练:判别比 712.8476,测试准确率 0.7300
每类 15 张训练:判别比 73.5109,测试准确率 0.8100
每类 30 张训练:判别比 28.1910,测试准确率 0.8500
每类 40 张训练:判别比 23.0695,测试准确率 0.8717
**每类只有 9 张的时候判别比高得离谱,测试准确率却不是最高——**
**因为 25 维里凑 27 个样本,类内散度矩阵几乎是奇异的,**
**它会把「噪声的方向」也算成可分方向。这就是小样本下判别分析的典型翻车。**
[5] 结论
四段连起来:**原始像素维数高但信息稀**;
**主成分把散得最开的方向找出来,压到 2 维也够用**;
**但它会被量纲拐跑,得先标准化**;
**想用上标签就换判别分析,代价是小样本时容易过拟合。**
这一节讲的是「提特征」的数学版:降维。
下一节把它用到图上,看图像识别整条流水线长什么样。
第 2 段那三张主成分图是本节最值回票价的地方。 第 1 个画出来是十字,第 2 个画出来是斜叉——正好是两个模板的形状。 没人告诉它这件事,它只是找了协方差最大的方向。 58.4% 的方差换 1.0000 的准确率,这个组合要记住。 它说明「保留多少方差」和「保留多少信息」不是一回事,方差里混着大量跟类别无关的部分。 第 3 段那个 100.0% 对 8.1% 是全篇最该抄下来的对照。 不标准化的时候,主成分整条趴在噪声上——这不是精度问题,是方向完全错了。 第 4 段的两张表要连着读。第一张说判别方向赢(22.8599 对 14.1566), 第二张说样本一少它就崩(判别比 712 而准确率只有 0.7300)。 一个方法的长处和短处,往往来自同一个机制。
30 秒实验
- 把第 1 段的翻转概率从 12% 改成 30%,重跑。 主成分压缩到 2 维的准确率会掉,而 25 维的掉得更快——噪声越大,降维的价值越大。
- 把第 2 段的模板换成三个更像的形状(比如 0、8、6),重跑主成分图。 前两个主成分会变成「哪边有缺口」这类方向——它挑的永远是「变化最大的地方」。
- 把第 4 段的小样本实验改成每类 3 张,重跑。 判别比会飙到几千,准确率掉到 0.5 以下——这就是奇异矩阵在刷分。
常见误区
- 以为「保留的方差越多越好」。Demo 第 2 段:只留 58.4% 的方差, 准确率 1.0000,比留 100% 的 0.9917 还高。 方差里混着噪声,全留下等于把噪声也留下。
- 以为「主成分一定比原始特征好」。不一定。 主成分只保证「重建误差小」,不保证「分类好」——它压根不看标签。
- 以为「判别比高就是好模型」。Demo 第 4 段:每类 9 张时判别比 712.8476, 测试准确率却是最低的 0.7300。 指标和目标是两回事,小样本下这种背离是常态。
- 以为「标准化只是习惯」。Demo 第 3 段:不标准化,主成分 100.0% 的载荷压在噪声维上, 准确率 0.9167;标准化后 0.9917。这是一步之差十几个点的事。
自测三问(点开对答案)
1. 主成分分析凭什么认为「散得最开的方向」就是「信息最多的方向」? —— 因为**如果一个方向上所有点都挤在一起,那个方向就分不出谁是谁**。 Demo 第 2 段:第 1 主成分占 31.0% 的方差,画回 5 乘 5 是一个十字; **第 2 个占 27.4%,画出来是斜叉——正好对应两个模板**。 **散得开,才有区分度;这是它唯一的判据,也是它全部的局限。** 2. 为什么「先标准化」是主成分的前提? —— 因为**主成分只看方差大小,不看方差有没有意义**。 Demo 第 3 段:**在 25 个像素后面挂一个数值大的噪声维,** **不标准化时第 1 主成分 100.0% 的载荷全压在它上面,** **25 个像素一点没进**,准确率从 0.9917 掉到 0.9167。 **标准化后那一维只占 8.1%,主成分才回到图形结构上。** 3. 判别分析的判别比那么高,为什么还会翻车? —— 因为**判别比是「在训练集上算出来的」**,而小样本时类内散度矩阵接近奇异。 Demo 第 4 段: **每类 9 张(25 维里一共 27 个样本),判别比 712.8476,测试准确率只有 0.7300**; 每类 40 张时判别比降到 23.0695,**准确率反而升到 0.8717**。 **求逆放大了噪声方向,指标被刷上去,判别力却没跟上。**下一节把降维这件事用到图上。 不过图像识别真正在工业界落地的做法不是主成分,而是几种更「土」的手工特征。 投影、梯度方向直方图,都算。 它们比主成分差吗?不一定。下一节用同一批图量给你看。
- ☐ (本节完,下一节 10.3 图像识别)