GAN with Tensorflow:生成对抗网络的基础知识
机器学习一般分为三类:监督学习,无监督学习和强化学习。
机器学习的类别
理解对象是监督/无监督学习的最终目标。我们可以使用训练过的基于数据的判别模型(Discriminative Model)对图像进行分类。我们还可以使用训练过的生成模型(Generative Model)创建样本图像。
Ian Goodfellow引入了GAN(生成对抗性网络)作为一种理解数据的新方法。
什么是GAN?
GAN由一个generator和一个discriminator组成,两者互为竞争对手,性能逐渐提高。
伊恩·古德费罗(Ian Goodfellow)将上述过程比作伪钞制造者generator)和警察(discriminator)。假钞制造者试图欺骗警察,另一方面,警察试图将这些假钞分为真钞和假钞。在这场比赛中,双方都培养了能力。最终,警察无法区分真钞和假钞。
GAN的概念
MNIST
样本mnist(数字1)矩阵
MNIST机器学习数据集由手写数字图像(矩阵)组成。在本教程中,我们将生成手写数字的示例图像。如前所述,我们需要知道组成数字图像的像素值的分布,以便生成数字图像。
G的分布(绿色)和 D的决定边界(蓝色)的演变
上图显示了G如何知道真实的分布(黑点)。当重复学习时,G的分布与真实的分布相吻合。最终完全匹配时,D不能区分两个数字图像(P = 0.5)。
对于学习,它需要训练网络(generators和discriminators)和DB。
Generators
2层神经网络
我们的generators很简单。它由两个全连接层组成。输入层节点个数与“n_noise”相同。输出层节点个数与mnist图像的分辨率“n_input”相同。2个全连接层网络有4个可训练变量:两个权值变量G_W1,G_W2和两个偏差变量G_b1, G_b2。隐藏层使用“relu”函数作为激活函数。mnist图像的像素范围为[0,1]。因此我们使用sigmoid激活函数作为输出层,将结果归一化为[0,1]。实现的Python代码如下(generators.py):
import tensorflow as tf class Generator: def __init__(self, n_input, n_noise): self.n_hidden = 256 self.n_input = n_input self.n_noise = n_noise def net(self, noise_z): with tf.variable_scope("generator"): G_W1 = tf.get_variable('G_W1', [self.n_noise, self.n_hidden], initializer = tf.random_normal_initializer(stddev=0.01)) G_b1 = tf.get_variable('G_b1', [self.n_hidden], initializer = tf.constant_initializer(0)) G_W2 = tf.get_variable('G_W2', [self.n_hidden, self.n_input], initializer = tf.random_normal_initializer(stddev=0.01)) G_b2 = tf.get_variable('G_b2', [self.n_input], initializer = tf.constant_initializer(0)) hidden = tf.nn.relu( tf.matmul(noise_z, G_W1) + G_b1) output = tf.nn.sigmoid( tf.matmul(hidden, G_W2) + G_b2) return output
Discriminator
我们的Discriminator也由两个全连接层组成。当然,输入节点的数量等于n_input。Discriminator输出为true/false。输入节点的个数是1。输出层使用sigmoid激活函数将结果归一化为[0,1]。如果确定输入为false,则输出接近于零,反之亦然。实现的Python代码如下(discriminators.py):
import tensorflow as tf class Discriminator: def __init__(self, n_input, n_noise): self.n_hidden = 256 self.n_input = n_input self.n_noise = n_noise def net(self, inputs, reuse = False): with tf.variable_scope("discriminator") as scope: if reuse: scope.reuse_variables() D_W1 = tf.get_variable('D_W1', [self.n_input, self.n_hidden], initializer = tf.random_normal_initializer(stddev=0.01)) D_b1 = tf.get_variable('D_b1', [self.n_hidden], initializer = tf.constant_initializer(0)) D_W2 = tf.get_variable('D_W2', [self.n_hidden, 1], initializer = tf.random_normal_initializer(stddev=0.01)) D_b2 = tf.get_variable('D_b2', [1], initializer = tf.constant_initializer(0)) hidden = tf.nn.relu( tf.matmul(inputs, D_W1) + D_b1) output = tf.nn.sigmoid( tf.matmul(hidden, D_W2) + D_b2) return output
训练模型
我们做了generator和discriminator。现在,我们需要训练DB(mnist数据集)。幸运的是,tensorflow提供了它。
import tensorflow as tf import matplotlib.pyplot as plt import numpy as np import os from generators import Generator from discriminators import Discriminator from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("./mnist/data/", one_hot=True) total_epoch = 100 batch_size = 100 learning_rate = 0.0002 n_input = 28 * 28 n_noise = 128 def get_noise(batch_size, n_noise): return np.random.normal(size=(batch_size, n_noise))
首先导入Python库:tensorflow,numpy,os,plt(用于保存结果图像)。并导入generator和discriminator类。
您只需通过代码行下载和存储mnist数据集即可。
mnist = input_data.read_data_sets("./mnist/data/", one_hot=True)
定义一些参数:total_epoch,batch_size,learning_rate
n_input是28 * 28,等于mnist图像的大小。
n_noise是潜在向量的长度,它定义为128
我们还定义了“get_noise”函数,它生成一个随机向量数组。
X = tf.placeholder(tf.float32, [None, n_input]) Z = tf.placeholder(tf.float32, [None, n_noise]) G = Generator(n_input, n_noise) G_out = G.net(Z) D = Discriminator(n_input, n_noise) D_gene = D.net(G_out, reuse = False) D_real = D.net(X, reuse = True)
之后我们定义了一个generator和discriminator。G.net(Z)从随机向量Z返回生成的样本(假样本).D.net()测量样本的真实程度。 D_gene表示假样本的实际得分,D_real表示mnist数据集中实际样本的实际得分。
loss_D = tf.reduce_mean(tf.log(D_real) + tf.log(1 - D_gene)) loss_G = tf.reduce_mean(tf.log(D_gene)) D_var_list = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='discriminator') G_var_list = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='generator') train_D = tf.train.AdamOptimizer(learning_rate).minimize(-loss_D, var_list=D_var_list) train_G = tf.train.AdamOptimizer(learning_rate).minimize(-loss_G, var_list=G_var_list)
我们希望D在真实图像时返回高分,并在假图像时返回低分。另一方面,G应该创造一个虚假的形象,欺骗D获得高分。这两个网络发生冲突。Ian Goodfellow介绍下面的函数V(G,D)来表示D和G之间的博弈。
下面的Python代码代表了上面的等式:
loss_D = tf.reduce_mean(tf.log(D_real) + tf.log(1 — D_gene))
我们训练D使其最大化。我们使用AdamOptimizer最小化函数,我们训练D最大化“-loss_D”
在上面的等式中,我们应该训练G以最小化log(1-D(G(z))。在学习的早期,log的梯度(1-D(G(z))很小并且它被非常缓慢地优化。 ,我们训练G以最大化log D(G(z))。参见下图。
在上面的方程中,我们应该训练G减少log(1 − D(G(z))。在学习初期,梯度的log(1−D(G(z))很小,优化的非常缓慢。我们训练G使log D(G(z))最大化。参考下图
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True)) sess.run(tf.global_variables_initializer()) total_batch = int(mnist.train.num_examples/batch_size) loss_val_D, loss_val_G = 0, 0 for epoch in range(total_epoch): for i in range(total_batch): batch_xs, batch_ys = mnist.train.next_batch(batch_size) noise = get_noise(batch_size, n_noise) _, loss_val_D = sess.run([train_D, loss_D], feed_dict={X: batch_xs, Z: noise}) _, loss_val_G = sess.run([train_G, loss_G], feed_dict={Z: noise}) print('Epoch:', '%04d' % epoch, 'D loss: {:.4}'.format(loss_val_D), 'G loss: {:.4}'.format(loss_val_G)) if epoch == 0 or (epoch + 1) % 10 == 0: sample_size = 10 noise = get_noise(sample_size, n_noise) samples = sess.run(G_out, feed_dict={Z: noise}) fig, ax = plt.subplots(1, sample_size, figsize=(sample_size, 1)) for i in range(sample_size): ax[i].set_axis_off() ax[i].imshow(np.reshape(samples[i], (28, 28))) plt.savefig('samples/{}.png'.format(str(epoch).zfill(3)), bbox_inches='tight') plt.close(fig) print('Complite')
使用sess.run(tf.global_variables_initializer())初始化所有变量。
我们可以通过sess.run([train_D])优化D,以便为输入提供数据。 train_D需要loss_D,它也需要D_gene,D_real。 D_gene取G_out, G_out取Z。D_real也取X。所以我们输入X和Z来执行sess.run([train_D,loss_D])。 X是从mnist数据集接收的batch_xs中分配的。 Z由get_noise函数生成的噪声分配。
与D中一样,G在上述Python代码中也得到了优化:ses .run([train_G, loss_G], feed_dict={Z: noise})。
我们也打印每个epoch的损失值。
此外,我们每10个epochs保存生成的图像。
生成的图像
生成的图像(假样本)看起来像真正的手写数字。
关于收敛过程的动画
上图显示,随着学习的重复,generator逐渐收敛。我们可以使用GAN创建数字图像,但它仍然有一些问题:在某些图像中,7和9没有明确区分。
使用DCGAN,您可以获得更好的图像。