在这篇文章中,我将向您展示如何生成可能在现实生活中不存在的新人脸。我将使用生成对抗网络(GAN)来完成任务。我正在使用CelebA数据集来训练网络。该数据集包含2,00,000个知名人物的图像。我假设您对GAN有理论上的理解。我将在本教程中使用Tensorflow框架。
这就是我们的管道的样子
- 归一化图像。
- 创建Generator和Discriminator网络。
- 训练神经网络并生成新面孔。
以下是我们机器学习数据集中的少量图像。
归一化图像
- 作为第一步,我们导入我们将使用的Python库。
- 我们使用PIL加载所有图像。在加载图像时,我们会裁剪脸部周围的所有图像并将其调整为(64,64,3)
- 这些图像的范围为(0,255)。我们将这些图像的位范围压缩在(-1,1)之间,这是在tanh激活的范围内。
Python代码如下:
import glob import numpy as np from PIL import Image import tensorflow as tf import matplotlib.pyplot as plt from tensorflow import reduce_mean from tensorflow.train import AdamOptimizer as adam from tensorflow.nn import sigmoid_cross_entropy_with_logits as loss from tensorflow.layers import dense, batch_normalization, conv2d_transpose, conv2d image_ids = glob.glob('../input/data/*') crop = (30, 55, 150, 175) images = [np.array((Image.open(i).crop(crop)).resize((64,64))) for i in image_ids] for i in range(len(images)): images[i] = ((images[i] - images[i].min())/(255 - images[i].min())) images[i] = images[i]*2-1 images = np.array(images)
辅助函数
def view_samples(epoch, samples, nrows, ncols, figsize=(10,10)): fig, axes = plt.subplots(figsize=figsize, nrows=nrows, ncols=ncols) for ax, img in zip(axes.flatten(), samples[epoch]): ax.axis('off') img = (((img+1)/2)*255).astype(np.uint8) im = ax.imshow(img) plt.subplots_adjust(wspace=0, hspace=0) return fig, axes
创建网络
- 具体来说,我在这里实现DCGAN。这叫做深度卷积生成对抗性网络。这是Ian Goodfellow在2014年推出的GAN标准的变体。DCGAN使用卷积层,而不是所有全连接层。
- GAN背后的概念是它有两个名为Generator和 Discriminator的网络。Generator的工作是从噪声中生成逼真的图像,并欺骗Discriminator。另一方面,Discriminator的工作是区分真实和虚假图像。这两个网络都是分开训练的。
- 在开始时,两个网络的工作都很差,但是当我们继续训练时,Discriminator在识别真实和假图像方面变得会更好,而Generator在生成真实图像方面变得更好,这样它就可以欺骗Discriminator。但是,训练GAN的任务并不容易。
- 网络的体系结构和超参数的选择与DCGAN论文中使用的非常相似。
Generator的Python实现如下:
def generator(noise, reuse=False, alpha=0.2, training=True): with tf.variable_scope('generator', reuse=reuse): x = dense(noise, 4*4*512) x = tf.reshape(x, (-1, 4, 4, 512)) x = batch_normalization(x, training=training) x = tf.maximum(0., x) x = conv2d_transpose(x, 256, 5, 2, padding='same') x = batch_normalization(x, training=training) x = tf.maximum(0., x) x = conv2d_transpose(x, 128, 5, 2, padding='same') x = batch_normalization(x, training=training) x = tf.maximum(0., x) x = conv2d_transpose(x, 64, 5, 2, padding='same') x = batch_normalization(x, training=training) x = tf.maximum(0., x) logits = conv2d_transpose(x, 3, 5, 2, padding='same') out = tf.tanh(logits) return out, logits
- 我们将均匀分布的噪声传递给Generator。发生器将此噪声转换为(64,64,3)尺寸的图像。我们在这个过程中使用转置 卷积。在转置卷积层之后使用批归一化层,而不是最后一层。我们在批归一层之后使用Relu激活。我们通过tanh激活将最终图像压缩到(- 1,1)之间的像素范围。
Discriminator的Python实现如下:
def discriminator(x, reuse=False, alpha=0.2, training=True): with tf.variable_scope('discriminator', reuse=reuse): x = conv2d(x, 32, 5, 2, padding='same') x = tf.maximum(alpha*x, x) x = conv2d(x, 64, 5, 2, padding='same') x = batch_normalization(x, training=training) x = tf.maximum(alpha*x, x) x = conv2d(x, 128, 5, 2, padding='same') x = batch_normalization(x, training=training) x = tf.maximum(alpha*x, x) x = conv2d(x, 256, 5, 2, padding='same') x = batch_normalization(x, training=training) x = tf.maximum(alpha*x, x) flatten = tf.reshape(x, (-1, 4*4*256)) logits = dense(flatten, 1) out = tf.sigmoid(logits) return out, logits
- 我们将传递一个形状张量(64,64,3)给Discriminator。Discriminator给出一个单一的输出,告诉这个图像是真的还是假的。我们通过sigmoid激活传递最终的输出。如果输出值接近于1,则表示Discriminator将图像识别为真实图像,如果输出值接近于0,则将图像识别为假图像。
def inputs(real_dim, noise_dim): inputs_real = tf.placeholder(tf.float32, (None, *real_dim), name='input_real') inputs_noise = tf.placeholder(tf.float32, (None, noise_dim), name='input_noise') return inputs_real, inputs_noise # building the graph tf.reset_default_graph() input_real, input_noise = inputs(input_shape, noise_size) gen_noise, gen_logits = generator(input_noise) dis_out_real, dis_logits_real = discriminator(input_real) dis_out_fake, dis_logits_fake = discriminator(gen_noise, reuse=True) # defining losses shape = dis_logits_real dis_loss_real = reduce_mean(loss(logits=dis_logits_real, labels=tf.ones_like(shape*smooth))) dis_loss_fake = reduce_mean(loss(logits=dis_logits_fake, labels=tf.zeros_like(shape))) gen_loss = reduce_mean(loss(logits=dis_logits_fake, labels=tf.ones_like(shape*smooth))) dis_loss = dis_loss_real + dis_loss_fake # defining optimizers total_vars = tf.trainable_variables() dis_vars = [var for var in total_vars if var.name[0] == 'd'] gen_vars = [var for var in total_vars if var.name[0] == 'g'] dis_opt = adam(learning_rate=learning_rate, beta1=beta1).minimize(dis_loss, var_list=dis_vars) gen_opt = adam(learning_rate=learning_rate, beta1=beta1).minimize(gen_loss, var_list=gen_vars)
- 定义了Generator和Discriminator两种不同的损失函数。优化器也是如此
训练网络
- 下面是超参数的选择。学习率为0.0002,噪声大小为100,标签平滑因子为0.9,LeakyRelu的leak参数为0.2,Adam optimizer的beta1为0.5。
# hyperparameters beta1 = 0.5 alpha = 0.2 smooth = 0.9 noise_size = 100 learning_rate = 0.0002 input_shape = (64,64,3)
- Batch-size是128。以下是训练的Python代码。
batch_size = 128 epochs = 100 saver = tf.train.Saver(var_list = gen_vars) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for e in range(epochs): iters = 10000//batch_size for i in range(1): batch_images= get_images(batch_size) batch_noise = np.random.uniform(-1, 1, size=(batch_size, noise_size)) sess.run(dis_opt, feed_dict={input_real: batch_images, input_noise: batch_noise}) sess.run(gen_opt, feed_dict={input_real: batch_images, input_noise: batch_noise}) loss_dis = sess.run(dis_loss, {input_noise: batch_noise, input_real: batch_images}) loss_gen = gen_loss.eval({input_real: batch_images, input_noise: batch_noise}) print("Epoch {}/{}...".format(e+1, epochs),"Discriminator Loss: {:.4f}...".format(loss_dis), "Generator Loss: {:.4f}".format(loss_gen)) sample_noise = np.random.uniform(-1, 1, size=(8, noise_size)) gen_samples = sess.run(generator(input_noise, reuse=True, alpha=alpha), feed_dict={input_noise: sample_noise}) view_samples(-1, gen_samples, 2, 4, (10,5)) plt.show() saver.save(sess, './checkpoints/generator.ckpt')
- 以下是网络训练后生成的图像。我们可以通过使网络更深来生成更逼真的图像,但是需要花费大量时间来训练模型。我们还可以修改我们的网络以生成高分辨率图像,但同样需要以训练时间为代价。
本文暂时没有评论,来添加一个吧(●'◡'●)