一维 GAN 跑 1200 轮,真假两边的判别概率都收到 0.50 附近,看起来大获成功。 但生成器的 a 从 0.102 掉到 0.007——假样本的标准差只剩 0.007,真实是 1.0。 判别器认不出来,不等于生成器学会了。
前面三种网络都是「给输入、要输出」。GAN 不一样:它有两个网络,目标相反,靠互相对抗来进步。
一、两个网络,目标相反
生成器:吃一个随机噪声,吐一个假样本。它的目标是让判别器判成「真」。 判别器:吃一个样本,输出它是真的概率。它的目标是真样本判高、假样本判低。 训练时交替走:先拿一批真样本和一批假样本训判别器,再固定判别器训生成器。 整场博弈的平衡点是「生成器造出来的东西和真实分布一模一样,判别器只能输出 0.5」。 注意生成器的梯度来自判别器,它压根不知道真实分布长什么样。它只知道自己被识破了没有。 这一点是理解 GAN 所有毛病的关键。
二、表里那个「成功」是假的
Demo 造的是最简单的情形:真实分布是均值 4、标准差 1 的一维正态,生成器是 a 乘 z 加 b, 判别器是 sigmoid(w 乘 x 加 b)。 跑 1200 轮后,「真样本被判真概率」是 0.502,「假样本被判真概率」是 0.501。 两个都是 0.5——平衡点到了。按教科书的说法,这时候训练成功。 但看生成器那两个参数:a 从 0.102 一路掉到 0.007,b 从 0.012 爬到 4.029。 a 是标准差。真实分布标准差是 1,生成器只给出 0.007。 也就是说它没有学会「造一个均值 4、有宽度的分布」,它把全部假样本堆在了 4.03 这一个小点上。
三、它钻了什么空子
判别器只看单个样本,而且是个线性函数。 一个点 x 等于 4.03 送进去,它的判断和「一堆点都在 4 附近」是差不多的。 生成器发现了这一点:把方差压到 0,判别器就分不出来了。 这是它这份「作业」的最优解,但不是我们要的答案。 这个现象叫模式坍缩:生成器只覆盖真实分布的一小块,甚至一个点。 真实图像上表现为「不管输入什么噪声,都生成同一张脸」。 所以判别器的 loss 收敛到 0.5,完全不能作为「训练成功」的证据。 这是 GAN 训练里最容易踩的坑,也是它出了名难调的原因。
四、真实场景怎么绕开
- 加强判别器。多尺度判别、一块一块判(PatchGAN),让它能看出纹理和分布。
- 换损失函数。Wasserstein GAN 用距离代替概率比,梯度不会在判别器太强时消失。
- 看样本不看 loss。定期存一批生成结果人工看,别只盯曲线。
- 控制两边能力。判别器太强,生成器梯度消失;生成器太强,判别器学不动。 这个平衡得靠调两边的更新频率。 这四条都是工程手段,没有一条是理论保证。 GAN 到今天仍然是「能跑出好结果,但说不清什么时候能跑出来」的状态。
五、动手 Demo:一维 GAN:判别器认不出了,生成器却没学会
真实分布取均值 4、标准差 1 的一维正态; 生成器是 a 乘 z 加 b(两个参数),判别器是 sigmoid(w 乘 x 加 b); 交替训练 1200 轮,每轮判别器走 3 步、生成器走 1 步,记录两边的参数和被判真的概率。
gw = float(((pr - 1) * xr).mean() + (pf * xf).mean()) # 判别器梯度
gb = float((pr - 1).mean() + pf.mean())
wd -= lr * gw
bd -= lr * gb
ga = float((2 * (pf - 1) * wd * z).mean()) # 生成器梯度
a -= 0.02 * ga
实跑输出:
--------------------------------------------------------------------
生成对抗网络:一维版本,看两个网络怎么互相逼
--------------------------------------------------------------------
真实分布 均值 4.0、标准差 1.0
生成器 g(z) = a × z + b,z 是标准正态
判别器 sigmoid(w × x + b)
初始生成器 a = 0.10,b = 0.00
轮次 生成器 a 生成器 b 真样本被判真概率 假样本被判真概率
---------------------------------------------------------------
1 0.102 0.012 0.892 0.488
100 0.011 2.665 0.675 0.395
300 0.011 5.404 0.401 0.449
600 0.018 3.532 0.526 0.544
900 0.010 4.145 0.479 0.488
1200 0.007 4.029 0.502 0.501
训练后生成分布 均值 4.029、标准差 0.007
真实分布 均值 4.0、标准差 1.0
表里最后三行「真假两列都落在 0.5 附近」,说明判别器已经分不出真假了——这是理论上的平衡点。
但真正要注意的是生成器那两列:a 一路掉到 0.007,也就是假样本的标准差只剩 0.007,而真实标准差是 1.0。
换句话说,它没有学会「造出一个均值 4、有宽度的分布」,而是把全部假样本堆在均值 4.03 这一个小点上。判别器只用一个线性函数看单个样本,确实分不出「一堆点全在 4」和「大部分点在 4 附近」——生成器就钻了这个空子。
这就是 GAN 最典型的失败模式,叫【模式坍缩】:判别器认不出来,不代表生成器学会了。
真实图像上同理,只是表现成「不管输入什么噪声,都生成同一张脸」。治它的办法是改判别器(多尺度、PatchGAN)或者换损失(Wasserstein),不是调学习率。
前 300 轮 b 从 0.012 爬到 5.404,冲过头了(真实均值是 4),之后回落到 4.029。 这一段来回摆是 GAN 训练的典型样子。 a 从 0.102 单调掉到 0.007,一路没回头。 最终生成分布的均值 4.029(差不多对),标准差 0.007(差了两个数量级)。 真假概率最后都落在 0.50 附近,看起来完美。如果只看这两列,会以为训练成功——这正是这一节要讲的陷阱。 我用的判别器是线性的,所以它天生看不出方差差别。换成一个能看二阶矩的判别器,结果会不一样。 但那时候问题会变成「判别器太强、生成器梯度消失」,难调的本质没变。
30 秒实验
- 把判别器每次更新改成 1 步,看模式坍缩来得更快还是更慢。
- 把生成器的初值 a 从 0.1 改成 0.8,看标准差还会不会掉到 0。
- 想一想:为什么判别器的 loss 收敛到 0.5 不能证明生成器学会了?
常见误区
- 「判别器分不出来就说明生成得好」——模式坍缩时它一样分不出来。
- 「生成器在逼近真实分布」——它只知道有没有被骗过,不知道真实分布长什么样。
- 「调小学习率就能稳」——GAN 的不稳来自博弈结构,不是学习率。
自测三问(点开对答案)
1. GAN 里两个网络各自的目标是什么? —— 生成器想让自己造的样本被判成真,判别器想把真假分开。 2. 什么是模式坍缩? —— 生成器只覆盖真实分布的一小块甚至一个点,判别器照样分辨不出。 3. 为什么判别器的 loss 不能当训练成功的指标? —— 坍缩的生成器也能让判别器输出 0.5,指标好看但结果没用。GAN 让网络学会了「造」。 下一节回到结构本身:同样是处理序列,如果顺序不是一条链而是一棵树,网络该怎么搭。
- ☐ (本节完,下一节 8.2.5 递归神经网络:对序列数据进行有效处理)