阶段 4 · 学会创造

自编码器:强迫网络
把信息压到最小的样子

上一章《文本生成与采样》讲的是「学到的东西怎么取出来」;这一章反过来问:那份「什么重要」本身,能不能让网络自己学出来?
前面所有模型都在做「输入 → 输出」。自编码器很奇怪: 它的输出就是它的输入。看起来什么都没做——但中间那道被掐窄的喉咙, 逼着网络不得不学会"什么才是这张图真正重要的东西"。

1

结构:一个被掐住的沙漏

输入是一张 28×28 的手写数字灰度图(MNIST 数据集),拉平成 784 个数——每个像素一个数。 网络要做的事:把这 784 个数压成一小段,再原样还原回 784 个数。

部件干什么常见结构
编码器 Encoder把输入压成一小段"编码" 784 → 256 → 64 → 16
瓶颈 Bottleneck整个设计的核心——必须比输入小得多 16 维(原来 784 维)
解码器 Decoder从编码还原出输入 16 → 64 → 256 → 784
损失函数输入和还原有多像(像素级) MSE 或 BCE

瓶颈里那几个数叫编码,也记作 z;所有可能的 z 组成的空间,就叫潜空间。 后面说的「编码 z」「潜空间里的一个点」,指的都是它。 为什么是 16?它是人定的超参:小了糊、大了不压缩——具体丢多少,数学那节会把它摊开算。 损失函数一栏的两个选择也用得上:MSE 直接量两张图差多少;BCE 把每个像素当成「有多亮」的概率来算,只在像素值归一化到 0~1 时用。

💡 关键在于"故意做不到"

如果瓶颈和输入一样宽,网络会直接学成恒等映射(输出和输入一模一样),什么也学不到。
正是因为它做不到完美还原,才被迫丢弃冗余、保留本质。 这和人有损压缩的记忆其实是一回事——你记不住一个房间的所有细节,但能记住"那是个什么样的房间"。

真算 · 瓶颈里只准留 k 个数,这张图会长什么样

2

一个最有说服力的例子:线性自编码器 = PCA

如果编码器和解码器都只有一层线性层、没有激活函数, 那这个"神经网络"其实退化成了一个古老的统计方法——主成分分析(PCA)。 它要做的只有一件事:找到一条直线,让数据投影上去损失最小。

互动 · 亲手转动这条投影线

👆 先悬停,再拖那个滑块

把鼠标移到下面公式里的 θ 上——上面那个「投影线角度」滑块会亮起来。
然后把它从 0° 一路拖到 180°,盯着那行「重建误差 MSE」看它在哪个角度最低—— 那个角度,就是这一节要找的答案。

💡 这条线会自己找出来

点「让网络自己找」——它会用真实的梯度下降去优化这个角度, 从你当前的位置一路爬到最优解。爬到的地方,就是 PCA 的主成分方向。
这意味着:一个神经网络学出来的东西,有时候能被一个一百多年前的统计学方法解释清楚。

3

加上非线性:它才真正超过 PCA

只要在编码器/解码器里加一层非线性激活,"找一条直线"就变成了"找一张弯曲的曲面"。 「流形」说的就是数据实际分布的那张弯曲的面——点并不是均匀铺满整个空间的。

对比 · 数据长在一张弯曲的流形上时会怎样

线性自编码器:只能找一条直线

非线性自编码器:能跟着曲线弯

🎯 类比

线性自编码器像一个只会用直尺的人。给他一条弯曲的绳子,他只能找"整体最接近的那条直线", 细节全丢。
非线性自编码器拿到了软尺,可以贴着绳子的形状量。同样是把绳子压成"一个数", 后者能保留的信息多得多。
这个类比管到「沿着哪条弯线」为止:软尺的意思是沿着弯的数据走,用「走到第几厘米」一个数就能说清一个点在哪。

4

谱系:自编码器的各种变体

同一道沙漏,六种「加约束」的方式

