生成对抗网络项目:1~5

news/2024/11/17 0:20:27/

原文:Generative Adversarial Networks Projects

协议:CC BY-NC-SA 4.0

译者:飞龙

本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。

不要担心自己的形象,只关心如何实现目标。——《原则》,生活原则 2.3.c

一、生成对抗网络简介

在本章中,我们将研究生成对抗网络GAN)。 它们是一种深度神经网络架构,它使用无监督的机器学习来生成数据。 他们在 2014 年由 Ian Goodfellow,Yoshua Bengio 和 Aaron Courville 的论文中介绍,可在以下链接中找到。 GAN 具有许多应用,包括图像生成和药物开发。

本章将向您介绍 GAN 的核心组件。 它将带您了解每个组件的工作方式以及 GAN 背后的重要概念和技术。 它还将简要概述使用 GAN 的优缺点 ,并深入了解某些实际应用。

本章将通过探讨以下主题来涵盖所有这些要点:

  • 什么是 GAN?
  • GAN 的架构
  • 与 GAN 相关的重要概念
  • GAN 的不同种类
  • GAN 的优缺点
  • GAN 的实际应用

什么是 GAN?

GAN 是由两个网络(生成器网络和判别器网络)组成的深度神经网络架构。 通过生成和辨别的多个周期,两个网络互相训练,同时试图互相取胜。

什么是生成器网络?

生成器网络使用现有数据来生成新数据。 例如,它可以使用现有图像生成新图像。 生成器的主要目标是从随机生成的数字向量(称为潜在空间)生成数据(例如图像,视频,音频或文本)。 在创建生成器网络时,我们需要指定网络的目标。 这可能是图像生成,文本生成,音频生成,视频生成等。

什么是判别器网络?

判别器网络试图区分真实数据和生成器网络生成的数据。 判别器网络尝试将传入的数据放入预定义的类别。 它可以执行多类分类或二分类。 通常,在 GAN 中执行二分类。

通过 GAN 中的对抗游戏进行训练

在 GAN 中,网络是通过对抗性游戏来训练的:两个网络相互竞争。 例如,假设我们要让 GAN 伪造艺术品:

  1. 第一个网络,即生成器,从未见过真实的艺术品,但正在尝试创建看起来像真实的艺术品。
  2. 第二个网络是判别器,试图识别艺术品是真实的还是伪造的。
  3. 生成器反过来试图通过在多次迭代中创建更逼真的艺术品来欺骗判别器,使其认为其假货是真实的交易。
  4. 判别器试图通过继续完善其自己的确定伪造品的标准来胜过生成器。
  5. 他们通过在每次迭代中对自己的过程中所做的成功更改提供反馈,从而互相指导。 此过程是 GAN 的训练。
  6. 最终,判别器将生成器训练到无法再确定哪个艺术品是真实的和哪个艺术品是伪造的这一点。

在此游戏中,同时训练两个网络。 当我们达到区分者无法区分真假艺术品的阶段时,网络便达到了称为纳什均衡的状态。 这将在本章稍后讨论。

GAN 的实际应用

GAN 具有一些相当有用的实际应用,其中包括:

  • 图像生成:生成器网络在经过样本图像训练后,可用于生成逼真的图像。 例如,如果我们要生成新的狗图像,则可以在狗的图像的数千个样本上训练 GAN。 训练完成后,生成器网络将能够生成与训练集中的图像不同的新图像。 图像生成用于市场营销,徽标生成,娱乐,社交媒体等。 在下一章中,我们将生成动漫人物的面孔。
  • 文本到图像的合成:从文本描述生成图像是 GAN 的一个有趣用例。 由于 GAN 能够根据您编写的某些文本生成新数据,因此在电影行业中可能会有所帮助。 在漫画行业,可以自动生成故事序列。
  • 人脸老化:对于娱乐和监视行业都非常有用。 这对于人脸验证特别有用,因为这意味着公司不需要随着人的变老而更改其安全系统。 age-cGAN 网络可以生成不同年龄的图像,然后可以将其用于训练用于人脸验证的鲁棒模型。
  • 图像到图像的转换:图像到图像的转换可用于将白天拍摄的图像转换为夜晚拍摄的图像,并将草图转换为绘画 ,以将图像的样式设置为类似于毕加索或梵高的绘画,将航空图像自动转换为卫星图像 ,并将马的图像转换为斑马的图像。 这些用例具有突破性,因为它们可以节省我们的时间。
  • 视频合成: GAN 也可以用于生成视频。 与我们手动创建内容相比,它们可以在更少的时间内生成内容。 它们可以提高电影创作者的工作效率,还可以使想在业余时间制作创意视频的业余爱好者获得支持。
  • 高分辨率图像生成:如果使用低分辨率相机拍摄照片,GAN 可以帮助您生成高分辨率图像而不会丢失任何基本细节。 这在网站上可能很有用。
  • 补全图像的缺失部分:如果您的图像中有些缺失的部分,GAN 可以帮助您恢复这些部分。

GAN 的详细架构

GAN 的架构具有两个基本元素:生成器网络和判别器网络。 每个网络都可以是任何神经网络,例如人工神经网络ANN),卷积神经网络CNN), 循环神经网络RNN),或长短期记忆LSTM)。 判别器必须具有完全连接的层,最后是分类器。

让我们仔细看一下 GAN 架构的组件。 在此示例中,我们将想象正在创建一个虚拟 GAN。

生成器的架构

虚拟 GAN 中的生成器网络是一个简单的前馈神经网络,具有五层:输入层,三个隐藏层和输出层。 让我们仔细看看生成器(虚拟)网络的配置:

编号层名称配置
1输入层input_shape=(batch_size, 100)output_shape=(batch_size, 100)
2密集层neurons=500input_shape=(batch_size, 100)output_shape=(batch_size, 500)
3密集层neurons=500input_shape=(batch_size, 500)output_shape=(batch_size, 500)
4密集层neurons=784input_shape=(batch_size, 500)output_shape=(batch_size, 784)
5重塑层input_shape=(batch_size, 784)output_shape=(batch_size, 28, 28)

上表显示了隐藏层的配置,以及网络中的输入和输出层。

下图显示了生成器网络中每一层的张量流以及张量的输入和输出形状:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-httkCl5a-1681652801299)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/9c07dac5-3b55-444a-b7a5-6119c5d9370b.png)]

生成器网络的架构。

让我们讨论一下前馈神经网络在数据正向传播期间如何处理信息:

  • 输入层采用从高斯(正态)分布中采样的 100 维向量,并将张量直接传递给第一隐藏层。

  • 三个隐藏层是分别具有 500、500 和 784 个单元的密集层。 第一隐藏层(密集层)将形状为[batch_size, 100]的张量转换为形状为[batch_size, 500]的张量。

  • 第二密集层生成形状为[batch_size, 500]的张量。

  • 第三隐藏层生成[batch_size, 784]形状的张量。

  • 在最后一个输出层中,该张量从[batch_size, 784]的形状改成[batch_size, 28, 28]的形状。 这意味着我们的网络将生成一批图像,其中一个图像的形状为[28, 28]

判别器的架构

我们 GAN 中的判别器是前馈神经网络,它具有五层,包括一个输入层和一个输出层,以及三个密集层。 判别器网络是一个分类器,与生成器网络略有不同。 它处理图像并输出该图像属于特定类别的概率。

下图显示了判别器网络中每一层的张量流以及张量的输入和输出形状:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zWqb8Pey-1681652801301)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/5577e65f-d54f-48ad-9f22-950a7abf3c36.png)]

判别器网络的架构。

让我们讨论判别器如何在网络训练期间以正向传播方式处理数据:

  1. 最初,它接收形状为28x28的输入。
  2. 输入层采用输入张量,该张量是形状为( batch_sizex28x28 )的张量,然后将其直接传递到第一隐藏层。
  3. 接下来,平坦化层将张量平坦化为 784 维向量,该向量将传递到第一隐藏(密集)层。 第一和第二隐藏层将其修改为 500 维向量。
  4. 最后一层是输出层,又是一个密集层,其中一个单元( 神经元)和 Sigmoid 为激活函数。 它输出一个值为 0 或 1 的值。值为 0 表示提供的图像是假的,而值为 1 表示提供的图像是真实的。

与 GAN 相关的重要概念

现在我们已经了解了 GAN 的架构,下面让我们看一下一些重要概念的简要概述。 我们首先来看 KL 散度。 理解 JS 差异非常重要,这是评估模型质量的重要措施。 然后,我们将研究纳什均衡,这是我们在训练中试图达到的状态。 最后,我们将仔细研究目标函数,了解这些目标函数对于很好地实现 GAN 至关重要。

Kullback-Leibler 散度

Kullback-Leibler 散度KL 散度),也称为相对熵,是一种用于识别两个概率分布之间相似性的方法。 它测量一个概率分布p与第二个预期概率分布q的差异。

用于计算两个概率分布p(x)q(x)之间的 KL 散度的公式如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fnMu768A-1681652801302)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/3ed12abb-fb0d-4205-848a-928127ec92ca.png)]

p(x)等于q(x)时,KL 散度将为零或最小值。

由于 KL 散度的不对称性质,我们不应该使用它来度量两个概率分布之间的距离。 因此,不应将其用作距离度量。

Jensen-Shannon 散度

Jensen-Shannon 散度(也称为信息半径IRaD)或总散度与平均值)是两个概率分布之间相似度的另一种度量。 它基于 KL 散度。 但是,与 KL 散度不同,JS 散度本质上是对称的,可用于测量两个概率分布之间的距离。 如果我们采用 Jensen-Shannon 发散的平方根,则会得到 Jensen-Shannon 距离,因此它是距离度量。

以下等式表示两个概率分布pq之间的 Jensen-Shannon 散度:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6B0QW1XI-1681652801302)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/ce0afe99-4137-4673-985a-b073ab66c347.png)]

在前面的等式中,p + q是中点度量,而D[KL]是 Kullback-Leibler 散度。

现在我们已经了解了 KL 散度和 Jenson-Shannon 散度,让我们讨论 GAN 的纳什均衡。

纳什均衡

纳什均衡描述了博弈论中的特定状态。 在非合作游戏中可以达到这种状态,在这种游戏中,每个玩家都根据自己对其他玩家的期望,尝试选择最佳策略来为自己获得最佳结果。 最终,所有参与者都达到了根据其他参与者做出的决定为自己选择最佳策略的地步。 在游戏的这一点上,他们不会从改变策略中获得任何好处。 这种状态是纳什均衡。

关于如何达到纳什均衡的一个著名例子是《囚徒困境》。 在此示例中,两名犯罪分子(A 和 B)因犯罪而被捕。 两者都放置在单独的单元中,彼此之间无法通信。 检察官只有足够的证据将他们定罪为较小的罪行,而没有主要罪行,这将使他们长期入狱。 为了定罪,检察官向他们提出要约:

  • 如果 A 和 B 都牵涉对方主要罪行,则将分别服刑 2 年。
  • 如果 A 暗示 B 但 B 保持沉默,则 A 将被释放,B 将被判入狱 3 年(反之亦然)。
  • 如果 A 和 B 都保持安静,则他们以较少的费用只能服刑 1 年。

从这三种情况来看,很明显,A 和 B 的最佳可能结局是保持安静并在监狱服刑 1 年。 但是,保持安静的风险为 3 年,因为 A 和 B 都无法知道对方也将保持安静。 因此,他们将达到一种状态,在这种状态下,他们的实际最佳策略是供认,因为这是提供最高奖励和最低惩罚的选择。 当达到这种状态时,任何罪犯都不会通过改变策略来获得任何好处; 因此,他们将达到纳什均衡。

目标函数

为了创建一个生成器网络,该生成器网络生成与真实图像相似的图像,我们尝试提高生成器生成的数据与实际数据之间的相似性。 为了测量相似度,我们使用目标函数。 这两个网络都有自己的目标函数,并且在训练过程中,它们会尽量减少各自的目标函数。 以下方程式表示 GAN 的最终目标函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fSRWpuW5-1681652801302)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/c682e8fd-bd22-4998-968f-227c26898f62.png)]

在前面的等式中,D(x)是判别器模型,G(z)是生成器模型,P(x)是实际数据分布,P(z)是生成器生成的数据的分布,E是预期的输出。

在训练期间,D判别器)想要使整个输出最大化,而G(生成器)希望使整个输出最小化,从而训练 GAN 使生成器和判别器网络达到平衡。 当它达到平衡时,我们说模型已经收敛。 这个平衡就是纳什平衡。 训练完成后,我们将获得一个生成器模型,该模型能够生成逼真的图像。

评分算法

计算 GAN 的准确率很简单。 GAN 的目标函数不是特定函数,例如均方误差或交叉熵。 GAN 在训练期间学习目标函数。 研究人员提出了许多计分算法来衡量模型的拟合程度。 让我们详细了解一些评分算法。

初始分数

初始分数是 GAN 使用最广泛的评分算法。 它使用预训练的 Inception V3 网络(在 Imagenet 上进行训练)来提取生成图像和真实图像的特征。 由 Shane Barrat 和 Rishi Sharma 在其论文《关于初始分数的注解》中提出。 初始分数或简称 IS,用于衡量所生成图像的质量和多样性。 让我们看一下IS的等式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mxFU5gtU-1681652801303)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/0d33c46a-0a5f-4027-919c-30b910e6d93b.png)]

在前面的等式中,符号x表示从分布中采样的样本。 P(g)x ~ P(g)代表相同的概念。 P(y|x)是条件类别分布,P(y)是边际类别分布。

要计算初始分数,请执行以下步骤:

  1. 首先采样由模型生成的N个图像,表示为(x^i)

  2. 然后,使用以下公式构建边际类分布:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bm1pI3Sf-1681652801304)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/f60e4201-0e24-4dab-b19c-c69d11994427.png)]

  3. 然后,使用以下公式计算 KL 散度和预期的改进:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNKVAcEV-1681652801304)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/0d33c46a-0a5f-4027-919c-30b910e6d93b.png)]

  4. 最后,计算结果的指数以得出初始分数。

如果模型的初始得分高,则其质量会很好。 尽管这是一项重要措施,但仍存在某些问题。 例如,即使模型每类生成一张图像,它也显示出很高的准确率,这意味着模型缺乏多样性。 为了解决此问题,提出了其他表现指标。 在下一节中,我们将介绍其中之一。

Fréchet 起始距离

为了克服初始分数的各种缺点,Martin Heusel 等人在他们的论文中提出了 Fréchlet 初始距离FID),《收敛到局部纳什均衡的两个时间维度更新规则所训练的 GAN》。

计算 FID 分数的公式如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kCNWapw2-1681652801304)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/c23de9d4-5560-4c9f-a0bd-a3569bbf6f5a.png)]

前面的等式表示实际图像x,与生成的图像g之间的 FID 分数。 为了计算 FID 分数,我们使用 Inception 网络从 Inception 网络的中间层提取特征映射。 然后,我们对多元高斯分布建模,以学习特征映射的分布。 此多元高斯分布具有μ的均值和的协方差,我们用它们来计算 FID 得分。 FID 分数越低,模型越好,它越有能力生成更高质量的更多图像。 完美的生成模型的 FID 得分为零。 使用 FID 得分而不是 Inception 得分的优势在于它对噪声具有鲁棒性,并且可以轻松地测量图像的多样性。

FID 的 TensorFlow 实现可在以下链接中找到。

学术界和工业界的研究人员最近提出了更多的评分算法。 我们不会在这里介绍所有这些内容。 在进一步阅读之前,请查看另一种称为模式得分的评分算法,有关该算法的信息可在以下链接中找到。

GAN 的变体

当前有成千上万种不同的 GAN,并且这个数字正以惊人的速度增长。 在本节中,我们将探索六种流行的 GAN 架构,我们将在本书的后续章节中对其进行更详细的介绍。

深度卷积生成对抗网络

Alec Radford,Luke Metz 和 Soumith Chintala 在名为《使用深度卷积生成对抗网络的无监督表示学习》的论文中提出了深层卷积 GANDCGAN),可通过以下链接获得 。 朴素 GAN 通常在其网络中没有卷积神经网络CNN)。 这是在 DCGAN 的引入下首次提出的。 我们将在第 3 章,“使用条件 GAN 进行人脸老化”中,学习如何使用 DCGAN 生成动漫人脸。

StackGAN

