生成对抗网络项目:1~5(2)https://developer.aliyun.com/article/1426893
生成器网络
生成器网络是一个 CNN,它采用 100 维向量z
,并生成大小为(64, 64, 3)
的图像。 让我们在 Keras 框架中实现生成器网络。
执行以下步骤以实现生成器网络:
- 首先创建生成器网络的两个输入层:
latent_dims = 100 num_classes = 6 # Input layer for vector z input_z_noise = Input(shape=(latent_dims, )) # Input layer for conditioning variable input_label = Input(shape=(num_classes, ))
- 接下来,沿着通道维度连接输入,如下所示:
x = concatenate([input_z_noise, input_label])
上一步将生成级联张量。
- 接下来,添加具有以下配置的密集(完全连接)块:
- 单元(节点):
2,048
- 输入大小:106
- 激活:
LeakyReLU
的alpha
等于0.2
- 退出:
0.2
:
x = Dense(2048, input_dim=latent_dims+num_classes)(x) x = LeakyReLU(alpha=0.2)(x) x = Dropout(0.2)(x)
- 接下来,使用以下配置添加第二个密集(完全连接)块:
- 单元(节点):16,384
- 批量规范化:是
- 激活:
alpha
等于0.2
的LeakyReLU
- 退出:
0.2
:
x = Dense(256 * 8 * 8)(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x) x = Dropout(0.2)(x)
- 接下来,将最后一个密集层的输出重塑为大小为
(8, 8, 256)
的三维张量:
x = Reshape((8, 8, 256))(x)
该层将生成张量为(batch_size
,8, 8, 256
)的张量。
- 接下来,添加一个上采样模块,该模块包含一个上采样层,其后是一个具有以下配置的 2D 卷积层和一个批归一化层:
- 上采样大小:
(2, 2)
- 过滤器:
128
- 核大小:
5
- 填充:
same
- 批量规范化:是,
momentum
等于0.8
- 激活:
LeakyReLU
,其中alpha
等于0.2
:
x = UpSampling2D(size=(2, 2))(x) x = Conv2D(filters=128, kernel_size=5, padding='same')(x) x = BatchNormalization(momentum=0.8)(x) x = LeakyReLU(alpha=0.2)(x)
Upsampling2D
is the process of repeating the rows a specified number of timesx
and repeating the columns a specified number of timesy
, respectively.
- 接下来,添加另一个上采样块(类似于上一层),如以下代码所示。 除了卷积层中使用的过滤器数为
128
之外,该配置与上一个块类似:
x = UpSampling2D(size=(2, 2))(x) x = Conv2D(filters=64, kernel_size=5, padding='same')(x) x = BatchNormalization(momentum=0.8)(x) x = LeakyReLU(alpha=0.2)(x)
- 接下来,添加最后一个上采样块。 该配置与上一层相似,除了在卷积层中使用了三个过滤器并且不使用批量归一化的事实:
x = UpSampling2D(size=(2, 2))(x) x = Conv2D(filters=3, kernel_size=5, padding='same')(x) x = Activation('tanh')(x)
- 最后,创建 Keras 模型并指定生成器网络的输入和输出:
model = Model(inputs=[input_z_noise, input_label], outputs=[x])
生成器网络的完整代码如下所示:
def build_generator(): """ Create a Generator Model with hyperparameters values defined as follows :return: Generator model """ latent_dims = 100 num_classes = 6 input_z_noise = Input(shape=(latent_dims,)) input_label = Input(shape=(num_classes,)) x = concatenate([input_z_noise, input_label]) x = Dense(2048, input_dim=latent_dims + num_classes)(x) x = LeakyReLU(alpha=0.2)(x) x = Dropout(0.2)(x) x = Dense(256 * 8 * 8)(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x) x = Dropout(0.2)(x) x = Reshape((8, 8, 256))(x) x = UpSampling2D(size=(2, 2))(x) x = Conv2D(filters=128, kernel_size=5, padding='same')(x) x = BatchNormalization(momentum=0.8)(x) x = LeakyReLU(alpha=0.2)(x) x = UpSampling2D(size=(2, 2))(x) x = Conv2D(filters=64, kernel_size=5, padding='same')(x) x = BatchNormalization(momentum=0.8)(x) x = LeakyReLU(alpha=0.2)(x) x = UpSampling2D(size=(2, 2))(x) x = Conv2D(filters=3, kernel_size=5, padding='same')(x) x = Activation('tanh')(x) model = Model(inputs=[input_z_noise, input_label], outputs=[x]) return model
现在,我们已经成功创建了生成器网络。 接下来,我们将为判别器网络编写代码。
判别器网络
判别器网络是 CNN。 让我们在 Keras 框架中实现判别器网络。
执行以下步骤以实现判别器网络:
- 首先创建两个输入层,因为我们的判别器网络将处理两个输入:
# Specify hyperparameters # Input image shape input_shape = (64, 64, 3) # Input conditioning variable shape label_shape = (6,) # Two input layers image_input = Input(shape=input_shape) label_input = Input(shape=label_shape)
- 接下来,添加具有以下配置的二维卷积块(Conv2D + 激活函数):
- 过滤器:
64
- 核大小:
3
- 步幅:
2
- 填充:
same
- 激活:
LeakyReLU
,其中alpha
等于0.2
:
x = Conv2D(64, kernel_size=3, strides=2, padding='same')(image_input) x = LeakyReLU(alpha=0.2)(x)
- 接下来,展开
label_input
使其具有(32, 32, 6)
的形状:
label_input1 = Lambda(expand_label_input)(label_input)
expand_label_input
函数如下:
# The expand_label_input function def expand_label_input(x): x = K.expand_dims(x, axis=1) x = K.expand_dims(x, axis=1) x = K.tile(x, [1, 32, 32, 1]) return x
前面的函数会将大小为(6, )
的张量转换为大小为(32, 32, 6)
的张量。
- 接下来,沿着通道维度将变换后的标签张量和最后一个卷积层的输出连接起来,如下所示:
x = concatenate([x, label_input1], axis=3)
- 添加具有以下配置的卷积块(2D 卷积层 + 批量归一化 + 激活函数):
- 过滤器:
128
- 核大小:
3
- 步幅:
2
- 填充:
same
- 批量规范化:是
- 激活:
LeakyReLU
,其中alpha
等于0.2
:
x = Conv2D(128, kernel_size=3, strides=2, padding='same')(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x)
- 接下来,再添加两个卷积块,如下所示:
x = Conv2D(256, kernel_size=3, strides=2, padding='same')(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x) x = Conv2D(512, kernel_size=3, strides=2, padding='same')(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x)
- 接下来,添加一个展开层:
x = Flatten()(x)
- 接下来,添加一个输出概率的密集层(分类层):
x = Dense(1, activation='sigmoid')(x)
- 最后,创建 Keras 模型并指定判别器网络的输入和输出:
model = Model(inputs=[image_input, label_input], outputs=[x])
判别器网络的整个代码如下:
def build_discriminator(): """ Create a Discriminator Model with hyperparameters values defined as follows :return: Discriminator model """ input_shape = (64, 64, 3) label_shape = (6,) image_input = Input(shape=input_shape) label_input = Input(shape=label_shape) x = Conv2D(64, kernel_size=3, strides=2, padding='same')(image_input) x = LeakyReLU(alpha=0.2)(x) label_input1 = Lambda(expand_label_input)(label_input) x = concatenate([x, label_input1], axis=3) x = Conv2D(128, kernel_size=3, strides=2, padding='same')(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x) x = Conv2D(256, kernel_size=3, strides=2, padding='same')(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x) x = Conv2D(512, kernel_size=3, strides=2, padding='same')(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x) x = Flatten()(x) x = Dense(1, activation='sigmoid')(x) model = Model(inputs=[image_input, label_input], outputs=[x]) return model
现在,我们已经成功创建了编码器,生成器和判别器网络。 在下一部分中,我们将组装所有内容并训练网络。
训练 cGAN
训练 cGAN 进行人脸老化的过程分为三个步骤:
- 训练 cGAN
- 初始潜在向量近似
- 潜在向量优化
我们将在以下各节中逐一介绍这些步骤。
训练 cGAN
这是训练过程的第一步。 在这一步中,我们训练生成器和判别器网络。 执行以下步骤:
- 首先指定训练所需的参数:
# Define hyperparameters data_dir = "/path/to/dataset/directory/" wiki_dir = os.path.join(data_dir, "wiki_crop") epochs = 500 batch_size = 128 image_shape = (64, 64, 3) z_shape = 100 TRAIN_GAN = True TRAIN_ENCODER = False TRAIN_GAN_WITH_FR = False fr_image_shape = (192, 192, 3)
- 接下来,为训练定义优化器。 我们将使用 Keras 中提供的
Adam
优化器。 初始化优化器,如以下代码所示:
# Define optimizers # Optimizer for the discriminator network dis_optimizer = Adam(lr=0.0002, beta_1=0.5, beta_2=0.999, epsilon=10e-8) # Optimizer for the generator network gen_optimizer = Adam(lr=0.0002, beta_1=0.5, beta_2=0.999, epsilon=10e-8) # Optimizer for the adversarial network adversarial_optimizer = Adam(lr=0.0002, beta_1=0.5, beta_2=0.999, epsilon=10e-8)
对于所有优化程序,请使用等于0.0002
的学习率,等于0.5
的beta_1
值,等于0.999
的beta_2
值以及等于10e-8
的epsilon
值。
- 接下来,加载并编译生成器和判别器网络。 在 Keras 中,我们必须在训练网络之前编译网络:
# Build and compile the discriminator network discriminator = build_discriminator() discriminator.compile(loss=['binary_crossentropy'], optimizer=dis_optimizer) # Build and compile the generator network generator = build_generator1() generator.compile(loss=['binary_crossentropy'], optimizer=gen_optimizer)
要编译网络,请使用binary_crossentropy
作为损失函数。
- 接下来,构建并编译对抗模型,如下所示:
# Build and compile the adversarial model discriminator.trainable = False input_z_noise = Input(shape=(100,)) input_label = Input(shape=(6,)) recons_images = generator([input_z_noise, input_label]) valid = discriminator([recons_images, input_label]) adversarial_model = Model(inputs=[input_z_noise, input_label], outputs=[valid]) adversarial_model.compile(loss=['binary_crossentropy'], optimizer=gen_optimizer)
要编译对抗模型,请使用binary_crossentropy
作为损失函数,并使用gen_optimizer
作为优化器。
- 接下来,存储损失的
TensorBoard
如下:
tensorboard = TensorBoard(log_dir="logs/{}".format(time.time())) tensorboard.set_model(generator) tensorboard.set_model(discriminator)
- 接下来,使用在“准备数据”部分中定义的
load_data
函数加载所有图像:
images, age_list = load_data(wiki_dir=wiki_dir, dataset="wiki")
- 接下来,将年龄数值转换为年龄类别,如下所示:
# Convert age to category age_cat = age_to_category(age_list)
age_to_category
函数的定义如下:
# This method will convert age to respective category def age_to_category(age_list): age_list1 = [] for age in age_list: if 0 < age <= 18: age_category = 0 elif 18 < age <= 29: age_category = 1 elif 29 < age <= 39: age_category = 2 elif 39 < age <= 49: age_category = 3 elif 49 < age <= 59: age_category = 4 elif age >= 60: age_category = 5 age_list1.append(age_category) return age_list1
age_cat
的输出应如下所示:
[1, 2, 4, 2, 3, 4, 2, 5, 5, 1, 3, 2, 1, 1, 2, 1, 2, 2, 1, 5, 4 , .............]
将年龄类别转换为单热编码的向量:
# Also, convert the age categories to one-hot encoded vectors final_age_cat = np.reshape(np.array(age_cat), [len(age_cat), 1]) classes = len(set(age_cat)) y = to_categorical(final_age_cat, num_classes=len(set(age_cat)))
将年龄类别转换为单热编码的向量后,y
的值应如下所示:
[[0\. 1\. 0\. 0\. 0\. 0.] [0\. 0\. 1\. 0\. 0\. 0.] [0\. 0\. 0\. 0\. 1\. 0.] ... [0\. 0\. 0\. 1\. 0\. 0.] [0\. 1\. 0\. 0\. 0\. 0.] [0\. 0\. 0\. 0\. 1\. 0.]]
y
的形状应为(total_values
,5
)。
- 接下来,加载所有图像并创建一个包含所有图像的
ndarray
:
# Read all images and create an ndarray loaded_images = load_images(wiki_dir, images, (image_shape[0], image_shape[1]))
load_images
函数的定义如下:
def load_images(data_dir, image_paths, image_shape): images = None for i, image_path in enumerate(image_paths): print() try: # Load image loaded_image = image.load_img(os.path.join(data_dir, image_path), target_size=image_shape) # Convert PIL image to numpy ndarray loaded_image = image.img_to_array(loaded_image) # Add another dimension (Add batch dimension) loaded_image = np.expand_dims(loaded_image, axis=0) # Concatenate all images into one tensor if images is None: images = loaded_image else: images = np.concatenate([images, loaded_image], axis=0) except Exception as e: print("Error:", i, e) return images
loaded_images
中的值应如下所示:
[[[[ 97\. 122\. 178.] [ 98\. 123\. 179.] [ 99\. 124\. 180.] ... [ 97\. 124\. 179.] [ 96\. 123\. 178.] [ 95\. 122\. 177.]] ... [[216\. 197\. 203.] [217\. 198\. 204.] [218\. 199\. 205.] ... [ 66\. 75\. 90.] [110\. 127\. 171.] [ 89\. 115\. 172.]]] [[[122\. 140\. 152.] [115\. 133\. 145.] [ 95\. 113\. 123.] ... [ 41\. 73\. 23.] [ 38\. 77\. 22.] [ 38\. 77\. 22.]] [[ 53\. 80\. 63.] [ 47\. 74\. 57.] [ 45\. 72\. 55.] ... [ 34\. 66...
- 接下来,创建一个
for
循环,该循环应运行的次数由周期数指定,如下所示:
for epoch in range(epochs): print("Epoch:{}".format(epoch)) gen_losses = [] dis_losses = [] number_of_batches = int(len(loaded_images) / batch_size) print("Number of batches:", number_of_batches)
- 接下来,在周期循环内创建另一个循环,并使它运行
num_batches
指定的次数,如下所示:
for index in range(number_of_batches): print("Batch:{}".format(index + 1))
我们用于判别器网络和对抗网络训练的整个代码将在此循环内。
- 接下来,对真实数据集中的一批图像和一批一次性编码的年龄向量进行采样:
images_batch = loaded_images[index * batch_size:(index + 1) * batch_size] images_batch = images_batch / 127.5 - 1.0 images_batch = images_batch.astype(np.float32) y_batch = y[index * batch_size:(index + 1) * batch_size]
image_batch
的形状应为[batch_size
和64, 64, 3
),y_batch
的形状应为(batch_size
和6
)。
- 接下来,从高斯分布中采样一批噪声向量,如下所示:
z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))
- 接下来,使用生成器网络生成伪造图像。 请记住,我们尚未训练生成器网络:
initial_recon_images = generator.predict_on_batch([z_noise, y_batch])
生成器网络有两个输入z_noise
和y_batch
,它们是我们在步骤 11 和 12 中创建的。
- 现在,在真实图像和伪图像上训练判别器网络:
d_loss_real = discriminator.train_on_batch([images_batch, y_batch], real_labels) d_loss_fake = discriminator.train_on_batch([initial_recon_images, y_batch], fake_labels)
此代码应在一批图像上训练判别器网络。 在每个步骤中,将对一批样本进行判别。
- 接下来,训练对抗网络。 通过冻结判别器网络,我们将仅训练生成器网络:
# Again sample a batch of noise vectors from a Gaussian(normal) distribution z_noise2 = np.random.normal(0, 1, size=(batch_size, z_shape)) # Samples a batch of random age values random_labels = np.random.randint(0, 6, batch_size).reshape(-1, 1) # Convert the random age values to one-hot encoders random_labels = to_categorical(random_labels, 6) # Train the generator network g_loss = adversarial_model.train_on_batch([z_noise2, sampled_labels], [1] * batch_size)
前面的代码将在一批输入上训练生成器网络。 对抗模型的输入是z_noise2
和random_labels
。
- 接下来,计算并打印损失:
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake) print("d_loss:{}".format(d_loss)) print("g_loss:{}".format(g_loss)) # Add losses to their respective lists gen_losses.append(g_loss) dis_losses.append(d_loss)
- 接下来,将损失写入 TensorBoard 以进行可视化:
write_log(tensorboard, 'g_loss', np.mean(gen_losses), epoch) write_log(tensorboard, 'd_loss', np.mean(dis_losses), epoch)
- 每隔 10 个周期取样并保存图像,如下所示:
if epoch % 10 == 0: images_batch = loaded_images[0:batch_size] images_batch = images_batch / 127.5 - 1.0 images_batch = images_batch.astype(np.float32) y_batch = y[0:batch_size] z_noise = np.random.normal(0, 1, size=(batch_size, z_shape)) gen_images = generator.predict_on_batch([z_noise, y_batch]) for i, img in enumerate(gen_images[:5]): save_rgb_img(img, path="results/img_{}_{}.png".format(epoch, i))
将前面的代码块放入周期循环中。 每隔 10 个时间段,它将生成一批伪图像并将其保存到结果目录。 这里, save_rgb_img()
是效用函数,定义如下:
def save_rgb_img(img, path): """ Save a rgb image """ fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.imshow(img) ax.axis("off") ax.set_title("Image") plt.savefig(path) plt.close()
- 最后,通过添加以下行来保存两个模型:
# Save weights only generator.save_weights("generator.h5") discriminator.save_weights("discriminator.h5") # Save architecture and weights both generator.save("generator.h5) discriminator.save("discriminator.h5")
如果您已成功执行本节中给出的代码,则说明您已成功训练了生成器和判别器网络。 在此步骤之后,生成器网络将开始生成模糊的人脸图像。 在下一部分中,我们将训练编码器模型以进行初始潜在向量近似。
初始潜在向量近似
正如我们已经讨论过的,cGAN 不会学习从图像到潜在向量的反向映射。 取而代之的是,编码器学习了这种反向映射,并能够生成潜在向量,我们可以将其用于在目标年龄生成人脸图像。 让我们训练编码器网络。
我们已经定义了训练所需的超参数。 执行以下步骤来训练编码器网络:
- 首先建立编码器网络。 添加以下代码以构建和编译网络:
# Build Encoder encoder = build_encoder() encoder.compile(loss=euclidean_distance_loss, optimizer='adam')
我们尚未定义euclidean_distance_loss
。 在构建编码器网络之前,让我们对其进行定义并添加以下内容:
def euclidean_distance_loss(y_true, y_pred): """ Euclidean distance loss """ return K.sqrt(K.sum(K.square(y_pred - y_true), axis=-1))
- 接下来,加载生成器网络,如下所示:
generator.load_weights("generator.h5")
在这里,我们正在加载上一步的权重,在该步骤中,我们成功地训练并保存了生成器网络的权重。
- 接下来,对一批潜在向量进行采样,如下所示:
z_i = np.random.normal(0, 1, size=(1000, z_shape))
- 接下来,对一批随机年龄数字进行采样,并将随机年龄数字转换为单热编码向量,如下所示:
y = np.random.randint(low=0, high=6, size=(1000,), dtype=np.int64) num_classes = len(set(y)) y = np.reshape(np.array(y), [len(y), 1]) y = to_categorical(y, num_classes=num_classes)
您可以随意采样。 在我们的例子中,我们正在采样 1,000 个值。
- 接下来,添加一个周期循环和一个批量步骤循环,如下所示:
for epoch in range(epochs): print("Epoch:", epoch) encoder_losses = [] number_of_batches = int(z_i.shape[0] / batch_size) print("Number of batches:", number_of_batches) for index in range(number_of_batches): print("Batch:", index + 1)
- 现在,从 1,000 个样本中采样一批潜伏向量和一批单热编码向量,如下所示:
z_batch = z_i[index * batch_size:(index + 1) * batch_size] y_batch = y[index * batch_size:(index + 1) * batch_size]
- 接下来,使用预训练的生成器网络生成伪造图像:
generated_images = generator.predict_on_batch([z_batch, y_batch])
- 最后,通过生成器网络在生成的图像上训练编码器网络:
encoder_loss = encoder.train_on_batch(generated_images, z_batch)
- 接下来,在每个周期之后,将编码器损失写入 TensorBoard,如下所示:
write_log(tensorboard, "encoder_loss", np.mean(encoder_losses), epoch)
- 我们需要保存训练有素的编码器网络。 通过添加以下代码来保存编码器模型:
encoder.save_weights("encoder.h5")
如果您已成功执行了本节中给出的代码,则将成功地训练编码器模型。 现在,我们的编码器网络已准备好生成初始潜向量。 在下一节中,我们将学习如何执行优化的潜在向量近似。
潜在向量优化
在前面的两个步骤中,我们成功地训练了生成器网络,判别器网络和编码器网络。 在本节中,我们将改进编码器和生成器网络。 在这些步骤中,我们将使用人脸识别(FR)网络,该网络会生成输入给它的特定输入的 128 维嵌入,以改善生成器和编码器网络。
执行以下步骤:
- 首先构建并加载编码器网络和生成器网络的权重:
encoder = build_encoder() encoder.load_weights("encoder.h5") # Load the generator network generator.load_weights("generator.h5")
- 接下来,创建一个网络以将图像从
(64, 64, 3)
形状调整为(192, 192, 3)
形状,如下所示:
# Define all functions before you make a call to them def build_image_resizer(): input_layer = Input(shape=(64, 64, 3)) resized_images = Lambda(lambda x: K.resize_images(x, height_factor=3, width_factor=3, data_format='channels_last'))(input_layer) model = Model(inputs=[input_layer], outputs=[resized_images]) return model
image_resizer = build_image_resizer() image_resizer.compile(loss=loss, optimizer='adam')
要使用 FaceNet,我们图像的高度和宽度应大于 150 像素。 前面的网络将帮助我们将图像转换为所需的格式。
- 建立人脸识别模型:
# Face recognition model fr_model = build_fr_model(input_shape=fr_image_shape) fr_model.compile(loss=loss, optimizer="adam")
请参阅这里以获取build_fr_model()
函数。
- 接下来,创建另一个对抗模型。 在此对抗模型中,我们将具有三个网络:编码器,生成器和人脸识别模型:
# Make the face recognition network as non-trainable fr_model.trainable = False # Input layers input_image = Input(shape=(64, 64, 3)) input_label = Input(shape=(6,)) # Use the encoder and the generator network latent0 = encoder(input_image) gen_images = generator([latent0, input_label]) # Resize images to the desired shape resized_images = Lambda(lambda x: K.resize_images(gen_images, height_factor=3, width_factor=3, data_format='channels_last'))(gen_images) embeddings = fr_model(resized_images) # Create a Keras model and specify the inputs and outputs to the network fr_adversarial_model = Model(inputs=[input_image, input_label], outputs=[embeddings]) # Compile the model fr_adversarial_model.compile(loss=euclidean_distance_loss, optimizer=adversarial_optimizer)
- 在第一个循环中添加一个
epoch
循环和一个批量步骤循环,如下所示:
for epoch in range(epochs): print("Epoch:", epoch) number_of_batches = int(len(loaded_images) / batch_size) print("Number of batches:", number_of_batches) for index in range(number_of_batches): print("Batch:", index + 1)
- 接下来,从真实图像列表中采样一批图像:
# Sample and normalize images_batch = loaded_images[index * batch_size:(index + 1) * batch_size] images_batch = images_batch / 255.0 images_batch = images_batch.astype(np.float32) # Sample a batch of age one-hot encoder vectors y_batch = y[index * batch_size:(index + 1) * batch_size]
- 接下来,使用 FR 网络为真实图像生成嵌入:
images_batch_resized = image_resizer.predict_on_batch(images_batch) real_embeddings = fr_model.predict_on_batch(images_batch_resized)
- 最后,训练对抗模型,这将训练编码器模型和生成器模型:
reconstruction_loss = fr_adversarial_model.train_on_batch([images_batch, y_batch], real_embeddings)
- 另外,将重建损失写入 TensorBoard 以进行进一步可视化:
# Write the reconstruction loss to Tensorboard write_log(tensorboard, "reconstruction_loss", reconstruction_loss, index)
- 保存两个网络的权重:
# Save improved weights for both of the networks generator.save_weights("generator_optimized.h5") encoder.save_weights("encoder_optimized.h5")
恭喜你! 我们现在已经成功地训练了 Age-cGAN 进行人脸老化。
可视化损失
要可视化训练损失,请启动 Tensorboard 服务器,如下所示:
tensorboard --logdir=logs
现在,在浏览器中打开 localhost:6006
。 TensorBoard 的标量部分包含两种损失的图表,如以下屏幕截图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mi272u8K-1681652801320)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/e705b732-209a-407e-bbb4-5ddae7424011.png)]
这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止训练,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。
可视化图
TensorBoard 的GRAPHS
部分包含两个网络的图。 如果网络表现不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QmxSTij3-1681652801320)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/63d2ac6d-4141-47c4-8807-19efe0bb901e.png)]
张量流和图内部的不同操作
Age-cGAN 的实际应用
年龄综合和年龄发展具有许多工业和消费者应用:
- 跨年龄人脸识别:可以将其合并到安全应用中,例如移动设备解锁或桌面解锁。 当前的人脸识别系统的问题在于它们需要随时间更新。 使用 Age-cGAN 网络,跨年龄人脸识别系统的寿命将更长。
- 寻找失落的孩子:这是 Age-cGAN 的有趣应用。 随着儿童年龄的增长,他们的人脸特征会发生变化,并且很难识别它们。 Age-cGAN 可以模拟指定年龄的人的脸。
- 娱乐:例如,在移动应用中,用于显示和共享指定年龄的朋友的照片。
- 电影中的视觉效果:手动模拟一个人的年龄是一个繁琐而漫长的过程。 Age-cGAN 可以加快此过程并降低创建和模拟人脸的成本。
总结
在本章中,我们介绍了年龄条件生成对抗网络(Age-cGAN)。 然后,我们研究了 Age-cGAN 的架构。 之后,我们学习了如何设置我们的项目,并研究了 Ages-cGAN 的 Keras 实现。 然后,我们在 Wiki 裁剪的数据集上训练了 Age-cGAN,并经历了 Age-cGAN 网络的所有三个阶段。 最后,我们讨论了 Age-cGAN 的实际应用。
在下一章中,我们将使用 GAN 的另一个变体生成动画角色:DCGAN。
四、使用 DCGAN 生成动漫角色
众所周知,卷积层确实擅长处理图像。 他们能够学习重要的特征,例如边缘,形状和复杂的对象,有效的, ,例如神经网络,例如 Inception,AlexNet, 视觉几何组(VGG)和 ResNet。 Ian Goodfellow 等人在其名为《生成对抗网络》的论文中提出了具有密集层的生成对抗网络(GAN),该网络可在以下链接获得。 复杂的神经网络,例如卷积神经网络(CNN),循环神经网络(RNN)和长短期记忆(LSTM)最初并未在 GAN 中进行测试。 深度卷积生成对抗网络(DCGAN)的发展是使用 CNN 进行图像生成的重要一步。 DCGAN 使用卷积层而不是密集层。 它们是由研究人员 Alec Radford , Luke Metz , Soumith Chintala 等,在其论文《使用深度卷积生成对抗网络的无监督表示学习》中提出的,可以在以下链接中找到。 从那时起,DCGAN 被广泛用于各种图像生成任务。 在本章中,我们将使用 DCGAN 架构生成动漫角色。
在本章中,我们将介绍以下主题:
- DCGAN 简介
- GAN 网络的架构细节
- 建立项目
- 为训练准备数据集
- DCGAN 的 Keras 实现以生成动画角色
- 在动漫角色数据集上训练 DCGAN
- 评估训练好的模型
- 通过优化超参数优化网络
- DCGAN 的实际应用
DCGAN 简介
CNN 在计算机视觉任务中非常出色,无论是用于分类图像还是检测图像中的对象。 CNN 善于理解图像,以至于激发研究人员在 GAN 网络中使用 CNN。 最初,GAN 官方论文的作者介绍了仅具有密集层的深层神经网络(DNN)。 在 GAN 网络的原始实现中未使用卷积层。 在以前的 GAN 中,生成器和判别器网络仅使用密集的隐藏层。 相反,作者建议在 GAN 设置中可以使用不同的神经网络架构。
DCGAN 扩展了在判别器和生成器网络中使用卷积层的思想。 DCGAN 的设置类似于朴素 GAN。 它由两个网络组成:生成器和判别器。 生成器是具有卷积层的 DNN,而判别器是具有卷积层的 DNN。 训练 DCGAN 类似于训练普通 GAN 网络。 在第一章中,我们了解到网络参与了非合作博弈,其中判别器网络将其误差反向传播到生成器网络,生成器网络使用此误差来提高其权重。
在下一部分中,我们将探索两个网络的架构。
DCGAN 的架构细节
如前所述,DCGAN 网络在两个网络中都使用卷积层。 重申一下,CNN 是一个具有卷积层,紧随其后的归一化或池化层以及紧随其后的激活函数的网络。 在 DCGAN 中,判别器网络会拍摄图像,在卷积和池化层的帮助下对图像进行降采样,然后使用密集的分类层将图像分类为真实图像或伪图像。 生成器网络从潜在空间中获取随机噪声向量,使用上采样机制对其进行上采样,最后生成图像。 我们使用 Leaky ReLU 作为隐藏层的激活函数,并在 0.4 和 0.7 之间进行滤除以避免过拟合。
让我们看一下两个网络的配置。
配置生成器网络
在继续之前,让我们看一下生成器网络的架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyfiFi8d-1681652801321)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/7201db6b-98f4-473e-8ba3-615cd8d132f1.png)]
来源:arXiv:1511.06434 [cs.LG]
上图包含了生成器网络架构中的不同层,并显示了它如何生成分辨率为64 x 64 x 3
的图像。
DCGAN 的生成器网络包含 10 层。 它执行跨步卷积以增加张量的空间分辨率。 在 Keras 中,上采样和卷积层的组合等于跨步卷积层。 基本上,生成器网络会从均匀分布中获取采样的噪声向量,并不断对其进行转换,直到生成最终图像为止。 换句话说,它采取形状的张量(batch_size, 100
),并输出形状的张量(batch_size, 64, 64, 3
)。
让我们看一下生成器网络中的不同层:
编号 | 层名称 | 配置 |
1 | 输入层 | input_shape=(batch_size, 100) ,output_shape=(batch_size, 100) |
2 | 密集层 | neurons=2048 ,input_shape=(batch_size, 100) ,output_shape=(batch_size, 2048) ,activation='relu' |
3. | 密集层 | neurons=16384 ,input_shape=(batch_size, 100) ,output_shape=(batch_size, 2048) ,batch_normalization=Yes ,activation='relu' |
4. | 重塑层 | input_shape=(batch_size=16384) ,outp ut_shape=(batch_size, 8, 8, 256) |
5. | 上采样层 | size=(2, 2) ,input_shape=(batch_size, 8, 8, 256) ,output_shape=(batch_size, 16, 16, 256) |
6. | 2D 卷积层 | filters=128 ,kernel_size=(5, 5) ,strides=(1, 1) ,padding='same' ,input_shape=(batch_size, 16, 16, 256) ,output_shape=(batch_size, 16, 16, 128) , activation='relu' |
7. | 上采样层 | size=(2, 2) ,input_shape=(batch_size, 16, 16, 128) ,output_shape=(batch_size, 32, 32, 128) |
8. | 2D 卷积层 | filters=64 ,kernel_size=(5, 5) ,strides=(1, 1) ,padding='same' ,activation=ReLU ,input_shape=(batch_size, 32, 32, 128) ,output_shape=(batch_size, 32, 32, 64) ,activation='relu' |
9. | 上采样层 | size=(2, 2) ,input_shape=(batch_size, 32, 32, 64) ,output_shape=(batch_size, 64, 64, 64) |
10. | 2D 卷积层 | filters=3 ,kernel_size=(5, 5) ,strides=(1, 1) ,padding='same' ,activation=ReLU ,input_shape=(batch_size, 64, 64, 64) ,output_shape=(batch_size, 64, 64, 3) ,activation='tanh' |
L 等人研究了张量如何从第一层流到最后一层。 下图显示了不同层的输入和输出形状:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tHKcTBz5-1681652801321)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/6cb9082b-a52f-48e7-8193-caf3c11e93ea.png)]
该配置对具有 TensorFlow 后端和channels_last
格式的 Keras API 有效。
生成对抗网络项目:1~5(4)https://developer.aliyun.com/article/1426895