变体加了什么约束学到了什么用在哪
欠完备自编码器瓶颈比输入窄 压缩表示——可以看成 PCA 的弯曲版:找的不再是直线,而是一条弯的曲线 / 曲面降维、特征提取
去噪自编码器 DAE输入加噪声,但要求还原干净的原图 更鲁棒的特征——它必须学会"噪声不是信号"(异常检测就靠这条:正常数据重建得好,没见过的数据重建误差大,按误差设阈值)预训练、异常检测
稀疏自编码器要求瓶颈里大部分维度是 0 可解释的特征。今天被大量用于机械可解释性(把大模型内部的特征拆开来看,阶段 8)特征解耦、SAE 可解释性
收缩自编码器 CAE惩罚编码器对输入的敏感度 在输入附近更平滑的表示理论分析
VAE瓶颈加概率分布约束 连续的潜空间,可以采样生成新数据生成模型(下一章)
掩码自编码器 MAE遮住 75% 的输入,只还原被遮的部分 大规模视觉自监督视觉预训练
M

数学 · 一个瓶颈到底丢了多少

整章只有一件事在起作用:中间那道窄口留了几个数。 留几个,就等于只允许数据在几个方向上展开——没留的那几个方向,全部算进误差。

换一团更规整的点云来看(两个方向的方差分别是 1 + ρ 和 1 − ρ,ρ 由下面的滑块控制):最长的那根轴是第一个方向,和它垂直的那根是第二个; 每个方向上数据散开的程度,就是它的方差 λ(读作「lambda」)。 「瓶颈留 k 个数」的意思是:只记录每个点在前 k 根轴上的位置,剩下那些轴上的散开,全部算成误差。

数学节 · 留几个方向、丢多少误差(左边是几何,右边是账本)

瓶颈留几个方向
—
重建误差 J
—
压缩比(输入 2 个数)
—

互动 · 每个符号管图上的哪一块(卡片下面那张 = 为什么 MSE 的最优解是「平均」)

🎬 自己验一遍

拖「相关系数 ρ」:数据越像一根斜着的长条,两个 λ 差得越远,一个方向就够用了。 把 ρ 拖到 0(数据变成一个圆),λ₁ 和 λ₂ 几乎相等——这时留 1 个方向,误差正好是总误差的一半。

5

代价:自编码器的三个硬伤

硬伤为什么后果
① 潜空间是"散点"不是"地图" 普通自编码器的损失函数只关心"这些点还原得好不好", 完全不关心点与点之间的空间 无法采样生成。随机取一个编码 z,解码出来往往是垃圾——下一章 VAE 的全部工作就是修这个
② 像素损失 ≠ 感知质量 MSE 会让模型倾向于输出"模糊的平均值"—— 当它不确定这一笔该粗一点还是细一点时,输出一个中间值能让 MSE 更小 重建图像总是糊的。这是所有像素级损失的共同问题
③ 会"抄近路" 如果瓶颈不够窄,或者训练太久,模型可能学会把输入几乎原样编码 (比如把每张图的一个像素值直接存进瓶颈) 看起来损失很低,但实际上没学到任何有用的表示

示意 · 潜空间是「散点」不是「地图」

解析解 · 瓶颈一宽到装得下输入,误差就掉到 0

⚠️ 第二条值得展开

用 MSE 训练的重建模型,输出天生偏模糊。原因很数学: MSE 的最优解是条件均值 E[x|z]——已知编码是 z 时,所有可能原图的平均。 当模型不确定时,输出均值是最优策略——而均值看起来就是模糊的。
所以 GAN(生成对抗网络:用另一个网络当裁判挑刺,后面章节会讲)会换掉像素损失,改用对抗损失(让另一个网络来当裁判)或感知损失(比的是人眼看到的结构像不像)。

6

小结