StackGAN 由 Han Zhang,Tao Xu,Li Hongsheng Li 等人在其题为《StackGAN:使用堆叠式生成对抗网络进行照片般逼真的文本到图像合成》的论文中提出,可从以下链接获得。 他们使用 StackGAN 来探索文本到图像的合成,并获得了令人印象深刻的结果。 StackGAN 是一对网络,当提供文本描述时,它们会生成逼真的图像。 我们将在第 6 章, “StackGAN – 文本到真实图像的图像合成”中,学习如何使用 StackGAN 从文本描述生成逼真的图像。。

循环 GAN

CycleGAN 由朱俊彦,Taesung Park,Phillip Isola 和 Alexei A. Efros 在题为《使用循环生成对抗网络的不成对图像到图像翻译》的论文中提出。 在以下链接中。 CycleGAN 具有一些非常有趣的潜在用途,例如将照片转换为绘画,反之亦然,将夏天拍摄的照片转换为冬天,反之亦然,或者将马的图像转换为斑马的图像,反之亦然。 我们将在第 7 章, “CycleGAN - 将绘画变成照片”中学习如何使用 CycleGAN 将绘画变成照片。

3D GAN

3D-GAN 由 Wu Jiajun Wu,Zhengkai Zhang,薛天凡,William T. Freeman 和 Joshua B. Tenenbaum 在其名为《通过 3D 生成对抗建模来学习对象形状的概率潜在空间》的论文中提出,可通过以下链接获得。 在制造业和 3D 建模行业中,生成对象的 3D 模型具有许多用例。 一旦在对象的 3D 模型上进行训练,3D-GAN 网络便能够生成不同对象的新 3D 模型。 我们将在第 2 章, “使用 3D-GAN 生成形状”中学习如何使用 3D-GAN 生成对象的 3D 模型。

人脸老化

有条件 GAN 的人脸老化由 Grigory Antipov,Moez Baccouche 和 Jean-Luc Dugelay 在他们的题为《使用条件生成对抗网络的人脸老化》的论文中提出,可通过以下链接获得。 人脸老化具有许多行业用例,包括跨年龄的人脸识别,寻找迷路的孩子以及娱乐。 我们将在第 3 章,“使用条件 GAN 的人脸老化”中,学习如何训练条件 GAN 生成给定目标年龄的人脸。

pix2pix

pix2pix 网络是由 Phillip Isola,朱俊彦,周廷辉和 Alexei A. Efros 在他们的论文《使用条件对抗网络的图像到图像翻译》中介绍的,可通过以下链接获得。 pix2pix 网络具有与 CycleGAN 网络类似的用例。 它可以将建筑物标签转换为建筑物图片(我们将在 pix2pix 章节中看到一个类似的示例),黑白图像转换为彩色图像,将白天至夜晚的图像,草图转换为照片, 和航拍图像到类似地图的图像。

有关现有所有 GAN 的列表,请参阅 GAN Zoo,这是 Avinash Hindupur 的文章。

GAN 的优势

与其他有监督或无监督学习方法相比,GAN 具有某些优势:

  • GAN 是一种无监督的学习方法 :获取带标签的数据是一个手动过程,需要花费大量时间。 GAN 不需要标签数据; 当他们学习数据的内部表示时,可以使用未标记的数据来训练他们。

  • GAN 生成数据:关于 GAN 的最好的事情之一是,它们生成的数据类似于真实数据。 因此,它们在现实世界中有许多不同的用途。 它们可以生成与真实数据无法区分的图像,文本,音频和视频。 GAN 生成的图像可应用于营销,电子商务,游戏,广告和许多其他行业。

  • GAN 学习数据的密度分布:GAN 学习数据的内部表示。 如前所述,GAN 可以学习混乱而复杂的数据分布。 这可以用于许多机器学习问题。

  • 受过训练的判别器是分类器:经过训练,我们得到了判别器和生成器。 判别器网络是分类器,可用于分类对象。

训练 GAN 的问题

与任何技术一样,GAN 也有一些问题。 这些问题通常与训练过程有关,包括模式崩溃,内部协变量偏移和梯度消失。 让我们更详细地看看这些。

模式崩溃

模式崩溃是指生成器网络生成变化不大的样本或模型开始生成相同图像的情况。 有时,概率分布实际上是多峰的,非常复杂。 这意味着它可能包含来自不同观察值的数据,并且对于样本的不同子图可能具有多个峰。 有时,GAN 不能为数据的多峰概率分布建模,并且会遭受模式崩溃。 所有生成的样本实际上都相同的情况称为完全崩溃。

我们可以使用许多方法来克服模式崩溃问题。 其中包括:

  • 通过针对不同模式训练多个模型(GAN)

  • 通过使用各种数据样本来训练 GAN

梯度消失

在反向传播期间,梯度从最后一层向第一层反向流动。 随着向后流动,它变得越来越小。 有时,梯度是如此之小,以至于初始层学习非常缓慢或完全停止学习。 在这种情况下,梯度完全不会改变初始层的权重值,因此有效地停止了网络中初始层的训练。 这被称为梯度消失问题。

如果我们使用基于梯度的优化方法训练更大的网络,此问题将变得更加严重。 当我们少量改变参数值时,基于梯度的优化方法通过计算网络输出的变化来优化参数值。 如果参数值的变化导致网络输出的微小变化,则权重变化将非常小,因此网络将停止学习。

当我们使用 Sigmoid 和 Tanh 等激活函数时,这也是一个问题。 Sigmoid 激活函数将值限制在 0 到 1 之间,将x的大值转换为大约 1,将x的小值或负值转换为大约零。 Tanh 激活函数将输入值压缩为 -1 和 1 之间的范围,将大输入值转换为大约 1,将小值转换为大约负 1。当应用反向传播时,我们使用微分链式规则, 有倍增作用。 当我们到达网络的初始层时,梯度(误差)呈指数下降,从而导致梯度消失。

为了克服这个问题,我们可以使用激活函数,例如 ReLU,LeakyReLU 和 PReLU。 这些激活函数的梯度在反向传播期间不会饱和,从而导致神经网络的有效训练。 另一种解决方案是使用批量规范化,该规范化将对网络隐藏层的输入规范化。

内部协变量偏移

当我们网络的输入分配发生变化时,就会发生内部协变量偏移。 当输入分布更改时,隐藏层将尝试学习以适应新的分布。 这减慢了训练过程。 如果进程变慢,则需要很长时间才能收敛到全局最小值。 当网络输入的统计分布与以前看到的输入完全不同时,就会出现此问题。 批量规范化和其他归一化技术可以解决此问题。 我们将在以下各节中进行探讨。

训练 GAN 时解决稳定性问题

训练稳定性是与 GAN 相关的最大问题之一。 对于某些数据集,由于此类问题,GAN 从未收敛。 在本节中,我们将研究一些可用于提高 GAN 稳定性的解决方案。

特征匹配

在 GAN 的训练过程中,我们使判别器网络的目标函数最大化,并使生成器网络的目标函数最小化。 这个目标函数有一些严重的缺陷。 例如,它没有考虑生成的数据和实际数据的统计信息。

特征匹配是 Tim Salimans,Ian Goodfellow 等人在其题为《GAN 训练的改进技术》的论文中提出的一种技术,旨在通过引入新的目标函数来改善 GAN 的收敛性。 生成器网络的新目标函数鼓励其生成具有统计信息的数据,该数据与真实数据相似。

要应用特征映射,网络不会要求判别器提供二进制标签。 相反,判别器网络提供从判别器网络的中间层提取的输入数据的激活或特征映射。 从训练的角度来看,我们训练判别器网络以学习真实数据的重要统计信息。 因此,目的在于通过学习那些判别特征,它应该能够从假数据中区分出真实数据。

为了从数学上理解这种方法,让我们首先看一下不同的表示法:

  • f(x):来自判别器网络中间层的真实数据的激活图或特征映射
  • f(G(z)):生成器网络从判别器网络中的中间层生成的数据的激活/特征映射。

这个新的目标函数可以表示为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNmfEk9j-1681652801305)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/d1541159-75a5-487e-805f-80a8c97436bd.png)]

使用此目标函数可以获得更好的结果,但是仍然不能保证收敛。

小批量判别

小批量判别是稳定 GAN 训练的另一种方法。 它是由 Ian Goodfellow 等人在《GAN 训练的改进技术》中提出的,该技术可从这里获得。 为了理解这种方法,让我们首先详细研究问题。 在训练 GAN 时,当我们将独立的输入传递给判别器网络时,梯度之间的协调可能会丢失,这将阻止判别器网络学习如何区分由生成器网络生成的各种图像。 这是模式崩溃,这是我们之前讨论的问题。 为了解决这个问题,我们可以使用小批量判别。 下图很好地说明了此过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y1ekTFa1-1681652801305)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/f542b337-7bc3-475e-b3ee-00a963352321.png)]

小批判别是一个多步骤的过程。 执行以下步骤,将小批量判别添加到您的网络:

  1. 提取样本的特征映射,然后将它们与张量T ∈ R^(A×B×C)相乘,生成矩阵M[i] ∈ R^(A×B)
  2. 然后,使用以下公式计算矩阵M[i]的行之间的 L1 距离:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xnqi95XT-1681652801306)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/4769bf48-3e9e-4976-999d-22513076b07d.png)]

  1. 然后,为特定示例x[i]计算所有距离的总和:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UKGen7Lx-1681652801306)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/c236f76b-da88-4ac1-9c4a-50cc4c3acb59.png)]

  1. 然后,将o(x[i])f(x[i])连接起来,并将其馈送到网络的下一层:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wHIbSBgi-1681652801307)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/d399507e-b812-41a5-8731-9b7e2ebf48aa.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5MwTvp7g-1681652801307)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/56ceb926-b75b-490c-91ff-9b736d389cb5.png)]

为了从数学上理解这种方法,让我们仔细看一下各种概念:

  • f(x[i]):来自判别器网络中间层的第i个样本的激活图或特征映射
  • T ∈ R^(A×B×C):三维张量,我们乘以f(x[i])
  • M[i] ∈ R^(A×B):将张量Tf(x[i])相乘时生成的矩阵
  • o(x[i]):对于特定示例x[i],采用所有距离的总和后的输出

小批量判别有助于防止模式崩溃,并提高训练稳定性的机会。

历史平均

历史平均是一种获取过去参数平均值并将其添加到生成器和判别器网络的各个成本函数的方法。 它是由 Ian Goodfellow 等人在先前提到的《GAN 训练的改进技术》中提出的。

历史平均值可以表示为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3nlUhEH-1681652801307)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/04528291-1308-47df-8d56-5b242a809cb1.png)]

在前面的等式中,θ[i]是在特定时间i的参数值。 这种方法也可以提高 GAN 的训练稳定性。

单面标签平滑

之前,分类器的标签/目标值为 0 或 1; 0 代表假图片,1 代表真实图片。 因此,GAN 容易出现对抗性示例,其中是神经网络的输入,导致神经网络的输出不正确。 标签平滑是一种向判别器网络提供平滑标签的方法。 这意味着我们可以使用十进制值,例如 0.9(真实),0.8(真实),0.1(虚假)或 0.2(虚假),而不是将每个示例都标记为 1(真实)或 0(虚假)。 我们对真实图像和伪图像的目标值(标签值)进行平滑处理。 标签平滑可以降低 GAN 中对抗性示例的风险。 要应用标签平滑,请为图像分配标签 0.9、0.8 和 0.7、0.1、0.2 和 0.3。 要查找有关标签平滑的更多信息,请参阅以下论文。

批量规范化

批量规范化是一种对特征向量进行归一化以使其没有均值或单位方差的技术。 它用于稳定学习并处理较差的权重初始化问题。 这是预处理步骤,适用于网络的隐藏层,可帮助我们减少内部协变量偏移。

批量规范化由 Ioffe 和 Szegedy 在其 2015 年论文《批量规范化:通过减少内部协变量偏移来加速深度网络训练》中引入。可以在以下链接中找到。

The benefits of batch normalization are as follows:

  • 减少内部协变量偏移:批量规范化有助于我们通过归一化值来减少内部协变量偏移。
  • 更快的训练:如果从正态/高斯分布中采样值 ,则网络将更快地训练。 批量规范化有助于将值白化到我们网络的内部层。 总体训练速度更快,但是由于涉及额外的计算,因此每次迭代都会变慢。
  • 更高的准确率:批量规范化提供了更好的准确率。
  • 较高的学习率:通常,当我们训练神经网络时,我们使用较低的学习率,这需要很长时间才能收敛网络。 通过批量规范化,我们可以使用更高的学习率,从而使我们的网络更快地达到全局最低水平。
  • 减少了丢弃法的需求:当我们使用丢弃时,我们会破坏网络内部层中的一些基本信息。 批量规范化充当一个正则化器,这意味着我们可以训练网络而无需退出层。

在批量规范化中,我们将规范化应用于所有隐藏层,而不是仅将其应用于输入层。

实例规范化

如上一节所述,批量规范化仅通过利用来自该批量的信息来对一批样本进行归一化。 实例规范化是一种略有不同的方法。 在实例归一化中,我们仅通过利用来自该特征映射的信息来归一化每个特征映射。 实例规范化由 Dmitry Ulyanov 和 Andrea Vedaldi 在标题为《实例规范化:用于快速风格化的缺失成分》的论文中介绍,可通过以下链接获得。

总结

在本章中,我们了解了 GAN 是什么以及组成标准 GAN 架构的组件。 我们还探讨了可用的各种 GAN。 建立 GAN 的基本概念后,我们继续研究 GAN 的构建和功能中涉及的基本概念。 我们了解了 GAN 的优缺点,以及有助于克服这些缺点的解决方案。 最后,我们了解了 GAN 的各种实际应用。

利用本章中 GAN 的基本知识,我们现在将进入下一章,在此我们将学习使用 GAN 生成各种形状。

二、3D-GAN – 使用 GAN 生成形状

3D-GAN 是用于 3D 形状生成的 GAN 架构。 由于处理 3D 图像涉及复杂性,因此 3D 形状生成通常是一个复杂的问题。 3D-GAN 是一种可以生成逼真的,变化的 3D 形状的解决方案,由吴嘉俊,张成凯,薛天凡等人在题为《通过 3D 生成对抗建模学习对象形状的概率潜在空间》的论文中介绍,可以在这个页面上找到该论文。 在本章中,我们将使用 Keras 框架实现 3D-GAN。

我们将涵盖以下主题:

  • 3D-GAN 基础知识简介
  • 建立项目
  • 准备数据
  • 3D-GAN 的 Keras 实现
  • 训练 3D-GAN
  • 超参数优化
  • 3D-GAN 的实际应用

3D-GAN 简介

3D 生成对抗网络3D-GAN)是 GAN 的变体,就像 StackGAN,CycleGAN 和超分辨率生成对抗网络SRGAN)一样 。 与朴素 GAN 相似,它具有生成器和判别器模型。 这两个网络都使用 3D 卷积层,而不是使用 2D 卷积。 如果提供足够的数据,它可以学习生成具有良好视觉质量的 3D 形状。

在仔细查看 3D-GAN 网络之前,让我们了解 3D 卷积。

3D 卷积

简而言之,3D 卷积操作沿xyz这三个方向对输入数据应用 3D 过滤器。 此操作将创建 3D 特征映射的堆叠列表。 输出的形状类似于立方体或长方体的形状。 下图说明了 3D 卷积操作。 左立方体的突出显示部分是输入数据。 内核位于中间,形状为(3, 3, 3)。 右侧的块是卷积运算的输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XgsNw7Zh-1681652801308)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/9128ea2e-31f5-472a-8ebc-f3add1a716e0.png)]

现在,我们对 3D 卷积有了基本的了解,让我们继续看一下 3D-GAN 的架构。

3D-GAN 的架构

3D-GAN 中的两个网络都是深度卷积神经网络。 生成器网络通常是一个上采样网络。 它对噪声向量(来自概率潜在空间的向量)进行上采样,以生成形状为的 3D 图像,该形状的长度,宽度,高度和通道与输入图像相似。 判别器网络是下采样网络。 使用一系列 3D 卷积运算和密集层,它可以识别提供给它的输入数据是真实的还是伪造的。

在接下来的两节中,我们将介绍生成器和判别器网络的架构。

生成器网络的架构

