0%

GAN学习笔记

到目前为止,我们学习的神经网络,都是输入一个x,经过一个复杂的函数,输出y。x可以是sequence,可以是图片,可以是数据;y可以是类别,可以是sequence。

接下来,我们要把神经网络作为一个generator来使用。network会同时看x和z得到输出。z是simple distribution。回忆CS229机器学习课程,想到可以使用正态分布、均匀分布等,我们自己决定。

z是不固定的。每次使用,随机生成一个z。因此,我们的神经网络的输出,就变成了一个复杂的分布,如图:

1

我们的输出变成了distribution。这种神经网络被称为generator,实现同样的输入可能有不同的输出。我们现在来学习GAN(生成对抗网络)。

先以unconditional generation为例。先把x拿掉。generator现在的输入就是z。假设z是normal distribution,维度自定。simple出一个z之后,给generator,生成结果。

2

现在介绍discriminator。它用来判断输入的图片是真的还是AI生成的。

3

discriminator与generator的结构可以由我们自己设计,可以用CNN、transformer等模型。

所以,GAN的过程是这样的,discriminator与generator“协同进化”:

4

现在来看算法概览。下文用G指generator,D指discriminator。


算法概览

在开始之前,初始化G和D的参数。

第一步,固定G的参数,train并update属于D的参数。我们也有一个database,里面有正常的数据,sample一些出来,与G生成的数据,一起去训练D。D会因此学习到什么样的数据是真的数据,什么样的数据是G生成的数据。

从图中可以看出,“二次元图片生成”任务,对于D来说,就是一个简单的regression:二次元标1,非二次元标0。

5

第二步,固定D的参数,train并update属于G的参数。我们的目的是,让G学会“糊弄”D。具体实现是这样的:G随便吃一个distribution的向量,产生数据,数据给D,D给图片打分,G的目标是让D的打分越高越好。

通俗一点,可以看作把G和D接起来,看作一个large network。不过,这个network有的参数不能改,有的可以改。让这个large network的输出(打分)越大越好,如图,

6

这个训练过程与之前训练神经网络没有不同。

随后,两个步骤反复执行。

7

来看看progressive GAN的结果。改变对G输入的向量,能得到一系列过度人物脸部图片,

8


理论介绍与WGAN

回顾一下我们的目标,

9

现在需要明确,我们究竟要maximize或minimize什么。
$$
G^* = \arg\min_G \text{Div}(P_G, P_{\text{data}})
$$
Div是Divergence(散度) 的缩写,用来衡量两个概率分布差异的函数。

如何计算Divergence?先从 $ P_G 和 P_{\text{data}} $ 中sample出一些数据,

10

接下来,就是

11

现在来详细解释这个图。

图的左上角表示D的训练目标就是看到 $ P_{\text{data}} $ 给较高的分数,反之给较低的分数。 这个训练目标用数学表示就是图的左下角。E指的是期望,求平均。

综上,我们的总体目的是:
$$
\min_G \max_D V(D, G) = \mathbb{E}{x \sim P{\text{data}}} [\log D(x)] + \mathbb{E}_{z \sim P_z} [\log(1 - D(G(z)))]
$$
先不证明。

这个原始损失函数就是基于JS divergence(Jensen-Shannon divergence(詹森-香农散度))的,公式:

设有两个概率分布 P 和 Q,则:
$$
\text{JS}(P \parallel Q) = \frac{1}{2} \text{KL}(P \parallel M) + \frac{1}{2} \text{KL}(Q \parallel M)
$$
其中:

  • $ M = \frac{1}{2}(P + Q) $ :是 P 和 Q 的平均分布。
  • $ \text{KL}(P \parallel M) $ :是 P 相对于 M 的 KL散度。

对于两个概率分布 P(x) 和 Q(x),KL 散度的定义是:
$$
D_{\mathrm{KL}}(P \parallel Q) = \sum_x P(x) \log \frac{P(x)}{Q(x)}
$$
(或在连续分布中用积分替代求和)

  • P(x):真实的分布(观测到的)
  • Q(x):你用来近似的分布(模型预测的)
  • 如果 P = Q,那么 KL 散度为 0,表示“完美近似”
  • KL 散度越大,表示 Q 假装成 P 的能力越差