它对应哪条线 ① 表达力 vs 泛化——而这一章只有一个数字说了算:瓶颈有多宽。 窄 + 全线性 → 只能找一条直线(就是 PCA);窄 + 非线性 → 能贴着弯曲的流形走; 宽到装得下输入 → 学会原样照抄,几乎学不到东西(第 5 节硬伤③)。
三档讲的是同一件事:重建误差不是越小越好,「能还原」不等于「学会了」。
一句话 这一章的做法,本质上是在把「你必须理解这张图」写成一条硬约束—— 输出必须等于输入,而中间只准留那么点地方。
「还原」这件事本身没有半点价值(它只是把输入抄回去), 有价值的是它被窄口逼出来的那个取舍。
它牺牲了什么 牺牲了潜空间的「地图性」(回扣暗线 B)。
重建损失只罚「还原得不像」,从来不罚「两个不同的点被塞到了同一处」。 于是学出来的 z 是一堆散落的坐标,不是一张连成片的图—— 随手挑一个 z 解码出来是垃圾,所以它不能生成。 这正是第 5 节硬伤①;下一章 VAE 的全部工作,就是往损失里加一项, 逼这张散点图变成地图。
🎬 自己验一遍:你能指着哪个互动说这句话

回到第 2 节那张画布。先拖那个「投影线角度」滑块,慢慢转一整圈—— 盯住下面跳动的 重建误差 MSE:它会有一个最低点。

然后点一次 「🚀 让网络自己找」,看它用真实的梯度下降一路爬到你刚才亲手摸到的那个角度。

那个「只准转一个角度、误差由你定」的受限状态,就是「表达力」的本体。 线性自编码器的全部自由度,只有这一个角度——所以它顶多等于 PCA,不可能更多。
再切到第 3 节那两幅对比图:同样把 2 维压成 1 维, 右边那条能弯的曲线,就是把「一个角度」换成了「一张曲面」—— 于是左边整片丢掉的波峰波谷,它全保住了。 那个瞬间你看到的就是「瓶颈宽度 + 非线性 = 表达力」。
如果你刚才只是觉得「这条线转起来挺顺手的」,那这一节对你就是没用的 ——回到第 2 节那张画布重新转一次,这次盯着数字。

它在六条暗线里站在哪

这一章在「参数账本」和「它假设了什么」两条上,答案比大多数章都具体。