生成器网络包含五个体积完全卷积的层,具有以下配置:

  • 卷积层:5
  • 过滤器: 512,256,128 和 64,1
  • 核大小4 x 4 x 44 x 4 x 44 x 4 x 44 x 4 x 44 x 4 x 4
  • 步幅:1、2、2、2、2 或(1, 1), (2, 2), (2, 2), (2, 2), (2, 2)
  • 批量规范化:是,是,是,是,否
  • 激活:ReLU, ReLU, ReLU, ReLU,Sigmoid
  • 池化层:否,否,否,否,否
  • 线性层:否,否,否,否,否

网络的输入和输出如下:

  • 输入:从概率潜在空间中采样的 200 维向量
  • 输出:形状为64x64x64的 3D 图像

下图显示了生成器的架构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G8EmurNr-1681652801308)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/adfe13fc-6e5c-4ffb-8230-ba4117391c77.png)]

下图显示了判别器网络中张量的流动以及每个层的张量的输入和输出形状。 这将使您对网络有更好的了解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y478OqUj-1681652801309)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/fd209aa6-8f96-45df-a754-7ae8cb364fa1.png)]

完全卷积网络是在网络末端没有完全连接的密集层的网络。 相反,它仅由卷积层组成,并且可以进行端到端训练,就像具有完全连接的层的卷积网络一样。 生成器网络中没有池化层。

判别器网络的架构

判别器网络包含五个具有以下配置的体积卷积层:

  • 3D 卷积层:5
  • 通道:64、128、256、512、1
  • 核大小:4、4、4、4、4、4
  • 步幅:2、2、2、2、1
  • 激活:LReLU,LReLU,LReLU,LReLU,Sigmoid
  • 批量规范化:是,是,是,是,无
  • 池化层:否,否,否,否,否
  • 线性层:否,否,否,否,否

网络的输入和输出如下:

  • 输入:形状为(64, 64, 64)的 3D 图像
  • 输出:输入数据属于真实或假类的概率

下图显示了判别器网络中每一层的张量流以及张量的输入和输出形状。 这将使您对判别器网络有更好的了解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hRpXdBFt-1681652801309)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/7fc08303-569e-499f-bc91-e2ae9684f947.png)]

判别器网络主要镜像生成器网络。 一个重要的区别是它使用中的 LeakyReLU 代替了 ReLU 作为激活函数。 而且,网络末端的 Sigmoid 层用于二分类,并预测所提供的图像是真实的还是伪造的。 最后一层没有规范化层,但是其他层使用批量规范化输入。

目标函数

目标函数是训练 3D-GAN 的主要方法。 它提供损失值,这些损失值用于计算梯度,然后更新权重值。 3D-GAN 的对抗损失函数如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FAVAKjJz-1681652801309)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/e8c1a187-a93a-40e6-92a9-3c823a391fd9.png)]

在这里, log(D(X))是二进制交叉熵损失或分类损失,log(1 - D(G(Z)))是对抗损失,z是来自概率空间p(Z)的潜向量, D(X)是判别器网络的输出, G(Z)是生成器网络的输出。

训练 3D-GAN

训练 3D-GAN 类似于训练朴素 GAN。 训练 3D-GAN 涉及的步骤如下:

  1. 从高斯(正态)分布中采样 200 维噪声向量。
  2. 使用生成器模型生成伪图像。
  3. 在真实图像(从真实数据中采样)和生成器网络生成的伪图像上训练生成器网络。
  4. 使用对抗模型训练生成器模型。 不要训练判别器模型。
  5. 对指定的周期数重复这些步骤。

我们将在后面的部分中详细探讨这些步骤。 让我们继续建立一个项目。

建立一个项目

该项目的源代码可在 GitHub 上的以下链接中找到。

运行以下命令来设置项目:

  1. 首先导航到父目录,如下所示:
cd Generative-Adversarial-Networks-Projects
  1. 接下来,将目录从当前目录更改为Chapter02目录:
cd Chapter02
  1. 接下来,为该项目创建一个 Python 虚拟环境:
virtualenv venv
  1. 之后,激活虚拟环境:
source venv/bin/activate
  1. 最后,安装requirements.txt文件中指示的所有要求:
pip install -r requirements.txt

我们现在已经成功建立了该项目。 有关更多信息,请参见代码存储库中包含的 README.md 文件。

准备数据

在本章中,我们将使用 3D ShapeNets 数据集,该数据集可从这个页面获得。 它由 Wu 和 Song 等人发行。 并包含 40 个对象类别的正确标注的 3D 形状。 我们将使用目录中可用的体积数据,我们将在本章稍后详细讨论。 在接下来的几节中,我们将下载,提取和浏览数据集。

3D ShapeNets 数据集仅用于学术用途。 如果您打算将数据集用于商业目的,请征求论文作者的许可,可以通过以下电子邮件地址与他们联系:shurans@cs.princeton.edu.

下载并提取数据集

运行以下命令以下载并提取数据集:

  1. 首先使用以下链接下载3DShapeNets
wget http://3dshapenets.cs.princeton.edu/3DShapeNetsCode.zip
  1. 下载文件后,运行以下命令将文件提取到适当的目录中:
unzip 3DShapeNetsCode.zip

现在,我们已经成功下载并提取了数据集。 它包含.mat(MATLAB)格式的图像。 每隔一张图像是 3D 图像。 在接下来的几节中,我们将学习体素,即 3D 空间中的点。

探索数据集

要了解数据集,我们需要可视化 3D 图像。 在接下来的几节中,我们将首先更详细地了解什么是体素。 然后,我们将加载并可视化 3D 图像。

什么是体素?

体像素或体素是三维空间中的一个点。 体素在xyz方向上定义了具有三个坐标的位置。 体素是表示 3D 图像的基本单位。 它们主要用于 CAT 扫描,X 射线和 MRI 中,以创建人体和其他 3D 对象的准确 3D 模型。 要处理 3D 图像,了解体素非常重要,因为这些是 3D 图像的组成。 包含下图,以使您了解 3D 图像中的体素是什么样的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P19Q015g-1681652801310)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/4a160433-e15b-440d-8a3f-91163db58421.png)]

在 3D 图像中的一系列体素。 阴影区域是单个体素。

前面的图像是体素的堆叠表示。 灰色长方体代表一个体素。 现在,您了解了什么是体素,让我们在下一部分中加载和可视化 3D 图像。

加载和可视化 3D 图像

3D ShapeNets 数据集包含.mat文件格式。 我们将这些.mat文件转换为 NumPy N 维数组。 我们还将可视化 3D 图像,以直观了解数据集。

执行以下代码以从.mat文件加载 3D 图像:

  1. 使用scipy中的loadmat()函数检索voxels。 代码如下:
import scipy.io as io
voxels = io.loadmat("path to .mat file")['instance']
  1. 加载的 3D 图像的形状为30x30x30。 我们的网络需要形状为64x64x64的图像。 我们将使用 NumPy 的 pad()方法将 3D 图像的大小增加到32x32x32
import numpy as np
voxels = np.pad(voxels, (1, 1), 'constant', constant_values=(0, 0))

pad()方法采用四个参数,它们是实际体素的 N 维数组,需要填充到每个轴边缘的值的数量,模式值(constant)和constant_values 被填充。

  1. 然后,使用scipy.ndimage模块中的zoom()函数将 3D 图像转换为大小为64x64x64的 3D 图像。
import scipy.ndimage as nd
voxels = nd.zoom(voxels, (2, 2, 2), mode='constant', order=0)

我们的网络要求图像的形状为64x64x64,这就是为什么我们将 3D 图像转换为这种形状的原因。

可视化 3D 图像

让我们使用 matplotlib 可视化 3D 图像,如以下代码所示:

  1. 首先创建一个 matplotlib 图并向其中添加一个子图:
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal')
  1. 接下来,将voxels添加到绘图中:
ax.voxels(voxels, edgecolor="red")
  1. 接下来,显示该图并将其另存为图像,以便稍后我们可以对其进行可视化和理解:
plt.show() plt.savefig(file_path)

第一个屏幕截图表示 3D 飞机上的飞机:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngCK0uCG-1681652801316)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/981169b9-a3b7-435a-8188-2b6f42b10d30.png)]

第二张屏幕截图表示 3D 平面中的表格:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ocHPqC5-1681652801317)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/25c92f62-a0a7-4c64-b768-577ac7dfe05b.png)]

第三个屏幕截图表示 3D 平面中的椅子:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1naXJiI9-1681652801317)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/51177648-2247-4346-b270-5139a5dcab46.png)]

我们已经成功下载,提取和浏览了数据集。 我们还研究了如何使用体素。 在下一节中,我们将在 Keras 框架中实现 3D-GAN。

3D-GAN 的 Keras 实现

在本节中,我们将在 Keras 框架中实现生成器网络和判别器网络。 我们需要创建两个 Keras 模型。 这两个网络都有各自独立的权重值。 让我们从生成器网络开始。

生成器网络

为了实现生成器网络,我们需要创建 Keras 模型并添加神经网络层。 实现生成器网络所需的步骤如下:

  1. 首先为不同的超参数指定值:
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
  1. 接下来,创建一个输入层,以允许网络进行输入。 生成器网络的输入是从概率潜在空间中采样的向量:
input_layer = Input(shape=gen_input_shape)
  1. 然后,添加第一个 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)
  1. 接下来,再添加四个 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)
  1. 然后,创建 Keras 模型并指定生成器网络的输入和输出:
model = Model(inputs=input_layer, outputs=a)
  1. 将生成器网络的整个代码包装在一个名为build_generator()的函数内:
def build_generator():"""Create a Generator Model with hyperparameters values defined as follows  :return: Generator network"""  z_size = 200gen_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) blocka = 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) blocksfor 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 模型并向其中添加神经网络层。 实现判别器网络所需的步骤如下:

  1. 首先为不同的超参数指定值:
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
  1. 接下来,创建一个输入层,以允许网络进行输入。 判别器网络的输入是形状为 64x64x64x1的 3D 图像:
dis_input_layer = Input(shape=dis_input_shape)
  1. 然后,添加第一个 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)
  1. 之后,再添加四个 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)
  1. 接下来,创建一个 Keras 模型,并为判别器网络指定输入和输出:
dis_model = Model(inputs=dis_input_layer, outputs=a)
  1. 将判别器网络的完整代码包装在一个函数中,如下所示:
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 blocka = 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 Blocksfor 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,请执行以下步骤:

  1. 首先指定训练所需的不同超参数的值,如下所示:
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'
  1. 接下来,创建并编译两个网络,如下所示:
# 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优化器的超参数值。

  1. 然后,创建并编译对抗模型:
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))
  1. 之后,提取并加载所有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 voxelsdef 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 volumeBatchvolumes = get3ImagesForACategory(obj='airplane', train=True, obj_ratio=1.0)
volumes = volumes[..., np.newaxis].astype(np.float)
  1. 接下来,添加TensorBoard回调并添加generatordiscriminator网络:
tensorboard = TensorBoard(log_dir="{}/{}".format(log_dir, time.time()))
tensorboard.set_model(generator)
tensorboard.set_model(discriminator)
  1. 添加一个循环,该循环将运行指定的周期数:
for epoch in range(epochs):print("Epoch:", epoch)# Create two lists to store lossesgen_losses = []dis_losses = []
  1. 在第一个循环中添加另一个循环以运行指定的批量数量:
    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, 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, :, :, :]
  1. 使用生成器网络生成伪造图像。 向其传递z_sample的一批噪声向量,并生成一批假图像:
gen_volumes = generator.predict(z_sample,verbose=3)
  1. 接下来,在由生成器生成的伪图像和一组真实图像中的一批真实图像上训练判别器网络。 另外,使判别器易于训练:
# 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)

前面的代码训练了判别器网络并计算了总的判别器损失。

  1. 训练同时包含generatordiscriminator网络的对抗模型:
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)
  1. 每隔一个周期生成并保存 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))
  1. 在每个周期之后,将平均损失保存到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"))

测试模型

要测试网络,请创建generatordiscriminator网络。 然后,加载学习的权重。 最后,使用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 进行人脸老化

条件 GANcGAN)是 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)在给定zy情况下建模我们数据的分布。 在此,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 的训练包括三个阶段:

  1. 条件 GAN 训练:在此阶段,我们训练生成器网络和判别器网络。
  2. 初始潜在向量近似:在此阶段,我们训练编码器网络。
  3. 潜在向量优化:在此阶段,我们同时优化编码器和生成器网络。

以下屏幕截图显示了 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]代表GD的参数 , 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的目录,其中包含本章的全部代码。 执行以下命令来设置项目:

  1. 首先,导航到父目录,如下所示:
cd Generative-Adversarial-Networks-Projects
  1. 现在,将目录从当前目录更改为 Chapter03
cd Chapter03
  1. 接下来,为该项目创建一个 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

我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。

  1. 接下来,激活新创建的虚拟环境:
source venv/bin/activate

激活虚拟环境后,所有其他命令将在此虚拟环境中执行。

  1. 接下来,通过执行以下命令,安装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 filemeta = loadmat(os.path.join(wiki_dir, "{}.mat".format(dataset)))# Load the list of all filesfull_path = meta[dataset][0, 0]["full_path"][0]# List of Matlab serial date numbersdob = meta[dataset][0, 0]["dob"][0]# List of years when photo was takenphoto_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 ageimages = []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 agereturn 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 yearif birth.month < 7:return taken - birth.yearelse: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 datetimeimport 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 框架中实现编码器网络开始。

执行以下步骤来实现编码器网络:

  1. 首先创建一个输入层:
input_layer = Input(shape=(64, 64, 3))
  1. 接下来,添加第一个卷积块,其中包含具有激活函数的 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)
  1. 接下来,再添加三个卷积块,其中每个都包含一个 2D 卷积层,然后是一个批量归一化层和一个激活函数,具有以下配置:
    • 过滤器64128256
    • 核大小555
    • 步幅222
    • 填充samesamesame
    • 批量规范化:每个卷积层后面都有一个批量规范化层
    • 激活LealyReLULeakyReLULeakyReLU,其中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)
  1. 接下来,将最后一个卷积块的输出展开,如下所示:
# Flatten layer enc = Flatten()(enc)

n维张量转换为一维张量(数组)称为“扁平化”。

  1. 接下来,添加具有以下配置的密集(完全连接)层,批量规范化层和激活函数:
    • 单元(节点):2,096
    • 批量规范化:是
    • 激活LeakyReLU,其中alpha等于0.2
# 1st Fully Connected Layer enc = Dense(4096)(enc)
enc = BatchNormalization()(enc)
enc = LeakyReLU(alpha=0.2)(enc)
  1. 接下来,使用以下配置添加第二个密集(完全连接)层:
    • 单元(节点)100
    • 激活:无:
# Second Fully Connected Layer enc = Dense(100)(enc)
  1. 最后,创建 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 Blockenc = Conv2D(filters=32, kernel_size=5, strides=2, padding='same')(input_layer)   enc = LeakyReLU(alpha=0.2)(enc)# 2nd Convolutional Blockenc = Conv2D(filters=64, kernel_size=5, strides=2, padding='same')(enc)enc = BatchNormalization()(enc)enc = LeakyReLU(alpha=0.2)(enc)# 3rd Convolutional Blockenc = Conv2D(filters=128, kernel_size=5, strides=2, padding='same')(enc)enc = BatchNormalization()(enc)enc = LeakyReLU(alpha=0.2)(enc)# 4th Convolutional Blockenc = Conv2D(filters=256, kernel_size=5, strides=2, padding='same')(enc)enc = BatchNormalization()(enc)enc = LeakyReLU(alpha=0.2)(enc)# Flatten layerenc = Flatten()(enc)# 1st Fully Connected Layerenc = Dense(4096)(enc)enc = BatchNormalization()(enc)enc = LeakyReLU(alpha=0.2)(enc)# Second Fully Connected Layerenc = Dense(100)(enc)# Create a modelmodel = Model(inputs=[input_layer], outputs=[enc])return model

我们现在已经成功地为编码器网络创建了 Keras 模型。 接下来,为生成器网络创建 Keras 模型。

生成器网络

生成器网络是一个 CNN,它采用 100 维向量z,并生成大小为(64, 64, 3)的图像。 让我们在 Keras 框架中实现生成器网络。

执行以下步骤以实现生成器网络:

  1. 首先创建生成器网络的两个输入层:
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, ))
  1. 接下来,沿着通道维度连接输入,如下所示:
