上一章 VAE 要求模型"重建得像",用的是逐像素的 MSE——代价是图发糊。 能不能干脆不逐像素比?GAN 换了个思路: 不要求像,只要求能骗过一个专门挑刺的对手。 这个想法极端简单,也极端难训练——这一章你会亲手把它训崩。
零和博弈:一方赢多少,另一方就输多少,两者加起来正好是零。这个游戏里有两个角色:
| 角色 | 输入 | 输出 | 它的目标 |
|---|---|---|---|
| 生成器 G 🎨 | 随机噪声 z ∼ N(0, I) I:每一维都独立地抽平均 0、方差 1 的钟形分布 |
一个假样本 | 让判别器以为这是真的 |
| 判别器 D 🔍 | 一个样本(真的或假的) | 它是真的概率 | 把真样本判真、假样本判假 |
一张图看清两个网络怎么接
把这条式子念出来:第一项是真样本被 D 判成「真的」的把握(取 log); 第二项是假样本被 D 判成「假的」的把握。 D 想让两项都大——它要分得清;G 只能动第二项,想让这一项小——它要骗过去。 式子里的 E 是「对很多样本取平均」,log 让「对该判对的样本几乎没把握」受到重罚(把握接近 0 时 log 趋向负无穷)。
造假币的和验钞员。造假者不断改进技术,验钞员不断学习识别。
关键是:造假者不需要知道"真钞长什么样"的任何先验知识——
他只需要知道"验钞员会不会收下这张"。
当验钞员再也分不出来时,造假者的技术就已经达到了以假乱真的水平。
VAE 用的是像素级损失(MSE),它直接告诉你"你错了多少"——
但也正因如此,它的最优解是"模糊的平均值"。
GAN 用的是对抗损失,它只说"像不像",不说"差多少"。
这个反馈信号弱得多、也更不稳定,但它不惩罚"具体某个像素差了 0.1"——
所以 GAN 能生成锐利的图像。这是它能赢在感知质量上的根本原因。
上一节说「G 只要骗过 D,就等于学会了真实分布」。这句话对不对,下面这张图能当场算给你看—— 图上的数字都是现算的。
先记住一件事:这场博弈里最强的判别器不是一个黑盒,而是一个能写出来的公式。 它只干一件事:在两个分布不一样的地方隆起,在一样的地方压平。
式里的 pdata 是真实分布、pG 是生成器造出的分布。 人话:某个位置上真样本占多大比例,D 就输出多大的数——两边一样多时正好是 0.5, 粉线就被压成一条水平线。
博弈要分两步看:先把 D 调到最强——它会把 V 顶到它能顶的最高处; 再看 G 能把这个最大值压到多低。V 越低,说明 G 越接近真实分布;G 学会时压到 −1.386。
互动 · 拖「生成器」,看判别器和博弈值怎么跟着变(全部当场算)
橙线是原画,绿线是临摹,粉线是老师指错的地方。
老师不是随便指的——他只在你和原画不一样的地方指。两幅画处处一样时,
他无处可指,粉线就被压成一条水平线。
所以「老师指不出错」和「临摹得一模一样」是同一件事——这就是 GAN 的全部赌注。
把「生成分布的位置」拖到 0、「宽度」拖到 1.0——两条曲线就完全重合了。
这时粉线是一条平的 0.5,博弈值停在 −1.386。这个数不是编的:
D 恒等于 0.5 时,log(0.5) = −0.693,两项加起来正好是 −2 × 0.693 = −log 4。
它也不是一个孤零零的数字:把上面 D* 的式子放进 V 里替换掉 D,化简后就得到 V = −log 4 + 2 × JS(真实 ‖ 生成)。
JS 散度量的是两个分布差多远——分开一点,V 就高一点。所以「G 压低 V」就是「G 缩小两个分布的距离」。
记住这个读数——它就是「这场游戏玩完了」。只要 V 还明显高于它,G 就还没学会。
下面是一个1 维 GAN:真实数据是两个高斯分布的混合。 生成器是 8→16→16→1、判别器是 1→16→16→1 的 MLP,训练在浏览器里进行。
训练一步的顺序是:先用一批真样本和一批假样本把 D 更新 2 次,再更新 G 一次。
一张图看清训练一步:D 学两小步,G 学一小步
互动 · 实时训练:看绿线一步步靠向橙线
把鼠标移到下面那两个主角 G 或 D 上——上面两个学习率滑块会分别亮起来。
它们对着同一个目标反向优化,而上面那两个学习率,就是它们较劲的速度。
互动 · 两个只差一个减号的损失,为什么一个是坑、一个是解药
两种损失只差一步:饱和版让 G 最小化 log(1 − D(G(z))),
非饱和版让 G 最大化 log D(G(z))(也就是最小化 −log D(G(z)))。
看两条曲线能不能叠在一起。理想状态是生成分布和真实分布完全重合。
但要小心一种假成功:生成器只学会了其中一个峰——
上面那条曲线看起来也"挺高",但它只是复读了一半的答案。这就是模式坍塌。
还有一点:真实项目里看不见 p_data,只有一个样本池,所以第 M 节那个 −1.386 算不出来。 训练时你能盯的只有两个损失和生成出来的样本本身。
互动 · 亲手复现三个失败模式
| 失败模式 | 现象 | 为什么 |
|---|---|---|
| 模式坍塌 Mode Collapse |
生成器只会造其中一种样本。真实数据有 10 个类别,它只生成 3 个 | 生成器发现"骗过判别器"的最省力方法不是学会所有模式, 而是找到判别器的一个弱点反复利用 |
| 判别器太强 | 训练初期 D 轻松分辨真假,G 的梯度趋近于 0,完全学不动 | 当 D 输出饱和(输出贴到 0 或 1,sigmoid 在两头几乎是平的)时,它几乎不再变化,回传给 G 的梯度趋近于 0。 数学上 G 失去了学习信号 |
| 震荡不收敛 | 分布来回摇摆,永远不重合 | GAN 的优化目标有均衡点,但它是鞍点、不是极小值—— G 和 D 同时用梯度下降会绕着它打转,永远停不下来。 (纳什均衡:双方都没有单方面改策略的动力。理论上 p_G = p_data、D = 1/2 就是它, 博弈值 −log 4;难的是怎么走到那儿) |
互动 · 为什么第三个失败模式会「绕着走」:同步梯度下降在一个鞍点上做圆周运动
模式坍塌不是 bug,而是 GAN 目标函数自带的漏洞。
G 的任务是"最大化骗过 D 的概率",不是"覆盖真实分布的所有模式"。
如果只造一个特别逼真的样本就能骗过 D,那从 G 的角度看这就是最优解。
所以修它常常要动目标函数,而不是只调参。这就是 WGAN、WGAN-GP、
Unrolled GAN 等一系列工作的动机(Unrolled GAN 的做法是:G 更新时先「预演」D 接下来几步会怎么反应,再决定怎么走)。
| 方法 | 改了什么 | 解决了什么 |
|---|---|---|
| DCGAN 2015 | 规定了一套卷积 GAN 的结构规范(不用池化、用步长卷积、批归一化) | 第一次让 GAN 稳定生成像样的图像 |
| WGAN 2017 | 换掉 JS 散度,改用推土机距离(Wasserstein:把一堆土搬成另一堆要花的最少力气,两个分布不重叠时也照样有梯度); 为此 D 最后不再过 sigmoid,直接输出一个分数(不再是 0~1 的概率) | 解决了 |
| WGAN-GP 2017 | 用梯度惩罚(额外罚「D 变化太陡」)替代 WGAN 里的权重裁剪 (把 D 的权重硬砍到一个范围内) | 更稳、不需要精调裁剪范围。成为很长一段时间里的常用配方之一 |
| Spectral Norm 2018 | 给 D 的每一层做谱归一化,限制 Lipschitz 常数(D 的输出对输入最多能变化多快) | 2018 年前后被广泛采用 |
| StyleGAN 2018-20 | 用风格调制的方式控制生成,逐层注入不同尺度的细节 | 人脸生成的巅峰,而且潜空间高度解耦(不同的控制维度各管各的事,互不串扰)—— 姿态、身份这类大属性,和雀斑、发丝这类随机细节,各归各的层管 |
| BigGAN 2018 | 巨大批量 + 类别条件 + 截断技巧(采样时把噪声往分布中心收一收,牺牲多样性换质量) | ImageNet 128×128 上的质量巅峰 |
互动 · JS 散度 vs 推土机距离:为什么前者治不了「
WGAN 那句"损失值第一次有了意义"。
原来的 GAN 损失是 JS 散度,它只在两个分布有重叠时才有有效梯度。
如果两个分布完全不重叠(训练初期很常见),梯度就基本是 0——学不动。
Wasserstein 距离在任何情况下都提供有意义的梯度,
而且它的值直接反映"两个分布差多远"。这是理论上的一次真正的修复。
这也是 WGAN 要去掉 D 最后那个 sigmoid 的原因:D 改输出一个没有上下界的分数——推土机距离要的就是这种分数。
| 领域 | 状态 |
|---|---|
| 图像生成 | 基本被扩散模型取代。质量和多样性都输了,而且扩散模型训练稳定得多 |
| 实时生成 / 边缘设备 | 一次前向确实最便宜,但这个场景已经不只属于 GAN: 扩散做蒸馏后(让小模型模仿大模型、把几十步压到几步;LCM-LoRA 4 步、SDXL-Turbo 1~4 步)也能进实时, 只是多一道蒸馏成本。30fps 场景(视频滤镜、游戏)GAN 仍更划算 |
| 图像超分 / 修复 | ESRGAN、GFPGAN 仍在广泛使用 |
| 对抗样本 | 在输入上加一点人眼看不出的扰动就能骗过模型,拿这类样本去训练就叫对抗训练—— 这个思想成了 AI 安全的基础,见阶段 7 |
| 对抗损失 | 判别器被拆出来当损失函数用。今天很多扩散模型和 超分模型的训练里都有"对抗损失"这一项 |
| 你手上的情况 | GAN 会怎么痛 | → 换成什么 |
|---|---|---|
| 要最好的质量、要多样性,能接受多步推理 | 质量和多样性都输给扩散,还要额外对付训练不稳定 | 扩散模型(今天的默认答案) |
| 要一个能读的训练信号,好判断训到哪了 | D 的损失和生成质量反相关,没有可靠的早停/选模信号(见小结暗线表) | 扩散的回归损失(每一步只是回归「加了多少噪声」,和普通回归一样稳);或至少换 WGAN 系,让损失有物理意义 |
| 数据里模式很多、一个都不能漏 | 漏掉一半模式也未必被扣分,得专门加多样性约束 | 扩散;或在 GAN 里加多样性正则 / 多判别器 |
一张图看清「学出来的损失函数」传给了谁
它最重要的贡献不是"能生成图片",而是提出了一个通用范式:
用一个学出来的网络当损失函数。
在 GAN 之前,
可以这么看:扩散模型用的"分数匹配"(学一个网络指出「往哪个方向去噪」)、
RLHF 用的"奖励模型"(学一个网络给回答打分),
都是"用一个网络来提供好不好"这个想法的近亲。
这一章只有一件事:把「什么叫好」也交给一个网络去学。它是七条线里第 ② 条最赤裸的一次露头(七条线讲这门课为什么能行;下面的暗线表是拆方法时该问的六个问题,两套不是一回事)。
回到第 3 节那张失败模式图。把按钮切到「判别器太强」—— 你会看到判别器的输出变成一条几乎垂直的阶跃线,而判别器输出的斜率(虚线)只在很窄的一条带里不为零,其余地方贴着 0。
那个瞬间你看到的就是「换个损失函数 = 换掉一套归纳偏置,而新偏置有个洞」: 判别器越准,它给出的「像不像」信号越锐利;可它一旦太准,梯度就没了。
一张图收尾 · 三种训崩,各有一条回路
| 暗线 | 这一章的回答 |
|---|---|
| A 信息流动 | 两条流在判别器里汇合:噪声 z(8 维)→ G 的 8→16→16→1 → 一个标量样本(就一个数);真实样本也进 D 的 1→16→16→1,两条流在出口汇成一个概率。和分类器不同:这里的「标签」是 D 现算的,不是数据自带的 |
| B 什么被牺牲了 | 换来锐利、不需要人设计像素损失;牺牲显式误差信号(再也没有「你差了 0.1」)、训练稳定性和模式覆盖率——损失值低不等于学得好 |
| C 参数账本 | 本章 G 约 0.4 K、D 约 0.3 K;真实规模 DCGAN 约 3.5 M / 2.8 M、StyleGAN2 的 G 约 30 M。训练一步要跑两个网络的前向 + 反向,约 6 次前向的算力——同样参数量的分类器只要 3 次 |
| D 跑在什么上 | 训练算力受限、推理延迟受限:训练每步多一整套前向 + 反向(两个网络),推理只有一次 G 的前向。稳定化技巧多半在控制「D 训得多强」。《硬件与算力账本》 |
| E 它假设了什么 | 假设「D 分不出真假」等价于「G 学会了真实分布」;这要求判别器足够强,而且两个分布有重叠的支撑集(概率真正不为零的那片地方)。真实数据在高维空间里几乎躺在一张低维流形(一张弯弯曲曲的薄片)上,两个分布常常不重叠,理论保证就断在第 4 节那张 JS 图上——这正是 WGAN 的出发点 |
| F 违背了哪个直觉 | 损失下降不等于学得更好:D 越强,G 越学不动,两者的好坏反着走——所以要盯着两边的损失一起看,任何一边太好都是坏消息 |
它接住了上一章的什么:《VAE 变分自编码器》用像素 MSE—— 它保证了「有明确的误差」,代价是模糊。这一章接着问:那不要显式误差呢?
它给下一章留了什么:《扩散模型 DDPM》接手了图像生成; 更后面的《RLHF 与 PPO》接走了「给回答打分」。
GAN = 造假者(G)和鉴定师(D)的零和博弈。G 只被要求"骗过 D",
不需要任何关于真实分布的显式知识。
好处:不用像素损失,所以图像锐利。
坏处:训练极不稳定——模式坍塌(只会造一种)、D 太强导致 G
WGAN 换掉 JS 散度是最重要的修正之一,它让损失值第一次有了意义。
今天图像生成已经被《扩散模型 DDPM》接管;而「让网络自己判好坏」这一步,已经被后面很多方法接着走了下去。
上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。
上面讲的博弈听上去玄,但真做起来,一个 GAN 的训练循环就十几行。 下面是一段能跑的 PyTorch,你刚才拖的那个「生成器 / 判别器学习率」,就是这里的两行优化器。