但是,詹森-香农散度其实并不那么适合,并且不管用不用它,GAN模型都非常难训练。现在要讨论一些GAN的训练技巧:

首先,我们要了解,$P_{data}$和$P_G$都是在高维空间中,低维的流形(Manifold(流形)是一个在局部看起来像欧几里得空间的空间)。这很好理解——高维空间中,肯定只有一个非常小的范围的向量代表的图像是“二次元人物”等等。我们对高维空间进行随机取样,肯定很难取到能生成图片的向量。目标分布与生成分布之间的重叠在某些情况下可以忽略(overlap can be ignored)。

12

同时,我们根本不知道$P_{data}$和$P_G$的分布 ,只能靠sampling来管中窥豹。这更让它们难以重叠。

13

综上,$P_{data}$和$P_G$ 被看为几乎没有重叠 。但是,如果两个distribution不重叠,那么JS divergence的结果永远是log2。所以,不采用JS divergence。这样的loss函数太差。

14

如果采用binary classifier,会达到100%的准确率,这也不行。可以理解为机器死记硬背下了结果。你根本不知道训练时,参数有没有优化得更好。

15

所以,我们要探寻新的方法去量化loss。我们现在来看看WGAN(Wasserstein GAN)。

Wasserstein distance的想法是这样的:想象你在开一台推土机,把分布P想象成一堆土,把分布Q想象成土堆放的目的地。

16

把P“挪到”Q的平均移动距离,就是Wasserstein distance。也就是
$$
W(P, Q) = \inf_{\gamma \in \Pi(P, Q)} \mathbb{E}_{(x, y) \sim \gamma} [d(x, y)]
$$
数学公式较为抽象。我们来看看具体的细节,首先,把“土堆”变化的可能方法有无限种,用不同的方法,算出来的距离也不同,

18

为了让Wasserstein distance有确定的值,我们的方法是:穷举所有的“moving plan”,看哪一个距离最小,作为Wasserstein distance。

看起来Wasserstein distance的计算很麻烦。我们先不讲具体方法,来看看假设计算成功了,有什么样的优势。

与JS distance对比一下,可以看到,

19

优势很明显。

WGAN用它来定义损失函数,现在,只需要maximize这个函数即可,
$$
L_{\text{WGAN}} = \mathbb{E}{x \sim p{\text{data}}} [D(x)] - \mathbb{E}{x \sim p{\text{gen}}} [D(G(z))]
$$
同时,这里有一个限制:D不能是一个随便的function,必须是一个1-Lipschitz的function,什么是 Lipschitz 函数?一个函数$ f: \mathbb{R}^n \to \mathbb{R} $被称为 K-Lipschitz,如果对任意两个输入 x 和 y,都有:
$$
|f(x) - f(y)| \leq K \cdot |x - y|
$$
其中:

  • K 是一个常数,叫做 Lipschitz 常数
  • ∥x−y∥ 是输入的距离(通常是欧几里得距离)。

如果 K = 1,这个函数就叫做 1-Lipschitz 函数

换句话说:

一个 1-Lipschitz 函数不能改变太快。两个输入点越靠近,它们的输出值也必须越接近,增长的“斜率”不能超过 1。

为什么要这样?如果不做任何限制,那么D(x)在定义的函数种,前一部分越大越好,后一部分越小越好,在generated与real真的没有重叠的情况下,会给real无限大的正值,给generated负无穷。因此,这个training根本无法收敛。加上这个限制以后,这个函数的max才会是Wasserstein distance。

为什么加上这个限制就可以解决刚才的问题?因为它要求discriminator不可以变化剧烈。既然要够平滑,那么就没法generated特别大而real特别小。

20

现在讨论如何做到1-Lipschitz的限制。

最原始的平滑方法是:

21

确实是很朴素的思路。之后人们又想出了新的方法:gradient penalty,具体思路是这样的:在生成的data取一个sample,在real的data取一个sample,两点连线之间再取一个sample,让这个点的gradient接近一。

22

此外,还有很多别的方法。效果比较好的是spectral normalization。

我们现在解决了loss的问题。但是,GAN依然非常难以训练。问题本质在哪里?

