我们先以一个非常简单的例子来介绍GAN的基本实现。假设我们的真实样本就是满足正态分布大小为200的向量,我们希望通过GAN,让我们的生成器可以生成与真实样本分布类似的样本。我们定义真实样本的生成函数,可以根据batch_size生成指定个数的真实样本,样本分布满足正态分布,均值为mu,标准差为sigma。相关代码在GitHub的code/keras-gan.py。
代码如下:
def x_sample(size=200,batch_size=32): x=[] for _ in range(batch_size): x.append(np.random.normal(mu, sigma, size)) return np.array(x)
定义噪声生成函数,生成满足-1~1均匀分布的噪声:
def z_sample(size=200,batch_size=32): z=[] for _ in range(batch_size): z.append(np.random.uniform(-1, 1, size)) return np.array(z)
1.Generator
我们创建Generator,架构如图13-7所示。主要参数包括:
·输入层大小为200。
·两个全连接层,结点数分别为256和200,激活函数均为relu。
·输出层大小为200。
图13-7 GAN的Generator
代码如下:
def generator_model(): model = Sequential() model.add(Dense(input_dim=200, units=256)) model.add(Activation('relu')) model.add(Dense(200)) model.add(Activation('sigmoid')) plot_model(model, show_shapes=True, to_file='gan/keras-gan-generator_model.png') return model
2.Discriminator
我们创建Discriminator,架构如图13-8所示。主要参数包括:
·输入层大小为200。
·结点数分别为256和1的全连接。
·使用激活函数sigmoid输出一维的分类概率。
图13-8 GAN的Discriminator
代码如下:
def discriminator_model(): model = Sequential() model.add(Reshape((200,), input_shape=(200,))) model.add(Dense(units=256)) model.add(Activation('relu')) model.add(Dense(1)) model.add(Activation('sigmoid')) plot_model(model, show_shapes=True, to_file='gan/keras-gan-discriminator_model.png') return model
3.对抗模型
GAN的对抗模型把Generator和Discriminator连接即可,不过需要将Discriminator参数设置为只允许手工更新,只有当设置trainable为True时才根据训练结果自动更新参数。完整的对抗模型结构如图13-9所示。由于Generator和Discriminator相连,且Discriminator参数不可更新,所以在使用对抗模型训练时,Generator被迫更新自己的参数适应Discriminator,以使整体的损失函数下降,正是这一过程让Generator可以从Discriminator得到反馈,生成更逼真的图像,代码如下:
图13-9 GAN的对抗模型
def generator_containing_discriminator(g, d): model = Sequential() model.add(g) d.trainable = False model.add(d) return model
4.训练过程
GAN的训练过程分为两步:第一步,生成一个大小为(batch_size,200)的在-1~1之间平均分布的噪声,使用Generator生成样本,然后和同样大小的真实样本合并,分别标记为0和1,对Discriminator进行训练。这个过程中Discriminator的trainable状态为True,训练过程会更新其参数。
代码如下:
noise=z_sample(batch_size=batch_size) image_batch=x_sample(batch_size=batch_size) generated_images = g.predict(noise, verbose=0) x= np.concatenate((image_batch, generated_images)) y=[1]*batch_size+[0]*batch_size d_loss = d.train_on_batch(x, y) print("d_loss : %f" % (d_loss))
第二步,生成一个大小为(batch_size,200)的在-1~1之间平均分布的噪声,使用Generator生成图像样本,标记为1,欺骗Discriminator,这个过程针对对抗模型进行训练。这个过程中Discriminator的trainable状态为False,训练过程不会更新其参数。训练完成后将重新将Discriminator的trainable状态设置为True,代码如下:
noise = z_sample(batch_size=batch_size) d.trainable = False g_loss = d_on_g.train_on_batch(noise, [1]*batch_size) d.trainable = True print("g_loss : %f" % (g_loss))
5.训练结果
我们的真实样本分布如图13-10所示,是典型的正态分布;我们的噪声分布如图13-11所示,满足-1~1的均匀分布。
图13-10 GAN示例中真实样本的分布
图13-11 GAN示例中噪声的样本分布
经过500轮训练后,如图13-12所示,生成的样本分布基本接近我们的真实样本了。
Ian Goodfellow在他的论文 [1] 中也形象了描绘了这一过程,如图13-13所示。
图13-12 GAN示例中经过500轮训练后生成的样本分布
图13-13 Ian Goodfellow论文中提到的GAN样本生成过程
[1] https://arxiv.org/pdf/1406.2661.pdf