赞
踩
生成式对抗网络(GAN,generative adversarial network)由 Goodfellow 等人于 2014 年提出,它可以替代VAE来学习图像的潜在空间。 '''
# 它能够迫使生成图像与真实图像在统计上几乎无法区分,从而生成相当逼真的合成图像。 #GAN工作原理:一个伪造者网络和一个专家网络,二者训练的目的都是为了打败彼此 因此,GAN由以下两部分组成。 生成器网络(generator network):它以一个随机向量(潜在空间中的一个随机点)作为输入,并将其解码为一张合成图像。 判别器网络(discriminator network)或对手(adversary):以一张图像(真实的或合成的均可)作为输入,并预测该图像是来自训练集还是由生成器网络创建。 训练生成器网络的目的是使其能够欺骗判别器网络,因此随着训练的进行,它能够逐渐生 成越来越逼真的图像,即看起来与真实图像无法区分的人造图像,以至于判别器网络无法区分二者; 与此同时,判别器也在不断适应生成器逐渐提高的能力,为生成图像的真实性设置了很高的标准。一旦训练结束,生成器就能够将其输入空间中的任何点转换为一张可信图像 通常来说,梯度下降是沿着静态的损失地形滚下山坡。但对于GAN而言,每下山一步, 都会对整个地形造成一点改变。它是一个动态的系统,其最优化过程寻找的不是一个最小值, 而是两股力量之间的平衡。 因此,GAN的训练极其困难,想要让GAN正常运行,需要对模型架构和训练参数进行大量的仔细调整 ''' 深度卷积生成式对抗网络(DCGAN,deep convolutional GAN),即生成器和判别器都是深度卷积神经网络的GAN。特别地,它在生成器中使用Conv2DTranspose 层进行图像上采样。 GAN的简要实现流程如下所示。 (1) generator网络将形状为(latent_dim,)的向量映射到形状为(32, 32, 3)的图像。 (2) discriminator 网络将形状为(32, 32, 3)的图像映射到一个二进制分数,用于评估图像为真的概率。 (3) gan网络将generator网络和discriminator网络连接在一起:gan(x) = discriminator (generator(x))。生成器将潜在空间向量解码为图像,判别器对这些图像的真实性进行评估,因此这个 gan 网络是将这些潜在向量映射到判别器的评估结果。 (4) 我们使用带有“真”/“假”标签的真假图像样本来训练判别器,就和训练普通的图像分类模型一样。 (5) 为了训练生成器,我们要使用 gan 模型的损失相对于生成器权重的梯度。这意味着, 在每一步都要移动生成器的权重,其移动方向是让判别器更有可能将生成器解码的图像划分为“真”。换句话说,我们训练生成器来欺骗判别器。 大量技巧: 1.我们使用tanh 作为生成器最后一层的激活,而不用 sigmoid,后者在其他类型的模型中更加常见 2.我们使用正态分布(高斯分布)对潜在空间中的点进行采样,而不用均匀分布。 3.随机性能够提高稳健性。训练GAN得到的是一个动态平衡,所以GAN可能以各种方式“卡住”。在训练过程中引入随机性有助于防止出现这种情况。我们通过两种方式引入随机性: 一种是在判别器中使用dropout,另一种是向判别器的标签添加随机噪声 4.稀疏的梯度会妨碍GAN的训练。在深度学习中,稀疏性通常是我们需要的属性,但在GAN中并非如此。有两件事情可能导致梯度稀疏:最大池化运算和 ReLU 激活。我们推荐使用步进卷积代替最大池化来进行下采样,还推荐使用 LeakyReLU 层来代替 ReLU 激活。LeakyReLU 和 ReLU类似,但它允许较小的负数激活值,从而放宽了稀疏性限制。 5.在生成的图像中,经常会见到棋盘状伪影,这是由生成器中像素空间的不均匀覆盖导致的(见图 8-17)。为了解决这个问题,每当在生成器和判别器中都使用步进的 Conv2DTranpose 或 Conv2D 时,使用的内核大小要能够被步幅大小整除。 '''
- import numpy as np
- import pandas as pd
- import matplotlib.pyplot as plt
- import pylab
- from pandas import DataFrame, Series
- from keras import models, layers, optimizers, losses, metrics
- from keras.utils.np_utils import to_categorical
-
- plt.rcParams['font.sans-serif'] = ['SimHei'] #指定默认字体
- plt.rcParams['axes.unicode_minus'] = False #解决保存图像是负号'-'显示为方块的问题
-
-
- #GAN生成器网络
- #generator 模型,它将一个向量(来自潜在空间,训练过程中对其随机 采样)转换为一张候选图像。GAN常见的诸多问题之一,就是生成器“卡在”看似噪声的生成图像上。一种可行的解决方案是在判别器和生成器中都使用 dropout。
- import keras
-
- latent_dim=32
- height=32
- width=32
- channels=3
-
- generator_input=keras.Input(shape=(latent_dim,))
- x=layers.Dense(128*16*16)(generator_input)
- x=layers.LeakyReLU()(x)
- x=layers.Reshape((16,16,128))(x)#将输入转换为大小为 16×16 的 128 个通道的特征图
-
- x=layers.Conv2D(256,5,padding='same')(x)
- x=layers.LeakyReLU()(x)
-
- x=layers.Conv2DTranspose(256,4,strides=2,padding='same')(x)#上采样为32*32
- x=layers.LeakyReLU()(x)
-
- x=layers.Conv2D(256,5,padding='same')(x)
- x=layers.LeakyReLU()(x)
- x=layers.Conv2D(256,5,padding='same')(x)
- x=layers.LeakyReLU()(x)
-
- x=layers.Conv2D(channels,7,activation='tanh',padding='same')(x)#生成一个大小为 32×32 的单通道特征图 (即 CIFAR10 图像的形状)
- generator=keras.models.Model(generator_input,x)
- generator.summary()
-
-
- #GAN判别器网络
- #discriminator模型,它接收一张候选图像(真实的或合成的)作为输入,并将其划分到这两个类别之一:“生成图像”或“来自训练集的真实图像”
-
- discrimination_input=layers.Input(shape=(height,width,channels))#判别器输入为生成图像与真实图像的拼接,以判断图像的‘真假’
- x=layers.Conv2D(128,3)(discrimination_input)
- x=layers.LeakyReLU()(x)
- x=layers.Conv2D(128,4,strides=2)(x)#卷积窗口4*4,步幅为2
- x=layers.LeakyReLU()(x)
- x=layers.Conv2D(128,4,strides=2)(x)
- x=layers.LeakyReLU()(x)
- x=layers.Conv2D(128,4,strides=2)(x)
- x=layers.LeakyReLU()(x)
- x=layers.Flatten()(x)
- x=layers.Dropout(0.4)(x)
- x=layers.Dense(1,activation='sigmoid')(x)#分类层(真或假)
- discriminator=keras.models.Model(discrimination_input,x)#将判别器模型实例化,这里它将形状为 (32, 32, 3)的输入转换为一个二进制分类决策(真/假)
- discriminator.summary()
- discriminator_optimizer=optimizers.RMSprop(
- lr=0.0008,
- clipvalue=1.0,#优化器中使用梯度裁剪(限制梯度的范围)[它是一个动态的系统,其最优化过程寻找的不是一个最小值,而是两股力量之间的平衡。]
- decay=1e-8#为了稳定训练过程,使用学习率衰减
- )
- discriminator.compile(optimizer=discriminator_optimizer,loss='binary_crossentropy')
-
-
- #对抗网络
- '''
- 最后,我们要设置GAN,将生成器和判别器连接在一起。
- 训练时,这个模型将让生成器向某个方向移动,从而提高它欺骗判别器的能力。这个模型将潜在空间的点转换为一个分类决策(即“真”或“假”),它训练的标签都是“真实图像”。
- 因此,训练 gan 将会更新 generator 的权重,使得 discriminator 在观察假图像时更有可能预测为“真”。
- 请注意,有一点很重要,就是在训练过程中需要将判别器设置为冻结(即不可训练),这样在训练 gan 时它的权重才不会更新。
- 如果在此过程中可以对判别器的权重进行更新,那么我们就是在训练判别器始终预测“真”,但这并不是我们想要的!
- '''
- discriminator.trainable=False#将判别器权重设置为不可训练 (仅应用于 gan 模型)
-
- gan_input=keras.Input(shape=(latent_dim,))
- gan_output=discriminator(generator(gan_input))
- gan=keras.models.Model(gan_input,gan_output)
- gan_optimizer = keras.optimizers.RMSprop(
- lr=0.0004,
- clipvalue=1.0,
- decay=1e-8
- )
- gan.compile(optimizer=gan_optimizer, loss='binary_crossentropy')
-
- #训练DCGAN
- '''
- 每轮都进行以下操作:
- (1) 从潜在空间中抽取随机的点(随机噪声)。
- (2) 利用这个随机噪声用 generator 生成图像。
- (3) 将生成图像与真实图像混合。
- (4) 使用这些混合后的图像以及相应的标签(真实图像为“真”,生成图像为“假”)来训练 discriminator,如图 8-18 所示。
- (5) 在潜在空间中随机抽取新的点。
- (6) 使用这些随机向量以及全部是“真实图像”的标签来训练gan。这会更新生成器的权重(只更新生成器的权重,因为判别器在 gan中被冻结),其更新方向是使得判别器能够将生成图像预测为“真实图像”。这个过程是训练生成器去欺骗判别器。
- '''
- import os
- import keras
- from keras.preprocessing import image
-
- (x_train, y_train), (_, _) = keras.datasets.cifar10.load_data()
- x_train = x_train[y_train.flatten() == 6]#选择青蛙图像(类别编号为 6)
- print(x_train.shape)#(5000, 32, 32, 3)
- x_train = x_train.reshape(
- (x_train.shape[0],) +
- (height, width, channels)).astype('float32') / 255.#数据标准化
- iterations=10000
- batch_size=20
- save_dir='datasets/gan_output'
- start=0#记录当前批处理的位置
- for step in range(iterations):
- random_latent_vectors=np.random.normal(size=(batch_size,latent_dim))#潜在空间中采样随机点
- generated_images=generator.predict(random_latent_vectors)#利用生成器解码为虚假图像
- stop=start+batch_size
- real_images=x_train[start:stop]#
- combined_images=np.concatenate([generated_images,real_images])#拼接,默认0轴(纵向)
- labels=np.concatenate([np.ones((batch_size,1)),np.zeros((batch_size,1))])#列向量,1表示生成的图像,0表示真实的图像
- labels+=0.05*np.random.random(labels.shape)#向标签中添加随机噪声
- d_loss=discriminator.train_on_batch(combined_images,labels)#返回判别器损失:使用的是二进制交叉熵
- random_latent_vectors=np.random.normal(size=(batch_size,latent_dim))
- misleading_targets=np.zeros((batch_size,1))
- a_loss=gan.train_on_batch(#通过gan模型训练生成器
- random_latent_vectors,
- misleading_targets#冻结判别器权重(置0)
- )
- start+=batch_size
- if start>len(x_train)-batch_size:
- start=0
- if step%100==0:#每100步保存并绘图
- gan.save_weights('gan.h5')#保存模型权重
- print('discriminator loss:', d_loss)
- print('adversarial loss',a_loss)
- img = image.array_to_img(generated_images[0] * 255., scale=False)#转换成图像并保存
- img.save(os.path.join(save_dir, 'generated_frog' + str(step) + '.png'))
- img=image.array_to_img(real_images[0]*255.,scale=False)
- img.save(os.path.join(save_dir,'real_frog'+str(step)+'.png'))
训练时你可能会看到,对抗损失开始大幅增加,而判别损失则趋向于零,即判别器最终支配了生成器。如果出现了这种情况,你可以尝试减小判别器的学习率,并增大判别器的 dropout 比率。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。