x = concatenate([input_z_noise, input_label])

上一步将生成级联张量。

  1. 接下来,添加具有以下配置的密集(完全连接)块:
    • 单元(节点)2,048
    • 输入大小:106
    • 激活LeakyReLUalpha等于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)
  1. 接下来,使用以下配置添加第二个密集(完全连接)块:
    • 单元(节点):16,384
    • 批量规范化:是
    • 激活alpha等于0.2LeakyReLU
    • 退出0.2
x = Dense(256 * 8 * 8)(x)
x = BatchNormalization()(x)
x = LeakyReLU(alpha=0.2)(x)
x = Dropout(0.2)(x)
  1. 接下来,将最后一个密集层的输出重塑为大小为(8, 8, 256)的三维张量:
x = Reshape((8, 8, 256))(x)

该层将生成张量为(batch_size8, 8, 256)的张量。

  1. 接下来,添加一个上采样模块,该模块包含一个上采样层,其后是一个具有以下配置的 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 timesxand repeating the columns a specified number of timesy, respectively.

  1. 接下来,添加另一个上采样块(类似于上一层),如以下代码所示。 除了卷积层中使用的过滤器数为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)
  1. 接下来,添加最后一个上采样块。 该配置与上一层相似,除了在卷积层中使用了三个过滤器并且不使用批量归一化的事实:
x = UpSampling2D(size=(2, 2))(x)
x = Conv2D(filters=3, kernel_size=5, padding='same')(x)
x = Activation('tanh')(x)
  1. 最后,创建 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 = 100num_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 框架中实现判别器网络。

执行以下步骤以实现判别器网络:

  1. 首先创建两个输入层,因为我们的判别器网络将处理两个输入:
# 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)
  1. 接下来,添加具有以下配置的二维卷积块(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)
  1. 接下来,展开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)的张量。

  1. 接下来,沿着通道维度将变换后的标签张量和最后一个卷积层的输出连接起来,如下所示:
x = concatenate([x, label_input1], axis=3)
  1. 添加具有以下配置的卷积块(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)
  1. 接下来,再添加两个卷积块,如下所示:
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)
  1. 接下来,添加一个展开层:
x = Flatten()(x)
  1. 接下来,添加一个输出概率的密集层(分类层):
x = Dense(1, activation='sigmoid')(x)
  1. 最后,创建 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 进行人脸老化的过程分为三个步骤:

  1. 训练 cGAN
  2. 初始潜在​​向量近似
  3. 潜在向量优化

我们将在以下各节中逐一介绍这些步骤。

训练 cGAN

这是训练过程的第一步。 在这一步中,我们训练生成器和判别器网络。 执行以下步骤:

  1. 首先指定训练所需的参数:
# 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)
  1. 接下来,为训练定义优化器。 我们将使用 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.5beta_1值,等于0.999beta_2值以及等于10e-8epsilon值。

  1. 接下来,加载并编译生成器和判别器网络。 在 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作为损失函数。

  1. 接下来,构建并编译对抗模型,如下所示:
# 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作为优化器。

  1. 接下来,存储损失的TensorBoard 如下:
tensorboard = TensorBoard(log_dir="logs/{}".format(time.time()))
tensorboard.set_model(generator)
tensorboard.set_model(discriminator)
  1. 接下来,使用在“准备数据”部分中定义的load_data函数加载所有图像:
images, age_list = load_data(wiki_dir=wiki_dir, dataset="wiki")
  1. 接下来,将年龄数值转换为年龄类别,如下所示:
# 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 = 0elif 18 < age <= 29:age_category = 1elif 29 < age <= 39:age_category = 2elif 39 < age <= 49:age_category = 3elif 49 < age <= 59:age_category = 4elif 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_values5)。

  1. 接下来,加载所有图像并创建一个包含所有图像的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 imageloaded_image = image.load_img(os.path.join(data_dir, image_path), target_size=image_shape)# Convert PIL image to numpy ndarrayloaded_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 tensorif images is None:images = loaded_imageelse: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...
  1. 接下来,创建一个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)
  1. 接下来,在周期循环内创建另一个循环,并使它运行 num_batches 指定的次数,如下所示:
 for index in range(number_of_batches):print("Batch:{}".format(index + 1))

我们用于判别器网络和对抗网络训练的整个代码将在此循环内。

  1. 接下来,对真实数据集中的一批图像和一批一次性编码的年龄向量进行采样:
        images_batch = loaded_images[index * batch_size:(index + 1) * batch_size]images_batch = images_batch / 127.5 - 1.0images_batch = images_batch.astype(np.float32)y_batch = y[index * batch_size:(index + 1) * batch_size]

image_batch的形状应为[batch_size64, 64, 3),y_batch的形状应为(batch_size6)。

  1. 接下来,从高斯分布中采样一批噪声向量,如下所示:
        z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))
  1. 接下来,使用生成器网络生成伪造图像。 请记住,我们尚未训练生成器网络:
        initial_recon_images = generator.predict_on_batch([z_noise, y_batch])

生成器网络有两个输入z_noisey_batch,它们是我们在步骤 11 和 12 中创建的。

  1. 现在,在真实图像和伪图像上训练判别器网络:
        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)

此代码应在一批图像上训练判别器网络。 在每个步骤中,将对一批样本进行判别。

  1. 接下来,训练对抗网络。 通过冻结判别器网络,我们将仅训练生成器网络:
        # 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 encodersrandom_labels = to_categorical(random_labels, 6)# Train the generator networkg_loss = adversarial_model.train_on_batch([z_noise2, sampled_labels], [1] * batch_size)

前面的代码将在一批输入上训练生成器网络。 对抗模型的输入是z_noise2random_labels

  1. 接下来,计算并打印损失:
        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 listsgen_losses.append(g_loss)dis_losses.append(d_loss)
  1. 接下来,将损失写入 TensorBoard 以进行可视化:
    write_log(tensorboard, 'g_loss', np.mean(gen_losses), epoch)write_log(tensorboard, 'd_loss', np.mean(dis_losses), epoch)
  1. 每隔 10 个周期取样并保存图像,如下所示:
 if epoch % 10 == 0:images_batch = loaded_images[0:batch_size]images_batch = images_batch / 127.5 - 1.0images_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()
  1. 最后,通过添加以下行来保存两个模型:
# 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 不会学习从图像到潜在向量的反向映射。 取而代之的是,编码器学习了这种反向映射,并能够生成潜在向量,我们可以将其用于在目标年龄生成人脸图像。 让我们训练编码器网络。

我们已经定义了训练所需的超参数。 执行以下步骤来训练编码器网络:

  1. 首先建立编码器网络。 添加以下代码以构建和编译网络:
# 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))
  1. 接下来,加载生成器网络,如下所示:
generator.load_weights("generator.h5")

在这里,我们正在加载上一步的权重,在该步骤中,我们成功地训练并保存了生成器网络的权重。

  1. 接下来,对一批潜在向量进行采样,如下所示:
z_i = np.random.normal(0, 1, size=(1000, z_shape))
  1. 接下来,对一批随机年龄数字进行采样,并将随机年龄数字转换为单热编码向量,如下所示:
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 个值。

  1. 接下来,添加一个周期循环和一个批量步骤循环,如下所示:
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. 现在,从 1,000 个样本中采样一批潜伏向量和一批单热编码向量,如下所示:
        z_batch = z_i[index * batch_size:(index + 1) * batch_size]y_batch = y[index * batch_size:(index + 1) * batch_size]
  1. 接下来,使用预训练的生成器网络生成伪造图像:
        generated_images = generator.predict_on_batch([z_batch, y_batch])
  1. 最后,通过生成器网络在生成的图像上训练编码器网络:
        encoder_loss = encoder.train_on_batch(generated_images, z_batch)
  1. 接下来,在每个周期之后,将编码器损失写入 TensorBoard,如下所示:
    write_log(tensorboard, "encoder_loss", np.mean(encoder_losses), epoch)
  1. 我们需要保存训练有素的编码器网络。 通过添加以下代码来保存编码器模型:
encoder.save_weights("encoder.h5")

如果您已成功执行了本节中给出的代码,则将成功地训练编码器模型。 现在,我们的编码器网络已准备好生成初始潜向量。 在下一节中,我们将学习如何执行优化的潜在向量近似。

潜在向量优化

在前面的两个步骤中,我们成功地训练了生成器网络,判别器网络和编码器网络。 在本节中,我们将改进编码器和生成器网络。 在这些步骤中,我们将使用人脸识别FR)网络,该网络会生成输入给它的特定输入的 128 维嵌入,以改善生成器和编码器网络。

执行以下步骤:

  1. 首先构建并加载编码器网络和生成器网络的权重:
encoder = build_encoder()
encoder.load_weights("encoder.h5")# Load the generator network generator.load_weights("generator.h5")
  1. 接下来,创建一个网络以将图像从(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 像素。 前面的网络将帮助我们将图像转换为所需的格式。

  1. 建立人脸识别模型:
# Face recognition model fr_model = build_fr_model(input_shape=fr_image_shape)
fr_model.compile(loss=loss, optimizer="adam")

请参阅这里以获取build_fr_model()函数。

  1. 接下来,创建另一个对抗模型。 在此对抗模型中,我们将具有三个网络:编码器,生成器和人脸识别模型:
# 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)
  1. 在第一个循环中添加一个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)
  1. 接下来,从真实图像列表中采样一批图像:
        # Sample and normalizeimages_batch = loaded_images[index * batch_size:(index + 1) * batch_size]images_batch = images_batch / 255.0images_batch = images_batch.astype(np.float32)# Sample a batch of age one-hot encoder vectorsy_batch = y[index * batch_size:(index + 1) * batch_size]
  1. 接下来,使用 FR 网络为真实图像生成嵌入:
        images_batch_resized = image_resizer.predict_on_batch(images_batch)         real_embeddings = fr_model.predict_on_batch(images_batch_resized)
  1. 最后,训练对抗模型,这将训练编码器模型和生成器模型:
        reconstruction_loss = fr_adversarial_model.train_on_batch([images_batch, y_batch], real_embeddings)
  1. 另外,将重建损失写入 TensorBoard 以进行进一步可视化:
 # Write the reconstruction loss to Tensorboard        write_log(tensorboard, "reconstruction_loss", reconstruction_loss, index)
  1. 保存两个网络的权重:
# 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=2048input_shape=(batch_size, 100)output_shape=(batch_size, 2048)activation='relu'
3.密集层neurons=16384input_shape=(batch_size, 100)output_shape=(batch_size, 2048)batch_normalization=Yesactivation='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=128kernel_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=64kernel_size=(5, 5)strides=(1, 1)padding='same'activation=ReLUinput_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=3kernel_size=(5, 5)strides=(1, 1)padding='same'activation=ReLUinput_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 有效。

配置判别器网络

在继续之前,让我们看一下判别器网络的架构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uV0IsSKA-1681652801321)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/0bbb9055-1c43-4f7d-a900-fdf515b6d068.png)]

上图给出了生成器网络架构的顶层概述。

如前所述,判别器网络是一个包含 10 层的 CNN(您可以根据需要向网络添加更多层)。 基本上,它会拍摄大小为64 x 64 x 3的图像,使用 2D 卷积层对其进行下采样,然后将其传递到完全连接的层进行分类。 它的输出是对给定图像是伪图像还是真实图像的预测。 可以为 0 或 1;可以为 0。 如果输出为 1,则传递到判别器的图像是真实的;如果输出为 0,则传递的图像是伪图像。

让我们看一下判别器网络中的各层:

编号层名称配置
1.输入层input_shape=(batch_size, 64, 64, 3)output_shape=(batch_size, 64, 64, 3)
2.2D 卷积层filters=128kernel_size=(5, 5)strides=(1, 1)padding='valid'input_shape=(batch_size, 64, 64, 3)output_shape=(batch_size, 64, 64, 128)activation='leakyrelu'leaky_relu_alpha=0.2
3.2D 最大池化pool_size=(2, 2)input_shape=(batch_size, 64, 64, 128)output_shape=(batch_size, 32, 32, 128)
4.2D 卷积层filters=256kernel_size=(3, 3)strides=(1, 1)padding='valid'input_shape=(batch_size, 32, 32, 128)output_shape=(batch_size, 30, 30, 256)activation='leakyrelu'leaky_relu_alpha=0.2
5.2D 最大池化pool_size=(2, 2)input_shape=(batch_size, 30, 30, 256)output_shape=(batch_size, 15, 15, 256)
6.2D 卷积层filters=512kernel_size=(3, 3)strides=(1, 1)padding='valid'input_shape=(batch_size, 15, 15, 256)output_shape=(batch_size, 13, 13, 512)activation='leakyrelu'leaky_relu_alpha=0.2
7.2D 最大池化pool_size=(2, 2)input_shape=(batch_size, 13, 13, 512)output_shape=(batch_size, 6, 6, 512)
8.展开层input_shape=(batch_size, 6, 6, 512)output_shape=(batch_size, 18432)
9.密集层neurons=1024input_shape=(batch_size, 18432)output_shape=(batch_size, 1024)activation='leakyrelu''leakyrelu_alpha'=0.2
10.密集层neurons=1input_shape=(batch_size, 1024)output_shape=(batch_size, 1)activation='sigmoid'

L 等人研究了张量如何从第一层流到最后一层。 下图显示了不同层的输入和输出形状:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uAIlExaG-1681652801326)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/9a5468ed-810e-429c-8a0a-af8eb1d673b0.png)]

该配置对具有 TensorFlow 后端和channels_last格式的 Keras API 有效。

设置项目

我们已经克隆/下载了所有章节的完整代码。 下载的代码包含一个名为Chapter04的目录,该目录包含本章的全部代码。 执行以下命令来设置项目:

  1. 首先,导航到父目录,如下所示:
cd Generative-Adversarial-Networks-Projects
  1. 现在,将目录从当前目录更改为Chapter04
cd Chapter04
  1. 接下来,为该项目创建一个 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

我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。

  1. 接下来,激活虚拟环境:
source venv/bin/activate

激活虚拟环境后,所有其他命令将在此虚拟环境中执行。

  1. 接下来,通过执行以下命令来安装requirements.txt文件中给出的所有要求:
pip install -r requirements.txt

您可以参考 README.md 文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。

在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一部分中,我们将使用数据集,包括下载和清理数据集。

下载并准备动漫角色数据集

要训练 DCGAN 网络,我们需要一个动漫人物数据集,其中包含人物的裁剪面孔。 收集数据集有多种方法。 我们可以使用公开可用的数据集,也可以抓取一个,只要不违反网站的抓取策略即可。 在本章中,我们将仅出于教育和演示目的刮取图像。 我们使用名为 gallery-dl 的搜寻器工具从 pixiv.net 抓取了图像。 这是一个命令行工具,可用于从网站下载图像集,例如 pixiv.net,exhentai.org,danbooru.donmai.us 和更多。 它可通过以下链接获得。

下载数据集

在本节中,我们将介绍安装依赖项和下载数据集所需的不同步骤。 在执行以下命令之前,激活为此项目创建的虚拟环境:

  1. 执行以下命令安装gallery-dl
pip install --upgrade gallery-dl
  1. 或者,您可以使用以下命令安装gallery-dl的最新开发版本:
pip install --upgrade https://github.com/mikf/gallery-dl/archive/master.zip
  1. 如果上述命令不起作用,请按照官方存储库中的说明进行操作:
# Official gallery-dl Github repo
https://github.com/mikf/gallery-dl
  1. 最后,执行以下命令以使用gallery-dl.danbooru.donmai.us下载图像:
gallery-dl https://danbooru.donmai.us/posts?tags=face

下载图像需要您自担风险。 所提供的信息仅用于教育目的,我们不支持非法刮取。 我们没有图像的版权,因为图像由其各自所有者托管。 出于商业目的,请联系网站的各自所有者或您所使用的内容。

探索数据集

在裁剪或调整图像大小之前,请查看下载的图像:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SRwTHOso-1681652801326)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/503cd7f0-4105-49a5-9095-d400768e7eb9.png)]

如您所见,有些图像还包含其他身体部位,我们在训练图像中不需要这些部位。 在下一部分中,我们将仅从这些图像中裁剪出人脸。 此外,我们会将所有图像调整为训练所需的大小。

裁剪数据集中的图像并调整其大小

