我们将使用GAN来生成人脸,使用到的数据集为CelebA
数据预处理
CelebA数据集中裁剪非脸部图像部分,然后调整到28*28维度。
建立神经网络
我们将通过部署以下函数来建立GANs的主要部分
- model_input
- discirminator
- generator
- model_loss
- model_opt
- train
Input
创建用于神经网络的占位符。
- 输入图像
- 噪声Z用于生成器输入
- 学习率
def model_inputs(image_width, image_height, image_channels, z_dim):
"""
Create the model inputs
:param image_width: The input image width
:param image_height: The input image height
:param image_channels: The number of image channels
:param z_dim: The dimension of Z
:return: Tuple of (tensor of real input images, tensor of z data, learning rate)
"""
# TODO: Implement Function
input_real = tf.placeholder(tf.float32, [None,image_width,image_height,image_channels],name = 'input_real')
input_z=tf.placeholder(tf.float32,[None, z_dim],name= 'input_z')
lr = tf.placeholder(tf.float32, name='lr')
return input_real,input_z,lr
Discriminator
辨别器这里用卷积神经网络+BN构建。
def discriminator(images, reuse=False,alpha = 0.2):
"""
Create the discriminator network
:param image: Tensor of input image(s)
:param reuse: Boolean if the weights should be reused
:return: Tuple of (tensor output of the discriminator, tensor logits of the discriminator)
"""
# TODO: Implement Function
with tf.variable_scope('discriminator', reuse=reuse):
#print(images.shape) 28*28*3
x1 = tf.layers.conv2d(images, 32, 5, strides=2, padding='same')
relu1 = tf.maximum(alpha * x1, x1)
x2 = tf.layers.conv2d(relu1, 64, 5, strides=2, padding='same')
bn2 = tf.layers.batch_normalization(x2, training=True)
relu2 = tf.maximum(alpha*bn2, bn2)
x3 = tf.layers.conv2d(relu2, 128, 5, strides=2, padding='same')
bn3 = tf.layers.batch_normalization(x3, training=True)
relu3 = tf.maximum(alpha*bn3, bn3)
flat = tf.reshape(relu3, (-1, 4*4*128))
logits = tf.layers.dense(flat, 1)
out = tf.sigmoid(logits)
return out, logits
Generator
用噪声向量Z生成图像
def generator(z, out_channel_dim, is_train=True,alpha = 0.2):
"""
Create the generator network
:param z: Input z
:param out_channel_dim: The number of channels in the output image
:param is_train: Boolean if generator is being used for training
:return: The tensor output of the generator
"""
# TODO: Implement Function
with tf.variable_scope('generator', reuse = not is_train):
# First fully connected layer
x1 = tf.layers.dense(z, 3*3*512)
# Reshape it to start the convolutional stack
x1 = tf.reshape(x1, (-1, 3, 3, 512))
x1 = tf.layers.batch_normalization(x1, training= is_train)
x1 = tf.maximum(alpha * x1, x1)
#print(x1.shape)
# 3x3x512 now
x2 = tf.layers.conv2d_transpose(x1, 256, 5, strides=2, padding='same')
x2 = tf.layers.batch_normalization(x2, training= is_train)
x2 = tf.maximum(alpha * x2, x2)
#print(x2.shape)
# 6x6x256 now,卷积核大小设为5*5
x3 = tf.layers.conv2d_transpose(x2, 128, 5, strides=2, padding='same')
x3 = tf.layers.batch_normalization(x3, training=is_train)
x3 = tf.maximum(alpha * x3, x3)
#print(x3.shape)
# 12x12x128 now
# Output layer
logits = tf.layers.conv2d_transpose(x3, out_channel_dim, 6, strides=2, padding='valid')
#print(logits.shape)
# 28x28x5now
out = tf.tanh(logits)
return out
损失函数
使用已实现的函数计算loss
- discriminator(images, reuse=False)
- generator(z, out_channel_dim, is_train=True)
def model_loss(input_real, input_z, out_channel_dim):
"""
Get the loss for the discriminator and generator
:param input_real: Images from the real dataset
:param input_z: Z input
:param out_channel_dim: The number of channels in the output image
:return: A tuple of (discriminator loss, generator loss)
"""
# TODO: Implement Function
g_model = generator(input_z, out_channel_dim)
d_model_real, d_logits_real = discriminator(input_real)
d_model_fake, d_logits_fake = discriminator(g_model, reuse=True)
d_loss_real = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_real, labels=tf.ones_like(d_model_real)))
d_loss_fake = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_fake, labels=tf.zeros_like(d_model_fake)))
g_loss = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_fake, labels=tf.ones_like(d_model_fake)))
d_loss = d_loss_real + d_loss_fake
return d_loss, g_loss
优化
部署 model_opt
函数实现对 GANs 的优化。使用 tf.trainable_variables
获取可训练的所有变量。通过变量空间名 discriminator
和 generator
来过滤变量。该函数应返回形如 (discriminator training operation, generator training operation) 的元组。
def model_opt(d_loss, g_loss, learning_rate, beta1):
"""
Get optimization operations
:param d_loss: Discriminator loss Tensor
:param g_loss: Generator loss Tensor
:param learning_rate: Learning Rate Placeholder
:param beta1: The exponential decay rate for the 1st moment in the optimizer
:return: A tuple of (discriminator training operation, generator training operation)
"""
# TODO: Implement Function
t_vars = tf.trainable_variables()
d_vars = [var for var in t_vars if var.name.startswith('discriminator')]
g_vars = [var for var in t_vars if var.name.startswith('generator')]
# Optimize
with tf.control_dependencies(tf.get_collection(tf.GraphKeys.UPDATE_OPS)):
d_train_opt = tf.train.AdamOptimizer(learning_rate, beta1=beta1).minimize(d_loss, var_list=d_vars)
g_train_opt = tf.train.AdamOptimizer(learning_rate, beta1=beta1).minimize(g_loss, var_list=g_vars)
return d_train_opt, g_train_opt
输出显示
使用该函数可以显示生成器 (Generator) 在训练过程中的当前输出,这会帮你评估 GANs 模型的训练程度
import numpy as np
def show_generator_output(sess, n_images, input_z, out_channel_dim, image_mode):
"""
Show example output for the generator
:param sess: TensorFlow session
:param n_images: Number of Images to display
:param input_z: Input Z Tensor
:param out_channel_dim: The number of channels in the output image
:param image_mode: The mode to use for images ("RGB" or "L")
"""
cmap = None if image_mode == 'RGB' else 'gray'
z_dim = input_z.get_shape().as_list()[-1]
example_z = np.random.uniform(-1, 1, size=[n_images, z_dim])
samples = sess.run(
generator(input_z, out_channel_dim, False),
feed_dict={input_z: example_z})
images_grid = helper.images_square_grid(samples, image_mode)
pyplot.imshow(images_grid, cmap=cmap)
pyplot.show()
训练
部署 train
函数以建立并训练 GANs 模型。记得使用以下你已完成的函数:
- model_inputs(image_width, image_height, image_channels, z_dim)
- model_loss(input_real, input_z, out_channel_dim)
- model_opt(d_loss, g_loss, learning_rate, beta1)
使用 show_generator_output
函数显示 generator
在训练过程中的输出。
注意:在每个批次 (batch) 中运行 show_generator_output
函数会显著增加训练时间与该 notebook 的体积。推荐每 100 批次输出一次 generator
的输出。
def train(epoch_count, batch_size, z_dim, learning_rate, beta1, get_batches, data_shape, data_image_mode):
"""
Train the GAN
:param epoch_count: Number of epochs
:param batch_size: Batch Size
:param z_dim: Z dimension
:param learning_rate: Learning Rate
:param beta1: The exponential decay rate for the 1st moment in the optimizer
:param get_batches: Function to get batches
:param data_shape: Shape of the data
:param data_image_mode: The image mode to use for images ("RGB" or "L")
"""
# TODO: Build Model
_, img_width, img_height, img_channels = data_shape
input_real, input_z, lr = model_inputs(img_width, img_height, img_channels, z_dim)
d_loss, g_loss = model_loss(input_real, input_z, img_channels)
d_opt, g_opt = model_opt(d_loss, g_loss, lr, beta1)
steps = 0
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for epoch_i in range(epoch_count):
for batch_images in get_batches(batch_size):
# TODO: Train Model
steps += 1
# Sample random noise for G
batch_z = np.random.uniform(-1, 1, size=(batch_size, z_dim))
# Run optimizers
_ = sess.run(d_opt, feed_dict={input_real: batch_images, input_z: batch_z,lr:learning_rate})
_ = sess.run(g_opt, feed_dict={input_real: batch_images,input_z: batch_z,lr:learning_rate })
if steps % 100 == 0:
# At the end of each epoch, get the losses and print them out
train_loss_d = d_loss.eval({input_z: batch_z, input_real: batch_images})
train_loss_g = g_loss.eval({input_z: batch_z})
print("Epoch {}/{}...".format(epoch_i+1, epoch_count),
"Discriminator Loss: {:.4f}...".format(train_loss_d),
"Generator Loss: {:.4f}".format(train_loss_g))
show_generator_output(sess, 25, input_z, img_channels, data_image_mode)
结果
以上为训练过程,GANs对超参数很敏感,需要找到一组比较好的参数。
最后的生成结果:
这里再善意的提醒:当你的图像还没训练好时,就不要想看大图了,会很恐怖的
Tips
这里用的Tesla K80显卡训练,比自己的显卡不知道快了多少倍,所以训练图像尽量还是选一个好一点的显卡吧。
这个是专门做计算用的卡,可别想着买一块,去试一试云计算吧。