生成对抗网络(GANs)是深度学习研究和开发中最活跃的领域之一,因为它们具有不可思议的生成合成结果的能力。在这个博客中,我们将通过一个具体的例子来构建基础的GANs。
GANs是深度学习研究和开发最活跃的领域,因为它们具有不可思议的生成合成结果的能力。在这篇文章中,我们将会搭建一个简单的GANs。接下来会从以下几个方面进行讲解:
- GAN工作的基本思想
- 实现一个基于GAN模型,从一个简单的分布中产生数据
- 可视化和分析GAN的不同方面,以更好地理解幕后发生的事情。
生成对抗网络(GAN)
GAN的基本思想很简单。它的核心是,两个具有竞争目标的代理工作在相反的目标上。这种相对简单的设置导致两个代理想出越来越复杂的方法来相互欺骗。这种情况可以在博弈论中建模为极大极小对策。
让我们举一个伪造货币的例子(危险)。在这个过程中,我们可以想象两种代理:罪犯和警察。让我们看看他们相互竞争的目标:
- 罪犯目标:罪犯的主要目标是想出复杂的伪造货币的方法,以让警察无法分辨假币和真币。
- 警察的目标:警察的主要目标是想出复杂的方法分辨真币和假币。
在这个过程中,警察发展了越来越多精细的技术来检测假币,罪犯发展了越来越精细的技术来制作假币,这就是对抗过程。
GAN利用对抗过程来训练两个神经网络,这两个网络相互竞争直到达到理想的平衡。在这种情况下,我们有一个发生器网络G(Z),它接收输入的随机噪音,并试图生成非常接近我们拥有的数据集的数据。另一种网络称为鉴别器网络D(X),它接收输入生成的数据,并试图鉴别生成的数据和真实数据。这个网络的核心实现了二分类,并输出输入数据实际上来自真实数据集(而不是合成的或假的数据)的概率。
通常理想的平衡点是生成器可以建模真实数据,判别器输出概率是0.5,因为生成数据和真是数据一样–判别器不确定数据是来源于真是数据还是生成器产生的数据。
你可能感兴趣为什么会需要这么复杂的学习过程?这种学习的优势是什么?这背后的直觉以及所有的生成方法都遵循了理查德·费曼的一句名言:
What I cannot create, I do not understand.
这是有意义的,因为如果我们能够从一个模型中生成真是数据分布,那意味着我们知道了模型的一切。大部分时间,这些真实的分布包含百万张图片并且我们可以使用一个拥有上千个参数的模型生成它们,这些参数捕捉到了给定图像的本质。
实现GAN
在这节,我们将会生成一个非常简单的数据分布,并尝试学习一个生成器。这节会分成3个部分。第一部分我们会写一个基本的函数来产生二次型分布(作为真实的数据分布)。第二部分,我们写代码实现生成器和判别器网络。然后我们会使用数据和网络实现训练。
这个实现的目标是学习一个新函数,它可以从与训练数据相同的分布中生成数据。训练的预期是我们的生成器网络开始产生符合二次分布的数据。虽然我们只是实现了一个简单的例子,但是针对更复杂的数据也是如此。
生成训练数据
通过使用numpy库实现生成真实数据的代码。
import numpy as np
def get_y(x):
return 10+x*x
def sample_data(n=10000,scale=100):
data=[]
x=scale*(np.random.random_sample((n,)),-0.5)
for i in range(n):
yi=get(x[i])
data.append(x[i],y[i])
return np.array(data)
产生的数据非常简单,曲线如下所示:
生成器和判别器网络实现
生成器网络结构:
def generator(Z, hsize=[16,16], reuse=False):
with tf.variable_scope("GAN/Generator",reuse=reuse):
h1=tf.layers.dense(Z,hsize[0],activation=tf.nn.leaky_relu)
h2=tf.layers.dense(h1,hsize[1],activation=tf.nn.leaky_relu)
out=tf.layers.dense(h2,2)
判别器结构:
def discriminator(X,hsize=[16,16],reuse=False):
with tf.variable_scope("GAN/Discriminator",reuse=reuse):
h1=tf.layers.dense(X,hsize[0],activation=tf.nn.leaky_relu)
h2=tf.layers.dense(h1,hsize[1],activation=tf.nn.leaky_relu)
h3=tf.layers.dense(h2,2)
out=tf.layers.dense(h3,1)
return out,h3
对抗训练
为了便于训练,我们分别为真实样本和随机噪声样本定义了以下占位符X和Z:
X=tf.placeholder(tf.float32,[None,2])
Z=tf.placeholder(tf.float32,[None,2])
我们还需要创建从生成器网络生成样本的图,并将真实和生成的样本提供给鉴别器网络。这是通过使用上面定义的函数和占位符来实现的:
G_sample=generator(Z)
r_logits,r_rep=discriminator(X)
f_logits,g_rep=discriminator(G_sample,reuse=True)
使用生成数据和真实数据的对数,我们定义了生成器和鉴别器网络的损失函数如下:
disc_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=r_logits,
labels=tf.ones_like(r_logits)) +
tf.nn.sigmoid_cross_entropy_with_logits(logits=f_logits,
labels=tf.zeros_like(f_logits)))
gen_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=f_logits,labels=tf.ones_like(f_logits)))
接下来,我们使用上面定义的损失函数和在生成器和鉴别器函数中定义的层的范围来定义两个网络的优化器。我们对这两个网络使用RMSProp优化器,学习率为0.001。使用scope,我们只获取给定网络的权值/变量。
gen_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,scope="GAN/Generator")
disc_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,scope="GAN/Discriminator")
gen_step = tf.train.RMSPropOptimizer(learning_rate=0.001).minimize(gen_loss,var_list = gen_vars) # G Train step
disc_step = tf.train.RMSPropOptimizer(learning_rate=0.001).minimize(disc_loss,var_list = disc_vars) # D Train step
然后我们开始训练两个网络直到指定的steps:
for i in range(10001):
X_batch=sample_data(n=batch_size)
Z_batch=sample_Z(batch_size,2)
_,dloss=sess.run([disc_step,disc_loss],feed_dict={X:X_batch,Z:Z_batch})
_,gloss=sess.run([gen_step,gen_loss],feed_dict={Z:Z_batch})
GAN网络分析
可视化训练loss
为了更好地理解这个过程中发生了什么,我们可以在每10次迭代后绘制训练损失。从下面的图表中,我们可以看到损失的变化是如何逐渐减少的,并且在训练结束时,损失几乎是恒定的。判别器和生成器的loss的微不足道的变化表明达到平衡。
训练过程中可视化样本
我们还可以在每1000次训练迭代后绘制真实的和生成的样本。这些图显示了生成器网络是如何从输入和数据集向量空间之间的随机初始映射开始,然后逐渐向类似真实数据集样本的方向发展的。如您所见,“假的”样本看起来越来越像“真实的”数据分布。
探讨
我们实现了一个用于从非常简单的数据分布生成数据的概念验证GAN模型。作为对好奇的读者的练习,我们建议修改上面的代码来做以下事情:
- 可视化判别器更新前后。
- 改变网络层的激活函数,查看训练过程的变化。
- 添加网络层和不同类型网络层,查看训练时间以及训练稳定的影响。
- 修改生成数据的代码,使其包含来自两种不同曲线的数据。
- 修改上面的代码使其可以用在更复杂的数据上。
原文链接: Building a simple Generative Adversarial Network (GAN) using TensorFlow
代码链接: basic-gans