G、D是互相成长的。假设某一次D没有train好,没办法分辨生成图片与真实图片的区别,那么G就没有“可以进步的目标”,也就是train会相应“跟着停下来”。有时需要调整Hyperparameter才能继续train。

这就要求G、D在训练时要“棋逢对手”,任何一方不能“放弃比赛”。

GAN for sequence generation来生成文字是很困难的,来看看这个例子。我们现在将一个seq2seq的model decoder作为G。难点在于,如果通过梯度下降训练G,想办法让它的数据在D的输出得分最高,是做不到的。假设现在改变了decoder的参数(小小的变化),那么它输出的distribution也会有小小的变化。由于这个变化很小很小,不会影响到分数最大(max)的token是什么。最后,就会导致D对这个数据给出的分数不变。

23

等一下!有一个地方值得思考!既然在这个问题中,对token的选择有max,因此导致gradient失效(具体是什么失效?是没办法对一个“选择”操作求导数,因此梯度就断了 ),那么在CNN中不也常用max pooling池化吗?为什么那里没有出现梯度消失的问题?

CNN中的 max pooling虽然选了最大值,但它是对连续值操作的,而且它在反向传播中是有定义梯度的

具体来说:

  • 假设一个pooling window是 [0.2, 0.8, 0.5],最大值是0.8。
  • 反向传播时,梯度只传给了“最大值那个位置”,也就是0.8的那个神经元,CNN 中的 max pooling 是在 前向传播中选出最大值的位置,然后在 反向传播时把梯度传给最大值所在的位置,别的都设为 0。
  • 所以虽然不是“所有地方都传梯度”,但梯度还是有的,是可导的

这跟token选择的argmax不同:

  • max pooling是作用在连续空间上,有梯度定义
  • token选择是离散空间的argmax或sampling,是不可导的操作

这不是典型的“梯度消失”(gradient vanishing),而是 梯度不存在(non-differentiability of sampling)

总结如下:

操作 是否离散 是否可导 是否能传播梯度 为什么
Argmax / Sampling(用于序列生成) ✅ 是 ❌ 否 ❌ 不行 离散选择,不可导
Max Pooling(用于CNN) ✅ 是 ✅ 是(sub-gradient) ✅ 可以 只把梯度传给最大值的位置
Softmax + Sampling ✅ 是 ❌ 否 ❌ 不行 采样阶段仍然是离散的
Softmax(本身) ❌ 否 ✅ 是 ✅ 可以 是连续可导的函数

对于max pooling与Argmax / Sampling的对比,我感觉这个例子可以加深理解:

假设你有这样一个输入:

1
输入: [2, 5, 1, 3]

你做一个 size=2 的 max pooling(步长=2),你就会变成:

1
2
3
max(2,5) = 5  
max(1,3) = 3
输出: [5, 3]

直接扔了别的值,拿最大的走了。虽然在前向传播里只保留了最大值,但记住了它来自谁。反向传播的时候,比如 loss 对输出的梯度是:

1
dL/d[5,3] = [a, b]

只把这个梯度 a 传给“5 的来源”(也就是 5 本人),b 传给 “3 的来源”(3 本人),其他的输入值全都不给梯度,设为 0。

最终回传的梯度是这样:

1
2
输入: [2, 5, 1, 3]
梯度: [0, a, 0, b]

数学上,max(x₁, x₂, …, xₙ) 的导数是一个 subgradient

  • 它只对最大值的位置为 1,其它为 0
  • 如果有多个最大值并列,那它会在它们之间分配梯度(可以随机,也可以平均)

为什么不能对 argmax 用这个办法?因为 argmax 是更“粗暴”的操作:

  • Max pooling:对一个小窗口选最大,记录来源,还能传回去。
  • Argmax:输出整个向量的 index(比如 “第 7 个词”),不保留值,只保留位置,梯度根本没法定义在哪个维度传回去

我突然有个有点幼稚的问题,毕竟我是初学者,

为什么 GAN 生成序列的时候不能用 max pooling避免“梯度不存在”?非得用 argmax?

我经过思考,发现,如果要对序列的某一步的softmax结果进行 max pooling,就必须让步长等于词表的长度。否则,就会出现这样的情况:

max pooling 取每两个值 pool 一下,变成:

1
2
3
max([0.0002, 0.001]) = 0.001
max([0.5, 0.0001]) = 0.5
...

