本文概述
在本教程中, 你将学习什么是通用对抗网络(GAN), 而无需深入探讨数学细节。之后, 你将学习如何编写可创建数字的简单GAN!
比喻
理解GAN的最简单方法是通过简单的类比:
假设有一家商店从顾客那里购买某些种类的葡萄酒, 然后他们将其转售。
但是, 有些邪恶的顾客为了赚钱而出售假酒。在这种情况下, 店主必须能够区分假冒和正宗的葡萄酒。
你可以想象, 最初, 伪造者在尝试出售假酒时可能会犯很多错误, 而且店主很容易识别出该酒不是真品。由于这些失败, 伪造者将继续尝试不同的技术来模拟正宗的葡萄酒, 并且其中一些最终将获得成功。现在, 伪造者知道某些技术已经超出了店主的控制, 他可以基于这些技术开始进一步改进假酒。
同时, 店主可能会从其他店主或葡萄酒专家那里得到一些反馈, 称她所拥有的某些葡萄酒不是原装葡萄酒。这意味着店主将不得不改进她确定葡萄酒是假酒还是正宗酒的方式。伪造者的目的是制造与原汁原味没有区别的葡萄酒, 而店主的目标是准确判断葡萄酒是否为真酒。
来回竞争是GAN的主要思想。
生成对抗网络的组成部分
使用上面的示例, 我们可以提出GAN的体系结构。
GAN中有两个主要组件:生成器和鉴别器。该示例中的店主称为鉴别器网络, 通常是卷积神经网络(因为GAN主要用于图像任务), 它分配图像真实的概率。
伪造者称为生成网络, 通常也是卷积神经网络(具有反卷积层)。该网络获取一些噪声矢量并输出图像。在训练生成网络时, 它会学习要改进/更改图像的哪些区域, 以便区分器将很难将其生成的图像与真实图像区分开。
当判别网络试图确定真实图像与伪图像之间的差异时, 生成网络将不断产生外观上与真实图像更接近的图像。最终目标是建立一个生成网络, 该网络可以生成与真实图像无法区分的图像。
Keras的简单生成对抗网络
既然你了解了GAN是什么以及它们的主要组成部分, 我们现在就可以开始编写一个非常简单的代码了。你将使用Keras, 如果你不熟悉此Python库, 则应先阅读本教程, 然后再继续。本教程基于此处开发的GAN。
你需要做的第一件事是通过pip安装以下软件包
- 辛苦
- matplotlib
- 张量流
- tqdm
你将使用matplotlib进行绘图, 将tensorflow用作Keras后端库, 并使用tqdm显示每个时期(迭代)的奇特进度条。
下一步是创建Python脚本。在此脚本中, 你首先需要导入将要使用的所有模块和功能。将给出使用它们时的解释。
import os
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm
from keras.layers import Input
from keras.models import Model, Sequential
from keras.layers.core import Dense, Dropout
from keras.layers.advanced_activations import LeakyReLU
from keras.datasets import mnist
from keras.optimizers import Adam
from keras import initializers
现在, 你要设置一些变量:
# Let Keras know that we are using tensorflow as our backend engine
os.environ["KERAS_BACKEND"] = "tensorflow"
# To make sure that we can reproduce the experiment and get the same results
np.random.seed(10)
# The dimension of our random noise vector.
random_dim = 100
在开始构建鉴别器和生成器之前, 你应该首先收集和预处理数据。你将使用流行的MNIST数据集, 该数据集包含一组从0到9的单个数字的图像。
def load_minst_data():
# load the data
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# normalize our inputs to be in the range[-1, 1]
x_train = (x_train.astype(np.float32) - 127.5)/127.5
# convert x_train with a shape of (60000, 28, 28) to (60000, 784) so we have
# 784 columns per row
x_train = x_train.reshape(60000, 784)
return (x_train, y_train, x_test, y_test)
请注意, mnist.load_data()是Keras的一部分, 可让你轻松地将MNIST数据集导入到工作区中。
现在, 你可以创建生成器和鉴别器网络。你将对两个网络都使用Adam优化器。对于生成器和鉴别器, 你将创建一个具有三个隐藏层的神经网络, 其激活函数为Leaky Relu。你还应该为鉴别器添加滤除层, 以提高其在看不见图像上的鲁棒性。
# You will use the Adam optimizer
def get_optimizer():
return Adam(lr=0.0002, beta_1=0.5)
def get_generator(optimizer):
generator = Sequential()
generator.add(Dense(256, input_dim=random_dim, kernel_initializer=initializers.RandomNormal(stddev=0.02)))
generator.add(LeakyReLU(0.2))
generator.add(Dense(512))
generator.add(LeakyReLU(0.2))
generator.add(Dense(1024))
generator.add(LeakyReLU(0.2))
generator.add(Dense(784, activation='tanh'))
generator.compile(loss='binary_crossentropy', optimizer=optimizer)
return generator
def get_discriminator(optimizer):
discriminator = Sequential()
discriminator.add(Dense(1024, input_dim=784, kernel_initializer=initializers.RandomNormal(stddev=0.02)))
discriminator.add(LeakyReLU(0.2))
discriminator.add(Dropout(0.3))
discriminator.add(Dense(512))
discriminator.add(LeakyReLU(0.2))
discriminator.add(Dropout(0.3))
discriminator.add(Dense(256))
discriminator.add(LeakyReLU(0.2))
discriminator.add(Dropout(0.3))
discriminator.add(Dense(1, activation='sigmoid'))
discriminator.compile(loss='binary_crossentropy', optimizer=optimizer)
return discriminator
现在是时候将生成器和鉴别器放在一起了!
def get_gan_network(discriminator, random_dim, generator, optimizer):
# We initially set trainable to False since we only want to train either the
# generator or discriminator at a time
discriminator.trainable = False
# gan input (noise) will be 100-dimensional vectors
gan_input = Input(shape=(random_dim, ))
# the output of the generator (an image)
x = generator(gan_input)
# get the output of the discriminator (probability if the image is real or not)
gan_output = discriminator(x)
gan = Model(inputs=gan_input, outputs=gan_output)
gan.compile(loss='binary_crossentropy', optimizer=optimizer)
return gan
为了完整起见, 你可以创建一个函数, 该函数每20个时间段保存一次生成的图像。由于这不是本课程的核心, 因此你不需要完全了解该功能。
# Create a wall of generated MNIST images
def plot_generated_images(epoch, generator, examples=100, dim=(10, 10), figsize=(10, 10)):
noise = np.random.normal(0, 1, size=[examples, random_dim])
generated_images = generator.predict(noise)
generated_images = generated_images.reshape(examples, 28, 28)
plt.figure(figsize=figsize)
for i in range(generated_images.shape[0]):
plt.subplot(dim[0], dim[1], i+1)
plt.imshow(generated_images[i], interpolation='nearest', cmap='gray_r')
plt.axis('off')
plt.tight_layout()
plt.savefig('gan_generated_image_epoch_%d.png' % epoch)
现在, 你已对网络的大部分进行了编码。剩下的就是训练该网络并查看你创建的图像。
def train(epochs=1, batch_size=128):
# Get the training and testing data
x_train, y_train, x_test, y_test = load_minst_data()
# Split the training data into batches of size 128
batch_count = x_train.shape[0] / batch_size
# Build our GAN netowrk
adam = get_optimizer()
generator = get_generator(adam)
discriminator = get_discriminator(adam)
gan = get_gan_network(discriminator, random_dim, generator, adam)
for e in xrange(1, epochs+1):
print '-'*15, 'Epoch %d' % e, '-'*15
for _ in tqdm(xrange(batch_count)):
# Get a random set of input noise and images
noise = np.random.normal(0, 1, size=[batch_size, random_dim])
image_batch = x_train[np.random.randint(0, x_train.shape[0], size=batch_size)]
# Generate fake MNIST images
generated_images = generator.predict(noise)
X = np.concatenate([image_batch, generated_images])
# Labels for generated and real data
y_dis = np.zeros(2*batch_size)
# One-sided label smoothing
y_dis[:batch_size] = 0.9
# Train discriminator
discriminator.trainable = True
discriminator.train_on_batch(X, y_dis)
# Train generator
noise = np.random.normal(0, 1, size=[batch_size, random_dim])
y_gen = np.ones(batch_size)
discriminator.trainable = False
gan.train_on_batch(noise, y_gen)
if e == 1 or e % 20 == 0:
plot_generated_images(e, generator)
if __name__ == '__main__':
train(400, 128)
在训练了400个时期之后, 你可以查看生成的图像。查看第一个历元之后产生的图像, 可以看到它没有任何真实的结构, 看着40个历元之后的图像, 数字开始成形, 最后, 即使经过400个历元之后的图像也显示清晰的数字, 即使一对夫妇仍然无法辨认。
1个纪元后的结果 | 40个纪元后的结果 | 400个时期后的结果 |
---|---|---|
! |
此代码在CPU上每个时期大约需要2分钟, 这就是选择此代码的主要原因。你可以通过使用更多的纪元并为生成器和鉴别器添加更多(和不同的)层来进行试验。但是, 在使用更复杂和更深的体系结构时, 如果仅使用CPU, 则运行时间也会增加。但是, 请不要阻止你进行实验!
总结
恭喜, 你已经完成了本教程的结尾, 在该教程中, 你以直观的方式学习了生成对抗网络(GAN)的基础知识!同样, 你在Keras库的帮助下实现了第一个模型。如果你想了解有关使用Python进行深度学习的更多信息, 请考虑参加srcmini的Python深度学习课程。
评论前必须登录!
注册