单步训练GAN技巧——把生成器和判别器loss合并成一个

我们知道普通的模型都是搭好架构,然后定义好loss,直接扔给优化器训练就行了。但是GAN不一样,一般来说它涉及有两个不同的loss,这两个loss需要交替优化。现在主流的方案是判别器和生成器都按照1:1的次数交替训练(各训练一次,必要时可以给两者设置不同的学习率,即TTUR),交替优化就意味我们需要传入两次数据(从内存传到显存)、执行两次前向传播和反向传播。

如果我们能把这两步合并起来,作为一步去优化,那么肯定能节省时间的,这也就是GAN的同步训练。

(注:本文不是介绍新的GAN,而是介绍GAN的新写法,这只是一道编程题,不是一道算法题~)

如果在TF中 #

 

如果是在tensorflow中,实现同步训练并不困难,因为我们定义好了判别器和生成器的训练算子了(假设为D_solverG_solver),那么直接执行

sess.run([D_solver, G_solver], feed_dict={x_in: x_train, z_in: z_train})

就行了。这建立在我们能分别获取判别器和生成器的参数、能直接操作sess.run的基础上。

更通用的方法 #

但是如果是Keras呢?Keras中已经把流程封装好了,一般来说我们没法去操作得如此精细。所以,下面我们介绍一个通用的技巧,只需要定义单一一个loss,然后扔给优化器,就能够实现GAN的训练。同时,从这个技巧中,我们还可以学习到如何更加灵活地操作loss来控制梯度。

判别器的优化 #

我们以GAN的hinge loss为例子,它的形式是:

D=G=argminD𝔼x∼p(x)[max(0,1+D(x))]+𝔼z∼q(z)[max(0,1−D(G(z)))]argminG𝔼z∼q(z)[D(G(z))](1)(1)D=arg⁡minD⁡Ex∼p(x)[max(0,1+D(x))]+Ez∼q(z)[max(0,1−D(G(z)))]G=arg⁡minG⁡Ez∼q(z)[D(G(z))]


注意argminDarg⁡minD意味着要固定GG,因为GG本身也是有优化参数的,不固定的话就应该是argminD,Garg⁡minD,G。

 

为了固定GG,除了“把GG的参数从优化器中去掉”这个方法之外,我们也可以利用stop_gradient去手动固定:

D,G=argminD,G𝔼x∼p(x)[max(0,1+D(x))]+𝔼z∼q(z)[max(0,1−D(Gng(z)))](2)(2)D,G=arg⁡minD,G⁡Ex∼p(x)[max(0,1+D(x))]+Ez∼q(z)[max(0,1−D(Gng(z)))]


这里

Gng(z)=stop_gradient(G(z))(3)(3)Gng(z)=stop_gradient(G(z))


这样一来,在式(2)(2)中,我们虽然同时放开了D,GD,G的权重,但是不断地优化式(2)(2),会变的只有DD,而GG是不会变的,因为我们用的是基于梯度下降的优化器,而GG的梯度已经被停止了,换句话说,我们可以理解为GG的梯度被强行设置为0,所以它的更新量一直都是0。

 

生成器的优化 #

现在解决了DD的优化,那么GG呢?stop_gradient可以很方便地放我们固定里边部分的梯度(比如D(G(z))D(G(z))的G(z)G(z)),但GG的优化是要我们去固定外边的DD,没有函数实现它。但不要灰心,我们可以用一个数学技巧进行转化。

首先,我们要清楚,我们想要D(G(z))D(G(z))里边的GG的梯度,不想要DD的梯度,如果直接对D(G(z))D(G(z))求梯度,那么同时会得到D,GD,G的梯度。如果直接求D(Gng(z))D(Gng(z))的梯度呢?只能得到DD的梯度,因为GG已经被停止了。那么,重点来了,将这两个相减,不就得到单纯的GG的梯度了吗!

D,G=argminD,G𝔼z∼q(z)[D(G(z))−D(Gng(z))](4)(4)D,G=arg⁡minD,G⁡Ez∼q(z)[D(G(z))−D(Gng(z))]


现在优化式(4)(4),那么DD是不会变的,改变的是GG。

 

注:不需要从链式法则来理解这种写法,而是要通过stop_gradient本身的意义来理解。对于L(D,G)L(D,G),不管G,DG,D的关系是什么,完整的梯度都是(∇DL,∇GL)(∇DL,∇GL),而把GG的梯度停止后,相当于GG的梯度强行设置为0的,也就是L(D,Gng)L(D,Gng)的梯度实际上为(∇DL,0)(∇DL,0),所以L(D,G)−L(D,Gng)L(D,G)−L(D,Gng)的梯度是(∇DL,∇GL)−(∇DL,0)=(0,∇GL)(∇DL,∇GL)−(∇DL,0)=(0,∇GL)。

值得一提的是,直接输出这个式子,结果是恒等于0,因为两部分都是一样的,直接相减自然是0,但它的梯度不是0。也就是说,这是一个恒等于0的loss,但是梯度却不恒等于0。

合成单一loss #

好了,现在式(2)(2)和式(4)(4)都同时放开了D,GD,G,大家都是argminarg⁡min,所以可以将两步合成一个loss:

D,G=argminD,G𝔼x∼p(x)[max(0,1+D(x))]+𝔼z∼q(z)[max(0,1−D(Gng(z)))]+λ𝔼z∼q(z)[D(G(z))−D(Gng(z))](5)(5)D,G=arg⁡minD,GEx∼p(x)[max(0,1+D(x))]+Ez∼q(z)[max(0,1−D(Gng(z)))]+λEz∼q(z)[D(G(z))−D(Gng(z))]


写出这个loss,就可以同时完成判别器和生成器的优化了,而不需要交替训练,但是效果基本上等效于1:1的交替训练。引入λλ的作用,相当于让判别器和生成器的学习率之比为1:λ1:λ。

 

参考代码:https://github.com/bojone/gan/blob/master/gan_one_step_with_hinge_loss.py

文章小结 #

文章主要介绍了实现GAN的一个小技巧,允许我们只写单个模型、用单个loss就实现GAN的训练。它本质上就是用stop_gradient来手动控制梯度的技巧,在其他任务上也可能用得到它。

所以,以后我写GAN都用这种写法了,省力省时~当然,理论上这种写法需要多耗些显存,这也算是牺牲空间换时间吧。

转载到请包括本文地址:https://spaces.ac.cn/archives/6387

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值