经典网络复现系列(三):GAN

1、简述
最近读了Gnerative Adversarial Nets(GAN)的论文,有种无间道的感觉。其他话不说先放张图。


训练网络的时候,需要大量的样本,样本数量的不足会导致网络训练的效果不好。那么我们可不可以自己生成一些样本?这时候GAN就派上了用场。
GAN蕴含了两个网络博弈的思想。它由两个网络结构组成,即生成器(generator)和鉴定器(discriminator)。生成器负责产生假的样本,鉴定器负责鉴定样本是真是假。如图所示







图片来自https://blog.csdn.net/on2way/article/details/72773771

 

生成器的输入是噪声,输出是造假的图片。

鉴定器的输入是图片,输出是D(x)。在网络结构中,最后一层是sigmoid(),因此输出在0-1之间。若D(x)接近1,判定为真,若D(x)接近0,则判定为假。若输出为0.5,则进入纳什平衡,不能判定真假,也就达到了以假乱真的效果。


怎么实现以假乱真呢?我们可以将生成器想象为造假集团(G),而鉴定器为检查部门(D),双方互有卧底。
1)最初,鉴定器一方的卧底拿到了真假样品,他们通过比较,改进了自己的检查机器(通过真假样本训练鉴定网络,使得鉴定器损失最小化),从而知道了什么样的样品是真的,什么样的样品是假的。在这个过程中,鉴定网络是知道哪些样本为真,哪些样本为假的。(真样本标签为1,假样本标签为0)
2)随后,生成器一方的卧底拿到了鉴定器的机器(鉴定网络),他们使用自己的造假机器(生成器)造出假的样品
,输入到机器中,然后根据结果不断改进自己的造假机器,直到鉴定机器区分不出来(保持鉴定网络不变,训练生成网络)。在这个过程中,造假集团灰希望鉴定器的输出越接近1越好(这样才能骗过去嘛),所以他们将假样本的标签都设定为真(1),努力改进机器,使得输出结果与全部为1的差距最小。  (也就是说,在这个过程中,将假样本的标签全部设置为1)
3)鉴定器一方的卧底再次拿到了真假样品,并改进自己的机器。
4).....


经过往复循环后,生成器一方产生的假样本越来越逼真。我们的目的也就达到了。




下面用数学表达式来进行展示
怎么提升D?
对于真实的样本,我们希望D(x)越大越好。
对于造假得到的样本,我们希望D(G(z))越小越好,也就是1-D(G(z))越大越好。
所以 max (  log(D(x))+log(1-(D(G(Z))))


怎么提升G?
对于造假的样本,希望输出的D(G(z))越大越好。
max  (log(D(G(z)))




2、网络结构
原论文中生成器使用了线性激活函数和sigmoid函数,鉴定器使用了maxout激活函数和dropout。我在复现的过程中,网络结构使用了batchnormalization.使用之后,dropout和L2损失都可以省略。
引入lrelu层,返回值为  lrelu=max(x,x*0.2)
D(鉴别器)

             input        (batch_size,28,28,1)
                |
       conv2d+bias+lrelu   (kernel:[4,4,1,64]  stride[1,2,2,1]  padding='SAME')
                |
  conv2d+bias+batch_normal+lrelu  (kernel:[4,4,64,128],stride:[1,2,2,1],padding='SAME')
                |
           reshaped        (out:  [batch_size,-1])
                |
     fc+batch_normal+lrelu  (fc:  out[1024])
                |
        fc+sigmoid








G(生成器)

                       input        (batch_size,62)
                          |
      fc+batch_nomal+relu  (fc:out[1024])
                          |
      fc+batch_normal+relu (fc:out[128*7**7])
                          |
                  reshaped        (out: [batch_size,7,7,128])
                          |
   deconv2d+batch_normal+relu    (kernel:[4,4,64,128],stride:[1,2,2,1],outshape:[batch_size,14,14,64])
                          |
          deconv+sigmoid    (kernel:[4,4,1,64] stride:[1,2,2,1],outshape:[batch_size,28,28,1])




3、复现中的小trick
1)首先要吐槽一下GAN的不稳定性。训练起来非常麻烦,更换了一下变量初始化的方式就不能得到理想效果,折腾了很久。关于提高训练的稳定性,在后面 GAN的发展 一节中。
2)关于变量的名字,它的组成有两部分,一部分是scope的名字,一部分是变量自身的名字。举个例子:

with tf.variable_scope('d_conv'):
       w = tf.get_variable('w', shape=shape,initializer=tf.truncated_normal_initializer(stddev=0.02))

 

 

 


在这里变量w的全名就是'd_conv/w'
可以看出命名是有层次的。


3)搭建网络时有如下几行:

 

 

 

 

d_real=discriminator(X_tr,True,False)
g_out=generator(z,True,False)
d_fake=discriminator(g_out,True,True)
    
fake=generator(z,False,True)



其中,前三行是训练时候用到,最后一行是测试的时候用到。
可以看到discriminator和generator都不止一次地被调用,这就涉及到了变量reuse的问题。
参考博客:https://www.cnblogs.com/weizhen/p/6751792.html


如果嫌麻烦,可以使用with tf.variable_scope(name_or_scope='name', reuse=tf.AUTO_REUSE)
参考博客 https://blog.csdn.net/JNingWei/article/details/78124526




4)有的时候,我们在最小化损失的时候,只想改变整个网络结构中一部分变量。例如在训练G的时候,我们要保持D的网络参数不变。应当怎么实现?

 

t_vars=tf.trainable_variables()
v_d=[var for var in t_vars if 'd_'in var.name ]##################
v_g=[var for var in t_vars if 'g_'in var.name ]





在命名的时候,D中的相关变量均以'd_'开头,G中的相关变量均以'g_'开头,使用上面的三行代码  v_d即为与D相关的变量,v_g即为与g相关的变量。


优化时:

 

with tf.control_dependencies(tf.get_collection(tf.GraphKeys.UPDATE_OPS)):
        d_op=tf.train.AdamOptimizer(learning_rate=FLAGS.learning_rate,beta1=FLAGS.beta1).minimize(d_loss,var_list=v_d)       
        g_op=tf.train.AdamOptimizer(learning_rate=5*FLAGS.learning_rate,beta1=FLAGS.beta1).minimize(g_loss,var_list=v_g) 



使用var_list指定要优化的变量。


5)产生(-1,1)之间均匀分布的噪声。


6)在sess.run()的时候,哪些placeholder与我们要run出的变量有关系,就在feed_dict中喂哪些placeholder,多余的不喂,否则会报错。


7)检查一个文件夹是否存在,如果没有,则创建文件夹

 

import os
if not os.path.isdir(dir):
    os.makedirs(dir)






4、代码地址:
https://github.com/zlrai5895/GAN_tensorflow
5、运行结果
分别是运行到3000   12300  22500步的结果

                 


6、GAN的发展


Wassertein GAN的提出解决了GAN训练不稳定和collapse mode 的问题
Wasserstein GAN(下面简称WGAN)成功地做到了以下爆炸性的几点:


1)彻底解决GAN训练不稳定的问题,不再需要小心平衡生成器和判别器的训练程度


2)基本解决了collapse mode的问题,确保了生成样本的多样性


3)训练过程中终于有一个像交叉熵、准确率这样的数值来指示训练的进程,这个数值越小代表GAN训练得越好,代表生成器产生的图像质量越高(如题图所示)


4)以上一切好处不需要精心设计的网络架构,最简单的多层全连接网络就可以做到




具体做法:
1)判别器最后一层去掉sigmoid


2)生成器和判别器的loss不取log


3)每次更新判别器的参数之后把它们的绝对值截断到不超过一个固定常数c


4)不要用基于动量的优化算法(包括momentum和Adam),推荐RMSProp,SGD也行






参考博客:
http://www.sohu.com/a/125598465_473283

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值