手把手教你用keras搭建GAN

版权声明:本文为博主原创文章,更多精彩文章请关注公众号【Jeemy110】 https://blog.csdn.net/l7H9JA4/article/details/85011191

   作者:陈   扬           

编辑:陈人和           



论文地址:https://arxiv.org/pdf/1406.2661.pdf

github:https://github.com/chenyang1999/KerasGAN/blob/master/gan/gan.py


前  言

大家好,我是中国海洋大学的陈扬。在遥远的九月份,我开始做了keras的系列教程,现在我主要的研究方向转到了生成对抗网络,生成对抗网络的代码实现和训练机制比分类模型都要复杂和难入门.之前一段时间时间一直在帮璇姐跑cvpr的实验代码,做了蛮多的对比实验,其中我就发现了,keras的代码实现和可阅读性很好,搭生成对抗网络网络GAN就好像搭乐高积木一样有趣哦。不只是demo哦,我还会在接下来的一系列 keras教程中教你搭建Alexnet,Vggnet,Resnet,DCGAN,ACGAN,CGAN,SRGAN,等等实际的模型并且教你如何在GPU服务器 上运行。
章节目录

  • SGAN

  • 代码实现

  • 训练

  • 结束语



01

SGAN

什么是生成对抗网络:

640?wx_fmt=png

简单的来说,就是给定一个噪声z的输入,通过生成器的变换把噪声的概率分布空间尽可能的去拟合真实数据的分布空间。

基本框架:

640?wx_fmt=png 

在这里,我们把生成器看的目标看成是要“以假乱真”,判别器的目标是要“明辩真假”。

核心公式:

640?wx_fmt=png

这个公式我们要分成两个部分来看,先看前半部分:

640?wx_fmt=png 这个公式的意思是,先看加号前面Ex~pdata(x) [logD(x)] ,我们希望D最大,所以log(D(x))应该最大,意味着我的判别器可以很好的识别出,真实世界图像是"true",在看加号后面Ez~pz(z) [log(1-D(G(z)))],要让log尽可能的大,需要的是D(G(z))尽可能的小,意味着我们生成模型的图片应该尽可能的被判别模型视为"FALSE"。

再看后半部分:

640?wx_fmt=png 我们应该让G尽可能的小,加号前面的式子并没有G,所以无关,在看加号后面的式子Ez~pz(z) [log(1-D(G(z)))],要让G尽可能地小,就要D(G(Z))尽可能的大,也就是说本来就一张→噪声生成的图片,判别器却被迷w惑了,以为是一张真实世界图片。这就是所谓的以假乱真。

640?wx_fmt=png 



02

代码实现

生成器:

```python
   def build_generator(self):

       model = Sequential()

       model.add(Dense(256, input_dim=self.latent_dim))
       model.add(LeakyReLU(alpha=0.2))
       model.add(BatchNormalization(momentum=0.8))
       model.add(Dense(512))
       model.add(LeakyReLU(alpha=0.2))
       model.add(BatchNormalization(momentum=0.8))
       model.add(Dense(1024))
       model.add(LeakyReLU(alpha=0.2))
       model.add(BatchNormalization(momentum=0.8))
       model.add(Dense(np.prod(self.img_shape), activation='tanh'))
       model.add(Reshape(self.img_shape))

       model.summary()

       noise = Input(shape=(self.latent_dim,))
       img = model(noise)

       return Model(noise, img)
```


生成器的输入是一个100维服从高斯分布的向量,输出是一张28*28*1的图片。

```
_________________________________________________________________
Layer (type)                 Output Shape              Param #  
=================================================================
input_2 (InputLayer)         (None, 100)               0        
_________________________________________________________________
sequential_2 (Sequential)    (None, 28, 28, 1)         1493520  
=================================================================
Total params: 1,493,520
Trainable params: 1,489,936
Non-trainable params: 3,584
```



判别器:

```python
   def build_discriminator(self):

       model = Sequential()

       model.add(Flatten(input_shape=self.img_shape))
       model.add(Dense(512))
       model.add(LeakyReLU(alpha=0.2))
       model.add(Dense(256))
       model.add(LeakyReLU(alpha=0.2))
       model.add(Dense(1, activation='sigmoid'))
       model.summary()

       img = Input(shape=self.img_shape)
       validity = model(img)

       return Model(img, validity)

```


 判别器的输入是一张28*28*1的图片和一个一维的真假标签,1代表是真实世界图片,0代表的的生成模型生成的图片。

```
_________________________________________________________________
Layer (type)                 Output Shape              Param #  
=================================================================
input_1 (InputLayer)         (None, 28, 28, 1)         0        
_________________________________________________________________
sequential_1 (Sequential)    (None, 1)                 533505    
=================================================================
Total params: 533,505
Trainable params: 533,505
Non-trainable params: 0
```


注意了!在SGAN(2104)中,作者并没有用卷积池化等操作,他只是用了最简单的full connection全连接层。


03

训练

条件随机场(Conditional Random Field,简称CRF)是一种判别式无向图模型。生成式模型是直接对联合分布进行建模,而判别式模型则是对条件分布进行建模。前面介绍的隐马尔可夫模型和马尔可夫随机场都是生成式模型,而条件随机场是判别式模型。