在本节中,我们将从图像中裁剪出面孔。 我们将使用python-animeface从图像中裁剪出面孔。 这是一个开源 GitHub 存储库,可从命令行的图像中自动裁剪人脸。 它可以通过以下链接公开获得。

执行以下步骤来裁剪图像并调整其大小:

  1. 首先,下载python-animeface
pip install animeface
  1. 接下来,导入任务所需的模块:
import glob
import osimport animeface
from PIL import Image
  1. 接下来,定义参数:
total_num_faces = 0
  1. 接下来,遍历所有图像以进行裁剪并一一调整其大小:
for index, filename in  enumerate(glob.glob('/path/to/directory/containing/images/*.*')):
  1. 在循环内,打开当前图像并检测其中的人脸:
  try:# Open imageim = Image.open(filename)# Detect facesfaces = animeface.detect(im)except Exception as e:print("Exception:{}".format(e))continue
  1. 接下来,获取图像中检测到的人脸坐标:
    fp = faces[0].face.pos# Get coordinates of the face detected in the imagecoordinates = (fp.x, fp.y, fp.x+fp.width, fp.y+fp.height)
  1. 现在,将人脸裁剪出图像:
 # Crop image  cropped_image = im.crop(coordinates)
  1. 接下来,将裁剪后的人脸图像调整为(64, 64)的大小:
 # Resize image  cropped_image = cropped_image.resize((64, 64), Image.ANTIALIAS)
  1. 最后,将裁剪并调整大小的图像保存到所需目录:
 cropped_image.save("/path/to/directory/to/store/cropped/images/filename.png"))

包装在 Python 函数中的完整代码如下所示:

import glob
import osimport animeface
from PIL import Imagetotal_num_faces = 0   for index, filename in enumerate(glob.glob('/path/to/directory/containing/images/*.*')):# Open image and detect facestry:im = Image.open(filename)faces = animeface.detect(im)except Exception as e:print("Exception:{}".format(e))continue# If no faces found in the current image if len(faces) == 0:print("No faces found in the image")continue    fp = faces[0].face.pos# Get coordinates of the face detected in the imagecoordinates = (fp.x, fp.y, fp.x+fp.width, fp.y+fp.height)# Crop imagecropped_image = im.crop(coordinates)# Resize imagecropped_image = cropped_image.resize((64, 64), Image.ANTIALIAS)# Show cropped and resized image# cropped_image.show()   # Save it in the output directory  cropped_image.save("/path/to/directory/to/store/cropped/images/filename.png"))print("Cropped image saved successfully")total_num_faces += 1   print("Number of faces detected till now:{}".format(total_num_faces))print("Total number of faces:{}".format(total_num_faces))

前面的脚本将从包含下载图像的文件夹中加载所有图像,使用python-animeface库检测人脸,然后从初始图像中裁剪出人脸。 然后,裁切后的图像将被调整为64 x 64的大小。如果要更改图像的大小,请相应地更改生成器和判别器的架构。 我们现在准备在我们的网络上工作。

使用 Keras 实现 DCGAN

在本节中,我们将在 Keras 框架中编写 DCGAN 的实现。 Keras 是一个使用 TensorFlow 或 Teano 作为后端的元框架。 它提供了用于神经网络的高级 API。 与低级框架(如 TensorFlow)相比,它还具有预构建的神经网络层,优化器,正则化器,初始化器和数据预处理层,可轻松进行原型制作。 让我们开始编写生成器网络的实现。

生成器

如 DCGAN 部分的“架构”中所述,生成器网络由一些 2D 卷积层,上采样层,整形层和批归一化层组成 。 在 Keras 中,每个操作都可以指定为一个层。 甚至激活函数也是 Keras 中的层,可以像正常的密集层一样添加到模型中。

执行以下步骤来创建生成器网络:

  1. 让我们开始创建一个Sequential Keras 模型:
gen_model = Sequential()
  1. 接下来,添加一个具有 2,048 个节点的密集层,然后添加一个激活层tanh
gen_model.add(Dense(units=2048))
gen_model.add(Activation('tanh'))
  1. 接下来,添加第二层,它也是一个具有 16,384 个神经元的密集层。 接下来是batch normalization层,其中default hyperparameterstanh作为激活函数
gen_model.add(Dense(256`8`8))
gen_model.add(BatchNormalization())
gen_model.add(Activation('tanh')) 

第二密集层的输出是大小为16384的张量。 此处,(256, 8, 8)是密集层中神经元的数量。

  1. 接下来,向网络中添加一个重塑层,以将张量从最后一层重塑为(batch_size, 8, 8, 256)**:**形状的张量
# Reshape layer
gen_model.add(Reshape((8, 8, 256), input_shape=(256`8`8,)))
  1. 接下来,添加 2D 上采样层以将形状从(8, 8, 256)更改为(16, 16, 256)。 上采样大小为(2, 2),这将张量的大小增加到其原始大小的两倍。 在这里,我们有 256 个张量为16 x 16:的张量。
gen_model.add(UpSampling2D(size=(2, 2)))
  1. 接下来,添加一个 2D 卷积层。 这使用指定数量的过滤器在张量上应用 2D 卷积。 在这里,我们使用 64 个过滤器和一个(5, 5)形状的内核:
gen_model.add(Conv2D(128, (5, 5), padding='same'))
gen_model.add(Activation('tanh')) 
  1. 接下来,添加 2D 向上采样层以将张量的形状从 (batch_size, 16, 16, 64)更改为(batch_size, 32, 32, 64)
gen_model.add(UpSampling2D(size=(2, 2))) 

2D 上采样层将张量的行和列分别以[0][1]的大小重复 。

  1. 接下来,在第二个 2D 卷积层上添加 64 过滤器和,将(5, 5)核大小tanh作为激活函数:
gen_model.add(Conv2D(64, (5, 5), padding='same'))
gen_model.add(Activation('tanh'))
  1. 接下来,添加 2D 上采样层,将形状从 (batch_size, 32, 32, 64)更改为(batch_size, 64, 64, 64)
gen_model.add(UpSampling2D(size=(2, 2))) 
  1. 最后,在第三个 2D 卷积层上添加三个过滤器核大小(5, 5) ,然后是tanh作为激活函数:
gen_model.add(Conv2D(3, (5, 5), padding='same'))
gen_model.add(Activation('tanh'))

生成器网络将输出(batch_size, 64, 64, 3)形状的张量。 这批张量中的一个图像张量类似于具有三个通道的大小为64 x 64的图像: 红色,绿色蓝色RGB)。

用 Python 方法包装的生成器网络的完整代码如下所示:

def get_generator():gen_model = Sequential()gen_model.add(Dense(input_dim=100, output_dim=2048))gen_model.add(LeakyReLU(alpha=0.2))gen_model.add(Dense(256 * 8 * 8))gen_model.add(BatchNormalization())gen_model.add(LeakyReLU(alpha=0.2))gen_model.add(Reshape((8, 8, 256), input_shape=(256 * 8 * 8,)))gen_model.add(UpSampling2D(size=(2, 2)))gen_model.add(Conv2D(128, (5, 5), padding='same'))gen_model.add(LeakyReLU(alpha=0.2))gen_model.add(UpSampling2D(size=(2, 2)))gen_model.add(Conv2D(64, (5, 5), padding='same'))gen_model.add(LeakyReLU(alpha=0.2))gen_model.add(UpSampling2D(size=(2, 2)))gen_model.add(Conv2D(3, (5, 5), padding='same'))gen_model.add(LeakyReLU(alpha=0.2))return gen_model

现在我们已经创建了生成器网络,让我们开始创建判别器网络。

判别器

如 DCGAN 的架构中所述,判别器网络具有三个 2D 卷积层,每个层均具有激活函数,后跟两个最大合并层。 网络的尾部包含两个完全连接的(密集)层,用作分类层。 首先,让我们看一下判别器网络中的不同层:

  • 所有卷积层都具有LeakyReLU作为激活函数,其alpha值为 0.2
  • 卷积层分别具有 128、256 和 512 个过滤器。 它们的核大小分别为(5, 5)(3, 3)(3, 3)
  • 在卷积层之后,我们有一个展开层,它将输入平坦化为一维张量。
  • 此后,网络具有两个密集层,分别具有 1,024 个神经元和一个神经元。
  • 第一密集层具有LeakyReLU作为激活函数,而第二层具有 Sigmoid 作为激活函数。 Sigmoid 激活用于二分类。 我们正在训练辨别器网络,以区分真实图像还是伪图像。

执行以下步骤来创建判别器网络:

  1. 让我们开始创建一个Sequential Keras 模型:
dis_model = Sequential()
  1. 添加一个 2D 卷积层,该层采用形状为(64, 64, 3)的输入图像。 该层的超参数如下。 另外,添加具有0.2alpha值的LeakyReLU作为激活函数:
    • 过滤器:128
    • 核大小(5, 5)
    • 填充:相同
dis_model.add(Conv2D(filters=128, kernel_size=5, padding='same', input_shape=(64, 64, 3)))
dis_model.add(LeakyReLU(alpha=0.2))
  1. 接下来,添加池大小为(2, 2)的 2D 最大池化层。 最大池用于对图像表示进行下采样,并通过在表示的非重叠子区域上使用最大过滤来应用它:
dis_model.add(MaxPooling2D(pool_size=(2, 2)))

来自第一层的输出张量的形状将为(batch_size, 32, 32, 128)

  1. 接下来,添加具有以下配置的另一个 2D 卷积层:
    • 过滤器:256
    • 核大小(3, 3)
    • 激活函数LeakyReLU,具有alpha 0.2
    • 2D 最大池中的池大小(2, 2)
dis_model.add(Conv2D(filters=256, kernel_size=3))
dis_model.add(LeakyReLU(alpha=0.2))
dis_model.add(MaxPooling2D(pool_size=(2, 2)))

该层的输出张量的形状将为 (batch_size, 30, 30, 256)

  1. 接下来,添加具有以下配置的第三个 2D 卷积层:
    • 过滤器: 512
    • 核大小(3, 3)
    • 激活函数LeakyReLU,带有alpha 0.2
    • 2D 最大池中的池大小(2, 2)
dis_model.add(Conv2D(512, (3, 3)))
dis_model.add(LeakyReLU(alpha=0.2))
dis_model.add(MaxPooling2D(pool_size=(2, 2)))

该层的输出张量的形状将为 (batch_size, 13, 13, 512)

  1. 接下来,添加一个展开层。 这将使输入变平而不影响批量大小。 它产生一个二维张量:
dis_model.add(Flatten())

来自平坦化层的张量的输出形状将为(batch_size, 18432,)

  1. 接下来,添加具有1024神经元和alpha 0.2 作为激活函数的LeakyReLU的密集层:
dis_model.add(Dense(1024))
dis_model.add(LeakyReLU(alpha=0.2))
  1. 最后,添加带有一个神经元的密集层以进行二分类。 Sigmoid 函数最适合二分类,因为它给出了分类的可能性:
dis_model.add(Dense(1))
dis_model.add(Activation('tanh'))

网络将生成形状为(batch_size, 1)的输出张量。 输出张量包含类的概率。

包裹在 Python 方法中的判别器网络的完整代码如下:

def get_discriminator():dis_model = Sequential()dis_model.add(Conv2D(128, (5, 5),padding='same',input_shape=(64, 64, 3)))dis_model.add(LeakyReLU(alpha=0.2))dis_model.add(MaxPooling2D(pool_size=(2, 2)))dis_model.add(Conv2D(256, (3, 3)))dis_model.add(LeakyReLU(alpha=0.2))dis_model.add(MaxPooling2D(pool_size=(2, 2)))dis_model.add(Conv2D(512, (3, 3)))dis_model.add(LeakyReLU(alpha=0.2))dis_model.add(MaxPooling2D(pool_size=(2, 2)))dis_model.add(Flatten())dis_model.add(Dense(1024))dis_model.add(LeakyReLU(alpha=0.2))dis_model.add(Dense(1))dis_model.add(Activation('sigmoid'))return dis_model

在本节中,我们已成功实现了判别器和生成器网络。 在下一部分中,我们将在“下载和准备动漫角色数据集”部分中准备的数据集上训练模型。

训练 DCGAN

同样,训练 DCGAN 类似于训练朴素 GAN 网络。 这是一个四步过程:

  1. 加载数据集。
  2. 构建和编译网络。
  3. 训练判别器网络。
  4. 训练生成器网络。

我们将在本节中一步一步地进行这些步骤。

让我们从定义变量和超参数开始:

dataset_dir = "/Path/to/dataset/directory/*.*" batch_size = 128  z_shape = 100 epochs = 10000 dis_learning_rate = 0.0005 gen_learning_rate = 0.0005 dis_momentum = 0.9 gen_momentum = 0.9 dis_nesterov = True gen_nesterov = True 

在这里,我们为训练指定了不同的超参数。 现在,我们将看到如何为训练加载数据集。

加载样本

要训​​练 DCGAN 网络,我们需要将数据集加载到内存中,并且需要定义一种机制来加载成批的内存。 执行以下步骤以加载数据集:

  1. 首先加载所有裁剪,调整大小并保存在cropped文件夹中的图像。 正确指定目录的路径,以便glob.glob方法可以创建其中所有文件的列表。 要读取图像,请使用scipy.misc模块中的imread方法。 以下代码显示了加载目录中所有图像的不同步骤:
# Loading images all_images = []
for index, filename in enumerate(glob.glob('/Path/to/cropped/images/directory/*.*')):image = imread(filename, flatten=False, mode='RGB')all_images.append(image)
  1. 接下来,为所有图像创建一个ndarray。 最终ndarray的形状将为(total_num_images, 64, 64, 3)。 此外,标准化所有图像:
# Convert to Numpy ndarray
X = np.array(all_images)
X = (X - 127.5) / 127.5 

现在我们已经加载了数据集,接下来我们将看到如何构建和编译网络。

建立和编译网络

在本节中,我们将构建和编译训练所需的网络:

  1. 首先定义训练所需的优化器,如下所示:
# Define optimizers dis_optimizer = SGD(lr=dis_learning_rate, momentum=dis_momentum, nesterov=dis_nesterov)
gen_optimizer = SGD(lr=gen_learning_rate, momentum=gen_momentum, nesterov=gen_nesterov)
  1. 接下来,创建生成器模型的实例,并编译生成器模型(编译将初始化权重参数,优化器算法,损失函数以及使用网络所需的其他必要步骤):
gen_model = build_generator()
gen_model.compile(loss='binary_crossentropy', optimizer=gen_optimizer)

使用binary_crossentropy作为生成器网络的loss函数,并使用gen_optimizer作为优化器。

  1. 接下来,创建判别模型的实例,并对其进行编译,如下所示:
dis_model = build_discriminator()
dis_model.compile(loss='binary_crossentropy', optimizer=dis_optimizer)

同样,使用binary_crossentropy作为判别器网络的损失函数,并使用dis_optimizer作为优化器。

  1. 接下来,创建一个对抗模型。 一个对抗者将两个网络都包含在一个模型中。 对抗模型的架构如下:
    • 输入 -> 生成器 -> 判别器 -> 输出

创建和编译对抗模型的代码如下:

adversarial_model = Sequential()
adversarial_model.add(gen_model)
dis_model.trainable = False adversarial_model.add(dis_model)

当我们训练该网络时,我们不想训练判别器网络,因此在将其添加到对抗模型之前,使其变为不可训练的。

编译对抗模型,如下所示:

adversarial_model.compile(loss='binary_crossentropy', optimizer=gen_optimizer)

使用binary_crossentropy作为损失函数,使用gen_optimizer作为对抗模型的优化器。

在开始训练之前,添加 TensorBoard 以可视化显示损失,如下所示:

tensorboard = TensorBoard(log_dir="logs/{}".format(time.time()), write_images=True, write_grads=True, write_graph=True)
tensorboard.set_model(gen_model)
tensorboard.set_model(dis_model)

我们将针对指定的迭代次数训练网络,因此创建一个应运行指定次数的循环。 在每个周期内,我们将在大小为 128 的微型批量上训练网络。 计算需要处理的批量数量:

for epoch in range(epcohs):print("Epoch is", epoch)number_of_batches = int(X.shape[0] / batch_size)print("Number of batches", number_of_batches)for index in range(number_of_batches):

