遗传算法与深度学习实战(32)——生成对抗网络详解与实现
遗传算法与深度学习实战(32)——生成对抗网络详解与实现
- 0. 前言
- 1. 生成对抗网络
- 2. 构建卷积生成对抗网络
- 小结
- 系列链接
0. 前言
生成对抗网络 (Generative Adversarial Networks
, GAN
) 是一种由两个相互竞争的神经网络组成的深度学习模型,它由一个生成网络和一个判别网络组成,通过彼此之间的博弈来提高生成网络的性能。生成对抗网络使用神经网络生成与原始图像集非常相似的新图像,它在图像生成中应用广泛,且 GAN
的相关研究正在迅速发展,以生成与真实图像难以区分的逼真图像。在本节中,我们将学习 GAN
网络的原理并使用 Keras
实现 GAN
。
1. 生成对抗网络
我们经常使用艺术伪造者和艺术鉴别者的类比来解释生成对抗网络 (Generative Adversarial Networks
, GAN
) ,艺术伪造者试图制造出可以欺骗艺术鉴别者赝品,鉴别者确定艺术品的真实性,并防止赝品流入市场。鉴别者通过评估真实艺术品和伪造品学习,伪造者对艺术品进行模仿,并且只能从鉴别者的反馈中学习。
而生成对抗网络包含两个网络:生成网络( Generator
,也称生成器)和判别网络( discriminator
,也称判别器)。在 GAN
网络训练过程中,需要有一个合理的图像样本数据集,生成网络从图像样本中学习图像表示,然后生成与图像样本相似的图像。判别网络接收(由生成网络)生成的图像和原始图像样本作为输入,并将图像分类为原始(真实)图像或生成(伪造)图像。
生成网络的目标是生成逼真的伪造图像骗过判别网络,判别网络的目标是将生成的图像分类为伪造图像,将原始图像样本分类为真实图像。本质上,GAN
中的对抗表示两个网络的相反性质,生成网络生成图像来欺骗判别网络,判别网络通过判别图像是生成图像还是原始图像来对输入图像进行分类:
在上图中,生成网络根据输入随机噪声生成图像,判别网络接收生成网络生成的图像,并将它们与真实图像样本进行比较,以判断生成的图像是真实的还是伪造的。生成网络尝试生成尽可能逼真的图像,而判别网络尝试判定生成网络生成图像的真实性,从而学习生成尽可能逼真的图像。
GAN
的关键思想是生成网络和判别网络之间的竞争和动态平衡,通过不断的训练和迭代,生成网络和判别网络会逐渐提高性能,生成网络能够生成更加逼真的样本,而判别网络则能够更准确地区分真实和伪造的样本。
通常,生成网络和判别网络交替训练,将生成网络和判别网络视为博弈双方,并通过两者之间的对抗来推动模型性能的提升,直到生成网络生成的样本能够以假乱真,判别网络无法分辨真实样本和生成样本之间的差异:
- 生成网络的训练过程:冻结判别网络权重,生成网络以噪声
z
作为输入,通过最小化生成网络与真实数据之间的差异来学习如何生成更好的样本,以便判别网络将图像分类为真实图像 - 判别网络的训练过程:冻结生成网络权重,判别网络通过最小化真实样本和假样本之间的分类误差来更新判别网络,区分真实样本和生成样本,将生成网络生成的图像分类为伪造图像
重复训练生成网络与判别网络,直到达到平衡,当判别网络能够很好地检测到生成的图像时,生成网络对应的损失比判别网络对应的损失要高得多。通过不断训练生成网络和判别网络,直到生成网络可以生成逼真图像,而判别网络无法区分真实图像和生成图像。
2. 构建卷积生成对抗网络
在本节中,我们将介绍深度卷积生成对抗网络 (Deep Convolutional Generative Adversarial Networks
, DCGAN
),探讨如何构建和训练 GAN
。DCGAN
是基于生成对抗网络 (Convolutional Generative Adversarial Networks
, GAN
) 的深度学习模型,相比传统的 GAN
模型,DCGAN
通过引入卷积神经网络 (Convolutional Neural Networks
, CNN
) 架构来提升生成网络和判别网络的性能。DCGAN
中的生成网络和判别网络都是使用卷积层和反卷积层构建的深度神经网络。
(1) 首先,导入所需库和数据集:
from tensorflow.keras.datasets import mnist as data
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Input, Dense, Activation, Flatten, Reshape, Dropout, ZeroPadding2D
from tensorflow.keras.layers import Conv2D, Conv2DTranspose, UpSampling2D, Convolution2D
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import LeakyReLU
from tensorflow.keras.optimizers import Adam, RMSprop
import numpy as np
import matplotlib.pyplot as plt
from livelossplot import PlotLosses
from tqdm import tqdm_notebook
import random
from tqdm.notebook import tqdm as tqdm
(train_images, train_labels), (test_images, test_labels) = data.load_data()
# Scale Dataset -1 to +1
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype("float32") / 127.5 - 1
(2) 本节使用 MNIST
手写数字数据集训练 GAN
。但是,为了减少训练时间,我们使用 extract()
函数从数据集中提取一个图像类别,extract()
函数以批图像和标签以及要提取的类号码作为输入。首先提取与标签等于类号码的索引,然后,索引列表用于从匹配类别的原始数据集中隔离子集图像,最后得到仅包含一个图像类别的数据集 train_images
。调用 extract()
函数,使用 5
代表数据集中的数字 5
:
def extract(images, labels, class_):
idx = labels == class_
print(idx)
imgs = images[idx]
print(imgs.shape)
return imgs
train_images = extract(train_images, train_labels, 5)
(3) 接下来,设置生成器和鉴别器的基本超参数和优化器。latent_dim
参数定义了输入到生成器中的潜空间大小。接下来,为生成器和鉴别器创建不同的优化器,以尝试平衡训练。之后,计算卷积常数用于构建网络,并提取通道数和图像形状:
latent_dim = 100
g_optimizer = Adam(0.0002, 0.5)
d_optimizer = RMSprop(.00001)
cs = int(train_images.shape[1] / 4)
print(train_images.shape)
channels = train_images.shape[3]
img_shape = (train_images.shape[1], train_images.shape[2], channels)
(4) GAN
是通过将鉴别器和生成器分解成单独的模型并组合起来构建的,这与构建自编码器 (AutoEncoder, AE) 类似。生成器架构类似于 AE
中的解码器,使用 build_generator()
函数创建卷积网络,从随机潜空间中生成图像:
def build_generator():
model = Sequential()
model.add(Dense(128 * cs * cs, activation="relu", input_dim=latent_dim))
model.add(Reshape((cs, cs, 128)))
model.add(UpSampling2D())
model.add(Conv2D(128, kernel_size=3, padding="same"))
model.add(BatchNormalization(momentum=0.8))
model.add(Activation("relu"))
model.add(UpSampling2D())
model.add(Conv2D(64, kernel_size=3, padding="same"))
model.add(BatchNormalization(momentum=0.8))
model.add(Activation("relu"))
model.add(Conv2D(channels, kernel_size=3, padding="same"))
model.add(Activation("tanh"))
model.summary(line_length = 100)
noise = Input(shape=(latent_dim,))
img = model(noise)
return Model(noise, img)
g = build_generator()
执行 build_generator()
函数后,输出模型摘要如下所示。
(5) 鉴别器的构造方式类似。模型从输入图像开始,使用大小为2的步幅来缩小图像。鉴别器的输出是一个二元值,用于将输入图像分类为假或真:
def build_discriminator():
model = Sequential()
model.add(Conv2D(32, kernel_size=3, strides=2, input_shape=img_shape, padding="same"))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.25))
model.add(Conv2D(64, kernel_size=3, strides=2, padding="same"))
model.add(ZeroPadding2D(padding=((0,1),(0,1))))
model.add(BatchNormalization(momentum=0.8))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.25))
model.add(Conv2D(128, kernel_size=3, strides=2, padding="same"))
model.add(BatchNormalization(momentum=0.8))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.25))
model.add(Conv2D(256, kernel_size=3, strides=1, padding="same"))
model.add(BatchNormalization(momentum=0.8))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
model.summary()
img = Input(shape=img_shape)
validity = model(img)
return Model(img, validity)
(6) 由于需要单独训练鉴别器和生成器,因此还要使用 d_optimizer
编译创建的模型,使用二元交叉熵损失和精度作为指标:
d = build_discriminator()
d.compile(loss='binary_crossentropy',
optimizer=d_optimizer,
metrics=['accuracy'])
(7) 使用模型生成器和鉴别器构建组合 GAN
模型。创建一个表示生成器潜空间输入的输入,然后,使用生成器创建图像生成输出 img
。之后,关闭 GAN
中的鉴别器模型的训练,生成器在 GAN
内部单独进行训练,使用 g_optimizer
作为单独的优化器,根据鉴别器输出的图像的有效性训练生成器:
z = Input(shape=(latent_dim,))
img = g(z)
# For the combined model we will only train the generator
d.trainable = False
# The discriminator takes generated images as input and determines validity
valid = d(img)
# The combined model (stacked generator and discriminator)
# Trains the generator to fool the discriminator
gan = Model(z, valid)
gan.compile(loss='binary_crossentropy', optimizer=g_optimizer)
def plot_generated(n_ex=10, dim=(1, 10), figsize=(12, 2)):
noise = np.random.normal(0, 1, size=(n_ex, latent_dim))
generated_images = g.predict(noise)
generated_images = generated_images.reshape(n_ex, img_shape[0], img_shape[1])
plt.figure(figsize=figsize)
for i in range(generated_images.shape[0]):
plt.subplot(dim[0], dim[1], i+1)
plt.imshow((1-generated_images[i])*255, interpolation='nearest', cmap='gray_r')
plt.axis('off')
plt.tight_layout()
plt.show()
(8) 由于使用单独的训练流程,所以不能简单地使用 Keras
的 model.fit()
函数,必须单独训练鉴别器和生成器。首先创建真实和伪造图像集的标签,对于真实图像标签值为 1
,对于伪造图像标签值为 0
。训练在两个循环内完成,外部循环由 epoch
控制,内部循环由计算出的批数控制,在循环内部,采样一组随机真实图像 imgs
,然后使用随机噪声生成一组伪造图像 gen_images
,然后,在鉴别器上训练并计算真实和伪造图像的损失。对于每组训练,传递了相应的标签,通过取真实和伪造图像上损失的平均值计算最终的鉴别器损失。最后,通过对生成的伪造图像传递真实图像的标签训练生成器:
EPOCHS = 10 #@param {type:"slider", min:5, max:100, step:1}
BATCH_SIZE = 256 #@param {type:"slider", min:64, max:256, step:2}
groups = { "Discriminator" : {"Real", "Fake"}, "Generator":{"Gen"}}
plotlosses = PlotLosses(groups=groups)
batches = int(train_images.shape[0] / BATCH_SIZE)
# Adversarial ground truths
valid = np.ones((BATCH_SIZE, 1))
fake = np.zeros((BATCH_SIZE, 1))
for e in range(EPOCHS):
for i in tqdm(range(batches)):
# Train Discriminator
idx = np.random.randint(0, train_images.shape[0], BATCH_SIZE)
imgs = train_images[idx]
# Sample noise and generate a batch of new images
noise = np.random.normal(0, 1, (BATCH_SIZE, latent_dim))
gen_imgs = g.predict(noise)
# Train the discriminator (real classified as ones and generated as zeros)
d_loss_real = d.train_on_batch(imgs, valid)
d_loss_fake = d.train_on_batch(gen_imgs, fake)
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
# Train Generator
g_loss = gan.train_on_batch(noise, valid)
loss = dict(Real = d_loss[0], Fake = d_loss[1], Gen = g_loss)
plotlosses.update(loss)
plotlosses.send()
plot_generated()
在一个数据类上对 GAN
进行 10
个epoch的训练的结果如下所示。除了生成的图像外,还可以看到生成器和鉴别器的训练损失。训练 GAN
的目标是最大化伪造图像的损失并最小化真实图像的损失。实质上,鉴别器需要变得更好地识别真实图像,但也需要变得更差地识别伪造图像,而生成器必须最小化创造伪造图像的损失。
通可以通过完成以下问题进一步了解 GAN
的基本原理:
- 增加或减少
BATCH_SIZE
,然后重新运行 - 增加或减少
g_optimizer
和d_optimizer
的学习率,观察修改任一优化器对GAN
训练的影响 - 不使用
extract
函数来限制数据集为一个类别,而使用完整数据集观察运行结果
小结
生成对抗网络是一种强大的深度学习模型,由生成器网络和判别器网络组成,通过彼此之间的竞争来提高性能,已经在图像生成、图像修复、图像转换和自然语言处理等领域取得了巨大的成功。其核心思想是通过生成器和判别器之间的博弈过程来实现真实样本的生成。生成器负责生成逼真的样本,而判别器则负责判断样本是真实还是伪造。通过不断的训练和迭代,生成器和判别器会相互竞争并逐渐提高性能。
系列链接
遗传算法与深度学习实战(1)——进化深度学习
遗传算法与深度学习实战(2)——生命模拟及其应用
遗传算法与深度学习实战(3)——生命模拟与进化论
遗传算法与深度学习实战(4)——遗传算法(Genetic Algorithm)详解与实现
遗传算法与深度学习实战(5)——遗传算法中常用遗传算子
遗传算法与深度学习实战(6)——遗传算法框架DEAP
遗传算法与深度学习实战(7)——DEAP框架初体验
遗传算法与深度学习实战(8)——使用遗传算法解决N皇后问题
遗传算法与深度学习实战(9)——使用遗传算法解决旅行商问题
遗传算法与深度学习实战(10)——使用遗传算法重建图像
遗传算法与深度学习实战(11)——遗传编程详解与实现
遗传算法与深度学习实战(12)——粒子群优化详解与实现
遗传算法与深度学习实战(13)——协同进化详解与实现
遗传算法与深度学习实战(14)——进化策略详解与实现
遗传算法与深度学习实战(15)——差分进化详解与实现
遗传算法与深度学习实战(16)——神经网络超参数优化
遗传算法与深度学习实战(17)——使用随机搜索自动超参数优化
遗传算法与深度学习实战(18)——使用网格搜索自动超参数优化
遗传算法与深度学习实战(19)——使用粒子群优化自动超参数优化
遗传算法与深度学习实战(20)——使用进化策略自动超参数优化
遗传算法与深度学习实战(21)——使用差分搜索自动超参数优化
遗传算法与深度学习实战(22)——使用Numpy构建神经网络
遗传算法与深度学习实战(23)——利用遗传算法优化深度学习模型
遗传算法与深度学习实战(24)——在Keras中应用神经进化优化
遗传算法与深度学习实战(25)——使用Keras构建卷积神经网络
遗传算法与深度学习实战(26)——编码卷积神经网络架构
遗传算法与深度学习实战(27)——进化卷积神经网络
遗传算法与深度学习实战(28)——卷积自编码器详解与实现
遗传算法与深度学习实战(29)——编码卷积自编码器架构
遗传算法与深度学习实战(30)——使用遗传算法优化自编码器模型
遗传算法与深度学习实战(31)——变分自编码器详解与实现