生成对抗网络项目:1~5(1)https://developer.aliyun.com/article/1426892
3D-GAN 的 Keras 实现
在本节中,我们将在 Keras 框架中实现生成器网络和判别器网络。 我们需要创建两个 Keras 模型。 这两个网络都有各自独立的权重值。 让我们从生成器网络开始。
生成器网络
为了实现生成器网络,我们需要创建 Keras 模型并添加神经网络层。 实现生成器网络所需的步骤如下:
- 首先为不同的超参数指定值:
z_size = 200 gen_filters = [512, 256, 128, 64, 1] gen_kernel_sizes = [4, 4, 4, 4, 4] gen_strides = [1, 2, 2, 2, 2] gen_input_shape = (1, 1, 1, z_size) gen_activations = ['relu', 'relu', 'relu', 'relu', 'sigmoid'] gen_convolutional_blocks = 5
- 接下来,创建一个输入层,以允许网络进行输入。 生成器网络的输入是从概率潜在空间中采样的向量:
input_layer = Input(shape=gen_input_shape)
- 然后,添加第一个 3D 转置卷积(或 3D 解卷积)块,如以下代码所示:
# First 3D transpose convolution( or 3D deconvolution) block a = Deconv3D(filters=gen_filters[0], kernel_size=gen_kernel_sizes[0], strides=gen_strides[0])(input_layer) a = BatchNormalization()(a, training=True) a = Activation(activation=gen_activations[0])(a)
- 接下来,再添加四个 3D 转置卷积(或 3D 解卷积)块,如下所示:
# Next 4 3D transpose convolution( or 3D deconvolution) blocks for i in range(gen_convolutional_blocks - 1): a = Deconv3D(filters=gen_filters[i + 1], kernel_size=gen_kernel_sizes[i + 1], strides=gen_strides[i + 1], padding='same')(a) a = BatchNormalization()(a, training=True) a = Activation(activation=gen_activations[i + 1])(a)
- 然后,创建 Keras 模型并指定生成器网络的输入和输出:
model = Model(inputs=input_layer, outputs=a)
- 将生成器网络的整个代码包装在一个名为
build_generator()
的函数内:
def build_generator(): """ Create a Generator Model with hyperparameters values defined as follows :return: Generator network """ z_size = 200 gen_filters = [512, 256, 128, 64, 1] gen_kernel_sizes = [4, 4, 4, 4, 4] gen_strides = [1, 2, 2, 2, 2] gen_input_shape = (1, 1, 1, z_size) gen_activations = ['relu', 'relu', 'relu', 'relu', 'sigmoid'] gen_convolutional_blocks = 5 input_layer = Input(shape=gen_input_shape) # First 3D transpose convolution(or 3D deconvolution) block a = Deconv3D(filters=gen_filters[0], kernel_size=gen_kernel_sizes[0], strides=gen_strides[0])(input_layer) a = BatchNormalization()(a, training=True) a = Activation(activation='relu')(a) # Next 4 3D transpose convolution(or 3D deconvolution) blocks for i in range(gen_convolutional_blocks - 1): a = Deconv3D(filters=gen_filters[i + 1], kernel_size=gen_kernel_sizes[i + 1], strides=gen_strides[i + 1], padding='same')(a) a = BatchNormalization()(a, training=True) a = Activation(activation=gen_activations[i + 1])(a) gen_model = Model(inputs=input_layer, outputs=a) gen_model.summary() return gen_model
我们已经成功地为生成器网络创建了 Keras 模型。 接下来,为判别器网络创建 Keras 模型。
判别器网络
同样,要实现判别器网络,我们需要创建 Keras 模型并向其中添加神经网络层。 实现判别器网络所需的步骤如下:
- 首先为不同的超参数指定值:
dis_input_shape = (64, 64, 64, 1) dis_filters = [64, 128, 256, 512, 1] dis_kernel_sizes = [4, 4, 4, 4, 4] dis_strides = [2, 2, 2, 2, 1] dis_paddings = ['same', 'same', 'same', 'same', 'valid'] dis_alphas = [0.2, 0.2, 0.2, 0.2, 0.2] dis_activations = ['leaky_relu', 'leaky_relu', 'leaky_relu', 'leaky_relu', 'sigmoid'] dis_convolutional_blocks = 5
- 接下来,创建一个输入层,以允许网络进行输入。 判别器网络的输入是形状为
64x64x64x1
的 3D 图像:
dis_input_layer = Input(shape=dis_input_shape)
- 然后,添加第一个 3D 卷积块,如下所示:
# The first 3D Convolution block a = Conv3D(filters=dis_filters[0], kernel_size=dis_kernel_sizes[0], strides=dis_strides[0], padding=dis_paddings[0])(dis_input_layer) a = BatchNormalization()(a, training=True) a = LeakyReLU(alphas[0])(a)
- 之后,再添加四个 3D 卷积块,如下所示:
# The next 4 3D Convolutional Blocks for i in range(dis_convolutional_blocks - 1): a = Conv3D(filters=dis_filters[i + 1], kernel_size=dis_kernel_sizes[i + 1], strides=dis_strides[i + 1], padding=dis_paddings[i + 1])(a) a = BatchNormalization()(a, training=True) if dis_activations[i + 1] == 'leaky_relu': a = LeakyReLU(dis_alphas[i + 1])(a) elif dis_activations[i + 1] == 'sigmoid': a = Activation(activation='sigmoid')(a)
- 接下来,创建一个 Keras 模型,并为判别器网络指定输入和输出:
dis_model = Model(inputs=dis_input_layer, outputs=a)
- 将判别器网络的完整代码包装在一个函数中,如下所示:
def build_discriminator(): """ Create a Discriminator Model using hyperparameters values defined as follows :return: Discriminator network """ dis_input_shape = (64, 64, 64, 1) dis_filters = [64, 128, 256, 512, 1] dis_kernel_sizes = [4, 4, 4, 4, 4] dis_strides = [2, 2, 2, 2, 1] dis_paddings = ['same', 'same', 'same', 'same', 'valid'] dis_alphas = [0.2, 0.2, 0.2, 0.2, 0.2] dis_activations = ['leaky_relu', 'leaky_relu', 'leaky_relu', 'leaky_relu', 'sigmoid'] dis_convolutional_blocks = 5 dis_input_layer = Input(shape=dis_input_shape) # The first 3D Convolutional block a = Conv3D(filters=dis_filters[0], kernel_size=dis_kernel_sizes[0], strides=dis_strides[0], padding=dis_paddings[0])(dis_input_layer) a = BatchNormalization()(a, training=True) a = LeakyReLU(dis_alphas[0])(a) # Next 4 3D Convolutional Blocks for i in range(dis_convolutional_blocks - 1): a = Conv3D(filters=dis_filters[i + 1], kernel_size=dis_kernel_sizes[i + 1], strides=dis_strides[i + 1], padding=dis_paddings[i + 1])(a) a = BatchNormalization()(a, training=True) if dis_activations[i + 1] == 'leaky_relu': a = LeakyReLU(dis_alphas[i + 1])(a) elif dis_activations[i + 1] == 'sigmoid': a = Activation(activation='sigmoid')(a) dis_model = Model(inputs=dis_input_layer, outputs=a) print(dis_model.summary()) return dis_model
在本节中,我们为判别器网络创建了 Keras 模型。 我们现在准备训练 3D-GAN。
训练 3D-GAN
训练 3D-GAN 类似于训练朴素 GAN。 我们首先在生成的图像和真实图像上训练判别器网络,但是冻结生成器网络。 然后,我们训练生成器网络,但冻结判别器网络。 我们对指定的周期数重复此过程。 在一次迭代中,我们按顺序训练两个网络。 训练 3D-GAN 是一个端到端的训练过程。 让我们一步一步地进行这些步骤。
训练网络
要训练 3D-GAN,请执行以下步骤:
- 首先指定训练所需的不同超参数的值,如下所示:
gen_learning_rate = 0.0025 dis_learning_rate = 0.00001 beta = 0.5 batch_size = 32 z_size = 200 DIR_PATH = 'Path to the 3DShapenets dataset directory' generated_volumes_dir = 'generated_volumes' log_dir = 'logs'
- 接下来,创建并编译两个网络,如下所示:
# Create instances generator = build_generator() discriminator = build_discriminator() # Specify optimizer gen_optimizer = Adam(lr=gen_learning_rate, beta_1=beta) dis_optimizer = Adam(lr=dis_learning_rate, beta_1=0.9) # Compile networks generator.compile(loss="binary_crossentropy", optimizer="adam") discriminator.compile(loss='binary_crossentropy', optimizer=dis_optimizer)
我们正在使用Adam
优化器作为优化算法,并使用binary_crossentropy
作为损失函数。 在第一步中指定Adam
优化器的超参数值。
- 然后,创建并编译对抗模型:
discriminator.trainable = False adversarial_model = Sequential() adversarial_model.add(generator) adversarial_model.add(discriminator) adversarial_model.compile(loss="binary_crossentropy", optimizer=Adam(lr=gen_learning_rate, beta_1=beta))
- 之后,提取并加载所有
airplane
图像以进行训练:
def getVoxelsFromMat(path, cube_len=64): voxels = io.loadmat(path)['instance'] voxels = np.pad(voxels, (1, 1), 'constant', constant_values=(0, 0)) if cube_len != 32 and cube_len == 64: voxels = nd.zoom(voxels, (2, 2, 2), mode='constant', order=0) return voxels def get3ImagesForACategory(obj='airplane', train=True, cube_len=64, obj_ratio=1.0): obj_path = DIR_PATH + obj + '/30/' obj_path += 'train/' if train else 'test/' fileList = [f for f in os.listdir(obj_path) if f.endswith('.mat')] fileList = fileList[0:int(obj_ratio * len(fileList))] volumeBatch = np.asarray([getVoxelsFromMat(obj_path + f, cube_len) for f in fileList], dtype=np.bool) return volumeBatch volumes = get3ImagesForACategory(obj='airplane', train=True, obj_ratio=1.0) volumes = volumes[..., np.newaxis].astype(np.float)
- 接下来,添加
TensorBoard
回调并添加generator
和discriminator
网络:
tensorboard = TensorBoard(log_dir="{}/{}".format(log_dir, time.time())) tensorboard.set_model(generator) tensorboard.set_model(discriminator)
- 添加一个循环,该循环将运行指定的周期数:
for epoch in range(epochs): print("Epoch:", epoch) # Create two lists to store losses gen_losses = [] dis_losses = []
- 在第一个循环中添加另一个循环以运行指定的批量数量:
number_of_batches = int(volumes.shape[0] / batch_size) print("Number of batches:", number_of_batches) for index in range(number_of_batches): print("Batch:", index + 1)
- 接下来,从一组真实图像中采样一批图像,并从高斯正态分布中采样一批噪声向量。 噪声向量的形状应为
(1, 1, 1, 200)
:
z_sample = np.random.normal(0, 0.33, size=[batch_size, 1, 1, 1, z_size]).astype(np.float32) volumes_batch = volumes[index * batch_size:(index + 1) * batch_size, :, :, :]
- 使用生成器网络生成伪造图像。 向其传递
z_sample
的一批噪声向量,并生成一批假图像:
gen_volumes = generator.predict(z_sample,verbose=3)
- 接下来,在由生成器生成的伪图像和一组真实图像中的一批真实图像上训练判别器网络。 另外,使判别器易于训练:
# Make the discriminator network trainable discriminator.trainable = True # Create fake and real labels labels_real = np.reshape([1] * batch_size, (-1, 1, 1, 1, 1)) labels_fake = np.reshape([0] * batch_size, (-1, 1, 1, 1, 1)) # Train the discriminator network loss_real = discriminator.train_on_batch(volumes_batch, labels_real) loss_fake = discriminator.train_on_batch(gen_volumes, labels_fake) # Calculate total discriminator loss d_loss = 0.5 * (loss_real + loss_fake)
前面的代码训练了判别器网络并计算了总的判别器损失。
- 训练同时包含
generator
和discriminator
网络的对抗模型:
z = np.random.normal(0, 0.33, size=[batch_size, 1, 1, 1, z_size]).astype(np.float32) # Train the adversarial model g_loss = adversarial_model.train_on_batch(z, np.reshape([1] * batch_size, (-1, 1, 1, 1, 1)))
另外,将损失添加到其各自的列表中,如下所示:
gen_losses.append(g_loss) dis_losses.append(d_loss)
- 每隔一个周期生成并保存 3D 图像:
if index % 10 == 0: z_sample2 = np.random.normal(0, 0.33, size=[batch_size, 1, 1, 1, z_size]).astype(np.float32) generated_volumes = generator.predict(z_sample2, verbose=3) for i, generated_volume in enumerate(generated_volumes[:5]): voxels = np.squeeze(generated_volume) voxels[voxels < 0.5] = 0. voxels[voxels >= 0.5] = 1. saveFromVoxels(voxels, "results/img_{}_{}_{}".format(epoch, index, i))
- 在每个周期之后,将平均损失保存到
tensorboard
:
# Save losses to Tensorboard write_log(tensorboard, 'g_loss', np.mean(gen_losses), epoch) write_log(tensorboard, 'd_loss', np.mean(dis_losses), epoch)
我的建议是将其训练 100 个周期,以查找代码中的问题。 解决了这些问题后,您就可以在 100,000 个周期内训练网络。
保存模型
训练完成后,通过添加以下代码来保存生成器和判别器模型的学习权重:
""" Save models """ generator.save_weights(os.path.join(generated_volumes_dir, "generator_weights.h5")) discriminator.save_weights(os.path.join(generated_volumes_dir, "discriminator_weights.h5"))
测试模型
要测试网络,请创建generator
和discriminator
网络。 然后,加载学习的权重。 最后,使用predict()
方法生成预测:
# Create models generator = build_generator() discriminator = build_discriminator() # Load model weights generator.load_weights(os.path.join(generated_volumes_dir, "generator_weights.h5"), True) discriminator.load_weights(os.path.join(generated_volumes_dir, "discriminator_weights.h5"), True) # Generate 3D images z_sample = np.random.normal(0, 0.33, size=[batch_size, 1, 1, 1, z_size]).astype(np.float32) generated_volumes = generator.predict(z_sample, verbose=3)
在本节中,我们成功地训练了 3D-GAN 的生成器和识别器。 在下一节中,我们将探讨超参数调整和各种超参数优化选项。
可视化损失
要可视化训练的损失,请按如下所示启动tensorboard
服务器。
tensorboard --logdir=logs
现在,在浏览器中打开 localhost:6006
。 TensorBoard 的标量部分包含两种损失的图表:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r8BSrgMU-1681652801317)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/b67a8b95-a1fe-4c06-be44-cec497dd35bf.png)]
生成器网络的损失图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ak0QeZM6-1681652801318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/7937c970-9e91-4f47-bc05-2b230ae1b0c6.png)]
判别器网络的损失图
这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止训练,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 尝试使用超参数,然后选择一组您认为可能会提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。
可视化图
TensorBoard 的GRAPHS
部分包含两个网络的图 。 如果网络表现不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的运算:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ik0IBSFg-1681652801318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/411f5f33-a51c-4f61-a795-4fb06231ea00.png)]
超参数优化
我们训练的模型可能不是一个完美的模型,但是我们可以优化超参数来改进它。 3D-GAN 中有许多可以优化的超参数。 其中包括:
- 批量大小:尝试使用 8、16、32、54 或 128 的批量大小值。
- 周期数:尝试 100 个周期,并将其逐渐增加到 1,000-5,000。
- 学习率:这是最重要的超参数。 用 0.1、0.001、0.0001 和其他较小的学习率进行实验。
- 生成器和判别器网络的不同层中的激活函数:使用 Sigmoid,tanh,ReLU,LeakyReLU,ELU,SeLU 和其他激活函数进行实验。
- 优化算法:尝试使用 Adam,SGD,Adadelta,RMSProp 和 Keras 框架中可用的其他优化器。
- 损失函数:二进制交叉熵是最适合 3D-GAN 的损失函数。
- 两个网络中的层数:根据可用的训练数据量,在网络中尝试不同的层数。 如果您有足够的可用数据来进行训练,则可以使网络更深。
我们还可以通过使用 Hyperopt 或 Hyperas 选择最佳的超参数集。
3D-GAN 的实际应用
3D-GAN 可以广泛用于各种行业,如下所示:
- 制造业:3D-GAN 可以是帮助快速创建原型的创新工具。 他们可以提出创意,并可以帮助模拟和可视化 3D 模型。
- 3D 打印:3D-GAN 生成的 3D 图像可用于在 3D 打印中打印对象。 创建 3D 模型的手动过程非常漫长。
- 设计过程:3D 生成的模型可以很好地估计特定过程的最终结果。 他们可以向我们展示将要构建的内容。
- 新样本:与其他 GAN 相似,3D-GAN 可以生成图像来训练监督模型。
总结
在本章中,我们探讨了 3D-GAN。 我们首先介绍了 3D-GAN,并介绍了架构以及生成器和判别器的配置。 然后,我们经历了建立项目所需的不同步骤。 我们还研究了如何准备数据集。 最后,我们在 Keras 框架中实现了 3D-GAN,并在我们的数据集中对其进行了训练。 我们还探讨了不同的超参数选项。 我们通过探索 3D-GAN 的实际应用来结束本章。
在下一章中,我们将学习如何使用条件生成对抗网络(cGAN)执行人脸老化。
三、使用条件 GAN 进行人脸老化
条件 GAN(cGAN)是 GAN 模型的扩展。 它们允许生成具有特定条件或属性的图像,因此被证明比朴素 GAN 更好。 在本章中,我们将实现一个 cGAN,该 cGAN 一旦经过训练,就可以执行自动人脸老化。 Grigory Antipov,Moez Baccouche 和 Jean-Luc Dugelay 在其名为《使用条件生成对抗网络的人脸老化》的论文中,首先介绍了我们将要实现的 cGAN 网络。 可以在以下链接中找到。
在本章中,我们将介绍以下主题:
- 介绍用于人脸老化的 cGAN
- 建立项目
- 准备数据
- cGAN 的 Keras 实现
- 训练 cGAN
- 评估和超参数调整
- 人脸老化的实际应用
介绍用于人脸老化的 cGAN
到目前为止,我们已经针对不同的用例实现了不同的 GAN 网络。 条件 GAN 扩展了普通 GAN 的概念,并允许我们控制生成器网络的输出。 人脸老化就是在不更改身份的情况下更改一个人的人脸年龄。 在大多数其他模型(包括 GAN)中,由于不考虑人脸表情和人脸配件(例如太阳镜或胡须),因此会使人的外观或身份损失 50%。 Age-cGAN 会考虑所有这些属性。 在本节中,我们将探索用于人脸老化的 cGAN。
了解 cGAN
cGAN 是 GAN 的一种,它取决于一些额外的信息。 我们将额外的y
信息作为额外的输入层提供给生成器。 在朴素 GAN 中,无法控制所生成图像的类别。 当我们向生成器添加条件y
时,我们可以使用y
生成特定类别的图像,该图像可以是任何类型的数据,例如类标签或整数数据 。朴素 GAN 只能学习一个类别,而为多个类别构造 GAN 则非常困难。 但是,可以使用 cGAN 生成针对不同类别具有不同条件的多模式模型。
下图显示了 cGAN 的架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yPtm31WZ-1681652801318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/7b4b89cd-9b42-4609-84e2-94086b34ac3a.png)]
cGAN 的训练目标函数可以表示为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKtYz0Os-1681652801318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/115b5d5f-6623-46dc-bed1-e99a6aa5910c.png)]
此处,G
是生成器网络,D
是判别器网络。 判别器的损失为log D(x|y)
, 生成器的损失为log(1 - D(G(z|y)))
。 我们可以说G(z|y)
在给定z
和y
情况下建模我们数据的分布。 在此,z
是从正态分布得出的大小为 100 的先验噪声分布。
Age-cGAN 的架构
用于人脸老化的 cGAN 的架构稍微复杂一些。 Age-cGan 由四个网络组成:编码器,FaceNet,生成器网络和判别器网络。 使用编码器,我们可以利用潜在的向量z[0]
来学习输入人脸图像和年龄条件的逆映射。 FaceNet 是人脸识别网络,用于学习输入图像x
与重构的图像x_tide
之间的差异。 我们有一个生成器网络,该网络使用由人脸图像和条件向量组成的隐藏表示并生成图像。 判别器网络将区分真实图像和伪图像。
cGAN 的问题在于它们无法学习将属性y
的输入图像x
逆映射到潜向量z
的任务。解决此问题的方法是使用编码器网络。 我们可以训练一个编码器网络来近似输入图像x
的逆映射。 在本节中,我们将探讨 Age-cGAN 涉及的网络。
编码器网络
编码器网络的主要目标是生成所提供图像的潜向量。 基本上,它会拍摄大小为(64, 64, 3)
的图像,并将其转换为 100 维向量。 编码器网络是一个深度卷积神经网络。 网络包含四个卷积块和两个密集层。 每个卷积块包含一个卷积层,一个批标准化层和一个激活函数。 在每个卷积块中,每个卷积层后面都有一个批量归一化层,但第一个卷积层除外。 Age-cGAN 部分的 “Keras 实现”将介绍编码器网络的配置。
生成器网络
生成器的主要目标是生成大小为(64, 64, 3)
的图像。 它需要一个 100 维的潜向量和一些额外的信息y
和尝试生成逼真的图像。 生成器网络也是一个深层卷积神经网络。 它由密集,上采样和卷积层组成。 它采用两个输入值:噪声向量和条件值。 条件值是提供给网络的附加信息。 对于 Age-cGAN,这就是年龄。 生成器网络的配置将在 Age-cGAN 部分的“Keras 实现”中进行介绍。
判别器网络
判别器网络的主要目标是识别所提供的图像是伪造的还是真实的。 它通过使图像经过一系列下采样层和一些分类层来实现。 换句话说,它可以预测图像是真实的还是伪造的。 像其他网络一样,判别器网络是另一个深度卷积网络。 它包含几个卷积块。 每个卷积块都包含一个卷积层,一个批量归一化层和一个激活函数,除了第一个卷积块之外,它没有批量归一化层。 判别器网络的配置将在 Age-cGAN 部分的“Keras 实现”中进行介绍。
人脸识别网络
人脸识别网络的主要目标是在给定图像中识别人的身份。 对于我们的任务,我们将使用没有完全连接的层的预训练的 Inception-ResNet-2 模型。 Keras 有一个很好的预训练模型库。 出于实验目的,您也可以使用其他网络,例如 Inception 或 ResNet-50。 要了解有关 Inception-ResNet-2 的更多信息,请访问链接。 一旦提供了图像,经过预训练的 Inception-ResNet-2 网络将返回相应的嵌入。 可以通过计算嵌入的欧几里德距离来计算针对真实图像和重建图像的提取的嵌入。 关于年龄识别网络的更多信息将在 Age-cGAN 部分的“Keras 实现”中进行介绍。
Age-cGAN 的阶段
Age-cGAN 具有多个训练阶段。 如上一节所述,Age-cGAN 具有四个网络,并经过三个阶段的训练。 Age-cGAN 的训练包括三个阶段:
- 条件 GAN 训练:在此阶段,我们训练生成器网络和判别器网络。
- 初始潜在向量近似:在此阶段,我们训练编码器网络。
- 潜在向量优化:在此阶段,我们同时优化编码器和生成器网络。
以下屏幕截图显示了 Age-cGAN 的各个阶段:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VneaMWyP-1681652801318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/3019472f-d08b-4fa2-9ea1-071be37bc6bd.png)]
Age-cGAN 的各个阶段,资料来源:使用条件生成对抗网络进行人脸老化
我们将在以下部分介绍 Age- cGAN 的所有阶段。
条件 GAN 的训练
在这个阶段,我们训练生成器网络和判别器网络。 经过训练后,生成器网络可以生成人脸的模糊图像。 此阶段类似于训练朴素 GAN,其中我们同时训练两个网络。
训练目标函数
用于训练 cGAN 的训练目标函数可以表示为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9kUVMlic-1681652801319)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/0e7b1187-382e-4584-9c8b-b39c1db1fd91.png)]
训练 cGAN 网络涉及优化函数,ν(θ[G], θ[D])
。 训练 cGAN 可以看作是 minimax 游戏,其中同时对生成器和判别器进行训练。 在上式中, θ[G}
代表生成器网络的参数,θ[D]
代表G
和D
的参数 , log D(x, y)
是判别器模型的损失,log(1 - D(G(z|y_tide), y_tide))
是生成器模型的损失, P(data)
是所有可能的图像的分布。
初始潜在向量近似
初始潜在向量近似是一种近似潜在向量以优化人脸图像重建的方法。 为了近似一个潜在向量,我们有一个编码器网络。 我们在生成的图像和真实图像上训练编码器网络。 训练后,编码器网络将开始从学习到的分布中生成潜在向量。 用于训练编码器网络的训练目标函数是欧几里得距离损失。
潜在向量优化
在潜在向量优化过程中,我们同时优化了编码器网络和生成器网络。 我们用于潜在向量优化的方程式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JtkLbpdA-1681652801319)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/98bda784-ced5-4608-8c1d-38477d40cb8f.png)]
FR
是人脸识别网络。 该方程式表明,真实图像和重建图像之间的欧几里得距离应最小。 在此阶段,我们尝试最小化距离以最大程度地保留身份。
设置项目
如果尚未使用所有章节的完整代码克隆存储库,请立即克隆存储库。 克隆的存储库有一个名为Chapter03
的目录,其中包含本章的全部代码。 执行以下命令来设置项目:
- 首先,导航到父目录,如下所示:
cd Generative-Adversarial-Networks-Projects
- 现在,将目录从当前目录更改为
Chapter03
:
cd Chapter03
- 接下来,为该项目创建一个 Python 虚拟环境:
virtualenv venv virtualenv venv -p python3 # Create a virtual environment using python3 interpreter virtualenv venv -p python2 # Create a virtual environment using python2 interpreter
我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。
- 接下来,激活新创建的虚拟环境:
source venv/bin/activate
激活虚拟环境后,所有其他命令将在此虚拟环境中执行。
- 接下来,通过执行以下命令,安装
requirements.txt
文件中提供的所有库:
pip install -r requirements.txt
您可以参考 README.md
文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。
在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一部分中,我们将处理数据集。
准备数据
在本章中,我们将使用Wiki-Cropped
数据集,其中包含 64 张以上 328 张不同人脸的图像。 作者还提供了数据集 ,该数据集仅包含已裁剪的人脸,因此我们无需裁剪人脸。
论文《没有人脸标志的单张图像的真实年龄和外表年龄的深度期望》,可在以下网址获得。作者从维基百科抓取了这些图片,并将其用于学术目的。 如果您打算将数据集用于商业目的,请通过以下方式与作者联系:rrothe@vision.ee.ethz.ch
。
您可以从以下链接手动下载数据集,并将所有压缩文件放置在 Age-cGAN 项目内的目录中。
执行以下步骤以下载和提取数据集。
下载数据集
要下载仅包含裁剪的面的数据集,请执行以下命令:
# Before download the dataset move to data directory cd data # Wikipedia : Download faces only wget https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/static/wiki_crop.tar
提取数据集
下载数据集后,手动将文件提取到数据文件夹中,或执行以下命令来提取文件:
# Move to data directory cd data # Extract wiki_crop.tar tar -xvf wiki_crop.tar
wiki_crop.tar
文件包含 62,328 张图像和一个包含所有标签的wiki.mat
文件。 scipy.io
库有一个称为loadmat
的方法,这是在 Python 中加载.mat
文件的非常方便的方法。 使用以下代码加载提取的.mat
文件:
def load_data(wiki_dir, dataset='wiki'): # Load the wiki.mat file meta = loadmat(os.path.join(wiki_dir, "{}.mat".format(dataset))) # Load the list of all files full_path = meta[dataset][0, 0]["full_path"][0] # List of Matlab serial date numbers dob = meta[dataset][0, 0]["dob"][0] # List of years when photo was taken photo_taken = meta[dataset][0, 0]["photo_taken"][0] # year # Calculate age for all dobs age = [calculate_age(photo_taken[i], dob[i]) for i in range(len(dob))] # Create a list of tuples containing a pair of an image path and age images = [] age_list = [] for index, image_path in enumerate(full_path): images.append(image_path[0]) age_list.append(age[index]) # Return a list of all images and respective age return images, age_list
photo_taken
变量是年份列表,dob
是 Matlab 列表中相应照片的序列号。 我们可以根据序列号和照片拍摄的年份来计算该人的年龄。 使用以下代码来计算年龄:
def calculate_age(taken, dob): birth = datetime.fromordinal(max(int(dob) - 366, 1)) # assume the photo was taken in the middle of the year if birth.month < 7: return taken - birth.year else: return taken - birth.year - 1
现在,我们已经成功下载并提取了数据集。 在下一节中,让我们研究 Age-cGAN 的 Keras 实现。
Age-cGAN 的 Keras 实现
像普通 GAN 一样,cGAN 的实现非常简单。 Keras 提供了足够的灵活性来编码复杂的生成对抗网络。 在本节中,我们将实现 cGAN 中使用的生成器网络,判别器网络和编码器网络。 让我们从实现编码器网络开始。
在开始编写实现之前,创建一个名为 main.py
的 Python 文件,并导入基本模块,如下所示:
import math import os import time from datetime import datetime import matplotlib.pyplot as plt import numpy as np import tensorflow as tf from keras import Input, Model from keras.applications import InceptionResNetV2 from keras.callbacks import TensorBoard from keras.layers import Conv2D, Flatten, Dense, BatchNormalization, Reshape, concatenate, LeakyReLU, Lambda, \ K, Conv2DTranspose, Activation, UpSampling2D, Dropout from keras.optimizers import Adam from keras.utils import to_categorical from keras_preprocessing import image from scipy.io import loadmat
编码器网络
编码器网络是卷积神经网络(CNN),可将图像(x
)编码为潜向量(z
)或潜在的向量表示。 让我们从在 Keras 框架中实现编码器网络开始。
执行以下步骤来实现编码器网络:
- 首先创建一个输入层:
input_layer = Input(shape=(64, 64, 3))
- 接下来,添加第一个卷积块,其中包含具有激活函数的 2D 卷积层,具有以下配置:
- 过滤器:
32
- 核大小:
5
- 步幅:
2
- 填充:
same
- 激活:
LeakyReLU
,其中alpha
等于0.2
:
# 1st Convolutional Block enc = Conv2D(filters=32, kernel_size=5, strides=2, padding='same')(input_layer) enc = LeakyReLU(alpha=0.2)(enc)
- 接下来,再添加三个卷积块,其中每个都包含一个 2D 卷积层,然后是一个批量归一化层和一个激活函数,具有以下配置:
- 过滤器:
64
,128
,256
- 核大小:
5
,5
,5
- 步幅:
2
,2
,2
- 填充:
same
,same
,same
- 批量规范化:每个卷积层后面都有一个批量规范化层
- 激活:
LealyReLU
,LeakyReLU
,LeakyReLU
,其中alpha
等于0.2
:
# 2nd Convolutional Block enc = Conv2D(filters=64, kernel_size=5, strides=2, padding='same')(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc) # 3rd Convolutional Block enc = Conv2D(filters=128, kernel_size=5, strides=2, padding='same')(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc) # 4th Convolutional Block enc = Conv2D(filters=256, kernel_size=5, strides=2, padding='same')(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc)
- 接下来,将最后一个卷积块的输出展开,如下所示:
# Flatten layer enc = Flatten()(enc)
将n
维张量转换为一维张量(数组)称为“扁平化”。
- 接下来,添加具有以下配置的密集(完全连接)层,批量规范化层和激活函数:
- 单元(节点):2,096
- 批量规范化:是
- 激活:
LeakyReLU
,其中alpha
等于0.2
:
# 1st Fully Connected Layer enc = Dense(4096)(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc)
- 接下来,使用以下配置添加第二个密集(完全连接)层:
- 单元(节点):
100
- 激活:无:
# Second Fully Connected Layer enc = Dense(100)(enc)
- 最后,创建 Keras 模型并指定编码器网络的输入和输出:
# Create a model model = Model(inputs=[input_layer], outputs=[enc])
编码器网络的完整代码如下所示:
def build_encoder(): """ Encoder Network :return: Encoder model """ input_layer = Input(shape=(64, 64, 3)) # 1st Convolutional Block enc = Conv2D(filters=32, kernel_size=5, strides=2, padding='same')(input_layer) enc = LeakyReLU(alpha=0.2)(enc) # 2nd Convolutional Block enc = Conv2D(filters=64, kernel_size=5, strides=2, padding='same')(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc) # 3rd Convolutional Block enc = Conv2D(filters=128, kernel_size=5, strides=2, padding='same')(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc) # 4th Convolutional Block enc = Conv2D(filters=256, kernel_size=5, strides=2, padding='same')(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc) # Flatten layer enc = Flatten()(enc) # 1st Fully Connected Layer enc = Dense(4096)(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc) # Second Fully Connected Layer enc = Dense(100)(enc) # Create a model model = Model(inputs=[input_layer], outputs=[enc]) return model
我们现在已经成功地为编码器网络创建了 Keras 模型。 接下来,为生成器网络创建 Keras 模型。
生成对抗网络项目:1~5(3)https://developer.aliyun.com/article/1426894