暗线这一章的回答
A 信息流动 一条「先瘦后胖、回到原地」的流:
784 → 256 → 64 → 16 → 64 → 256 → 784
别的章里形状一路在变形(序列→向量、图→向量), 这一章输入和输出的形状完全一样,只有中间那个 16 是窄口。 所有信息都必须从这 16 个数的洞里挤过去——整章的机制只有这一条。
挤过去一次,就是一个具体的压缩比: 16 × 4 byte = 64 byte, 而一张 28×28 灰度图是 784 byte—— 12 倍。(两种字节别混:图片存的是 0~255 的整数,一个像素 1 字节;模型算的是带小数的数,float32 一个数 4 字节。)
B 什么被牺牲了 用「重建细节」换来了「一套无监督学出来的特征」(无监督 = 训练时不需要人工标签。这套特征好不好,拿编码去做分类或异常检测就能验)。
去掉的信息不是随机的:它优先丢的是高频细节、保留的是低频结构—— 高频指图里变化很快的细节(笔画边缘),低频指变化很慢的大轮廓。 (这正是 MSE 的偏好,也是第 5 节硬伤②「重建图发糊」的同一个原因)。
更要紧的是第二笔代价:它牺牲了潜空间的连续性。 一个普通的自编码器把每个样本编码成一个孤立的点, 点与点之间的空间从来没被训练过——重建损失从头到尾没提过「点与点之间要连起来」。 这一笔代价直接决定了下两章(VAE、扩散)要发明什么。
C 参数账本 就用页首那张 MNIST 沙漏图上的数字算一遍(偏置都算进去):
· 编码器三层加起来 218,448 个参数(每层的算式见下面的公式卡);
· 解码器对称,同样 218,448; · 全模型 ≈ 43.7 万参数 ≈ 1.7 MB(float32,指每个数占 4 字节)。
两个对比数字:ResNet-50 是 2,560 万参数,是它的 58 倍; 而一张 MNIST 图只有 784 个数 ≈ 0.8 KB(像素是 0~255 的整数,每像素 1 字节)。
结论有点难堪:这个模型的参数,比它要还原的一张图多出 500 多倍。 训练集里有几万张图,模型不是只记一张;但参数多、瓶颈又不够窄的时候, 它就有条件把训练样本直接搬过去,而不是学共性——第 5 节硬伤③「抄近路」,在账本上就是这句话。
D 跑在什么上 两个都不是——这一章既不算力受限,也不带宽受限,它只是小得过分。
· 一次前向约 2 × 43.7 万 ≈ 87 万 次乘加, 在消费级显卡上一秒能跑好几万张图,训练停在几十秒量级;
· 最贵的动作是编码器第一层那一发 784×256 矩阵乘, 它一个人就占了全模型参数的 约 46%(200,960 / 436,896); 瓶颈那一层 64×16 小到可以忽略。
所以这章的结论要反过来读:自编码器不是因为快才重要, 而是因为它把「压缩」从一个存储问题变成了一个学习问题。 真正用它的时候(比如把图像压成编码 z,再在这种潜空间里跑扩散), 省下的是下游模型的算力和显存——这笔账记在下一级,不记在这一级, 见 《硬件与算力账本》。
E 它假设了什么 两条假设,第二条几乎没人提。
· 流形假设:它假设「数据虽住在 784 维空间里,真实自由度远小于 784」—— 手写数字的笔迹变化,来来回回就那么些种。 这条假设一旦不成立(数据真的均匀铺满 784 维),自编码器什么也压不出来。 第 3 节那两幅对比图就是它的最直观版本:数据其实躺在一条弯线上(1 维流形), 所以从 2 维压到 1 维几乎不丢东西——但前提是你能沿着那条弯线压。
· 「没学到的东西 = 噪声」假设:用 MSE 时它默认 凡是还原不出来的部分都是随机噪声,所以最优策略是输出条件均值, 于是画面发糊。这条假设也是错的——丢掉的往往是高频细节而非噪声。
F 违背了哪个直觉 「输出等于输入,那它什么也没学。」
这是每个第一次看到自编码器的人都会有的反应,而它是错的: 任务本身确实没有信息量,约束才有——把所有答案抄一遍谁都会, 「只准记住 16 个数」才是那个逼出理解的难处。
第二个反直觉:「压得越狠,学到的东西越多」。 常识是压得越狠信息丢得越多;这里恰恰相反, 瓶颈一旦宽到装得下输入,它就退化成一次复制,学到的东西反而归零。 有损,才是它有效的原因。
第三个更反直觉:「一百多年前的 PCA 就正好是它的最优解。」 线性自编码器无论怎么训,最终都只能落回 PCA 那张老地图上—— 神经网络在这件事上并没有比统计学高明,它只是能加非线性。

上面 A、C 两行里的账,写成正经的数学:

图 · 同一道窄口,两本账(数个数 vs 数字节)

图 · 参数都花在哪

一句话带走进自编码器

自编码器 = 用一个很窄的瓶颈逼网络学会"什么重要"。
线性版本就是 PCA。
它的真正价值不在压缩,而在"无监督地学到好特征"(去噪、稀疏、掩码自编码器都是这个思路)。
但它有一个致命局限:潜空间是一堆散点,不是一张地图,所以不能生成。 ——这正是下一章 VAE 要修的;它接住了上一章《文本生成与采样》的「怎么取出来」。

7

拓展阅读

上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。

📄 这一章的说法从哪来

💻 工业界怎么写

自编码器不是二十年前的玩具——VAE、扩散模型的潜空间、MAE 预训练里都有它。 它在一份真实代码里长什么样?

∑ 更严格的形式