首推这篇博客,本文内容多参考自此。WGAN (原理解析)open in new window
本文也另外参考了这篇博客open in new window
结果!
尽管表现差了些,但也比之前的Qriginal GAN要强多了,这里出现了训练不稳定的情况,但还不至于直接导致梯度消失问题。
从最终结果来看,成绩还没那么糟糕,较多数的点也聚集在黄点的区域附近了。
原因?
原始GAN的缺陷
通俗的解释就是,判别器训练的太好,以至于生成器训练不动!
我们先来看一下Original GAN的损失函数:
L(G,D)=2DJS(Pr∣∣Pg)−2log2
为避免你遗忘,再复习一下这两个定义:
DKL(p∣∣q)=∫xp(x)logq(x)p(x)dx
DJS=21DKL(p∣∣2p+q)+21DKL(q∣∣2p+q)
根据前一篇的推导,我们可以看到,如果我们能缩小这个JS散度的话,就可以把真实分布和生成分布拉近了,而这正是我们希望看到的,生成器生成的东西会更接近真实。
然而现实总是残酷的,问题就出现在这个JS散度上。我们对它的希望只能在两个分布有所重叠且重叠部分不可忽略的情况下才能成立。反过来说就是,如果两个分布没有重叠或者有可忽略的重叠,JS散度的结果只能是一个常数( lg2,可自行验证),而常数意味着梯度必然为0!
我们试着探究一下,对于任意一个x,必然分如下四种情况:
P1(x)=0∧P2(x)=0;P1(x)=0∧P2(x)=0;P1(x)=0∧P2(x)=0;P1(x)=0∧P2(x)=0;
对于第一种情况是对JS散度无贡献的;而第二种情况又因为重叠部分可忽略以至于贡献过小,也可看作无贡献;而对于后两种情况,计算结果皆为lg2。
而真实分布和生成分布不重叠或者重叠部分可忽略的情况出现的可能性有多大?可以说非常大。而严格来说,则是当Pr和Pg的支撑集是高维空间中的低维流形时Pr和Pg重叠部分测度为0的概率为1。
现在,我们再想,一般生成器都是从低维空间中随机生成某一编码向量,再通过神经网络生成高维数据,然而尽管最后数据是高维的,可实质上的维度还是和编码向量维度相同或者比编码向量维度还小(考虑到神经网络带来的映射降维)——因为向量的所有变化都只能分布在低维空间中,而所有的变化又与高维数据形成映射,也就是说,高维空间中的事件空间仅含有低维空间内的所有变化,而不包含高维空间的其余部分。「这就是这位博主说的“撑不满”整个高维空间的意思。」
正是有上面的“撑不满”的问题,真实分布和生成分布就很难有重叠部分,首先要明白,我们寻找的重叠部分实际上是和真实分布、生成分布同维的,那么在整个高维空间中,两个分布即使能相交,得到的重叠部分只能是低维流形,这对于我们要找到的重叠部分而言差的很远,测度显然为0(可能没有测度,也可能测度过小而可忽略)。
综上所述,在(近似)最优判别器中,最小化生成器L(G,D)等价于最小化Pr和Pg的JS散度,然而当Pr和Pg不重叠或者重叠部分测度为0时,无论两个分布相距多远,JS散度始终是lg2,导致梯度几乎为0,梯度消失,生成器就无法训练。
而我们再宏观把握这个问题:
1.由于Pr和Pg几乎不能有不可忽略的重叠,不论两者实际的距离有多小,必然存在一个最优分割曲面能恰好将两者隔开,而且仅仅是那些测度为0的重叠部分不能被隔开。
2.以神经网络为主体的判别器可无限拟合上面提到的曲面,从而能够出现这样一个最优判别器,对几乎整个真实分布给出概率为1,而对几乎整个生成分布给出概率为0,而只有那些可忽略的测度为0的部分,最优判别器才不能很好地分类。
3.判别器对真实分布和生成分布的概率是常数,这造成生成器损失函数的梯度变成0。
这下就清楚原始GAN的问题所在了:如果判别器训练得太好,生成器的梯度就消失,Loss函数无法下降;如果判别器训练得不好,生成器梯度不准,会生成许多无用的样本。只有将判别器训练得恰到好处才能表现良好,然而这一点非常难做到,像博主所说,“在同一轮训练的前后不同阶段这个火候都可能不一样”,因此原始GAN非常难训练。
后来Ian Goodfellow提出了一种改进方式,将生成器的Loss改成Ex∼[−logD(x)],然后将KL散度变换成含判别器D的形式:
KL(Pg∣∣Pr)=Ex∼Pg[logPr(x)Pg(x)]=Ex∼Pg[logPr(x)+(x)Pr(x)Pr(x)+Pg(x)Pg(x)]=Ex∼Pg[logD(x)1−D(x)]=Ex∼Pglog[1−D(x)]−Ex∼PglogD(x)
Ex∼Pg[−logD(x)]=KL(Pg∣∣Pr)−Ex∼Pglog[1−D(x)]=KL(Pg∣∣Pr)−2JS(Pr∣∣Pg)+2log2+Ex∼PrlogD(x)
很容易看出来最后两项不依赖生成器,因而最小化该公式就等价于最小化KL(Pg∣∣Pr)−2JS(Pr∣∣Pg),但很明显,这是在同时做两件相互矛盾的事——既要最小化KL散度,又要最大化JS散度(回顾之前我写的Original GAN可知,JS本身就是在以KL散度来定义)。尽管看起来可能像构成博弈,但最后造成了梯度不稳定。
另外,前面的KL散度项也存在问题,因为KL散度本身不是对称的衡量,KL(Pg∣∣Pr)和KL(Pr∣∣Pg)是有区别的,为什么要提及不对称的问题?我们看一下关于KL(Pg∣∣Pr)的两种情况:
当Pg(x)→0,Pr(x)→1时,Pg(x)logPr(x)Pg(x)→0,对KL(Pg∣∣Pr)的贡献趋近于0;
当Pg(x)→1,Pr(x)→0时,Pg(x)logPr(x)Pg(x)→+∞,对KL(Pg∣∣Pr)的贡献趋近于正无穷。
这代表什么意思?一言以蔽之,惩罚失衡。对于第一种情况,生成器没能生成真实的样本,惩罚却微小,而对于第二种情况,生成器生成了不真实的样本,惩罚却巨大。这样会导致生成器宁可生成重复但不会错的样本,也不会去生成多样的样本。其实这就是常见的。
WGAN前作的改进
WGAN前作针对生成器随机初始化后的生成分布很难与真实分布有不可忽略的重叠的问题提出了一种解决方案:向生成样本和真实样本添加噪声。这样就让原本的两个低维流形强行弥散到整个高维空间,从而使两者产生不可忽略的重叠。
显然一旦存在重叠,JS散度就可以发挥作用,当两个分布靠近的时候,JS散度也会减小,而不再是恒为常数了,这样就可以解决生成器梯度消失问题了。训练过程中还可以对添加的噪声进行退火,缓慢减小其方差,直到最后两个低维流体已经有不可忽略的重叠的时候把噪声完全拿掉,JS也能正常发挥作用,继续拉近两个低维流形,直到他们接近完全重合为止。
采取这样的方案后,对原来的损失函数
Ex∼Pr(x)[logD(x)]+Ex∼Pg(x)[log(1−D(x))]=2JS(Pr∣∣Pg)−2log2
取反可得判别器最小loss为
minLD(Pr+ϵ,Pg+ϵ)=−Ex∼Pr+ϵ[logD(x)]−Ex∼Pg+ϵ[log(1−D(x))]
WGAN 出现的全过程
在这里就要引入一个新的概念:Wasserstein距离(又称Earth-Mover距离、EM距离),定义如下:
W(Pr,Pg)=γ∼Π(Pr,Pg)infE(x,y)∼γ[∣∣x−y∣∣]
其中Π(Pr,)是Pr和Pg组合起来的所有可能的联合分布集合,显然,其边缘分布为Pr和Pg。又由于对每一个可能的联合分布γ而言,总可以从Π(Pr,)中采样(x,y)∼γ得到一个真实样本x和生成样本y,并算出这两个样本的距离∣∣x−y∣∣,就可以借此算出该联合分布γ下样本对距离的期望值E(x,y)∼γ[∣∣x−y∣∣],在所有可能的联合分布中取到这些期望值的下界γ∼Π(Pr,Pg)infE(x,y)∼γ[∣∣x−y∣∣],这个下界就被定义为Wasserstein距离。
这里直接引用博主的解释,博主的讲解还是比较准确的:
直观上可以把E(x,y)∼γ[∣∣x−y∣∣]理解为在γ这个“路径规划”下把Pr这堆“沙土”挪到Pg“位置”所需的“消耗”,而W(Pr,Pg)就是“最优路径规划”下的“最小消耗”,所以才叫Earth-Mover(推土机)距离。
Wasserstein距离相比KL散度和JS散度的优势在于,即使真实分布和生成分布没有任何重叠,Wasserstein距离仍然能够反映它们的远近程度。下面我们来做一个平行比较:
其中P1表示在线段AB上的分布,P2表示在线段CD上的分布
KL(P1∣∣P2)={+∞ifϑ=00ifϑ=0
JS(P1∣∣P2)={+∞ifϑ=00ifϑ=0
W(P1,P2)=∣ϑ∣
从中我们不难发现,KL散度、JS散度在ϑ=0附近发生突变,这意味着在ϑ=0处不可导,因而不可能存在梯度,更谈何下降?
那么我们理解Wasserstein距离之后,该如何构建GAN网络模型呢?将它定义为生成器的Loss函数,产生有意义的梯度,从而将生成分布拉向真实分布吗?然而我们回看这个定义,会发现如果要求解γ∼Π(Pr,)inf的话,按照定义我们应该把每一个期望值都算出来,然后去寻找下界。很明显这效率不高,也几乎无法实现。不过作者用了一系列定理得出一套能使用的公式:
W(Pr,)=K1∣∣f∣∣L≤KsupEx∼Pr[f(x)]−Ex∼Pg[f(x)]
具体过程请去查阅相关论文open in new window的附录C。
在进一步解释这个公式之前,我们需要知道利普希茨连续(Lipschitz Continuity)的概念:
对于在实数集的子集的函数f:D⊆R→R,若存在常数K,使得∣f(a)−f(b)∣≤K∣a−b∣∀a,b∈D,则称f符合利普希茨条件,对于f最小的常数K称为 f的利普希茨常数。
若K<1,f称为收缩映射。
利普希茨条件也可对任意度量空间的函数定义:
给定两个度量空间(M,dM),(N,dN),U⊆M。若对于函数f:U→N,存在常数K使得dN(f(a),f(b))≤KdM(a,b)∀a,b∈U 则说它符合利普希茨条件。
若存在K≥1使得K1dM(a,b)≤dN(f(a),f(b))≤KdM(a,b)∀a,b∈U 则称f为双李普希茨(bi-Lipschitz)。
——摘自维基百科
这里要特别指出,博主在这里给出的解释是不准确的,利普希茨连续函数f(x)不一定处处可微。
它其实就是在一个连续函数f上面额外施加了一个限制,要求存在一个常数K≥0使得定义域内的任意两个元素x1和x2都满足∣f(x1)−f(x2)∣≤K∣x1−x2∣,此时称函数f的Lipschitz常数为K。
如果用数学方式说明,则已知f(x)是利普希茨连续函数,
∵a=b由定义知当∣a−b∣→0时,∃K使得∣a−b∣∣f(a)−f(b)∣≤K。而∣a−b∣→0+lim∣a−b∣∣f(a)−f(b)∣≤K∣a−b∣→0−lim∣a−b∣∣f(a)−f(b)∣≤K⎭⎬⎫⇒∣a−b∣→0+lim∣a−b∣∣f(a)−f(b)∣=∣a−b∣→0−lim∣a−b∣∣f(a)−f(b)∣
另外再提起一个函数f(x)=∣x∣,f′(x)={1−1x≥0x<0,显然可以发现它是利普希茨连续函数,因为它符合利普希茨连续条件的定义。而对于x=0处却不可微「左导数和右导数不等而导致导数不存在」。
因此个人认为应该这样理解:利普希茨连续函数是广义的有限制的连续函数,广义指的是不需要处处可微,有限制指的是导函数有界。显然利普希茨连续条件限制了函数的最大局部变化幅度。
下面的这个公式
W(Pr,Pg)=K1∣∣f∣∣L≤KsupEx∼Pr[f(x)]−Ex∼Pg[f(x)](A)
在要求∣∣f∣∣L≤K的条件下对所有可能满足条件的f取到Ex∼Pr[f(x)]−Ex∼Pg[f(x)]的上界,再缩小K倍。
如果存在某函数fω,这函数由ω个参数确定,则(A)式可以写成:
W(Pr,Pg)≈K1ω∣fω∣L≤KmaxEx∼Pr[fω(x)]−Ex∼Pg[fω(x)](B)
这样定义出来的函数虽然不能囊括所有可能,至少可以足够接近我们希望求得的原式。那么如何得到上面提到的函数?「由ω个参数确定」不觉得熟悉吗?这个其实就可以用神经网络模型来拟合。那么就可以在(B)式的基础上定义Loss函数了,只要能寻找出上界就可以,由于我们只有梯度下降求解器,我们需要适当使用图形变换技巧转化一下(B)式,比如取其相反数。
最后还不能忘记公式的前提条件∣∣f∣∣L≤K,当然,K究竟有多大这无关紧要「不是无穷大就行」,因为我们只是需要梯度的方向。那么如何满足这个条件?作者采取的做法是始终将神经网络内的参数ωi限制在闭区间[−c,c],这样关于输入样本x的导数就会被限制在确定的范围,至于到底是什么范围,就不是我们关心的了,有界就行。
现在我们构造一下判别器的损失函数。含参数ω、最后一层不是非线性激活层的判别器网络fω在限制ω不超过某个范围的条件下,使
L=Ex∼Pr[fw(x)]−Ex∼Pg[fw(x)]
取到最大,此时L就会接近真实分布与生成分布之间的Wasserstein距离「忽略系数K」。
这里要特别注意一下的是: Original GAN的判别器要做的是真假二分类任务,因此最后一层才会采用sigmoid函数处理,而现在WGAN的判别器做的是近似拟合Wasserstein距离,是回归问题,因此最后一层不使用非线性结构,要把sigmoid函数去掉。 |
接着要与判别器博弈的生成器需要近似最小化Wasserstein距离,也就是最小化L,前面足够充分讲解了Wasserstein距离的优良性质,我们不用再担心会有梯度消失的问题。另外L的第一项只与判别器有关而与生成器无关,因此生成器的损失函数不含$ \mathbb{E}_{x \sim P_r} [f_w(x)] $这一项「因为这一项会是常数」。
最终整个WGAN的两个Loss函数就构建完成了:
LG=−Ex∼Pg[fw(x)]LD=Ex∼Pg[fw(x)]−Ex∼Pr[fw(x)]
实现!
只消在Original GAN代码的基础上再加上:
def weights_init(m):
if isinstance(m, nn.Linear):
nn.init.kaiming_normal_(m.weight)
m.bias.data.fill_(0)
然后在main函数中初始化变量_clip为0.025「你也可以自己设定」,然后在声明的G和D后面插入两行代码:
G.apply(weights_init)
D.apply(weights_init)
并在判别器训练的代码块后生成器训练的代码块之前添加如下代码:
for w in D.parameters():
w.data.clamp_(-_clip, _clip)
为了出现上面贴的图,我将后面的if epoch %
后的数改成10,for epoch in range
后括号内的数改成1000。
到此为止代码也就改完了,我们的WGAN也实现了!
完整代码请点这里查看-->·open in new window
“睿智”作者的话(不重要)
光是学WGAN到写这长篇报(fèi)告(huà)就花了快半个月了,也是多艰多难啊!不过最终总算是完成了,完结撒花🌸撒花!🌸~~~
如果想学无监督学习,数学真的是硬核,对线性代数、概率论要求比较高,涉及的数学领域也很广,就目前来看,我见过的有涉及实分析、线性代数、概率论、数学分析、凸分析、复分析等的知识,虽然学到了些许特别零碎的可能没什么用的小知识点。
不过不得不感叹的是我非常敬佩Martin Arjovsky等人,只是做了简单的四处改动,却能获得如此惊人的成效,从根本上解决Original GAN难训练、不稳定的问题,不错的战斗成果,即使是我也会感到心潮澎湃!🤣🤣🤣