现在,我们将仔细研究训练过程。 以下几点说明了 DCGAN 训练中涉及的不同步骤:

  • 最初,这两个网络都是幼稚的并且具有随机权重。
  • 训练 DCGAN 网络的标准过程是首先对一批样本进行判别器训练。
  • 为此,我们需要假样本和真实样本。 我们已经有了真实的样本,因此现在需要生成伪样本。
  • 要生成伪样本,请在均匀分布上创建形状为(100, )的潜向量。 将此潜向量馈入未经训练的生成器网络。 生成器网络将生成伪样本,我们将其用于训练判别器网络。
  • 合并真实图像和伪图像以创建一组新的样本图像。 我们还需要创建一个标签数组:真实图像使用标签 1,伪图像使用标签 0。

训练判别器网络

执行以下步骤来训练判别器网络:

  1. 首先从正态分布中采样一批噪声向量,如下所示:
z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))

要对值进行采样,请使用 Numpy 库中np.random模块中的normal()方法。

  1. 接下来,从所有图像集中采样一批真实图像:
image_batch = X[index * batch_size:(index + 1) * batch_size]
  1. 接下来,使用生成器网络生成一批伪图像:
generated_images = gen_model.predict_on_batch(z_noise)
  1. 接下来,创建真实标签和伪标签:
y_real = np.ones(batch_size) - np.random.random_sample(batch_size) * 0.2 y_fake = np.random.random_sample(batch_size) * 0.2
  1. 接下来,在真实图像和真实标签上训练判别器网络:
 dis_loss_real = dis_model.train_on_batch(image_batch, y_real)
  1. 同样,在伪造图像和伪造标签上对其进行训练:
dis_loss_fake = dis_model.train_on_batch(generated_images, y_fake)
  1. 接下来,计算平均损失并将其打印到控制台:
d_loss = (dis_loss_real+dis_loss_fake)/2 print("d_loss:", d_loss)

到目前为止,我们一直在训练判别器网络。 在下一部分中,让我们训练生成器网络。

训练生成器网络

为了训练生成器网络,我们必须训练对抗模型。 当我们训练对抗模型时,它只训练生成器网络,而冻结判别器网络。 由于我们已经训练过判别器网络,因此我们不会对其进行训练。 执行以下步骤来训练对抗模型:

  1. 首先重新创建一批噪声向量。 从高斯/正态分布中采样以下噪声向量:
z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))
  1. 接下来,对这批噪声向量训练对抗模型,如下所示:
g_loss = adversarial_model.train_on_batch(z_noise, [1] * batch_size)

我们在一批噪声向量和实数标签上训练对抗模型。 在这里,实数标签是一个所有值均等于 1 的向量。我们还在训练生成器,以欺骗判别器网络。 为此,我们为它提供一个向量,该向量的所有值均等于 1。在此步骤中,生成器将接收来自生成器网络的反馈,并相应地进行改进。

  1. 最后,将生成器损失打印到控制台以跟踪损失:
print("g_loss:", g_loss)

有一种被动的方法可以评估训练过程。 每 10 个周期后,生成伪造图像并手动检查图像质量:

 if epoch % 10 == 0:z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))gen_images1 = gen_model.predict_on_batch(z_noise)for img in gen_images1[:2]:save_rgb_img(img, "results/one_{}.png".format(epoch))

这些图像将帮助您决定是继续训练还是尽早停止训练。 如果生成的高分辨率图像的质量良好,请停止训练。 或者继续训练,直到您的模型变好为止。

我们已经成功地在动画角色数据集上训练了 DCGAN 网络。 现在我们可以使用该模型生成动漫人物图像。

生成图像

为了生成图像,我们需要一个从潜在空间采样的噪声向量。 Numpy 有一种称为uniform()的方法,可以根据均匀分布生成向量。 让我们看看如何在以下步骤中生成图像:

  1. 通过添加以下代码行,创建大小为(batch_size, 100)的噪声向量:
z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))
  1. 然后,使用生成器模型的predict_on_batch方法生成图像。 将上一步中创建的噪声向量馈入其中:
gen_images = gen_model.predict_on_batch(z_noise)
  1. 现在我们已经生成了图像,通过添加以下代码行将其保存。 创建一个名为results的目录来存储生成的图像:
imsave('results/image_{}.jpg'.format(epoch),gen_images[0])

现在,您可以打开这些生成的图像以测量生成的模型的质量。 这是一种评估模型表现的被动方法。

保存模型

在 Keras 中保存模型只需要一行代码。 要保存生成器模型,请添加以下行:

# Specify the path for the generator model
gen_model.save("directory/for/the/generator/model.h5") 

同样,通过添加以下行来保存判别器模型:

# Specify the path for the discriminator model
dis_model.save("directory/for/the/discriminator/model.h5") 

可视化生成的图像

在将网络训练了 100 个时间段后,生成器将开始生成合理的图像。 让我们看一下生成的图像。

在 100 个周期之后,图像显示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ybF9YUlh-1681652801327)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/bad2e9c8-167e-436b-9078-4fa53cffc0a2.png)]

在 200 个周期之后,图像显示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CaseySAv-1681652801327)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/82a9152b-d3fb-467b-837f-96af60995452.png)]

要生成非常好的图像,请将网络训练 10,000 个周期。

可视化损失

为了可视化训练的损失,请按以下方式启动 TensorBoard 服务器:

tensorboard --logdir=logs

现在,在浏览器中打开localhost:6006。 Tensorboard 的标量部分包含两种损失的图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ObBR232L-1681652801327)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/590d57c0-43e3-4ca8-ad30-8bd20887ecc5.png)]

这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止训练,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。

可视化图

Tensorboard 的GRAPHS部分包含两个网络的图。 如果网络表现不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LXsBK9yD-1681652801327)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/9bdd11ae-60a0-4d18-b9ba-9e9a46e9514f.png)]

调整超参数

超参数是模型的属性,在模型训练期间是固定的。 不同的参数可以具有不同的精度。 让我们看一下一些常用的超参数:

  • 学习率
  • 批量大小
  • 周期数
  • 生成器优化器
  • 判别器优化器
  • 层数
  • 密集层中的单元数
  • 激活函数
  • 损失函数

在“使用 Keras 实现 DCGAN”的部分中,学习率是固定的:生成器模型为 0.0005,判别器模型为 0.0005。 批量大小为 128。调整这些值可能会导致我们创建更好的模型。 如果您的模型没有生成合理的图像,请尝试更改这些值,然后再次运行模型。

DCGAN 的实际应用

可以为不同的使用案例定制 DCGAN。 DCGAN 的各种实际应用包括:

  • 动画角色的生成:目前,动画师使用计算机软件手动绘制字符,有时还绘制在纸上。 这是一个手动过程,通常需要很多时间。 使用 DCGAN,可以在更短的时间内生成新的动漫角色,从而改善了创作过程。

  • 数据集的扩充:如果您想训练一个监督的机器学习模型,要训练一个好的模型,您将需要一个大的数据集。 DCGAN 可以通过扩展现有数据集来提供帮助,因此可以增加监督模型训练所需的数据集大小。

  • MNIST 字符的生成:MNIST 数据集包含 60,000 张手写数字图像。 要训​​练复杂的监督学习模型,MNIST 数据集是不够的。 DCGAN 一旦受过训练,将生成可以添加到原始数据集中的新数字。

  • 人脸生成:DCGAN 使用卷积神经网络,非常擅长生成逼真的图像。

  • 特征提取器:训练后,可以使用判别器从中间层提取特征。 这些提取的特征在样式迁移和人脸识别等任务中很有用。 样式迁移涉及生成图像的内部表示,用于计算样式和内容损失。 请参阅以下论文,以了解有关样式迁移的更多信息。

总结

在本章中,我们介绍了深度卷积生成对抗网络。 我们从基本介绍 DCGAN 开始,然后深入探讨了 DCGAN 网络的架构。 之后,我们设置项目并安装必要的依赖项。 然后,我们研究了下载和准备数据集所需的不同步骤。 然后,我们准备了网络的 Keras 实现,并在我们的数据集中对其进行了训练。 经过训练后,我们将其用于生成新的动漫角色。 我们还探讨了 DCGAN 在实际用例中的不同应用。

在下一章中,我们将研究用于高分辨率图像生成的 SRGAN。

五、使用 SRGAN 生成逼真的图像

超分辨率生成对抗网络SRGAN ,是生成对抗网络GAN), 低分辨率图像中的高分辨率图像,具有更好的细节和更高的质量 。 CNN 较早用于产生高分辨率图像,该图像可以更快地训练并达到高水平的精度。 但是,在某些情况下,它们无法恢复更精细的细节,并且通常会生成模糊的图像。 在本章中,我们将在 Keras 框架中实现一个 SRGAN 网络,该网络将能够生成高分辨率图像。 SRGAN 在标题为《使用生成对抗网络的逼真的单图像超分辨率》的论文中引入,作者是 Christian Ledig,Lucas Theis,Ferenc Huszar, Jose Caballero,Andrew Cunningham 等,可以在以下链接中找到。

在本章中,将涵盖以下主题:

  • SRGAN 简介
  • 建立项目
  • 下载 CelebA 数据集
  • SRGAN 的 Keras 实现
  • 训练和优化 SRGAN 网络
  • SRGAN 的实际应用

SRGAN 简介

与其他 GAN 一样,SRGAN 包含一个生成器网络和一个判别器网络。 两个网络都很深。 这两个网络的功能指定如下:

  • 生成器:生成器网络拍摄大小为64x64x3的低分辨率图像,并且在一系列卷积和上采样层之后, 生成形状为256x256x3的超分辨率图片
  • 判别器:判别器网络拍摄高分辨率图像,并尝试识别给定的图像是真实的(来自真实数据样本)还是(由生成器)伪造的

SRGAN 的架构

在 SRGAN 中,这两个网络都是深度卷积神经网络。 它们包含卷积层和上采样层。 每个卷积层之后是批量归一化操作和一个激活层。 我们将在以下各节中详细探讨网络。 下图显示了 SRGAN 的架构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GOueF0Pp-1681652801327)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/fe3eced0-c452-4b7a-9f6d-dd8228048ab9.png)]

在以下各节中,让我们详细了解网络的架构。

生成器网络的架构

如前一节所述,生成器网络是深度卷积神经网络。 生成器网络包含以下块:

  • 前残差块
  • 残差块
  • 后残差块
  • 上采样块
  • 最后的卷积层

让我们一一探讨这些块:

  • 前残差块:前残差块包含单个 2D 卷积层和 relu 作为激活函数。 块的配置如下:
层名称超参数输入形状输出形状
2D 卷积层Filters=64, kernel_size=3, strides=1, padding='same', activation='relu'(64, 64, 3)(64, 64, 64)
  • 残差块:残差块包含两个 2D 卷积层。 两层之后是动量值等于 0.8 的批归一化层。 每个残差块的配置如下:
层名称超参数输入形状输出形状
2D 卷积层Filters=64, kernel_size=3, strides=1, padding='same', activation='relu'(64, 64, 64)(64, 64, 64)
批量规范化层Momentum=0.8(64, 64, 64)(64, 64, 64)
2D 卷积层Filters=64, kernel_size=3, strides=1, padding='same'(64, 64, 64)(64, 64, 64)
批量规范化层Momentum=0.8(64, 64, 64)(64, 64, 64)
加法层None(64, 64, 64)(64, 64, 64)

加法层计算输入到块的张量和最后一批归一化层的输出之和。 生成器网络包含 16 个具有上述配置的残差块。

  • 后残差块:后残差块还包含单个 2D 卷积层和relu作为激活函数。 卷积层之后是批量归一化层,其动量值为 0.8。 后残差块的配置如下:
层名称超参数输入形状输出形状
2D 卷积层Filters=64, kernel_size=3, strides=1, padding='same'(64, 64, 64)(64, 64, 64)
批量规范化层Momentum=0.8(64, 64, 64)(64, 64, 64)
  • 上采样块:上采样块包含一个上采样层和一个 2D 卷积层,并使用 relu 作为激活函数。 生成器网络中有两个上采样模块。 第一个上采样模块的配置如下:
层名称超参数输入形状输出形状
2D 上采样层Size=(2, 2)(64, 64, 64)(128, 128, 64)
2D 卷积层Filters=256, kernel_size=3, strides=1, padding='same', activation='relu'(128, 128, 256)(128, 128, 256)

第二个上采样模块的配置如下:

层名称超参数输入形状输出形状
2D 上采样层Size=(2, 2)(128, 128, 256)(256, 256, 256)
2D 卷积层Filters=256, kernel_size=3, strides=1, padding='same', activation='relu'(256, 256, 256)(256, 256, 256)
  • 最后的卷积层:最后一层是使用 tanh 作为激活函数的 2D 卷积层。 它生成形状为(256, 256, 3)的图像。 最后一层的配置如下:
层名称超参数输入形状输出形状
2D 卷积层Filters=3, kernel_size=9, strides=1, padding='same', activation='tanh'(256, 256, 256)(256, 256, 3)

这些超参数最适合 Keras 框架。 如果使用任何其他框架,请相应地对其进行修改。

判别器网络的架构

判别器网络也是一个深度卷积网络。 它包含八个卷积块,后跟两个密集(完全连接)层。 每个卷积块后面都有一个批量归一化层。 网络的末端有两个密集层,它们充当分类块。 最后一层预测图像属于真实数据集或伪数据集的概率。 判别器网络的详细配置如下表所示:

层名称超参数输入形状输出形状
输入层没有(256, 256, 3)(256, 256, 3)
2D 卷积层filter= 64, kernel_size = 3, stride= 1, padding='same', activcation='leakyrelu'(256, 256, 3)(256, 256, 64)
2D 卷积层filter= 64, kernel_size = 3, stride= 2, padding='same', activcation='leakyrelu'(256, 256, 64)(128, 128, 64)
批量规范化层momentum= 0.8(128, 128, 64)(128, 128, 64)
2D 卷积层filter= 128, kernel_size = 3, stride= 1, padding='same', activcation='leakyrelu'(128, 128, 64)(128, 128, 128)
批量规范化层momentum= 0.8(128, 128, 128)(128, 128, 128)
2D 卷积层filter= 128, kernel_size = 3, stride= 2, padding='same', activcation='leakyrelu'(128, 128, 128)(64, 64, 128)
批量规范化层momentum= 0.8(64, 64, 128)(64, 64, 128)
2D 卷积层filter= 256, kernel_size = 3, stride= 1, padding='same', activcation='leakyrelu'(64, 64, 128)(64, 64, 256)
批量规范化层momentum= 0.8(64, 64, 256)(64, 64, 256)
2D 卷积层filter= 256, kernel_size = 3, stride= 2, padding='same', activcation='leakyrelu'(64, 64, 256)(32, 32, 256)

现在,我们对这两个网络的架构有了清晰的了解。 在下一部分中,让我们看一下训练 SRGAN 所需的目标函数。

训练目标函数

要训​​练 SRGAN,有一个目标函数或损失函数,我们需要将其最小化以训练模型。 SRGAN 的目标函数称为感知损失函数,它是两个损失函数的加权和,如下所示:

  • 内容损失
  • 对抗损失

在以下各节中,让我们详细研究内容损失和对抗损失。

内容损失

内容损失有两种类型,如下所示:

  • MSE 像素损失
  • VGG 损失

让我们详细讨论这些损失。

MSE 像素损失

内容损失是在真实图像的每个像素值与生成的图像的每个像素值之间计算的均方误差。 像素级 MSE 损失计算出生成的图像与实际图像有多大差异。 像素级 MSE 损失的计算公式如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HTs0aZcZ-1681652801328)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/3b2c1077-9f9a-4cda-8670-cd8d737041f8.png)]

在此,G[θ[G]](I^(LR))表示由所生成的网络所生成的高分辨率图像。 I^(HR)代表从真实数据集中采样的高分辨率图像。

VGG 损失

VGG 损失是另一个内存损失函数,可应用于生成的图像和真实图像。 VGG19 是一种非常流行的深度神经网络,主要用于图像分类。 VGG19 由 Simonyan 和 Zisserman 在他们的论文《用于大规模图像识别的超深度卷积网络》中引入,可在这里获得。 预训练的 VGG19 网络的中间层用作特征提取器,可用于提取生成的图像和真实图像的特征映射。 VGG 损失基于这些提取的特征映射。 计算为生成的图像和真实图像的特征映射之间的欧几里得距离。 VGG 损失的公式如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GlvrEO5U-1681652801328)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/c854836e-df68-4886-bb7c-8577a41d732c.png)]

