GAN生成器与判别器 本节摘要:Goodfellow 2014 的奇思妙想是——彻底跳过密度。两个网络,一个造假,一个抓假,它俩对抗直到假货与真货无法区分。这套博弈在数学上是一个极小极大(minimax)游戏,它本不该工作,它常常也确实不工作,但当它工作时,产出的样本在窄领域至今仍是文献里最锐利的。本节讲透:为什么 VAE 模糊而 GAN 锐利(损失奖励「合理性」而非像素距离)、非饱和损失为什么是命脉、模式崩塌与梯度消失如何发生、WGAN 与谱归一化如何稳住训练。读完本节,你会明白为什么 2026 年 GAN 不再是开放领域的 SOTA,却仍是 StyleGAN 人脸、对抗蒸馏(SDXL-Turbo)和扩散训练中感知损失的核心。
本节摘要:Goodfellow 2014 的奇思妙想是——彻底跳过密度。两个网络,一个造假,一个抓假,它俩对抗直到假货与真货无法区分。这套博弈在数学上是一个极小极大(minimax)游戏,它本不该工作,它常常也确实不工作,但当它工作时,产出的样本在窄领域至今仍是文献里最锐利的。本节讲透:为什么 VAE 模糊而 GAN 锐利(损失奖励「合理性」而非像素距离)、非饱和损失为什么是命脉、模式崩塌与梯度消失如何发生、WGAN 与谱归一化如何稳住训练。读完本节,你会明白为什么 2026 年 GAN 不再是开放领域的 SOTA,却仍是 StyleGAN 人脸、对抗蒸馏(SDXL-Turbo)和扩散训练中感知损失的核心。
对应原课程:Phase 08 · Lesson 03 ·
gans-generator-discriminator(原英文phases/08-generative-ai/03-gans-generator-discriminator/docs/en.md)。
阅读完本节,你应当能够:
log p(x),用判别器学一个「合理性」损失。-log D(G(z)) 而非原始的 log(1 - D(G(z)))。VAE 的样本之所以模糊,是因为它的 MSE 解码器损失对 L2 是贝叶斯最优的——最优解是一堆合理数字的均值,而均值天生就是一张模糊图。你真正想要的,是一个奖励合理性(plausibility)、而非「逐像素逼近某个目标」的损失。但合理性没有闭式,你只能学它。
Goodfellow 的主意:训一个分类器 D(x) 区分真假,再训一个生成器 G(z) 去骗过 D。G 收到的损失信号,就是「D 当前认为什么像真」——这个信号会随着 G 的进步而移动,永远在追一个移动靶。如果两个网络都收敛,G 就学到了数据分布,而从头到尾没写过 log p(x)。
这就是对抗训练(adversarial training),数学上是一个 minimax 博弈:
min_G max_D E_real[log D(x)] + E_fake[log(1 - D(G(z)))]
💡 在 2026 年,GAN 已不再是 SOTA 生成器(扩散与流匹配夺走了王冠)。但 StyleGAN 2/3 仍是史上最锐利的人脸模型;GAN 的判别器被当作扩散训练中的感知损失;对抗训练驱动着那些让你能实时跑扩散的快速一步蒸馏(SDXL-Turbo、SD3-Turbo、LCM)。
为什么它能工作? 如果 G 完美匹配了 p_data,那么 D 无论如何都做不到比随机猜更好,会到处输出 0.5;G 拿不到任何梯度,游戏停在均衡点。
为什么它会崩? 三种典型崩法:
G 找到某一个 D 无法分辨的模式,然后永远只产这一个样本。D 学得太快、太自信,log D 进入饱和区,G 拿不到有效梯度。| 年份 | 创新 | 修复了什么 |
|---|---|---|
| 2015 | DCGAN | 卷积 / 反卷积、批归一化、LeakyReLU——第一个稳定架构。 |
| 2017 | WGAN、WGAN-GP | 用 Wasserstein 距离 + 梯度惩罚替换 BCE,修复梯度消失。 |
| 2017 | 谱归一化 | 给判别器加 Lipschitz 约束,2026 年仍在用。 |
| 2018 | Progressive GAN | 先训低分辨率再逐层加,第一批百万像素结果。 |
| 2019 | StyleGAN / StyleGAN2 | 映射网络 + 自适应实例归一化,窄领域照片级真实度的标杆。 |
| 2021 | StyleGAN3 | 无混叠、平移等变——2026 年仍是人脸金标准。 |
| 2022 | StyleGAN-XL | 条件化、类别感知、更大规模。 |
| 2024 | R3GAN | 用更强的正则化重新包装,无技巧即可上 1024²。 |
code/main.py 在一维混合双高斯数据上训一个迷你 GAN。生成器与判别器都是单隐层 MLP,我们手写前向、反向与 minimax 循环。目标是让你亲眼看见两大失败模式(模式崩塌 + 梯度消失)在训练过程中如何发生。
原始 Goodfellow 损失 log(1 - D(G(z))) 在 D 把假货高置信度判为假时会趋于 0——此时 G 的梯度基本为零,G 无法改进。非饱和形式 -log D(G(z)) 的渐近行为正相反:D 越自信,损失越爆炸,给 G 一个强信号。
def g_loss(d_fake): # 最大化 log D(G(z)) <=> 最小化 -log D(G(z)) return -sum(math.log(max(p, 1e-8)) for p in d_fake) / len(d_fake)
⚠️
max(p, 1e-8)是为了防止log(0)爆炸;真实训练中应配合更稳的数值技巧(如 WGAN 的损失)。
for step in range(steps): # 训 D real_batch = sample_real(batch_size) fake_batch = [G(z) for z in sample_noise(batch_size)] update_D(real_batch, fake_batch) # 训 G(必须用新生成的假样本,否则梯度是陈旧的) fake_batch = [G(z) for z in sample_noise(batch_size)] update_G(fake_batch)
关键细节:G 那一步必须用新生成的假样本,否则你拿的是上一轮 G 算出来的梯度,训练会偏离。
if step % 200 == 0: samples = [G(z) for z in sample_noise(500)] mode_a = sum(1 for s in samples if s < 0) mode_b = 500 - mode_a if min(mode_a, mode_b) < 50: print(" [!] mode collapse: one mode is starved")
模式崩塌的典型症状:两个真实模式中,有一个停止被生成。而一旦 D 不再把它当假样本看见,它就不再纠正 G——这是一个自我强化的死亡螺旋。
判别器太强:把 D 的学习率砍掉 2~5 倍,或给 D 输入加实例 / 层噪声。一旦 D 准确率 >95%,G 基本就死了。
生成器记住一个模式:给 D 输入加噪声,用 minibatch discrimination 层,或切到 WGAN-GP。
批归一化泄漏统计量:真样本与假样本流过同一层 BN 会混掉各自的统计量。改用实例归一化或谱归一化。
Inception Score 被钻空子:FID 和 IS 在样本量低时噪声很大。评估时至少用 1 万个样本。
「一步采样」对条件任务是谎言:你仍然需要 CFG 无分类器引导比例、截断(truncation)技巧、重采样,才能拿到能用的输出。
💡 WGAN 的核心贡献:把 BCE 换成 Wasserstein 距离(地球移动距离)。BCE 在
D自信时梯度消失;Wasserstein 距离的梯度与D的置信度无关,永远平滑。代价是D必须满足 Lipschitz 约束(用权重裁剪或梯度惩罚 WGAN-GP 来近似)。
| 场景 | 选什么 |
|---|---|
| 照片级人脸、固定姿态 | StyleGAN3(最锐利、最小) |
| 动漫 / 风格化人脸 | StyleGAN-XL 或 Stable Diffusion LoRA |
| 图像到图像翻译 | Pix2Pix / CycleGAN(第 04 节)或 ControlNet(第 08 节) |
| 快速一步文生图 | 扩散的对抗蒸馏(SDXL-Turbo、SD3-Turbo) |
| 扩散训练器内的感知损失 | 在图像裁剪上跑一个小 GAN 判别器 |
| 多模态、开放领域 | 别用——上扩散或流匹配 |
结论:GAN 锐利但窄。一旦你的领域打开——照片、任意文本提示、视频——就切到扩散。对抗这个技巧作为组件(感知损失、蒸馏)活了下来,但不再是独立的生成器。
| 维度 | VAE | GAN | 扩散 |
|---|---|---|---|
| 密度 | 显式(下界) | 隐式(无) | 显式(下界) |
| 采样步数 | 1 步 | 1 步 | 几十步 |
| 样本锐度 | 模糊 | 锐利 | 锐利 |
| 训练稳定 | 稳定 | 不稳定 | 稳定 |
| 似然可评估 | 是 | 否 | 是(下界) |
| 失败模式 | 后验崩塌、模糊 | 模式崩塌、梯度消失 | 慢 |
GAN 与扩散在「锐度」上并列第一,但 GAN 训练不稳、无法算似然——这正是它被扩散取代的两大原因。
本节产出一个调试技能文件(位于原课程 outputs/skill-gan-debugger.md)。
skill-gan-debugger.md:输入一个跑崩的 GAN(损失曲线、样本网格、数据集大小),输出一个排序的「可能原因 + 一句话修复 + 重跑协议」清单。简单。 用默认设置跑 code/main.py。然后把 D_LR = 5 * G_LR 重跑。G 的损失多快塌成一个常数?
中等。 把 Goodfellow BCE 损失换成 WGAN 损失:loss_D = E[D(fake)] − E[D(real)]、loss_G = −E[D(fake)],并把 D 权重裁剪到 [-0.01, 0.01]。训练更稳了吗?比较墙上时钟收敛速度。
困难。 把一维示例扩展到二维(8 个高斯排成环)。追踪 G 在第 1k、5k、10k 步各捕获了几个模式。实现 minibatch discrimination,重新测量。
G 追着一个移动靶,直到假货无法分辨;从头到尾不写 log p(x)。min_G max_D E[log D(x)] + E[log(1 - D(G(z)))];均衡点是 D 处处输出 0.5。G 用 -log D(G(z)) 而非 log(1 - D(G(z))),否则 D 一自信 G 的梯度就消失。G 永远产一个模式)、梯度消失(D 学太快);修复靠降 D 学习率、WGAN-GP、谱归一化、minibatch discrimination。下一节,我们进入桶 3 的进阶——条件 GAN 与 Pix2Pix:把对抗训练从「无条件生成」升级为「图像到图像翻译」,用成对监督 + 对抗损失,实现轮廓转照片、线稿转彩图。