这确实得到了一个更短的向量,改变了词的概率分布,但没法再知道每个新位置对应哪个词了,因为池化把词位置信息搞没了。

现在来让max pooling的步长等于词列表的长度 (即对整个softmax输出做pooling),那么实际上就是选出概率最大值。这时候就会变成选择softmax输出中的最大概率,就是在离散空间中进行argmax操作。那显然在这种情况下,梯度传播也会遇到不存在的挑战,因为最大值的选择是离散的,这仍然是一个不可导的操作。当最大值的位置选定后,其它位置的梯度就会是0。因此,模型也只能根据最大概率的位置更新参数,而无法根据其它位置的概率来调整模型。梯度失效。

现在我们回到刚才的问题。GAN遇到了不能用gradient descent train的问题,可以当作 reinforcement learning 的问题,硬做一下。

对于GAN结果的评估,有很多方法。例如,用image classifier,结果的几率分布越集中,就说明效果更好。但是,这种方法会被 mode collapse的问题“骗过去”。如图,蓝色星星是真正的数据分布,红色的是GAN生成的data。可以注意到,GAN没有完整地学习到数据的分布,但是评估效果表面上会很好。在“二次元人物生成”任务中,GAN生成了很多同一张二次元人物的面孔,这张面孔反复出现,本质就是GAN没有学好分布,

24

如何避免?目前还在研究。例如,BGAN会把train point一路上记下来,在mode collapse之前,把train停下来。

现在来看一个更难被侦测的问题:mode dropping。这个问题是,真实的数据分布大于生成的数据。不像mode collapse那样“反复生成同一张图”,看起来样本之间不完全重复,但遗漏了部分真实分布的支持区域

维度 Mode Collapse Mode Dropping
概念 生成器输出收敛到少数几个模式 生成器不覆盖全部真实模式
样本多样性 极低(甚至完全相同) 一定多样性,但丢了一部分
体现 所有输出几乎一样 输出不包含某些“类型”的数据
原因 生成器收敛到骗过判别器的“捷径” GAN训练中未能探索完整数据空间
是否输出单一模式 ✅ 是 ❌ 不一定
真实分布 vs 生成分布 生成分布过窄 生成分布偏移或缺少区域

25

怎么做去看diversity够不够呢?过去,常常用image classify,把所有的distribution平均起来,如果平均的distribution非常集中,就代表多样性不够。

26

还有一些方法,先不赘述。

现在来看看conditional GAN。这个可以完成text to image、pix2pix 的任务。

27

28

还有有趣的sound to image任务,其中输入的声音从左到右变大,

29

现在来看看GAN的一个应用:之前都是supervised的情况下,现在来看看GAN在不成对、不标记的data下的训练。什么样的任务是unpaired data呢?像image style transfer这样的任务,比如,要将真人的照片转成二次元风格的照片。

在这种情况下,如何解决?cycle GAN可以解决。为了让输入与输出相关,会训练两个generator,一个将人物转为二次元,一个将二次元转回人物,用cycle consistency loss作为loss函数,Cycle consistency指的是循环一致性:
假设有两个映射:

  • G:A→B
  • F:B→A

那么cycle consistency要求:
$$
F(G(a)) \approx a \quad \text{for all } a \in A
$$

$$
G(F(b)) \approx b \quad \text{for all } b \in B
$$

损失叫cycle consistency loss,比如用L1距离就是:
$$
\mathcal{L}{cycle}(G, F) = \mathbb{E}{a \sim p_{data}(a)}[|F(G(a)) - a|1] + \mathbb{E}{b \sim p_{data}(b)}[|G(F(b)) - b|_1]
$$
30

会不会有这样的问题发生?第一个generator学会“在domain x”里看到一张带眼镜的图片,就在生成的图片左上方做一个标记;第二个generator学会看到生成的图片左上方有一个标记,就生成一张戴眼镜的图片。而生成的二次元图片中不带有眼镜。这种问题是会发生的,需要解决。

很多人在同一时期(17年3、4月)提出了相似的cycle GAN的模型。

cycle GAN也可以用在文字风格上。例如,将negative的句子“你真笨”变为positive的句子“你真聪明”。

31

这种unsupervised的方法还可以运用在之前学习的任务中,

32