定义模型:

```python

def __init__(self):
       self.img_rows = 28
       self.img_cols = 28
       self.channels = 1
       self.img_shape = (self.img_rows, self.img_cols, self.channels)
       self.latent_dim = 100

       optimizer = Adam(0.0002, 0.5)

       # Build and compile the discriminator
       self.discriminator = self.build_discriminator()
       self.discriminator.summary()
       self.discriminator.compile(loss='binary_crossentropy',
           optimizer=optimizer,
           metrics=['accuracy'])

       # Build the generator
       self.generator = self.build_generator()
       self.generator.summary ()
       # The generator takes noise as input and generates imgs
       z = Input(shape=(self.latent_dim,))
       img = self.generator(z)

       # For the combined model we will only train the generator
       self.discriminator.trainable = False

       # The discriminator takes generated images as input and determines validity
       validity = self.discriminator(img)
       # The combined model  (stacked generator and discriminator)
       # Trains the generator to fool the discriminator
       self.combined = Model(z, validity)
       self.combined.summary()
       self.combined.compile(loss='binary_crossentropy', optimizer=optimizer
```


判别器discriminator只训练判别器的参数;生成器的训练是把生成器和判别器两个网络连在一起,但是冻结判别器的学习率,一起组成combined.用的都是binary_crossentropy二分类的交叉熵作为损失函数.

训练部分:

```
def train(self, epochs, batch_size=128, sample_interval=50):

   # Load the dataset
   (X_train, _), (_, _) = mnist.load_data()

   # Rescale -1 to 1
   X_train = X_train / 127.5 - 1.
   X_train = np.expand_dims(X_train, axis=3)

   # Adversarial ground truths
   valid = np.ones((batch_size, 1))
   fake = np.zeros((batch_size, 1))

   for epoch in range(epochs):

       # ---------------------
       #  Train Discriminator
       # ---------------------

       # Select a random batch of images
       idx = np.random.randint(0, X_train.shape[0], batch_size)
       imgs = X_train[idx]

       noise = np.random.normal(0, 1, (batch_size, self.latent_dim))

       # Generate a batch of new images
       gen_imgs = self.generator.predict(noise)

       # Train the discriminator
       d_loss_real = self.discriminator.train_on_batch(imgs, valid)
       d_loss_fake = self.discriminator.train_on_batch(gen_imgs, fake)
       d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)

       # ---------------------
       #  Train Generator
       # ---------------------

       noise = np.random.normal(0, 1, (batch_size, self.latent_dim))

       # Train the generator (to have the discriminator label samples as valid)
       g_loss = self.combined.train_on_batch(noise, valid)

       # Plot the progress
       print ("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (epoch, d_loss[0], 100*d_loss[1], g_loss))

       # If at save interval => save generated image samples
       if epoch % sample_interval == 0:
           self.sample_images(epoch)
```


先加载数据集,然后每一次训练从数据集里面随机选取一张图片进行训练,训练的时候,真实图片对应的标签是valid=1,生成器生成的图片对应的标签是fake=0;
训练的时候,先训练dloss,dloss由真实世界图片和生成图片以及其标签进行训练.在训练判别器的时候,真实世界图片对应真实的标签valid,生成的图片对应fake标签,也就是让判别器"明辨真假"的过程.在训练生成器的时候,我们输入高斯噪声和噪声标签,等于是告诉生成对抗网络,我给你一个"假的"图片,但是是"真的"标签,也就是我们让生成器以假乱真的过程.不断的在"明辨真假"和"以假乱真"的两个过程不断迭代训练,最终,生成器可以很好的"以假乱真",判别器可以很好的"明辨真假".当我们把生成器的图片给"人"看的时候,人就会被"以假乱真"了。

在服务器上训练:

在训练了30000epoch后

```
30000 [D loss: 0.693933, acc.: 57.81%] [G loss: 0.853226]

```


640?wx_fmt=png

我们看得到,判别器分辨真假的能力接近1/2,相当于已经被生成器以假乱真了。


04

结束语

在github上,有完整的gan.py代码,未来我还会出这一集的视频,在bilibili上播放,我希望能通过说的方式,带你从code的角度去理解复杂的生成对抗网络 未来我还出一系列我们研究过程中运用到的对比试验的model,比如DCGAN,ACGAN,CGAN,SRGAN等欢迎大家持续支持我们的公众号。





 640?wx_fmt=gif

END








往期回顾

【1】 机器学习论文笔记—如何利用高效的搜索算法来搜索网络的拓扑结构

【2】 手把手教你用keras--像搭乐高积木一样搭建神经网络(lenet)

【3】 轻量级CNN网络之MobileNetv2

【4】 【学习干货】目标检测算法之SSD

【5】 干货|(DL~4)对象定位和检测









机器学习算法工程师


                            一个用心的公众号

640?wx_fmt=jpeg

长按,识别,加关注

进群,学习,得帮助

你的关注,我们的热度,

我们一定给你学习最大的帮助





展开阅读全文

没有更多推荐了,返回首页