在此,φ[i, j]表示由 VGG19 网络生成的特征映射。 φ[i, j](I^(HR))代表提取的真实图像特征映射,φ[i, j](G[θ[G]](I^(LR)))代表提取的高分辨率图像生成的特征映射。 整个方程表示生成图像和真实图像的特征映射之间的欧式距离。

前述内容损失中的任何一种都可以用于训练 SRGAN。 对于我们的实现,我们将使用 VGG 损失。

对抗损失

根据判别器网络返回的概率计算对抗损失。 在对抗模型中,判别器网络被馈送有由生成的网络生成的生成的图像。 对抗损失可以用以下等式表示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gZ8PgVOg-1681652801328)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/cf90c58f-3995-48a8-b78a-0ab81cb7ae9d.png)]

这里,G[θG](I^(LR))是所生成的图像 , D[θD](G[θG](I^(LR)))表示所生成的图像是真实图像的概率。

知觉损失函数是内容损失和对抗损失的加权和,表示为以下方程式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k7AxCrt4-1681652801328)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/4e2039a0-5a7e-4038-b68d-f19fa142d2d8.png)]

在此,总的感知损失由l^(SR)表示。 l^(SR)[X]是内容损失,可以是像素级 MSE 损失或 VGG 损失。

通过最小化感知损失值,生成器网络试图欺骗判别器。 随着感知损失的值减小,生成器网络开始生成更逼真的图像。

现在开始进行该项目。

设置项目

如果尚未使用所有章节的完整代码克隆存储库,请立即克隆存储库。 下载的代码有一个名为Chapter05的目录,其中包含本章的全部代码。 执行以下命令来设置项目:

  1. 首先,导航到父目录,如下所示:
cd Generative-Adversarial-Networks-Projects
  1. 现在,将目录从当前目录更改为 Chapter05
cd Chapter05
  1. 接下来,为该项目创建一个 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

我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。

  1. 接下来,激活新创建的虚拟环境:
source venv/bin/activate

激活虚拟环境后,所有其他命令将在此虚拟环境中执行。

  1. 接下来,通过执行以下命令,安装requirements.txt 文件中提供的所有库:
pip install -r requirements.txt

您可以参考 README.md 文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。

在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一节中,让我们研究数据集。 我们将探索下载和格式化数据集的各种步骤。

下载 CelebA 数据集

在本章中,我们将使用大型 CelebFaces 属性CelebA)数据集,该数据集可从这里获得。 数据集包含 202、599 名名人的人脸图像。

该数据集仅可用于非商业研究目的,不能用于商业目的。 如果您打算将数据集用于商业目的,请寻求图像所有者的许可。

我们将使用 CelebA 数据集训练我们的 SRGAN 网络。 执行以下步骤下载和提取数据集:

  1. 从以下链接下载数据集:
https://www.dropbox.com/sh/8oqt9vytwxb3s4r/AAB06FXaQRUNtjW9ntaoPGvCa?dl=0
  1. 通过执行以下命令从下载的img_align_celeba.zip中提取图像:
unzip img_align_celeba.zip

现在,我们已经下载并提取了数据集。 现在,我们可以开始进行 SRGAN 的 Keras 实现。

SRGAN 的 Keras 实现

正如我们所讨论的,SRGAN 在 Imagenet 数据集上具有三个神经网络,一个生成器,一个判别器和一个预训练的 VGG19 网络。 在本节中,我们将编写所有网络的实现。 让我们从实现生成器网络开始。

在开始编写实现之前,创建一个名为main.py的 Python 文件并导入基本模块,如下所示:

import glob
import osimport numpy as np
import tensorflow as tf
from keras import Input
from keras.applications import VGG19
from keras.callbacks import TensorBoard
from keras.layers import BatchNormalization, Activation, LeakyReLU, Add, Dense, PReLU, Flatten
from keras.layers.convolutional import Conv2D, UpSampling2D
from keras.models import Model
from keras.optimizers import Adam
from keras_preprocessing.image import img_to_array, load_img
from scipy.misc import imsave

生成器网络

我们已经在“生成器网络的架构”中探讨了生成器网络的架构。 让我们首先在 Keras 框架中编写生成器网络的层,然后使用 Keras 框架的函数式 API 创建 Keras 模型。

执行以下步骤以在 Keras 中实现生成器网络:

  1. 首先定义生成器网络所需的超参数:
residual_blocks = 16 momentum = 0.8  input_shape = (64, 64, 3)
  1. 接下来,创建一个输入层,以将输入提供给网络,如下所示:
input_layer = Input(shape=input_shape)

输入层获取形状为(64, 64, 3)的输入图像,并将其传递到网络中的下一层。

  1. 接下来,如下所示添加残差前块(2D 卷积层):
    配置:
    • 过滤器64
    • 核大小9
    • 步幅1
    • 填充same
    • 激活relu:
gen1 = Conv2D(filters=64, kernel_size=9, strides=1, padding='same', activation='relu')(input_layer)
  1. 接下来,编写一个包含残差块完整代码的方法,如下所示:
def residual_block(x):"""Residual block """  filters = [64, 64]kernel_size = 3strides = 1padding = "same"momentum = 0.8activation = "relu"    res = Conv2D(filters=filters[0], kernel_size=kernel_size, strides=strides, padding=padding)(x)res = Activation(activation=activation)(res)res = BatchNormalization(momentum=momentum)(res)res = Conv2D(filters=filters[1], kernel_size=kernel_size, strides=strides, padding=padding)(res)res = BatchNormalization(momentum=momentum)(res)# Add res and xres = Add()([res, x])return res
  1. 现在,使用在最后一步中定义的residual_block函数添加 16 个残差块:
res = residual_block(gen1)
for i in range(residual_blocks - 1):res = residual_block(res)

前残差块的输出进入第一个残差块。 第一个残差块的输出将到达第二个残差块,依此类推,直到第 16 个残差块。

  1. 接下来,添加后残差块(2D 卷积层,然后是批量归一化层),如下所示:
    配置:
    • 过滤器64
    • 核大小3
    • 步幅1
    • 填充same
    • 批量规范化:是(动量为 0.8):
gen2 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(res)
gen2 = BatchNormalization(momentum=momentum)(gen2)
  1. 现在,添加Add层,以获取残留前块的输出gen1和后残差块的输出gen2之和。 该层生成另一个类似形状的张量。 有关更多详细信息,请参考“ 生成器网络的架构”部分。
gen3 = Add()([gen2, gen1])
  1. 接下来,添加一个上采样块,如下所示:
    配置:
    • 上采样大小2
    • 过滤器256
    • 核大小3
    • 步幅1
    • 填充same
    • 激活PReLU:
gen4 = UpSampling2D(size=2)(gen3)
gen4 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen4)
gen4 = Activation('relu')(gen4)
  1. 接下来,添加另一个上采样块,如下所示:
    配置:
    • 上采样大小2
    • 过滤器256
    • 核大小3
    • 步幅1
    • 填充same
    • 激活PReLU:
gen5 = UpSampling2D(size=2)(gen4)
gen5 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen5)
gen5 = Activation('relu')(gen5)
  1. 最后,添加输出卷积层:
    配置:
    * 过滤器3(等于通道数)
    * 核大小9
    * 步幅1
    * 填充same
    * 激活tanh:
gen6 = Conv2D(filters=3, kernel_size=9, strides=1, padding='same')(gen5)
output = Activation('tanh')(gen6)

一旦定义了网络中的所有层,就可以创建 Keras 模型。 我们已经使用 Keras 的函数式 API 定义了 Keras 顺序图。 让我们通过指定网络的输入和输出来创建 Keras 模型。

  1. 现在,创建 Keras 模型并指定模型的输入和输出,如下所示:
model = Model(inputs=[input_layer], outputs=[output], name='generator')

我们已经成功地为生成器网络创建了 Keras 模型。 现在,将生成器网络的整个代码包装在 Python 函数中,如下所示:

def build_generator():"""Create a generator network using the hyperparameter values defined below  :return:"""  residual_blocks = 16momentum = 0.8input_shape = (64, 64, 3)# Input Layer of the generator networkinput_layer = Input(shape=input_shape)# Add the pre-residual blockgen1 = Conv2D(filters=64, kernel_size=9, strides=1, padding='same',  activation='relu')(input_layer)# Add 16 residual blocksres = residual_block(gen1)for i in range(residual_blocks - 1):res = residual_block(res)# Add the post-residual blockgen2 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(res)gen2 = BatchNormalization(momentum=momentum)(gen2)# Take the sum of the output from the pre-residual block(gen1) and the post-residual block(gen2)gen3 = Add()([gen2, gen1])# Add an upsampling blockgen4 = UpSampling2D(size=2)(gen3)gen4 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen4)gen4 = Activation('relu')(gen4)# Add another upsampling blockgen5 = UpSampling2D(size=2)(gen4)gen5 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen5)gen5 = Activation('relu')(gen5)# Output convolution layergen6 = Conv2D(filters=3, kernel_size=9, strides=1, padding='same')(gen5)output = Activation('tanh')(gen6)# Keras modelmodel = Model(inputs=[input_layer], outputs=[output], name='generator')return model

我们已经成功地为生成器网络创建了 Keras 模型。 在下一节中,我们将为判别器网络创建 Keras 模型。

判别器网络

我们已经在“使用 Keras 框架的函数式 API 创建 Keras 模型”中探讨了判别器网络的架构。 让我们首先在 Keras 框架中编写判别器网络的层。

执行以下步骤以在 Keras 中实现判别器网络:

  1. 首先定义判别器网络所需的超参数:
leakyrelu_alpha = 0.2 momentum = 0.8 input_shape = (256, 256, 3)
  1. 接下来,创建一个输入层,以将输入提供给网络,如下所示:
input_layer = Input(shape=input_shape)
  1. 接下来,添加一个卷积块,如下所示:
    配置:
    • 过滤器64
    • 核大小3
    • 步幅1
    • 填充same
    • 激活LeakyReLUalpha等于 0.2:
dis1 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(input_layer)
dis1 = LeakyReLU(alpha=leakyrelu_alpha)(dis1)
  1. 接下来,添加另外七个卷积块,如下所示:
    配置:
    • 过滤器64128128256256512512
    • 核大小3333333
    • 步幅2121212
    • 每个卷积层的填充same
    • 激活LealyReLU,每个卷积层的alpha等于 0.2:
# Add the 2nd convolution block dis2 = Conv2D(filters=64, kernel_size=3, strides=2, padding='same')(dis1)
dis2 = LeakyReLU(alpha=leakyrelu_alpha)(dis2)
dis2 = BatchNormalization(momentum=momentum)(dis2)# Add the third convolution block dis3 = Conv2D(filters=128, kernel_size=3, strides=1, padding='same')(dis2)
dis3 = LeakyReLU(alpha=leakyrelu_alpha)(dis3)
dis3 = BatchNormalization(momentum=momentum)(dis3)# Add the fourth convolution block dis4 = Conv2D(filters=128, kernel_size=3, strides=2, padding='same')(dis3)
dis4 = LeakyReLU(alpha=leakyrelu_alpha)(dis4)
dis4 = BatchNormalization(momentum=0.8)(dis4)# Add the fifth convolution block dis5 = Conv2D(256, kernel_size=3, strides=1, padding='same')(dis4)
dis5 = LeakyReLU(alpha=leakyrelu_alpha)(dis5)
dis5 = BatchNormalization(momentum=momentum)(dis5)# Add the sixth convolution block dis6 = Conv2D(filters=256, kernel_size=3, strides=2, padding='same')(dis5)
dis6 = LeakyReLU(alpha=leakyrelu_alpha)(dis6)
dis6 = BatchNormalization(momentum=momentum)(dis6)# Add the seventh convolution block dis7 = Conv2D(filters=512, kernel_size=3, strides=1, padding='same')(dis6)
dis7 = LeakyReLU(alpha=leakyrelu_alpha)(dis7)
dis7 = BatchNormalization(momentum=momentum)(dis7)# Add the eight convolution block dis8 = Conv2D(filters=512, kernel_size=3, strides=2, padding='same')(dis7)
dis8 = LeakyReLU(alpha=leakyrelu_alpha)(dis8)
dis8 = BatchNormalization(momentum=momentum)(dis8)
  1. 接下来,添加具有 1,024 个节点的密集层,如下所示:
    配置:
    • 节点1024
    • 激活LeakyReLUalpha等于 0.2:
dis9 = Dense(units=1024)(dis8)
dis9 = LeakyReLU(alpha=0.2)(dis9)
  1. 然后,添加一个密集层以返回概率,如下所示:
output = Dense(units=1, activation='sigmoid')(dis9)
  1. 最后,创建 Keras 模型并指定网络的输入和输出:
model = Model(inputs=[input_layer], outputs=[output], name='discriminator')

将判别器网络的整个代码如下包装在函数中:

def build_discriminator():"""Create a discriminator network using the hyperparameter values defined below  :return:"""  leakyrelu_alpha = 0.2momentum = 0.8input_shape = (256, 256, 3)input_layer = Input(shape=input_shape)# Add the first convolution blockdis1 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(input_layer)dis1 = LeakyReLU(alpha=leakyrelu_alpha)(dis1)# Add the 2nd convolution blockdis2 = Conv2D(filters=64, kernel_size=3, strides=2, padding='same')(dis1)dis2 = LeakyReLU(alpha=leakyrelu_alpha)(dis2)dis2 = BatchNormalization(momentum=momentum)(dis2)# Add the third convolution blockdis3 = Conv2D(filters=128, kernel_size=3, strides=1, padding='same')(dis2)dis3 = LeakyReLU(alpha=leakyrelu_alpha)(dis3)dis3 = BatchNormalization(momentum=momentum)(dis3)# Add the fourth convolution blockdis4 = Conv2D(filters=128, kernel_size=3, strides=2, padding='same')(dis3)dis4 = LeakyReLU(alpha=leakyrelu_alpha)(dis4)dis4 = BatchNormalization(momentum=0.8)(dis4)# Add the fifth convolution blockdis5 = Conv2D(256, kernel_size=3, strides=1, padding='same')(dis4)dis5 = LeakyReLU(alpha=leakyrelu_alpha)(dis5)dis5 = BatchNormalization(momentum=momentum)(dis5)# Add the sixth convolution blockdis6 = Conv2D(filters=256, kernel_size=3, strides=2, padding='same')(dis5)dis6 = LeakyReLU(alpha=leakyrelu_alpha)(dis6)dis6 = BatchNormalization(momentum=momentum)(dis6)# Add the seventh convolution blockdis7 = Conv2D(filters=512, kernel_size=3, strides=1, padding='same')(dis6)dis7 = LeakyReLU(alpha=leakyrelu_alpha)(dis7)dis7 = BatchNormalization(momentum=momentum)(dis7)# Add the eight convolution blockdis8 = Conv2D(filters=512, kernel_size=3, strides=2, padding='same')(dis7)dis8 = LeakyReLU(alpha=leakyrelu_alpha)(dis8)dis8 = BatchNormalization(momentum=momentum)(dis8)# Add a dense layerdis9 = Dense(units=1024)(dis8)dis9 = LeakyReLU(alpha=0.2)(dis9)# Last dense layer - for classificationoutput = Dense(units=1, activation='sigmoid')(dis9)model = Model(inputs=[input_layer], outputs=[output], name='discriminator')return model

在这一部分,我们已经成功地为判别器网络创建了 Keras 模型。 在下一节中,我们将构建 VGG19 网络,如“SRGAN 简介”中所示。

VGG19 网络

我们将使用预训练的 VGG19 网络。 VGG19 网络的目的是提取生成的图像和真实图像的特征映射。 在本节中,我们将在 Keras 中使用预训练的权重构建和编译 VGG19 网络:

  1. 首先指定输入形状:
input_shape = (256, 256, 3)
  1. 接下来,加载预训练的 VGG19 并指定模型的输出:
vgg = VGG19(weights="imagenet")
vgg.outputs = [vgg.layers[9].output]
  1. 接下来,创建符号 input_tensor ,这将是我们对 VGG19 网络的符号输入,如下所示:
input_layer = Input(shape=input_shape)
  1. 接下来,使用 VGG19 网络提取特征:
features = vgg(input_layer)
  1. 最后,创建 Keras model并为网络指定inputsoutputs
model = Model(inputs=[input_layer], outputs=[features])

最后,将 VGG19 模型的整个代码包装在一个函数中,如下所示:

def build_vgg():"""Build the VGG network to extract image features """  input_shape = (256, 256, 3)# Load a pre-trained VGG19 model trained on 'Imagenet' datasetvgg = VGG19(weights="imagenet")vgg.outputs = [vgg.layers[9].output]input_layer = Input(shape=input_shape)# Extract featuresfeatures = vgg(input_layer)# Create a Keras modelmodel = Model(inputs=[input_layer], outputs=[features])return model

对抗网络

对抗网络是使用生成器,判别器和 VGG19 的组合网络。 在本节中,我们将创建一个对抗网络。

执行以下步骤来创建对抗网络:

  1. 首先为网络创建输入层:
input_low_resolution = Input(shape=(64, 64, 3))

对抗网络将收到(64, 64, 3)形状的图像,这就是我们创建输入层的原因。

  1. 接下来,使用生成器网络生成伪造的高分辨率图像,如下所示:
fake_hr_images = generator(input_low_resolution)
  1. 接下来,使用 VGG19 网络提取伪造图像的特征,如下所示:
fake_features = vgg(fake_hr_images)
  1. 接下来,使判别器网络在对抗网络中不可训练:
discriminator.trainable = False

我们使判别器网络不可训练,因为我们不想在训练生成器网络时训练判别器网络。

  1. 接下来,将伪造的图像传递到判别器网络:
output = discriminator(fake_hr_images)
  1. 最后,创建一个 Keras 模型,这将是我们的对抗模型:
model = Model(inputs=[input_low_resolution], outputs=[output, fake_features])
  1. 将对抗模型的整个代码包装在 Python 函数中:
def build_adversarial_model(generator, discriminator, vgg):input_low_resolution = Input(shape=(64, 64, 3))fake_hr_images = generator(input_low_resolution)fake_features = vgg(fake_hr_images)discriminator.trainable = False    output = discriminator(fake_hr_images)model = Model(inputs=[input_low_resolution],outputs=[output, fake_features])for layer in model.layers:print(layer.name, layer.trainable)print(model.summary())return model

我们现在已经成功地在 Keras 中实现了网络。 接下来,我们将在“数据准备”部分中下载的数据集上训练网络。

训练 SRGAN

训练 SRGAN 网络是一个分为两个步骤的过程。 第一步,我们训练判别器网络。 在第二步中,我们训练对抗网络,最终训练生成器网络。 让我们开始训练网络。

执行以下步骤来训练 SRGAN 网络:

  1. 首先定义训练所需的超参数:
# Define hyperparameters data_dir = "Paht/to/the/dataset/img_align_celeba/*.*" epochs = 20000 batch_size = 1   # Shape of low-resolution and high-resolution images low_resolution_shape = (64, 64, 3)
high_resolution_shape = (256, 256, 3)
  1. 接下来,定义训练优化器。 对于所有网络,我们将使用学习率等于 0.0002 且beta_1等于0.5的 Adam 优化器:
# Common optimizer for all networks common_optimizer = Adam(0.0002, 0.5)

建立和编译网络

在本节中,我们将完成构建和编译网络所需的不同步骤:

  1. 构建和编译 VGG19 网络:
vgg = build_vgg()
vgg.trainable = False vgg.compile(loss='mse', optimizer=common_optimizer, metrics=['accuracy'])

要编译 VGG19,请使用mse作为损失,使用accuracy作为度量,并使用common_optimizer作为优化器。 编译网络之前,请先禁用训练,因为我们不想训练 VGG19 网络。

  1. 接下来,构建并编译discriminator网络,如下所示:
discriminator = build_discriminator()
discriminator.compile(loss='mse', optimizer=common_optimizer, metrics=['accuracy'])

要编译判别器网络,使用 mse 作为损失, accuracy 作为度量,并使用 common_optimizer 作为优化器。

  1. 接下来,构建生成器网络:
generator = build_generator()
  1. 接下来,创建一个对抗模型。 首先创建两个输入层:
input_high_resolution = Input(shape=high_resolution_shape)
input_low_resolution = Input(shape=low_resolution_shape)
  1. 接下来,使用生成器网络从低分辨率图像中象征性地生成高分辨率图像:
generated_high_resolution_images = generator(input_low_resolution)

使用 VGG19 提取生成图像的特征映射:

features = vgg(generated_high_resolution_images)

使判别器网络不可训练,因为我们不想在对抗模型训练期间训练判别器模型 :

discriminator.trainable = False
  1. 接下来,使用判别器网络获取生成的高分辨率伪图像的概率:
probs = discriminator(generated_high_resolution_images)

在此,probs表示所生成的图像属于真实数据集的概率。

  1. 最后,创建并编译对抗网络:
adversarial_model = Model([input_low_resolution, input_high_resolution], [probs, features])
adversarial_model.compile(loss=['binary_crossentropy', 'mse'], loss_weights=[1e-3, 1], optimizer=common_optimizer)

要编译对抗模型,请使用binary_crossentropymse作为损失函数,common_optimizer作为优化器,并使用[0.001, 1]作为损失权重。

  1. 添加Tensorboard以可视化训练损失并可视化网络图:
tensorboard = TensorBoard(log_dir="logs/".format(time.time()))
tensorboard.set_model(generator)
tensorboard.set_model(discriminator)
  1. 创建一个循环,该循环应运行指定的周期数:
for epoch in range(epochs):print("Epoch:{}".format(epoch))

完成此步骤后,所有代码都将在此for循环内。

  1. 接下来,对一批高分辨率和低分辨率图像进行采样,如下所示:
high_resolution_images, low_resolution_images = sample_images(data_dir=data_dir,   batch_size=batch_size,low_resolution_shape=low_resolution_shape,     high_resolution_shape=high_resolution_shape)

sample_images函数的代码如下。 它具有很强的描述性,通过阅读可以理解。 它包含加载和调整图像大小并生成高分辨率和低分辨率图像的不同步骤:

def sample_images(data_dir, batch_size, high_resolution_shape, low_resolution_shape):# Make a list of all images inside the data directoryall_images = glob.glob(data_dir)# Choose a random batch of imagesimages_batch = np.random.choice(all_images, size=batch_size)low_resolution_images = []high_resolution_images = []for img in images_batch:# Get an ndarray of the current imageimg1 = imread(img, mode='RGB')img1 = img1.astype(np.float32)# Resize the imageimg1_high_resolution = imresize(img1, high_resolution_shape)img1_low_resolution = imresize(img1, low_resolution_shape)# Do a random flipif np.random.random() < 0.5:img1_high_resolution = np.fliplr(img1_high_resolution)img1_low_resolution = np.fliplr(img1_low_resolution)high_resolution_images.append(img1_high_resolution)low_resolution_images.append(img1_low_resolution)return np.array(high_resolution_images), np.array(low_resolution_images)
  1. 接下来,对图像进行归一化以将像素值转换为[-1, 1]之间的范围,如下所示:
    high_resolution_images = high_resolution_images / 127.5 - 1.low_resolution_images = low_resolution_images / 127.5 - 1.

将像素值转换为 -1 到 1 的范围非常重要。我们的生成器网络的末尾有tanhtanh激活函数将值压缩到相同范围。 在计算损失时,必须使所有值都在同一范围内。

训练判别器网络

本节中给出的步骤显示了如何训练判别器网络。 这是最后一系列步骤的延续:

  1. 使用generator网络生成伪造的高分辨率图像:
generated_high_resolution_images = generator.predict(low_resolution_images)
  1. 创建一批真实标签和伪标签:
    real_labels = np.ones((batch_size, 16, 16, 1))fake_labels = np.zeros((batch_size, 16, 16, 1))
  1. 在真实图像和真实标签上训练判别器网络:
d_loss_real = discriminator.train_on_batch(high_resolution_images, real_labels)
  1. 在生成的图像和伪造标签上训练判别器:
d_loss_fake = discriminator.train_on_batch(generated_high_resolution_images, fake_labels)
  1. 最后,计算总的判别器损失:
    d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)

现在,我们添加了代码来训练判别器网络。 接下来,添加代码以训练对抗模型,从而训练生成器网络。

训练生成器网络

本节中给出的步骤显示了如何训练生成器网络。 这是最后一系列步骤的延续:

  1. 再次,对一批高分辨率和低分辨率图像进行采样并对其进行归一化:
    high_resolution_images, low_resolution_images = sample_images(data_dir=data_dir, batch_size=batch_size,low_resolution_shape=low_resolution_shape,  high_resolution_shape=high_resolution_shape)# Normalize images    high_resolution_images = high_resolution_images / 127.5 - 1.low_resolution_images = low_resolution_images / 127.5 - 1.
  1. 使用 VGG19 网络提取真实高分辨率图像的特征映射(内部表示):
    image_features = vgg.predict(high_resolution_images)
  1. 最后,训练对抗模型并为其提供适当的输入,如下所示:
    g_loss = adversarial_model.train_on_batch([low_resolution_images, high_resolution_images],[real_labels, image_features])
  1. 在每个周期完成之后,将损失写入 TensorBoard 以将其可视化:
    write_log(tensorboard, 'g_loss', g_loss[0], epoch)write_log(tensorboard, 'd_loss', d_loss[0], epoch)
  1. 每隔 100 个周期后,使用生成器网络生成高分辨率的伪图像并保存以使其可视化:
 if epoch % 100 == 0:high_resolution_images, low_resolution_images = sample_images(data_dir=data_dir, batch_size=batch_size,low_resolution_shape=low_resolution_shape,high_resolution_shape=high_resolution_shape)# Normalize imageshigh_resolution_images = high_resolution_images / 127.5 - 1.low_resolution_images = low_resolution_images / 127.5 - 1.# Generate fake high-resolution images  generated_images = generator.predict_on_batch(low_resolution_images)# Savefor index, img in enumerate(generated_images):save_images(low_resolution_images[index], high_resolution_images[index], img,path="results/img_{}_{}".format(epoch, index))

这些图像将帮助您决定是继续训练还是尽早停止训练。 如果生成的高分辨率图像的质量良好,请停止训练。 或者,继续训练,直到您的模型变好为止。

我们现在已经成功地在CelebA数据集上训练了 SRGAN 网络。 训练完成后,生成高分辨率图像非常容易。 拍摄大小为64 x 64 x 3的低分辨率图像,并将其传递给generator.predict()函数,该函数将生成高分辨率图像。

保存模型

在 Keras 中保存模型只需要一行代码。 要保存generator模型,请添加以下行:

# Specify the path for the generator model
gen_model.save("directory/for/the/generator/model.h5")

同样,通过添加以下行来保存discriminator模型:

# Specify the path for the discriminator model
dis_model.save("directory/for/the/discriminator/model.h5") 

可视化生成的图像

经过大量时间后,生成器将开始生成良好的图像。 让我们看一下生成的图像:

  • 在 1,000 个周期之后,图像显示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BONfufBj-1681652801329)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/fcd5c713-f0e8-4b2b-b914-06b9cfdb3600.png)]

  • 5,000 个周期后,图像显示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xltyNNiI-1681652801329)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/f099cb7a-a2b7-476b-9476-f7e47eb947b4.png)]

  • 10,000 个周期后,图像显示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ubtPDh2y-1681652801329)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/96ef5573-9de4-4bf6-a6ec-848e6f3c622c.png)]

  • 15,000 个周期后,图像显示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ad5qSoYa-1681652801329)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/fd0ed18a-8cd7-4200-a3e9-81c6f771302a.png)]

  • 经过 20,000 个周期后,图像显示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t4f0a9cr-1681652801330)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/d58519f5-35a1-4b3b-831f-163b995eeabd.png)]

要生成非常好的图像,请将网络训练 30,000-50,000 个周期。

可视化损失

要显示训练损失,请启动,,tensorboard,,服务器,如下所示:

tensorboard --logdir=logs

现在,在浏览器中打开 localhost:6006 。 TensorBoard 的标量部分包含两种损失的图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4etk5xVC-1681652801330)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/777f3571-0412-4464-bb22-c1445c488875.png)]

这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止训练,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 尝试使用超参数,然后选择一组您认为可能会提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。

可视化图

TensorBoard 的 GRAPHS 部分包含两个网络的图。 如果网络表现不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的运算:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CqU8hgv2-1681652801330)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/5d8ca1c2-04b5-4106-b5c6-5c96ca28ce46.png)]

SRGAN 的实际应用

现在,让我们看一下 SRGAN 的实际应用:

  • 恢复旧照片
  • 行业应用,例如自动提高徽标,横幅和小册子的分辨率
  • 自动为用户增加社交媒体图像的分辨率
  • 拍摄时自动增强相机上的照片
  • 提高医学图像的分辨率

总结

在本章中,我们首先介绍 SRGAN。 然后,我们研究了生成器和判别器网络的架构。 后来,我们执行了该项目所需的设置。 然后,我们收集并探索了数据集。 之后,我们在训练 SRGAN 之前先在 Keras 中实现了该项目,评估了训练后的 SRGAN 网络,并使用超参数优化技术对训练后的模型进行了优化。 最后,我们简要介绍了 SRGAN 的一些不同应用。

在下一章中,我们将介绍 StackGAN 及其不同的应用。


http://www.ppmy.cn/news/750042.html

相关文章

CSS3_01_圆角_边框_渐变_字体

CSS3 是最新的 CSS 标准。 我们的 CSS3 教程向您讲解 CSS3 中的新特性。 手册说明: CSS3使用了层叠样式表技术&#xff0c;可以对网页布局、字体、颜色、背景灯效果做出控制。 css3作为css的进阶版&#xff0c;拆分和增加了盒子模型、列表模块、语言模块 、背景边框 、文字特效…

Workflow 规则大全 最新版

视频类 全网视频VIP https://workflow.is/workflows/62fbf95b774642cf846b144f070d4866 视频下载 8合1https://workflow.is/workflows/d1342b439de542ffaee1994ebdadc90e FOOTAGE视频下载 https://workflow.is/workflows/72b4fd0a59c3434abec6b4a4a15ec5a0 抖音无水印视频下载 …

vb.net中datagridview取消首行选中_解锁Excel中那些隐藏很深但很实用的功能!真的太好用了!...

相信不少同学在使用Excel一段时间后&#xff0c;都会吐槽Excel工程师在安排各种功能按钮的时候&#xff0c;并没有把最好的位置留给最常用最有用的命令。比如我们经常需要插入工作表行或列&#xff0c;但是需要通过在“开始”选项卡的“单元格”组中单击“插入”按钮&#xff0…

【渝粤教育】广东开放大学 插画与漫画 形成性考核 (27)

选择题 题目&#xff1a;先从整体开始绘制的一般顺序是 题目&#xff1a;正常头身比角色转化为Q版角色&#xff0c;基本可以用几个词说完&#xff0c;那就是 题目&#xff1a;影响衣服皱褶的的因素有 题目&#xff1a;关于女性漫画人物正面面部特征&#xff0c;以下说法正确的是…

linux应用docker基本使用(一)

相关文章&#xff1a; linux docker安装及报错处理_做测试的喵酱的博客-CSDN博客 一、mac 安装及使用docker_docker mac_做测试的喵酱的博客-CSDN博客 Docker 容器使用 | 菜鸟教程 linux上docker容器运行web应用简单介绍(二&#xff09;_做测试的喵酱的博客-CSDN博客 dock…

让nextcloud支持heic、heif,显示heic缩略图

一、安装heic支持组件 yum install libheif*二、安装ImageMagick //下载资源&#xff0c;结尾改为.tar.gz,并解压 wget https://codeload.github.com/ImageMagick/ImageMagick/tar.gz/refs/tags/7.1.0-16进入指定目录 cd /root/ImageMagick-7.1.0-16///配置&#xff0c;查看是…

Linux 查看内存使用情况 释放内存

Linux 查看内存使用情况 释放内存 free -m/free -g Linux释放内存的命令&#xff1a; echo 1 > /proc/sys/vm/drop_caches

Linux下查看内存和CPU信息

1、查看CPU信息命令 cat /proc/cpuinfo 2、使用top命令查看CPU的使用情况 top 3、free命令查看内存 free total used free shared buffers cached Mem: 32830804 32507748 323056 0 1190024 27605852 4、df(disk free)&#xff1a;显示磁盘分区上可以使用的磁盘空间 -a #查看…