diff --git a/new/gan-proj/0.md b/new/gan-proj/0.md new file mode 100644 index 0000000000000000000000000000000000000000..99a82110b90d97fb143a639ac0191d2ab1e0e43e --- /dev/null +++ b/new/gan-proj/0.md @@ -0,0 +1,145 @@ + + +# 前言 + + + +**生成对抗网络**( **GAN** )具有建立下一代模型的潜力,因为它们可以模拟任何数据分布。 由于它是**机器学习**( **ML** )增长最快的领域之一,因此正在该领域进行重大研究和开发工作。 当您在 GAN 域中构建八个端到端项目时,本书将测试训练神经网络的无监督技术。 + +*生成对抗网络项目*首先介绍了用于构建高效项目的概念,工具和库。 您还将在本书的不同项目中使用各种数据集。 在每一章中,复杂性和操作的水平都会提高,从而帮助您掌握 GAN 域。 + +您将介绍 3D-GAN,DCGAN,StackGAN 和 CycleGAN 等流行的方法,并且您将通过它们的实际实现来理解生成模型的体系结构和功能。 + +到本书结尾,您将准备在工作中或项目中建立,训练和优化自己的端到端 GAN 模型。 + + + + + +# 这本书是给谁的 + + + +如果您是数据科学家,ML 开发人员,深度学习从业人员或 AI 爱好者,并且正在寻找项目指南以测试您在构建现实 GAN 模型中的知识和专长,那么该书适合您。 + + + + + +# 本书涵盖的内容 + + + +[第 1 章](../Text/1.html)和*生成对抗网络* 的介绍始于 GAN 的概念。 读者将学习什么是判别器,什么是生成器以及什么是博弈论。 接下来的几个主题将涵盖生成器的结构,鉴别器的结构,生成器和鉴别器的目标函数,GAN 的训练算法,Kullback-Leibler 和 Jensen-Shannon 发散,GAN 的评估矩阵,GAN 的不同问题, GAN 中梯度消失和爆炸,纳什均衡,批量归一化和正则化的问题。 + +[第 2 章](../Text/2.html)和 *3D-GAN –使用 GAN 生成形状* 首先简要介绍 3D-GAN 和各种架构细节。 在本章中,我们将训练 3D-GAN 生成真实世界的 3D 形状。 我们编写代码来收集 3D Shapenet 数据集,对其进行清理并使其经过培训。 然后,我们将使用 Keras 深度学习库为 3D-GAN 编写代码。 + +[第 3 章](../Text/3.html)和*使用条件 GAN 进行人脸老化* ,向读者介绍了条件生成对抗网络(cGAN)和 Age-cGAN。 我们将学习数据准备中的不同步骤,例如下载,清理和格式化数据。 我们将使用 IMDb Wiki 图像数据集。 我们将使用 Keras 框架为 Age-cGAN 编写代码。 接下来,我们将在 IMDb Wiki 图像数据集上训练网络。 最后,我们将使用年龄作为条件参数的训练模型来生成图像,而训练后的模型将针对不同年龄的人的脸部生成图像。 + +[第 4 章](../Text/4.html)和*使用 DCGAN 生成动漫角色* 从 DCGAN 的介绍开始。 我们将学习数据准备中的不同步骤,例如收集动漫角色数据集,清理数据集并准备进行训练。 我们将在 Jupyter Notebook 中介绍 DCGAN 的 Keras 实现。 接下来,我们将学习训练 DCGAN 的不同方法,并为其选择不同的超参数。 最后,我们将使用训练有素的模型生成动漫角色。 另外,我们将讨论 DCGAN 的实际应用。 + +[第 5 章](../Text/5.html)和*使用 SRGAN 生成逼真的图像* 解释了如何训练 SRGAN 生成逼真的图像。 训练过程的第一步是收集数据集,然后清理它并格式化以进行训练。 读者将学习从何处收集数据集,如何清理数据集以及如何将其转换为可用于培训的格式。 + +[第 6 章](../Text/6.html), *StackGAN –文本到逼真的图像合成* ,本章将首先介绍 StackGAN。 数据收集和数据准备是重要的步骤,我们将学习收集数据集,清理数据集并格式化以进行培训的过程。 我们将在 Jupyter Notebook 内的 Keras 中为 StackGAN 编写代码。 接下来,我们将在 CUB 数据集上训练网络。 最后,在完成模型训练后,我们将从文本描述中生成逼真的图像。 我们将讨论 StackGAN 的不同行业应用以及如何在生产中部署它们。 + +[第 7 章](../Text/7.html), *CycleGAN –将绘画变成照片* ,介绍了如何训练 CycleGAN 以 将画作变成照片。 我们将首先介绍 CycleGAN,并研究它们的不同应用。 我们将介绍不同的数据收集,数据清理和数据格式化技术。 接下来,我们将编写 CycleGAN 的 Keras 实现,并在 Jupyter Notebook 中获得有关代码的详细说明。 我们将在准备好的数据集上训练 CycleGAN。 我们将测试我们训练有素的模型,以将绘画转换为照片。 最后,我们看一下 CycleGAN 的实际应用。 + +[第 8 章](../Text/8.html),*条件 GAN –使用条件对抗网络* 进行图像到图像的翻译,介绍了如何训练条件 GAN 进行图像到图像的翻译。 我们将首先介绍条件 GAN 和不同的数据准备技术,例如数据收集,数据清理和数据格式化。 接下来,我们将在 Jupyter Notebook 的 Keras 中编写条件 GAN 的代码。 接下来,我们学习如何在已经准备好的数据集上训练条件 GAN。 我们将探索不同的超参数进行训练。 最后,我们将测试条件 GAN,并讨论实际应用中图像到图像转换的不同用例。 + +[第 9 章](../Text/9.html),*预测 GAN 的未来*,是最后一章。 在介绍了 GAN 的基础知识并完成了六个项目之后,本章将使读者了解 GAN 的未来。 在这里,我们将研究在过去的 3-4 年中,GAN 的采用取得了惊人的成就,以及该行业对它的接受程度。 我还将讨论我对 GAN 未来的个人看法。 + + + + + +# 充分利用这本书 + + + +熟悉深度学习和 Keras,并且需要一些先验知识 TensorFlow。 使用 Python 3 进行编码的经验会很有用。 + + + + + +# 下载示例代码文件 + + + +您可以从 [www.packt.com](http://www.packt.com) 的帐户中下载本书的示例代码文件。 如果您在其他地方购买了此书,则可以访问 [www.packt.com/support](http://www.packt.com/support) 并注册以将文件直接通过电子邮件发送给您。 + +您可以按照以下步骤下载代码文件: + +1. 登录或注册 [www.packt.com](http://www.packt.com) 。 +2. 选择支持标签。 +3. 单击代码下载&勘误表。 +4. 在搜索框中输入书籍的名称,然后按照屏幕上的说明进行操作。 + +下载文件后,请确保使用以下最新版本解压缩或解压缩文件夹: + +* Windows 的 WinRAR / 7-Zip +* Mac 版 Zipeg / iZip / UnRarX +* 适用于 Linux 的 7-Zip / PeaZip + +本书的代码包也托管在 GitHub 的 [https://github.com/PacktPublishing/Generative-Adversarial-Networks-Projects](https://github.com/PacktPublishing/Generative-Adversarial-Networks-Projects) 。 如果代码有更新,它将在现有的 GitHub 存储库中进行更新。 + +在 **[https://github.com/PacktPublishing/](https://github.com/PacktPublishing/) 中,我们还提供了丰富的图书和视频目录中的其他代码包。** 。 去看一下! + + + + + +# 使用约定 + + + +本书中使用了许多文本约定。 + +`CodeInText`:表示文本中的词,数据库表名称,文件夹名称,文件名,文件扩展名,路径名,伪 URL,用户输入和 Twitter 句柄。 这里是一个示例:“ 使用 `scipy` 中的 `loadmat()` 函数来检索 `voxels`。” + +代码块设置如下: + +```py +import scipy.io as io +voxels = io.loadmat("path to .mat file")['instance'] +``` + +任何命令行输入或输出的编写方式如下: + +```py +pip install -r requirements.txt +``` + +**粗体**:表示您在屏幕上看到的新术语,重要单词或顺序。 + +Warnings or important notes appear like this.Tips and tricks appear like this. + + + +# 保持联系 + + + +始终欢迎读者的反馈。 + +**一般反馈**:如果您对本书的任何方面都有疑问,在邮件主题中提及书名,并通过`customercare@packtpub.com`向我们发送电子邮件。 + +**勘误表**:尽管我们已尽一切努力确保内容的准确性,但还是会发生错误。 如果您在这本书中发现错误,请向我们报告,我们将不胜感激。 请访问 [www.packt.com/submit-errata](http://www.packt.com/submit-errata) ,选择您的书籍,点击勘误提交表格链接,然后输入详细信息。 + +**盗版**:如果您在 Internet 上以任何形式遇到我们的作品的任何非法副本,请向我们提供位置地址或网站名称,我们将不胜感激。 请通过`copyright@packt.com`与我们联系,并提供材料链接。 + +**如果您有兴趣成为作家**:如果您有一个专业的话题并且对写作或撰写书籍感兴趣,请访问 [authors.packtpub.com](http://authors.packtpub.com/) ]。 + + + + + +# 评论 + + + +请留下评论。 阅读和使用本书后,为什么不在您购买本书的网站上留下评论? 然后,潜在的读者可以查看并使用您的公正意见来做出购买决定,Packt 的我们可以了解您对我们产品的看法,我们的作者可以在书中看到您的反馈。 谢谢! + +有关 Packt 的更多信息,请访问 [packt.com](http://www.packt.com/) 。 + + diff --git a/new/gan-proj/1.md b/new/gan-proj/1.md new file mode 100644 index 0000000000000000000000000000000000000000..ef6a80a34abb467430cc0bb28f7d649014215512 --- /dev/null +++ b/new/gan-proj/1.md @@ -0,0 +1,571 @@ + + +# 生成对抗网络简介 + + + +在本章中,我们将研究**生成对抗网络**( **GAN** )。 它们是一种深度神经网络架构,它使用无监督的机器学习来生成数据。 他们在 2014 年由 Ian Goodfellow,Yoshua Bengio 和 Aaron Courville 的论文中介绍了 , ,可在以下链接中找到: [https:/ /arxiv.org/pdf/1406.2661](https://arxiv.org/pdf/1406.2661) 。 GAN 具有许多应用,包括图像生成和药物开发。 + +本章将向您介绍 GAN 的核心组件。 它将带您了解每个组件的工作方式以及 GAN 背后的重要概念和技术。 它还将简要概述使用 GAN 的优缺点 ,并深入了解某些实际应用。 + +本章将通过探讨以下主题来涵盖所有这些要点: + +* 什么是 GAN? +* GAN 的架构 +* 与 GAN 相关的重要概念 +* GAN 的不同品种 +* GAN 的优缺点 +* GAN 的实际应用 + + + + + +# 什么是 GAN? + + + +GAN 是由两个网络(生成器网络和鉴别器网络)组成的深度神经网络体系结构。 通过生成和辨别的多个周期,两个网络互相训练,同时试图互相取胜。 + + + + + +# 什么是发电机网络? + + + +生成器网络使用现有数据来生成新数据。 例如,它可以使用现有图像生成新图像。 生成器的主要目标是从随机生成的数字矢量(称为**潜在空间**)生成数据(例如图像,视频,音频或文本)。 在创建发电机网络时,我们需要指定网络的目标。 这可能是图像生成,文本生成,音频生成,视频生成等。 + + + + + +# 什么是鉴别网络? + + + +鉴别器网络试图区分真实数据和生成器网络生成的数据。 d 标识符网络尝试将传入的数据放入预定义的类别。 它可以执行多类分类或二进制分类。 通常,在 GAN 中执行二进制分类。 + + + + + +# 通过 GAN 中的对抗游戏进行训练 + + + +在 GAN 中,网络是通过对抗性游戏来训练的:两个网络相互竞争。 例如,假设我们要让 GAN 伪造艺术品 : + +1. 第一个网络,即生成器,从未见过真实的艺术品,但正在尝试创建看起来像真实的艺术品。 +2. 第二个网络是鉴别器,试图识别艺术品是真实的还是伪造的。 +3. 生成器反过来试图通过在多次迭代中创建更逼真的艺术品来欺骗鉴别器,使其认为其假货是真实的交易。 +4. 鉴别器试图通过继续完善其自己的确定伪造品的标准来胜过生成器。 +5. 他们通过在每次迭代中对自己的过程中所做的成功更改提供反馈,从而互相指导。 此过程是 GAN 的培训。 +6. 最终,鉴别器将生成器训练到无法再确定哪个艺术品是真实的和哪个艺术品是伪造的这一点。 + +在此游戏中,同时训练两个网络。 当我们达到区分者无法区分真假艺术品的阶段时,网络便达到了称为**纳什均衡**的状态。 这将在本章稍后讨论。 + + + + + +# GAN 的实际应用 + + + +GAN 具有一些相当有用的实际应用,其中包括: + +* **图像生成** :G 能量网络在经过样本图像训练后,可用于生成逼真的图像。 例如,如果我们要生成新的狗图像,则可以在 d 个 ogs 的 图像的数千个样本上训练 GAN。 训练完成后,生成器网络将能够生成与训练集中的图像不同的新图像。 图像生成用于市场营销,徽标生成,娱乐,社交媒体等。 在下一章中,我们将生成动漫人物的面孔。 +* **文本到图像的合成** : 从文本描述生成图像是 GAN 的一个有趣用例。 由于 GAN 能够根据您编写的某些文本生成新数据,因此在电影行业中可能会有所帮助。 在漫画行业,可以自动生成故事序列。 +* **面部老化** :T 对于娱乐和监视行业都非常有用。 这对于面部验证特别有用,因为这意味着 公司不需要随着 人的变老而更改其安全系统。 age-cGAN 网络可以生成不同年龄的图像,然后可以将其用于训练用于面部验证的鲁棒模型。 +* **图像到图像的转换** :I 图像到图像的转换可用于将白天拍摄的图像转换为夜晚拍摄的图像,并将草图转换为绘画 ,以将图像的样式设置为类似于毕加索或梵高的绘画,将航空图像自动转换为卫星图像 ,并转换为 将马的图像转换为斑马的图像。 这些用例具有突破性,因为它们可以节省我们的时间。 +* **视频合成** : GAN 也可以用于生成视频。 与我们手动创建内容相比,它们可以在更少的时间内生成内容。 它们可以提高电影创作者的工作效率,还可以使想在业余时间制作创意视频的业余爱好者获得支持。 +* **高分辨率图像生成** :I 如果使用低分辨率相机拍摄照片,GAN 可以帮助您生成高分辨率图像而不会丢失任何基本细节。 这在网站上可能很有用。 +* **补齐图像的缺失部分** :如果 您的图像中有些缺失的部分,GAN 可以帮助您恢复这些部分。 + + + + + +# GAN 的详细架构 + + + +GAN 的体系结构具有两个基本元素:生成器网络和鉴别器网络。 每个网络都可以是任何神经网络,例如**人工神经网络**( **ANN** ),**卷积神经网络**( **CNN** ), **递归神经网络** ( **RNN** ),或**长短期记忆**( **LSTM** )。 鉴别器必须具有完全连接的层,最后是分类器。 + +让我们仔细看一下 GAN 架构的组件。 在此示例中,我们将想象正在创建一个虚拟 GAN。 + + + + + +# 发电机的架构 + + + +虚拟 GAN 中的生成器网络是一个简单的前馈神经网络,具有五层:输入层,三个隐藏层和输出层。 让我们仔细看看生成器(虚拟)网络的配置: + +| **层#** | **图层名称** | **配置** | +| 1 | 输入层 | `input_shape=(batch_size, 100)`,`output_shape=(batch_size, 100)` | +| 2 | 致密层 | `neurons=500`,`input_shape=(batch_size, 100)` , `output_shape=(batch_size, 500)` | +| 3 | 致密层 | `neurons=500`,`input_shape=(batch_size, 500)` , `output_shape=(batch_size, 500)` | +| 4 | 致密层 | `neurons=784`,`input_shape=(batch_size, 500)`,`output_shape=(batch_size, 784)` | +| 5 | 重塑层 | `input_shape=(batch_size, 784)`,`output_shape=(batch_size, 28, 28)` | + +上表显示了隐藏层的配置,以及网络中的输入和输出层。 + +下图显示了发电机网络中每一层的张量流以及张量的输入和输出形状: + +![](img/9c07dac5-3b55-444a-b7a5-6119c5d9370b.png) + +The architecture of the generator network. + +让我们讨论一下前馈神经网络在数据正向传播期间如何处理信息: + +* 输入层采用从高斯(正态)分布中采样的 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/5577e65f-d54f-48ad-9f22-950a7abf3c36.png) + +The architecture of the discriminator network + +让我们讨论鉴别器如何在网络训练期间以正向传播方式处理数据: + +1. 最初,它接收形状为 28x28 的输入。 +2. 输入层采用输入张量,该张量是形状为( `batch_sizex28x28` )的张量,然后将其直接传递到第一隐藏层。 +3. 接下来,平坦化层将张量平坦化为 784 维向量,该向量将传递到第一隐藏(密集)层。 第一和第二隐藏层将其修改为 500 维向量。 +4. 最后一层是输出层,又是一个密集层,其中一个单元( 神经元)和乙状结肠为激活功能。 它输出一个值为 0 或 1 的值。值为 0 表示提供的图像是假的,而值为 1 表示提供的图像是真实的。 + + + + + +# 与 GAN 相关的重要概念 + + + +现在我们已经了解了 GAN 的体系结构,下面让我们看一下一些重要概念的简要概述。 我们首先来看 KL 散度。 理解 JS 差异非常重要,这是评估模型质量的重要措施。 然后,我们将研究纳什均衡,这是我们在训练中试图达到的状态。 最后,我们将仔细研究目标函数,了解这些目标函数对于很好地实现 GAN 至关重要。 + + + + + +# Kullback-Leibler 散度 + + + +**Kullback-Leibler 发散**( **KL 发散**),也称为**相对熵**,是一种用于识别两个概率分布之间相似性的方法。 它测量一个概率分布 *p* 与第二个预期概率分布 *q* 的差异。 + +用于计算两个概率分布 *p(x)*和 *q(x)*之间的 KL 散度的公式如下: + +![](img/3ed12abb-fb0d-4205-848a-928127ec92ca.png) + +当 *p(x)*等于 *q(x)*时,KL 散度将为零或最小值。 + +由于 KL 散度的不对称性质,我们不应该使用它来度量两个概率分布之间的距离。 因此,不应将其用作距离度量。 + + + + + +# 詹森-香农分歧 + + + +**Jensen-Shannon** 散度(也称为**信息半径**( **IRaD** )或**总散度与平均值**)[ 是两个概率分布之间相似度的另一种度量。 它基于 KL 散度。 但是,与 KL 散度不同,JS 散度本质上是对称的,可用于测量两个概率分布之间的距离。 如果我们采用 Jensen-Shannon 发散的平方根,则会得到 Jensen-Shannon 距离,因此它是距离度量。 + +以下 等式表示两个概率分布 *p* 和 *q* 之间的 Jensen-Shannon 散度: ] + +![](img/ce0afe99-4137-4673-985a-b073ab66c347.png) + +在前面的等式中,( *p + q* )是中点度量,而![](img/36d35229-c3f4-4edf-a334-65867998c3aa.png)是 Kullback-Leibler 发散。 + +现在我们已经了解了 KL 散度和 Jenson-Shannon 散度,让我们讨论 GAN 的 Nash 平衡。 + + + + + +# 纳什均衡 + + + +纳什均衡描述了博弈论中的特定状态。 在非合作游戏中可以达到这种状态,在这种游戏中,每个玩家都根据自己对其他玩家的期望,尝试选择最佳策略来为自己获得最佳结果。 最终,所有参与者都达到了根据其他参与者做出的决定为自己选择最佳策略的地步。 在游戏的这一点上,他们不会从改变策略中获得任何好处。 这种状态是纳什均衡。 + +关于如何达到纳什均衡的一个著名例子是《囚徒困境》。 在此示例中,两名犯罪分子(A 和 B)因犯罪而被捕。 两者都放置在单独的单元中,彼此之间无法通信。 检察官只有足够的证据将他们定罪为较小的罪行,而没有主要罪行,这将使他们长期入狱。 为了定罪,检察官向他们提出要约: + +* 如果 A 和 B 都牵涉对方主要罪行,则将分别服刑 2 年。 +* 如果 A 暗示 B 但 B 保持沉默,则 A 将被释放,B 将被判入狱 3 年(反之亦然)。 +* 如果 A 和 B 都保持安静,则他们以较少的费用只能服刑 1 年。 + +从这三种情况来看,很明显,A 和 B 的最佳可能结局是保持安静并在监狱服刑 1 年。 但是,保持安静的风险为 3 年,因为 A 和 B 都无法知道对方也将保持安静。 因此,他们将达到一种状态,在这种状态下,他们的实际最佳策略是供认,因为这是提供最高奖励和最低惩罚的选择。 当达到这种状态时,任何罪犯都不会通过改变策略来获得任何好处; 因此,他们将达到纳什均衡。 + + + + + +# 目标功能 + + + +为了创建一个生成器网络,该生成器网络生成与真实图像相似的图像,我们尝试提高生成器生成的数据与实际数据之间的相似性。 为了测量相似度,我们使用目标函数。 这两个网络都有自己的目标功能,并且在培训过程中,它们会尽量减少各自的目标功能。 以下方程式表示 GAN 的最终目标函数:![](img/c682e8fd-bd22-4998-968f-227c26898f62.png) + +在前面的等式中,![](img/a86a4cb0-47d0-4123-8c40-207579ac79c4.png)是鉴别器模型,![](img/692e8b22-1448-49a9-8d2e-6ccfcba6e5b3.png)是生成器模型,![](img/1cf58b02-f255-4082-8b3a-1db3bf0ac832.png)是实际数据分布,![](img/8cdff081-d911-4e9a-80c3-fa21211eb3bb.png)是生成器生成的数据的分布,![](img/39400e6f-0df8-46a1-b80c-17516e3c6adf.png)是 预期的输出。 + +在训练期间, **D** (**鉴别器**)想要使整个输出最大化,而 G(生成器)希望使整个输出最小化,从而训练 GAN 达到平衡 生成器和鉴别器网络。 当它达到平衡时,我们说模型已经收敛。 这个平衡就是纳什平衡。 训练完成后,我们将获得一个生成器模型,该模型能够生成逼真的图像。 + + + + + +# 计分算法 + + + +计算 GAN 的准确性很简单。 GAN 的目标函数不是特定函数,例如均方误差或交叉熵。 GAN 在训练期间学习目标功能。 研究人员提出了许多计分算法来衡量模型的拟合程度。 让我们详细了解一些评分算法。 + + + + + +# 初始分数 + + + +初始分数是 GAN 使用最广泛的评分算法。 它使用预训练的 Inception V3 网络(在 Imagenet 上进行训练)来提取生成图像和真实图像的特征。 由 Shane Barrat 和 Rishi Sharma 在其论文*关于初始分数*的注释( [https://arxiv.org/pdf/1801.01973.pdf](https://arxiv.org/pdf/1801.01973.pdf) )中提出。 初始分数或简称 IS,用于衡量所生成图像的质量和多样性。 让我们看一下 *IS* 的等式: + +![](img/0d33c46a-0a5f-4027-919c-30b910e6d93b.png) + +在前面的等式中,符号 *x* 表示从分布中采样的样本。 ![](img/ba528a08-5d57-47cf-8a7e-eb0974b86cdc.png)和![](img/6dcc68ed-e0e3-4959-9aa6-aa40d12b3ad6.png)代表相同的概念。 ![](img/bd2a23f1-e11e-4c02-81ef-cbd6783ffc5d.png)是条件类别分布,![](img/c5dea8f8-7091-4c69-bfec-3987093fbaaa.png)是边际类别分布。 + +要计算初始分数,请执行以下步骤: + +1. 首先采样由模型生成的 *N* 个图像,表示为![](img/c6b693e8-ae58-4258-bacf-0f7e31655099.png) +2. 然后,使用以下公式构建边际类分布: + +![](img/f60e4201-0e24-4dab-b19c-c69d11994427.png) + +3. 然后,使用以下公式计算 KL 散度和预期的改进: + +![](img/0d33c46a-0a5f-4027-919c-30b910e6d93b.png) + +4. 最后,计算结果的指数以得出初始分数。 + +如果模型的初始得分高,则其质量会很好。 尽管这是一项重要措施,但仍存在某些问题。 例如,即使模型每类生成一张图像,它也显示出很高的准确性,这意味着模型缺乏多样性。 为了解决此问题,提出了其他性能指标。 在下一节中,我们将介绍其中之一。 + + + + + +# Fréchet 起始距离 + + + +为了克服初始分数的各种缺点,Martin Heusel 等人在他们的论文 *GAN 中提出了**Fréchlet 初始距离**( **FID** ), 规模更新规则收敛到局部 Nash 平衡*( [https://arxiv.org/pdf/1706.08500.pdf](https://arxiv.org/pdf/1706.08500.pdf) )。 + +计算 FID 分数的公式如下: + +![](img/c23de9d4-5560-4c9f-a0bd-a3569bbf6f5a.png) + +前面的等式表示实际图像 *x,*与生成的图像 *g* 之间的 FID 分数。 为了计算 FID 分数,我们使用 Inception 网络从 Inception 网络的中间层提取特征图。 然后,我们对多元高斯分布建模,以学习特征图的分布。 此多元高斯分布具有 **![](img/f5267071-9d06-4928-bc77-cfc450d6e3a6.png)** 的均值和![](img/91c575c7-94bc-4803-ad27-cc9d948d77be.png) 的协方差,我们用它们来计算 FID 得分。 FID 分数越低,模型越好,它越有能力生成更高质量的更多图像。 完美的生成模型的 FID 得分为零。 使用 FID 得分而不是 Inception 得分的优势在于它对噪声具有鲁棒性,并且可以轻松地测量图像的多样性。 + +The TensorFlow implementation of FID can be found at the following link: [https://www.tensorflow.org/api_docs/python/tf/contrib/gan/eval/frechet_classifier_distance](https://www.tensorflow.org/api_docs/python/tf/contrib/gan/eval/frechet_classifier_distance) +There are more scoring algorithms available that have been recently proposed by researchers in academia and industry. We won't be covering all of these here. Before reading any further, take a look at another scoring algorithm called the Mode Score, information about which can be found at the following link: [https://arxiv.org/pdf/1612.02136.pdf](https://arxiv.org/pdf/1612.02136.pdf). + + + +# GAN 的变体 + + + +当前有成千上万种不同的 GAN,并且这个数字正以惊人的速度增长。 在本节中,我们将探索六种流行的 GAN 架构,我们将在本书的后续章节中对其进行更详细的介绍。 + + + + + +# 深度卷积生成对抗网络 + + + +Alec Radford,Luke Metz 和 Soumith Chintala 在名为*深度卷积生成对抗网络*的无监督表示学习*中提出了**深层卷积 GAN** ( **DCGAN** )。 ,可通过以下链接获得:* [https://arxiv.org/pdf/1511.06434.pdf](https://arxiv.org/pdf/1511.06434.pdf) 。 香草 GAN 通常在其网络中没有**卷积神经网络**( **CNN** )。 这是在 DCGAN 的引入下首次提出的。 我们将在[第 3 章](../Text/3.html)和*使用条件 GAN* **进行面部老化,以学习如何使用 DCGAN 生成**动漫 ch 动漫面孔。 + + + + + +# StackGAN + + + +StackGAN 由 Han Zhang,Tao Xu,Li Hongsheng Li 等人在其题为*的论文中提出。StackGAN:使用堆叠式生成对抗网络*进行文本到照片般逼真的图像合成,可从以下链接获得: [https://arxiv.org/pdf/1612.03242.pdf](https://arxiv.org/pdf/1612.03242.pdf) 。 他们使用 StackGAN 来探索文本到图像的合成,并获得了令人印象深刻的结果。 StackGAN 是一对网络,当提供文本描述时,它们会生成逼真的图像。 我们将在[第 6 章](../Text/6.html), *StackGAN –文本到真实图像的图像合成中,学习如何使用 StackGAN 从文本描述生成逼真的图像。* 。 + + + + + +# 循环 GAN + + + +CycleGAN 由朱俊彦,Taesung Park,Phillip Isola 和 Alexei A. Efros 在题为*使用循环一致对抗网络*的不成对图像到图像翻译的论文中提出。 在以下链接中: [https://arxiv.org/pdf/1703.10593](https://arxiv.org/pdf/1703.10593) 。 CycleGAN 具有一些非常有趣的潜在用途,例如将照片转换为绘画,反之亦然,将夏天拍摄的照片转换为冬天,反之亦然,或者将马的图像转换为斑马的图像,反之亦然。 我们将在[第 7 章](../Text/7.html), *CycleGAN-将绘画变成照片*中学习如何使用 CycleGAN 将绘画变成照片。 + + + + + +# 3D GAN + + + +3D-GAN 由 Wu Jiajun Wu,Zhengkai Zhang,薛天凡,William T. Freeman 和 Joshua B. Tenenbaum 在其名为*的论文中提出,该论文通过 3D 生成-专家对模型学习概率形状的对象形状*,可通过以下链接获得: [https://arxiv.org/pdf/1610.07584](https://arxiv.org/pdf/1610.07584) 。 在制造业和 3D 建模行业中,生成对象的 3D 模型具有许多用例。 一旦在对象的 3D 模型上进行训练,3D-GAN 网络便能够生成不同对象的新 3D 模型。 我们将在[第 2 章](../Text/2.html)和 *3D-GAN-使用* GAN 生成形状中学习如何使用 3D-GAN 生成对象的 3D 模型。 + + + + + +# 年龄 + + + +有条件 GAN 的面部老化由 Grigory Antipov,Moez Baccouche 和 Jean-Luc Dugelay 在他们的题为*有条件的通用对抗网络*的面部老化的论文中提出,可通过以下链接获得: [https://arxiv.org/pdf/1702.01983.pdf](https://arxiv.org/pdf/1702.01983.pdf) 。 面部老化具有许多行业用例,包括跨年龄的面部识别,寻找迷路的孩子以及娱乐。 我们将在[第 3 章](../Text/3.html), *使用条件 GAN* 进行面部衰老中,学习如何训练条件 GAN 生成给定目标年龄的人脸。 + + + + + +# pix2pix + + + +pix2pix 网络 是由 Phillip Isola , 朱俊彦 , 周廷辉介绍的 和 Alexei A. Efros 在他们的论文*带条件对抗网络*的图像到图像翻译中,可通过以下链接获得: [https://arxiv.org/abs/1611.07004](https://arxiv.org/abs/1611.07004) 。 pix2pix 网络具有与 CycleGAN 网络类似的用例。 它可以将建筑物标签转换为建筑物图片(我们将在 pix2pix 章节中看到一个类似的示例),b 缺失并将白色图像转换为彩色图像,将白天至夜晚的图像,草图转换为照片, 和航拍图像到类似地图的图像。 + +For a list of all the GANs in existence, refer to *The GAN Zoo*, an article by Avinash Hindupur available at [https://github.com/hindupuravinash/the-gan-zoo](https://github.com/hindupuravinash/the-gan-zoo). + + + +# GAN 的优势 + + + +与其他有监督或无监督学习方法相比,GAN 具有某些优势: + +* **GAN 是一种无监督的学习方法** :Ac 查询带标签的数据是一个手动过程,需要花费大量时间。 GAN 不需要标签数据; 当他们学习数据的内部表示时,可以使用未标记的数据来培训他们。 +* **GAN 生成数据**:关于 GAN 的最好的事情之一是,它们生成的数据类似于真实数据。 因此,它们在现实世界中有许多不同的用途。 它们可以生成与真实数据无法区分的图像,文本,音频和视频。 GAN 生成的图像可应用于营销,电子商务,游戏,广告和许多其他行业。 + +* **GAN 学习数据的密度分布**:GAN 学习数据的内部表示。 如前所述,GAN 可以学习混乱而复杂的数据分布。 这可以用于许多机器学习问题。 +* **受过训练的鉴别器是分类器**:经过训练,我们得到了鉴别器和生成器。 鉴别器网络是分类器,可用于分类对象。 + + + + + +# 训练 GAN 的问题 + + + +与任何技术一样,GAN 也有一些问题。 这些问题通常与训练过程有关,包括模式崩溃,内部协变量偏移和梯度消失。 让我们更详细地看看这些。 + + + + + +# 模式崩溃 + + + +模式崩溃是指发电机网络生成变化不大的样本或模型开始生成相同图像的情况。 有时,概率分布实际上是多峰的,非常复杂。 这意味着它可能包含来自不同观察值的数据,并且对于样品的不同子图可能具有多个峰。 有时,GAN 不能为数据的多峰概率分布建模,并且会遭受模式崩溃。 所有生成的样本实际上都相同的 情况称为 c 完全崩溃。 + +我们可以使用许多方法来克服模式崩溃问题。 其中包括: + +* 通过针对不同模式训练多个模型(GAN) + +* 通过使用各种数据样本训练 GAN 来 + + + + + +# 消失的渐变 + + + +在反向传播期间,梯度从最后一层向第一层反向流动。 随着向后流动,它变得越来越小。 有时,梯度是如此之小,以至于初始层学习非常缓慢或完全停止学习。 在这种情况下,梯度完全不会改变初始层的权重值,因此有效地停止了网络中初始层的训练。 这被称为**消失梯度**问题。 + +如果我们使用基于梯度的优化方法训练更大的网络,此问题将变得更加严重。 当我们少量改变参数值时,基于梯度的优化方法通过计算网络输出的变化来优化参数值。 如果参数值的变化导致网络输出的微小变化,则权重变化将非常小,因此网络将停止学习。 + +当我们使用 Sigmoid 和 Tanh 等 培养功能时,这也是一个问题。 乙状结肠激活函数将值限制在 0 到 1 之间,将 *x* 的大值转换为大约 1,将 *x* 的小值或负值转换为大约零。 Tanh 激活函数将输入值压缩为-1 和 1 之间的范围,将大输入值转换为大约 1,将小值转换为大约负 1。当应用反向传播时,我们使用微分链式规则, 有倍增作用。 当我们到达网络的初始层时,梯度(误差)呈指数下降,从而导致梯度消失。 + +为了克服这个问题,我们可以使用激活功能,例如 ReLU,LeakyReLU 和 PReLU。 这些激活函数的梯度在反向传播期间不会饱和,从而导致神经网络的有效训练。 另一种解决方案是使用批处理规范化,该规范化将对网络隐藏层的输入规范化。 + + + + + +# 内部协变量移位 + + + +当我们网络的输入分配发生变化时,就会发生内部协变量偏移。 当输入分布更改时,隐藏层将尝试学习以适应新的分布。 这减慢了训练过程。 如果进程变慢,则需要很长时间才能收敛到全局最小值。 当网络输入的统计分布与以前看到的输入完全不同时,就会出现此问题。 批量归一化和其他归一化技术可以解决此问题。 我们将在以下各节中进行探讨。 + + + + + +# 训练 GAN 时解决稳定性问题 + + + +训练稳定性是与 GAN 相关的最大问题之一。 对于某些数据集,由于此类问题,GAN 从未收敛。 在本节中,我们将研究一些可用于提高 GAN 稳定性的解决方案。 + + + + + +# 特征匹配 + + + +在 GAN 的训练过程中,我们使鉴别器网络的目标函数最大化,并使生成器网络的目标函数最小化。 这个目标函数有一些严重的缺陷。 例如,它没有考虑生成的数据和实际数据的统计信息。 + +特征匹配是 Tim Salimans,Ian Goodfellow 等人在其题为*训练 GAN 的改进技术*的论文中提出的一种技术,旨在通过引入新的目标函数来改善 GAN 的收敛性。 生成器网络的新目标函数鼓励其生成具有统计信息的数据,该数据与真实数据相似。 + +要应用特征映射,网络不会要求鉴别器提供二进制标签。 相反,鉴别器网络提供从鉴别器网络的中间层提取的输入数据的激活或特征图。 从训练的角度来看,我们训练鉴别器网络以学习真实数据的重要统计信息。 因此,目的在于通过学习那些鉴别特征,它应该能够从假数据中区分出真实数据。 + +为了从数学上理解这种方法,让我们首先看一下不同的表示法: + +* ![](img/e726a3d7-269a-440f-97f9-d911339a21ba.png):来自鉴别器网络中间层的真实数据的激活图或特征图 +* ![](img/290e169c-7581-48c6-94b7-8ee69c49b6b0.png):生成器网络从鉴别器网络中的中间层生成的数据的激活/功能映射。 + +这个新的目标函数可以表示为: + +![](img/d1541159-75a5-487e-805f-80a8c97436bd.png) + +使用此目标函数可以获得更好的结果,但是仍然不能保证收敛。 + + + + + +# 小批量判别 + + + +小批量判别是稳定 GAN 训练的另一种方法。 它是由 Ian Goodfellow 等人在 *GAN 训练的改进技术*中提出的,该技术可从 [https://arxiv.org/pdf/1606.03498.pdf](https://arxiv.org/pdf/1606.03498.pdf) 获得。 为了理解这种方法,让我们首先详细研究问题。 在训练 GAN 时,当我们将独立的输入传递给鉴别器网络时,梯度之间的协调可能会丢失,这将阻止鉴别器网络学习如何区分由生成器网络生成的各种图像。 这是模式崩溃,这是我们之前讨论的问题。 为了解决这个问题,我们可以使用小批量判别。 下图很好地说明了此过程: + +![](img/f542b337-7bc3-475e-b3ee-00a963352321.png) + +迷你批判别是一个多步骤的过程。 执行以下步骤,将小批量鉴别添加到您的网络: + +1. 提取样本的特征图,然后将它们与张量![](img/3942e803-5863-4db1-85c6-0eb923a480be.png)相乘,生成矩阵![](img/f87a45a6-3e9e-4ea6-988f-7117b3a91088.png)。 +2. 然后,使用以下公式计算矩阵![](img/0301aefd-7590-4301-8702-5ed8867ccab2.png)的行之间的 L1 距离: + +![](img/4769bf48-3e9e-4976-999d-22513076b07d.png) + +3. 然后,为特定示例![](img/63321408-24e4-4905-88c2-365d44294fae.png)计算所有距离的总和: + +![](img/c236f76b-da88-4ac1-9c4a-50cc4c3acb59.png) + +3. 然后,将![](img/06bd836c-ea3f-4644-80ad-47923503546b.png)与![](img/f59e102b-292b-4043-bf0f-fb2aedc95ba9.png)连接起来,并将其馈送到网络的下一层: + +![](img/d399507e-b812-41a5-8731-9b7e2ebf48aa.png) + +![](img/56ceb926-b75b-490c-91ff-9b736d389cb5.png) + +为了从数学上理解这种方法,让我们仔细看一下各种概念: + +* ![](img/a37c8674-a39f-42a9-9f75-b61cf9a6e50f.png):来自鉴别器网络中间层的![](img/1ad83b3d-2320-40bc-9857-78f2c2099096.png)样本的激活图或特征图 +* ![](img/0814fc40-ef62-4b25-a11f-8c520f7bcbc9.png):三维张量,我们乘以![](img/c20b6dec-de99-467f-8b00-3a836163e900.png) +* ![](img/b1d25f9f-289b-40c8-a602-acf98b8637fe.png):将张量 *T* 和![](img/06a7c0a5-f5d9-43d2-8673-4fe3e1adbdb0.png)相乘时生成的矩阵 +* ![](img/96bdaff0-225d-4918-8f15-8a43dd6b9d7d.png):对于特定示例![](img/8957072f-83ad-4a39-b6c5-0e06647201d7.png),采用所有距离的总和后的输出 + +小批量判别有助于防止模式崩溃,并提高训练稳定性的机会。 + + + + + +# 历史平均 + + + +历史平均是一种获取过去参数平均值并将其添加到生成器和鉴别器网络的各个成本函数的方法。 它是由 Ian Goodfellow 等人在先前提到的*训练 GAN 的改进技术*中提出的。 + +历史平均值可以表示为: + +![](img/04528291-1308-47df-8d56-5b242a809cb1.png) + +在前面的等式中,![](img/f2b9329d-27bc-421d-a8be-8852bea54071.png)是在特定时间 *i* 的参数值。 这种方法也可以提高 GAN 的训练稳定性。 + + + + + +# 单面标签平滑 + + + +之前,分类器的标签/目标值为 0 或 1; 0 代表假图片,1 代表真实图片。 因此,GAN 容易出现对抗性示例,其中是神经网络的输入,导致神经网络的输出不正确。 标签平滑是一种向鉴别器网络提供平滑标签的方法。 这意味着我们可以使用十进制值,例如 0.9(true),0.8(true),0.1(fake)或 0.2(fake),而不是将每个示例都标记为 1(true)或 0(fake)。 我们对真实图像和伪图像的目标值(标签值)进行平滑处理。 标签平滑可以降低 GAN 中对抗性示例的风险。 要应用标签平滑,请为图像分配标签 0.9、0.8 和 0.7、0.1、0.2 和 0.3。 要查找有关标签平滑的更多信息,请参阅以下论文: [https://arxiv.org/pdf/1606.03498.pdf](https://arxiv.org/pdf/1606.03498.pdf) 。 + + + + + +# 批量标准化 + + + +批量归一化是一种对特征向量进行归一化以使其没有均值或单位方差的技术。 它用于稳定学习并处理较差的权重初始化问题。 这是预处理步骤,适用于网络的隐藏层,可帮助我们减少内部协变量偏移。 + +批次归一化由 Ioffe 和 Szegedy 在其 2015 年论文*批次归一化:通过减少内部协变量偏移来加速深度网络训练中引入。* 可以在以下链接中找到: [https://arxiv.org/pdf/1502.03167.pdf](https://arxiv.org/pdf/1502.03167.pdf) 。 + +The benefits of batch normalization are as follows: + +* **减少内部协变量偏移**:批量归一化有助于我们通过归一化值来减少内部协变量偏移。 +* **更快的训练**:如果从正态/高斯分布中采样值 ,则网络将更快地训练。 批处理规范化有助于将值白化到我们网络的内部层。 总体训练速度更快,但是由于涉及额外的计算,因此每次迭代都会变慢。 +* **更高的准确性**:批量标准化提供了更好的准确性。 +* **较高的学习率**:通常,当我们训练神经网络时,我们使用较低的学习率,这需要很长时间才能收敛网络。 通过批量归一化,我们可以使用更高的学习率,从而使我们的网络更快地达到全球最低水平。 +* **减少了辍学的需求**:当我们使用辍学时,我们会破坏网络内部层中的一些基本信息。 批量归一化充当一个正则化器,这意味着我们可以训练网络而无需退出层。 + +在批处理规范化中,我们将规范化应用于所有隐藏层,而不是仅将其应用于输入层。 + + + + + +# 实例规范化 + + + +如上一节所述,批次归一化仅通过利用来自该批次的信息来对一批样本进行归一化。 实例规范化是一种略有不同的方法。 在实例归一化中,我们仅通过利用来自该特征图的信息来归一化每个特征图。 实例规范化由 Dmitry Ulyanov 和 Andrea Vedaldi 在标题为*的论文中介绍了实例规范化:快速风格化*的缺失成分,可通过以下链接获得: [https://arxiv.org/pdf /1607.08022.pdf](https://arxiv.org/pdf/1607.08022.pdf) 。 + + + + + +# 概要 + + + +在本章中,我们了解了 GAN 是什么以及组成标准 GAN 体系结构的组件。 我们还探讨了可用的各种 GAN。 建立 GAN 的基本概念后,我们继续研究 GAN 的构建和功能中涉及的基本概念。 我们了解了 GAN 的优缺点,以及有助于克服这些缺点的解决方案。 最后,我们了解了 GAN 的各种实际应用。 + +利用本章中 GAN 的基本知识,我们现在将进入下一章,在此我们将学习使用 GAN 生成各种形状。 + + diff --git a/new/gan-proj/2.md b/new/gan-proj/2.md new file mode 100644 index 0000000000000000000000000000000000000000..72b38996acaa7dda69e155371644557374033d86 --- /dev/null +++ b/new/gan-proj/2.md @@ -0,0 +1,841 @@ + + +# 3D-GAN-使用 GAN 生成形状 + + + +3D-GAN 是用于 3D 形状生成的 GAN 体系结构。 由于处理 3D 图像涉及复杂性,因此 3D 形状生成通常是一个复杂的问题。 3D-GAN 是一种可以生成逼真的,变化的 3D 形状的解决方案,由吴嘉俊,张成凯,薛天凡等人在题为*的论文中介绍,该文章通过 3D 生成专家模型学习对象形状的概率潜在空间 建模*。 可以在 [http://3dgan.csail.mit.edu/papers/3dgan_nips.pdf](http://3dgan.csail.mit.edu/papers/3dgan_nips.pdf) 上找到该论文。 在本章中,我们将使用 Keras 框架实现 3D-GAN。 + +我们将涵盖以下主题: + +* 3D-GAN 基础知识简介 +* 设置项目 +* 准备数据 +* 3D-GAN 的 Keras 实现 +* 训练 3D-GAN +* 超参数优化 +* 3D-GAN 的实际应用 + + + + + +# 3D-GAN 简介 + + + +**3D 生成对抗网络**( **3D-GAN** )是 GAN 的变体,就像 StackGAN,CycleGAN 和 **S** **上层分辨率一样 生成对抗网络**( **SRGAN** )。 与香草 GAN 相似,它具有生成器和鉴别器模型。 这两个网络都使用 3D 卷积层,而不是使用 2D 卷积。 如果提供足够的数据,它可以学习生成具有良好视觉质量的 3D 形状。 + +在仔细查看 3D-GAN 网络之前,让我们了解 3D 卷积。 + + + + + +# 3D 卷积 + + + +简而言之,3D 卷积操作沿 *x* , *y* 和 *z* 这三个方向对输入数据应用 3D 滤波器。 此操作将创建 3D 特征图的堆叠列表。 输出的形状类似于立方体或长方体的形状。 下图说明了 3D 卷积操作。 左立方体的突出显示部分是输入数据。 内核位于中间,形状为(3,3,3)。 右侧的块是卷积运算的输出: + +![](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 4, 4 x 4 x 4, 4 x 4 x 4, 4 x 4 x 4, 4 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/adfe13fc-6e5c-4ffb-8230-ba4117391c77.png) + +下图显示了区分器网络中张量的流动以及每个层的张量的输入和输出形状。 这将使您对网络有更好的了解: + +![](img/fd209aa6-8f96-45df-a754-7ae8cb364fa1.png) + +A fully convolutional network is a network without fully connected dense layers at the end of the network. Instead, it just consists of convolutional layers and can be end-to-end trained, like a convolutional network with fully connected layers. There are no pooling layers in a generator network. + + + +# 鉴别器网络的架构 + + + +鉴别器网络包含五个具有以下配置的体积卷积层: + +* **3D 卷积层**:5 +* **通道**:64、128、256、512、1 +* **内核大小**:4、4、4、4、4、4 +* **大步前进**:2、2、2、2、1 +* **激活**:泄漏 ReLU,泄漏 ReLU,泄漏 ReLU,泄漏 ReLU,乙状结肠 +* **批量标准化**:是,是,是,是,无 +* **汇聚层**:否,否,否,否,否 +* **线性层**:否,否,否,否,否 + +网络的输入和输出如下: + +* **输入**:形状为(64,64,64)的 3D 图像 +* **输出**:输入数据属于真实或假类的概率 + +下图显示了鉴别器网络中每一层的张量流以及张量的输入和输出形状。 这将使您对鉴别器网络有更好的了解: + +![](img/7fc08303-569e-499f-bc91-e2ae9684f947.png) + +鉴别器网络主要镜像生成器网络。 一个重要的区别是它使用中的 LeakyReLU 代替了 ReLU 作为激活功能。 而且,网络末端的 S 形层用于二进制分类,并预测所提供的图像是真实的还是伪造的。 最后一层没有规范化层,但是其他层使用批处理规范化输入。 + + + + + +# 目标功能 + + + +目标函数是训练 3D-GAN 的主要方法。 它提供损耗值,这些损耗值用于计算梯度,然后更新权重值。 3D-GAN 的对抗损失函数如下: + +![](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. 对指定的时期数重复这些步骤。 + +我们将在后面的部分中详细探讨这些步骤。 让我们继续建立一个项目。 + + + + + +# 建立一个项目 + + + +该项目的 urce 代码可在 GitHub 上的以下链接中找到: [https://github.com/PacktPublishing/Generative-Adversarial-Networks-Projects](https://github.com/PacktPublishing/Generative-Adversarial-Networks-Projects) 。 + +运行以下命令来设置项目: + +1. 首先导航到父目录,如下所示: + +```py +cd Generative-Adversarial-Networks-Projects +``` + +2. 接下来,将目录从当前目录更改为`Chapter02`目录: + +```py +cd Chapter02 +``` + +3. 接下来,为该项目创建一个 Python 虚拟环境: + +```py +virtualenv venv +``` + +4. 之后,激活虚拟环境: + +```py +source venv/bin/activate +``` + +5. 最后,安装`requirements.txt`文件中指示的所有要求: + +```py +pip install -r requirements.txt +``` + +我们现在已经成功建立了该项目。 有关更多信息,请参见代码存储库中包含的 `README.md` 文件。 + + + + + +# 准备数据 + + + +在本章中,我们将使用 3D ShapeNets 数据集,该数据集可从 [http://3dshapenets.cs.princeton.edu/3DShapeNetsCode.zip](http://3dshapenets.cs.princeton.edu/3DShapeNetsCode.zip) 获得。 它由 Wu 和 Song 等人发行。 并包含 40 个对象类别的正确注释的 3D 形状。 我们将使用目录中可用的体积数据,我们将在本章稍后详细讨论。 在接下来的几节中,我们将下载,提取和浏览数据集。 + +The 3D ShapeNets dataset is for academic use only. If you intend to use the dataset for commercial purposes, request permission from the authors of the paper, who can be reached at the following email address: `shurans@cs.princeton.edu`. + + + +# 下载并提取数据集 + + + +运行以下命令以下载并提取数据集: + +1. 首先使用以下链接下载`3DShapeNets`: + +```py +wget http://3dshapenets.cs.princeton.edu/3DShapeNetsCode.zip +``` + +2. 下载文件后,运行以下命令将文件提取到适当的目录中: + +```py +unzip 3DShapeNetsCode.zip +``` + +现在,我们已经成功下载并提取了数据集。 它包含`.mat`(MATLAB)格式的图像。 每隔一张图像是 3D 图像。 在接下来的几节中,我们将学习体素,即 3D 空间中的点。 + + + + + +# 探索数据集 + + + +要了解数据集,我们需要可视化 3D 图像。 在接下来的几节中,我们将首先更详细地了解什么是体素。 然后,我们将加载并可视化 3D 图像。 + + + + + +# 什么是体素? + + + +**体像素**或体素是三维空间中的一个点。 体素在 *x* , *y* 和 *z* 方向上定义了具有三个坐标的位置。 体素是表示 3D 图像的基本单位。 它们主要用于 CAT 扫描,X 射线和 MRI 中,以创建人体和其他 3D 对象的准确 3D 模型。 要处理 3D 图像,了解体素非常重要,因为这些是 3D 图像的组成。 包含下图,以使您了解 3D 图像中的体素是什么样的: + +![](img/4a160433-e15b-440d-8a3f-91163db58421.png) + +A series of voxels in a 3D image. The shaded region is a single voxel. + +前面的图像是体素的堆叠表示。 灰色长方体代表一个体素。 现在,您了解了什么是体素,让我们在下一部分中加载和可视化 3D 图像。 + + + + + +# 加载和可视化 3D 图像 + + + +3D ShapeNets 数据集包含 `.mat` [ 文件格式。 我们将这些`.mat`文件转换为 NumPy ndarrays。 我们还将可视化 3D 图像,以直观了解数据集。 + +执行以下代码以从`.mat`文件加载 3D 图像: + +1. 使用`scipy`中的`loadmat()`功能检索`voxels`。 代码如下: + +```py +import scipy.io as io +voxels = io.loadmat("path to .mat file")['instance'] +``` + +2. 加载的 3D 图像的形状为`30x30x30`。 我们的网络需要形状为`64x64x64`的图像。 我们将使用 NumPy 的 `pad()`方法将 3D 图像的尺寸增加到`32x32x32`: + +```py +import numpy as np +voxels = np.pad(voxels, (1, 1), 'constant', constant_values=(0, 0)) +``` + +`pad()`方法采用四个参数,它们是实际体素的 ndarray,需要填充到每个轴边缘的值的数量,模式值(`constant`)和`constant_values` 被填充。 + +3. 然后,使用`scipy.ndimage`模块中的`zoom()`功能将 3D 图像转换为尺寸为`64x64x64`的 3D 图像。 + +```py +import scipy.ndimage as nd +voxels = nd.zoom(voxels, (2, 2, 2), mode='constant', order=0) +``` + +我们的网络要求图像的形状为`64x64x64`,这就是为什么我们将 3D 图像转换为这种形状的原因。 + + + + + +# 可视化 3D 图像 + + + +让我们使用 matplotlib 可视化 3D 图像,如以下代码所示: + +1. 首先创建一个 matplotlib 图并向其中添加一个子图: + +```py +fig = plt.figure() +ax = fig.gca(projection='3d') +ax.set_aspect('equal') +``` + +2. 接下来,将`voxels`添加到绘图中: + +```py +ax.voxels(voxels, edgecolor="red") +``` + +3. 接下来,显示该图并将其另存为图像,以便稍后我们可以对其进行可视化和理解: + +```py +plt.show() plt.savefig(file_path) +``` + +第一个屏幕截图表示 3D 飞机上的飞机: + +![](img/981169b9-a3b7-435a-8188-2b6f42b10d30.png) + +第二张屏幕截图表示 3D 平面中的表格: + +![](img/25c92f62-a0a7-4c64-b768-577ac7dfe05b.png) + +第三个屏幕截图表示 3D 平面中的椅子: + +![](img/51177648-2247-4346-b270-5139a5dcab46.png) + +我们已经成功下载,提取和浏览了数据集。 我们还研究了如何使用体素。 在下一节中,我们将在 Keras 框架中实现 3D-GAN。 + + + + + +# 3D-GAN 的 Keras 实现 + + + +在本节中,我们将在 Keras 框架中实现生成器网络和鉴别器网络。 我们需要创建两个 Keras 模型。 这两个网络都有各自独立的权重值。 让我们从发电机网络开始。 + + + + + +# 发电机网络 + + + +为了实现发电机网络,我们需要创建 Keras 模型并添加神经网络层。 实施发电机网络所需的步骤如下: + +1. 首先为不同的超参数指定值: + +```py +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 +``` + +2. 接下来,创建一个输入层,以允许网络进行输入。 生成器网络的输入是从概率潜在空间中采样的向量: + +```py +input_layer = Input(shape=gen_input_shape) +``` + +3. 然后,添加第一个 3D 转置卷积(或 3D 解卷积)块,如以下代码所示: + +```py +# 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) +``` + +4. 接下来,再添加四个 3D 转置卷积(或 3D 解卷积)块,如下所示: + +```py +# 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) +``` + +5. 然后,创建 Keras 模型并指定发电机网络的输入和输出: + +```py +model = Model(inputs=input_layer, outputs=a) +``` + +6. 将生成器网络的整个代码包装在一个名为`build_generator()`的函数内: + +```py +def build_generator(): + """ + Create a Generator Model with hyperparameters values defined as follows :return: Generator network + """ z_size = 200 + gen_filters = [512, 256, 128, 64, 1] + gen_kernel_sizes = [4, 4, 4, 4, 4] + gen_strides = [1, 2, 2, 2, 2] + gen_input_shape = (1, 1, 1, z_size) + gen_activations = ['relu', 'relu', 'relu', 'relu', 'sigmoid'] + gen_convolutional_blocks = 5 input_layer = Input(shape=gen_input_shape) + + # First 3D transpose convolution(or 3D deconvolution) block + a = Deconv3D(filters=gen_filters[0], + kernel_size=gen_kernel_sizes[0], + strides=gen_strides[0])(input_layer) + a = BatchNormalization()(a, training=True) + a = Activation(activation='relu')(a) + + # Next 4 3D transpose convolution(or 3D deconvolution) blocks + for i in range(gen_convolutional_blocks - 1): + a = Deconv3D(filters=gen_filters[i + 1], + kernel_size=gen_kernel_sizes[i + 1], + strides=gen_strides[i + 1], padding='same')(a) + a = BatchNormalization()(a, training=True) + a = Activation(activation=gen_activations[i + 1])(a) + + gen_model = Model(inputs=input_layer, outputs=a) + + gen_model.summary() + return gen_model +``` + +我们已经成功地为发电机网络创建了 Keras 模型。 接下来,为鉴别器网络创建 Keras 模型。 + + + + + +# 鉴别网络 + + + +同样,要实现区分器网络,我们需要创建 Keras 模型并向其中添加神经网络层。 实施鉴别器网络所需的步骤如下: + +1. 首先为不同的超参数指定值: + +```py +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 +``` + +2. 接下来,创建一个输入层,以允许网络进行输入。 鉴别器网络的输入是形状为 `64x64x64x1`的 3D 图像: + +```py +dis_input_layer = Input(shape=dis_input_shape) +``` + +3. 然后,添加第一个 3D 卷积块,如下所示: + +```py +# 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) +``` + +4. 之后,再添加四个 3D 卷积块,如下所示: + +```py +# 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) +``` + +5. 接下来,创建一个 Keras 模型,并为鉴别器网络指定输入和输出: + +```py +dis_model = Model(inputs=dis_input_layer, outputs=a) +``` + +6. 将鉴别器网络的完整代码包装在一个函数中,如下所示: + +```py +def build_discriminator(): + """ + Create a Discriminator Model using hyperparameters values defined as follows :return: Discriminator network + """ dis_input_shape = (64, 64, 64, 1) + dis_filters = [64, 128, 256, 512, 1] + dis_kernel_sizes = [4, 4, 4, 4, 4] + dis_strides = [2, 2, 2, 2, 1] + dis_paddings = ['same', 'same', 'same', 'same', 'valid'] + dis_alphas = [0.2, 0.2, 0.2, 0.2, 0.2] + dis_activations = ['leaky_relu', 'leaky_relu', 'leaky_relu', + 'leaky_relu', 'sigmoid'] + dis_convolutional_blocks = 5 dis_input_layer = Input(shape=dis_input_shape) + + # The first 3D Convolutional block + a = Conv3D(filters=dis_filters[0], + kernel_size=dis_kernel_sizes[0], + strides=dis_strides[0], + padding=dis_paddings[0])(dis_input_layer) + a = BatchNormalization()(a, training=True) + a = LeakyReLU(dis_alphas[0])(a) + + # Next 4 3D Convolutional Blocks + for i in range(dis_convolutional_blocks - 1): + a = Conv3D(filters=dis_filters[i + 1], + kernel_size=dis_kernel_sizes[i + 1], + strides=dis_strides[i + 1], + padding=dis_paddings[i + 1])(a) + a = BatchNormalization()(a, training=True) + if dis_activations[i + 1] == 'leaky_relu': + a = LeakyReLU(dis_alphas[i + 1])(a) + elif dis_activations[i + 1] == 'sigmoid': + a = Activation(activation='sigmoid')(a) + + dis_model = Model(inputs=dis_input_layer, outputs=a) + print(dis_model.summary()) + return dis_model +``` + +在本节中,我们为鉴别器网络创建了 Keras 模型。 我们现在准备训练 3D-GAN。 + + + + + +# 训练 3D-GAN + + + +训练 3D-GAN 类似于训练香草 GAN。 我们首先在生成的图像和真实图像上训练鉴别器网络,但是冻结生成器网络。 然后,我们训练生成器网络,但冻结区分器网络。 我们对指定的时期数重复此过程。 在一次迭代中,我们按顺序训练两个网络。 训练 3D-GAN 是一个端到端的训练过程。 让我们一步一步地进行这些步骤。 + + + + + +# 训练网络 + + + +要训​​练 3D-GAN,请执行以下步骤: + +1. 首先指定训练所需的不同超参数的值,如下所示: + +```py +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' +``` + +2. 接下来,创建并编译两个网络,如下所示: + +```py +# 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`优化器的超参数值。 + +3. 然后,创建并编译对抗模型: + +```py +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)) +``` + +4. 之后,提取并加载所有`airplane`图像以进行训练: + +```py +def getVoxelsFromMat(path, cube_len=64): + voxels = io.loadmat(path)['instance'] voxels = np.pad(voxels, (1, 1), 'constant', constant_values=(0, 0)) if cube_len != 32 and cube_len == 64: + voxels = nd.zoom(voxels, (2, 2, 2), mode='constant', order=0) return voxels + def get3ImagesForACategory(obj='airplane', train=True, cube_len=64, obj_ratio=1.0): + obj_path = DIR_PATH + obj + '/30/' + obj_path += 'train/' if train else 'test/' + fileList = [f for f in os.listdir(obj_path) if f.endswith('.mat')] + fileList = fileList[0:int(obj_ratio * len(fileList))] + volumeBatch = np.asarray([getVoxelsFromMat(obj_path + f, cube_len) for f in fileList], dtype=np.bool) + return volumeBatch + +volumes = get3ImagesForACategory(obj='airplane', train=True, obj_ratio=1.0) +volumes = volumes[..., np.newaxis].astype(np.float) +``` + +5. 接下来,添加`TensorBoard`回调并添加`generator`和`discriminator`网络: + +```py +tensorboard = TensorBoard(log_dir="{}/{}".format(log_dir, time.time())) +tensorboard.set_model(generator) +tensorboard.set_model(discriminator) +``` + +6. 添加一个循环,该循环将运行指定的时期数: + +```py +for epoch in range(epochs): + print("Epoch:", epoch) + + # Create two lists to store losses + gen_losses = [] + dis_losses = [] +``` + +7. 在第一个循环中添加另一个循环以运行指定的批次数量: + +```py + 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) +``` + +8. 接下来,从一组真实图像中采样一批图像,并从高斯正态分布中采样一批噪声矢量。 噪声矢量的形状应为`(1, 1, 1, 200)`: + +```py +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, + :, :, :] +``` + +9. 使用生成器网络生成伪造图像。 向其传递`z_sample`的一批噪声矢量,并生成一批假图像: + +```py +gen_volumes = generator.predict(z_sample,verbose=3) +``` + +10. 接下来,在由生成器生成的伪图像和一组真实图像中的一批真实图像上训练鉴别器网络。 另外,使鉴别器易于训练: + +```py +# 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) +``` + +前面的代码训练了鉴别器网络并计算了总的鉴别器损耗。 + +11. 训练同时包含`generator`和`discriminator`网络的对抗模型: + +```py +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))) + +``` + +另外,将损失添加到其各自的列表中,如下所示: + +```py + gen_losses.append(g_loss) + dis_losses.append(d_loss) +``` + +12. 每隔一个纪元生成并保存 3D 图像: + +```py + 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)) +``` + +13. 在每个时期之后,将平均损失保存到`tensorboard`: + +```py +# Save losses to Tensorboard write_log(tensorboard, 'g_loss', np.mean(gen_losses), epoch) +write_log(tensorboard, 'd_loss', np.mean(dis_losses), epoch) +``` + +My advice is to train it for 100 epochs to find the problems in the code. Once you have solved those problems, you can then train the network over the course of 100,000 epochs. + + + +# 保存模型 + + + +训练完成后,通过添加以下代码来保存生成器和鉴别器模型的学习权重: + +```py +""" Save models """ generator.save_weights(os.path.join(generated_volumes_dir, "generator_weights.h5")) +discriminator.save_weights(os.path.join(generated_volumes_dir, "discriminator_weights.h5")) +``` + + + + + +# 测试模型 + + + +要测试网络,请创建`generator`和`discriminator`网络。 然后,加载学习的权重。 最后,使用`predict()`方法生成预测: + +```py +# 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`,,服务器。 + +```py +tensorboard --logdir=logs +``` + +现在,在浏览器中打开 `localhost:6006` 。 TensorBoard 的 标量 部分包含两种损失的图表: + +![](img/b67a8b95-a1fe-4c06-be44-cec497dd35bf.png) + +Loss plot for the generator network + +![](img/7937c970-9e91-4f47-bc05-2b230ae1b0c6.png) + +Loss plot for the discriminator network + +这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止培训,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 尝试使用超参数,然后选择一组您认为可能会提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。 + + + + + +# 可视化图形 + + + +TensorBoard 的 GRAPHS 部分包含两个网络的图形 。 如果网络性能不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的运算: + +![](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 和其他较小的学习率进行实验。 +* **发生器和鉴别器网络**的不同层中的激活功能:使用 S 型,tanh,ReLU,LeakyReLU,ELU,SeLU 和其他激活功能进行实验。 +* **优化算法**:尝试使用 Adam,SGD,Adadelta,RMSProp 和 Keras 框架中可用的其他优化器。 +* **损失函数**:二进制交叉熵是最适合 3D-GAN 的损失函数。 +* **两个网络中的层数**:根据可用的训练数据量,在网络中尝试不同的层数。 如果您有足够的可用数据来进行训练,则可以使网络更深。 + +我们还可以通过使用 Hyperopt( [https://github.com/hyperopt/hyperopt](https://github.com/hyperopt/hyperopt) )或 Hyperas( [https:// github .com / maxpumperla / hyperas](https://github.com/maxpumperla/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 **)执行面部老化。** + + diff --git a/new/gan-proj/3.md b/new/gan-proj/3.md new file mode 100644 index 0000000000000000000000000000000000000000..34d4284d7b8744b99524b478ebe69c1d08793755 --- /dev/null +++ b/new/gan-proj/3.md @@ -0,0 +1,1402 @@ + + +# 使用条件 GAN 进行面部老化 + + + +**条件 GAN** ( **cGAN** )是 GAN 模型的扩展。 它们允许生成具有特定条件或属性的图像,因此被证明比香草 GAN 更好。 在本章中,我们将实现一个 cGAN,该 cGAN 一旦经过培训,就可以执行自动面部老化。 Grigory Antipov,Moez Baccouche 和 Jean-Luc Dugelay 在其名为 *有条件生成对抗网络* 的人脸衰老的论文中,首先介绍了我们将要实现的 cGAN 网络。 可以在以下链接中找到: [https://arxiv.org/pdf/1702.01983.pdf](https://arxiv.org/pdf/1702.01983.pdf) 。 + +在本章中,我们将介绍以下主题: + +* 介绍用于面部衰老的 cGAN +* 设置项目 +* 准备数据 +* cGAN 的 Keras 实现 +* 训练 cGAN +* 评估和超参数调整 +* 面部老化的实际应用 + + + + + +# 介绍用于面部衰老的 cGAN + + + +到目前为止,我们已经针对不同的用例实现了不同的 GAN 网络。 条件 GAN 扩展了普通 GAN 的概念,并允许我们控制发电机网络的输出。 面部衰老就是在不更改身份的情况下更改一个人的面部年龄。 在大多数其他模型(包括 GAN)中,由于不考虑面部表情和面部配件(例如太阳镜或胡须),因此会使人的外观或身份损失 50%。 Age-cGAN 会考虑所有这些属性。 在本节中,我们将探索用于面部衰老的 cGAN。 + + + + + +# 了解 cGAN + + + +cGAN 是 GAN 的一种,它取决于一些额外的信息。 我们将额外的 *y* 信息作为额外的输入层提供给生成器。 在香草 GAN 中,无法控制所生成图像的类别。 当我们向生成器添加条件 *y* 时,我们可以使用 *y* 生成特定类别的图像,该图像可以是任何类型的数据,例如类标签或整数数据 。 Vanilla GAN 只能学习一个类别,而为多个类别构造 GAN 则非常困难。 但是,可以使用 cGAN 生成针对不同类别具有不同条件的多模式模型。 + +下图显示了 cGAN 的体系结构: + +![](img/7b4b89cd-9b42-4609-84e2-94086b34ac3a.png) + +cGAN 的训练目标函数可以表示为: + +![](img/115b5d5f-6623-46dc-bed1-e99a6aa5910c.png) + +此处, *G* 是生成器网络, *D* 是鉴别器网络。 鉴别器 的损失为![](img/78a30b0f-14dc-4d20-ac13-5f3a30bc78d7.png), 发生器的损失为![](img/e81d7c4b-bd30-4887-9e84-303e9cea0a89.png)。 我们可以说 *G(z | y)*在给定 * z * 和 *和*。 在此, *z* 是从正态分布得出的尺寸为 100 的先验噪声分布。 + + + + + +# Age-cGAN 的架构 + + + +用于面部老化的 cGAN 的架构稍微复杂一些。 Age- cGan 由四个网络组成:编码器,FaceNet,生成器网络和鉴别器网络。 使用编码器,我们可以利用潜在的向量![](img/577645cb-a80b-4a10-98e8-5cf21f336b77.png)来学习输入面部图像和年龄条件的逆映射。 FaceNet 是面部识别网络,用于学习输入图像 *x* 与重构的图像![](img/a77a78e0-3ea2-4ce3-8f96-deca552c9b9f.png)之间的差异。 我们有一个生成器网络,该网络使用由人脸图像和条件向量组成的隐藏表示并生成图像。 鉴别器网络将区分真实图像和伪图像。 + +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 的更多信息,请访问链接 [https://arxiv.org/pdf/1602.07261.pdf](https://arxiv.org/pdf/1602.07261.pdf) 。 一旦提供了图像,经过预训练的 Inception-ResNet-2 网络将返回相应的嵌入。 可以通过计算嵌入的欧几里德距离来计算针对真实图像和重建图像的提取的嵌入。 关于年龄识别网络的更多信息将在 Age-cGAN 部分的 *Keras 实现中进行介绍。* + + + + + +# 时代的阶段 + + + +Age-cGAN 具有多个培训阶段。 如上一节所述,Age-cGAN 具有四个网络,并经过三个阶段的培训。 Age-cGAN 的训练包括三个阶段: + +1. **有条件的 GAN 训练:**在此阶段,我们训练生成器网络和鉴别器网络。 +2. **初始潜在矢量逼近:**在此阶段,我们训练编码器网络。 +3. **潜矢量优化:**在此阶段,我们同时优化编码器和发电机网络。 + +以下屏幕截图显示了 Age-cGAN 的各个阶段: + +![](img/3019472f-d08b-4fa2-9ea1-071be37bc6bd.png) + +Stages in the Age-cGANSource: Face Aging with Conditional Generative Adversarial Networks, https://arxiv.org/pdf/1702.01983.pdf + +我们将在以下部分介绍 Age- cGAN 的所有阶段。 + + + + + +# Conditional GAN training + + + +在这个阶段,我们训练生成器网络和鉴别器网络。 经过训练后,生成器网络可以生成面部的模糊图像。 此阶段类似于训练香草 GAN,其中我们同时训练两个网络。 + + + + + +# 训练目标功能 + + + +用于训练 cGAN 的训练目标函数可以表示为: + +![](img/0e7b1187-382e-4584-9c8b-b39c1db1fd91.png) + +训练 cGAN 网络涉及优化, 功能, ![](img/e08d1823-e871-40dc-b01f-d924c155a813.png)。 训练 cGAN 可以看作是 minimax 游戏,其中同时对生成器和鉴别器进行训练。 在上式中, ![](img/7ce6d943-ee01-4777-b805-c300627ec0ec.png)代表发电机网络的参数,![](img/2e9c2935-f734-48ee-afae-ee24ccfa0b0a.png) 代表 *G* 和 *D* 的 参数 , ![](img/6923fbb8-a6ef-4c14-ab83-c8586f272630.png)是鉴别器模型的损失,![](img/869b6fae-e95a-4138-9df9-033a8e157e49.png)是 对于生成器模型的损失, ![](img/17a59550-253c-4e61-b15e-637119f829ca.png)是分布 所有可能的图像。 + + + + + +# 初始潜在​​矢量近似 + + + +**初始潜在矢量近似** 是一种近似潜在矢量以优化人脸图像重建的方法。 为了近似一个潜在向量,我们有一个编码器网络。 我们在生成的图像和真实图像上训练编码器网络。 训练后,编码器网络将开始从学习到的分布中生成潜在矢量。 用于训练编码器网络的训练目标函数是欧几里得距离损失。 + + + + + +# 潜在向量优化 + + + +在潜在矢量优化过程中,我们同时优化了编码器网络和发电机网络。 我们用于潜在向量优化的方程式如下: + +![](img/98bda784-ced5-4608-8c1d-38477d40cb8f.png) + +*FR* 是面部识别网络。 该方程式表明,真实图像和重建图像之间的欧几里得距离应最小。 在此阶段,我们尝试最小化距离以最大程度地保留身份。 + + + + + +# 设置项目 + + + +如果尚未使用所有章节的完整代码克隆存储库,请立即克隆存储库。 克隆的存储库有一个名为`Chapter03`的目录,其中包含本章的全部代码。 执行以下命令来设置项目: + +1. 首先,导航到父目录,如下所示: + +```py +cd Generative-Adversarial-Networks-Projects +``` + +2. 现在,将目录从当前目录更改为 `Chapter03`: + +```py +cd Chapter03 +``` + +3. 接下来,为该项目创建一个 Python 虚拟环境: + +```py +virtualenv venv +virtualenv venv -p python3 # Create a virtual environment using python3 interpreter +virtualenv venv -p python2 # Create a virtual environment using python2 interpreter +``` + +我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。 + +4. 接下来,激活新创建的虚拟环境: + +```py +source venv/bin/activate +``` + +激活虚拟环境后,所有其他命令将在此虚拟环境中执行。 + +5. 接下来,通过执行以下命令,安装`requirements.txt` 文件中提供的所有库: + +```py +pip install -r requirements.txt +``` + +您可以参考 `README.md` 文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。 + +在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一部分中,我们将处理数据集。 + + + + + +# 准备数据 + + + +在本章中,我们将使用`Wiki-Cropped`数据集,其中包含 64 张以上 328 张不同人脸的图像。 作者还提供了数据集 ,该数据集仅包含已裁剪的面部,因此我们无需裁剪面部。 + +The authors of the paper titled *Deep expectation of real and apparent age from a single image without facial landmarks,* which is available at[https://www.vision.ee.ethz.ch/en/publications/papers/articles/eth_biwi_01299.pdf](https://www.vision.ee.ethz.ch/en/publications/papers/articles/eth_biwi_01299.pdf), have scraped these images from Wikipedia and made them available for academic purposes. If you intend to use the dataset for commercial purposes, contact the authors at `rrothe@vision.ee.ethz.ch`. + +您可以从以下链接手动下载数据集,并将所有压缩文件放置在 Age-cGAN 项目内的目录中,网址为 [https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki /](https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/) + +执行以下步骤以下载和提取数据集。 + + + + + +# 下载数据集 + + + +要下载仅包含裁剪的面的数据集,请执行以下命令: + +```py +# 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 +``` + + + + + +# 提取数据集 + + + +下载数据集后,手动将文件提取到数据文件夹中,或执行以下命令来提取文件: + +```py +# 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`文件: + +```py +def load_data(wiki_dir, dataset='wiki'): + # Load the wiki.mat file + meta = loadmat(os.path.join(wiki_dir, "{}.mat".format(dataset))) + + # Load the list of all files + full_path = meta[dataset][0, 0]["full_path"][0] + + # List of Matlab serial date numbers + dob = meta[dataset][0, 0]["dob"][0] + + # List of years when photo was taken + photo_taken = meta[dataset][0, 0]["photo_taken"][0] # year # Calculate age for all dobs age = [calculate_age(photo_taken[i], dob[i]) for i in range(len(dob))] + + # Create a list of tuples containing a pair of an image path and age + images = [] + age_list = [] + for index, image_path in enumerate(full_path): + images.append(image_path[0]) + age_list.append(age[index]) + + # Return a list of all images and respective age + return images, age_list +``` + +`photo_taken`变量是年份列表,`dob`是 Matlab 列表中相应照片的序列号。 我们可以根据序列号和照片拍摄的年份来计算该人的年龄。 使用以下代码来计算年龄: + +```py +def calculate_age(taken, dob): + birth = datetime.fromordinal(max(int(dob) - 366, 1)) + + # assume the photo was taken in the middle of the year + if birth.month < 7: + return taken - birth.year + else: + return taken - birth.year - 1 +``` + +现在,我们已经成功下载并提取了数据集。 在下一节中,让我们研究 Age-cGAN 的 Keras 实现。 + + + + + +# Age-cGAN 的 Keras 实现 + + + +像普通 GAN 一样,cGAN 的实现非常简单。 Keras 提供了足够的灵活性来编码复杂的生成对抗网络。 在本节中,我们将实现 cGAN 中使用的生成器网络,鉴别器网络和编码器网络。 让我们从实现编码器网络开始。 + +在开始编写实现之前,创建一个名为 `main.py` 的 Python 文件,并导入基本模块,如下所示: + +```py +import math +import os +import time +from datetime import datetime + +import matplotlib.pyplot as plt +import numpy as np +import tensorflow as tf +from keras import Input, Model +from keras.applications import InceptionResNetV2 +from keras.callbacks import TensorBoard +from keras.layers import Conv2D, Flatten, Dense, BatchNormalization, Reshape, concatenate, LeakyReLU, Lambda, \ + K, Conv2DTranspose, Activation, UpSampling2D, Dropout +from keras.optimizers import Adam +from keras.utils import to_categorical +from keras_preprocessing import image +from scipy.io import loadmat +``` + + + + + +# 编码器网络 + + + +编码器网络是**卷积神经网络**( **CNN** ),可将图像( *x* )编码为潜矢量( *z* )或 潜在的矢量表示。 让我们从在 Keras 框架中实现编码器网络开始。 + +执行以下步骤来实现编码器网络: + +1. 首先创建一个输入层: + +```py +input_layer = Input(shape=(64, 64, 3)) +``` + +2. 接下来,添加第一个卷积块,其中包含具有激活功能的 2D 卷积层,具有以下配置: + * **过滤器**:`32` + * **内核大小**:`5` + * **大步前进**:`2` + * **填充**:``same`` + * **激活**:`LeakyReLU`,其中`alpha`等于`0.2`: + +```py +# 1st Convolutional Block enc = Conv2D(filters=32, kernel_size=5, strides=2, padding='same')(input_layer) enc = LeakyReLU(alpha=0.2)(enc) +``` + +3. 接下来,再添加三个卷积块,其中每个都包含一个 2D 卷积层,然后是一个批处理归一化层和一个激活函数,具有以下配置: + * **过滤器**:`64`,`128`,`256` + * **内核大小**:`5`,`5`,`5` + * **跨越**:`2`,`2`,`2` + * **填充**:`same`,`same`,``same`` + * **批量归一化**:每个卷积层后面都有一个批量归一化层 + * **激活**:`LealyReLU`,`LeakyReLU`,`LeakyReLU`,其中`alpha`等于`0.2`: + +```py +# 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) +``` + +4. 接下来,将最后一个卷积块的输出展平,如下所示: + +```py +# Flatten layer enc = Flatten()(enc) +``` + +Converting an *n*-dimensional tensor to a one-dimensional tensor (array) is called **flattening**. + +5. 接下来,添加具有以下配置的密集(完全连接)层,批处理规范化层和激活功能: + * **单位(节点)**:2,096 + * **批量标准化**:是 + * **激活**:`LeakyReLU`,其中`alpha`等于`0.2`: + +```py +# 1st Fully Connected Layer enc = Dense(4096)(enc) +enc = BatchNormalization()(enc) +enc = LeakyReLU(alpha=0.2)(enc) +``` + +6. 接下来,使用以下配置添加第二个密集(完全连接)层: + * **单位(节点)**:`100` + * **激活**:无: + +```py +# Second Fully Connected Layer enc = Dense(100)(enc) +``` + +7. 最后,创建 Keras 模型并指定编码器网络的输入和输出: + +```py +# Create a model model = Model(inputs=[input_layer], outputs=[enc]) +``` + +编码器网络的完整代码如下所示: + +```py +def build_encoder(): + """ + Encoder Network :return: Encoder model + """ input_layer = Input(shape=(64, 64, 3)) + + # 1st Convolutional Block + enc = Conv2D(filters=32, kernel_size=5, strides=2, padding='same')(input_layer) enc = LeakyReLU(alpha=0.2)(enc) + + # 2nd Convolutional Block + enc = Conv2D(filters=64, kernel_size=5, strides=2, padding='same')(enc) + enc = BatchNormalization()(enc) + enc = LeakyReLU(alpha=0.2)(enc) + + # 3rd Convolutional Block + enc = Conv2D(filters=128, kernel_size=5, strides=2, padding='same')(enc) + enc = BatchNormalization()(enc) + enc = LeakyReLU(alpha=0.2)(enc) + + # 4th Convolutional Block + enc = Conv2D(filters=256, kernel_size=5, strides=2, padding='same')(enc) + enc = BatchNormalization()(enc) + enc = LeakyReLU(alpha=0.2)(enc) + + # Flatten layer + enc = Flatten()(enc) + + # 1st Fully Connected Layer + enc = Dense(4096)(enc) + enc = BatchNormalization()(enc) + enc = LeakyReLU(alpha=0.2)(enc) + + # Second Fully Connected Layer + enc = Dense(100)(enc) + + # Create a model + model = Model(inputs=[input_layer], outputs=[enc]) + return model +``` + +我们现在已经成功地为编码器网络创建了 Keras 模型。 接下来,为发电机网络创建 Keras 模型。 + + + + + +# 发电机网络 + + + +生成器网络是一个 CNN,它采用 100 维矢量 *z* ,并生成尺寸为(64、64、3)的图像。 让我们在 Keras 框架中实现生成器网络。 + +执行以下步骤以实现生成器网络: + +1. 首先创建生成器网络的两个输入层: + +```py +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, )) +``` + +2. 接下来,沿着通道维度连接输入,如下所示: + +```py +x = concatenate([input_z_noise, input_label]) +``` + +上一步将生成级联张量。 + +3. 接下来,添加具有以下配置的密集(完全连接)块: + * **单位(节点)**:`2,048` + * **输入尺寸**:106 + * **激活**:`LeakyReLU`的`alpha`等于``0.2`` + * **退出**:`0.2`: + +```py +x = Dense(2048, input_dim=latent_dims+num_classes)(x) +x = LeakyReLU(alpha=0.2)(x) +x = Dropout(0.2)(x) +``` + +4. 接下来,使用以下配置添加第二个密集(完全连接)块: + * **单位(节点)**:16,384 + * **批量标准化**:是 + * **激活**:`alpha`等于`0.2`的`LeakyReLU` + * **退出**:`0.2`: + +```py +x = Dense(256 * 8 * 8)(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) +x = Dropout(0.2)(x) +``` + +5. 接下来,将最后一个密集层的输出重塑为尺寸为`(8, 8, 256)`的三维张量: + +```py +x = Reshape((8, 8, 256))(x) +``` + +该层将生成张量为(`batch_size`,`8, 8, 256`)的张量。 + +6. 接下来,添加一个升采样模块,该模块包含一个升采样层,其后是一个具有以下配置的 2-D 卷积层和一个批归一化层: + * **上采样大小**:`(2, 2)` + * **过滤器**:`128` + * **内核大小**:`5` + * **填充**:`same` + * **批量规范化**:是,`momentum`等于`0.8` + * **激活**:`LeakyReLU`,其中`alpha`等于`0.2`: + +```py +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 times *x* and repeating the columns a specified number of times *y*, respectively. + +7. 接下来,添加另一个 Upsampling 块(类似于上一层),如以下代码所示。 除了卷积层中使用的过滤器数为`128`之外,该配置与上一个块类似: + +```py +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) +``` + +8. 接下来,添加最后一个升采样块。 该配置与上一层相似,除了在卷积层中使用了三个过滤器并且不使用批处理归一化的事实: + +```py +x = UpSampling2D(size=(2, 2))(x) +x = Conv2D(filters=3, kernel_size=5, padding='same')(x) +x = Activation('tanh')(x) +``` + +9. 最后,创建 Keras 模型并指定发电机网络的输入和输出: + +```py +model = Model(inputs=[input_z_noise, input_label], outputs=[x]) +``` + +发电机网络的完整代码如下所示: + +```py +def build_generator(): + """ + Create a Generator Model with hyperparameters values defined as follows :return: Generator model + """ latent_dims = 100 + num_classes = 6 input_z_noise = Input(shape=(latent_dims,)) + input_label = Input(shape=(num_classes,)) + + x = concatenate([input_z_noise, input_label]) + + x = Dense(2048, input_dim=latent_dims + num_classes)(x) + x = LeakyReLU(alpha=0.2)(x) + x = Dropout(0.2)(x) + + x = Dense(256 * 8 * 8)(x) + x = BatchNormalization()(x) + x = LeakyReLU(alpha=0.2)(x) + x = Dropout(0.2)(x) + + x = Reshape((8, 8, 256))(x) + + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(filters=128, kernel_size=5, padding='same')(x) + x = BatchNormalization(momentum=0.8)(x) + x = LeakyReLU(alpha=0.2)(x) + + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(filters=64, kernel_size=5, padding='same')(x) + x = BatchNormalization(momentum=0.8)(x) + x = LeakyReLU(alpha=0.2)(x) + + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(filters=3, kernel_size=5, padding='same')(x) + x = Activation('tanh')(x) + + model = Model(inputs=[input_z_noise, input_label], outputs=[x]) + return model +``` + +现在,我们已经成功创建了发电机网络。 接下来,我们将为鉴别器网络编写代码。 + + + + + +# 鉴别网络 + + + +鉴别器网络是 CNN。 让我们在 Keras 框架中实现鉴别器网络。 + +执行以下步骤以实现鉴别器网络: + +1. 首先创建两个输入层,因为我们的鉴别器网络将处理两个输入: + +```py +# 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) +``` + +2. 接下来,添加具有以下配置的二维卷积块(Conv2D +激活功能): + * **过滤器** = `64` + * **内核大小**:`3` + * **大步前进**:`2` + * **填充**:`same` + * **激活**:`LeakyReLU`,其中`alpha`等于`0.2`: + +```py +x = Conv2D(64, kernel_size=3, strides=2, padding='same')(image_input) +x = LeakyReLU(alpha=0.2)(x) +``` + +3. 接下来,展开`label_input`使其具有`(32, 32, 6)`的形状: + +```py +label_input1 = Lambda(expand_label_input)(label_input) +``` + +`expand_label_input`功能如下: + +```py +# 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)的张量。 + +4. 接下来,沿着通道维度将变换后的标签张量和最后一个卷积层的输出串联起来,如下所示: + +```py +x = concatenate([x, label_input1], axis=3) +``` + +5. 添加具有以下配置的卷积块(2D 卷积层+批处理归一化+激活函数): + * **过滤器**:`128` + * **内核大小**:`3` + * **大步前进**:`2` + * **填充**:`same` + * **批量标准化**:是 + * **激活**:`LeakyReLU`,其中`alpha`等于`0.2`: + +```py +x = Conv2D(128, kernel_size=3, strides=2, padding='same')(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) +``` + +6. 接下来,再添加两个卷积块,如下所示: + +```py +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) +``` + +7. 接下来,添加一个扁平层: + +```py +x = Flatten()(x) +``` + +8. 接下来,添加一个输出概率的密集层(分类层): + +```py +x = Dense(1, activation='sigmoid')(x) +``` + +9. 最后,创建 Keras 模型并指定鉴别器网络的输入和输出: + +```py +model = Model(inputs=[image_input, label_input], outputs=[x]) +``` + +鉴别器网络的整个代码如下: + +```py +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. 首先指定培训所需的参数: + +```py +# 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) +``` + +2. 接下来,为训练定义优化器。 我们将使用 Keras 中提供的`Adam`优化器。 初始化优化器,如以下代码所示: + +```py +# Define optimizers +# Optimizer for the discriminator network dis_optimizer = Adam(lr=0.0002, beta_1=0.5, beta_2=0.999, epsilon=10e-8) + +# Optimizer for the generator network +gen_optimizer = Adam(lr=0.0002, beta_1=0.5, beta_2=0.999, epsilon=10e-8) + +# Optimizer for the adversarial network +adversarial_optimizer = Adam(lr=0.0002, beta_1=0.5, beta_2=0.999, epsilon=10e-8) +``` + +对于所有优化程序,请使用等于`0.0002`的学习速率,等于`0.5`的`beta_1`值,等于`0.999`的`beta_2`值以及等于`10e-8`的 epsilon 值。 + +3. 接下来,加载并编译生成器和鉴别器网络。 在 Keras 中,我们必须在训练网络之前编译网络: + +```py +# 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`作为损失函数。 + +4. 接下来,构建并编译对抗模型,如下所示: + +```py +# 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`作为优化器。 + +5. 接下来,存储损失的 dd `TensorBoard` 如下: + +```py +tensorboard = TensorBoard(log_dir="logs/{}".format(time.time())) +tensorboard.set_model(generator) +tensorboard.set_model(discriminator) +``` + +6. 接下来,使用在*准备数据*部分中定义的`load_data`函数加载所有图像: + +```py +images, age_list = load_data(wiki_dir=wiki_dir, dataset="wiki") +``` + +7. 接下来,将年龄数值转换为年龄类别,如下所示: + +```py +# Convert age to category +age_cat = age_to_category(age_list) +``` + +`age_to_category`函数的定义如下: + +```py +# This method will convert age to respective category +def age_to_category(age_list): + age_list1 = [] + + for age in age_list: + if 0 < age <= 18: + age_category = 0 + elif 18 < age <= 29: + age_category = 1 + elif 29 < age <= 39: + age_category = 2 + elif 39 < age <= 49: + age_category = 3 + elif 49 < age <= 59: + age_category = 4 + elif age >= 60: + age_category = 5 age_list1.append(age_category) + return age_list1 +``` + +`age_cat`的输出应如下所示: + +```py +[1, 2, 4, 2, 3, 4, 2, 5, 5, 1, 3, 2, 1, 1, 2, 1, 2, 2, 1, 5, 4 , .............] +``` + +将年龄类别转换为一键编码的向量: + +```py +# 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* 的值应如下所示: + +```py +[[0\. 1\. 0\. 0\. 0\. 0.] + [0\. 0\. 1\. 0\. 0\. 0.] + [0\. 0\. 0\. 0\. 1\. 0.] + ... + [0\. 0\. 0\. 1\. 0\. 0.] + [0\. 1\. 0\. 0\. 0\. 0.] + [0\. 0\. 0\. 0\. 1\. 0.]] +``` + +*y* 的形状应为(`total_values`,`5`)。 + +8. 接下来,加载所有图像并创建一个包含所有图像的`ndarray`: + +```py +# Read all images and create an ndarray +loaded_images = load_images(wiki_dir, images, (image_shape[0], image_shape[1])) +``` + +`load_images`函数的定义如下: + +```py +def load_images(data_dir, image_paths, image_shape): + images = None for i, image_path in enumerate(image_paths): + print() + try: + # Load image + loaded_image = image.load_img(os.path.join(data_dir, image_path), target_size=image_shape) + + # Convert PIL image to numpy ndarray + loaded_image = image.img_to_array(loaded_image) + + # Add another dimension (Add batch dimension) + loaded_image = np.expand_dims(loaded_image, axis=0) + + # Concatenate all images into one tensor + if images is None: + images = loaded_image + else: + images = np.concatenate([images, loaded_image], axis=0) + except Exception as e: + print("Error:", i, e) + + return images +``` + +`loaded_images`中的值应如下所示: + +```py +[[[[ 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... +``` + +9. 接下来,创建一个`for`循环,该循环应运行的次数由时期数指定,如下所示: + +```py +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) +``` + +10. 接下来,在纪元循环内创建另一个循环,并使它运行 `num_batches` 指定的次数,如下所示: + +```py + for index in range(number_of_batches): + print("Batch:{}".format(index + 1)) +``` + +我们用于区分网络和对抗网络训练的整个代码将在此循环内。 + +11. 接下来,对真实数据集中的一批图像和一批一次性编码的年龄向量进行采样: + +```py + images_batch = loaded_images[index * batch_size:(index + 1) * batch_size] + images_batch = images_batch / 127.5 - 1.0 + images_batch = images_batch.astype(np.float32) + + y_batch = y[index * batch_size:(index + 1) * batch_size] +``` + +`image_batch`的形状应为[`batch_size`和`64, 64, 3`),`y_batch`的形状应为(`batch_size`和`6`)。 + +12. 接下来,从高斯分布中采样一批噪声矢量,如下所示: + +```py + z_noise = np.random.normal(0, 1, size=(batch_size, z_shape)) +``` + +13. 接下来,使用生成器网络生成伪造图像。 请记住,我们尚未训练发电机网络: + +```py + initial_recon_images = generator.predict_on_batch([z_noise, y_batch]) +``` + +生成器网络有两个输入`z_noise`和`y_batch`,它们是我们在步骤 11 和 12 中创建的。 + +14. 现在,在真实图像和伪图像上训练鉴别器网络: + +```py + 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) +``` + +此代码应在一批图像上训练鉴别器网络。 在每个步骤中,将对一批样本进行鉴别。 + +15. 接下来,训练对抗网络。 通过冻结鉴别器网络,我们将仅训练生成器网络: + +```py + # Again sample a batch of noise vectors from a Gaussian(normal) distribution + z_noise2 = np.random.normal(0, 1, size=(batch_size, z_shape)) + + # Samples a batch of random age values random_labels = np.random.randint(0, 6, batch_size).reshape(-1, 1) + + # Convert the random age values to one-hot encoders + random_labels = to_categorical(random_labels, 6) + + # Train the generator network + g_loss = adversarial_model.train_on_batch([z_noise2, sampled_labels], [1] * batch_size) +``` + +前面的代码将在一批输入上训练发电机网络。 对抗模型的输入是`z_noise2`和`random_labels`。 + +16. 接下来,计算并打印损失: + +```py + d_loss = 0.5 * np.add(d_loss_real, d_loss_fake) + print("d_loss:{}".format(d_loss)) + print("g_loss:{}".format(g_loss)) + + # Add losses to their respective lists + gen_losses.append(g_loss) + dis_losses.append(d_loss) +``` + +17. 接下来,将损失写入 TensorBoard 以进行可视化: + +```py + write_log(tensorboard, 'g_loss', np.mean(gen_losses), epoch) + write_log(tensorboard, 'd_loss', np.mean(dis_losses), epoch) +``` + +18. 每隔 10 个周期取样并保存图像,如下所示: + +```py + if epoch % 10 == 0: + images_batch = loaded_images[0:batch_size] + images_batch = images_batch / 127.5 - 1.0 + images_batch = images_batch.astype(np.float32) + + y_batch = y[0:batch_size] + z_noise = np.random.normal(0, 1, size=(batch_size, z_shape)) + + gen_images = generator.predict_on_batch([z_noise, y_batch]) + + for i, img in enumerate(gen_images[:5]): + save_rgb_img(img, path="results/img_{}_{}.png".format(epoch, i)) +``` + +将前面的代码块放入 epochs 循环中。 每隔 10 个时间段,它将生成一批伪图像并将其保存到结果目录。 这里, `save_rgb_img()` 是效用函数,定义如下: + +```py +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() +``` + +19. 最后,通过添加以下行来保存两个模型: + +```py +# 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. 首先建立编码器网络。 添加以下代码以构建和编译网络: + +```py +# Build Encoder encoder = build_encoder() +encoder.compile(loss=euclidean_distance_loss, optimizer='adam') +``` + +我们尚未定义`euclidean_distance_loss`。 在构建编码器网络之前,让我们对其进行定义并添加以下内容: + +```py +def euclidean_distance_loss(y_true, y_pred): + """ + Euclidean distance loss """ return K.sqrt(K.sum(K.square(y_pred - y_true), axis=-1)) +``` + +2. 接下来,加载生成器网络,如下所示: + +```py +generator.load_weights("generator.h5") +``` + +在这里,我们正在加载上一步的权重,在该步骤中,我们成功地训练并保存了发电机网络的权重。 + +3. 接下来,对一批潜在向量进行采样,如下所示: + +```py +z_i = np.random.normal(0, 1, size=(1000, z_shape)) +``` + +4. 接下来,对一批随机年龄数字进行采样,并将随机年龄数字转换为单热编码矢量,如下所示: + +```py +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 个值。 + +5. 接下来,添加一个纪元循环和一个批处理步骤循环,如下所示: + +```py +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) +``` + +6. 现在,从 1,000 个样本中采样一批潜伏向量和一批单热编码向量,如下所示: + +```py + z_batch = z_i[index * batch_size:(index + 1) * batch_size] + y_batch = y[index * batch_size:(index + 1) * batch_size] +``` + +7. 接下来,使用预先训练的生成器网络生成伪造图像: + +```py + generated_images = generator.predict_on_batch([z_batch, y_batch]) +``` + +8. 最后,通过生成器网络在生成的图像上训练编码器网络: + +```py + encoder_loss = encoder.train_on_batch(generated_images, z_batch) +``` + +9. 接下来,在每个时期之后,将编码器损耗写入 TensorBoard,如下所示: + +```py + write_log(tensorboard, "encoder_loss", np.mean(encoder_losses), epoch) +``` + +10. 我们需要保存训练有素的编码器网络。 通过添加以下代码来保存编码器模型: + +```py +encoder.save_weights("encoder.h5") +``` + +如果您已成功执行了本节中给出的代码,则将成功地训练编码器模型。 现在,我们的编码器网络已准备好生成初始潜矢量。 在下一节中,我们将学习如何执行优化的潜在矢量近似。 + + + + + +# 潜在向量优化 + + + +在前面的两个步骤中,我们成功地训练了生成器网络,鉴别器网络和编码器网络。 在本节中,我们将改进编码器和发电机网络。 在这些步骤中,我们将使用**人脸识别**( **FR** )网络,该网络会生成输入给它的特定输入的 128 维嵌入,以改善生成器和编码器 网络。 + +执行以下步骤: + +1. 首先构建并加载编码器网络和生成器网络的权重: + +```py +encoder = build_encoder() +encoder.load_weights("encoder.h5") + +# Load the generator network generator.load_weights("generator.h5") +``` + +2. 接下来,创建一个网络以将图像从`(64, 64, 3)`形状调整为`(192, 192, 3)`形状,如下所示: + +```py +# 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 +``` + +```py +image_resizer = build_image_resizer() +image_resizer.compile(loss=loss, optimizer='adam') +``` + +要使用 FaceNet,我们图像的高度和宽度应大于 150 像素。 前面的网络将帮助我们将图像转换为所需的格式。 + +3. 建立人脸识别模型: + +```py +# Face recognition model fr_model = build_fr_model(input_shape=fr_image_shape) +fr_model.compile(loss=loss, optimizer="adam") +``` + +请参阅 [https://github.com/PacktPublishing/Generative-Adversarial-Networks-Projects/Age-cGAN/main.py](https://github.com/PacktPublishing/Generative-Adversarial-Networks-Projects/Age-cGAN/main.py) 以获取`build_fr_model()`功能。 + +4. 接下来,创建另一个对抗模型。 在此对抗模型中,我们将具有三个网络:编码器,生成器和面部识别模型: + +```py +# 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) +``` + +5. 在第一个循环中添加一个`epoch`循环和一个批处理步骤循环,如下所示: + +```py +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) +``` + +6. 接下来,从真实图像列表中采样一批图像: + +```py + # Sample and normalize + images_batch = loaded_images[index * batch_size:(index + 1) * batch_size] + images_batch = images_batch / 255.0 + images_batch = images_batch.astype(np.float32) + + # Sample a batch of age one-hot encoder vectors + y_batch = y[index * batch_size:(index + 1) * batch_size] +``` + +7. 接下来,使用 FR 网络为真实图像生成嵌入: + +```py + images_batch_resized = image_resizer.predict_on_batch(images_batch) real_embeddings = fr_model.predict_on_batch(images_batch_resized) +``` + +8. 最后,训练对抗模型,这将训练编码器模型和生成器模型: + +```py + reconstruction_loss = fr_adversarial_model.train_on_batch([images_batch, y_batch], real_embeddings) +``` + +9. 另外,将重建损失写入 TensorBoard 以进行进一步可视化: + +```py + # Write the reconstruction loss to Tensorboard write_log(tensorboard, "reconstruction_loss", reconstruction_loss, index) +``` + +10. 保存两个网络的权重: + +```py +# Save improved weights for both of the networks generator.save_weights("generator_optimized.h5") +encoder.save_weights("encoder_optimized.h5") +``` + +恭喜你! 我们现在已经成功地训练了 Age-cGAN 进行面部衰老。 + + + + + +# 可视化损失 + + + +要可视化培训损失,请启动 Tensorboard 服务器,如下所示: + +```py +tensorboard --logdir=logs +``` + +现在,在浏览器中打开 `localhost:6006` 。 TensorBoard 的 标量 部分包含两种损失的图表,如以下屏幕截图所示: + +![](img/e705b732-209a-407e-bbb4-5ddae7424011.png) + +这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止培训,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。 + + + + + +# 可视化图形 + + + +TensorBoard 的 GRAPHS 部分包含两个网络的图形。 如果网络性能不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作: + +![](img/63d2ac6d-4141-47c4-8807-19efe0bb901e.png) + +Flow of tensors and different operations inside the graphs + + + +# 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。 + + diff --git a/new/gan-proj/4.md b/new/gan-proj/4.md new file mode 100644 index 0000000000000000000000000000000000000000..0cf3e0c7a50346030c2e3f662fa087bf2b4ea563 --- /dev/null +++ b/new/gan-proj/4.md @@ -0,0 +1,1000 @@ + + +# 使用 DCGAN 生成动漫角色 + + + +众所周知,卷积层确实擅长处理图像。 他们能够学习重要的特征,例如边缘,形状和复杂的对象,有效的, ,例如神经网络,例如 Inception,AlexNet, **视觉几何组** ( **VGG** )和 ResNet。 Ian Goodfellow 等人在其名为*生成对抗性网络*的论文中提出了具有密集层的**生成对抗网络**( **GAN** ),该网络可在 以下链接: [https://arxiv.org/pdf/1406.2661.pdf](https://arxiv.org/pdf/1406.2661.pdf) 。 复杂的神经网络,例如**卷积神经网络**( **CNN** ),**递归神经网络**( **RNN** )和**长短 长期记忆**( **LSTM** )最初并未在 GAN 中进行测试。 **深度卷积生成对抗网络**( **DCGAN** )的发展是使用 CNN 进行图像生成的重要一步。 DCGAN 使用卷积层而不是密集层。 它们是由研究人员 Alec Radford , Luke Metz , Soumith Chintala 等提出的,我在其论文中,*通过深度卷积生成对抗性技术* *网络*的无监督表示学习,可以在以下链接中找到:[https:// arxiv.org/pdf/1511.06434.pdf](https://arxiv.org/pdf/1511.06434.pdf) 。 从那时起,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/7201db6b-98f4-473e-8ba3-615cd8d132f1.png) + +Source: arXiv:1511.06434 [cs.LG] + +上图包含了生成器网络体系结构中的不同层,并显示了它如何生成分辨率为 64 x 64 x 3 的图像。 + +DCGAN 的发电机网络包含 10 层。 它执行跨步卷积以增加张量的空间分辨率。 在 Keras 中,上采样和卷积层的组合等于跨步卷积层。 基本上,生成器网络会从均匀分布中获取采样的噪声矢量,并不断对其进行转换,直到生成最终图像为止。 换句话说,它采取形状的张量(`batch_size, 100`),并输出形状的张量(`batch_size, 64, 64, 3`)。 + +让我们看一下发电机网络中的不同层: + +| **层#** | **图层名称** | **配置** | +| 1 | 输入层 | `input_shape=(batch_size, 100)`,`output_shape=(batch_size, 100)` | +| 2 | 致密层 | `neurons=2048`,`input_shape=(batch_size, 100)`,`output_shape=(batch_size, 2048)`,`activation='relu'` | +| 3. | 致密层 | `neurons=16384`,`input_shape=(batch_size, 100)`,`output_shape=(batch_size, 2048)`,`batch_normalization=Yes`,`activation='relu'` | +| 4. | 重塑层 | `input_shape=(batch_size=16384)`,`outp` `ut_shape=(batch_size, 8, 8, 256)` | +| 5. | 上采样层 | `size=(2, 2)`,`input_shape=(batch_size, 8, 8, 256)`,`output_shape=(batch_size, 16, 16, 256)` | +| 6. | 2D 卷积层 | `filters=128`,`kernel_size=(5, 5)`,`strides=(1, 1)`,`padding='same'`,`input_shape=(batch_size, 16, 16, 256)`,`output_shape=(batch_size, 16, 16, 128)`, `activation='relu'` | +| 7. | 上采样层 | `size=(2, 2)`,`input_shape=(batch_size, 16, 16, 128)`,`output_shape=(batch_size, 32, 32, 128)` | +| 8. | 2D 卷积层 | `filters=64`,`kernel_size=(5, 5)`,`strides=(1, 1)`,`padding='same'`,`activation=ReLU`,`input_shape=(batch_size, 32, 32, 128)`,`output_shape=(batch_size, 32, 32, 64)`,`activation='relu'` | +| 9. | 上采样层 | `size=(2, 2)`,`input_shape=(batch_size, 32, 32, 64)`,`output_shape=(batch_size, 64, 64, 64)` | +| 10. | 2D 卷积层 | `filters=3`,`kernel_size=(5, 5)`,`strides=(1, 1)`,`padding='same'`,`activation=ReLU`,`input_shape=(batch_size, 64, 64, 64)`,`output_shape=(batch_size, 64, 64, 3)`,`activation='tanh'` | + +L 等人研究了张量如何从第一层流到最后一层。 下图显示了不同层的输入和输出形状: + +![](img/6cb9082b-a52f-48e7-8193-caf3c11e93ea.png) + +This configuration is valid for Keras APIs with the TensorFlow backend and the `channels_last` format. + + + +# 配置鉴别器网络 + + + +在继续之前,让我们看一下鉴别器网络的架构: + +![](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=128`,`kernel_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. | MaxPooling2D | `pool_size=(2, 2)`,`input_shape=(batch_size, 64, 64, 128)`,`output_shape=(batch_size, 32, 32, 128)` | +| 4. | 2D 卷积层 | `filters=256`,`kernel_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. | MaxPooling2D | `pool_size=(2, 2)`,`input_shape=(batch_size, 30, 30, 256)`,`output_shape=(batch_size, 15, 15, 256)` | +| 6. | 2D 卷积层 | `filters=512`,`kernel_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. | MaxPooling2D | `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=1024`,`input_shape=(batch_size, 18432)`,`output_shape=(batch_size, 1024)`,`activation='leakyrelu'`,`'leakyrelu_alpha'=0.2` | +| 10. | 致密层 | `neurons=1`,`input_shape=(batch_size, 1024)`,`output_shape=(batch_size, 1)`,`activation='sigmoid'` | + +L 等人研究了张量如何从第一层流到最后一层。 下图显示了不同层的输入和输出形状: + +![](img/9a5468ed-810e-429c-8a0a-af8eb1d673b0.png) + +These configurations are valid for Keras APIs with the TensorFlow backend and the `channels_last` format. + + + +# 设置项目 + + + +我们已经克隆/下载了所有章节的完整代码。 下载的代码包含一个名为`Chapter04`的目录,该目录包含本章的全部代码。 执行以下命令来设置项目: + +1. 首先,导航到父目录,如下所示: + +```py +cd Generative-Adversarial-Networks-Projects +``` + +2. 现在,将目录从当前目录更改为`Chapter04`: + +```py +cd Chapter04 +``` + +3. 接下来,为该项目创建一个 Python 虚拟环境: + +```py +virtualenv venv +virtualenv venv -p python3 # Create a virtual environment using + python3 interpreter +virtualenv venv -p python2 # Create a virtual environment using + python2 interpreter +``` + +我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。 + +4. 接下来,激活虚拟环境: + +```py +source venv/bin/activate +``` + +激活虚拟环境后,所有其他命令将在此虚拟环境中执行。 + +5. 接下来,通过执行以下命令来安装`requirements.txt`文件中给出的所有要求: + +```py +pip install -r requirements.txt +``` + +您可以参考 `README.md` 文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。 + +在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一部分中,我们将使用数据集,包括下载和清理数据集。 + + + + + +# 下载并准备动漫角色数据集 + + + +要训练 DCGAN 网络,我们需要一个动漫人物数据集,其中包含人物的裁剪面孔。 收集数据集有多种方法。 我们可以使用公开可用的数据集,也可以抓取一个,只要不违反网站的抓取政策即可。 在本章中,我们将仅出于教育和演示目的刮取图像。 我们使用名为 `gallery-dl` 的搜寻器工具从 `pixiv.net` 抓取了图像。 这是一个命令行工具,可用于从网站下载图像集,例如 [pixiv.net](http://pixiv.net) , [exhentai.org](http://exhentai.org) , [danbooru.donmai.us](http://danbooru.donmai.us) , 和更多。 它可通过以下链接获得: [https://github.com/mikf/gallery-dl](https://github.com/mikf/gallery-dl) 。 + + + + + +# 下载数据集 + + + +在本节中,我们将介绍安装依赖项和下载数据集所需的不同步骤。 在执行以下命令之前,激活为此项目创建的虚拟环境: + +1. 执行以下命令安装`gallery-dl`: + +```py +pip install --upgrade gallery-dl +``` + +2. 或者,您可以使用以下命令安装`gallery-dl`的最新开发版本: + +```py +pip install --upgrade https://github.com/mikf/gallery-dl/archive/master.zip +``` + +3. 如果上述命令不起作用,请按照官方存储库中的说明进行操作: + +```py +# Official gallery-dl Github repo +https://github.com/mikf/gallery-dl +``` + +4. 最后,执行以下命令以使用`gallery-dl.`从`danbooru.donmai.us`下载图像: + +```py +gallery-dl https://danbooru.donmai.us/posts?tags=face +``` + +Download images at your own risk. The information given is for educational purposes only and we don't support illegal scraping. We don't have copyright of the images, as the images are hosted by their respective owners. For commercial purposes, please contact the respective owner of the website or the content that you are using. + + + +# 探索数据集 + + + +在裁剪或调整图像大小之前,请查看下载的图像: + +![](img/503cd7f0-4105-49a5-9095-d400768e7eb9.png) + +如您所见,有些图像还包含其他身体部位,我们在训练图像中不需要这些部位。 在下一部分中,我们将仅从这些图像中裁剪出脸部。 此外,我们会将所有图像调整为训练所需的尺寸。 + + + + + +# 裁剪数据集中的图像并调整其大小 + + + +在本节中,我们将从图像中裁剪出面孔。 我们将使用 **python-animeface** 从图像中裁剪出面孔。 这是一个开源 GitHub 存储库,可从命令行的图像中自动裁剪面部。 它可以通过以下链接公开获得: [https://github.com/nya3jp/python-animeface](https://github.com/nya3jp/python-animeface) 。 + +执行以下步骤来裁剪图像并调整其大小: + +1. 首先,下载`python-animeface`: + +```py +pip install animeface +``` + +2. 接下来,导入任务所需的模块: + +```py +import glob +import os + +import animeface +from PIL import Image +``` + +3. 接下来,定义参数: + +```py +total_num_faces = 0 +``` + +3. 接下来,遍历所有图像以进行裁剪并一一调整其大小: + +```py +for index, filename in + enumerate(glob.glob('/path/to/directory/containing/images/*.*')): +``` + +4. 在循环内,打开当前图像并检测其中的人脸: + +```py + try: + # Open image + im = Image.open(filename) + + # Detect faces + faces = animeface.detect(im) + except Exception as e: + print("Exception:{}".format(e)) + continue +``` + +5. 接下来,获取图像中检测到的脸部坐标: + +```py + fp = faces[0].face.pos + + # Get coordinates of the face detected in the image + coordinates = (fp.x, fp.y, fp.x+fp.width, fp.y+fp.height) +``` + +6. 现在,将脸部裁剪出图像: + +```py + # Crop image cropped_image = im.crop(coordinates) +``` + +7. 接下来,将裁剪后的面部图像调整为`(64, 64)`的尺寸: + +```py + # Resize image cropped_image = cropped_image.resize((64, 64), Image.ANTIALIAS) +``` + +8. 最后,将裁剪并调整大小的图像保存到所需目录: + +```py + cropped_image.save("/path/to/directory/to/store/cropped/images/filename.png")) +``` + +包装在 Python 函数中的完整代码如下所示: + +```py +import glob +import os + +import animeface +from PIL import Image + +total_num_faces = 0 for index, filename in enumerate(glob.glob('/path/to/directory/containing/images/*.*')): + # Open image and detect faces + try: + 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 image + coordinates = (fp.x, fp.y, fp.x+fp.width, fp.y+fp.height) + + # Crop image + cropped_image = im.crop(coordinates) + + # Resize image + cropped_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 模型: + +```py +gen_model = Sequential() +``` + +2. 接下来,添加一个具有 2,048 个节点的密集层,然后添加一个激活层`tanh`: + +```py +gen_model.add(Dense(units=2048)) +gen_model.add(Activation('tanh')) +``` + +3. 接下来,添加第二层,它也是一个具有 16,384 个神经元的密集层。 接下来是 `batch normalization` 层,其中 `default hyperparameters` 和 `tanh` 作为 **激活功能**: + +```py +gen_model.add(Dense(256*8*8)) +gen_model.add(BatchNormalization()) +gen_model.add(Activation('tanh')) +``` + +第二致密层的输出是大小为[ **16384** )的张量。 此处,[ **256、8、8** )是致密层中神经元的数量。 + +4. 接下来,向网络中添加一个重塑层,以将张量从最后一层重塑为(`batch_size, 8, 8, 256`)**:**形状的张量 + +```py +# Reshape layer +gen_model.add(Reshape((8, 8, 256), input_shape=(256*8*8,))) +``` + +5. 接下来,添加 2D 上采样层以将形状从(8,8,256)更改为(16,16,256)。 上采样大小为(2,2),这将张量的大小增加到其原始大小的两倍。 在这里,我们有 256 个张量为 16 x 16:的张量。 + +```py +gen_model.add(UpSampling2D(size=(2, 2))) +``` + +6. 接下来,添加一个 2D 卷积层。 这使用指定数量的滤波器在张量上应用 2D 卷积。 在这里,我们使用 64 个过滤器和一个`(5, 5)`形状的内核: + +```py +gen_model.add(Conv2D(128, (5, 5), padding='same')) +gen_model.add(Activation('tanh')) +``` + +7. 接下来,添加 2D 向上采样层以将张量的形状从 `(batch_size, 16, 16, 64)`更改为`(batch_size, 32, 32, 64)`: + +```py +gen_model.add(UpSampling2D(size=(2, 2))) +``` + +2D 上采样层 将张量 的行和列分别以[0]和[1]的大小重复 。 + +8. 接下来,在第二个 2D 卷积层上添加 `64` 过滤器和,将`(5, 5)` 的**内核大小** `tanh` 作为激活功能: + +```py +gen_model.add(Conv2D(64, (5, 5), padding='same')) +gen_model.add(Activation('tanh')) +``` + +9. 接下来,添加 2D 上采样层,将形状从 `(batch_size, 32, 32, 64)`更改为`(batch_size, 64, 64, 64)`: + +```py +gen_model.add(UpSampling2D(size=(2, 2))) +``` + +10. 最后,在第三个 2D 卷积层上添加三个 **滤镜** , **内核大小`(5, 5)` ,然后是 ] `tanh` 作为激活功能:** + +```py +gen_model.add(Conv2D(3, (5, 5), padding='same')) +gen_model.add(Activation('tanh')) +``` + +生成器网络将输出`(batch_size, 64, 64, 3)`形状的张量。 这批张量中的一个图像张量类似于具有三个通道的尺寸为 64 x 64 的图像: **红色,绿色** **和** **蓝色**( **RGB** )。 + +用 Python 方法包装的生成器网络的完整代码如下所示: + +```py +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`作为激活功能,而第二层具有乙状结肠作为激活功能。 乙状结肠激活用于二进制分类。 我们正在训练辨别器网络,以区分真实图像还是伪图像。 + +执行以下步骤来创建鉴别器网络: + +1. 让我们开始创建一个`Sequential` Keras 模型: + +```py +dis_model = Sequential() +``` + +2. 添加一个 2D 卷积层,该层采用形状为(64,64,3)的输入图像。 该层的超参数如下。 另外,添加具有`0.2`的 alpha 值的`LeakyReLU`作为激活功能: + * **过滤器:** 128 + * **内核大小:** (5,5) + * **填充:** 相同: + +```py +dis_model.add(Conv2D(filters=128, kernel_size=5, padding='same', + input_shape=(64, 64, 3))) +dis_model.add(LeakyReLU(alpha=0.2)) +``` + +3. 接下来,添加池大小为`(2, 2)`的 2D 最大池化层。 最大池用于对图像表示进行下采样,并通过在表示的非重叠子区域上使用 max-filter 来应用它: + +```py +dis_model.add(MaxPooling2D(pool_size=(2, 2))) +``` + +来自第一层的输出张量的形状将为`(batch_size, 32, 32, 128)`。 + +4. 接下来,添加具有以下配置的另一个 2D 卷积层: + * **过滤器**:256 + * **内核大小** :( 3,3) + * **激活函数**: `LeakyReLU`,具有 alpha 0.2 + * **2D 最大池中的池大小**:(2、2): + +```py +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)`。 + +5. 接下来,添加具有以下配置的第三个 2D 卷积层: + * **过滤器**: 512 + * **内核大小** :( 3 ,3) + * **激活功能**:`LeakyReLU` ,带有 alpha 0.2 + * **2D 最大池中的池大小** :( 2 ,2): + +```py +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)`。 + +6. 接下来,添加一个扁平层。 这将使输入变平而不影响批量大小。 它产生一个二维张量: + +```py +dis_model.add(Flatten()) +``` + +来自平坦化层的张量的输出形状将为`(batch_size, 18432,)`。 + +7. 接下来,添加具有`1024`神经元和`alpha` 0.2 作为激活功能的`LeakyReLU`的致密层: + +```py +dis_model.add(Dense(1024)) +dis_model.add(LeakyReLU(alpha=0.2)) +``` + +8. 最后,添加带有一个神经元的密集层以进行二进制分类。 S 型函数最适合二进制分类,因为它给出了分类的可能性: + +```py +dis_model.add(Dense(1)) +dis_model.add(Activation('tanh')) +``` + +网络将生成形状为`(batch_size, 1)`的输出张量。 输出张量包含类的概率。 + +包裹在 Python 方法中的鉴别器网络的完整代码如下: + +```py +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 类似于训练 Vanilla GAN 网络。 这是一个四步过程: + +1. 加载数据集。 +2. 构建和编译网络。 +3. 训练鉴别器网络。 +4. 训练发电机网络。 + +我们将在本节中一步一步地进行这些步骤。 + +让我们从定义变量和超参数开始: + +```py +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`方法。 以下代码显示了加载目录中所有图像的不同步骤: + +```py +# 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) +``` + +2. 接下来,为所有图像创建一个`ndarray`。 最终`ndarray`的形状将为`(total_num_images, 64, 64, 3)`。 此外,标准化所有图像: + +```py +# Convert to Numpy ndarray +X = np.array(all_images) +X = (X - 127.5) / 127.5 +``` + +现在我们已经加载了数据集,接下来我们将看到如何构建和编译网络。 + + + + + +# 建立和编译网络 + + + +在本节中,我们将构建和编译培训所需的网络: + +1. 首先定义培训所需的优化器,如下所示: + +```py +# 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) +``` + +2. 接下来,创建生成器模型的实例,并编译生成器模型(编译将初始化权重参数,优化器算法,损失函数以及使用网络所需的其他必要步骤): + +```py +gen_model = build_generator() +gen_model.compile(loss='binary_crossentropy', optimizer=gen_optimizer) +``` + +使用`binary_crossentropy`作为生成器网络的`loss`功能,并使用`gen_optimizer`作为优化器。 + +3. 接下来,创建鉴别模型的实例,并对其进行编译,如下所示: + +```py +dis_model = build_discriminator() +dis_model.compile(loss='binary_crossentropy', optimizer=dis_optimizer) +``` + +同样,使用`binary_crossentropy`作为鉴别器网络的损失函数,并使用`dis_optimizer`作为优化器。 + +4. 接下来,创建一个对抗模型。 一个对抗者将两个网络都包含在一个模型中。 对抗模型的架构如下: + * *输入->生成器->鉴别器->输出* + +创建和编译对抗模型的代码如下: + +```py +adversarial_model = Sequential() +adversarial_model.add(gen_model) +dis_model.trainable = False adversarial_model.add(dis_model) +``` + +当我们训练该网络时,我们不想训练鉴别器网络,因此在将其添加到对抗模型之前,使其变为不可训练的。 + +编译对抗模型,如下所示: + +```py +adversarial_model.compile(loss='binary_crossentropy', optimizer=gen_optimizer) +``` + +使用`binary_crossentropy`作为损失函数,使用`gen_optimizer`作为对抗模型的优化器。 + +在开始训练之前,添加 TensorBoard 以可视化显示损失,如下所示: + +```py +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 的微型批次上训练网络。 计算需要处理的批次数量: + +```py +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. 首先从正态分布中采样一批噪声矢量,如下所示: + +```py +z_noise = np.random.normal(0, 1, size=(batch_size, z_shape)) +``` + +要对值进行采样,请使用 Numpy 库中`np.random`模块中的`normal()`方法。 + +2. 接下来,从所有图像集中采样一批真实图像: + +```py +image_batch = X[index * batch_size:(index + 1) * batch_size] +``` + +3. 接下来,使用生成器网络生成一批伪图像: + +```py +generated_images = gen_model.predict_on_batch(z_noise) +``` + +4. 接下来,创建真实标签和伪标签: + +```py +y_real = np.ones(batch_size) - np.random.random_sample(batch_size) * 0.2 y_fake = np.random.random_sample(batch_size) * 0.2 +``` + +5. 接下来,在真实图像和真实标签上训练鉴别器网络: + +```py + dis_loss_real = dis_model.train_on_batch(image_batch, y_real) +``` + +6. 同样,在伪造图像和伪造标签上对其进行训练: + +```py +dis_loss_fake = dis_model.train_on_batch(generated_images, y_fake) +``` + +7. 接下来,计算平均损失并将其打印到控制台: + +```py +d_loss = (dis_loss_real+dis_loss_fake)/2 print("d_loss:", d_loss) +``` + +到目前为止,我们一直在训练鉴别器网络。 在下一部分中,让我们训练发电机网络。 + + + + + +# 训练发电机网络 + + + +为了训练发电机网络,我们必须训练对抗模型。 当我们训练对抗模型时,它只训练发电机网络,而冻结鉴别器网络。 由于我们已经训练过鉴别器网络,因此我们不会对其进行训练。 执行以下步骤来训练对抗模型: + +1. 首先重新创建一批噪声矢量。 从高斯/正态分布中采样以下噪声矢量: + +```py +z_noise = np.random.normal(0, 1, size=(batch_size, z_shape)) +``` + +2. 接下来,对这批噪声向量训练对抗模型,如下所示: + +```py +g_loss = adversarial_model.train_on_batch(z_noise, [1] * batch_size) +``` + +我们在一批噪声向量和实数标签上训练对抗模型。 在这里,*实数标签*是一个所有值均等于 1 的向量。我们还在训练生成器,以欺骗鉴别器网络。 为此,我们为它提供一个向量,该向量的所有值均等于 1。在此步骤中,生成器将接收来自生成器网络的反馈,并相应地进行改进。 + +3. 最后,将生成器损耗打印到控制台以跟踪损耗: + +```py +print("g_loss:", g_loss) +``` + +有一种被动的方法可以评估培训过程。 每 10 个周期后,生成伪造图像并手动检查图像质量: + +```py + 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)) +``` + +这些 图像将帮助您决定是继续训练还是尽早停止训练。 如果生成的高分辨率图像的质量良好,请停止训练。 或者继续训练,直到您的模型变好为止。 + +我们已经成功地在 ANIME 角色数据集上训练了 DCGAN 网络。 现在我们可以使用该模型生成动漫人物图像。 + + + + + +# 产生影像 + + + +为了生成图像,我们需要一个从潜在空间采样的噪声矢量。 Numpy 有一种称为`uniform()`的方法,可以根据均匀分布生成矢量。 让我们看看如何在以下步骤中生成图像: + +1. 通过添加以下代码行,创建尺寸为`(batch_size, 100)`的噪声矢量: + +```py +z_noise = np.random.normal(0, 1, size=(batch_size, z_shape)) +``` + +2. 然后,使用生成器模型的`predict_on_batch`方法生成图像。 将上一步中创建的噪声矢量馈入其中: + +```py +gen_images = gen_model.predict_on_batch(z_noise) +``` + +3. 现在我们已经生成了图像,通过添加以下代码行将其保存。 创建一个名为`results`的目录来存储生成的图像: + +```py +imsave('results/image_{}.jpg'.format(epoch),gen_images[0]) +``` + +现在,您可以打开这些生成的图像以测量生成的模型的质量。 这是一种评估模型性能的被动方法。 + + + + + +# 保存模型 + + + +在 Keras 中保存模型只需要一行代码。 要保存生成器模型,请添加以下行: + +```py +# Specify the path for the generator model +gen_model.save("directory/for/the/generator/model.h5") +``` + +同样,通过添加以下行来保存鉴别器模型: + +```py +# Specify the path for the discriminator model +dis_model.save("directory/for/the/discriminator/model.h5") +``` + + + + + +# 可视化生成的图像 + + + +在将网络训练了 100 个时间段后,生成器将开始生成合理的图像。 让我们看一下生成的图像。 + +在 100 个时代之后,图像显示如下: + +![](img/bad2e9c8-167e-436b-9078-4fa53cffc0a2.png) + +在 200 个时代之后,图像显示如下: + +![](img/82a9152b-d3fb-467b-837f-96af60995452.png) + +要生成非常好的图像,请将网络训练 10,000 个纪元。 + + + + + +# 可视化损失 + + + +为了可视化训练的损失,请按以下方式启动 TensorBoard 服务器: + +```py +tensorboard --logdir=logs +``` + +现在,在浏览器中打开`localhost:6006`。 Tensorboard 的**标量**部分包含两种损失的图: + +![](img/590d57c0-43e3-4ca8-ad30-8bd20887ecc5.png) + +这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止培训,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。 + + + + + +# 可视化图形 + + + +Tensorboard 的 **GRAPHS** 部分包含两个网络的图形。 如果网络性能不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作: + +![](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 使用卷积神经网络,非常擅长生成逼真的图像。 +* **特征提取器**:训练后,可以使用鉴别器从中间层提取特征。 这些提取的功能在样式转移和面部识别等任务中很有用。 样式转移涉及生成图像的内部表示,用于计算样式和内容损失。 请参阅以下论文,以了解有关样式转换的更多信息: [https://arxiv.org/pdf/1508.06576.pdf](https://arxiv.org/pdf/1508.06576.pdf) 。 + + + + + +# 概要 + + + +在本章中,我们介绍了深度卷积生成对抗网络。 我们从基本介绍 DCGAN 开始,然后深入探讨了 DCGAN 网络的体系结构。 之后,我们设置项目并安装必要的依赖项。 然后,我们研究了下载和准备数据集所需的不同步骤。 然后,我们准备了网络的 Keras 实现,并在我们的数据集中对其进行了训练。 经过培训后,我们将其用于生成新的动漫角色。 我们还探讨了 DCGAN 在实际用例中的不同应用。 + +在下一章中,我们将研究用于高分辨率图像生成的 SRGAN。 + + diff --git a/new/gan-proj/5.md b/new/gan-proj/5.md new file mode 100644 index 0000000000000000000000000000000000000000..aad6a10eaff8bc966f9e58a450d78b78fbd2c5cf --- /dev/null +++ b/new/gan-proj/5.md @@ -0,0 +1,1206 @@ + + +# 使用 SRGAN 生成逼真的图像 + + + +**超分辨率生成对抗网络**或 **SRGAN** ,是**生成对抗网络**( **GAN** ), 低分辨率图像中的高分辨率图像,具有更好的细节和更高的质量 。 CNN 较早用于产生高分辨率图像,该图像可以更快地训练并达到高水平的精度。 但是,在某些情况下,它们无法恢复更精细的细节,并且通常会生成模糊的图像。 在本章中,我们将在 Keras 框架中实现一个 SRGAN 网络,该网络将能够生成高分辨率图像。 SRGAN 在标题为*使用生成对抗网络的逼真的单图像超分辨率的论文中引入,*,作者是 Christian Ledig,Lucas Theis,Ferenc Huszar, Jose Caballero,Andrew Cunningham 等*和*可以在以下链接中找到: [https://arxiv.org/pdf/1609.04802.pdf](https://arxiv.org/pdf/1609.04802.pdf) 。 + +在本章中, e 将涵盖以下主题: + +* SRGAN 简介 +* 设置项目 +* 下载 CelebA 数据集 +* SRGAN 的 Keras 实现 +* 培训 SRGAN 和网络优化 +* SRGAN 的实际应用 + + + + + +# SRGAN 简介 + + + +与其他 GAN 一样,SRGAN 包含一个生成器网络和一个鉴别器网络。 两个网络都很深。 这两个网络的功能指定如下: + +* **生成器**:生成器网络拍摄尺寸为 64x64x3 的低分辨率图像,并且在一系列卷积和上采样层之后, 生成超分辨率 形状为 256x256x3 的图片 +* **鉴别符** :鉴别符网络拍摄高分辨率图像,并尝试识别给定的图像是真实的(来自真实数据样本)还是伪造的(由 发电机) + + + + + +# SRGAN 的体系结构 + + + +在 SRGAN 中,这两个网络都是深度卷积神经网络。 它们包含卷积层和上采样层。 每个卷积层之后是批处理归一化操作和一个激活层。 我们将在以下各节中详细探讨网络。 下图显示了 SRGAN 的体系结构: + +![](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) | + +These hyperparameters are best-suited for the Keras framework. If you are using any other framework, modify them accordingly. + + + +# 鉴别器网络的架构 + + + +鉴别器网络也是一个深度卷积网络。 它包含八个卷积块,后跟两个密集(完全连接)层。 每个卷积块后面都有一个批处理归一化层。 网络的末端有两个密集层,它们充当分类块。 最后一层预测图像属于真实数据集或伪数据集的概率。 鉴别器网络的详细配置如下表所示: + +| **图层名称** | **超参数** | **输入形状** | **输出形状** | +| 输入层 | 没有 | (256, 256, 3) | (256, 256, 3) | +| 2D 卷积层 | 过滤器= 64,kernel_size = 3,步幅= 1,填充='相同',激活='leakyrelu' | (256, 256, 3) | (256, 256, 64) | +| 2D 卷积层 | 过滤器= 64,kernel_size = 3,步幅= 2,填充='相同',激活='leakyrelu' | (256, 256, 64) | (128, 128, 64) | +| 批量归一化层 | 动量= 0.8 | (128, 128, 64) | (128, 128, 64) | +| 2D 卷积层 | 过滤器= 128,kernel_size = 3,步幅= 1,填充='相同',激活='leakyrelu' | (128, 128, 64) | (128, 128, 128) | +| 批量归一化层 | 动量= 0.8 | (128, 128, 128) | (128, 128, 128) | +| 2D 卷积层 | 过滤器= 128,kernel_size = 3,步幅= 2,填充='相同',激活='leakyrelu' | (128, 128, 128) | (64, 64, 128) | +| 批量归一化层 | 动量= 0.8 | (64, 64, 128) | (64, 64, 128) | +| 2D 卷积层 | 过滤器= 256,kernel_size = 3,步幅= 1,填充='相同',激活='leakyrelu' | (64, 64, 128) | (64, 64, 256) | +| 批量归一化层 | 动量= 0.8 | (64, 64, 256) | (64, 64, 256) | +| 2D 卷积层 | 过滤器= 256,kernel_size = 3,步幅= 2,填充='相同',激活='leakyrelu' | (64, 64, 256) | (32, 32, 256) | + +现在,我们对这两个网络的架构有了清晰的了解。 在下一部分中,让我们看一下训练 SRGAN 所需的目标函数。 + + + + + +# 训练目标功能 + + + +要训​​练 SRGAN,有一个目标函数或损失函数,我们需要将其最小化以训练模型。 SRGAN 的目标函数称为**感知损失**函数,它是两个损失函数的加权和,如下所示: + +* 内容丢失 +* 对抗损失 + +在以下各节中,让我们详细研究内容损失和对抗损失。 + + + + + +# 内容丢失 + + + +内容丢失有两种类型,如下所示: + +* MSE 像素损失 +* VGG 损失 + +让我们详细讨论这些损失。 + + + + + +# MSE 像素损失 + + + +内容损失是在真实图像的每个像素值与生成的图像的每个像素值之间计算的均方误差。 像素级 MSE 损失计算出生成的图像与实际图像有多大差异。 像素级 MSE 损失的计算公式如下: + +![](img/3b2c1077-9f9a-4cda-8670-cd8d737041f8.png) + +在此,![](img/c38be29e-d867-4b09-845b-a83600eebf1c.png)表示由所生成的网络所生成的高分辨率图像。 ![](img/851c0456-0b76-4d6e-b1d3-ef0735cb79b4.png)代表从真实数据集中采样的高分辨率图像。 + + + + + +# VGG 损失 + + + +VGG 丢失是另一个内容丢失功能,可应用于生成的图像和真实图像。 VGG19 是一种非常流行的深度神经网络,主要用于图像分类。 VGG19 由 Simonyan 和 Zisserman 在他们的论文*用于大规模图像识别的超深度卷积网络* **,** 中引入,可在[获得 https://arxiv.org/pdf/1409.1556.pdf](https://arxiv.org/pdf/1409.1556.pdf) 。 预训练的 VGG19 网络的中间层用作特征提取器,可用于提取生成的图像和真实图像的特征图。 VGG 损失基于这些提取的特征图。 计算为生成的图像和真实图像的特征图之间的欧几里得距离。 VGG 损耗的公式如下: + +![](img/c854836e-df68-4886-bb7c-8577a41d732c.png) + +在此,![](img/b2181e11-3658-4c93-8a03-365c47c6aae6.png)表示由 VGG19 网络生成的特征图。 ![](img/bb5729b4-1a7b-463f-a097-47e123ef56ab.png)代表提取的真实图像特征图,![](img/af06eff5-c037-40a2-ac33-df687b277046.png)代表提取的高分辨率图像生成的特征图。 整个方程表示生成图像和真实图像的特征图之间的欧式距离。 + +前述内容损失中的任何一种都可以用于训练 SRGAN。 对于我们的实现,我们将使用 VGG 损失。 + + + + + +# 对抗损失 + + + +根据鉴别器网络返回的概率计算对抗损失。 在对抗模型中,鉴别器网络被馈送有由生成的网络生成的生成的图像。 对抗损失可以用以下等式表示: + +![](img/cf90c58f-3995-48a8-b78a-0ab81cb7ae9d.png) + +这里,![](img/92bdc164-79c8-410c-8665-8c0e9ab3e654.png)是所生成的图像 , ![](img/a8a70426-5d0b-4804-b69c-6bde1c0b0651.png)表示所生成的图像是真实图像的概率。 + +知觉损失函数是内容损失和对抗损失的加权和,表示为以下方程式: + +![](img/4e2039a0-5a7e-4038-b68d-f19fa142d2d8.png) + +在此,总的感知损失由![](img/1ce9659c-27e3-423a-a3b8-be7e3e05b096.png)表示。 ![](img/1629f39f-eccc-4ba0-bec4-6263e873ed59.png)是内容损失,可以是像素级 MSE 损失或 VGG 损失。 + +通过最小化感知损失值,生成器网络试图欺骗鉴别器。 随着感知损失的值减小,生成器网络开始生成更逼真的图像。 + +现在开始进行该项目。 + + + + + +# 设置项目 + + + +如果尚未使用所有章节的完整代码克隆存储库,请立即克隆存储库。 下载的代码有一个名为`Chapter05`的目录,其中包含本章的全部代码。 执行以下命令来设置项目: + +1. 首先,导航到父目录,如下所示: + +```py +cd Generative-Adversarial-Networks-Projects +``` + +2. 现在,将目录从当前目录更改为 `Chapter05`: + +```py +cd Chapter05 +``` + +3. 接下来,为该项目创建一个 Python 虚拟环境: + +```py +virtualenv venv +virtualenv venv -p python3 # Create a virtual environment using + python3 interpreter +virtualenv venv -p python2 # Create a virtual environment using + python2 interpreter +``` + +我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。 + +4. 接下来,激活新创建的虚拟环境: + +```py +source venv/bin/activate +``` + +激活虚拟环境后,所有其他命令将在此虚拟环境中执行。 + +5. 接下来,通过执行以下命令,安装`requirements.txt` 文件中提供的所有库: + +```py +pip install -r requirements.txt +``` + +您可以参考 `README.md` 文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。 + +在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一节中,让我们研究数据集。 我们将探索下载和格式化数据集的各种步骤。 + + + + + +# 下载 CelebA 数据集 + + + +在本章中,我们将使用大型 **CelebFaces 属性**( **CelebA** )数据集,该数据集可从 [http://mmlab.ie.cuhk.edu.hk 获得 /projects/CelebA.html](http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html) 。 数据集包含 202、599 名名人的面部图像。 + +The dataset is available for non-commercial research purposes only and can't be used for commercial purposes. If you intend to use the dataset for commercial purposes, seek permissions from the owners of the images. + +我们将使用 `CelebA` 数据集训练我们的 SRGAN 网络。 执行以下步骤下载和提取数据集: + +1. 从以下链接下载数据集: + +```py +https://www.dropbox.com/sh/8oqt9vytwxb3s4r/AAB06FXaQRUNtjW9ntaoPGvCa?dl=0 +``` + +2. 通过执行以下命令从下载的`img_align_celeba.zip`中提取图像: + +```py +unzip img_align_celeba.zip +``` + +现在,我们已经下载并提取了数据集。 现在,我们可以开始进行 SRGAN 的 Keras 实现。 + + + + + +# SRGAN 的硬实施 + + + +正如我们所讨论的,SRGAN 在 Imagenet 数据集上具有三个神经网络,一个生成器,一个鉴别器和一个预训练的 VGG19 网络。 在本节中,我们将编写所有网络的实现。 让我们从实现生成器网络开始。 + +在开始编写实现之前,创建一个名为`main.py`的 Python 文件并导入基本模块,如下所示: + +```py +import glob +import os + +import 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. 首先定义生成器网络所需的超参数: + +```py +residual_blocks = 16 momentum = 0.8 input_shape = (64, 64, 3) +``` + +2. 接下来,创建一个输入层,以将输入提供给网络,如下所示: + +```py +input_layer = Input(shape=input_shape) +``` + +The input layer takes an input image of a shape of (64, 64, 3) and passes it to the next layer in the network. + +3. 接下来,如下所示添加残差前块(2D 卷积层): + 配置: + * **过滤器**:`64` + * **内核大小**:`9` + * **大步前进**:`1` + * **填充**:`same` + * **激活**:`relu:` + +```py +gen1 = Conv2D(filters=64, kernel_size=9, strides=1, padding='same', activation='relu')(input_layer) +``` + +4. 接下来,编写一个包含剩余块完整代码的方法,如下所示: + +```py +def residual_block(x): + """ + Residual block """ filters = [64, 64] + kernel_size = 3 + strides = 1 + padding = "same" + momentum = 0.8 + activation = "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 x + res = Add()([res, x]) + return res +``` + +5. 现在,使用在最后一步中定义的`residual_block`函数添加 16 个剩余块: + +```py +res = residual_block(gen1) +for i in range(residual_blocks - 1): + res = residual_block(res) +``` + +前残留块的输出进入第一个残留块。 第一个残差块的输出将到达第二个残差块,依此类推,直到第 16 个残差块。 + +6. 接下来,添加残留后块(2D 卷积层,然后是批处理归一化层),如下所示: + 配置: + * **过滤器**:`64` + * **内核大小**:`3` + * **大步前进**:``1`` + * **填充**:`same` + * **批量归一化**:是(动量= 0.8): + +```py +gen2 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(res) +gen2 = BatchNormalization(momentum=momentum)(gen2) +``` + +7. 现在,添加`Add`层,以获取残留前块的输出`gen1`和残留后块的输出`gen2`之和。 该层生成另一个类似形状的张量。 有关更多详细信息,请参考“ *生成器网络*的体系结构”部分。 + +```py +gen3 = Add()([gen2, gen1]) +``` + +8. 接下来,添加一个升采样块,如下所示: + 配置: + * **上采样大小**:`2` + * **文件管理器**:`256` + * **内核大小**:`3` + * **大步前进**:`1` + * **填充**:`same` + * **激活**:`PReLU:` + +```py +gen4 = UpSampling2D(size=2)(gen3) +gen4 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen4) +gen4 = Activation('relu')(gen4) +``` + +9. 接下来,添加另一个上采样块,如下所示: + 配置: + * **上采样大小**: `2` + * **文件管理器**: `256` + * **内核大小**: `3` + * **大步前进**: `1` + * **填充**: `same` + * **激活**: `PReLU:` + +```py +gen5 = UpSampling2D(size=2)(gen4) +gen5 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen5) +gen5 = Activation('relu')(gen5) +``` + +10. 最后,添加输出卷积层: + 配置: + * **过滤器**:`3`(等于通道数) + * **内核大小**:`9` + * **大步前进**:`1` + * **填充**:`same` + * **激活**:`tanh:` + +```py +gen6 = Conv2D(filters=3, kernel_size=9, strides=1, padding='same')(gen5) +output = Activation('tanh')(gen6) +``` + +一旦定义了网络中的所有层,就可以创建 Keras 模型。 我们已经使用 Keras 的功能 API 定义了 Keras 顺序图。 让我们通过指定网络的输入和输出来创建 Keras 模型。 + +11. 现在,创建 Keras 模型并指定模型的输入和输出,如下所示: + +```py +model = Model(inputs=[input_layer], outputs=[output], name='generator') +``` + +我们已经成功地为发电机网络创建了 Keras 模型。 现在,将生成器网络的整个代码包装在 Python 函数中,如下所示: + +```py +def build_generator(): + """ + Create a generator network using the hyperparameter values defined below :return: + """ residual_blocks = 16 + momentum = 0.8 + input_shape = (64, 64, 3) + + # Input Layer of the generator network + input_layer = Input(shape=input_shape) + + # Add the pre-residual block + gen1 = Conv2D(filters=64, kernel_size=9, strides=1, padding='same', + activation='relu')(input_layer) + + # Add 16 residual blocks + res = residual_block(gen1) + for i in range(residual_blocks - 1): + res = residual_block(res) + + # Add the post-residual block + gen2 = 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 block + gen4 = UpSampling2D(size=2)(gen3) + gen4 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen4) + gen4 = Activation('relu')(gen4) + + # Add another upsampling block + gen5 = UpSampling2D(size=2)(gen4) + gen5 = Conv2D(filters=256, kernel_size=3, strides=1, + padding='same')(gen5) + gen5 = Activation('relu')(gen5) + + # Output convolution layer + gen6 = Conv2D(filters=3, kernel_size=9, strides=1, padding='same')(gen5) + output = Activation('tanh')(gen6) + + # Keras model + model = Model(inputs=[input_layer], outputs=[output], + name='generator') + return model +``` + +我们已经成功地为发电机网络创建了 Keras 模型。 在下一节中,我们将为鉴别器网络创建 Keras 模型。 + + + + + +# 鉴别网络 + + + +我们已经在*中探讨了鉴别器网络的体系结构。 让我们 首先在 Keras 框架中编写区分网络的层,然后使用 Keras 框架的功能 API 创建 Keras 模型。* + +执行以下步骤以在 Keras 中实现鉴别器网络: + +1. 首先定义鉴别器网络所需的超参数: + +```py +leakyrelu_alpha = 0.2 momentum = 0.8 input_shape = (256, 256, 3) +``` + +2. 接下来,创建一个输入层,以将输入提供给网络,如下所示: + +```py +input_layer = Input(shape=input_shape) +``` + +3. 接下来,添加一个卷积块,如下所示: + 配置: + * **过滤器**:`64` + * **内核大小**:`3` + * **大步前进**:`1` + * **填充**:`same` + * **激活**:`LeakyReLU`,alpha 等于 0.2: + +```py +dis1 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(input_layer) +dis1 = LeakyReLU(alpha=leakyrelu_alpha)(dis1) +``` + +4. 接下来,添加另外七个卷积块,如下所示: + 配置: + * **过滤器**:`64`,`128`,`128`,`256`,`256`,`512`,`512` + * **内核大小**:`3`,`3`,`3`,`3`,`3`,`3`,`3` + * **跨越**:`2`,`1`,`2`,`1`,`2`,`1`,`2` + * 每个卷积层的**填充**:`same` + * **激活**:`LealyReLU`,每个卷积层的 alpha 等于 0.2: + +```py +# 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) +``` + +5. 接下来,添加具有 1,024 个节点的密集层,如下所示: + 配置: + * **节点**:`1024` + * **激活**:`LeakyReLU`,alpha 等于 0.2: + +```py +dis9 = Dense(units=1024)(dis8) +dis9 = LeakyReLU(alpha=0.2)(dis9) +``` + +6. 然后,添加一个密集层以返回概率,如下所示: + +```py +output = Dense(units=1, activation='sigmoid')(dis9) +``` + +7. 最后,创建 Keras 模型并指定网络的输入和输出: + +```py +model = Model(inputs=[input_layer], outputs=[output], + name='discriminator') +``` + +W 将功能内的鉴别器网络的整个代码说唱如下: + +```py +def build_discriminator(): + """ + Create a discriminator network using the hyperparameter values defined below :return: + """ leakyrelu_alpha = 0.2 + momentum = 0.8 + input_shape = (256, 256, 3) + + input_layer = Input(shape=input_shape) + + # Add the first convolution block + dis1 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(input_layer) + dis1 = LeakyReLU(alpha=leakyrelu_alpha)(dis1) + + # 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) + + # Add a dense layer + dis9 = Dense(units=1024)(dis8) + dis9 = LeakyReLU(alpha=0.2)(dis9) + + # Last dense layer - for classification + output = 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. 首先指定输入形状: + +```py +input_shape = (256, 256, 3) +``` + +2. 接下来,加载预训练的 VGG19 并指定模型的输出: + +```py +vgg = VGG19(weights="imagenet") +vgg.outputs = [vgg.layers[9].output] +``` + +3. 接下来,创建符号 `input_tensor` ,这将是我们对 VGG19 网络的符号输入,如下所示: + +```py +input_layer = Input(shape=input_shape) +``` + +4. 接下来,使用 VGG19 网络提取功能: + +```py +features = vgg(input_layer) +``` + +5. 最后,创建 Keras `model`并为网络指定`inputs`和`outputs`: + +```py +model = Model(inputs=[input_layer], outputs=[features]) +``` + +最后,将 VGG19 模型的整个代码包装在一个函数中,如下所示: + +```py +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' dataset + vgg = VGG19(weights="imagenet") + vgg.outputs = [vgg.layers[9].output] + + input_layer = Input(shape=input_shape) + + # Extract features + features = vgg(input_layer) + + # Create a Keras model + model = Model(inputs=[input_layer], outputs=[features]) + return model +``` + + + + + +# 对抗网络 + + + +对抗网络是使用生成器,鉴别器和 VGG19 的组合网络。 在本节中,我们将创建一个对抗网络。 + +执行以下步骤来创建对抗网络: + +1. 首先为网络创建输入层: + +```py +input_low_resolution = Input(shape=(64, 64, 3)) +``` + +对抗网络将收到`(64, 64, 3)`形状的图像,这就是我们创建输入层的原因。 + +2. 接下来,使用生成器网络生成伪造的高分辨率图像,如下所示: + +```py +fake_hr_images = generator(input_low_resolution) +``` + +3. 接下来,使用 VGG19 网络提取伪造图像的特征,如下所示: + +```py +fake_features = vgg(fake_hr_images) +``` + +4. 接下来,使鉴别器网络在对抗网络中不可训练: + +```py +discriminator.trainable = False +``` + +我们使鉴别器网络不可训练,因为我们不想在训练生成器网络时训练鉴别器网络。 + +5. 接下来,将伪造的图像传递到鉴别器网络: + +```py +output = discriminator(fake_hr_images) +``` + +6. 最后,创建一个 Keras 模型,这将是我们的对抗模型: + +```py +model = Model(inputs=[input_low_resolution], outputs=[output, + fake_features]) +``` + +7. 将对抗模型的整个代码包装在 Python 函数中: + +```py +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. 首先定义训练所需的超参数: + +```py +# 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) +``` + +2. 接下来,定义训练优化器。 对于所有网络,我们将使用学习率等于 0.0002 且`beta_1`等于`0.5`的 Adam 优化器: + +```py +# Common optimizer for all networks common_optimizer = Adam(0.0002, 0.5) +``` + + + + + +# 建立和编译网络 + + + +在本节中,我们将完成构建和编译网络所需的不同步骤: + +1. 构建和编译 VGG19 网络: + +```py +vgg = build_vgg() +vgg.trainable = False vgg.compile(loss='mse', optimizer=common_optimizer, metrics= + ['accuracy']) +``` + +要编译 VGG19,请使用`mse`作为损失,使用`accuracy`作为度量,并使用`common_optimizer`作为优化器。 编译网络之前,请先禁用培训,因为我们不想培训 VGG19 网络。 + +2. 接下来,构建并编译`discriminator`网络,如下所示: + +```py +discriminator = build_discriminator() +discriminator.compile(loss='mse', optimizer=common_optimizer, + metrics=['accuracy']) +``` + +要编译鉴别器网络,使用 `mse` 作为损耗, `accuracy` 作为度量,并使用 `common_optimizer` 作为优化器。 + +3. 接下来,构建发电机网络: + +```py +generator = build_generator() +``` + +4. 接下来,创建一个对抗模型。 首先创建两个输入层: + +```py +input_high_resolution = Input(shape=high_resolution_shape) +input_low_resolution = Input(shape=low_resolution_shape) +``` + +5. 接下来,使用生成器网络从低分辨率图像中象征性地生成高分辨率图像: + +```py +generated_high_resolution_images = generator(input_low_resolution) +``` + +使用 VGG19 提取生成图像的特征图: + +```py +features = vgg(generated_high_resolution_images) +``` + +使鉴别器网络不可训练,因为我们不想在对抗模型训练期间训练鉴别器模型 : + +```py +discriminator.trainable = False +``` + +6. 接下来,使用鉴别器网络获取生成的高分辨率伪图像的概率: + +```py +probs = discriminator(generated_high_resolution_images) +``` + +在此,`probs`表示所生成的图像属于真实数据集的概率。 + +7. 最后,创建并编译对抗网络: + +```py +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_crossentropy`和`mse`作为损失函数,`common_optimizer`作为优化器,并使用`[0.001, 1]`作为损失权重。 + +8. 添加`Tensorboard`以可视化训练损失并可视化网络图: + +```py +tensorboard = TensorBoard(log_dir="logs/".format(time.time())) +tensorboard.set_model(generator) +tensorboard.set_model(discriminator) +``` + +9. 创建一个循环,该循环应运行指定的时期数: + +```py +for epoch in range(epochs): + print("Epoch:{}".format(epoch)) +``` + +完成此步骤后,所有代码都将在此 for 循环内。 + +10. 接下来,对一批高分辨率和低分辨率图像进行采样,如下所示: + +```py +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` 功能 的代码如下。 它具有很强的描述性,通过阅读可以理解。 它包含加载和调整图像大小并生成高分辨率和低分辨率图像的不同步骤: + +```py +def sample_images(data_dir, batch_size, high_resolution_shape, low_resolution_shape): + # Make a list of all images inside the data directory + all_images = glob.glob(data_dir) + + # Choose a random batch of images + images_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 image + img1 = imread(img, mode='RGB') + img1 = img1.astype(np.float32) + + # Resize the image + img1_high_resolution = imresize(img1, high_resolution_shape) + img1_low_resolution = imresize(img1, low_resolution_shape) + + # Do a random flip + if 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) +``` + +11. 接下来,对图像进行归一化以将像素值转换为`[-1, 1]`之间的范围,如下所示: + +```py + high_resolution_images = high_resolution_images / 127.5 - 1. + low_resolution_images = low_resolution_images / 127.5 - 1. +``` + +将像素值转换为-1 到 1 的范围非常重要。我们的生成器网络的末尾有`tanh`。 `tanh`激活功能将值压缩到相同范围。 在计算损耗时,必须使所有值都在同一范围内。 + + + + + +# 训练鉴别器网络 + + + +本节中给出的步骤显示了如何训练鉴别器网络。 这是最后一系列步骤的延续: + +1. 使用`generator`网络生成伪造的高分辨率图像: + +```py +generated_high_resolution_images = + generator.predict(low_resolution_images) +``` + +2. 创建一批真实标签和伪标签: + +```py + real_labels = np.ones((batch_size, 16, 16, 1)) + fake_labels = np.zeros((batch_size, 16, 16, 1)) +``` + +3. 在真实图像和真实标签上训练鉴别器网络: + +```py +d_loss_real = discriminator.train_on_batch(high_resolution_images, + real_labels) +``` + +4. 在生成的图像和伪造标签上训练鉴别器: + +```py +d_loss_fake = discriminator.train_on_batch(generated_high_resolution_images, fake_labels) +``` + +5. 最后,计算总的鉴别器损失: + +```py + d_loss = 0.5 * np.add(d_loss_real, d_loss_fake) +``` + +现在,我们添加了代码来训练鉴别器网络。 接下来,添加代码以训练对抗模型,从而训练生成器网络。 + + + + + +# 训练发电机网络 + + + +本节中给出的步骤显示了如何训练发电机网络。 这是最后一系列步骤的延续: + +1. 再次,对一批高分辨率和低分辨率图像进行采样并对其进行归一化: + +```py + 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. +``` + +2. 使用 VGG19 网络提取真实高分辨率图像的特征图(内部表示): + +```py + image_features = vgg.predict(high_resolution_images) +``` + +3. 最后,训练对抗模型并为其提供适当的输入,如下所示: + +```py + g_loss = adversarial_model.train_on_batch([low_resolution_images, high_resolution_images], + [real_labels, image_features]) +``` + +4. 在每个时期完成之后,将损失写入 TensorBoard 以将其可视化: + +```py + write_log(tensorboard, 'g_loss', g_loss[0], epoch) + write_log(tensorboard, 'd_loss', d_loss[0], epoch) +``` + +5. 每隔 100 个周期后,使用生成器网络生成高分辨率的伪图像并保存以使其可视化: + +```py + 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 images + high_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) + + # Save + for 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 64x3 的低分辨率图像,并将其传递给`generator.predict()`函数,该函数将生成高分辨率图像。 + + + + + +# 保存模型 + + + +在 Keras 中保存模型只需要一行代码。 要保存`generator`模型,请添加以下行: + +```py +# Specify the path for the generator model +gen_model.save("directory/for/the/generator/model.h5") +``` + +同样,通过添加以下行来保存`discriminator`模型: + +```py +# Specify the path for the discriminator model +dis_model.save("directory/for/the/discriminator/model.h5") +``` + + + + + +# 可视化生成的图像 + + + +经过大量时间后,生成器将开始生成良好的图像。 让我们看一下生成的图像: + +* 在 1,000 个时代之后,图像显示如下: + +![](img/fcd5c713-f0e8-4b2b-b914-06b9cfdb3600.png) + +* 5,000 次之后,图像显示如下: + +![](img/f099cb7a-a2b7-476b-9476-f7e47eb947b4.png) + +* 10,000 个纪元后,图像显示如下: + +![](img/96ef5573-9de4-4bf6-a6ec-848e6f3c622c.png) + +* 15,000 个纪元后,图像显示如下: + +![](img/fd0ed18a-8cd7-4200-a3e9-81c6f771302a.png) + +* 经过 20,000 个纪元后,图像显示如下: + +![](img/d58519f5-35a1-4b3b-831f-163b995eeabd.png) + +要生成非常好的图像,请将网络训练 30,000-50,000 个纪元。 + + + + + +# 可视化损失 + + + +要显示培训损失,请启动,,`tensorboard`,,服务器,如下所示: + +```py +tensorboard --logdir=logs +``` + +现在,在浏览器中打开 `localhost:6006` 。 TensorBoard 的标量部分包含两种损失的图: + +![](img/777f3571-0412-4464-bb22-c1445c488875.png) + +这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止培训,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 尝试使用超参数,然后选择一组您认为可能会提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。 + + + + + +# 可视化图形 + + + +TensorBoard 的 GRAPHS 部分包含两个网络的图形。 如果网络性能不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的运算: + +![](img/5d8ca1c2-04b5-4106-b5c6-5c96ca28ce46.png) + + + + + +# SRGAN 的实际应用 + + + +现在,让我们看一下 SRGAN 的实际应用: + +* 恢复旧照片 +* 行业应用,例如自动提高徽标,横幅和小册子的分辨率 +* 自动为用户增加社交媒体图像的分辨率 +* 拍摄时自动增强相机上的照片 +* 提高医学图像的分辨率 + + + + + +# 概要 + + + +在本章中,我们首先介绍 SRGAN。 然后,我们研究了生成器和鉴别器网络的体系结构。 后来,我们执行了该项目所需的设置。 然后,我们收集并探索了数据集。 之后,我们在训练 SRGAN 之前先在 Keras 中实施了该项目,评估了训练后的 SRGAN 网络,并使用超参数优化技术对训练后的模型进行了优化。 最后,我们简要介绍了 SRGAN 的一些不同应用。 + +在下一章中,我们将介绍 StackGAN 及其不同的应用程序。 + + diff --git a/new/gan-proj/6.md b/new/gan-proj/6.md new file mode 100644 index 0000000000000000000000000000000000000000..732d3c3a9aa7ddbee718ecfb430d68607624fcc7 --- /dev/null +++ b/new/gan-proj/6.md @@ -0,0 +1,2013 @@ + + +# StackGAN-文本到逼真的图像合成 + + + +文本到图像的合成是**生成对抗网络**( **GAN** )的用例之一,它具有许多工业应用,就像前面章节中描述的 GAN 一样。 从文本描述中合成图像非常困难,因为要构建可以生成反映文本含义的图像的 模型非常困难。 一个试图解决这个问题的网络是 StackGAN。 在本章中,我们将使用 TensorFlow 作为后端在 Keras 框架中实现 StackGAN。 + +在本章中,我们将介绍以下主题: + +* StackGAN 简介 +* StackGAN 的架构 +* 数据收集与准备 +* StackGAN 的 Keras 实现 +* 训练 StackGAN +* 评估模型 +* pix2pix 网络的实际应用 + + + + + +# StackGAN 简介 + + + +之所以这样称呼 StackGAN,是因为它具有两个 GAN,这些 GAN 堆叠在一起形成了一个能够生成高分辨率图像的网络。 它分为两个阶段,第一阶段和第二阶段。 Stage-I 网络生成具有基本颜色和粗略草图的低分辨率图像,并以文本嵌入为条件;而 Stage-II 网络获取由 Stage-I 网络生成的图像,并生成以条件为基础的高分辨率图像 文字嵌入。 基本上,第二个网络会纠正缺陷并添加引人注目的细节,从而产生更逼真的高分辨率图像。 + +我们可以将 StackGAN 网络与画家的作品进行比较。 当画家开始工作时,他们会绘制原始形状,例如线条,圆形和矩形。 然后,他们尝试填充颜色。 随着绘画的进行,越来越多的细节被添加。 在 StackGAN 中,Stage-I 与绘制基本形状有关,而 Stage-II 与校正由 Stage-I 网络生成的图像中的缺陷有关。 Stage-II 还添加了更多细节,以使图像看起来更逼真。 这两个阶段的生成器网络都是**条件生成对抗网络**( **CGAN** )。 f irst GAN 以文本描述为条件,而第二网络以文本描述和第一个 GAN 生成的图像为条件。 + + + + + +# StackGAN 的体系结构 + + + +StackGAN 是一个两阶段的网络。 每个阶段都有两个生成器和两个鉴别器。 StackGAN 由许多网络组成,这些网络如下: + +* **Stack-I GAN** :文本编码器,条件增强网络,生成器网络,鉴别器网络,嵌入压缩器网络 +* **Stack-II GAN** :t ext 编码器,条件增强网络,生成器网络,鉴别器网络,嵌入压缩器网络 + +![](img/6f2f1522-849c-4ee4-9c08-57884ea1b6b1.png) + +Source: arXiv:1612.03242 [cs.CV] + +上图是不言自明的。 它代表了 StackGAN 网络的两个阶段。 如您所见,第一步是生成尺寸为`64x64`的图像。 然后,第二阶段拍摄这些低分辨率图像,并生成尺寸为`256x256`的高分辨率图像。 在接下来的几节中,我们将探讨 StackGAN 网络中的不同组件。 但是,在进行此操作之前,让我们熟悉本章中使用的符号: + +| **表示法** | **说明** | +| Ť | 这是真实数据分发的文本描述。 | +| 与 | 这是来自高斯分布的随机采样噪声向量。 | +| ![](img/e32e6af2-66f3-4230-a504-a531da126b72.png) | 这是预训练编码器生成的给定文本描述的文本嵌入。 | +| ![](img/69081f49-8ca4-4a69-a42c-b82ddc2285ca.png) | 此文本条件变量是从分布中采样的高斯条件变量。 它抓住了的不同含义。 | +| ![](img/a72a5335-9a86-417e-a15d-4d7b491b447d.png) | 这是条件高斯分布。 | +| N(0,I) | 这是正态分布。 | +| ![](img/d857d518-1a01-4b38-a794-c85183029413.png) | 这是一个对角协方差矩阵。 | +| 数据 | 这是真正的数据分配。 | +| 个人电脑 | 这就是高斯分布。 | +| D1 | 这是第一阶段的鉴别器。 | +| G1 | 这是 Stage-I 生成器。 | +| D2 | 这是第二阶段的鉴别器。 | +| G2 | 这是 Stage-II 生成器。 | +| N2 | 这些是随机噪声变量的尺寸。 | +| ![](img/c9a26056-2b9d-44f1-bc1b-4fbb29cfd2af.png) | 这些是 Stack-II GAN 的高斯潜在变量。 | + + + + + +# 文字编码器网络 + + + +文本编码器网络的唯一目的是将文本描述(t)转换为文本嵌入(![](img/b202e2a1-36da-4851-8a99-1cf84585df1d.png) )。 在本章中,我们不会训练文本编码器网络。 我们将使用经过预训练的文本嵌入。 按照*数据准备*部分中给出的步骤下载预训练的文本嵌入。 如果您想训练自己的文本编码器,请参阅*学习细粒度视觉描述的深度表示*,该文章可在 [中找到 https://arxiv.org/pdf/ 1605.05395.pdf ](https://arxiv.org/pdf/1605.05395.pdf) 。 文本编码器网络将句子编码为 1,024 维文本嵌入。 文本编码器网络在两个阶段都是相同的。 + + + + + +# 条件增强块 + + + +**条件增强**( **CA** )网络从表示为![](img/cb774d54-76ee-4c94-89a7-94e8b16c4493.png)的分布中采样随机潜在变量![](img/d86d468c-b92c-41c9-83f1-75baf727847c.png)。 我们将在后面的部分中详细了解这种分布。 添加 CA 块有很多优点,如下所示: + +* 它为网络增加了随机性。 +* 通过捕获具有各种姿势和外观的各种对象,它使生成器网络变得强大。 +* 它产生更多的图像-文本对。 使用大量的图文对,我们可以训练一个可以处理干扰的健壮网络。 + + + + + +# 获取条件增强变量 + + + +从文本编码器获得文本嵌入(![](img/8ed3de7f-a1df-4723-9600-3ccffc5f16e4.png))后,将它们馈送到完全连接的层以生成诸如平均值![](img/a45ac6e1-28df-432c-a28c-ea1a2251e320.png)和标准偏差![](img/3920b802-2eb7-4c4c-a835-754920e124a5.png)的值。然后使用这些值 通过将![](img/3df2dc52-a6ff-4461-aedd-6fc37114d111.png)放置在矩阵(![](img/aacb3462-7c27-4168-a445-52c2b2553c59.png))的对角线上来创建对角协方差矩阵。 最后,我们使用![](img/29d32b85-e867-4d9e-9e51-814681394bb9.png)和![](img/331b771d-6468-4640-a8fc-df5075645e70.png)创建高斯分布,可以表示为: + +![](img/bcc5921a-1e7e-497a-b134-61a55d04cecb.png) + +然后,我们从刚创建的高斯分布中采样![](img/464b155a-0a20-4761-bb03-4f4b36f72554.png)。 计算![](img/9261c4cf-3f8a-464b-adec-1aec0c855008.png)的公式如下: + +![](img/5bbaa568-0f1e-431b-b8d1-f8bacae72282.png) + +前面的方程式很不言自明。 为了对![](img/464b155a-0a20-4761-bb03-4f4b36f72554.png)进行采样,我们首先将![](img/8b2ac2fe-e616-4cfd-89e4-2a876127bef7.png)和进行元素逐个相乘,然后将输出添加到![](img/83c4f3c0-a5f7-48c4-b0b3-767e6017e43c.png)中。 我们将在 * StackGAN * 部分的 Keras 实现中详细介绍如何计算 CA 变量![](img/464b155a-0a20-4761-bb03-4f4b36f72554.png)。 + + + + + +# 阶段 I + + + +StackGAN 网络的主要组成部分是生成器网络和鉴别器网络。 在本节中,我们将详细探讨这两个网络。 + + + + + +# 发电机网络 + + + +Stage-I 生成器网络是具有几个上采样层的深度卷积神经网络。 g 生成器网络是 CGAN ,其条件是处于![](img/10c2247d-25a3-41dc-9b01-e117a05f6b1d.png) 和随机变量 [ ![](img/f17e1ecf-417e-4b78-ad5c-6e5a25ae8e26.png)。 生成器网络采用高斯条件变量![](img/10c2247d-25a3-41dc-9b01-e117a05f6b1d.png)和随机噪声变量 *z* ,并生成尺寸为`64x64x3` 的图像。 生成的低分辨率图像可能具有原始形状和基本颜色,但会存在各种缺陷。 这里, *z* 是从维数为![](img/79328706-d7f6-47a5-b93c-7c1c308af456.png)的高斯分布![](img/b3edb215-2b94-4a1a-92f6-c97f1c779948.png)采样的随机噪声变量。 生成器网络生成的图像可以表示为![](img/7846fcd5-c526-476d-bde4-e03e5d0980d0.png)。 让我们看一下生成器网络的体系结构,如以下屏幕截图所示: + +![](img/04c840a1-a6ae-4b3d-a141-ed58f9dae21b.png) + +![](img/c4ba3b4c-3d2b-4156-8e94-aea9a2e8c781.png) + +The architecture of the generator network in Stage-I + +如您所见,生成器网络包含几个卷积层,其中每个卷积层后跟一个批处理规范化层或一个激活层。 它的唯一目的是生成尺寸为`64x64x3`的图像。 现在我们有了生成器网络的基本概念,让我们探索鉴别器网络。 + + + + + +# 鉴别网络 + + + +类似于生成器网络,鉴别器网络是一个深度卷积神经网络,其中包含一系列下采样卷积层。 下采样层从图像生成特征图,无论它们是真实数据分布![](img/c534d4df-c504-4ff3-83b6-61db0e52035b.png)的真实图像还是生成器网络生成的图像。 然后,我们将特征映射连接到文本嵌入。 我们使用压缩和空间复制将嵌入的文本转换为连接所需的格式。 空间压缩和复制包括一个完全连接的层,该层用于压缩嵌入到![](img/31009ecb-449c-41d6-81c4-dd1d976fb6f2.png)维输出的文本,然后通过空间复制将其转换为![](img/42f86eb5-0593-4986-8a38-397be9b66e56.png)维张量。 然后将特征图以及压缩的和空间复制的文本嵌入沿通道维级联。 最后,我们具有一个节点的完全连接层,该层用于二进制分类。 让我们看一下鉴别器网络的架构,如以下屏幕截图所示: + +![](img/fc0175ae-38b7-42aa-99ec-1bdbe36a8edf.png) + +![](img/3fd25fcc-55fa-4bce-b1d7-4f4d801ba076.png) + +The architecture of the Stage-I discriminator network + +如您所见,生成器网络包含几个卷积层。 鉴别器网络的唯一目的是区分来自真实数据分布的图像和生成器网络生成的图像。 现在,我们来看看 StackGAN 的 Stage I 中使用的损耗。 + + + + + +# StackGAN 第一阶段的损失 + + + +StackGAN 的阶段 I 中使用了两个损失,如下所示: + +* 发电机损耗 +* 鉴别器损失 + +鉴别符损失![](img/0a355bdc-0c6f-4cd3-a62e-4b46a301c9f6.png)可以表示为: + +![](img/01fddc7c-38fb-4dc6-ac3b-3c73b448e65a.png) + +公式前面的很不言自明。 它代表了鉴别器网络的损失函数,其中两个网络都以文本嵌入为条件。 + +发电机损耗![](img/b46d75ce-88c8-480a-ba3d-572b93f4c717.png)可以表示为: + +![](img/6567c751-fa0d-4009-b4bd-b864d4ecab7c.png) + +公式前面的也很容易解释。 它代表了生成器网络的损失函数,其中两个网络都以文本嵌入为条件。 此外,它还包括损失函数的 KL 发散项。 + + + + + +# 二代 + + + +Stage-II StackGAN 的主要组件是发电机网络和鉴别器网络。 生成器网络是编码器-解码器类型的网络。 在此阶段不使用随机噪声 *z* ,假设 ![](img/94999cb2-2d49-4263-b900-3deef877ccd1.png)已保留了随机性,其中![](img/94999cb2-2d49-4263-b900-3deef877ccd1.png)是发生器网络生成的图像 第一阶段 + +我们首先使用预训练的文本编码器生成高斯条件变量 ![](img/308050a3-9d10-4f05-9e8e-d1e4e07bbb93.png) 。 这将生成嵌入 ![](img/b7167483-2ee2-469e-af9f-278f36699c79.png) 的相同文本。 第一阶段和第二阶段条件增强具有不同的完全连接层,用于生成不同的均值和标准 偏差。 这意味着 Stage-II GAN 学会了在文本嵌入中捕获有用的信息,而这些信息被 Stage-I GAN 省略了。 + +Stack-I GAN 生成的图像存在的问题是,它们可能缺少生动的对象部分,它们可能包含形状失真,并且它们可能会忽略对于生成逼真的图像非常重要的重要细节。 Stack-II GAN 建立在 Stack-I GAN 的输出上。 Stack-II GAN 取决于 Stack-I GAN 生成的低分辨率图像和文本描述。 它通过校正缺陷产生高分辨率的图像。 + + + + + +# 发电机网络 + + + +生成器网络还是深层卷积神经网络。 第 I 阶段的结果是低分辨率图像,它经过几个下采样层以生成图像特征。 然后,将图像特征和文本条件变量沿通道尺寸连接在一起。 之后,将级联张量馈入一些残差块,这些残差块学习跨图像和文本特征的多峰表示。 最后,最后一个操作的输出被馈送到一组上采样层,这些上采样层生成尺寸为`256x256x3`的高分辨率图像。 让我们看一下生成器网络的体系结构,如下图所示: + +![](img/028e54b4-8184-45bd-8edf-abc7b7e8de97.png) + +![](img/cd2083c4-1490-4eef-a931-c924f6366e86.png) + +![](img/f0da2f12-07bd-477a-97a6-dc21589a0b1f.png) + +![](img/6b5db762-e527-4194-85ab-83cc4ff9a1b5.png) + +![](img/e196749b-1617-4fcb-bbc5-14c392c944f1.png) + +The architecture of the Stage-II generator + +该生成器网络的唯一目的是从低分辨率图像生成高分辨率图像。 低分辨率图像首先由 Stage-I 的生成器网络生成,然后馈送到 Stage-II 的生成器网络,后者生成高分辨率图像。 + + + + + +# 鉴别网络 + + + +类似于生成器网络,鉴别器网络是一个深度卷积神经网络,并且包含额外的下采样层,因为图像的大小比 Stage-I 中的鉴别器网络大。 鉴别器是一个可识别匹配的鉴别器(有关更多信息,请参见以下链接: [https://arxiv.org/pdf/1605.05396.pdf](https://arxiv.org/pdf/1605.05396.pdf) ),它使我们可以更好地实现 图片和条件文字。 在训练期间,鉴别器将真实图像及其对应的文本描述作为正样本对,而负样本对则由两组组成。 第一组是具有不匹配的文本嵌入的真实图像,而第二组是具有相应的文本嵌入的合成图像。 让我们看一下鉴别器网络的体系结构,如下图所示: + +![](img/a8c04bff-bd5a-4350-8b08-bd2ef6ae193b.png) + +![](img/73371faa-5c7e-475f-a169-4229e9d04b24.png) + +![](img/42eb238c-1bba-415e-b416-469f04e29932.png) + +The architecture of the Stage-II discriminator network. + +关于鉴别器网络架构的 M 矿石信息可以在 StackGAN 部分的 *Keras 实现中找到。* + + + + + +# StackGAN 第二阶段的损失 + + + +与任何其他 GAN 相似,Stack-II GAN 中的生成器 *G* 和鉴别器 *D* 也可以通过最大化鉴别器的损耗并将生成器网络的损耗最小化来训练 。 + +发电机损耗![](img/d0ecd579-7d64-4b7c-a9c7-666c14c0e1cc.png)可以表示为: + +![](img/24684f0c-814c-4b33-bfe7-c177cfe19a46.png) + +前面的方程式非常不言自明。 它代表了鉴别器网络的损失函数,其中两个网络都以文本嵌入为条件。 一个主要区别是生成器网络以![](img/2fc3c44d-59b8-4da3-b9b7-b5daabb95bb6.png)和![](img/3720bf75-1dba-477b-b833-865282482a25.png)作为输入,其中![](img/134a6916-3b21-48a7-88ee-a115d848fbe5.png)是 Stage-I 生成的图像,![](img/885a0848-1055-4c3b-976f-11f36d73232b.png)是 CA 变量。 + +鉴别符损失![](img/a1fce921-d434-45e3-8f15-472f02dcebc5.png)可以表示为: + +![](img/8d494e68-1020-471d-91de-e2d6acf9552c.png) + +前面的方程式也很容易解释。 它代表了生成器网络的损失函数,其中两个网络都以文本嵌入为条件。 它还包括对损失函数的 **Kullback-Leibler** ( **KL** )发散项。 + + + + + +# 设置项目 + + + +如果尚未使用所有章节的完整代码克隆存储库,请立即克隆存储库。 下载的代码有一个名为`Chapter06`的目录,其中包含本章的全部代码。 执行以下命令来设置项目: + +1. 首先,导航到父目录,如下所示: + +```py +cd Generative-Adversarial-Networks-Projects +``` + +2. 现在,将目录从当前目录更改为 `Chapter06`: + +```py +cd Chapter06 +``` + +3. 接下来,为该项目创建一个 Python 虚拟环境: + +```py +virtualenv venv +virtualenv venv -p python3 # Create a virtual environment using + python3 interpreter +virtualenv venv -p python2 # Create a virtual environment using + python2 interpreter +``` + +我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。 + +4. 激活新创建的虚拟环境: + +```py +source venv/bin/activate +``` + +激活虚拟环境后,将在其中执行所有其他命令。 + +5. 通过执行以下命令,安装`requirements.txt` 文件中提供的所有库: + +```py +pip install -r requirements.txt +``` + +您可以参考 `README.md` 文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。 + +在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一节中,我们将处理数据集。 + + + + + +# 资料准备 + + + +在本节中,我们将使用 CUB 数据集,该数据集是不同鸟类的图像数据集,可以在以下链接中找到: [http://www.vision.caltech.edu/visipedia/CUB-200 -2011.html](http://www.vision.caltech.edu/visipedia/CUB-200-2011.html) 。 CUB 数据集包含 11,788 个高分辨率图像。 我们还将需要 char-CNN-RNN 文本嵌入,可以在以下链接中找到它们: [https://drive.google.com/open?id=0B3y_msrWZaXLT1BZdVdycDY5TEE](https://drive.google.com/open?id=0B3y_msrWZaXLT1BZdVdycDY5TEE) 。 这些是预训练的文本嵌入。 按照以下几节中给出的说明下载和提取数据集。 + + + + + +# 下载数据集 + + + +可以从 [http://www.vision.caltech.edu/visipedia/CUB-200-2011.html](http://www.vision.caltech.edu/visipedia/CUB-200-2011.html) 手动下载`CUB`数据集。 另外,我们可以执行以下命令来下载数据集: + +```py +wget http://www.vision.caltech.edu/visipedia-data/CUB-200-2011/CUB_200_2011.tgz +``` + +下载数据集后,我们可以将其提取并放在`data/birds/`目录中。 + +从以下链接下载 char-CNN-RNN 嵌入: [https://drive.google.com/open?id=0B3y_msrWZaXLT1BZdVdycDY5TEE](https://drive.google.com/open?id=0B3y_msrWZaXLT1BZdVdycDY5TEE) 。 + + + + + +# 提取数据集 + + + +`CUB`数据集是压缩文件,需要提取。 使用以下命令提取`CUB`数据集: + +```py +tar -xvzf CUB_200_2011.tgz +``` + +使用以下命令提取 char-CNN-RNN 嵌入: + +```py +unzip birds.zip +``` + +最后,将`CUB_200_2011`放在`data/birds`目录中。 现在可以使用我们的数据集了。 + + + + + +# 探索数据集 + + + +CUB 数据集总共包含 200 种不同鸟类的 11,788 张图像。 CUB 数据集中的图像包括以下内容: + +![](img/65c94f59-5ff4-47de-91ea-301d83e34811.png) + +这四张图片显示了黑脚信天翁,长尾小鹦鹉 Auklet,bobolink 和 B randt 的 mor。 + +在设计网络之前,了解数据集非常重要。 确保您仔细浏览 CUB 数据集中的图像。 + + + + + +# StackGAN 的 Keras 实现 + + + +StackGAN 的 Keras 实现分为两部分:第一阶段和第二阶段。 我们将在以下各节中实现这些阶段。 + + + + + +# 阶段 I + + + +Stage-I StackGAN 包含生成器网络和鉴别器网络。 它还具有一个文本编码器网络和一个条件增强网络(CA 网络),下面将对此进行详细说明。 生成器网络获取文本条件变量(![](img/5636ece2-f10e-4485-bd3b-527ba2c14606.png))以及噪声矢量( *x* )。 经过一组上采样层后,它会生成尺寸为`64x64x3`的低分辨率图像。 鉴别器网络拍摄此低分辨率图像,并尝试识别图像是真实的还是伪造的。 生成器网络是具有一组下采样层的网络,其后是连接,然后是分类层。 在以下各节中,我们将详细探讨 StackGAN 的体系结构。 + +Stage-I StackGAN 网络中使用的网络如下: + +* 文本编码器网络 +* 条件增强网络 +* 发电机网络 +* 鉴别网络 + +但是,在开始编写实现之前,请创建一个 Python 文件 `main.py` ,然后按如下所示导入基本模块: + +```py +import os +import pickle +import random +import time + +import PIL +import numpy as np +import pandas as pd +import tensorflow as tf +from PIL import Image +from keras import Input, Model +from keras import backend as K +from keras.callbacks import TensorBoard +from keras.layers import Dense, LeakyReLU, BatchNormalization, ReLU, Reshape, UpSampling2D, Conv2D, Activation, \ + concatenate, Flatten, Lambda, Concatenate +from keras.optimizers import Adam +from keras_preprocessing.image import ImageDataGenerator +from matplotlib import pyplot as plt +``` + + + + + +# 文字编码器网络 + + + +文本编码器网络的唯一目的是将文本描述(t)转换为文本嵌入(![](img/bd66dab8-77b6-42b8-bda8-3dd03f9285e8.png))。 该网络将句子编码为 1,024 维文本嵌入。 我们已经下载了预训练的 char-CNN-RNN 文本嵌入。 我们将使用它们来训练我们的网络。 + + + + + +# 条件增强网络 + + + +CA 网络的目的是将文本嵌入矢量(![](img/de8c23e2-ee05-44ea-876e-adfe515c5b5b.png))转换为条件潜在变量(![](img/69b142c0-77c5-4a16-b698-d93249bc30c4.png))。 在 CA 网络中,文本嵌入矢量穿过具有非线性的完全连接层,从而产生均值![](img/39b79c42-5da0-4eb5-92c4-146d419591ac.png)和对角协方差矩阵![](img/1032f91a-c420-45d3-846b-6d2f4add232c.png)。 + +以下代码显示了如何创建 CA 网络: + +1. 首先创建一个具有 256 个节点的完整连接层,并使用`LeakyReLU`作为激活功能: + +```py +input_layer = Input(shape=(1024,)) +x = Dense(256)(input_layer) +mean_logsigma = LeakyReLU(alpha=0.2)(x) +``` + +输入形状为(`batch_size`,`1024`),输出形状为(`batch_size`,`256`)。 + +2. 接下来,将`mean_logsigma`分为`mean`和`log_sigma`张量: + +```py +mean = x[:, :128] +log_sigma = x[:, 128:] +``` + +此操作将创建两个张量的张量(`batch_size`,`128`)和(`batch_size`和`128`)。 + +3. 接下来,使用以下代码计算文本条件变量。 有关如何生成文本条件变量的更多信息,请参见 StackGAN 小节的*体系结构中的*条件增强(CA)块*部分:* + +```py +stddev = K.exp(log_sigma) +epsilon = K.random_normal(shape=K.constant((mean.shape[1], ), dtype='int32')) +c = stddev * epsilon + mean +``` + +这将产生一个张量为(`batch_size`,`128`)的张量,这是我们的文本条件变量。 CA 网络的完整代码如下: + +```py +def generate_c(x): + mean = x[:, :128] + log_sigma = x[:, 128:] + + stddev = K.exp(log_sigma) + epsilon = K.random_normal(shape=K.constant((mean.shape[1], ), dtype='int32')) + c = stddev * epsilon + mean + + return c +``` + +条件块的整个代码如下所示: + +```py +def build_ca_model(): + input_layer = Input(shape=(1024,)) + x = Dense(256)(input_layer) + mean_logsigma = LeakyReLU(alpha=0.2)(x) + + c = Lambda(generate_c)(mean_logsigma) + return Model(inputs=[input_layer], outputs=[c]) +``` + +在代码前面的中,`build_ca_model()`方法创建一个 Keras 模型,其中具有一个完全连接的层并且以`LeakyReLU`作为激活函数。 + + + + + +# 发电机网络 + + + +生成器网络是**条件生成对抗网络**( **CGAN** )。 我们将要创建的生成器网络以 text-condition 变量为条件。 它采用从潜在空间采样的随机噪声矢量,并生成形状为`64x64x3`的图像。 + +让我们从编写生成器网络的代码开始: + +1. 首先创建一个输入层,以将输入(噪声变量)馈送到网络: + +```py +input_layer2 = Input(shape=(100, )) +``` + +2. 接下来,将文本条件变量与 noise 变量沿维度 1 串联: + +```py +gen_input = Concatenate(axis=1)([c, input_layer2]) +``` + +此处,`c`是文本条件变量。 在上一步中,我们编写了代码来生成文本条件变量,`gen_input`将成为我们对生成器网络的输入。 + +3. 接下来,创建具有`128*8*4*4 (16,384)`节点的`dense layer`和`ReLU`激活层,如下所示: + +```py +x = Dense(128 * 8 * 4 * 4, use_bias=False)(gen_input) +x = ReLU()(x) +``` + +4. 之后,将最后一层的输出重塑为尺寸为`(batch_size, 4, 4, 128*8)`的张量: + +```py +x = Reshape((4, 4, 128 * 8), input_shape=(128 * 8 * 4 * 4,))(x) +``` + +该操作将二维张量整形为二维张量。 + +5. 接下来,创建一个二维向上采样卷积块。 该块包含一个上采样层,一个卷积层和一个批归一化层。 批量归一化后,使用`ReLU`作为此块的激活功能: + +```py +x = UpSampling2D(size=(2, 2))(x) +x = Conv2D(512, kernel_size=3, padding="same", strides=1, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) +``` + +6. 之后,再创建三个 3D 上采样卷积块,如下所示: + +```py +x = UpSampling2D(size=(2, 2))(x) +x = Conv2D(256, kernel_size=3, padding="same", strides=1, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) + +x = UpSampling2D(size=(2, 2))(x) +x = Conv2D(128, kernel_size=3, padding="same", strides=1, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) + +x = UpSampling2D(size=(2, 2))(x) +x = Conv2D(64, kernel_size=3, padding="same", strides=1, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) +``` + +7. 最后,创建一个卷积层,它将生成一个低分辨率图像: + +```py +x = Conv2D(3, kernel_size=3, padding="same", strides=1, use_bias=False)(x) +x = Activation(activation='tanh')(x) +``` + +8. 现在,通过如下指定网络的输入和输出来创建 Keras 模型:: + +```py +stage1_gen = Model(inputs=[input_layer, input_layer2], outputs=[x, mean_logsigma]) +``` + +这里, *x* 将是模型的输出,其形状将是(batch_size,64,64,3)。 + +生成器网络的整个代码如下所示: + +```py +def build_stage1_generator(): + """ + Builds a generator model """ input_layer = Input(shape=(1024,)) + x = Dense(256)(input_layer) + mean_logsigma = LeakyReLU(alpha=0.2)(x) + + c = Lambda(generate_c)(mean_logsigma) + + input_layer2 = Input(shape=(100,)) + + gen_input = Concatenate(axis=1)([c, input_layer2]) + + x = Dense(128 * 8 * 4 * 4, use_bias=False)(gen_input) + x = ReLU()(x) + + x = Reshape((4, 4, 128 * 8), input_shape=(128 * 8 * 4 * 4,))(x) + + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(512, kernel_size=3, padding="same", strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(256, kernel_size=3, padding="same", strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(128, kernel_size=3, padding="same", strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(64, kernel_size=3, padding="same", strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + + x = Conv2D(3, kernel_size=3, padding="same", strides=1, use_bias=False)(x) + x = Activation(activation='tanh')(x) + + stage1_gen = Model(inputs=[input_layer, input_layer2], outputs=[x, mean_logsigma]) + return stage1_gen +``` + +此模型在单个网络中同时包含 CA 网络和生成器网络。 它需要两个输入并返回两个输出。 输入是文本嵌入和噪声变量,而输出是生成的图像和`mean_logsigma`。 + +我们现在已经成功实现了发电机网络。 让我们进入鉴别器网络。 + + + + + +# 鉴别网络 + + + +鉴别器网络是分类器网络。 它包含一组下采样层,并对给定图像是真实的还是伪造的进行分类。 + +让我们从编写网络代码开始: + +1. 首先创建一个输入层以将输入提供给网络: + +```py +input_layer = Input(shape=(64, 64, 3)) +``` + +2. 接下来,添加具有以下参数的二维卷积层: + * **过滤器** = `64` + * **内核大小** = `(4, 4)` + * **大步前进** = `2` + * **填充** = `'same'` + * **使用偏差** = `False` + * **用`alpha=0.2`激活** = `LeakyReLU` + +```py +stage1_dis = Conv2D(64, (4, 4), + padding='same', strides=2, + input_shape=(64, 64, 3), use_bias=False)(input_layer) +stage1_dis = LeakyReLU(alpha=0.2)(stage1_dis) +``` + +3. 之后,添加两个卷积层,每个卷积层之后是一个批处理归一化层和一个`LeakyReLU`激活函数,具有以下参数: + * **过滤器:** `128` + * **内核大小:** `(4, 4)` + * **大步前进** = `2` + * **填充** = `'same'` + * **使用偏差** = `False` + * **用`alpha=0.2`激活** = `LeakyReLU` + +```py +x = Conv2D(128, (4, 4), padding='same', strides=2, use_bias=False)(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) +``` + +4. 接下来,再添加一个 2D 卷积层,然后添加批处理规范化层和`LeakyReLU`激活函数,并具有以下参数: + * **过滤器:** `256` + * **内核大小:** `(4, 4)` + * **大步前进** = `2` + * **填充** = `'same'` + * **使用偏差** = `False` + * **用`alpha=0.2`激活** = `LeakyReLU` + +```py +x = Conv2D(256, (4, 4), padding='same', strides=2, use_bias=False)(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) +``` + +5. 之后,再添加一个 2D 卷积层,然后添加批处理规范化层和 LeakyReLU 激活函数,并具有以下参数: + * **过滤器:** `512` + * **内核大小:** `(4, 4)` + * **大步前进** = `2` + * **填充** = `'same'` + * **使用偏差** = `False` + * **激活** = `LeakyReLU` 和 `alpha=0.2` + +```py +x = Conv2D(512, (4, 4), padding='same', strides=2, use_bias=False)(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) +``` + +6. 然后,创建另一个输入层以接收空间复制和压缩的文本嵌入: + +```py +input_layer2 = Input(shape=(4, 4, 128)) +``` + +7. 添加一个连接层以连接`x`和`input_layer2`: + +```py +merged_input = concatenate([x, input_layer2]) +``` + +8. 之后,添加另一个 2D 卷积层,然后添加批处理归一化层,并使用以下参数将`LeakyReLU`作为激活函数: + * **过滤器** = `512` + * **内核大小** = `1` + * **大步前进** = `1` + * **填充** = `'same'` + * **批量标准化** =是 + * **用`alpha 0.2`激活** = `LeakyReLU` + +```py +x2 = Conv2D(64 * 8, kernel_size=1, + padding="same", strides=1)(merged_input) +x2 = BatchNormalization()(x2) +x2 = LeakyReLU(alpha=0.2)(x2) +``` + +9. 现在,拉平张量并添加一个密集的分类器层: + +```py +# Flatten the tensor +x2 = Flatten()(x2) + +# Classifier layer +x2 = Dense(1)(x2) +x2 = Activation('sigmoid')(x2) +``` + +10. 最后,创建 Keras 模型: + +```py +stage1_dis = Model(inputs=[input_layer, input_layer2], outputs=[x2]) +``` + +该模型输出输入图像属于真实类别或伪类别的概率。 鉴别器网络的完整代码如下: + +```py +def build_stage1_discriminator(): input_layer = Input(shape=(64, 64, 3)) + + x = Conv2D(64, (4, 4), + padding='same', strides=2, + input_shape=(64, 64, 3), use_bias=False)(input_layer) + x = LeakyReLU(alpha=0.2)(x) + + x = Conv2D(128, (4, 4), padding='same', strides=2, use_bias=False)(x) + x = BatchNormalization()(x) + x = LeakyReLU(alpha=0.2)(x) + + x = Conv2D(256, (4, 4), padding='same', strides=2, use_bias=False)(x) + x = BatchNormalization()(x) + x = LeakyReLU(alpha=0.2)(x) + + x = Conv2D(512, (4, 4), padding='same', strides=2, use_bias=False)(x) + x = BatchNormalization()(x) + x = LeakyReLU(alpha=0.2)(x) + + input_layer2 = Input(shape=(4, 4, 128)) + + merged_input = concatenate([x, input_layer2]) + + x2 = Conv2D(64 * 8, kernel_size=1, + padding="same", strides=1)(merged_input) + x2 = BatchNormalization()(x2) + x2 = LeakyReLU(alpha=0.2)(x2) + x2 = Flatten()(x2) + x2 = Dense(1)(x2) + x2 = Activation('sigmoid')(x2) + + stage1_dis = Model(inputs=[input_layer, input_layer2], outputs=[x2]) + return stage1_dis +``` + +该模型有两个输入和一个输出。 输入是低分辨率图像和压缩文本嵌入,而输出是概率。 既然我们已经成功地编写了鉴别网络的实现,那么让我们创建对抗网络。 + + + + + +# 对抗模型 + + + +要创建对抗模型,请同时使用生成器网络和鉴别器网络,并创建一个新的 Keras 模型。 + +1. 首先创建三个输入层以将输入馈送到网络: + +```py +def build_adversarial_model(gen_model, dis_model): + input_layer = Input(shape=(1024,)) + input_layer2 = Input(shape=(100,)) + input_layer3 = Input(shape=(4, 4, 128)) +``` + +2. 然后,使用生成器网络生成低分辨率图像: + +```py + # Get output of the generator model + x, mean_logsigma = gen_model([input_layer, input_layer2]) + + # Make discriminator trainable false + dis_model.trainable = False +``` + +3. 接下来,使用鉴别器网络获得概率: + +```py + # Get output of the discriminator models valid = dis_model([x, input_layer3]) +``` + +4. 最后,创建对抗模型,获取三个输入并返回两个输出。 + +```py + model = Model(inputs=[input_layer, input_layer2, input_layer3], outputs=[valid, mean_logsigma]) + return model +``` + +现在,我们的对抗模型已经准备就绪。 这种对抗模型是一种端到端的可训练模型。 在本节中,我们研究了 StackGAN 模型的 Stage-I 中涉及的网络。 在下一部分中,我们将研究 StackGAN 的第二阶段所涉及的网络的实现。 + + + + + +# 第二阶段 + + + +Stage-II StackGAN 与 Stage-I StackGAN 略有不同。 生成器模型的输入是条件变量(![](img/8bd40bf0-5919-4004-920c-9abffb3fe400.png))和生成器网络在阶段 I 中生成的低分辨率图像。 + +它包含五个组成部分: + +* 文字编码器 +* 条件增强网络 +* 下采样块 +* 残留块 +* 上采样块 + +文本编码器和 CA 网络与之前在 Stage-I 部分中使用的相似。 现在,我们将介绍发生器网络的三个组件,分别是下采样块,残差块和上采样块。 + + + + + +# 发电机网络 + + + +发电机网络由三个不同的模块组成。 我们将逐一编写每个模块的代码。 让我们从下采样模块开始。 + + + + + +# 下采样块 + + + +该块从 Stage-1 的生成器获取尺寸为`64x64x3`的低分辨率图像,并将其下采样以生成形状为`16x16x512`的张量。 图像经过一系列 2D 卷积块。 + +在本节中,我们将为降采样模块编写实现。 + +1. 首先创建第一个下采样块。 该块包含一个以`ReLU`作为激活函数的 2D 卷积层。 在应用 2D 卷积之前,请在各侧用零填充输入。 该块中不同层的配置如下: + * **填充大小:** `(1, 1)` + * **过滤器:** `128` + * **内核大小:** `(3, 3)` + * **大步走:** ``1`` + * **激活:** `ReLU` + +```py +x = ZeroPadding2D(padding=(1, 1))(input_lr_images) +x = Conv2D(128, kernel_size=(3, 3), strides=1, use_bias=False)(x) +x = ReLU()(x) +``` + +2. 接下来,添加具有以下配置的第二个卷积块: + * **填充大小:** `(1, 1)` + * **过滤器:** `256` + * **内核大小:** `(4, 4)` + * **大步走过:** `2` + * **批量标准化:**是 + * **激活:** `ReLU` + +```py +x = ZeroPadding2D(padding=(1, 1))(x) +x = Conv2D(256, kernel_size=(4, 4), strides=2, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) +``` + +3. 之后,添加另一个具有以下配置的卷积块: + * **填充大小:** `(1, 1)` + * **过滤器:** `512` + * **内核大小:** `(4, 4)` + * **大步走过:** `2` + * **批量标准化:** 是 + * **激活:** `ReLU` + +```py +x = ZeroPadding2D(padding=(1, 1))(x) +x = Conv2D(512, kernel_size=(4, 4), strides=2, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) +``` + +下采样块生成形状为`16x16x512`的张量。 之后,我们有一系列残差块。 在将该张量馈送到残差块之前,我们需要将其连接到文本条件变量。 执行此操作的代码如下: + +```py +# This block will extend the text conditioning variable and concatenate it to the encoded images tensor. +def joint_block(inputs): + c = inputs[0] + x = inputs[1] + + c = K.expand_dims(c, axis=1) + c = K.expand_dims(c, axis=1) + c = K.tile(c, [1, 16, 16, 1]) + return K.concatenate([c, x], axis=3) +# This is the lambda layer which we will add to the generator network +c_code = Lambda(joint_block)([c, x]) +``` + +在此,`c`的形状为(batch_size,228),`x`的形状为(batch_size,16、16、512)。 `c_code`的形状为(batch_size,640)。 + + + + + +# 剩余块 + + + +剩余块包含两个 2D 卷积层,每个层之后是批处理归一化层和一个激活函数。 + +1. 让我们定义残留块。 此代码完全描述了剩余块: + +```py +def residual_block(input): + """ + Residual block in the generator network :return: + """ x = Conv2D(128 * 4, kernel_size=(3, 3), padding='same', strides=1)(input) + x = BatchNormalization()(x) + x = ReLU()(x) + + x = Conv2D(128 * 4, kernel_size=(3, 3), strides=1, padding='same')(x) + x = BatchNormalization()(x) + + x = add([x, input]) + x = ReLU()(x) + + return x +``` + +初始输入被添加到第二个 2D 卷积层的输出中。 结果张量将是块的输出。 + +2. 接下来,添加具有以下超参数的 2D 卷积块: + * **填充大小:** `(1, 1)` + * **过滤器:** `512` + * **内核大小:** `(3, 3)` + * **大步走过:** `1` + * **批量归一化:** `Yes` + * **激活:** `ReLU` + +```py +x = ZeroPadding2D(padding=(1, 1))(c_code) +x = Conv2D(512, kernel_size=(3, 3), strides=1, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) +``` + +3. 之后,依次添加四个残差块: + +```py +x = residual_block(x) +x = residual_block(x) +x = residual_block(x) +x = residual_block(x) +``` + +上采样块将从残余块接收该输出张量。 让我们为升采样块编写代码。 + + + + + +# 上采样块 + + + +上采样块包含可提高图像空间分辨率并生成尺寸`256x256x3`的高分辨率图像的图层。 + +让我们为升采样块编写代码: + +1. 首先,添加一个包含 2D 上采样层,2D 卷积层,批归一化和激活函数的上采样块。 块中使用的不同参数如下: + * **上采样大小:** `(2, 2)` + * **过滤器:** `512` + * **内核大小:** `3` + * **填充:** `"same"` + * **大步走:** `1` + * **批量标准化:** `Yes` + * **激活:** `ReLU` + +```py +x = UpSampling2D(size=(2, 2))(x) +x = Conv2D(512, kernel_size=3, padding="same", strides=1, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) +``` + +2. 接下来,再添加三个上采样块。 该块中使用的超参数可以很容易地从下面给出的代码中推断出来: + +```py +x = UpSampling2D(size=(2, 2))(x) +x = Conv2D(256, kernel_size=3, padding="same", strides=1, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) + +x = UpSampling2D(size=(2, 2))(x) +x = Conv2D(128, kernel_size=3, padding="same", strides=1, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) + +x = UpSampling2D(size=(2, 2))(x) +x = Conv2D(64, kernel_size=3, padding="same", strides=1, use_bias=False)(x) +x = BatchNormalization()(x) +x = ReLU()(x) +``` + +3. 添加最后的卷积层。 该层是最后一层,它负责生成高分辨率图像。 + +```py +x = Conv2D(3, kernel_size=3, padding="same", strides=1, use_bias=False)(x) +x = Activation('tanh')(x) +``` + +最后,使用部分前面的创建生成器模型: + +```py +model = Model(inputs=[input_layer, input_lr_images], outputs=[x, mean_logsigma]) +``` + +现在,我们已经准备好生成器模型,并使用该模型来生成高分辨率图像。 以下是用于生成器网络的完整代码,用于清晰 : + +```py +def build_stage2_generator(): + """ + Create a generator network for Stage-II StackGAN + """ # 1\. CA Augementation Network + input_layer = Input(shape=(1024,)) + input_lr_images = Input(shape=(64, 64, 3)) + + ca = Dense(256)(input_layer) + mean_logsigma = LeakyReLU(alpha=0.2)(ca) + c = Lambda(generate_c)(mean_logsigma) + + # 2\. Image Encoder + x = ZeroPadding2D(padding=(1, 1))(input_lr_images) + x = Conv2D(128, kernel_size=(3, 3), strides=1, use_bias=False)(x) + x = ReLU()(x) + + x = ZeroPadding2D(padding=(1, 1))(x) + x = Conv2D(256, kernel_size=(4, 4), strides=2, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + + x = ZeroPadding2D(padding=(1, 1))(x) + x = Conv2D(512, kernel_size=(4, 4), strides=2, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + + # Concatenation block + c_code = Lambda(joint_block)([c, x]) + + # 3\. Residual Blocks + x = ZeroPadding2D(padding=(1, 1))(c_code) + x = Conv2D(512, kernel_size=(3, 3), strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + x = residual_block(x) + x = residual_block(x) + x = residual_block(x) + x = residual_block(x) + + # 4\. Upsampling blocks + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(512, kernel_size=3, padding="same", strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(256, kernel_size=3, padding="same", strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(128, kernel_size=3, padding="same", strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + + x = UpSampling2D(size=(2, 2))(x) + x = Conv2D(64, kernel_size=3, padding="same", strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + x = ReLU()(x) + + x = Conv2D(3, kernel_size=3, padding="same", strides=1, use_bias=False)(x) + x = Activation('tanh')(x) + + model = Model(inputs=[input_layer, input_lr_images], outputs=[x, mean_logsigma]) + return model +``` + + + + + +# 鉴别网络 + + + +Stage-II StackGAN 的鉴别器网络是一系列下采样层,然后是串联块,然后是分类器。 让我们为每个块编写代码。 + +首先创建输入层,如下所示: + +```py +input_layer = Input(shape=(256, 256, 3)) +``` + + + + + +# 下采样块 + + + +下采样块具有对图像进行下采样的几层。 + +首先在下采样模块中添加不同的层。 本节中的代码是非常说明性的,可以轻松理解: + +```py +x = Conv2D(64, (4, 4), padding='same', strides=2, input_shape=(256, 256, 3), use_bias=False)(input_layer) +x = LeakyReLU(alpha=0.2)(x) + +x = Conv2D(128, (4, 4), padding='same', strides=2, use_bias=False)(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) + +x = Conv2D(256, (4, 4), padding='same', strides=2, use_bias=False)(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) + +x = Conv2D(512, (4, 4), padding='same', strides=2, use_bias=False)(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) + +x = Conv2D(1024, (4, 4), padding='same', strides=2, use_bias=False)(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) + +x = Conv2D(2048, (4, 4), padding='same', strides=2, use_bias=False)(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) + +x = Conv2D(1024, (1, 1), padding='same', strides=1, use_bias=False)(x) +x = BatchNormalization()(x) +x = LeakyReLU(alpha=0.2)(x) + +x = Conv2D(512, (1, 1), padding='same', strides=1, use_bias=False)(x) +x = BatchNormalization()(x) + +x2 = Conv2D(128, (1, 1), padding='same', strides=1, use_bias=False)(x) +x2 = BatchNormalization()(x2) +x2 = LeakyReLU(alpha=0.2)(x2) + +x2 = Conv2D(128, (3, 3), padding='same', strides=1, use_bias=False)(x2) +x2 = BatchNormalization()(x2) +x2 = LeakyReLU(alpha=0.2)(x2) + +x2 = Conv2D(512, (3, 3), padding='same', strides=1, use_bias=False)(x2) +x2 = BatchNormalization()(x2) +``` + +之后,我们有两个输出,分别是`x`和`x2`。 将这两个张量相加以创建相同形状的张量。 我们还需要应用`LeakyReLU`激活功能: + +```py +added_x = add([x, x2]) +added_x = LeakyReLU(alpha=0.2)(added_x) +``` + + + + + +# 串联块 + + + +为空间复制和压缩的嵌入创建另一个输入层: + +```py +input_layer2 = Input(shape=(4, 4, 128)) +``` + +将下采样块的输出连接到空间压缩的嵌入: + +```py +input_layer2 = Input(shape=(4, 4, 128)) + +merged_input = concatenate([added_x, input_layer2]) +``` + + + + + +# 全连接分类器 + + + +然后将合并后的输入馈送到具有一个卷积层和一个密集层的块中,以进行分类: + +```py +x3 = Conv2D(64 * 8, kernel_size=1, padding="same", strides=1)(merged_input) +x3 = BatchNormalization()(x3) +x3 = LeakyReLU(alpha=0.2)(x3) +x3 = Flatten()(x3) +x3 = Dense(1)(x3) +x3 = Activation('sigmoid')(x3) +``` + +`x3`是此鉴别器网络的输出。 这将输出通过的图像是真实的还是伪造的概率。 + +最后,创建一个模型: + +```py +stage2_dis = Model(inputs=[input_layer, input_layer2], outputs=[x3]) +``` + +如您所见,此模型采用两个输入并返回一个输出。 + +鉴别器网络的完整代码如下: + +```py +def build_stage2_discriminator(): + input_layer = Input(shape=(256, 256, 3)) + + x = Conv2D(64, (4, 4), padding='same', strides=2, input_shape=(256, 256, 3), use_bias=False)(input_layer) + x = LeakyReLU(alpha=0.2)(x) + + x = Conv2D(128, (4, 4), padding='same', strides=2, use_bias=False)(x) + x = BatchNormalization()(x) + x = LeakyReLU(alpha=0.2)(x) + + x = Conv2D(256, (4, 4), padding='same', strides=2, use_bias=False)(x) + x = BatchNormalization()(x) + x = LeakyReLU(alpha=0.2)(x) + + x = Conv2D(512, (4, 4), padding='same', strides=2, use_bias=False)(x) + x = BatchNormalization()(x) + x = LeakyReLU(alpha=0.2)(x) + + x = Conv2D(1024, (4, 4), padding='same', strides=2, use_bias=False)(x) + x = BatchNormalization()(x) + x = LeakyReLU(alpha=0.2)(x) + + x = Conv2D(2048, (4, 4), padding='same', strides=2, use_bias=False)(x) + x = BatchNormalization()(x) + x = LeakyReLU(alpha=0.2)(x) + + x = Conv2D(1024, (1, 1), padding='same', strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + x = LeakyReLU(alpha=0.2)(x) + + x = Conv2D(512, (1, 1), padding='same', strides=1, use_bias=False)(x) + x = BatchNormalization()(x) + + x2 = Conv2D(128, (1, 1), padding='same', strides=1, use_bias=False)(x) + x2 = BatchNormalization()(x2) + x2 = LeakyReLU(alpha=0.2)(x2) + + x2 = Conv2D(128, (3, 3), padding='same', strides=1, use_bias=False)(x2) + x2 = BatchNormalization()(x2) + x2 = LeakyReLU(alpha=0.2)(x2) + + x2 = Conv2D(512, (3, 3), padding='same', strides=1, use_bias=False)(x2) + x2 = BatchNormalization()(x2) + + added_x = add([x, x2]) + added_x = LeakyReLU(alpha=0.2)(added_x) + + input_layer2 = Input(shape=(4, 4, 128)) + + # Concatenation block + merged_input = concatenate([added_x, input_layer2]) + + x3 = Conv2D(64 * 8, kernel_size=1, padding="same", strides=1)(merged_input) + x3 = BatchNormalization()(x3) + x3 = LeakyReLU(alpha=0.2)(x3) + x3 = Flatten()(x3) + x3 = Dense(1)(x3) + x3 = Activation('sigmoid')(x3) + + stage2_dis = Model(inputs=[input_layer, input_layer2], outputs=[x3]) + return stage2_dis +``` + +我们现在已经成功地为两个 StackGAN 创建了模型:Stage-I 和 Stage-II。 让我们继续训练模型。 + + + + + +# 训练 StackGAN + + + +在本节中,我们将学习如何训练这两个 StackGAN。 在第一小节中,我们将训练 Stage-I StackGAN。 在第二小节中,我们将训练 Stage-II StackGAN。 + + + + + +# 训练 Stage-I StackGAN + + + +在开始训练之前,我们需要指定基本的超参数。 超参数是在训练过程中不会改变的值。 让我们先这样做: + +```py +data_dir = "Specify your dataset directory here/Data/birds" train_dir = data_dir + "/train" test_dir = data_dir + "/test" image_size = 64 batch_size = 64 z_dim = 100 stage1_generator_lr = 0.0002 stage1_discriminator_lr = 0.0002 stage1_lr_decay_step = 600 epochs = 1000 condition_dim = 128 embeddings_file_path_train = train_dir + "/char-CNN-RNN-embeddings.pickle" embeddings_file_path_test = test_dir + "/char-CNN-RNN-embeddings.pickle" filenames_file_path_train = train_dir + "/filenames.pickle" filenames_file_path_test = test_dir + "/filenames.pickle" class_info_file_path_train = train_dir + "/class_info.pickle" class_info_file_path_test = test_dir + "/class_info.pickle" cub_dataset_dir = data_dir + "/CUB_200_2011" +``` + +然后,我们需要加载数据集。 + + + + + +# 加载数据集 + + + +加载数据集是一个需要几个步骤的过程。 让我们一步一步地探索每个步骤。 + +1. 第一步是加载存储在 pickle 文件中的类 ID。 以下代码将加载类 ID 并返回所有 ID 的列表: + +```py +def load_class_ids(class_info_file_path): + """ + Load class ids from class_info.pickle file """ with open(class_info_file_path, 'rb') as f: + class_ids = pickle.load(f, encoding='latin1') + return class_ids +``` + +2. 接下来,加载文件名,这些文件名也存储在 pickle 文件中。 可以按照以下步骤进行: + +```py +def load_filenames(filenames_file_path): + """ + Load filenames.pickle file and return a list of all file names """ with open(filenames_file_path, 'rb') as f: + filenames = pickle.load(f, encoding='latin1') + return filenames +``` + +3. 之后,我们需要加载文本嵌入,这些嵌入也位于 pickle 文件中。 加载文件并检索文本嵌入,如下所示: + +```py +def load_embeddings(embeddings_file_path): + """ + Load embeddings """ with open(embeddings_file_path, 'rb') as f: + embeddings = pickle.load(f, encoding='latin1') + embeddings = np.array(embeddings) + print('embeddings: ', embeddings.shape) + return embeddings +``` + +4. 接下来,获取边界框,该边界框用于从原始图像提取对象。 下面的不言自明的代码显示了如何检索边界框: + +```py +def load_bounding_boxes(dataset_dir): + """ + Load bounding boxes and return a dictionary of file names and corresponding bounding boxes """ # Paths bounding_boxes_path = os.path.join(dataset_dir, 'bounding_boxes.txt') + file_paths_path = os.path.join(dataset_dir, 'images.txt') + + # Read bounding_boxes.txt and images.txt file + df_bounding_boxes = pd.read_csv(bounding_boxes_path, + delim_whitespace=True, header=None).astype(int) + df_file_names = pd.read_csv(file_paths_path, delim_whitespace=True, header=None) + + # Create a list of file names + file_names = df_file_names[1].tolist() + + # Create a dictionary of file_names and bounding boxes + filename_boundingbox_dict = {img_file[:-4]: [] for img_file in file_names[:2]} + + # Assign a bounding box to the corresponding image + for i in range(0, len(file_names)): + # Get the bounding box + bounding_box = df_bounding_boxes.iloc[i][1:].tolist() + key = file_names[i][:-4] + filename_boundingbox_dict[key] = bounding_box + + return filename_boundingbox_dict +``` + +5. 接下来,编写一种加载和裁剪图像的方法。 以下代码加载图像并将其裁剪在提供的边界框周围。 它还将图像调整为指定大小: + +```py +def get_img(img_path, bbox, image_size): + """ + Load and resize image """ img = Image.open(img_path).convert('RGB') + width, height = img.size + if bbox is not None: + R = int(np.maximum(bbox[2], bbox[3]) * 0.75) + center_x = int((2 * bbox[0] + bbox[2]) / 2) + center_y = int((2 * bbox[1] + bbox[3]) / 2) + y1 = np.maximum(0, center_y - R) + y2 = np.minimum(height, center_y + R) + x1 = np.maximum(0, center_x - R) + x2 = np.minimum(width, center_x + R) + img = img.crop([x1, y1, x2, y2]) + img = img.resize(image_size, PIL.Image.BILINEAR) + return img +``` + +6. 最后,结合前面所有的方法来获取数据集,这是我们训练所需的。 此代码返回所有图像,其标签和相应的嵌入。 我们需要这些来进行培训: + +```py +def load_dataset(filenames_file_path, class_info_file_path, cub_dataset_dir, embeddings_file_path, image_size): + filenames = load_filenames(filenames_file_path) + class_ids = load_class_ids(class_info_file_path) + bounding_boxes = load_bounding_boxes(cub_dataset_dir) + all_embeddings = load_embeddings(embeddings_file_path) + + X, y, embeddings = [], [], [] + + # TODO: Change filenames indexing + for index, filename in enumerate(filenames[:500]): + # print(class_ids[index], filenames[index]) + bounding_box = bounding_boxes[filename] + + try: + # Load images + img_name = '{}/images/{}.jpg'.format(cub_dataset_dir, filename) + img = get_img(img_name, bounding_box, image_size) + + all_embeddings1 = all_embeddings[index, :, :] + + embedding_ix = random.randint(0, all_embeddings1.shape[0] - 1) + embedding = all_embeddings1[embedding_ix, :] + + X.append(np.array(img)) + y.append(class_ids[index]) + embeddings.append(embedding) + except Exception as e: + print(e) + + X = np.array(X) + y = np.array(y) + embeddings = np.array(embeddings) + + return X, y, embeddings +``` + +7. 最后,加载数据集并将其用于训练: + +```py +X_train, y_train, embeddings_train = load_dataset(filenames_file_path=filenames_file_path_train, + class_info_file_path=class_info_file_path_train, + cub_dataset_dir=cub_dataset_dir, + embeddings_file_path=embeddings_file_path_train, + image_size=(64, 64)) + +X_test, y_test, embeddings_test = load_dataset(filenames_file_path=filenames_file_path_test, + class_info_file_path=class_info_file_path_test, + cub_dataset_dir=cub_dataset_dir, + embeddings_file_path=embeddings_file_path_test, + image_size=(64, 64)) +``` + +现在我们已经成功加载了数据集进行训练,让我们创建一些模型。 + + + + + +# 建立模型 + + + +让我们使用 *StackGAN* 的 Keras 实现下的 *Stage-I StackGAN* 的 部分中的方法创建模型。 我们将使用四个模型:生成器模型,鉴别器模型,压缩文本嵌入的压缩器模型以及同时包含生成器和鉴别器的对抗模型: + +1. 首先定义培训所需的优化器: + +```py +dis_optimizer = Adam(lr=stage1_discriminator_lr, beta_1=0.5, beta_2=0.999) +gen_optimizer = Adam(lr=stage1_generator_lr, beta_1=0.5, beta_2=0.999) +``` + +2. 然后按如下所示构建和编译不同的网络: + +```py +ca_model = build_ca_model() +ca_model.compile(loss="binary_crossentropy", optimizer="adam") + +stage1_dis = build_stage1_discriminator() +stage1_dis.compile(loss='binary_crossentropy', optimizer=dis_optimizer) + +stage1_gen = build_stage1_generator() +stage1_gen.compile(loss="mse", optimizer=gen_optimizer) + +embedding_compressor_model = build_embedding_compressor_model() +embedding_compressor_model.compile(loss="binary_crossentropy", optimizer="adam") + +adversarial_model = build_adversarial_model(gen_model=stage1_gen, dis_model=stage1_dis) +adversarial_model.compile(loss=['binary_crossentropy', KL_loss], loss_weights=[1, 2.0], + optimizer=gen_optimizer, metrics=None) +``` + +此处,`KL_loss`是自定义损失函数,定义如下: + +```py +def KL_loss(y_true, y_pred): + mean = y_pred[:, :128] + logsigma = y_pred[:, :128] + loss = -logsigma + .5 * (-1 + K.exp(2\. * logsigma) + K.square(mean)) + loss = K.mean(loss) + return loss +``` + +现在我们已经准备好数据集和模型,因此我们可以开始训练模型。 + +另外,添加 TensorBoard 以存储损失以进行可视化,如下所示: + +```py +tensorboard = TensorBoard(log_dir="logs/".format(time.time())) +tensorboard.set_model(stage1_gen) +tensorboard.set_model(stage1_dis) +tensorboard.set_model(ca_model) +tensorboard.set_model(embedding_compressor_model) +``` + + + + + +# 训练模型 + + + +训练模型需要几个步骤: + +1. 用真实和假标签创建两个张量。 在训练生成器和鉴别器时将需要这些。 使用标签平滑处理,该内容在[第 1 章](../Text/1.html)和*生成对抗网络*中介绍: + +```py +real_labels = np.ones((batch_size, 1), dtype=float) * 0.9 fake_labels = np.zeros((batch_size, 1), dtype=float) * 0.1 +``` + +2. 接下来,创建一个 for 循环,该循环应运行的次数由时期数指定,如下所示: + +```py +for epoch in range(epochs): + print("========================================") + print("Epoch is:", epoch) + print("Number of batches", int(X_train.shape[0] / batch_size)) + + gen_losses = [] + dis_losses = [] +``` + +3. 之后,计算一批批次并编写一个 for 循环,该循环将针对指定数量的批次运行: + +```py + number_of_batches = int(X_train.shape[0] / batch_size) + for index in range(number_of_batches): + print("Batch:{}".format(index+1)) +``` + +4. 为当前迭代采样一批数据(一个小批量)。 创建噪声矢量,选择一批图像和一批嵌入物,并对图像进行规范化: + +```py + # Create a batch of noise vectors + z_noise = np.random.normal(0, 1, size=(batch_size, z_dim)) + image_batch = X_train[index * batch_size:(index + 1) * batch_size] + embedding_batch = embeddings_train[index * batch_size:(index + 1) * batch_size] + + # Normalize images + image_batch = (image_batch - 127.5) / 127.5 +``` + +5. 接下来,使用生成器模型通过传递`embedding_batch`和`z_noise`来生成伪图像: + +```py + fake_images, _ = stage1_gen.predict([embedding_batch, z_noise], verbose=3) +``` + +这将生成以一组嵌入和一组噪声向量为条件的一组伪图像。 + +6. 使用压缩器模型压缩嵌入。 空间复制它以将其转换为形状为(batch_size,4,4,128)的张量: + +```py + compressed_embedding = embedding_compressor_model.predict_on_batch(embedding_batch) + compressed_embedding = np.reshape(compressed_embedding, (-1, 1, 1, condition_dim)) + compressed_embedding = np.tile(compressed_embedding, (1, 4, 4, 1)) +``` + +7. 接下来,在生成器生成的伪图像,真实数据集中的真实图像和错误图像上训练鉴别器模型: + +```py + dis_loss_real = stage1_dis.train_on_batch([image_batch, compressed_embedding], + np.reshape(real_labels, (batch_size, 1))) + dis_loss_fake = stage1_dis.train_on_batch([fake_images, compressed_embedding], + np.reshape(fake_labels, (batch_size, 1))) + dis_loss_wrong = stage1_dis.train_on_batch([image_batch[:(batch_size - 1)], compressed_embedding[1:]], + np.reshape(fake_labels[1:], (batch_size-1, 1))) +``` + +现在,我们已经成功地在三组数据上训练了鉴别器:真实图像,伪图像和错误图像。 现在让我们训练对抗模型: + +8. 接下来,训练对抗模型。 为它提供三个输入和相应的真值。 此操作将计算梯度并更新一批数据的权重。 + +```py + g_loss = adversarial_model.train_on_batch([embedding_batch, z_noise, compressed_embedding],[K.ones((batch_size, 1)) * 0.9, K.ones((batch_size, 256)) * 0.9]) +``` + +9. 接下来,计算损失并将其存储以进行评估。 最好不断打印出不同的损失以跟踪培训: + +```py +d_loss = 0.5 * np.add(dis_loss_real, 0.5 * np.add(dis_loss_wrong, dis_loss_fake)) + + print("d_loss:{}".format(d_loss)) + + print("g_loss:{}".format(g_loss)) + + dis_losses.append(d_loss) + gen_losses.append(g_loss) +``` + +10. 在每个时期完成之后,将任何损失存储到 TensorBoard 中: + +```py + write_log(tensorboard, 'discriminator_loss', np.mean(dis_losses), epoch) + write_log(tensorboard, 'generator_loss', np.mean(gen_losses[0]), epoch) +``` + +11. 在每个时期之后,要评估进度,生成图像并将其保存在结果目录中。 + +```py + z_noise2 = np.random.normal(0, 1, size=(batch_size, z_dim)) + embedding_batch = embeddings_test[0:batch_size] + fake_images, _ = stage1_gen.predict_on_batch([embedding_batch, z_noise2]) + + # Save images for i, img in enumerate(fake_images[:10]): + save_rgb_img(img, "results/gen_{}_{}.png".format(epoch, i)) +``` + +此处,`save_rgb_img()`是一个实用函数,定义如下: + +```py +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() +``` + +12. 将每个模型的权重保存在 StackGAN 的 Stage-I 中。 + +```py +stage1_gen.save_weights("stage1_gen.h5") +stage1_dis.save_weights("stage1_dis.h5") +``` + +恭喜,我们已经成功培训了 StackGAN 的 Stage-I。 现在,我们拥有训练有素的生成器网络,可以生成尺寸为`64x64x3`的图像。 这些图像将具有基本颜色和原始形状。 在下一部分中,我们将训练 Stage-II StackGAN。 + + + + + +# 训练 Stage II StackGAN + + + +请执行以下步骤来训练 Stage-II StackGAN。 + +首先指定用于训练 Stage-II StackGAN 的超参数: + +```py +# Specify hyperparamters data_dir = "Path to the dataset/Data/birds" train_dir = data_dir + "/train" test_dir = data_dir + "/test" hr_image_size = (256, 256) +lr_image_size = (64, 64) +batch_size = 8 z_dim = 100 stage1_generator_lr = 0.0002 stage1_discriminator_lr = 0.0002 stage1_lr_decay_step = 600 epochs = 10 condition_dim = 128 embeddings_file_path_train = train_dir + "/char-CNN-RNN-embeddings.pickle" embeddings_file_path_test = test_dir + "/char-CNN-RNN-embeddings.pickle" filenames_file_path_train = train_dir + "/filenames.pickle" filenames_file_path_test = test_dir + "/filenames.pickle" class_info_file_path_train = train_dir + "/class_info.pickle" class_info_file_path_test = test_dir + "/class_info.pickle" cub_dataset_dir = data_dir + "/CUB_200_2011" +``` + + + + + +# 加载数据集 + + + +使用创建 Stage-I StackGAN 时在*加载数据集*部分中定义的方法。 分别加载高分辨率和低分辨率数据集。 另外,分别加载训练和测试数据集: + +```py +X_hr_train, y_hr_train, embeddings_train = load_dataset(filenames_file_path=filenames_file_path_train, + class_info_file_path=class_info_file_path_train, + cub_dataset_dir=cub_dataset_dir, + embeddings_file_path=embeddings_file_path_train, + image_size=(256, 256)) + +X_hr_test, y_hr_test, embeddings_test = load_dataset(filenames_file_path=filenames_file_path_test, + class_info_file_path=class_info_file_path_test, + cub_dataset_dir=cub_dataset_dir, + embeddings_file_path=embeddings_file_path_test, + image_size=(256, 256)) + +X_lr_train, y_lr_train, _ = load_dataset(filenames_file_path=filenames_file_path_train, + class_info_file_path=class_info_file_path_train, + cub_dataset_dir=cub_dataset_dir, + embeddings_file_path=embeddings_file_path_train, + image_size=(64, 64)) + +X_lr_test, y_lr_test, _ = load_dataset(filenames_file_path=filenames_file_path_test, + class_info_file_path=class_info_file_path_test, + cub_dataset_dir=cub_dataset_dir, + embeddings_file_path=embeddings_file_path_test, + image_size=(64, 64)) +``` + + + + + +# 建立模型 + + + +与之前一样创建 Keras 模型,这些模型在 *StackGAN* 的 Keras 实现的 *Stage-I StackGAN* 部分中指定: + +首先定义培训所需的优化器: + +```py +dis_optimizer = Adam(lr=stage1_discriminator_lr, beta_1=0.5, beta_2=0.999) +gen_optimizer = Adam(lr=stage1_generator_lr, beta_1=0.5, beta_2=0.999) +``` + +我们将使用 `Adam optimizer`,其学习率等于`0.0002`,`beta_1`值等于`0.5`,`beta_2`等于`0.999`。 + +现在构建和创建模型: + +```py +stage2_dis = build_stage2_discriminator() +stage2_dis.compile(loss='binary_crossentropy', optimizer=dis_optimizer) + +stage1_gen = build_stage1_generator() +stage1_gen.compile(loss="binary_crossentropy", optimizer=gen_optimizer) + +stage1_gen.load_weights("stage1_gen.h5") + +stage2_gen = build_stage2_generator() +stage2_gen.compile(loss="binary_crossentropy", optimizer=gen_optimizer) + +embedding_compressor_model = build_embedding_compressor_model() +embedding_compressor_model.compile(loss='binary_crossentropy', optimizer='adam') + +adversarial_model = build_adversarial_model(stage2_gen, stage2_dis, stage1_gen) +adversarial_model.compile(loss=['binary_crossentropy', KL_loss], loss_weights=[1.0, 2.0], + optimizer=gen_optimizer, metrics=None) +``` + +`KL_loss`是自定义损失函数,在*训练 Stage-I StackGAN* 部分中指定。 + +现在,我们已经为 Stage-II StackGAN 准备了数据集和模型。 让我们训练模型。 + + + + + +# 训练模型 + + + +让我们逐步进行此过程。 + +1. 首先添加 TensorBoard 来存储损失: + +```py +tensorboard = TensorBoard(log_dir="logs/".format(time.time())) +tensorboard.set_model(stage2_gen) +tensorboard.set_model(stage2_dis) +``` + +2. 然后,创建两个张量分别为`real`和`fake`的张量。 在训练生成器和鉴别器时将需要这些。 使用标签平滑处理,[第 1 章](../Text/1.html)和*生成对抗网络* 对此进行了介绍: + +```py +real_labels = np.ones((batch_size, 1), dtype=float) * 0.9 fake_labels = np.zeros((batch_size, 1), dtype=float) * 0.1 +``` + +3. 接下来,创建一个`for`循环,该循环应运行由时期数指定的次数,如下所示: + +```py +for epoch in range(epochs): + print("========================================") + print("Epoch is:", epoch) + + gen_losses = [] + dis_losses = [] +``` + +4. 在纪元循环内创建另一个循环,该循环将运行指定的批次数量: + +```py + print("Number of batches:{}".format(number_of_batches)) + for index in range(number_of_batches): + print("Batch:{}".format(index)) +``` + +5. 采样培训所需的数据: + +```py + # Create a mini-batch of noise vectors z_noise = np.random.normal(0, 1, size=(batch_size, z_dim)) + X_hr_train_batch = X_hr_train[index * batch_size:(index + 1) * batch_size] + embedding_batch = embeddings_train[index * batch_size:(index + 1) * batch_size] + + # Normalize images + X_hr_train_batch = (X_hr_train_batch - 127.5) / 127.5 +``` + +6. 接下来,使用生成器网络生成尺寸为`256x256x2`的伪图像。 在此步骤中,我们首先使用 Stage-1 的生成器网络生成低分辨率的伪图像。 然后,我们在 Stage-II 中使用生成器网络生成以低分辨率图像为条件的高分辨率图像。 + +```py + lr_fake_images, _ = stage1_gen.predict([embedding_batch, z_noise], verbose=3) + hr_fake_images, _ = stage2_gen.predict([embedding_batch, lr_fake_images], verbose=3) +``` + +7. 使用压缩器模型压缩嵌入。 空间复制它以将其转换为形状为(batch_size,4,4,128)的张量 + +```py + compressed_embedding = embedding_compressor_model.predict_on_batch(embedding_batch) + compressed_embedding = np.reshape(compressed_embedding, (-1, 1, 1, condition_dim)) + compressed_embedding = np.tile(compressed_embedding, (1, 4, 4, 1)) +``` + +8. 之后,在伪图像,真实图像和错误图像上训练鉴别器模型: + +```py + dis_loss_real = stage2_dis.train_on_batch([X_hr_train_batch, compressed_embedding], + np.reshape(real_labels, (batch_size, 1))) + dis_loss_fake = stage2_dis.train_on_batch([hr_fake_images, compressed_embedding], + np.reshape(fake_labels, (batch_size, 1))) + dis_loss_wrong = stage2_dis.train_on_batch([X_hr_train_batch[:(batch_size - 1)], compressed_embedding[1:]], + np.reshape(fake_labels[1:], (batch_size-1, 1))) +``` + +9. 接下来,训练对抗模型。 这是生成器模型和鉴别器模型的组合。 我们为它提供三个输入和相应的真值: + +```py + g_loss = adversarial_model.train_on_batch([embedding_batch, z_noise, compressed_embedding],[K.ones((batch_size, 1)) * 0.9, K.ones((batch_size, 256)) * 0.9]) +``` + +10. 计算损失并将其保存以进行评估: + +```py + d_loss = 0.5 * np.add(dis_loss_real, 0.5 * np.add(dis_loss_wrong, dis_loss_fake)) + print("d_loss:{}".format(d_loss)) + + print("g_loss:{}".format(g_loss)) +``` + +在每个时期之后,将损失保存到 TensorBoard 中: + +```py + write_log(tensorboard, 'discriminator_loss', np.mean(dis_losses), epoch) + write_log(tensorboard, 'generator_loss', np.mean(gen_losses)[0], epoch) +``` + +11. 在每个时期之后,要评估进度,生成图像并将其保存在结果目录中。 在以下代码中,我们仅保存第一个生成的图像。 相应地更改此设置以适当保存图像。 + +```py + # Generate and save images after every 2nd epoch if epoch % 2 == 0: + # z_noise2 = np.random.uniform(-1, 1, size=(batch_size, z_dim)) + z_noise2 = np.random.normal(0, 1, size=(batch_size, z_dim)) + embedding_batch = embeddings_test[0:batch_size] + + lr_fake_images, _ = stage1_gen.predict([embedding_batch, z_noise2], verbose=3) + hr_fake_images, _ = stage2_gen.predict([embedding_batch, lr_fake_images], verbose=3) + + # Save images + for i, img in enumerate(hr_fake_images[:10]): + save_rgb_img(img, "results2/gen_{}_{}.png".format(epoch, i)) +``` + +此处,`save_rgb_img()`是实用程序功能,在训练第 I 阶段 StackGAN 部分的*中定义。* + +12. 最后,保存模型或其权重值: + +```py +# Saving the models stage2_gen.save_weights("stage2_gen.h5") +stage2_dis.save_weights("stage2_dis.h5") +``` + +恭喜,我们现在已经成功完成了 StageII II StackGAN 的培训。 现在,我们有了一个生成器网络,可以生成尺寸 `256x256x3`的逼真的图像。 如果为生成器网络提供文本嵌入和噪声变量,它将生成 `256x256x3`分辨率图像。 让我们可视化网络的损失图。 + + + + + +# 可视化生成的图像 + + + +在将网络训练了 500 个时间段后,网络将开始生成如下所示的体面图像。 + +![](img/030151f2-b1cf-4a2a-9d44-d55bc65f9c7c.png) + +Images generated by Stage-I and Stage-II of the StackGAN network + +我建议您将网络训练 1000 个纪元。 如果一切顺利,则在 1000 个时期之后,生成器网络将开始生成逼真的图像。 + + + + + +# 可视化损失 + + + +为了可视化训练的损失,请按以下方式启动 TensorBoard 服务器: + +```py +tensorboard --logdir=logs +``` + +现在,在浏览器中打开 `localhost:6006` 。 TensorBoard 的 **标量** 部分包含两个损失的曲线图,如下所示: + +阶段 I 的鉴别器网络的损耗图如下所示: + +![](img/8d391386-69b9-4890-bd5a-a078ba996754.png) + +阶段 I 的发电机网络的损耗图如下所示: + +![](img/905056c0-7994-444e-b42b-4ebce2f093d9.png) + +可以类似地从 Tensorboard 获得 Stage II 的发电机网络和鉴别器网络的损耗图。 + +这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止培训,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。 + + + + + +# 可视化图形 + + + +TensorBoard 的 **GRAPHS** 部分包含两个网络的图形。 如果网络性能不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作: + +![](img/b2df0366-0c95-4e61-a490-7efa0da76e73.png) + + + + + +# StackGAN 的实际应用 + + + +StackGAN 的行业应用包括: + +* 自动生成高分辨率图像以用于娱乐或教育目的 +* **创建漫画**:使用 StackGAN 可以将漫画创建过程缩短至几天,因为 StackGAN 可以自动生成漫画并协助创作过程 +* **电影创作**:StackGAN 可以通过根据文本描述生成帧来协助电影创作者 +* **艺术创作**:StackGAN 可以通过根据文字描述生成草图来协助艺术家 + + + + + +# 概要 + + + +在本章中,我们了解并实现了 StackGAN 网络,该网络可从文本描述生成高分辨率图像。 我们从对 StackGAN 的基本介绍开始,在其中我们探讨了 StackGAN 的体系结构细节,并发现了用于培训 StackGAN 的损失。 然后,我们下载并准备了数据集。 之后,我们开始在 Keras 框架中实现 StackGAN。 实施之后,我们依次训练了 Stage-I 和 Stage-II StackGANS。 成功训练网络后,我们评估了模型并将其保存以备将来使用。 + +在下一章中,我们将与 CycleGAN 合作,该网络可以将绘画转换为照片。 + + diff --git a/new/gan-proj/7.md b/new/gan-proj/7.md new file mode 100644 index 0000000000000000000000000000000000000000..663201f7f75321c7415459c2b2a5bbb425983e1a --- /dev/null +++ b/new/gan-proj/7.md @@ -0,0 +1,1119 @@ + + +# CycleGAN-将绘画变成照片 + + + +CycleGAN 是一种**生成对抗性** **网络**( **GAN** )的类型,用于跨域传输任务,例如更改图像的样式,将绘画转变为照片, 反之亦然,例如照片增强功能,更改照片的季节等等。 CycleGAN 由朱俊彦,Taesung Park,Phillip Isola 和 Alexei A. Efros 在题为*的非配对图像到图像转换中使用循环一致对抗网络*引入。 该产品于 2018 年 2 月在加州大学伯克利分校的 **Berkeley AI Research** ( **BAIR** )实验室生产,可通过以下链接获得: [https://arxiv.org/pdf/1703.10593.pdf](https://arxiv.org/pdf/1703.10593.pdf) 。 由于其广泛的使用案例,CycleGAN 在 GAN 社区引起了轰动。 在本章中,我们将与 CycleGAN 一起使用,尤其是使用它们将绘画转换为照片。 + +在本章中,我们将介绍以下主题: + +* CycleGAN 简介 +* CycleGAN 的架构 +* 数据收集与准备 +* Keras 的 CycleGAN 实现 +* 目标功能 +* 训练 CycleGAN +* CycleGAN 的实际应用 + + + + + +# CycleGAN 简介 + + + +为了将照片变成一幅画或一幅画,再将它们变成照片,普通 GAN 需要一对图像。 CycleGAN 是一种 GAN 网络,可以将图像从一个域 X 转换为另一个域 Y,而无需配对图像。 CycleGAN 尝试学习发电机网络,而发电机网络又学习了两个映射。 CycleGAN 无需训练大多数 GAN 中使用的单个发电机网络,而是训练两个发电机和两个鉴别器网络。 + +CycleGAN 中有两个发电机网络,如下所示: + +1. **生成器 A** :学习映射![](img/5ee63015-1266-4f90-b3cc-dca5d99f293e.png),其中 X 是源域, *Y* 是目标域。 它从源域 *A* 拍摄图像,并将其转换为与目标域 *B* 相似的图像。 基本上,网络的目的是学习映射,以使 *G(X)*与 *Y* 相似。 +2. **生成器 B** :学习映射![](img/4afb529e-38ff-40f5-b294-dd6545cd5251.png),然后从目标域 B 中获取图像,并将其转换为与源域 A 中的图像相似的图像。类似地, 网络要学习另一个映射,以便 *F(G(X)*类似于 *X* )。 + +这两个网络的架构相同,但我们分别对其进行培训。 + +CycleGAN 中有两个鉴别器网络,如下所示: + +1. **鉴别器 A** :鉴别器 *A* 的工作是区分由生成器网络 *A* 生成的图像,这些图像表示为 *G(X)* 和来自源域 *A* 的真实图像,它们表示为 *X* 。 +2. **鉴别符 B** :鉴别符 *B* 的工作是区分由生成器网络 *B* 生成的图像,这些图像表示为 *F(Y)* 以及来自源域 *B* 的真实图像,它们表示为 *Y* 。 + +两个网络的架构是相同的。 与生成器网络类似,我们分别训练区分器网络。 如下图所示: + +![](img/969f8518-7d6b-42ce-9030-fdc7ca31818b.png) + +Illustration of a CycleGAN with two generator and two adversarial discriminator networks. Source: https://arxiv.org/pdf/1703.10593.pdf + +在下一节中,让我们详细了解 CycleGAN 的体系结构。 + + + + + +# CycleGAN 的架构 + + + +CycleGAN 总体上由两种体系结构组成:生成器和鉴别器。 生成器体系结构用于创建两个模型,生成器 A 和生成器 B。鉴别器体系结构用于创建另外两个模型,鉴别器 A 和鉴别器 B。我们现在将在接下来的两节中介绍两个网络的体系结构。 + + + + + +# 发电机的架构 + + + +生成器网络是自动编码器类型的网络。 它以图像作为输入并输出另一个图像。 它由两部分组成:编码器和解码器。 编码器包含具有下采样功能的卷积层,并将`128x128x3`形状的输入转换为内部表示。 解码器包含两个上采样块和最后一个卷积层,该层将内部表示形式转换为`128x128x3`形状的输出。 + +发电机网络包含以下块: + +* 卷积块 +* 残留块 +* 上采样块 +* 最后的卷积层 + +让我们逐一介绍每个组件: + +* **卷积块**:卷积块包含 2D 卷积层,然后是实例规范化层和 relu 作为激活函数。 请参阅[第 1 章](../Text/1.html), *生成对抗网络简介* ,以了解有关*实例规范化*的更多信息。 + +生成器网络包含三个卷积块,其配置如下: + +| **图层名称** | **超参数** | **输入形状** | **输出形状** | +| 2D 卷积层 | `filters=32, kernel_size=7, strides=1, padding='same'` | (128, 128, 3) | (128, 128, 32) | +| 实例规范化层 | `axis=1` | (128, 128, 32) | (128, 128, 32) | +| 激活层 | `activation='relu'` | (128, 128, 32) | (128, 128, 32) | +| 2D 卷积层 | `filters=64, kernel_size=3, strides=2, padding='same'` | (128, 128, 32) | (64, 64, 64) | +| 实例规范化层 | `axis=1` | (64, 64, 64) | (64, 64, 64) | +| 激活层 | `activation='relu'` | (64, 64, 64) | (64, 64, 64) | +| 2D 卷积层 | `filters=128, kernel_size=3, strides=2, padding='same'` | (64, 64, 64) | (32, 32, 128) | +| 实例规范化层 | `axis=1` | (32, 32, 128) | (32, 32, 128) | +| 激活层 | `activation='relu'` | (32, 32, 128) | (32, 32, 128) | + +* **残余块**:残余块包含两个 2D 卷积层。 两层之后是动量值等于 0.8 的批归一化层。 发电机网络包含六个残差块,其配置如下: + +| **图层名称** | **超参数** | **输入形状** | **输出形状** | +| 2D 卷积层 | `filters=128, kernel_size=3, strides=1, padding='same'` | (32, 32, 128) | (32、32、128) | +| 批量归一化层 | `axis=3, momentum=0.9, epsilon=1e-5` | (32、32、128) | (32、32、128) | +| 2D 卷积层 | `filters=138, kernel_size=3, strides=1, padding='same'` | (32、32、128) | ((32,32,128) | +| 批量归一化层 | `axis=3, momentum=0.9, epsilon=1e-5` | (32、32、128) | (32、32、128) | +| 加法层 | `None` | (32、32、128) | (32、32、128) | + +加法层计算输入到块的 张量 与最后一批归一化层的输出之和。 + +* **上采样块**:上采样块包含转置 2D 卷积层,并使用`relu`作为激活函数。 发生器网络中有两个上采样模块。 第一个升采样模块的配置如下: + +| **图层名称** | **超参数** | **输入形状** | **输出形状** | +| 转置 2D 卷积层 | `filters=64, kernel_size=3, strides=2, padding='same', use_bias=False` | (32, 32, 128) | (64, 64, 64) | +| 实例规范化层 | `axis=1` | (64, 64, 64) | (64, 64, 64) | +| 激活层 | `activation='relu'` | (64, 64, 64) | (64, 64, 64) | + +第二个上采样模块的配置如下: + +| **图层名称** | **超参数** | **输入形状** | **输出形状** | +| 转置 2D 卷积层 | `filters=32, kernel_size=3, strides=2, padding='same', use_bias=False` | (64, 64, 64) | (128, 128, 32) | +| 实例规范化层 | `axis=1` | ( 128、128、32) | ( 128、128、32) | +| 激活层 | `activation='relu'` | ( 128、128、32) | ( 128、128、32) | + +* **最后的卷积层**:最后一层是使用`tanh`作为激活函数的 2D 卷积层。 它生成形状为(256、256、3)的图像。 最后一层的配置如下: + +| **图层名称** | **超参数** | **输入形状** | **输出形状** | +| 2D 卷积层 | `filters=3, kernel_size=7, strides=1, padding='same', activation='tanh'` | (128, 128, 32) | (128, 128, 3) | + +These hyperparameters are best suited for the Keras framework. If you are using any other framework, modify accordingly. + + + +# 鉴别器的架构 + + + +鉴别器网络的架构类似于 PatchGAN 网络中的鉴别器架构。 它是一个深度卷积神经网络,包含多个卷积块。 基本上,它会拍摄形状为(128、128、3)的图像,并预测该图像是真实的还是假的。 它包含几个 ZeroPadding2D 层,可以在以下链接中找到其文档: [https://keras.io/layers/convolutional/#zeropadding2d](https://keras.io/layers/convolutional/#zeropadding2d) 。 下表详细显示了鉴别器网络的体系结构: + +| **图层名称** | **超参数** | **输入形状** | **输出形状** | +| 输入层 | `none` | (128, 128, 3) | (128, 128, 3) | +| ZeroPadding2D 层 | `padding(1, 1)` | (128, 128, 3) | (130, 130, 3) | +| 2D 卷积层 | `filters=64, kernel_size=4, strides=2, padding='valid'` | (130, 130, 3) | (64,64,64) | +| 激活层 | `activation='leakyrelu', alpha=0.2` | (64,64,64) | (64,64,64) | +| ZeroPadding2D 层 | `padding(1, 1)` | (64, 64, 64) | (66, 66, 64) | +| 2D 卷积层 | `filters=128, kernel_size=4, strides=2, padding='valid'` | (66, 66, 64) | (32、32、128) | +| 实例规范化层 | `axis=1` | (32, 32, 128) | (32, 32, 128) | +| 激活层 | `activation='leakyrelu', alpha=0.2` | (32、32、128) | (32、32、128) | +| ZeroPadding2D 层 | `padding(1, 1)` | (32, 32, 128) | (34, 34, 128) | +| 2D 卷积层 | `filters=256, kernel_size=4, strides=2, padding='valid'` | (34, 34, 128) | (16,16,256) | +| 实例规范化层 | `axis=1` | (16,16,256) | (16,16,256) | +| 激活层 | `activation='leakyrelu', alpha=0.2` | (16,16,256) | (16,16,256) | +| ZeroPadding2D 层 | `padding(1, 1)` | (16, 16, 256) | (18, 18, 256) | +| 2D 卷积层 | `filters=512, kernel_size=4, strides=2, padding='valid'` | (18, 18, 256) | (8,8,512) | +| 实例规范化层 | `axis=1` | (8,8,512) | (8,8,512) | +| 激活层 | `activation='leakyrelu', alpha=0.2` | (8,8,512) | (8,8,512) | +| ZeroPadding2D 层 | `padding(1, 1)` | (8,8,512) | (10, 10, 512) | +| 2D 卷积层 | `filters=1, kernel_size=4, strides=1, padding='valid', activation='sigmoid'` | (10, 10, 512) | (7,7,1) | + +鉴别器网络返回形状为(7,7,1)的张量。 现在,我们已经介绍了这两个网络的详细架构。 在下一节中,我们将介绍训练 CycleGAN 所需的目标函数。 + +The ZeroPadding2D layer adds rows and columns of zeros at the top, bottom, left, and right of an image tensor. + + + +# 训练目标功能 + + + +与其他 GAN 相似,CycleGAN 具有训练目标函数,我们需要将其最小化以训练模型。 损失函数是以下损失的加权总和: + +1. 对抗损失 +2. 循环一致性损失 + +在以下各节中,让我们详细研究对抗性损失和周期一致性损失。 + + + + + +# 对抗损失 + + + +对抗性损失是实际分布 A 或 B 的图像与生成器网络生成的图像之间的损失。 我们有两个映射功能,我们将对两个映射应用对抗性损失。 + +映射![](img/4777940b-3156-48bb-8ab8-a0b8d3d97978.png)的对抗损失如下所示: + +![](img/f278fd84-eb49-4d88-a98e-31396fe4ea3a.png) + +在此,x 是来自分布 A 的一个域的图像,y 是来自分布 B 的另一个域的图像。鉴别符![](img/c420501d-952a-4e5b-84b0-0aa891624a10.png)试图区分 G 映射(![](img/e57cd8de-afc5-4e00-a0d4-b87786d7318f.png))生成的图像和实数 图像 y 来自不同的分布 B。鉴别器![](img/e0cef545-08a1-4089-a238-9b6945c1d821.png)试图区分 F 映射生成的图像(![](img/968121df-410e-4bff-9e76-57d447e34db5.png))和来自分布 A 的真实图像 x。G 的目的是使对抗损失函数最小 对抗对手 D,后者不断尝试使其最大化。 + + + + + +# 循环一致性损失 + + + +仅使用对抗损失的问题在于,网络可以将同一组输入图像映射到目标域中图像的任何随机排列。 因此,任何学习的映射都可以学习类似于目标分布的输出分布。 ![](img/95c36bf0-5253-4227-9d80-7ab5e826094a.png)和![](img/de2f63f2-3d6f-4cb6-90d0-9a9789508d31.png)之间可能有许多可能的映射功能。 循环一致性损失通过减少可能的映射数来克服了这个问题。 周期一致映射功能是可以将图像 x 从域 A 转换为域 B 中的另一个图像 y 并生成原始图像的功能。 + +前向循环一致性映射功能如下所示: + +![](img/b6e02c98-294c-42d4-b687-3c2c7e84644a.png) + +向后循环一致映射函数如下所示: + +![](img/bf736fcf-a789-4a17-b1a9-a452b23e07f9.png) + +循环一致性损失的公式如下: + +![](img/bae8d146-d70d-479e-bc91-801a8140d502.png) + +由于循环一致性损失,由 *F(G(x))*和 *G(F(y))*重构的图像分别类似于 x 和 y。 + + + + + +# 完整目标功能 + + + +完整目标函数是对抗损失和周期一致性损失两者的加权总和,表示如下: + +![](img/b72c39e8-2d9b-487b-83df-3b73ab2c550f.png) + +在此,![](img/a04311c3-8eb2-45e2-98c0-e691deb818e0.png)是第一个对抗性损失,![](img/c9de7008-1df0-4381-92f5-fcfc5aa276a3.png)是第二个对抗性损失。 在生成器 A 和鉴别器 B 上计算第一个对抗损失。在生成器 B 和鉴别器 A 上计算第二个对抗损失。 + +要训​​练 CycleGAN,我们需要优化以下功能: + +![](img/b0e30420-c79d-434a-b6d9-6fcaaa2a61ad.png) + +前面的等式表明,训练一个 CycleGAN,需要最小化发电机网络的损耗,并使鉴别器网络的损耗最大化。 优化之后,我们将获得一组训练有素的网络,能够从绘画中生成照片。 + + + + + +# 设置项目 + + + +如果尚未使用所有章节的完整代码克隆存储库,请立即克隆存储库。 下载的代码有一个名为`Chapter07`的目录,其中包含本章的全部代码。 执行以下命令来设置项目: + +1. 首先,导航到父目录,如下所示: + +```py +cd Generative-Adversarial-Networks-Projects +``` + +2. 现在,将目录从当前目录更改为 `Chapter07`,如以下示例所示: + +```py +cd Chapter07 +``` + +3. 接下来,为该项目创建一个 Python 虚拟环境,如以下代码所示: + +```py +virtualenv venv +virtualenv venv -p python3 # Create a virtual environment using + python3 interpreter +virtualenv venv -p python2 # Create a virtual environment using + python2 interpreter +``` + +我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。 + +4. 激活新创建的虚拟环境,如以下代码所示: + +```py +source venv/bin/activate +``` + +激活虚拟环境后,所有其他命令将在该虚拟环境中执行。 + +5. 通过执行以下命令,安装`requirements.txt` 文件中提供的所有库: + +```py +pip install -r requirements.txt +``` + +您可以参考 `README.md` 文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。 + +在本节中,我们已经成功设置了项目并安装了必需的依赖项。 在下一节中,我们将处理数据集。 + + + + + +# 下载数据集 + + + +在本章中,我们将使用`monet2photo`数据集。 该数据集是开放源代码,可以由 UC Berkeley 的 **Berkeley AI Research** ( **BAIR** )实验室使用。 您可以从以下链接选择手动下载数据集: [https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets/monet2photo.zip](https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets/monet2photo.zip) 。 + +下载后,将其解压缩到根目录中。 + +或者,要自动下载数据集,请执行以下命令: + +```py +wget https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets/monet2photo.zip +upzip monet2photo.zip +``` + +这些命令将下载数据集并将其解压缩到项目的根目录中。 + +The `monet2photo` dataset is available for educational purposes only. To use it in commercial projects, you have to get permission from the BAIR laboratory, UC Berkeley. We don't hold the copyright for the images available in the dataset. + + + +# 硬实施 CycleGAN + + + +如本章前面的 *CycleGAN 简介*部分中所述,CycleGAN 具有两种网络体系结构,即生成器网络和鉴别器网络。 在本节中,我们将编写所有网络的实现。 + +但是,在开始编写实现之前,请创建一个 Python 文件`main.py`并导入基本模块,如下所示: + +```py +from glob import glob +import matplotlib.pyplot as plt +import numpy as np +import tensorflow as tf +from keras import Input, Model +from keras.layers import Conv2D, BatchNormalization, Activation, Add, Conv2DTranspose, \ + ZeroPadding2D, LeakyReLU +from keras.optimizers import Adam +from keras_contrib.layers import InstanceNormalization +from scipy.misc import imread, imresize +``` + + + + + +# 发电机网络 + + + +在本章前面的*发电机网络的体系结构*部分中,我们已经探讨了发电机网络的体系结构。 让我们首先在 Keras 框架中编写生成器网络的层,然后使用 Keras 框架的功能 API 创建 Keras 模型。 + +执行以下步骤以在 Keras 中实现生成器网络: + +1. 首先定义生成器网络所需的超参数,如下所示: + +```py +input_shape = (128, 128, 3) +residual_blocks = 6 +``` + +2. 接下来,创建一个输入层,将输入馈送到网络,如下所示: + +```py +input_layer = Input(shape=input_shape) +``` + +3. 将第一个卷积块与先前在*生成器网络*部分的体系结构中指定的超参数相加,如下所示: + +```py +x = Conv2D(filters=32, kernel_size=7, strides=1, padding="same")(input_layer) +x = InstanceNormalization(axis=1)(x) +x = Activation("relu")(x) +``` + +4. 添加第二个卷积块,如下所示: + +```py +x = Conv2D(filters=64, kernel_size=3, strides=2, padding="same")(x) +x = InstanceNormalization(axis=1)(x) +x = Activation("relu")(x) +``` + +5. 添加第三个卷积块,如下所示: + +```py +x = Conv2D(filters=128, kernel_size=3, strides=2, padding="same")(x) +x = InstanceNormalization(axis=1)(x) +x = Activation("relu")(x) +``` + +6. 定义残差块,如下所示: + +```py +def residual_block(x): + """ + Residual block """ res = Conv2D(filters=128, kernel_size=3, strides=1, padding="same")(x) + res = BatchNormalization(axis=3, momentum=0.9, epsilon=1e-5)(res) + res = Activation('relu')(res) + + res = Conv2D(filters=128, kernel_size=3, strides=1, padding="same")(res) + res = BatchNormalization(axis=3, momentum=0.9, epsilon=1e-5)(res) + + return Add()([res, x]) +``` + +现在,使用`residual_block()`函数向模型添加六个残差块,如以下示例所示: + +```py +for _ in range(residual_blocks): + x = residual_block(x) +``` + +7. 接下来,添加一个升采样块,如下所示: + +```py +x = Conv2DTranspose(filters=64, kernel_size=3, strides=2, padding='same', use_bias=False)(x) +x = InstanceNormalization(axis=1)(x) +x = Activation("relu")(x) +``` + +8. 添加另一个升采样模块,如下所示: + +```py +x = Conv2DTranspose(filters=32, kernel_size=3, strides=2, padding='same', use_bias=False)(x) +x = InstanceNormalization(axis=1)(x) +x = Activation("relu")(x) +``` + +9. 最后,添加输出卷积层,如下所示: + +```py +x = Conv2D(filters=3, kernel_size=7, strides=1, padding="same")(x) +output = Activation('tanh')(x) +``` + +这是发电机网络的最后一层。 它生成形状为(128、128、3)的图像。 + +10. 现在,通过为网络指定`inputs`和`outputs`来创建 Keras 模型,如下所示: + +```py +model = Model(inputs=[input_layer], outputs=[output]) +``` + +发电机网络的整个代码如下所示: + +```py +def build_generator(): + """ + Create a generator network using the hyperparameter values defined below """ input_shape = (128, 128, 3) + residual_blocks = 6 + input_layer = Input(shape=input_shape) + + # First Convolution block + x = Conv2D(filters=32, kernel_size=7, strides=1, padding="same")(input_layer) + x = InstanceNormalization(axis=1)(x) + x = Activation("relu")(x) + + # 2nd Convolution block + x = Conv2D(filters=64, kernel_size=3, strides=2, padding="same")(x) + x = InstanceNormalization(axis=1)(x) + x = Activation("relu")(x) + + # 3rd Convolution block + x = Conv2D(filters=128, kernel_size=3, strides=2, padding="same")(x) + x = InstanceNormalization(axis=1)(x) + x = Activation("relu")(x) + + # Residual blocks + for _ in range(residual_blocks): + x = residual_block(x) + + # Upsampling blocks + # 1st Upsampling block + x = Conv2DTranspose(filters=64, kernel_size=3, strides=2, padding='same', use_bias=False)(x) + x = InstanceNormalization(axis=1)(x) + x = Activation("relu")(x) + + # 2nd Upsampling block + x = Conv2DTranspose(filters=32, kernel_size=3, strides=2, padding='same', use_bias=False)(x) + x = InstanceNormalization(axis=1)(x) + x = Activation("relu")(x) + + # Last Convolution layer + x = Conv2D(filters=3, kernel_size=7, strides=1, padding="same")(x) + output = Activation('tanh')(x) + + model = Model(inputs=[input_layer], outputs=[output]) + return model +``` + +我们已经成功地为发电机网络创建了 Keras 模型。 在下一节中,我们将为鉴别器网络创建 Keras 模型。 + + + + + +# 鉴别网络 + + + +我们已经在 *中探索了鉴别器网络的体系结构。*部分 让我们 首先在 Keras 框架中编写区分网络的层,然后使用 Keras 框架的功能 API 创建 Keras 模型。 + +执行以下步骤以在 Keras 中实现鉴别器网络: + +1. 首先定义鉴别器网络所需的超参数,如下所示: + +```py +input_shape = (128, 128, 3) +hidden_layers = 3 +``` + +2. 接下来,添加一个输入层,将输入馈送到网络,如下所示: + +```py +input_layer = Input(shape=input_shape) +``` + +3. 接下来,添加一个二维零填充层,如下所示: + +```py +x = ZeroPadding2D(padding=(1, 1))(input_layer) +``` + +该层将在 *x* 和 *y* 轴上为输入张量添加填充。 + +4. 接下来,使用先前在*鉴别器网络*部分的体系结构中指定的超参数添加卷积块,如下所示: + +```py +x = Conv2D(filters=64, kernel_size=4, strides=2, padding="valid")(x) +x = LeakyReLU(alpha=0.2)(x) +``` + +5. 接下来,添加另一个 2D 零填充层,如下所示: + +```py +x = ZeroPadding2D(padding=(1, 1))(x) +``` + +6. 接下来,使用先前在*鉴别器网络* 部分中指定的超参数添加三个卷积块,如下所示: + +```py +for i in range(1, hidden_layers + 1): + x = Conv2D(filters=2 ** i * 64, kernel_size=4, strides=2, padding="valid")(x) + x = InstanceNormalization(axis=1)(x) + x = LeakyReLU(alpha=0.2)(x) + x = ZeroPadding2D(padding=(1, 1))(x) +``` + +每个卷积块都有两个卷积层,实例规范化层,激活层和 2D 零填充层。 + +7. 现在,将最终(`output`)卷积层添加到网络,如下所示: + +```py +output = Conv2D(filters=1, kernel_size=4, strides=1, activation="sigmoid")(x) +``` + +8. 最后,通过指定网络的输入和输出来创建 Keras 模型,如下所示: + +```py +model = Model(inputs=[input_layer], outputs=[output]) +``` + +鉴别器网络的整个代码如下所示: + +```py +def build_discriminator(): + """ + Create a discriminator network using the hyperparameter values defined below """ input_shape = (128, 128, 3) + hidden_layers = 3 input_layer = Input(shape=input_shape) + + x = ZeroPadding2D(padding=(1, 1))(input_layer) + + # 1st Convolutional block + x = Conv2D(filters=64, kernel_size=4, strides=2, padding="valid")(x) + x = LeakyReLU(alpha=0.2)(x) + + x = ZeroPadding2D(padding=(1, 1))(x) + + # 3 Hidden Convolution blocks + for i in range(1, hidden_layers + 1): + x = Conv2D(filters=2 ** i * 64, kernel_size=4, strides=2, padding="valid")(x) + x = InstanceNormalization(axis=1)(x) + x = LeakyReLU(alpha=0.2)(x) + + x = ZeroPadding2D(padding=(1, 1))(x) + + # Last Convolution layer + output = Conv2D(filters=1, kernel_size=4, strides=1, activation="sigmoid")(x) + + model = Model(inputs=[input_layer], outputs=[output]) + return model +``` + +我们也已经成功地为鉴别器网络创建了 Keras 模型。 在下一部分中,我们将训练网络。 + + + + + +# 训练 CycleGAN + + + +我们已经在 *CycleGANs 简介*部分中介绍了训练目标功能。 我们还为两个网络分别创建了 Keras 模型。 训练 CycleGAN 是一个多步骤的过程。 我们将执行以下步骤来训练网络: + +1. 加载数据集 +2. 创建生成器和鉴别器网络 +3. 训练网络以达到指定次数 +4. 绘制损失 +5. 生成新图像 + +让我们在开始训练网络之前定义基本变量,如下所示: + +```py +data_dir = "/Path/to/dataset/directory/*.*" batch_size = 1 epochs = 500 +``` + + + + + +# 加载数据集 + + + +在执行其他任何操作之前,请执行以下步骤来加载数据集: + +1. 首先使用`glob`模块创建图像路径列表,如下所示: + +```py +imagesA = glob(data_dir + '/testA/*.*') +imagesB = glob(data_dir + '/testB/*.*') +``` + +我们具有来自两个域 A 和 B 的数据,这就是为什么我们创建了两个列表的原因。 + +2. 接下来,遍历列表。 在循环中加载,调整大小和水平翻转图像,如下所示: + +```py +allImagesA = [] +allImagesB = [] + # Iterate over the lists +for index, filename in enumerate(imagesA): + # Load images + imgA = imread(filename, mode='RGB') + imgB = imread(imagesB[index], mode='RGB') + + # Resize images + imgA = imresize(imgA, (128, 128)) + imgB = imresize(imgB, (128, 128)) + + # Randomly horizontally flip images + if np.random.random() > 0.5: + imgA = np.fliplr(imgA) + imgB = np.fliplr(imgB) + + allImagesA.append(imgA) + allImagesB.append(imgB) +``` + +3. 现在,对图像进行归一化以使像素值在-1 和 1 之间的范围内,如下所示: + +```py +# Normalize images allImagesA = np.array(allImagesA) / 127.5 - 1. allImagesB = np.array(allImagesB) / 127.5 - 1. +``` + +加载数据集的整个代码如下所示: + +```py +def load_images(data_dir): + imagesA = glob(data_dir + '/testA/*.*') + imagesB = glob(data_dir + '/testB/*.*') + + allImagesA = [] + allImagesB = [] + + for index, filename in enumerate(imagesA): + # Load images + imgA = imread(filename, mode='RGB') + imgB = imread(imagesB[index], mode='RGB') + + # Resize images + imgA = imresize(imgA, (128, 128)) + imgB = imresize(imgB, (128, 128)) + + # Randomly horizontally flip images + if np.random.random() > 0.5: + imgA = np.fliplr(imgA) + imgB = np.fliplr(imgB) + + allImagesA.append(imgA) + allImagesB.append(imgB) + + # Normalize images + allImagesA = np.array(allImagesA) / 127.5 - 1. + allImagesB = np.array(allImagesB) / 127.5 - 1. return allImagesA, allImagesB +``` + +前面的函数将返回两个 Numpy ndarray。 在开始训练之前,我们将使用它来加载和预处理图像。 + + + + + +# 建立和编译网络 + + + +在本节中,让我们构建必要的网络并准备进行培训。 执行以下步骤: + +1. 首先定义培训所需的优化器,如以下代码所示: + +```py +# Define the common optimizer common_optimizer = Adam(0.0002, 0.5) +``` + +我们将使用`Adam`优化器,其中`learning_rate`等于 0.0002,并且`beta_1`值等于 0.5。 + +2. 首先创建鉴别器网络,如以下代码所示: + +```py +discriminatorA = build_discriminator() +discriminatorB = build_discriminator() +``` + +如*鉴别器网络的架构*部分所述,CycleGAN 具有两个鉴别器网络。 + +3. 接下来,编译网络,如下所示: + +```py +discriminatorA.compile(loss='mse', optimizer=common_optimizer, metrics=['accuracy']) +discriminatorB.compile(loss='mse', optimizer=common_optimizer, metrics=['accuracy']) +``` + +使用`mse`作为损失函数,并使用`accuracy`作为度量标准来编译网络。 + +4. 接下来,创建发电机网络 A(`generatorAToB`)和 B(`generatorBToA`)。 生成器网络 A 的输入是数据集 A 的真实图像(realA),输出将是重构图像(fakeB)。 生成器网络 B 的输入是来自数据集 B 的真实图像(realB),输出将是重构图像(fakeA),如下所示: + +```py +generatorAToB = build_generator() +generatorBToA = build_generator() +``` + +如 *CycleGAN 的体系结构*部分所述,CycleGAN 具有两个生成器网络。 `generatorAToB`会将图像从域 A 转换为域 B。`generatorBToA`会将图像从域 B 转换为域 A。 + +现在,我们已经创建了两个生成器网络和两个鉴别器网络。 在下一个小节中,我们将创建并编译一个对抗网络。 + + + + + +# 创建和编译对抗网络 + + + +对抗网络是一个组合网络。 它在单个 Keras 模型中使用所有四个网络。 创建对抗网络的主要目的是训练生成器网络。 当我们训练对抗网络时,它只训练生成器网络,但冻结了鉴别器网络的训练。 让我们创建一个具有所需功能的对抗模型。 + +1. 首先为网络创建两个输入层,如下所示: + +```py +inputA = Input(shape=(128, 128, 3)) +inputB = Input(shape=(128, 128, 3)) +``` + +两个输入都将拍摄尺寸为(128、128、3)的图像。 这些是符号输入变量,不包含实际值。 它们用于创建 Keras 模型(TensorFlow 图)。 + +2. 接下来,使用生成器网络生成伪造图像,如下所示: + +```py +generatedB = generatorAToB(inputA) +generatedA = generatorBToA(inputB) +``` + +使用符号输入层生成图像。 + +3. 现在,再次使用生成器网络重建原始图像,如下所示: + +```py +reconstructedA = generatorBToA(generatedB) +reconstructedB = generatorAToB(generatedA) +``` + +4. 使用生成器网络生成伪造图像,如下所示: + +```py +generatedAId = generatorBToA(inputA) +generatedBId = generatorAToB(inputB) +``` + +生成器网络 A(`generatorAToB`)将图像从域 A 转换为域 B。类似地,生成器网络 B(`generatorBToA`)将图像从域 B 转换为域 A。 + +5. 接下来,使两个鉴别器网络均不可训练,如下所示: + +```py +discriminatorA.trainable = False discriminatorB.trainable = False +``` + +我们不想在我们的对抗网络中训练鉴别器网络。 + +6. 使用鉴别器网络来预测每个生成的图像是真实的还是伪造的,如下所示: + +```py +probsA = discriminatorA(generatedA) +probsB = discriminatorB(generatedB) +``` + +7. 创建 Keras 模型并指定网络的输入和输出,如下所示: + +```py +adversarial_model = Model(inputs=[inputA, inputB],outputs=[probsA, probsB, reconstructedA, reconstructedB, generatedAId, generatedBId]) +``` + +我们的对抗网络将采用两个输入值(即张量),并返回六个输出值(即张量)。 + +8. 接下来,按如下所示编译对抗网络: + +```py +adversarial_model.compile(loss=['mse', 'mse', 'mae', 'mae', 'mae', 'mae'], + loss_weights=[1, 1, 10.0, 10.0, 1.0, 1.0], + optimizer=common_optimizer) +``` + +对抗网络返回六个值,我们需要为每个输出值指定损失函数。 对于前两个值,我们使用**均方误差**损失,因为这是**对抗性损失**的一部分。 对于接下来的四个值,我们使用**平均绝对误差**损失,这是周期一致性损失的一部分。 六个不同损失的权重值为 1,1,10.0,10.0,1.0,1.0。 我们正在使用`common_optimizer`训练网络。 + +现在,我们已经成功为对抗网络创建了 Keras 模型。 如果您在理解 Keras 模型的工作方式时遇到困难,请查看 TensorFlow 图及其功能的文档。 + +在开始培训之前,请执行以下两个基本步骤。 TensorBoard 将在后面的部分中使用: + +添加 TensorBoard 来存储损失和图形以用于可视化目的,如下所示: + +```py +tensorboard = TensorBoard(log_dir="logs/{}".format(time.time()), write_images=True, write_grads=True, + write_graph=True) +tensorboard.set_model(generatorAToB) +tensorboard.set_model(generatorBToA) +tensorboard.set_model(discriminatorA) +tensorboard.set_model(discriminatorB) +``` + +创建一个包含所有等于 1 的值的 4 维数组,该数组表示真实标签。 同样,创建另一个所有值均等于零的三维数组,其中 代表伪标签,如下所示: + +```py +real_labels = np.ones((batch_size, 7, 7, 1)) +fake_labels = np.zeros((batch_size, 7, 7, 1)) +``` + +使用 numpy 的`ones()`和`zeros()`函数创建所需的 ndarray。 现在我们已经准备好基本组件,让我们开始培训。 + + + + + +# 开始训练 + + + +要针对指定的时期数训练网络,请执行以下步骤: + +1. 首先加载两个域的数据集,如下所示: + +```py +imagesA, imagesB = load_images(data_dir=data_dir) +``` + +我们已经在*中定义了`load_images`功能。* + +2. 接下来,创建一个`for`循环,该循环应运行由时期数指定的次数,如下所示: + +```py +for epoch in range(epochs): + print("Epoch:{}".format(epoch)) +``` + +3. 创建两个列表以存储所有迷你批次的损失,如下所示: + +```py +dis_losses = [] +gen_losses = [] +``` + +4. 计算`epochs`循环内的迷你批处理数量,如下所示: + +```py +num_batches = int(min(imagesA.shape[0], imagesB.shape[0]) / + batch_size) +print("Number of batches:{}".format(num_batches)) +``` + +5. 接下来,在 epochs 循环内创建另一个循环,并使其运行`num_batches`指定的次数,如下所示: + +```py + for index in range(num_batches): + print("Batch:{}".format(index)) +``` + +我们用于区分网络和对抗网络训练的整个代码将在此循环内。 + + + + + +# 训练鉴别器网络 + + + +本小节中的代码是上一节中代码的延续。 在这里,您将看到如何训练鉴别器网络: + +1. 首先对两个域的图像进行小批量采样,如以下代码所示: + +```py + batchA = imagesA[index * batch_size:(index + 1) * batch_size] + batchB = imagesB[index * batch_size:(index + 1) * batch_size] +``` + +2. 接下来,使用生成器网络生成伪造图像,如下所示: + +```py + generatedB = generatorAToB.predict(batchA) + generatedA = generatorBToA.predict(batchB) +``` + +3. 然后,对鉴别器网络 A 进行真实图像和伪图像(由生成器网络生成)的训练,如下所示: + +```py + dALoss1 = discriminatorA.train_on_batch(batchA, real_labels) + dALoss2 = discriminatorB.train_on_batch(generatedA, fake_labels) +``` + +此步骤将在真实图像和伪图像的微型批次上训练鉴别器 A,并会稍微改善网络。 + +4. 接下来,对鉴别器 B 进行真实图像和伪图像的训练,如下所示: + +```py +dBLoss1 = discriminatorB.train_on_batch(batchB, real_labels) +dbLoss2 = discriminatorB.train_on_batch(generatedB, fake_labels) +``` + +5. 现在,计算鉴别器网络的总损耗值,如下所示: + +```py +d_loss = 0.5 * np.add(0.5 * np.add(dALoss1, dALoss2), 0.5 * + np.add(dBLoss1, dbLoss2)) +``` + +到目前为止,我们一直在添加代码来训练鉴别器网络。 在下一部分中,我们将训练对抗性网络以训练生成器网络。 + + + + + +# 训练对抗网络 + + + +为了训练对抗网络,我们需要输入值和地面真实值。 网络的输入值为`batchA`和`batchB`。 基本真值是`real_labels`,`real_labels`,`batchA`,`batchB`,`batchA`和`batchB`,如下所示: + +```py + g_loss = adversarial_model.train_on_batch([batchA, batchB], + [real_labels, real_labels, batchA, batchB, batchA, batchB]) +``` + +此步骤将训练发电机网络,而无需训练发电机网络。 + +在每个微型批处理上完成一次迭代(循环)之后,将损耗存储在名为`dis_losses`和`gen_losses`的列表中,如下所示: + +```py + dis_losses.append(d_loss) + gen_losses.append(g_loss) +``` + +每 10 个周期后,使用生成器网络生成一组图像: + +```py +# Sample and save images after every 10 epochs if epoch % 10 == 0: + # Get a batch of test data + batchA, batchB = load_test_batch(data_dir=data_dir, batch_size=2) + + # Generate images + generatedB = generatorAToB.predict(batchA) + generatedA = generatorBToA.predict(batchB) + + # Get reconstructed images + reconsA = generatorBToA.predict(generatedB) + reconsB = generatorAToB.predict(generatedA) + + # Save original, generated and reconstructed images + for i in range(len(generatedA)): + save_images(originalA=batchA[i], generatedB=generatedB[i], recosntructedA=reconsA[i], + originalB=batchB[i], generatedA=generatedA[i], reconstructedB=reconsB[i], + path="results/gen_{}_{}".format(epoch, i)) +``` + +将前面的代码块放入`epochs`循环中。 每隔 10 个时间段,它将生成一批伪图像并将其保存到结果目录。 + +接下来,将平均损失存储到 TensorBoard 中以进行可视化。 既存储损失,也要存储生成器网络的平均损失和鉴别器网络的平均损失,如以下示例所示: + +```py +write_log(tensorboard, 'discriminator_loss', np.mean(dis_losses), + epoch) +write_log(tensorboard, 'generator_loss', np.mean(gen_losses), epoch) +``` + +将前面的代码块放入`epochs`循环中。 + + + + + +# 保存模型 + + + +在 Keras 中保存模型只需要一行代码。 要保存生成器模型,请添加以下行: + +```py +# Specify the path for the generator A model +generatorAToB.save("directory/for/the/generatorAToB/model.h5") + +# Specify the path for the generator B model +generatorBToA.save("directory/for/the/generatorBToA/model.h5") +``` + +同样,通过添加以下行来保存鉴别器模型: + +```py +# Specify the path for the discriminator A model +discriminatorA.save("directory/for/the/discriminatorA/model.h5") + +# Specify the path for the discriminator B model +discriminatorB.save("directory/for/the/discriminatorB/model.h5") +``` + + + + + +# 可视化生成的图像 + + + +在将网络训练了 100 个时期之后,网络将开始生成体面的图像。 让我们看一下生成器网络生成的图像。 + +10 个时期后,图像显示如下: + +![](img/50a98c0c-f195-40b5-9025-436b7d52523f.png) + +在 20 个时代之后,图像显示如下: + +![](img/34d03920-182c-4630-bba6-25d7f728c1f6.png) + +我建议您将网络训练 1000 个纪元。 如果一切顺利,则在 1000 个时期之后,生成器网络将开始生成逼真的图像。 + + + + + +# 可视化损失 + + + +为了可视化训练的损失,请按以下方式启动 TensorBoard 服务器: + +```py +tensorboard --logdir=logs +``` + +现在,在浏览器中打开`localhost:6006`。 TensorBoard 的**标量**部分包含两个损失的图,如以下示例所示: + +鉴别器网络的损耗图如下所示: + +![](img/69b11dd7-e38d-46ad-a293-69d3827daf0d.png) + +发电机网络的损耗图如下所示: + +![](img/4b6517ec-d185-4ec3-a4f9-acfbba2ee45f.png) + +这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止培训,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。 + + + + + +# 可视化图形 + + + +TensorBoard 的 GRAPHS 部分包含两个网络的图形。 如果网络性能不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作,如以下示例所示: + +![](img/9bd3d80d-905f-4e80-8eb0-ee3827a69a8c.png) + + + + + +# CycleGAN 的实际应用 + + + +CycleGAN 有许多应用。 在本章中,我们使用了 CycleGAN 将画作转换为照片。 它们还可以在以下情况下使用: + +* **样式转换**:例如,将 p 个 hotos 转换为绘画,反之亦然,将马的图像转换为斑马,反之亦然,将橘子的图像转换为苹果,反之亦然 +* **照片增强**:CycleGAN 可用于增强图片质量 +* **季节转换**:例如,将冬天的图片变成夏天的图片,反之亦然 +* **游戏风格转移**:CycleGAN 可用于将游戏 A 的风格转移到游戏 B + + + + + +# 概要 + + + +在本章中,我们学习了如何使用 CycleGAN 将绘画转换为照片。 我们从介绍 CyleGAN 入手,并探讨了 CycleGAN 所涉及的网络架构。 我们还探讨了训练 CycleGAN 所需的不同损失函数。 接下来是在 Keras 框架中实施 CycleGAN。 我们在`monet2photo`数据集上训练了 CycleGAN,并可视化了生成的图像,损耗和不同网络的图形。 在结束本章之前,我们探讨了 CycleGAN 的实际用例。 + +在下一章中,我们将在 pix2pix 网络上进行图像到图像的翻译。 在 pix2pix 中,我们将探索条件 GAN 进行图像翻译。 + + + + + +# 进一步阅读 + + + +CycleGAN 有许多已知的用例。 使用以下文章寻求帮助,探索新用途: + +* *通过深度学习(CycleGAN)将 Fortnite 变成 PUBG:* [https://towardsdatascience.com/turning-fortnite-into-pubg-with-deep-learning-cyclegan- 2f9d339dcdb0](https://towardsdatascience.com/turning-fortnite-into-pubg-with-deep-learning-cyclegan-2f9d339dcdb0) +* [*GAN-CycleGAN(用图片玩魔术):*](https://towardsdatascience.com/turning-fortnite-into-pubg-with-deep-learning-cyclegan-2f9d339dcdb0) [https://medium.com/@jonathan_hui/gan-cyclegan-6a50e7600d7](https://medium.com/@jonathan_hui/gan-cyclegan-6a50e7600d7) +* *CycleGANs 简介:* [https://medium.com/coding-blocks/introduction-to-cyclegans-1dbdb8fbe781](https://medium.com/coding-blocks/introduction-to-cyclegans-1dbdb8fbe781) +* *在 TensorFlow 中理解和实现 CycleGAN:* [https://hardikbansal.github.io/CycleGANBlog/](https://hardikbansal.github.io/CycleGANBlog/) + + diff --git a/new/gan-proj/8.md b/new/gan-proj/8.md new file mode 100644 index 0000000000000000000000000000000000000000..e6254975d748312187bf1fa5cb927d807c4fd579 --- /dev/null +++ b/new/gan-proj/8.md @@ -0,0 +1,1422 @@ + + +# 条件 GAN-使用条件对抗网络的图像到图像翻译 + + + +Pix2pix 是一种**生成对抗网络**( **GAN** ),用于图像到图像的翻译。 图像到图像转换是一种将图像的一种表示形式转换为另一种表示形式的方法。 Pix2pix 学习从输入图像到输出图像的映射。 它可用于将黑白图像转换为彩色图像,将草图转换为照片,将白天图像转换为夜间图像,将卫星图像转换为地图图像。 pix2pix 网络最早是由菲利普·伊索拉(Phillip Isola),朱俊彦,周婷慧,阿列克谢·埃弗罗斯(Alexei A. Efros)在名为*和条件对抗网络*的图像到图像转换中引入的。 可以在以下链接中找到: [https://arxiv.org/pdf/1611.07004.pdf](https://arxiv.org/pdf/1611.07004.pdf) 。 + +在本章中,我们将介绍以下主题: + +* 介绍 Pix2pix 网络 +* Pix2pix 网络的架构 +* 数据收集与准备 +* Pix2pix 的 Keras 实现 +* 目标功能 +* 培训 Pix2pix +* 评估训练好的模型 +* Pix2pix 网络的实际应用 + + + + + +# 介绍 Pix2pix + + + +Pix2pix 是条件 GAN 的变体。 我们已经在 [第 3 章](../Text/3.html)和*中使用条件 GAN(cGAN)*进行面部老化处理。 在继续之前,请确保您了解什么是 cGAN。 一旦熟悉了 cGAN,就可以继续本章。 Pix2pix 是一种 GAN,能够使用**机器学习**( **ML** )的无监督方法执行图像到图像的翻译。 经过训练后,pix2pix 可以将图像从域 A 转换为域 B。香草 CNN 也可以用于图像到图像的转换,但是它们不会生成逼真的图像。 另一方面,pix2pix 显示出巨大的潜力,能够生成逼真的图像。 我们将训练 pix2pix 将立面的标签转换为立面的图像。 让我们从了解 pix2pix 的体系结构开始。 + + + + + +# pix2pix 的体系结构 + + + +与其他 GAN 相似,pix2pix 由两个网络组成:生成器网络和鉴别器网络。 发电机网络的体系结构受 U-Net( [https://arxiv.org/pdf/1505.04597.pdf](https://arxiv.org/pdf/1505.04597.pdf) )的体系结构的启发。 同样,鉴别器网络的架构也受到 PatchGAN( [https://arxiv.org/pdf/1604.04382.pdf](https://arxiv.org/pdf/1604.04382.pdf) )架构的启发。 这两个网络都是深度卷积神经网络。 在本节中,我们将详细探讨 pix2pix。 + + + + + +# 发电机网络 + + + +正如我们在上一节中提到的,发电机网络在很大程度上受到 U-Net 架构的启发。 U-Net 的架构与自动编码器网络的架构几乎相同。 它们之间的主要区别在于,U-Net 网络在编码器中的各层之间具有跳过连接,并且生成器网络和自动编码器的解码器部分不具有跳过连接。 U-Net 网络由两个网络组成:编码器网络和解码器网络。 下图从基本层面说明了 U-Net 的体系结构: + +![](img/ba22d993-cf32-42a1-86b9-94c2b37311e8.png) + +上图应使您了解 U-Net 的体系结构。 如您所见,第一层的输出直接与最后一层合并。 第二层的输出与第二层的第二层合并,依此类推。 如果 *n* 是层的总数,则在编码器网络的第 i th 层与解码器的第[ *ni* ]层之间存在跳过连接 网络。 层可以是这些层中的任何层。 让我们一个一个地仔细研究两个网络。 + + + + + +# 编码器网络 + + + +编码器网络是发电机网络的初始网络,包含八个**卷积块**,其配置如下: + +| **图层名称** | **超参数** | **输入形状** | **输出形状** | +| 第一 2D 卷积层 | 过滤器= 64,kernel_size = 4,步幅= 2,padding ='same', | (256, 256, 1) | (128, 128, 64) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | (128、128、64) | ( 128、128、64) | +| 第二 2D 卷积层 | 过滤器= 128,kernel_size = 4,步幅= 2,padding =“相同”, | (128, 128, 64) | (64, 64, 128) | +| 批次归一化层 | 没有 | ( 64、64、128) | ( 64、64、128) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | ( 64、64、128) | ( 64、64、128) | +| 第三 2D 卷积层 | 过滤器= 256,kernel_size = 4,步幅= 2,padding ='same', | ( 64、64、128) | (32, 32, 256) | +| 批次归一化层 | 没有 | (32、32、256) | (32、32、256) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | (32、32、256) | (32, 32, 256) | +| 第四层 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 2,padding =“相同”, | (32, 32, 256) | (16, 16, 512) | +| 批次归一化层 | 没有 | (16,16,512) | (16,16,512) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | (16,16,512) | (16,16,512) | +| 第五层 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 2,padding =“相同”, | (16, 16, 512) | (8, 8, 512) | +| 批次归一化层 | 没有 | ( 8,8,512) | ( 8,8,512) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | ( 8,8,512) | ( 8,8,512) | +| 第六层 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 2,padding =“相同”, | (8, 8, 512) | (4, 4, 512) | +| 批次归一化层 | 没有 | ( 4,4,512) | ( 4,4,512) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | ( 4,4,512) | ( 4,4,512) | +| 第七层 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 2,padding =“相同”, | (4, 4, 512) | (2, 2, 512) | +| 批次归一化层 | 没有 | ( 2,2,512) | ( 2,2,512) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | ( 2,2,512) | ( 2,2,512) | +| 第八层 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 2,padding =“相同”, | (2, 2, 512) | (1, 1, 512) | +| 批次归一化层 | 没有 | ( 1,1,512) | ( 1,1,512) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | ( 1,1,512) | ( 1,1,512) | + +编码器网络之后是解码器网络。 我们将在下一节中了解解码器网络的体系结构。 + + + + + +# 解码器网络 + + + +发生器网络中的解码器网络还包括八个**上采样卷积块**。 八个上采样卷积块的配置如下: + +| **图层名称** | **超参数** | **输入形状** | **输出形状** | +| 第一 2D 上采样层 | 大小=(2,2) | ( 1,1,512) | ( 2,2,512) | +| 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 1,填充=“相同”, | (2, 2, 512) | (2, 2, 512) | +| 批次归一化层 | 没有 | ( 2,2,512) | ( 2,2,512) | +| 辍学层 | 辍学= 0.5 | (2, 2, 512) | ( 2,2,512) | +| 级联层(编码器网络中的第 7 个 Conv 层) | 轴= 3 | (2, 2, 512) | (2, 2, 1024) | +| 激活层 | 激活=“恢复” | (2,2,1024) | ( 2,2,1024) | +| 第二 2D 上采样层 | 大小=(2,2) | ( 2,2,1024) | (4,4,1024 ) | +| 2D 卷积层 | 过滤器= 1024,kernel_size = 4,步幅= 1,填充=“相同”, | (4,4,1024 ) | ( 4,4,1024 ) | +| 批次归一化层 | 没有 | ( 4,4,1024 ) | ( 4,4,1024 ) | +| 辍学层 | 辍学= 0.5 | ( 4,4,1024 ) | ( 4,4,1024 ) | +| 级联层(编码器网络中的第 6 个 Conv 层) | 轴= 3 | ( 4,4,1024 ) | ( 4,4,1536 ) | +| 激活层 | 激活=“恢复” | ( 4,4,1536) | ( 4,4,1536 ) | +| 第三 2D 上采样层 | 大小=(2,2) | ( 4,4,1536) | (8 ,8、1536 ) | +| 2D 卷积层 | 过滤器= 1024,kernel_size = 4,步幅= 1,填充=“相同”, | (8, 8, 1536) | (8, 8, 1024) | +| 批次归一化层 | 没有 | ( 8,8,1024) | ( 8,8,1024) | +| 辍学层 | 辍学= 0.5 | ( 8,8,1024) | ( 8,8,1024) | +| 级联层(编码器网络中的第 5 个 Conv 层) | 轴= 3 | ( 8,8,1024) | (8, 8, 1536) | +| 激活层 | 激活=“恢复” | (8,8,1536) | ( 8,8,1536 ) | +| 第四层 2D 上采样层 | 大小=(2,2) | ( 8,8,1536 ) | ( 16,16,1536) | +| 2D 卷积层 | 过滤器= 1024,kernel_size = 4,步幅= 1,填充=“相同”, | (16, 16, 1536) | (16, 16, 1024) | +| 批次归一化层 | 没有 | ( 16,16,1024) | ( 16,16,1024) | +| 串联层(编码器网络的第 4 个 Conv 层) | 轴= 3 | ( 16,16,1024) | (16, 16, 1536) | +| 激活层 | 激活=“恢复” | ( 16,16,1536) | ( 16,16,1536) | +| 第五层 2D 上采样层 | 大小=(2,2) | ( 16,16,1536 ) | ( 32、32、1536) | +| 2D 卷积层 | 过滤器= 1024,kernel_size = 4,步幅= 1,填充=“相同”, | ( 32、32、1536) | (32, 32, 1024) | +| 批次归一化层 | 没有 | ( 32、32、1024) | ( 32、32、1024) | +| 串联层(编码器网络的第 3 个 Conv 层) | 轴= 3 | ( 32、32、1024) | ( 32、32、1280) | +| 激活层 | 激活=“恢复” | ( 32、32、1280) | ( 32、32、1280) | +| 第六层 2D 上采样层 | 大小=(2,2) | ( 64、64、1280) | ( 64、64、1280) | +| 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 1,填充=“相同”, | ( 64、64、1280) | ( 64、64、512) | +| 批次归一化层 | 没有 | ( 64、64、512) | ( 64、64、512) | +| 串联层(编码器网络中的第二个 Conv 层) | 轴= 3 | ( 64、64、512) | ( 64、64、640) | +| 激活层 | 激活=“恢复” | ( 64、64、640) | ( 64、64、640) | +| 第七层 2D 上采样层 | 大小=(2,2) | ( 64、64、640) | ( 128、128、640) | +| 2D 卷积层 | 过滤器= 256,kernel_size = 4,步幅= 1,填充=“相同”, | ( 128、128、640) | ( 128、128、256) | +| 批次归一化层 | 没有 | ( 128、128、256) | ( 128、128、256) | +| 串联层(编码器网络中的第一个转化层) | 轴= 3 | ( 128、128、256) | ( 128、128、320) | +| 激活层 | 激活=“恢复” | ( 128、128、320) | ( 128、128、320) | +| 第八层 2D 上采样层 | 大小=(2,2) | ( 128、128、320) | ( 256、256、320) | +| 2D 卷积层 | 过滤器= 1,kernel_size = 4,步幅= 1,填充=“相同”, | ( 256、256、320) | ( 256,256,1) | +| 激活层 | 激活='tanh' | ( 256、256、1) | ( 256,256,1) | + +生成器网络具有**七个跳过连接**,可以将其定义如下: + +* 第 1 个编码器模块的输出到第个第 7 个 解码器模块的输出。 +* 从第 2 个编码器模块到 的输出的串联第 6 个 解码器模块 +* 从第 3 个编码器模块到 的第 5 个 解码器模块的输出的并置 +* 从第 4 个编码器模块到 第 4 个 解码器模块的输出的串联 +* 从第 5 个编码器模块到 第 3 个 解码器模块的输出的串联 +* 从第 6 个编码器模块到 第 2 个 解码器模块的输出串联 +* 将第 7 个编码器模块的输出连接到第一个 st 解码器模块的 + +串联沿着通道轴发生。 编码器网络的最后一层将张量传递到解码器网络的第一层。 在编码器网络的最后一块和解码器网络的最后一块没有串联。 + +发电机网络由这两个网络组成。 基本上,编码器网络是下采样器,而解码器网络是上采样器。 编码器网络 d 自己对尺寸为(256、256、1)的图像采样为尺寸为(1、1、1、512)的内部表示。 另一方面,解码器网络将 的内部表示以(1、1、1,512)维度上采样为(256、256、1)维度的输出图像 。 + +We will cover more on the architecture in the *Keras implementation of pix2pix* section. + + + +# 鉴别网络 + + + +pix2pix 中鉴别器网络的架构受到 PatchGAN 网络架构的启发。 PatchGAN 网络包含八个卷积块,如下所示: + +| **图层名称** | **超参数** | **输入形状** | **输出形状** | +| 第一 2D 卷积层 | 过滤器= 64,kernel_size = 4,步幅= 2,padding ='same', | (256, 256, 1) | ( 256、256、64) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | (128、128、64 ) | (128,128,64 ) | +| 第二 2D 卷积层 | 过滤器= 128,kernel_size = 4,步幅= 2,padding =“相同”, | (128, 128, 64) | (64, 64, 128) | +| 批次归一化层 | 没有 | ( 64、64、128) | ( 64、64、128) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | ( 64、64、128)[ | ( 64、64、128) | +| 第三 2D 卷积层 | 过滤器= 256,kernel_size = 4,步幅= 2,padding ='same', | ( 64、64、128) | ( 32、32、256) | +| 批次归一化层 | 没有 | ( 32、32、256) | ( 32、32、256) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | ( [ 32,32,256) | ( 32、32、256) | +| 第四层 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 2,padding =“相同”, | ( 32、32、256) | (16, 16, 512) | +| 批次归一化层 | 没有 | (16,16,512 ) | (16,16,512 ) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | (16、16、512 ) | (16,16,512 ) | +| 第五层 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 2,padding =“相同”, | (16,16,512 ) | (8, 8, 512) | +| 批次归一化层 | 没有 | (8, 8, 512) | (8, 8, 512) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | (8、8、512) | (8, 8, 512) | +| 第六层 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 2,padding =“相同”, | (8, 8, 512) | (4, 4, 512) | +| 批次归一化层 | 没有 | (4, 4, 512) | (4, 4, 512) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | (4、4、512) | (4, 4, 512) | +| 第七层 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 2,padding =“相同”, | (4, 4, 512) | (2, 2, 512) | +| 批次归一化层 | 没有 | ( 2,2,512) | ( 2,2,512) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | ( [ 2,2,512) | ( 2,2,512) | +| 第八层 2D 卷积层 | 过滤器= 512,kernel_size = 4,步幅= 2,padding =“相同”, | (4, 4, 512) | (1, 1, 512) | +| 批次归一化层 | 没有 | ( 1,1,512) | ( 1,1,512) | +| 激活层 | Activation ='leakyrelu',alpha = 0.2 | ( [ 1,1,512) | ( 1,1,512) | +| 展平层 | 没有 | (1、1、512) | (512, ) | +| 致密层 | 单位= 2,激活='softmax' | (1、1、512) | (2, ) | + +下表突出显示了鉴别器网络的体系结构和配置。 fl1 tten 层将张量展平为一维数组。 + +The remaining layers in the discriminator network are covered in the *The Keras implementation of pix2pix* section of this chapter. + +现在,我们已经探索了这两个网络的体系结构和配置。 现在,我们将探讨训练 pix2pix 所需的训练目标函数。 + + + + + +# 训练目标功能 + + + +Pix2pix 是一个条件生成对抗网络,并且对于 c 附加 GAN s 的目标函数可以表示为: + +![](img/a2a19bc3-64c1-48bd-b2d8-4b1303293dce.png) + +在这里,网络 *G* (生成器)正试图使针对对手 *D* (鉴别器)的先前功能最小化,而对手 *D* 则试图使先前的功能最大化 功能。 + +如果必须比较香草 GAN 和条件 GAN 的目标函数,则香草 GAN 的目标函数如下: + +![](img/b2a6e013-9ffb-4a51-93fb-91454976c9c4.png) + +为了减少图像的模糊,我们可以向目标函数添加一个 L1 损失函数。 L1 损失函数可以表示为: + +![](img/e5f5e27a-31d3-4fcc-a69c-3fd555c755b4.png) + +在该等式中, *y* 是原始图像, *G(x,z)*是生成器网络生成的图像。 L1 损失是由原始图像的所有像素值与生成的图像的所有像素值之间的所有绝对差值的总和[t HTG4]来计算的。 + +pix2pix 的最终目标函数如下: + +![](img/aea66002-edd3-4c8f-bea7-91ebc8e0aaf4.png) + +这是条件 GAN 的损失函数和 L1 损失函数的加权和。 + +现在我们对 pix2pix 网络有了基本的了解。 在开始在 Keras 中实现 pix2pix 之前,让我们设置项目。 + + + + + +# 设置项目 + + + +如果尚未使用所有章节的完整代码克隆存储库,请立即克隆存储库。 克隆的存储库有一个名为`Chapter09`的目录,其中包含本章的全部代码。 执行以下命令来设置项目: + +1. 首先,导航到父目录,如下所示: + +```py +cd Generative-Adversarial-Networks-Projects +``` + +2. 现在,将目录从当前目录更改为 `Chapter09`: + +```py +cd Chapter09 +``` + +3. 接下来,为该项目创建一个 Python 虚拟环境: + +```py +virtualenv venv +virtualenv venv -p python3 # Create a virtual environment using python3 interpreter +virtualenv venv -p python2 # Create a virtual environment using python2 interpreter +``` + +我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。 + +4. 接下来,激活新创建的虚拟环境: + +```py +source venv/bin/activate +``` + +激活虚拟环境后,所有其他命令将在此虚拟环境中执行。 + +5. 接下来,通过执行以下命令,安装`requirements.txt` 文件中提供的所有库: + +```py +pip install -r requirements.txt +``` + +您可以参考 `README.md` 文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。 + +在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一部分中,我们将处理数据集。 现在,我们将探讨下载和格式化数据集所需的各个步骤。 + + + + + +# 准备数据 + + + +在本章中,我们将使用 Facades 数据集,该数据集可从以下链接获得: + +[ht](http://efrosgans.eecs.berkeley.edu/pix2pix/datasets/facades.tar.gz) [tp://efrosgans.eecs.berkeley.edu/pix2pix/datasets/facades.tar.gz](http://efrosgans.eecs.berkeley.edu/pix2pix/datasets/facades.tar.gz) 。 + +该数据集包含立面标签和地面真实立面图像。 外观通常是建筑物的正面,外观标签是外观图像的建筑标签。 下载数据集后,我们将了解有关立面的更多信息。 执行以下命令以下载和提取数据集: + +1. 通过执行以下命令下载数据集: + +```py +# Before downloading the dataset navigate to data directory +cd data + +# Download the dataset +wget http://efrosgans.eecs.berkeley.edu/pix2pix/datasets/facades.tar.gz +``` + +2. 下载数据集后,使用以下命令提取数据集: + +```py +tar -xvzf facades.tar.gz +``` + +下载的数据集的文件结构如下: + +![](img/dd18e210-40f0-4f5e-b898-37d023af1add.png) + +数据集分为训练,测试和验证数据集。 让我们来提取图像。 + +执行以下步骤以加载数据集: + +1. 首先创建包含外观标签的`.h5`文件列表和包含外观图像的`.h5`另一个列表,如下所示: + +```py +data_dir_path = os.path.join(data_dir, data_type) + +# Get all .h5 files containing training images facade_photos_h5 = [f for f in os.listdir(os.path.join(data_dir_path, 'images')) if '.h5' in f] +facade_labels_h5 = [f for f in os.listdir(os.path.join(data_dir_path, 'facades')) if '.h5' in f] +``` + +2. 接下来,遍历列表以依次加载每个图像: + +```py +final_facade_photos = None final_facade_labels = None for index in range(len(facade_photos_h5)): +``` + +此步骤之后的所有代码都将位于前面的`for`循环中。 + +3. 接下来,加载包含图像的`h5`文件,并检索实际图像的 Numpy NDArrays: + +```py +facade_photos_path = data_dir_path + '/images/' + + facade_photos_h5[index] +facade_labels_path = data_dir_path + '/facades/' + + facade_labels_h5[index] + +facade_photos = h5py.File(facade_photos_path, 'r') +facade_labels = h5py.File(facade_labels_path, 'r') +``` + +4. 接下来,将图像调整为所需的图像尺寸,如下所示: + +```py +# Resize and normalize images num_photos = facade_photos['data'].shape[0] +num_labels = facade_labels['data'].shape[0] + +all_facades_photos = np.array(facade_photos['data'], dtype=np.float32) +all_facades_photos = all_facades_photos.reshape((num_photos, img_width, img_height, 1)) / 255.0 all_facades_labels = np.array(facade_labels['data'], dtype=np.float32) +all_facades_labels = all_facades_labels.reshape((num_labels, img_width, img_height, 1)) / 255.0 +``` + +5. 接下来,将调整大小的图像添加到最终的 NDArrays 中: + +```py +if final_facade_photos is not None and final_facade_labels is not None: +final_facade_photos = np.concatenate([final_facade_photos, + all_facades_photos], axis=0) +final_facade_labels = np.concatenate([final_facade_labels, all_facades_labels], axis=0) + else: + final_facade_photos = all_facades_photos + final_facade_labels = all_facades_labels +``` + +加载和调整图像大小的整个代码如下所示: + +```py +def load_dataset(data_dir, data_type, img_width, img_height): + data_dir_path = os.path.join(data_dir, data_type) + + # Get all .h5 files containing training images + facade_photos_h5 = [f for f in os.listdir(os.path.join(data_dir_path, 'images')) if '.h5' in f] + facade_labels_h5 = [f for f in os.listdir(os.path.join(data_dir_path, 'facades')) if '.h5' in f] + + final_facade_photos = None + final_facade_labels = None for index in range(len(facade_photos_h5)): + facade_photos_path = data_dir_path + '/images/' + facade_photos_h5[index] + facade_labels_path = data_dir_path + '/facades/' + facade_labels_h5[index] + + facade_photos = h5py.File(facade_photos_path, 'r') + facade_labels = h5py.File(facade_labels_path, 'r') + + # Resize and normalize images + num_photos = facade_photos['data'].shape[0] + num_labels = facade_labels['data'].shape[0] + + all_facades_photos = np.array(facade_photos['data'], dtype=np.float32) + all_facades_photos = all_facades_photos.reshape((num_photos, img_width, img_height, 1)) / 255.0 all_facades_labels = np.array(facade_labels['data'], dtype=np.float32) + all_facades_labels = all_facades_labels.reshape((num_labels, img_width, img_height, 1)) / 255.0 if final_facade_photos is not None and final_facade_labels is not None: + final_facade_photos = np.concatenate([final_facade_photos, all_facades_photos], axis=0) + final_facade_labels = np.concatenate([final_facade_labels, all_facades_labels], axis=0) + else: + final_facade_photos = all_facades_photos + final_facade_labels = all_facades_labels + + return final_facade_photos, final_facade_labels +``` + +先前的功能将从训练,测试和验证目录中的`.h5`文件加载图像。 + + + + + +# 可视化图像 + + + +可视化外观标签和外观图像的 Python 函数如下所示: + +```py +def visualize_bw_image(img): + """ + Visualize a black and white image """ fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.imshow(img, cmap='gray', interpolation='nearest') + ax.axis("off") + ax.set_title("Image") + plt.show() +``` + +使用前面的功能可以使外观标签或外观照片可视化,如下所示: + +```py +visualize_bw_image(image) +visualize_bw_image(image) +``` + +建筑物外墙的图像示例如下: + +![](img/3af97dd6-cd57-48b8-92bb-cd83f75125d4.png) + +下图表示前面的门面图像的建筑标签: + +![](img/1afafd90-be55-4633-97c5-a1d1eb7fd6c3.png) + +我们将训练一个 pix2pix 网络,该网络能够从立面标签生成立面图像。 让我们开始使用生成器和鉴别器的 Keras 实现。 + + + + + +# pix2pix 的 Keras 实现 + + + +如前所述,pix2pix 具有两个网络:一个生成器和一个鉴别器。 该生成器受 U-Net 架构的启发。 同样,鉴别器网络也受到 PatchGAN 架构的启发。 我们将在以下部分中实现这两个网络。 + +在开始编写实现之前,创建一个 Python 文件 `main.py` ,然后按以下方式导入基本模块: + +```py +import os +import time + +import h5py +import keras.backend as K +import matplotlib.pyplot as plt +import numpy as np +from cv2 import imwrite +from keras import Input, Model +from keras.layers import Convolution2D, LeakyReLU, BatchNormalization, UpSampling2D, Dropout, Activation, Flatten, Dense, Lambda, Reshape, concatenate +from keras.optimizers import Adam +``` + + + + + +# 发电机网络 + + + +生成器网络从源域 A 拍摄尺寸为(256、256、1)的图像,并将其转换为目标域 B 中尺寸为(256、256、1)的图像。 基本上,它将图像从源域 A 转换为目标域 B。让我们在 Keras 框架中实现生成器网络。 + +执行以下步骤来创建发电机网络: + +1. 首先定义生成器网络所需的超参数: + +```py +kernel_size = 4 strides = 2 leakyrelu_alpha = 0.2 upsampling_size = 2 dropout = 0.5 output_channels = 1 input_shape = (256, 256, 1) +``` + +2. 现在创建一个输入层,将输入输入到网络,如下所示: + +```py +input_layer = Input(shape=input_shape) +``` + +The input layer takes an input image of a shape of (256, 256, 1) and passes it to the next layer in the network. + +如上所述,生成器网络具有两个部分:编码器和解码器。 在接下来的几个步骤中,我们将编写编码器部分的代码。 + +3. 使用先前在,,*,pix2pix* 部分部分中指示的参数,将第一个卷积块添加到生成器网络: + +```py +# 1st Convolutional block in the encoder network encoder1 = Convolution2D(filters=64, kernel_size=kernel_size, padding='same', strides=strides)(input_layer) +encoder1 = LeakyReLU(alpha=leakyrelu_alpha)(encoder1) +``` + +第一卷积块包含具有激活功能的 2D 卷积层。 与其他七个卷积块不同,它没有批处理规范化层。 + +4. 将其他七个卷积块添加到生成器网络: + +```py +# 2nd Convolutional block in the encoder network encoder2 = Convolution2D(filters=128, kernel_size=kernel_size, padding='same', + strides=strides)(encoder1) +encoder2 = BatchNormalization()(encoder2) +encoder2 = LeakyReLU(alpha=leakyrelu_alpha)(encoder2) + +# 3rd Convolutional block in the encoder network encoder3 = Convolution2D(filters=256, kernel_size=kernel_size, padding='same', + strides=strides)(encoder2) +encoder3 = BatchNormalization()(encoder3) +encoder3 = LeakyReLU(alpha=leakyrelu_alpha)(encoder3) + +# 4th Convolutional block in the encoder network encoder4 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same', + strides=strides)(encoder3) +encoder4 = BatchNormalization()(encoder4) +encoder4 = LeakyReLU(alpha=leakyrelu_alpha)(encoder4) + +# 5th Convolutional block in the encoder network encoder5 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same', + strides=strides)(encoder4) +encoder5 = BatchNormalization()(encoder5) +encoder5 = LeakyReLU(alpha=leakyrelu_alpha)(encoder5) + +# 6th Convolutional block in the encoder network encoder6 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same', + strides=strides)(encoder5) +encoder6 = BatchNormalization()(encoder6) +encoder6 = LeakyReLU(alpha=leakyrelu_alpha)(encoder6) + +# 7th Convolutional block in the encoder network encoder7 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same', + strides=strides)(encoder6) +encoder7 = BatchNormalization()(encoder7) +encoder7 = LeakyReLU(alpha=leakyrelu_alpha)(encoder7) + +# 8th Convolutional block in the encoder network encoder8 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same', + strides=strides)(encoder7) +encoder8 = BatchNormalization()(encoder8) +encoder8 = LeakyReLU(alpha=leakyrelu_alpha)(encoder8) +``` + +这是发电机网络中编码器部分的末端。 生成器网络中的第二部分是解码器。 在接下来的几个步骤中,让我们为解码器编写代码。 + +5. 将第一个上采样卷积块添加到先前在 *pix2pix* 部分的体系结构中指示的参数中: + +```py +# 1st Upsampling Convolutional Block in the decoder network decoder1 = UpSampling2D(size=upsampling_size)(encoder8) +decoder1 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same')(decoder1) +decoder1 = BatchNormalization()(decoder1) +decoder1 = Dropout(dropout)(decoder1) +decoder1 = concatenate([decoder1, encoder7], axis=3) +decoder1 = Activation('relu')(decoder1) +``` + +第一个上采样模块从编码器部分的最后一层获取输入。 它具有一个 2D 上采样层,一个 2D 卷积层,一个批处理归一化层,一个脱落层,一个串联操作和一个激活函数。 请参阅 Keras 文档以找到有关这些层的更多信息,该文档可从 [https://keras.io/](https://keras.io/) 获得。 + +6. 同样,添加以下七个卷积块,如下所示: + +```py +# 2nd Upsampling Convolutional block in the decoder network decoder2 = UpSampling2D(size=upsampling_size)(decoder1) +decoder2 = Convolution2D(filters=1024, kernel_size=kernel_size, padding='same')(decoder2) +decoder2 = BatchNormalization()(decoder2) +decoder2 = Dropout(dropout)(decoder2) +decoder2 = concatenate([decoder2, encoder6]) +decoder2 = Activation('relu')(decoder2) + +# 3rd Upsampling Convolutional block in the decoder network decoder3 = UpSampling2D(size=upsampling_size)(decoder2) +decoder3 = Convolution2D(filters=1024, kernel_size=kernel_size, padding='same')(decoder3) +decoder3 = BatchNormalization()(decoder3) +decoder3 = Dropout(dropout)(decoder3) +decoder3 = concatenate([decoder3, encoder5]) +decoder3 = Activation('relu')(decoder3) + +# 4th Upsampling Convolutional block in the decoder network decoder4 = UpSampling2D(size=upsampling_size)(decoder3) +decoder4 = Convolution2D(filters=1024, kernel_size=kernel_size, padding='same')(decoder4) +decoder4 = BatchNormalization()(decoder4) +decoder4 = concatenate([decoder4, encoder4]) +decoder4 = Activation('relu')(decoder4) + +# 5th Upsampling Convolutional block in the decoder network decoder5 = UpSampling2D(size=upsampling_size)(decoder4) +decoder5 = Convolution2D(filters=1024, kernel_size=kernel_size, padding='same')(decoder5) +decoder5 = BatchNormalization()(decoder5) +decoder5 = concatenate([decoder5, encoder3]) +decoder5 = Activation('relu')(decoder5) + +# 6th Upsampling Convolutional block in the decoder network decoder6 = UpSampling2D(size=upsampling_size)(decoder5) +decoder6 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same')(decoder6) +decoder6 = BatchNormalization()(decoder6) +decoder6 = concatenate([decoder6, encoder2]) +decoder6 = Activation('relu')(decoder6) + +# 7th Upsampling Convolutional block in the decoder network decoder7 = UpSampling2D(size=upsampling_size)(decoder6) +decoder7 = Convolution2D(filters=256, kernel_size=kernel_size, padding='same')(decoder7) +decoder7 = BatchNormalization()(decoder7) +decoder7 = concatenate([decoder7, encoder1]) +decoder7 = Activation('relu')(decoder7) + +# Last Convolutional layer decoder8 = UpSampling2D(size=upsampling_size)(decoder7) +decoder8 = Convolution2D(filters=output_channels, kernel_size=kernel_size, padding='same')(decoder8) +decoder8 = Activation('tanh')(decoder8) +``` + +最后一层的激活函数为`'tanh'`,因为我们打算让生成器生成介于-1 到 1 之间的值。`'concatenate'`层用于添加跳过连接。 最后一层将生成张量为(256,256,1)的张量。 + +The 'concatenate' layer concatenates tensors along the channel dimension. You can provide a value for the axis, along which you want your tensors to be concatenated. + +7. 最后,通过指定发电机网络的输入和输出来创建 Keras 模型: + +```py +# Create a Keras model +model = Model(inputs=[input_layer], outputs=[decoder8]) +``` + +Python 函数内部的生成器网络的整个代码如下所示: + +```py +def build_unet_generator(): + """ + Create U-Net Generator using the hyperparameter values defined + below """ kernel_size = 4 + strides = 2 + leakyrelu_alpha = 0.2 + upsampling_size = 2 + dropout = 0.5 + output_channels = 1 + input_shape = (256, 256, 1) + + input_layer = Input(shape=input_shape) + + # Encoder Network # 1st Convolutional block in the encoder network encoder1 = Convolution2D(filters=64, kernel_size=kernel_size, + padding='same', + strides=strides)(input_layer) + encoder1 = LeakyReLU(alpha=leakyrelu_alpha)(encoder1) + + # 2nd Convolutional block in the encoder network + encoder2 = Convolution2D(filters=128, kernel_size=kernel_size, + padding='same', + strides=strides)(encoder1) + encoder2 = BatchNormalization()(encoder2) + encoder2 = LeakyReLU(alpha=leakyrelu_alpha)(encoder2) + + # 3rd Convolutional block in the encoder network + encoder3 = Convolution2D(filters=256, kernel_size=kernel_size, + padding='same', + strides=strides)(encoder2) + encoder3 = BatchNormalization()(encoder3) + encoder3 = LeakyReLU(alpha=leakyrelu_alpha)(encoder3) + + # 4th Convolutional block in the encoder network + encoder4 = Convolution2D(filters=512, kernel_size=kernel_size, + padding='same', + strides=strides)(encoder3) + encoder4 = BatchNormalization()(encoder4) + encoder4 = LeakyReLU(alpha=leakyrelu_alpha)(encoder4) + + # 5th Convolutional block in the encoder network + encoder5 = Convolution2D(filters=512, kernel_size=kernel_size, + padding='same', + strides=strides)(encoder4) + encoder5 = BatchNormalization()(encoder5) + encoder5 = LeakyReLU(alpha=leakyrelu_alpha)(encoder5) + + # 6th Convolutional block in the encoder network + encoder6 = Convolution2D(filters=512, kernel_size=kernel_size, + padding='same', + strides=strides)(encoder5) + encoder6 = BatchNormalization()(encoder6) + encoder6 = LeakyReLU(alpha=leakyrelu_alpha)(encoder6) + + # 7th Convolutional block in the encoder network + encoder7 = Convolution2D(filters=512, kernel_size=kernel_size, + padding='same', + strides=strides)(encoder6) + encoder7 = BatchNormalization()(encoder7) + encoder7 = LeakyReLU(alpha=leakyrelu_alpha)(encoder7) + + # 8th Convolutional block in the encoder network + encoder8 = Convolution2D(filters=512, kernel_size=kernel_size, + padding='same', + strides=strides)(encoder7) + encoder8 = BatchNormalization()(encoder8) + encoder8 = LeakyReLU(alpha=leakyrelu_alpha)(encoder8) + + # Decoder Network # 1st Upsampling Convolutional Block in the decoder network decoder1 = UpSampling2D(size=upsampling_size)(encoder8) + decoder1 = Convolution2D(filters=512, kernel_size=kernel_size, + padding='same')(decoder1) + decoder1 = BatchNormalization()(decoder1) + decoder1 = Dropout(dropout)(decoder1) + decoder1 = concatenate([decoder1, encoder7], axis=3) + decoder1 = Activation('relu')(decoder1) + + # 2nd Upsampling Convolutional block in the decoder network + decoder2 = UpSampling2D(size=upsampling_size)(decoder1) + decoder2 = Convolution2D(filters=1024, kernel_size=kernel_size, + padding='same')(decoder2) + decoder2 = BatchNormalization()(decoder2) + decoder2 = Dropout(dropout)(decoder2) + decoder2 = concatenate([decoder2, encoder6]) + decoder2 = Activation('relu')(decoder2) + + # 3rd Upsampling Convolutional block in the decoder network + decoder3 = UpSampling2D(size=upsampling_size)(decoder2) + decoder3 = Convolution2D(filters=1024, kernel_size=kernel_size, + padding='same')(decoder3) + decoder3 = BatchNormalization()(decoder3) + decoder3 = Dropout(dropout)(decoder3) + decoder3 = concatenate([decoder3, encoder5]) + decoder3 = Activation('relu')(decoder3) + + # 4th Upsampling Convolutional block in the decoder network + decoder4 = UpSampling2D(size=upsampling_size)(decoder3) + decoder4 = Convolution2D(filters=1024, kernel_size=kernel_size, + padding='same')(decoder4) + decoder4 = BatchNormalization()(decoder4) + decoder4 = concatenate([decoder4, encoder4]) + decoder4 = Activation('relu')(decoder4) + + # 5th Upsampling Convolutional block in the decoder network + decoder5 = UpSampling2D(size=upsampling_size)(decoder4) + decoder5 = Convolution2D(filters=1024, kernel_size=kernel_size, + padding='same')(decoder5) + decoder5 = BatchNormalization()(decoder5) + decoder5 = concatenate([decoder5, encoder3]) + decoder5 = Activation('relu')(decoder5) + + # 6th Upsampling Convolutional block in the decoder network + decoder6 = UpSampling2D(size=upsampling_size)(decoder5) + decoder6 = Convolution2D(filters=512, kernel_size=kernel_size, + padding='same')(decoder6) + decoder6 = BatchNormalization()(decoder6) + decoder6 = concatenate([decoder6, encoder2]) + decoder6 = Activation('relu')(decoder6) + + # 7th Upsampling Convolutional block in the decoder network + decoder7 = UpSampling2D(size=upsampling_size)(decoder6) + decoder7 = Convolution2D(filters=256, kernel_size=kernel_size, + padding='same')(decoder7) + decoder7 = BatchNormalization()(decoder7) + decoder7 = concatenate([decoder7, encoder1]) + decoder7 = Activation('relu')(decoder7) + + # Last Convolutional layer + decoder8 = UpSampling2D(size=upsampling_size)(decoder7) + decoder8 = Convolution2D(filters=output_channels, + kernel_size=kernel_size, padding='same')(decoder8) + decoder8 = Activation('tanh')(decoder8) + + model = Model(inputs=[input_layer], outputs=[decoder8]) + return model +``` + +我们现在已经成功地为发电机网络创建了 Keras 模型。 在下一节中,我们将为鉴别器网络创建 Keras 模型。 + + + + + +# 鉴别网络 + + + +鉴别器网络受 PatchGAN 架构的启发。 它包含八个卷积块,一个密集层和一个平坦层。 鉴别器网络获取从尺寸为(256、256、1)的图像中提取的一组补丁,并预测给定补丁的概率。 让我们在 Keras 中实现鉴别器。 + +1. 首先初始化生成器网络所需的超参数: + +```py +kernel_size = 4 strides = 2 leakyrelu_alpha = 0.2 padding = 'same' num_filters_start = 64 # Number of filters to start with num_kernels = 100 kernel_dim = 5 patchgan_output_dim = (256, 256, 1) +patchgan_patch_dim = (256, 256, 1) + +# Calculate number of patches +number_patches = int((patchgan_output_dim[0] / patchgan_patch_dim[0]) * (patchgan_output_dim[1] / patchgan_patch_dim[1])) +``` + +2. 让我们向网络添加一个输入层。 这需要一个张量为 `patchgan_patch_dim`的张量的补丁: + +```py +input_layer = Input(shape=patchgan_patch_dim) +``` + +3. 接下来,如下所示将卷积层添加到网络。 *pix2pix 的体系结构*部分中提供了该块的配置: + +```py +des = Convolution2D(filters=64, kernel_size=kernel_size, padding=padding, strides=strides)(input_layer) +des = LeakyReLU(alpha=leakyrelu_alpha)(des) +``` + +4. 接下来,使用以下代码计算卷积块的数量: + +```py +# Calculate the number of convolutional layers total_conv_layers = int(np.floor(np.log(patchgan_output_dim[1]) / + np.log(2))) +list_filters = [num_filters_start * min(total_conv_layers, (2 ** i)) for i in range(total_conv_layers)] +``` + +5. 接下来,使用先前在 *pix2pix* 部分的体系结构中指示的超参数值添加另外七个卷积块: + +```py +# Next 7 Convolutional blocks for filters in list_filters[1:]: + des = Convolution2D(filters=filters, kernel_size=kernel_size, padding=padding, strides=strides)(des) + des = BatchNormalization()(des) + des = LeakyReLU(alpha=leakyrelu_alpha)(des) + +``` + +6. 接下来,将平坦的层添加到网络,如下所示: + +```py +flatten_layer = Flatten()(des) +``` + +平坦层将 *n* 维张量转换为一维张量。 + +7. 类似地,添加具有两个节点/神经元的密集层,并添加 `softmax` 作为激活函数。 这需要一个来自 `Flatten` 层的张量,并将其转换为尺寸为 `(batch_size, 2)`的张量: + +```py +dense_layer = Dense(units=2, activation='softmax')(flatten_layer) +``` + +`softmax`函数将向量转换为概率分布。 + +8. 接下来,为 PatchGAN 网络创建 Keras 模型,如下所示: + +```py +model_patch_gan = Model(inputs=[input_layer], outputs=[dense_layer, flatten_layer]) +``` + +PatchGAN 模型将输入张量作为输入,并输出两个张量,一个来自密集层,另一个来自平坦层。 我们的 PatchGAN 网络现已准备就绪。 但是,它本身不能用作鉴别器。 而是将单个补丁分类为真实或伪造类别。 要创建完整的鉴别器,请按照下列步骤操作: + +1. 我们将从输入图像中提取色块,并将它们逐一馈送到 PatchGAN。 创建一个等于补丁数量的输入层列表,如下所示: + +```py +# Create a list of input layers equal to number of patches list_input_layers = [Input(shape=patchgan_patch_dim) for _ in range(number_patches)] +``` + +2. 接下来,将补丁传递到 PatchGAN 网络并获得概率分布: + +```py +# Pass the patches to the PatchGAN and get probability distribution output1 = [model_patch_gan(patch)[0] for patch in list_input_layers] +output2 = [model_patch_gan(patch)[1] for patch in list_input_layers] +``` + +如果我们有多个面片,则`output1`和`output2`都是张量的列表。 我们现在应该有两个张量列表。 + +3. 如果您有多个补丁,请沿着渠道维度将其串联起来以计算永久损耗: + +```py +# In case of multiple patches, concatenate them along the channel dimension to calculate perceptual loss if len(output1) > 1: + output1 = concatenate(output1) +else: + output1 = output1[0] + +# In case of multiple patches, merge output2 as well if len(output2) > 1: + output2 = concatenate(output2) +else: + output2 = output2[0] +``` + +4. 接下来,创建一个密集层,如下所示: + +```py +dense_layer2 = Dense(num_kernels * kernel_dim, use_bias=False, activation=None) +``` + +5. 接下来,添加一个自定义损失层。 该层计算馈入该层的张量的最小批判别: + +```py +custom_loss_layer = Lambda(lambda x: K.sum( + K.exp(-K.sum(K.abs(K.expand_dims(x, 3) - K.expand_dims(K.permute_dimensions(x, pattern=(1, 2, 0)), 0)), 2)), 2)) +``` + +6. 接下来,使`output2`张量通过`dense_layer2`: + +```py +output2 = dense_layer2(output2) +``` + +7. 接下来,将`output2`重塑为`(num_kernels, kernel_dim)`的张量: + +```py +output2 = Reshape((num_kernels, kernel_dim))(output2) +``` + +8. 接下来,将`output2`张量传递到`custom_loss_layer`: + +```py +output2 = custom_loss_layer(output2) +``` + +9. 接下来,连接`output1`和`output2`以创建一个张量,并将其通过密集层: + +```py +output1 = concatenate([output1, output2]) +final_output = Dense(2, activation="softmax")(output1) +``` + +将`"softmax"`用作最后一个密集层的激活功能。 这将返回概率分布。 + +10. 最后,通过如下指定网络的输入和输出来创建鉴别器模型: + +```py +discriminator = Model(inputs=list_input_layers, outputs=[final_output]) +``` + +鉴别器网络的完整代码如下: + +```py +def build_patchgan_discriminator(): + """ + Create PatchGAN discriminator using the hyperparameter values defined below """ kernel_size = 4 + strides = 2 + leakyrelu_alpha = 0.2 + padding = 'same' + num_filters_start = 64 # Number of filters to start with + num_kernels = 100 + kernel_dim = 5 + patchgan_output_dim = (256, 256, 1) + patchgan_patch_dim = (256, 256, 1) + number_patches = int( + (patchgan_output_dim[0] / patchgan_patch_dim[0]) * (patchgan_output_dim[1] / patchgan_patch_dim[1])) + + input_layer = Input(shape=patchgan_patch_dim) + + des = Convolution2D(filters=64, kernel_size=kernel_size, padding=padding, strides=strides)(input_layer) + des = LeakyReLU(alpha=leakyrelu_alpha)(des) + + # Calculate the number of convolutional layers + total_conv_layers = int(np.floor(np.log(patchgan_output_dim[1]) / np.log(2))) + list_filters = [num_filters_start * min(total_conv_layers, (2 ** i)) for i in range(total_conv_layers)] + + # Next 7 Convolutional blocks + for filters in list_filters[1:]: + des = Convolution2D(filters=filters, kernel_size=kernel_size, padding=padding, strides=strides)(des) + des = BatchNormalization()(des) + des = LeakyReLU(alpha=leakyrelu_alpha)(des) + + # Add a flatten layer + flatten_layer = Flatten()(des) + + # Add the final dense layer + dense_layer = Dense(units=2, activation='softmax')(flatten_layer) + + # Create the PatchGAN model + model_patch_gan = Model(inputs=[input_layer], outputs=[dense_layer, flatten_layer]) + + # Create a list of input layers equal to the number of patches + list_input_layers = [Input(shape=patchgan_patch_dim) for _ in range(number_patches)] + + # Pass the patches through the PatchGAN network + output1 = [model_patch_gan(patch)[0] for patch in list_input_layers] + output2 = [model_patch_gan(patch)[1] for patch in list_input_layers] + + # In case of multiple patches, concatenate outputs to calculate perceptual loss + if len(output1) > 1: + output1 = concatenate(output1) + else: + output1 = output1[0] + + # In case of multiple patches, merge output2 as well + if len(output2) > 1: + output2 = concatenate(output2) + else: + output2 = output2[0] + + # Add a dense layer + dense_layer2 = Dense(num_kernels * kernel_dim, use_bias=False, activation=None) + + # Add a lambda layer + custom_loss_layer = Lambda(lambda x: K.sum( + K.exp(-K.sum(K.abs(K.expand_dims(x, 3) - K.expand_dims(K.permute_dimensions(x, pattern=(1, 2, 0)), 0)), 2)), 2)) + + # Pass the output2 tensor through dense_layer2 + output2 = dense_layer2(output2) + + # Reshape the output2 tensor + output2 = Reshape((num_kernels, kernel_dim))(output2) + + # Pass the output2 tensor through the custom_loss_layer + output2 = custom_loss_layer(output2) + + # Finally concatenate output1 and output2 + output1 = concatenate([output1, output2]) + final_output = Dense(2, activation="softmax")(output1) + + # Create a discriminator model + discriminator = Model(inputs=list_input_layers, outputs=[final_output]) + return discriminator +``` + +我们现在已经成功创建了鉴别器网络。 接下来,让我们创建一个对抗网络。 + + + + + +# 对抗网络 + + + +在本节中,我们将创建一个**对抗网络**,其中包含 U-Net 生成器网络和 PatchGAN 鉴别器网络。 执行以下步骤来创建对抗网络: + +1. 首先初始化超参数: + +```py +input_image_dim = (256, 256, 1) +patch_dim = (256, 256) +``` + +2. 接下来,创建一个输入层,将输入馈送到网络,如下所示: + +```py +input_layer = Input(shape=input_image_dim) +``` + +3. 接下来,使用生成器网络生成伪造的图像: + +```py +generated_images = generator(input_layer) +``` + +4. 接下来,从生成的图像中提取补丁: + +```py +# Chop the generated images into patches img_height, img_width = input_img_dim[:2] +patch_height, patch_width = patch_dim + +row_idx_list = [(i * patch_height, (i + 1) * patch_height) for i in range(int(img_height / patch_height))] +column_idx_list = [(i * patch_width, (i + 1) * patch_width) for i in range(int(img_width / patch_width))] + +generated_patches_list = [] +for row_idx in row_idx_list: + for column_idx in column_idx_list: + generated_patches_list.append(Lambda(lambda z: z[:, column_idx[0]:column_idx[1], row_idx[0]:row_idx[1], :], + output_shape=input_img_dim)(generated_images)) +``` + +5. 冻结了鉴别器网络的训练,因为我们不想训练 鉴别器网络: + +```py +discriminator.trainable = False +``` + +6. 现在,我们应该有一个补丁列表。 通过 PatchGAN 鉴别器网络传递它们: + +```py +dis_output = discriminator(generated_patches_list) +``` + +7. 最后,通过如下指定网络的输入和输出来创建 Keras 模型: + +```py +model = Model(inputs=[input_layer], outputs=[generated_images, + dis_output]) +``` + +这些步骤使用两个网络(生成器网络和鉴别器网络)创建对抗模型。 对抗模型的整个代码如下: + +```py +def build_adversarial_model(generator, discriminator): + """ + Create an adversarial model """ input_image_dim = (256, 256, 1) + patch_dim = (256, 256) + + # Create an input layer + input_layer = Input(shape=input_image_dim) + + # Use the generator network to generate images + generated_images = generator(input_layer) + + # Extract patches from the generated images + img_height, img_width = input_img_dim[:2] + patch_height, patch_width = patch_dim + + row_idx_list = [(i * patch_height, (i + 1) * patch_height) for i in range(int(img_height / patch_height))] + column_idx_list = [(i * patch_width, (i + 1) * patch_width) for i in range(int(img_width / patch_width))] + + generated_patches_list = [] + for row_idx in row_idx_list: + for column_idx in column_idx_list: + generated_patches_list.append(Lambda(lambda z: z[:, column_idx[0]:column_idx[1], row_idx[0]:row_idx[1], :], + output_shape=input_img_dim)(generated_images)) + + discriminator.trainable = False # Pass the generated patches through the discriminator network + dis_output = discriminator(generated_patches_list) + + # Create a model + model = Model(inputs=[input_layer], outputs=[generated_images, dis_output]) + return model +``` + +现在,我们已经成功地为发电机网络,鉴别器网络和对抗模型创建了模型。 我们准备训练 pix2pix。 在下一部分中,我们将在 Facades 数据集中训练 pix2pix 网络。 + + + + + +# 训练 pix2pix 网络 + + + +像任何其他 GAN 一样,训练 pix2pix 网络是一个两步过程。 第一步,我们训练鉴别器网络。 在第二步中,我们训练对抗网络,最终训练发电机网络。 让我们开始训练网络。 + +执行以下步骤来训练 SRGAN 网络: + +1. 首先定义训练所需的超参数: + +```py +epochs = 500 num_images_per_epoch = 400 batch_size = 1 img_width = 256 img_height = 256 num_channels = 1 input_img_dim = (256, 256, 1) +patch_dim = (256, 256) + +# Specify dataset directory path +dataset_dir = "pix2pix-keras/pix2pix/data/facades_bw" +``` + +2. 接下来,定义通用优化器,如下所示: + +```py +common_optimizer = Adam(lr=1E-4, beta_1=0.9, beta_2=0.999, + epsilon=1e-08) +``` + +对于所有网络,我们将使用`Adam`优化器,其中`learning rate`等于 1e-4, `beta_1` 等于 0.9, `beta_2`等于 0.999,并且`epsilon`等于 1e-08。 + +3. 接下来,构建并编译 PatchGAN 鉴别器网络,如下所示: + +```py +patchgan_discriminator = build_patchgan_discriminator() +patchgan_discriminator.compile(loss='binary_crossentropy', optimizer=common_optimizer) +``` + +要编译鉴别器模型,请使用`binary_crossentropy`作为损失函数,并使用`common_optimizer`作为训练优化器。 + +4. 现在,构建并编译生成器网络,如下所示: + +```py +unet_generator = build_unet_generator() +unet_generator.compile(loss='mae', optimizer=common_optimizer) +``` + +要编译鉴别器模型,请使用 `mse` 作为损失函数,并使用 `common_optimizer` 作为训练优化器。 + +5. 接下来,构建并编译对抗模型,如下所示: + +```py +adversarial_model = build_adversarial_model(unet_generator, patchgan_discriminator) +adversarial_model.compile(loss=['mae', 'binary_crossentropy'], loss_weights=[1E2, 1], optimizer=common_optimizer) +``` + +要编译对抗模型,请使用损失列表 `['mse', 'binary_crossentropy']` 和 `common_optimizer` 作为训练优化器。 + +6. 现在,按如下所示加载训练,验证和测试数据集: + +```py +training_facade_photos, training_facade_labels = load_dataset(data_dir=dataset_dir, data_type='training',img_width=img_width, img_height=img_height) + +test_facade_photos, test_facade_labels = load_dataset(data_dir=dataset_dir, data_type='testing',img_width=img_width, img_height=img_height) + +validation_facade_photos, validation_facade_labels = load_dataset(data_dir=dataset_dir, data_type='validation',img_width=img_width, img_height=img_height) +``` + +`load_dataset` 功能 是 *D* *ata 制备* 部分中定义的 。 每一组包含所有图像的一组 ndarray。 每组的尺寸将为 `(#total_images, 256, 256, 1)` **。** + +7. 添加 `tensorboard` 以可视化训练损失并可视化网络图: + +```py +tensorboard = TensorBoard(log_dir="logs/".format(time.time())) +tensorboard.set_model(unet_generator) +tensorboard.set_model(patchgan_discriminator) +``` + +8. 接下来,创建一个 for 循环,该循环应运行的次数由时期数指定,如下所示: + +```py +for epoch in range(epochs): + print("Epoch:{}".format(epoch)) +``` + +9. 创建两个列表以存储所有迷你批次的损失: + +```py +dis_losses = [] +gen_losses = [] + +# Initialize a variable +batch_counter = 1 +``` + +10. 接下来,在 epochs 循环内创建另一个循环,并使它运行`num_batches`指定的次数,如下所示: + +```py +num_batches = int(training_facade_photos.shape[0] / batch_size) for index in range(int(training_facade_photos.shape[0] / batch_size)): +print("Batch:{}".format(index)) +``` + +我们用于区分网络和对抗网络训练的整个代码将在此循环内。 + +11. 接下来,对培训和验证数据进行小批量采样,如下所示: + +```py + train_facades_batch = training_facade_labels[index * batch_size:(index + 1) * batch_size] + train_images_batch = training_facade_photos[index * batch_size:(index + 1) * batch_size] + + val_facades_batch = validation_facade_labels[index * batch_size:(index + 1) * batch_size] + val_images_batch = validation_facade_photos[index * batch_size:(index + 1) * batch_size] +``` + +12. 接下来,生成一批假图像并从中提取补丁。 如下使用`generate_and_extract_patches`功能: + +```py +patches, labels = generate_and_extract_patches(train_images_batch, train_facades_batch, unet_generator,batch_counter, patch_dim) +``` + +`generate_and_extract_patches`功能定义如下: + +```py +def generate_and_extract_patches(images, facades, generator_model, batch_counter, patch_dim): + # Alternatively, train the discriminator network on real and generated images + if batch_counter % 2 == 0: + # Generate fake images + output_images = generator_model.predict(facades) + + # Create a batch of ground truth labels + labels = np.zeros((output_images.shape[0], 2), dtype=np.uint8) + labels[:, 0] = 1 else: + # Take real images + output_images = images + + # Create a batch of ground truth labels + labels = np.zeros((output_images.shape[0], 2), dtype=np.uint8) + labels[:, 1] = 1 patches = [] + for y in range(0, output_images.shape[0], patch_dim[0]): + for x in range(0, output_images.shape[1], patch_dim[1]): + image_patches = output_images[:, y: y + patch_dim[0], x: x + patch_dim[1], :] + patches.append(np.asarray(image_patches, dtype=np.float32)) + + return patches, labels +``` + +前面的功能使用生成器网络生成伪图像,然后从生成的图像中提取补丁。 现在,我们应该有一个补丁列表及其基本真理值。 + +13. 现在,在生成的补丁上训练鉴别器网络: + +```py +d_loss = patchgan_discriminator.train_on_batch(patches, labels) +``` + +这将在提取的补丁和地面真相标签上训练鉴别器网络。 + +14. 接下来,训练对抗模型。 对抗性模型将训练生成器网络,但冻结区分器网络的训练。 使用以下代码: + +```py +labels = np.zeros((train_images_batch.shape[0], 2), dtype=np.uint8) + labels[:, 1] = 1 # Train the adversarial model g_loss = adversarial_model.train_on_batch(train_facades_batch, [train_images_batch, labels]) +``` + +15. 每个小批量完成后增加批计数器: + +```py +batch_counter += 1 +``` + +16. 在每个微型批处理上完成一次迭代(循环)后,将损失存储在名为`dis_losses`和`gen_losses`的列表中: + +```py +dis_losses.append(d_loss) +gen_losses.append(g_loss) +``` + +17. 另外,将平均损失存储到 TensorBoard 以进行可视化。 既存储损失,也要存储发电机网络的平均损失和鉴别器网络的平均损失: + +```py +write_log(tensorboard, 'discriminator_loss', np.mean(dis_losses), + epoch) +write_log(tensorboard, 'generator_loss', np.mean(gen_losses), epoch) +``` + +18. 每 10 个周期后,使用生成器网络生成一组图像: + +```py + # After every 10th epoch, generate and save images for visualization if epoch % 10 == 0: + # Sample a batch of validation datasets + val_facades_batch = validation_facade_labels[0:5] + val_images_batch = validation_facade_photos[0:5] + + # Generate images + validation_generated_images = unet_generator.predict(val_facades_batch) + + # Save images + save_images(val_images_batch, val_facades_batch, validation_generated_images, epoch, 'validation', limit=5) +``` + +将前面的代码块放入 epochs 循环中。 每隔 10 个时间段,它将生成一批伪图像并将其保存到结果目录。 这里,`save_images()`是如下定义的效用函数: + +```py +def save_images(real_images, real_sketches, generated_images, num_epoch, dataset_name, limit): + real_sketches = real_sketches * 255.0 + real_images = real_images * 255.0 + generated_images = generated_images * 255.0 # Save some images only + real_sketches = real_sketches[:limit] + generated_images = generated_images[:limit] + real_images = real_images[:limit] + + # Create a stack of images + X = np.hstack((real_sketches, generated_images, real_images)) + + # Save stack of images + imwrite('results/X_full_{}_{}.png'.format(dataset_name, num_epoch), X[0]) +``` + +现在,我们已经成功地在立面数据集上训练了 pix2pix 网络。 对网络进行 1000 个时代的培训,以获取高质量的发电机网络。 + + + + + +# 保存模型 + + + +在 Keras 中保存模型只需要一行代码。 要保存生成器模型,请添加以下行: + +```py +# Specify the path for the generator model +unet_generator.save_weights("generator.h5") +``` + +同样,通过添加以下行来保存鉴别器模型: + +```py +# Specify the path for the discriminator model +patchgan_discriminator.save_weights("discriminator.h5") +``` + + + + + +# 可视化生成的图像 + + + +在将网络训练了 20 个时间段后,网络将开始生成体面的图像:让我们看一下由生成器网络生成的图像。 + +在 20、50、150 和 200 个时代(从左到右) 之后,图像如下所示: + +![](img/81830986-deb9-4db9-9dba-518aa6313466.png) ![](img/515bff70-925a-4f3b-a995-470637fd676e.png) ![](img/550e5193-b6a1-49ba-81eb-cd1fe71c7634.png) ![](img/277d3877-16f5-4ce5-b5f0-2703bc2d3075.png) + +每个块均包含垂直堆叠的外观标签,生成的照片和实际图像。 我建议您将网络训练 1000 个纪元。 如果一切顺利,则在 1000 个时期之后,生成器网络将开始生成逼真的图像。 + + + + + +# 可视化损失 + + + +要可视化培训损失,请启动 T ensorBoard 服务器,如下所示: + +```py +tensorboard --logdir=logs +``` + +现在,在浏览器中打开 `localhost:6006` 。 TensorBoard 的 **标量** 部分包含两种损失的图表,如以下屏幕截图所示: + +![](img/ff705572-5fb8-4216-883c-63bbfb809286.png) + +SCALARS section of TensorBoard + +这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止培训,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 尝试使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。 + + + + + +# 可视化图形 + + + +TensorBoard 的 GRAPHS 部分包含两个网络的图形。 如果网络性能不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作: + +![](img/7c0476a9-d31c-4ef3-b3b3-2193680e7c0c.png) + +Flow of tensors and different operations inside each graph + + + +# pix2pix 网络的实际应用 + + + +pix2pix 网络有很多应用。 其中包括: + +* 将像素级分割转换为真实图像 +* 要将白天图像转换为夜间图像,反之亦然 +* 将卫星平面图像转换为地图图像 +* 将草图转换为照片 +* 将黑白图像转换为彩色图像,反之亦然 + +以下是从指定的官方文件中拍摄的图像。 它显示了 pix2pix 网络的不同用例: + +![](img/beb77a0a-2316-4a3e-9bf9-dead211714b4.png) + +Source: Image-to-Image Translation with Conditional Adversarial Networks:Source: arXiv:1611.07004 [cs.CV] + + + +# 概要 + + + +在本章中,我们了解了 pix2pix 网络是什么,并探讨了其体系结构。 我们从 d 自身加载开始,准备要训练的数据集,然后准备了项目,并研究了 pix2pix 网络的 K 时代实现。 之后,我们研究了训练 pix2pix 网络的目标函数。 然后,我们在立面数据集上训练了 pix2pix 网络,并探索了 pix2pix 网络的一些 p 实际应用。 + +在下一章中,我们将预测 GAN 的未来。 我们将研究 GAN 领域在不久的将来会发生什么,以及它将如何改变我们的行业和我们的日常生活。 + + diff --git a/new/gan-proj/9.md b/new/gan-proj/9.md new file mode 100644 index 0000000000000000000000000000000000000000..a939ac373577fd47de8302cb8c79c79d63441418 --- /dev/null +++ b/new/gan-proj/9.md @@ -0,0 +1,185 @@ + + +# 预测 GAN 的未来 + + + +如果您已完成本书各章中的所有练习,则为学习和编码**生成对抗网络**( **GAN** )进行了很长的路要走。 世界应用。 GAN 有可能在许多不同行业中造成破坏。 科学家和研究人员开发了各种可用于构建商业应用的 GAN。 在本书中,我们探索并实现了一些最著名的 GAN 架构。 + +因此,让我们回顾一下到目前为止所学到的东西: + +* 我们从对 GAN 的简要介绍开始,学习了各种重要概念。 +* 然后,我们探索了 3D-GAN,这是一种可以生成 3D 图像的 GAN。 我们训练了 3D-GAN,以生成现实世界对象的 3D 模型,例如*飞机*或*桌子。* +* 在第三章中,我们探索了用于面部衰老的条件 GAN。 我们学习了如何使用条件 GAN 将处于年龄的的人脸图像转换为同一张脸的图像。 我们还讨论了 Age-cGAN 的各种实际应用。 +* 之后,我们探索了**深度卷积生成对抗网络**( **DCGANs** ),该网络用于生成动漫人物的面孔。 +* 在第五章中,我们探讨了**超分辨率生成对抗网络**( **SRGAN** ),该网络可用于从低分辨率图像生成高分辨率图像。 之后,我们讨论了 SRGAN 如何解决一些非常有趣的现实问题。 +* 然后,我们探索了 StackGAN,我们将其用于执行文本到图像的合成任务。 我们在训练 StackGAN 之前先探索了一个数据集,然后通过讨论 StackGAN 的实际应用来结束本章。 + +* 在第七章中,我们探索了 CycleGAN,这是一次图像到图像的翻译任务。 我们的目标是将绘画变成照片。 我们还讨论了 CycleGAN 的实际应用 +* 最后,在第八章中,我们探讨了 pix2pix 网络,这是一种条件 GAN。 我们训练了 pix2pix 网络,以根据建筑标签生成立面图像。 像其他章节一样,我们通过讨论 pix2pix 网络的实际应用来结束本章。 + +在本章中,我们将介绍以下主题: + +* 我们对 GAN 未来的预测 +* GAN 的潜在未来应用 +* 可以探索 GAN 的其他区域 + + + + + +# 我们对 GAN 未来的预测 + + + +我认为,GAN 的未来将具有以下特点: + +* 研究团体公开接受 GAN 及其应用。 +* 令人印象深刻的结果-到目前为止,GAN 在使用常规方法难以执行的任务上显示出非常令人印象深刻的结果。 例如,将低分辨率图像转换为高分辨率图像以前是一项艰巨的任务,通常是使用 CNN 进行的。 GAN 体系结构(例如 SRGAN 或 pix2pix)显示了 GAN 在此应用中的潜力,而 StackGAN 网络已被证明对文本到图像的合成任务很有用。 如今,任何人都可以创建 SRGAN 网络并将其训练在自己的图像上。 +* 深度学习技术的进步。 +* GAN 用于商业应用。 +* GAN 训练过程的成熟。 + + + + + +# 改善现有的深度学习方法 + + + +监督式深度学习方法需要大量数据来训练模型。 获取该数据既昂贵又费时。 有时,无法获得数据,因为它不是公开可用的,或者如果它是公开可用的,则数据集的大小可能很小。 这是 GAN 可以营救的地方。 一旦使用相当小的数据集进行了训练,便可以部署 GAN 来从同一域生成新数据。 例如,假设您正在处理图像分类任务。 您有一个数据集,但是它不足以完成您的任务。 我们可以在现有映像上训练 GAN,然后将其部署到同一域中以生成新映像。 尽管 GAN 目前存在训练不稳定性的问题,但一些研究人员表明,可以生成逼真的图像。 + + + + + +# GAN 商业应用的演变 + + + +未来几年,我们将看到 GAN 的更多商业应用。 GAN 的许多商业应用已经开发,并给人留下了积极的印象。 例如,移动应用程序 Prisma 是 GAN 最早获得广泛成功的应用程序之一。 我们可能会在不久的将来看到 GAN 的民主化,一旦我们这样做了,我们将开始看到 GAN 改善了我们的日常生活。 + + + + + +# GAN 培训过程的成熟 + + + +自 2014 年成立以来,四年后,GAN 仍然遭受训练不稳定问题的困扰。 有时,GAN 根本无法收敛,因为两个网络都偏离了它们的训练路径。 在编写本书时,我多次遭受这个问题的困扰。 研究人员为稳定 GAN 的训练做出了许多努力。 我预测,随着深度学习领域的进步,该过程将日趋成熟,并且我们很快将能够毫无问题地训练模型。 + + + + + +# GAN 的潜在未来应用 + + + +GAN 的未来是光明的! 我认为在不久的将来有可能会使用 GAN: + +* 从文本创建图表 +* 生成网站设计 +* 压缩数据 +* 药物发现与开发 +* 产生文字 +* 产生音乐 + + + + + +# 从文本创建图表 + + + +设计图表是一个漫长的过程。 这需要数小时的劳动并且需要特定的技能。 在市场营销和社会促销中,信息图表的魅力十足。 它们是社交媒体营销的主要成分。 有时,由于漫长的创建过程,公司不得不采用效率较低的策略来解决。 AI 和 GAN 可以帮助设计师进行创作。 + + + + + +# 生成网站设计 + + + +同样,设计网站是一个手动的,创造性的过程,需要熟练的手动工作,并且需要很长时间。 GAN 可以通过提供可以用作灵感的初始设计来协助设计师,从而节省大量金钱和时间。 + + + + + +# 压缩数据 + + + +互联网使我们能够将大量数据传输到任何位置,但这是有代价的。 GAN 使我们能够提高图像和视频的分辨率。 我们可以将低分辨率的图像和视频传输到它们所需的位置,然后可以使用 GAN 来提高数据质量,而这需要更少的带宽。 这带来了很多可能性。 + + + + + +# 药物发现与开发 + + + +使用 GAN 进行药物开发可能听起来像是一个梦想,但考虑到所需的化学和生物学特性,GAN 已被用于生成分子结构。 制药公司在新药的研发上花费了数十亿美元。 用于药物开发的 GAN 可以大大降低该成本。 + + + + + +# 用于生成文本的 GAN + + + +GAN 已被证明对图像生成任务很有用。 GAN 中的大部分研究目前集中在高分辨率图像生成,文本到图像合成,样式转换,图像到图像翻译以及其他类似任务上。 目前,对于使用 GAN 生成文本的研究还不多。 这是因为 GAN 旨在生成连续的值,因此训练 GAN 以获得离散值确实具有挑战性。 将来, 预测将在文本生成任务中进行更多的研究。 + + + + + +# 用于生成音乐的 GAN + + + +使用 GAN 进行音乐生成是另一个尚未得到充分探索的领域。 音乐创作的过程充满创造力,非常复杂。 GAN 具有改变音乐产业的潜力,如果发生这种情况,我们可能很快就会聆听 GAN 创建的曲目。 + + + + + +# 探索 GAN + + + +您可以探索的其他 GAN 架构包括: + +* **BigGAN** :*大规模 GAN 训练,用于高保真自然图像合成*( [https://arxiv.org/pdf/1809.11096.pdf](https://arxiv.org/pdf/1809.11096.pdf) ) +* **WaveGAN** :使用生成的对抗网络合成音频( [https://arxiv.org/abs/1802.04208](https://arxiv.org/abs/1802.04208) ) +* **开始**:*开始:边界均衡生成对抗网络*( [https://arxiv.org/abs/1703.10717](https://arxiv.org/abs/1703.10717) ) +* **AC-GAN** :*使用辅助分类器 GAN* ( [https://arxiv.org/abs/1610.09585](https://arxiv.org/abs/1610.09585) )的条件图像合成 + +* **AdaGAN** : *AdaGAN:增强生成模型*( [https://arxiv.org/abs/1701.02386v1](https://arxiv.org/abs/1701.02386v1) ) +* **ArtGAN** : *ArtGAN:带有条件分类 GAN* 的艺术品合成( [https://arxiv.org/abs/1702.03410](https://arxiv.org/abs/1702.03410) ) +* **BAGAN** : *BAGAN:平衡 GAN* 的数据增强( [https://arxiv.org/abs/1803.09655](https://arxiv.org/abs/1803.09655) ) +* **BicycleGAN** :*迈向多模态图像到图像翻译*( [https://arxiv.org/abs/1711.11586](https://arxiv.org/abs/1711.11586) ) +* **CapsGAN** : *CapsGAN:为生成对抗网络使用动态路由*( [https://arxiv.org/abs/1806.03968](https://arxiv.org/abs/1806.03968) ) +* **E-GAN** :*进化生成对抗网络*( [https://arxiv.org/abs/1803.00657](https://arxiv.org/abs/1803.00657) ) +* **WGAN** : *Wasserstein GAN* ( [h](https://arxiv.org/abs/1701.07875v2) [ttps://arxiv.org/abs/1701.07875v2](https://arxiv.org/abs/1701.07875v2) ) + +研究人员还开发了数百种其他 GAN 架构。 + + + + + +# 概要 + + + +在本书中,我的目的是让您了解 GAN 及其在世界上的应用。 你的想象力是唯一的限制。 有大量可用的不同 GAN 架构,并且它们正在变得越来越成熟。 GAN 仍有很长的路要走,因为它们仍然存在诸如训练不稳定性和模式崩溃之类的问题,但是现在已经提出了各种解决方案,包括标签平滑,实例规范化和小批量区分。 我希望这本书对您实现 GAN 有所帮助。 如果您有任何疑问,请给我发送电子邮件到 [ahikailash1@gmail.com](mailto:ahikailash1@gmail.com) 。 + + diff --git a/new/gan-proj/img/01fddc7c-38fb-4dc6-ac3b-3c73b448e65a.png b/new/gan-proj/img/01fddc7c-38fb-4dc6-ac3b-3c73b448e65a.png new file mode 100644 index 0000000000000000000000000000000000000000..8ac7e3c384d535a159c34bad9e2e12096dcccddc Binary files /dev/null and b/new/gan-proj/img/01fddc7c-38fb-4dc6-ac3b-3c73b448e65a.png differ diff --git a/new/gan-proj/img/028e54b4-8184-45bd-8edf-abc7b7e8de97.png b/new/gan-proj/img/028e54b4-8184-45bd-8edf-abc7b7e8de97.png new file mode 100644 index 0000000000000000000000000000000000000000..72a468836bb32961e61d728d7217d10eaf28a04f Binary files /dev/null and b/new/gan-proj/img/028e54b4-8184-45bd-8edf-abc7b7e8de97.png differ diff --git a/new/gan-proj/img/030151f2-b1cf-4a2a-9d44-d55bc65f9c7c.png b/new/gan-proj/img/030151f2-b1cf-4a2a-9d44-d55bc65f9c7c.png new file mode 100644 index 0000000000000000000000000000000000000000..2a3bf1b13292633742644dd14094f015a30eb5cf Binary files /dev/null and b/new/gan-proj/img/030151f2-b1cf-4a2a-9d44-d55bc65f9c7c.png differ diff --git a/new/gan-proj/img/0301aefd-7590-4301-8702-5ed8867ccab2.png b/new/gan-proj/img/0301aefd-7590-4301-8702-5ed8867ccab2.png new file mode 100644 index 0000000000000000000000000000000000000000..e01425e48938dc0f0d88884b160670ff599d3ede Binary files /dev/null and b/new/gan-proj/img/0301aefd-7590-4301-8702-5ed8867ccab2.png differ diff --git a/new/gan-proj/img/04528291-1308-47df-8d56-5b242a809cb1.png b/new/gan-proj/img/04528291-1308-47df-8d56-5b242a809cb1.png new file mode 100644 index 0000000000000000000000000000000000000000..e72248a3c2e292c929f3155f57b8e30b24f20f08 Binary files /dev/null and b/new/gan-proj/img/04528291-1308-47df-8d56-5b242a809cb1.png differ diff --git a/new/gan-proj/img/04c840a1-a6ae-4b3d-a141-ed58f9dae21b.png b/new/gan-proj/img/04c840a1-a6ae-4b3d-a141-ed58f9dae21b.png new file mode 100644 index 0000000000000000000000000000000000000000..2d3d63496c53a5ed3df08bd6049e5cd7df7a54f7 Binary files /dev/null and b/new/gan-proj/img/04c840a1-a6ae-4b3d-a141-ed58f9dae21b.png differ diff --git a/new/gan-proj/img/06a7c0a5-f5d9-43d2-8673-4fe3e1adbdb0.png b/new/gan-proj/img/06a7c0a5-f5d9-43d2-8673-4fe3e1adbdb0.png new file mode 100644 index 0000000000000000000000000000000000000000..cd00ca5f7cc4c61d23ae932d99f2c7c31891b71b Binary files /dev/null and b/new/gan-proj/img/06a7c0a5-f5d9-43d2-8673-4fe3e1adbdb0.png differ diff --git a/new/gan-proj/img/06bd836c-ea3f-4644-80ad-47923503546b.png b/new/gan-proj/img/06bd836c-ea3f-4644-80ad-47923503546b.png new file mode 100644 index 0000000000000000000000000000000000000000..d1d1a8f4a6a9e6800e81af797148849e70f71275 Binary files /dev/null and b/new/gan-proj/img/06bd836c-ea3f-4644-80ad-47923503546b.png differ diff --git a/new/gan-proj/img/0814fc40-ef62-4b25-a11f-8c520f7bcbc9.png b/new/gan-proj/img/0814fc40-ef62-4b25-a11f-8c520f7bcbc9.png new file mode 100644 index 0000000000000000000000000000000000000000..37f27d82431e059a286727b30bf2401c97291e69 Binary files /dev/null and b/new/gan-proj/img/0814fc40-ef62-4b25-a11f-8c520f7bcbc9.png differ diff --git a/new/gan-proj/img/0a355bdc-0c6f-4cd3-a62e-4b46a301c9f6.png b/new/gan-proj/img/0a355bdc-0c6f-4cd3-a62e-4b46a301c9f6.png new file mode 100644 index 0000000000000000000000000000000000000000..08e5bda993cb3f83202fd83305809b2c56390557 Binary files /dev/null and b/new/gan-proj/img/0a355bdc-0c6f-4cd3-a62e-4b46a301c9f6.png differ diff --git a/new/gan-proj/img/0bbb9055-1c43-4f7d-a900-fdf515b6d068.png b/new/gan-proj/img/0bbb9055-1c43-4f7d-a900-fdf515b6d068.png new file mode 100644 index 0000000000000000000000000000000000000000..9224731a17ac8a60ec3039d0d10ead1c2ce0d8a4 Binary files /dev/null and b/new/gan-proj/img/0bbb9055-1c43-4f7d-a900-fdf515b6d068.png differ diff --git a/new/gan-proj/img/0d33c46a-0a5f-4027-919c-30b910e6d93b.png b/new/gan-proj/img/0d33c46a-0a5f-4027-919c-30b910e6d93b.png new file mode 100644 index 0000000000000000000000000000000000000000..91e9e8ceb16b21e5ce5b8a99df784b7cb57b26fe Binary files /dev/null and b/new/gan-proj/img/0d33c46a-0a5f-4027-919c-30b910e6d93b.png differ diff --git a/new/gan-proj/img/0e7b1187-382e-4584-9c8b-b39c1db1fd91.png b/new/gan-proj/img/0e7b1187-382e-4584-9c8b-b39c1db1fd91.png new file mode 100644 index 0000000000000000000000000000000000000000..724628a8d80c44908aa7b555f01fbec029b9e618 Binary files /dev/null and b/new/gan-proj/img/0e7b1187-382e-4584-9c8b-b39c1db1fd91.png differ diff --git a/new/gan-proj/img/1032f91a-c420-45d3-846b-6d2f4add232c.png b/new/gan-proj/img/1032f91a-c420-45d3-846b-6d2f4add232c.png new file mode 100644 index 0000000000000000000000000000000000000000..59acd1d82c9c891d1b80815aaee1246da8585bac Binary files /dev/null and b/new/gan-proj/img/1032f91a-c420-45d3-846b-6d2f4add232c.png differ diff --git a/new/gan-proj/img/10c2247d-25a3-41dc-9b01-e117a05f6b1d.png b/new/gan-proj/img/10c2247d-25a3-41dc-9b01-e117a05f6b1d.png new file mode 100644 index 0000000000000000000000000000000000000000..b070ff5d73ee350748c50aaf6ea08c76b2b14e5c Binary files /dev/null and b/new/gan-proj/img/10c2247d-25a3-41dc-9b01-e117a05f6b1d.png differ diff --git a/new/gan-proj/img/115b5d5f-6623-46dc-bed1-e99a6aa5910c.png b/new/gan-proj/img/115b5d5f-6623-46dc-bed1-e99a6aa5910c.png new file mode 100644 index 0000000000000000000000000000000000000000..ba58a7f09869db1e599dabfde45551d228811202 Binary files /dev/null and b/new/gan-proj/img/115b5d5f-6623-46dc-bed1-e99a6aa5910c.png differ diff --git a/new/gan-proj/img/134a6916-3b21-48a7-88ee-a115d848fbe5.png b/new/gan-proj/img/134a6916-3b21-48a7-88ee-a115d848fbe5.png new file mode 100644 index 0000000000000000000000000000000000000000..4d4d60a6c95bb8c050209d9a8a3fe5c1d4d30444 Binary files /dev/null and b/new/gan-proj/img/134a6916-3b21-48a7-88ee-a115d848fbe5.png differ diff --git a/new/gan-proj/img/1629f39f-eccc-4ba0-bec4-6263e873ed59.png b/new/gan-proj/img/1629f39f-eccc-4ba0-bec4-6263e873ed59.png new file mode 100644 index 0000000000000000000000000000000000000000..404c7e6f24fc4ee5cbdf7f108290fd86170bf64a Binary files /dev/null and b/new/gan-proj/img/1629f39f-eccc-4ba0-bec4-6263e873ed59.png differ diff --git a/new/gan-proj/img/17a59550-253c-4e61-b15e-637119f829ca.png b/new/gan-proj/img/17a59550-253c-4e61-b15e-637119f829ca.png new file mode 100644 index 0000000000000000000000000000000000000000..598f32e462af63144337058deff1be17b271a0f8 Binary files /dev/null and b/new/gan-proj/img/17a59550-253c-4e61-b15e-637119f829ca.png differ diff --git a/new/gan-proj/img/1ad83b3d-2320-40bc-9857-78f2c2099096.png b/new/gan-proj/img/1ad83b3d-2320-40bc-9857-78f2c2099096.png new file mode 100644 index 0000000000000000000000000000000000000000..c7521c36a9aa48eb248cfabec1003dc96d9b0a97 Binary files /dev/null and b/new/gan-proj/img/1ad83b3d-2320-40bc-9857-78f2c2099096.png differ diff --git a/new/gan-proj/img/1afafd90-be55-4633-97c5-a1d1eb7fd6c3.png b/new/gan-proj/img/1afafd90-be55-4633-97c5-a1d1eb7fd6c3.png new file mode 100644 index 0000000000000000000000000000000000000000..8acf38800b12b40256f1a5566112da0622389915 Binary files /dev/null and b/new/gan-proj/img/1afafd90-be55-4633-97c5-a1d1eb7fd6c3.png differ diff --git a/new/gan-proj/img/1ce9659c-27e3-423a-a3b8-be7e3e05b096.png b/new/gan-proj/img/1ce9659c-27e3-423a-a3b8-be7e3e05b096.png new file mode 100644 index 0000000000000000000000000000000000000000..c939e166cb0dadd1ff7d6510d711ab8a79f650d6 Binary files /dev/null and b/new/gan-proj/img/1ce9659c-27e3-423a-a3b8-be7e3e05b096.png differ diff --git a/new/gan-proj/img/1cf58b02-f255-4082-8b3a-1db3bf0ac832.png b/new/gan-proj/img/1cf58b02-f255-4082-8b3a-1db3bf0ac832.png new file mode 100644 index 0000000000000000000000000000000000000000..014bc2f903bcb8934688b73823d6a088af5508bf Binary files /dev/null and b/new/gan-proj/img/1cf58b02-f255-4082-8b3a-1db3bf0ac832.png differ diff --git a/new/gan-proj/img/24684f0c-814c-4b33-bfe7-c177cfe19a46.png b/new/gan-proj/img/24684f0c-814c-4b33-bfe7-c177cfe19a46.png new file mode 100644 index 0000000000000000000000000000000000000000..49876dc05525778576f2a25adc758d021c25f3fa Binary files /dev/null and b/new/gan-proj/img/24684f0c-814c-4b33-bfe7-c177cfe19a46.png differ diff --git a/new/gan-proj/img/25c92f62-a0a7-4c64-b768-577ac7dfe05b.png b/new/gan-proj/img/25c92f62-a0a7-4c64-b768-577ac7dfe05b.png new file mode 100644 index 0000000000000000000000000000000000000000..a2425d0c9adc5750bbe0f6d7d3f178567bca3d4d Binary files /dev/null and b/new/gan-proj/img/25c92f62-a0a7-4c64-b768-577ac7dfe05b.png differ diff --git a/new/gan-proj/img/277d3877-16f5-4ce5-b5f0-2703bc2d3075.png b/new/gan-proj/img/277d3877-16f5-4ce5-b5f0-2703bc2d3075.png new file mode 100644 index 0000000000000000000000000000000000000000..802385a6983219b1d9e4cb96c9135fc20654cc92 Binary files /dev/null and b/new/gan-proj/img/277d3877-16f5-4ce5-b5f0-2703bc2d3075.png differ diff --git a/new/gan-proj/img/290e169c-7581-48c6-94b7-8ee69c49b6b0.png b/new/gan-proj/img/290e169c-7581-48c6-94b7-8ee69c49b6b0.png new file mode 100644 index 0000000000000000000000000000000000000000..7328c272dbad08288d2d1d7a4ae22e4d8cf48e41 Binary files /dev/null and b/new/gan-proj/img/290e169c-7581-48c6-94b7-8ee69c49b6b0.png differ diff --git a/new/gan-proj/img/29d32b85-e867-4d9e-9e51-814681394bb9.png b/new/gan-proj/img/29d32b85-e867-4d9e-9e51-814681394bb9.png new file mode 100644 index 0000000000000000000000000000000000000000..d61d9d5bb23993c30a087bfd472ea66ea1dd5ce9 Binary files /dev/null and b/new/gan-proj/img/29d32b85-e867-4d9e-9e51-814681394bb9.png differ diff --git a/new/gan-proj/img/2e9c2935-f734-48ee-afae-ee24ccfa0b0a.png b/new/gan-proj/img/2e9c2935-f734-48ee-afae-ee24ccfa0b0a.png new file mode 100644 index 0000000000000000000000000000000000000000..6e5eded2d7b94c95499d34ebf42e6bf1b79b7fa9 Binary files /dev/null and b/new/gan-proj/img/2e9c2935-f734-48ee-afae-ee24ccfa0b0a.png differ diff --git a/new/gan-proj/img/2fc3c44d-59b8-4da3-b9b7-b5daabb95bb6.png b/new/gan-proj/img/2fc3c44d-59b8-4da3-b9b7-b5daabb95bb6.png new file mode 100644 index 0000000000000000000000000000000000000000..4d4d60a6c95bb8c050209d9a8a3fe5c1d4d30444 Binary files /dev/null and b/new/gan-proj/img/2fc3c44d-59b8-4da3-b9b7-b5daabb95bb6.png differ diff --git a/new/gan-proj/img/3019472f-d08b-4fa2-9ea1-071be37bc6bd.png b/new/gan-proj/img/3019472f-d08b-4fa2-9ea1-071be37bc6bd.png new file mode 100644 index 0000000000000000000000000000000000000000..f0b90dab8bebbc5ad26cf7e4159a438c8406e08e Binary files /dev/null and b/new/gan-proj/img/3019472f-d08b-4fa2-9ea1-071be37bc6bd.png differ diff --git a/new/gan-proj/img/308050a3-9d10-4f05-9e8e-d1e4e07bbb93.png b/new/gan-proj/img/308050a3-9d10-4f05-9e8e-d1e4e07bbb93.png new file mode 100644 index 0000000000000000000000000000000000000000..014905b2acf72d0fb284bf28d8346b66ec1bd5fe Binary files /dev/null and b/new/gan-proj/img/308050a3-9d10-4f05-9e8e-d1e4e07bbb93.png differ diff --git a/new/gan-proj/img/31009ecb-449c-41d6-81c4-dd1d976fb6f2.png b/new/gan-proj/img/31009ecb-449c-41d6-81c4-dd1d976fb6f2.png new file mode 100644 index 0000000000000000000000000000000000000000..ae50962f768b072cfe962547960a0b1f65018cbc Binary files /dev/null and b/new/gan-proj/img/31009ecb-449c-41d6-81c4-dd1d976fb6f2.png differ diff --git a/new/gan-proj/img/331b771d-6468-4640-a8fc-df5075645e70.png b/new/gan-proj/img/331b771d-6468-4640-a8fc-df5075645e70.png new file mode 100644 index 0000000000000000000000000000000000000000..a6390d6f0a6b12f934a86d9d288b220e045bdf96 Binary files /dev/null and b/new/gan-proj/img/331b771d-6468-4640-a8fc-df5075645e70.png differ diff --git a/new/gan-proj/img/34d03920-182c-4630-bba6-25d7f728c1f6.png b/new/gan-proj/img/34d03920-182c-4630-bba6-25d7f728c1f6.png new file mode 100644 index 0000000000000000000000000000000000000000..19fb3f252b79397e57ac0922ae43a7b614a2bf7b Binary files /dev/null and b/new/gan-proj/img/34d03920-182c-4630-bba6-25d7f728c1f6.png differ diff --git a/new/gan-proj/img/36d35229-c3f4-4edf-a334-65867998c3aa.png b/new/gan-proj/img/36d35229-c3f4-4edf-a334-65867998c3aa.png new file mode 100644 index 0000000000000000000000000000000000000000..8e73a550e48fcd1a97c125afab2c513a9baf59f3 Binary files /dev/null and b/new/gan-proj/img/36d35229-c3f4-4edf-a334-65867998c3aa.png differ diff --git a/new/gan-proj/img/3720bf75-1dba-477b-b833-865282482a25.png b/new/gan-proj/img/3720bf75-1dba-477b-b833-865282482a25.png new file mode 100644 index 0000000000000000000000000000000000000000..014905b2acf72d0fb284bf28d8346b66ec1bd5fe Binary files /dev/null and b/new/gan-proj/img/3720bf75-1dba-477b-b833-865282482a25.png differ diff --git a/new/gan-proj/img/3920b802-2eb7-4c4c-a835-754920e124a5.png b/new/gan-proj/img/3920b802-2eb7-4c4c-a835-754920e124a5.png new file mode 100644 index 0000000000000000000000000000000000000000..43edd346bae48c41814974808f44498981327f75 Binary files /dev/null and b/new/gan-proj/img/3920b802-2eb7-4c4c-a835-754920e124a5.png differ diff --git a/new/gan-proj/img/39400e6f-0df8-46a1-b80c-17516e3c6adf.png b/new/gan-proj/img/39400e6f-0df8-46a1-b80c-17516e3c6adf.png new file mode 100644 index 0000000000000000000000000000000000000000..13ee8bb2f1d8daae2875fde4e8d448923085165d Binary files /dev/null and b/new/gan-proj/img/39400e6f-0df8-46a1-b80c-17516e3c6adf.png differ diff --git a/new/gan-proj/img/3942e803-5863-4db1-85c6-0eb923a480be.png b/new/gan-proj/img/3942e803-5863-4db1-85c6-0eb923a480be.png new file mode 100644 index 0000000000000000000000000000000000000000..37f27d82431e059a286727b30bf2401c97291e69 Binary files /dev/null and b/new/gan-proj/img/3942e803-5863-4db1-85c6-0eb923a480be.png differ diff --git a/new/gan-proj/img/39b79c42-5da0-4eb5-92c4-146d419591ac.png b/new/gan-proj/img/39b79c42-5da0-4eb5-92c4-146d419591ac.png new file mode 100644 index 0000000000000000000000000000000000000000..22303a9c8928666da806df187451b21066626803 Binary files /dev/null and b/new/gan-proj/img/39b79c42-5da0-4eb5-92c4-146d419591ac.png differ diff --git a/new/gan-proj/img/3af97dd6-cd57-48b8-92bb-cd83f75125d4.png b/new/gan-proj/img/3af97dd6-cd57-48b8-92bb-cd83f75125d4.png new file mode 100644 index 0000000000000000000000000000000000000000..37defd275b7150388005001d92da79c6abfe761d Binary files /dev/null and b/new/gan-proj/img/3af97dd6-cd57-48b8-92bb-cd83f75125d4.png differ diff --git a/new/gan-proj/img/3b2c1077-9f9a-4cda-8670-cd8d737041f8.png b/new/gan-proj/img/3b2c1077-9f9a-4cda-8670-cd8d737041f8.png new file mode 100644 index 0000000000000000000000000000000000000000..04b145a50f002ec93fabb7548b1d393c2a225b4c Binary files /dev/null and b/new/gan-proj/img/3b2c1077-9f9a-4cda-8670-cd8d737041f8.png differ diff --git a/new/gan-proj/img/3df2dc52-a6ff-4461-aedd-6fc37114d111.png b/new/gan-proj/img/3df2dc52-a6ff-4461-aedd-6fc37114d111.png new file mode 100644 index 0000000000000000000000000000000000000000..43edd346bae48c41814974808f44498981327f75 Binary files /dev/null and b/new/gan-proj/img/3df2dc52-a6ff-4461-aedd-6fc37114d111.png differ diff --git a/new/gan-proj/img/3ed12abb-fb0d-4205-848a-928127ec92ca.png b/new/gan-proj/img/3ed12abb-fb0d-4205-848a-928127ec92ca.png new file mode 100644 index 0000000000000000000000000000000000000000..f1a2b21e879f20dbb748f2d20cc55d042f21a2fa Binary files /dev/null and b/new/gan-proj/img/3ed12abb-fb0d-4205-848a-928127ec92ca.png differ diff --git a/new/gan-proj/img/3fd25fcc-55fa-4bce-b1d7-4f4d801ba076.png b/new/gan-proj/img/3fd25fcc-55fa-4bce-b1d7-4f4d801ba076.png new file mode 100644 index 0000000000000000000000000000000000000000..6d29cf627e6651fbb2cd4fb0f40402bad7537d6e Binary files /dev/null and b/new/gan-proj/img/3fd25fcc-55fa-4bce-b1d7-4f4d801ba076.png differ diff --git a/new/gan-proj/img/411f5f33-a51c-4f61-a795-4fb06231ea00.png b/new/gan-proj/img/411f5f33-a51c-4f61-a795-4fb06231ea00.png new file mode 100644 index 0000000000000000000000000000000000000000..dd83e26fe369fa30ae6a838e0bbb7f9c10cb5615 Binary files /dev/null and b/new/gan-proj/img/411f5f33-a51c-4f61-a795-4fb06231ea00.png differ diff --git a/new/gan-proj/img/42eb238c-1bba-415e-b416-469f04e29932.png b/new/gan-proj/img/42eb238c-1bba-415e-b416-469f04e29932.png new file mode 100644 index 0000000000000000000000000000000000000000..82ef864520b2ab0304f1a703b7c8580a345a253d Binary files /dev/null and b/new/gan-proj/img/42eb238c-1bba-415e-b416-469f04e29932.png differ diff --git a/new/gan-proj/img/42f86eb5-0593-4986-8a38-397be9b66e56.png b/new/gan-proj/img/42f86eb5-0593-4986-8a38-397be9b66e56.png new file mode 100644 index 0000000000000000000000000000000000000000..99899333c528f8b3190e58ffe3e94f87f575c3e3 Binary files /dev/null and b/new/gan-proj/img/42f86eb5-0593-4986-8a38-397be9b66e56.png differ diff --git a/new/gan-proj/img/464b155a-0a20-4761-bb03-4f4b36f72554.png b/new/gan-proj/img/464b155a-0a20-4761-bb03-4f4b36f72554.png new file mode 100644 index 0000000000000000000000000000000000000000..8023d7d4124a2aa9f35a776eddef53fe7942b20f Binary files /dev/null and b/new/gan-proj/img/464b155a-0a20-4761-bb03-4f4b36f72554.png differ diff --git a/new/gan-proj/img/4769bf48-3e9e-4976-999d-22513076b07d.png b/new/gan-proj/img/4769bf48-3e9e-4976-999d-22513076b07d.png new file mode 100644 index 0000000000000000000000000000000000000000..05f7aa15a175fcbe13c3c4a6aafb748b6bda5d6f Binary files /dev/null and b/new/gan-proj/img/4769bf48-3e9e-4976-999d-22513076b07d.png differ diff --git a/new/gan-proj/img/4777940b-3156-48bb-8ab8-a0b8d3d97978.png b/new/gan-proj/img/4777940b-3156-48bb-8ab8-a0b8d3d97978.png new file mode 100644 index 0000000000000000000000000000000000000000..3ab708b6536ad2455919050f66cd8217c27b4a67 Binary files /dev/null and b/new/gan-proj/img/4777940b-3156-48bb-8ab8-a0b8d3d97978.png differ diff --git a/new/gan-proj/img/4a160433-e15b-440d-8a3f-91163db58421.png b/new/gan-proj/img/4a160433-e15b-440d-8a3f-91163db58421.png new file mode 100644 index 0000000000000000000000000000000000000000..8e7db2b4aaa8403d22ae171c80de29904319c573 Binary files /dev/null and b/new/gan-proj/img/4a160433-e15b-440d-8a3f-91163db58421.png differ diff --git a/new/gan-proj/img/4afb529e-38ff-40f5-b294-dd6545cd5251.png b/new/gan-proj/img/4afb529e-38ff-40f5-b294-dd6545cd5251.png new file mode 100644 index 0000000000000000000000000000000000000000..098a648d38de584f6f498cf6b6d6bbe6fc2aa586 Binary files /dev/null and b/new/gan-proj/img/4afb529e-38ff-40f5-b294-dd6545cd5251.png differ diff --git a/new/gan-proj/img/4b6517ec-d185-4ec3-a4f9-acfbba2ee45f.png b/new/gan-proj/img/4b6517ec-d185-4ec3-a4f9-acfbba2ee45f.png new file mode 100644 index 0000000000000000000000000000000000000000..149bbcabcf00071c756c9204cfaa0ebcdf41243b Binary files /dev/null and b/new/gan-proj/img/4b6517ec-d185-4ec3-a4f9-acfbba2ee45f.png differ diff --git a/new/gan-proj/img/4e2039a0-5a7e-4038-b68d-f19fa142d2d8.png b/new/gan-proj/img/4e2039a0-5a7e-4038-b68d-f19fa142d2d8.png new file mode 100644 index 0000000000000000000000000000000000000000..94b1603137e778cb99c21ef58659fd40ffa6e90e Binary files /dev/null and b/new/gan-proj/img/4e2039a0-5a7e-4038-b68d-f19fa142d2d8.png differ diff --git a/new/gan-proj/img/503cd7f0-4105-49a5-9095-d400768e7eb9.png b/new/gan-proj/img/503cd7f0-4105-49a5-9095-d400768e7eb9.png new file mode 100644 index 0000000000000000000000000000000000000000..21b260a0966f7b8d991364e41dc14efc69804f87 Binary files /dev/null and b/new/gan-proj/img/503cd7f0-4105-49a5-9095-d400768e7eb9.png differ diff --git a/new/gan-proj/img/50a98c0c-f195-40b5-9025-436b7d52523f.png b/new/gan-proj/img/50a98c0c-f195-40b5-9025-436b7d52523f.png new file mode 100644 index 0000000000000000000000000000000000000000..73b7ab1a72f5a2b81045ae2de7054e73c6b36ba5 Binary files /dev/null and b/new/gan-proj/img/50a98c0c-f195-40b5-9025-436b7d52523f.png differ diff --git a/new/gan-proj/img/51177648-2247-4346-b270-5139a5dcab46.png b/new/gan-proj/img/51177648-2247-4346-b270-5139a5dcab46.png new file mode 100644 index 0000000000000000000000000000000000000000..d6a43fad91196a2dfd6f7a790a8f0b1b49f40fd5 Binary files /dev/null and b/new/gan-proj/img/51177648-2247-4346-b270-5139a5dcab46.png differ diff --git a/new/gan-proj/img/515bff70-925a-4f3b-a995-470637fd676e.png b/new/gan-proj/img/515bff70-925a-4f3b-a995-470637fd676e.png new file mode 100644 index 0000000000000000000000000000000000000000..789fdd1034623146f90e41c7fbe7956fd4140d6e Binary files /dev/null and b/new/gan-proj/img/515bff70-925a-4f3b-a995-470637fd676e.png differ diff --git a/new/gan-proj/img/550e5193-b6a1-49ba-81eb-cd1fe71c7634.png b/new/gan-proj/img/550e5193-b6a1-49ba-81eb-cd1fe71c7634.png new file mode 100644 index 0000000000000000000000000000000000000000..910831f68b7fc0ead99af21b3a0d03d812a4cbd0 Binary files /dev/null and b/new/gan-proj/img/550e5193-b6a1-49ba-81eb-cd1fe71c7634.png differ diff --git a/new/gan-proj/img/5577e65f-d54f-48ad-9f22-950a7abf3c36.png b/new/gan-proj/img/5577e65f-d54f-48ad-9f22-950a7abf3c36.png new file mode 100644 index 0000000000000000000000000000000000000000..3d901a2599190deeda82b014d2838bf78bbb63de Binary files /dev/null and b/new/gan-proj/img/5577e65f-d54f-48ad-9f22-950a7abf3c36.png differ diff --git a/new/gan-proj/img/5636ece2-f10e-4485-bd3b-527ba2c14606.png b/new/gan-proj/img/5636ece2-f10e-4485-bd3b-527ba2c14606.png new file mode 100644 index 0000000000000000000000000000000000000000..8023d7d4124a2aa9f35a776eddef53fe7942b20f Binary files /dev/null and b/new/gan-proj/img/5636ece2-f10e-4485-bd3b-527ba2c14606.png differ diff --git a/new/gan-proj/img/56ceb926-b75b-490c-91ff-9b736d389cb5.png b/new/gan-proj/img/56ceb926-b75b-490c-91ff-9b736d389cb5.png new file mode 100644 index 0000000000000000000000000000000000000000..fe1a0f8d83735e36c40ae5ddff870233d34ba2a2 Binary files /dev/null and b/new/gan-proj/img/56ceb926-b75b-490c-91ff-9b736d389cb5.png differ diff --git a/new/gan-proj/img/577645cb-a80b-4a10-98e8-5cf21f336b77.png b/new/gan-proj/img/577645cb-a80b-4a10-98e8-5cf21f336b77.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f6753c0cf3e641b6a7c5849145e9ddd9506f97 Binary files /dev/null and b/new/gan-proj/img/577645cb-a80b-4a10-98e8-5cf21f336b77.png differ diff --git a/new/gan-proj/img/590d57c0-43e3-4ca8-ad30-8bd20887ecc5.png b/new/gan-proj/img/590d57c0-43e3-4ca8-ad30-8bd20887ecc5.png new file mode 100644 index 0000000000000000000000000000000000000000..27ebc23230e14d29d1ac6dce59ba9b20fab3ffc5 Binary files /dev/null and b/new/gan-proj/img/590d57c0-43e3-4ca8-ad30-8bd20887ecc5.png differ diff --git a/new/gan-proj/img/5bbaa568-0f1e-431b-b8d1-f8bacae72282.png b/new/gan-proj/img/5bbaa568-0f1e-431b-b8d1-f8bacae72282.png new file mode 100644 index 0000000000000000000000000000000000000000..9f5712fd649e61453062c0c716b6b15ac5fa4d53 Binary files /dev/null and b/new/gan-proj/img/5bbaa568-0f1e-431b-b8d1-f8bacae72282.png differ diff --git a/new/gan-proj/img/5d8ca1c2-04b5-4106-b5c6-5c96ca28ce46.png b/new/gan-proj/img/5d8ca1c2-04b5-4106-b5c6-5c96ca28ce46.png new file mode 100644 index 0000000000000000000000000000000000000000..ae22c0a159dfa2058aca13e2673b41c3b17053f2 Binary files /dev/null and b/new/gan-proj/img/5d8ca1c2-04b5-4106-b5c6-5c96ca28ce46.png differ diff --git a/new/gan-proj/img/5ee63015-1266-4f90-b3cc-dca5d99f293e.png b/new/gan-proj/img/5ee63015-1266-4f90-b3cc-dca5d99f293e.png new file mode 100644 index 0000000000000000000000000000000000000000..3ab708b6536ad2455919050f66cd8217c27b4a67 Binary files /dev/null and b/new/gan-proj/img/5ee63015-1266-4f90-b3cc-dca5d99f293e.png differ diff --git a/new/gan-proj/img/61a7b2ff-8f7e-4c5f-96c4-575ea2f11138.png b/new/gan-proj/img/61a7b2ff-8f7e-4c5f-96c4-575ea2f11138.png new file mode 100644 index 0000000000000000000000000000000000000000..a719bed49764066c396d0f6d71b1589ad9e4bb0e Binary files /dev/null and b/new/gan-proj/img/61a7b2ff-8f7e-4c5f-96c4-575ea2f11138.png differ diff --git a/new/gan-proj/img/63321408-24e4-4905-88c2-365d44294fae.png b/new/gan-proj/img/63321408-24e4-4905-88c2-365d44294fae.png new file mode 100644 index 0000000000000000000000000000000000000000..80340bad50e47d041bcf102f6c2b4974af6b1467 Binary files /dev/null and b/new/gan-proj/img/63321408-24e4-4905-88c2-365d44294fae.png differ diff --git a/new/gan-proj/img/63d2ac6d-4141-47c4-8807-19efe0bb901e.png b/new/gan-proj/img/63d2ac6d-4141-47c4-8807-19efe0bb901e.png new file mode 100644 index 0000000000000000000000000000000000000000..0decc8aaa6a3a96247694d3c7efbb98d7e1eb15b Binary files /dev/null and b/new/gan-proj/img/63d2ac6d-4141-47c4-8807-19efe0bb901e.png differ diff --git a/new/gan-proj/img/6567c751-fa0d-4009-b4bd-b864d4ecab7c.png b/new/gan-proj/img/6567c751-fa0d-4009-b4bd-b864d4ecab7c.png new file mode 100644 index 0000000000000000000000000000000000000000..ebbe273cd559743c6befd94b8e850d6800d153af Binary files /dev/null and b/new/gan-proj/img/6567c751-fa0d-4009-b4bd-b864d4ecab7c.png differ diff --git a/new/gan-proj/img/65c94f59-5ff4-47de-91ea-301d83e34811.png b/new/gan-proj/img/65c94f59-5ff4-47de-91ea-301d83e34811.png new file mode 100644 index 0000000000000000000000000000000000000000..0258beaa60dff0e2316f8873adb9badfdcb09530 Binary files /dev/null and b/new/gan-proj/img/65c94f59-5ff4-47de-91ea-301d83e34811.png differ diff --git a/new/gan-proj/img/69081f49-8ca4-4a69-a42c-b82ddc2285ca.png b/new/gan-proj/img/69081f49-8ca4-4a69-a42c-b82ddc2285ca.png new file mode 100644 index 0000000000000000000000000000000000000000..b070ff5d73ee350748c50aaf6ea08c76b2b14e5c Binary files /dev/null and b/new/gan-proj/img/69081f49-8ca4-4a69-a42c-b82ddc2285ca.png differ diff --git a/new/gan-proj/img/6923fbb8-a6ef-4c14-ab83-c8586f272630.png b/new/gan-proj/img/6923fbb8-a6ef-4c14-ab83-c8586f272630.png new file mode 100644 index 0000000000000000000000000000000000000000..40e8f8fd3678e3c129309b6c738add266d82e2d5 Binary files /dev/null and b/new/gan-proj/img/6923fbb8-a6ef-4c14-ab83-c8586f272630.png differ diff --git a/new/gan-proj/img/692e8b22-1448-49a9-8d2e-6ccfcba6e5b3.png b/new/gan-proj/img/692e8b22-1448-49a9-8d2e-6ccfcba6e5b3.png new file mode 100644 index 0000000000000000000000000000000000000000..4f6d3099e72f4a0b879b28f9555bfdd280924f49 Binary files /dev/null and b/new/gan-proj/img/692e8b22-1448-49a9-8d2e-6ccfcba6e5b3.png differ diff --git a/new/gan-proj/img/69b11dd7-e38d-46ad-a293-69d3827daf0d.png b/new/gan-proj/img/69b11dd7-e38d-46ad-a293-69d3827daf0d.png new file mode 100644 index 0000000000000000000000000000000000000000..9b8907ab659e1ead6b515455f43fd497232a75ed Binary files /dev/null and b/new/gan-proj/img/69b11dd7-e38d-46ad-a293-69d3827daf0d.png differ diff --git a/new/gan-proj/img/69b142c0-77c5-4a16-b698-d93249bc30c4.png b/new/gan-proj/img/69b142c0-77c5-4a16-b698-d93249bc30c4.png new file mode 100644 index 0000000000000000000000000000000000000000..8023d7d4124a2aa9f35a776eddef53fe7942b20f Binary files /dev/null and b/new/gan-proj/img/69b142c0-77c5-4a16-b698-d93249bc30c4.png differ diff --git a/new/gan-proj/img/6b5db762-e527-4194-85ab-83cc4ff9a1b5.png b/new/gan-proj/img/6b5db762-e527-4194-85ab-83cc4ff9a1b5.png new file mode 100644 index 0000000000000000000000000000000000000000..9ada4204a6934792b5340752d1934474906aebae Binary files /dev/null and b/new/gan-proj/img/6b5db762-e527-4194-85ab-83cc4ff9a1b5.png differ diff --git a/new/gan-proj/img/6cb9082b-a52f-48e7-8193-caf3c11e93ea.png b/new/gan-proj/img/6cb9082b-a52f-48e7-8193-caf3c11e93ea.png new file mode 100644 index 0000000000000000000000000000000000000000..807f67908fe292669399c3561ef34d96501987aa Binary files /dev/null and b/new/gan-proj/img/6cb9082b-a52f-48e7-8193-caf3c11e93ea.png differ diff --git a/new/gan-proj/img/6dcc68ed-e0e3-4959-9aa6-aa40d12b3ad6.png b/new/gan-proj/img/6dcc68ed-e0e3-4959-9aa6-aa40d12b3ad6.png new file mode 100644 index 0000000000000000000000000000000000000000..4f13020261ae2bd905602d93162a7a1716783bbc Binary files /dev/null and b/new/gan-proj/img/6dcc68ed-e0e3-4959-9aa6-aa40d12b3ad6.png differ diff --git a/new/gan-proj/img/6f2f1522-849c-4ee4-9c08-57884ea1b6b1.png b/new/gan-proj/img/6f2f1522-849c-4ee4-9c08-57884ea1b6b1.png new file mode 100644 index 0000000000000000000000000000000000000000..dc07cbeec28bf054288effa53fe70af8d47cdcdb Binary files /dev/null and b/new/gan-proj/img/6f2f1522-849c-4ee4-9c08-57884ea1b6b1.png differ diff --git a/new/gan-proj/img/7201db6b-98f4-473e-8ba3-615cd8d132f1.png b/new/gan-proj/img/7201db6b-98f4-473e-8ba3-615cd8d132f1.png new file mode 100644 index 0000000000000000000000000000000000000000..816bfa0a45c578b345ef4a7b349106aad29cb61b Binary files /dev/null and b/new/gan-proj/img/7201db6b-98f4-473e-8ba3-615cd8d132f1.png differ diff --git a/new/gan-proj/img/73371faa-5c7e-475f-a169-4229e9d04b24.png b/new/gan-proj/img/73371faa-5c7e-475f-a169-4229e9d04b24.png new file mode 100644 index 0000000000000000000000000000000000000000..4347b29228cb7455b8426d190a4decd0c8f5808e Binary files /dev/null and b/new/gan-proj/img/73371faa-5c7e-475f-a169-4229e9d04b24.png differ diff --git a/new/gan-proj/img/777f3571-0412-4464-bb22-c1445c488875.png b/new/gan-proj/img/777f3571-0412-4464-bb22-c1445c488875.png new file mode 100644 index 0000000000000000000000000000000000000000..4eb17cc12dd14d18295f5439530ce0e4a5c49eda Binary files /dev/null and b/new/gan-proj/img/777f3571-0412-4464-bb22-c1445c488875.png differ diff --git a/new/gan-proj/img/7846fcd5-c526-476d-bde4-e03e5d0980d0.png b/new/gan-proj/img/7846fcd5-c526-476d-bde4-e03e5d0980d0.png new file mode 100644 index 0000000000000000000000000000000000000000..67310982828206c9f8c50950b32de866ebb2274d Binary files /dev/null and b/new/gan-proj/img/7846fcd5-c526-476d-bde4-e03e5d0980d0.png differ diff --git a/new/gan-proj/img/78a30b0f-14dc-4d20-ac13-5f3a30bc78d7.png b/new/gan-proj/img/78a30b0f-14dc-4d20-ac13-5f3a30bc78d7.png new file mode 100644 index 0000000000000000000000000000000000000000..55585a925e1c71ae93a0b02914df507daf99cebc Binary files /dev/null and b/new/gan-proj/img/78a30b0f-14dc-4d20-ac13-5f3a30bc78d7.png differ diff --git a/new/gan-proj/img/79328706-d7f6-47a5-b93c-7c1c308af456.png b/new/gan-proj/img/79328706-d7f6-47a5-b93c-7c1c308af456.png new file mode 100644 index 0000000000000000000000000000000000000000..99e5d048c9b9975412278cc74a19d3302b3b082b Binary files /dev/null and b/new/gan-proj/img/79328706-d7f6-47a5-b93c-7c1c308af456.png differ diff --git a/new/gan-proj/img/7937c970-9e91-4f47-bc05-2b230ae1b0c6.png b/new/gan-proj/img/7937c970-9e91-4f47-bc05-2b230ae1b0c6.png new file mode 100644 index 0000000000000000000000000000000000000000..959b1c7e638c5551e3df666aff1a0c7ab3cced7b Binary files /dev/null and b/new/gan-proj/img/7937c970-9e91-4f47-bc05-2b230ae1b0c6.png differ diff --git a/new/gan-proj/img/7a1ee66f-996d-45d0-9a4f-47a608ed2e8d.png b/new/gan-proj/img/7a1ee66f-996d-45d0-9a4f-47a608ed2e8d.png new file mode 100644 index 0000000000000000000000000000000000000000..4ad40fd522d1936ec15c1067f315d3424bd7a366 Binary files /dev/null and b/new/gan-proj/img/7a1ee66f-996d-45d0-9a4f-47a608ed2e8d.png differ diff --git a/new/gan-proj/img/7b4b89cd-9b42-4609-84e2-94086b34ac3a.png b/new/gan-proj/img/7b4b89cd-9b42-4609-84e2-94086b34ac3a.png new file mode 100644 index 0000000000000000000000000000000000000000..3c9ccc4330f9522420ef2fc8d4e5df1d58d39d69 Binary files /dev/null and b/new/gan-proj/img/7b4b89cd-9b42-4609-84e2-94086b34ac3a.png differ diff --git a/new/gan-proj/img/7c0476a9-d31c-4ef3-b3b3-2193680e7c0c.png b/new/gan-proj/img/7c0476a9-d31c-4ef3-b3b3-2193680e7c0c.png new file mode 100644 index 0000000000000000000000000000000000000000..0955ed353232e4cf0fd518d9efe63dcb2982adc5 Binary files /dev/null and b/new/gan-proj/img/7c0476a9-d31c-4ef3-b3b3-2193680e7c0c.png differ diff --git a/new/gan-proj/img/7ce6d943-ee01-4777-b805-c300627ec0ec.png b/new/gan-proj/img/7ce6d943-ee01-4777-b805-c300627ec0ec.png new file mode 100644 index 0000000000000000000000000000000000000000..e51785756eb1fa28b0ae96d92638d9743420cacd Binary files /dev/null and b/new/gan-proj/img/7ce6d943-ee01-4777-b805-c300627ec0ec.png differ diff --git a/new/gan-proj/img/7fc08303-569e-499f-bc91-e2ae9684f947.png b/new/gan-proj/img/7fc08303-569e-499f-bc91-e2ae9684f947.png new file mode 100644 index 0000000000000000000000000000000000000000..443405e3699d793b620af9623e12ef9df5ef15bb Binary files /dev/null and b/new/gan-proj/img/7fc08303-569e-499f-bc91-e2ae9684f947.png differ diff --git a/new/gan-proj/img/81830986-deb9-4db9-9dba-518aa6313466.png b/new/gan-proj/img/81830986-deb9-4db9-9dba-518aa6313466.png new file mode 100644 index 0000000000000000000000000000000000000000..96568abba7f9697a6ffcdeb102bf4772766bec5a Binary files /dev/null and b/new/gan-proj/img/81830986-deb9-4db9-9dba-518aa6313466.png differ diff --git a/new/gan-proj/img/82a9152b-d3fb-467b-837f-96af60995452.png b/new/gan-proj/img/82a9152b-d3fb-467b-837f-96af60995452.png new file mode 100644 index 0000000000000000000000000000000000000000..37e82a4334c59308caf275e3758e142abc898679 Binary files /dev/null and b/new/gan-proj/img/82a9152b-d3fb-467b-837f-96af60995452.png differ diff --git a/new/gan-proj/img/83c4f3c0-a5f7-48c4-b0b3-767e6017e43c.png b/new/gan-proj/img/83c4f3c0-a5f7-48c4-b0b3-767e6017e43c.png new file mode 100644 index 0000000000000000000000000000000000000000..d61d9d5bb23993c30a087bfd472ea66ea1dd5ce9 Binary files /dev/null and b/new/gan-proj/img/83c4f3c0-a5f7-48c4-b0b3-767e6017e43c.png differ diff --git a/new/gan-proj/img/851c0456-0b76-4d6e-b1d3-ef0735cb79b4.png b/new/gan-proj/img/851c0456-0b76-4d6e-b1d3-ef0735cb79b4.png new file mode 100644 index 0000000000000000000000000000000000000000..212276bcbea6a5ac46a3cef61fec77862ddc9589 Binary files /dev/null and b/new/gan-proj/img/851c0456-0b76-4d6e-b1d3-ef0735cb79b4.png differ diff --git a/new/gan-proj/img/869b6fae-e95a-4138-9df9-033a8e157e49.png b/new/gan-proj/img/869b6fae-e95a-4138-9df9-033a8e157e49.png new file mode 100644 index 0000000000000000000000000000000000000000..14e0aba8ea2fca981b4aee4dba060d680a136d11 Binary files /dev/null and b/new/gan-proj/img/869b6fae-e95a-4138-9df9-033a8e157e49.png differ diff --git a/new/gan-proj/img/885a0848-1055-4c3b-976f-11f36d73232b.png b/new/gan-proj/img/885a0848-1055-4c3b-976f-11f36d73232b.png new file mode 100644 index 0000000000000000000000000000000000000000..014905b2acf72d0fb284bf28d8346b66ec1bd5fe Binary files /dev/null and b/new/gan-proj/img/885a0848-1055-4c3b-976f-11f36d73232b.png differ diff --git a/new/gan-proj/img/8957072f-83ad-4a39-b6c5-0e06647201d7.png b/new/gan-proj/img/8957072f-83ad-4a39-b6c5-0e06647201d7.png new file mode 100644 index 0000000000000000000000000000000000000000..80340bad50e47d041bcf102f6c2b4974af6b1467 Binary files /dev/null and b/new/gan-proj/img/8957072f-83ad-4a39-b6c5-0e06647201d7.png differ diff --git a/new/gan-proj/img/8b2ac2fe-e616-4cfd-89e4-2a876127bef7.png b/new/gan-proj/img/8b2ac2fe-e616-4cfd-89e4-2a876127bef7.png new file mode 100644 index 0000000000000000000000000000000000000000..43edd346bae48c41814974808f44498981327f75 Binary files /dev/null and b/new/gan-proj/img/8b2ac2fe-e616-4cfd-89e4-2a876127bef7.png differ diff --git a/new/gan-proj/img/8bd40bf0-5919-4004-920c-9abffb3fe400.png b/new/gan-proj/img/8bd40bf0-5919-4004-920c-9abffb3fe400.png new file mode 100644 index 0000000000000000000000000000000000000000..8023d7d4124a2aa9f35a776eddef53fe7942b20f Binary files /dev/null and b/new/gan-proj/img/8bd40bf0-5919-4004-920c-9abffb3fe400.png differ diff --git a/new/gan-proj/img/8cdff081-d911-4e9a-80c3-fa21211eb3bb.png b/new/gan-proj/img/8cdff081-d911-4e9a-80c3-fa21211eb3bb.png new file mode 100644 index 0000000000000000000000000000000000000000..d78cf1ae4f548f3608f888eff866a4acce46f149 Binary files /dev/null and b/new/gan-proj/img/8cdff081-d911-4e9a-80c3-fa21211eb3bb.png differ diff --git a/new/gan-proj/img/8d391386-69b9-4890-bd5a-a078ba996754.png b/new/gan-proj/img/8d391386-69b9-4890-bd5a-a078ba996754.png new file mode 100644 index 0000000000000000000000000000000000000000..63fe80941c4e75d59607da4ebb9a11928c6506be Binary files /dev/null and b/new/gan-proj/img/8d391386-69b9-4890-bd5a-a078ba996754.png differ diff --git a/new/gan-proj/img/8d494e68-1020-471d-91de-e2d6acf9552c.png b/new/gan-proj/img/8d494e68-1020-471d-91de-e2d6acf9552c.png new file mode 100644 index 0000000000000000000000000000000000000000..aa19acfaa2385794e778442e52f888bb1b70f87e Binary files /dev/null and b/new/gan-proj/img/8d494e68-1020-471d-91de-e2d6acf9552c.png differ diff --git a/new/gan-proj/img/8ed3de7f-a1df-4723-9600-3ccffc5f16e4.png b/new/gan-proj/img/8ed3de7f-a1df-4723-9600-3ccffc5f16e4.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad178efc5749e90334234714c1b7be34bace35d Binary files /dev/null and b/new/gan-proj/img/8ed3de7f-a1df-4723-9600-3ccffc5f16e4.png differ diff --git a/new/gan-proj/img/905056c0-7994-444e-b42b-4ebce2f093d9.png b/new/gan-proj/img/905056c0-7994-444e-b42b-4ebce2f093d9.png new file mode 100644 index 0000000000000000000000000000000000000000..3995a8c14e3649215d9d0cd80f87b2c31b1bf593 Binary files /dev/null and b/new/gan-proj/img/905056c0-7994-444e-b42b-4ebce2f093d9.png differ diff --git a/new/gan-proj/img/9128ea2e-31f5-472a-8ebc-f3add1a716e0.png b/new/gan-proj/img/9128ea2e-31f5-472a-8ebc-f3add1a716e0.png new file mode 100644 index 0000000000000000000000000000000000000000..99d21f43382955317b5375e6b5b70a44b184dd74 Binary files /dev/null and b/new/gan-proj/img/9128ea2e-31f5-472a-8ebc-f3add1a716e0.png differ diff --git a/new/gan-proj/img/91c575c7-94bc-4803-ad27-cc9d948d77be.png b/new/gan-proj/img/91c575c7-94bc-4803-ad27-cc9d948d77be.png new file mode 100644 index 0000000000000000000000000000000000000000..0ea7ce1742034fa4c771e17ea344bc5cfc436bf0 Binary files /dev/null and b/new/gan-proj/img/91c575c7-94bc-4803-ad27-cc9d948d77be.png differ diff --git a/new/gan-proj/img/9261c4cf-3f8a-464b-adec-1aec0c855008.png b/new/gan-proj/img/9261c4cf-3f8a-464b-adec-1aec0c855008.png new file mode 100644 index 0000000000000000000000000000000000000000..8023d7d4124a2aa9f35a776eddef53fe7942b20f Binary files /dev/null and b/new/gan-proj/img/9261c4cf-3f8a-464b-adec-1aec0c855008.png differ diff --git a/new/gan-proj/img/92bdc164-79c8-410c-8665-8c0e9ab3e654.png b/new/gan-proj/img/92bdc164-79c8-410c-8665-8c0e9ab3e654.png new file mode 100644 index 0000000000000000000000000000000000000000..709829b4b9871fe4d9f45a3aa743df1af4a00348 Binary files /dev/null and b/new/gan-proj/img/92bdc164-79c8-410c-8665-8c0e9ab3e654.png differ diff --git a/new/gan-proj/img/94999cb2-2d49-4263-b900-3deef877ccd1.png b/new/gan-proj/img/94999cb2-2d49-4263-b900-3deef877ccd1.png new file mode 100644 index 0000000000000000000000000000000000000000..4d4d60a6c95bb8c050209d9a8a3fe5c1d4d30444 Binary files /dev/null and b/new/gan-proj/img/94999cb2-2d49-4263-b900-3deef877ccd1.png differ diff --git a/new/gan-proj/img/95c36bf0-5253-4227-9d80-7ab5e826094a.png b/new/gan-proj/img/95c36bf0-5253-4227-9d80-7ab5e826094a.png new file mode 100644 index 0000000000000000000000000000000000000000..80340bad50e47d041bcf102f6c2b4974af6b1467 Binary files /dev/null and b/new/gan-proj/img/95c36bf0-5253-4227-9d80-7ab5e826094a.png differ diff --git a/new/gan-proj/img/968121df-410e-4bff-9e76-57d447e34db5.png b/new/gan-proj/img/968121df-410e-4bff-9e76-57d447e34db5.png new file mode 100644 index 0000000000000000000000000000000000000000..8763434e1af80b1feedc69acc5b05db6877df684 Binary files /dev/null and b/new/gan-proj/img/968121df-410e-4bff-9e76-57d447e34db5.png differ diff --git a/new/gan-proj/img/969f8518-7d6b-42ce-9030-fdc7ca31818b.png b/new/gan-proj/img/969f8518-7d6b-42ce-9030-fdc7ca31818b.png new file mode 100644 index 0000000000000000000000000000000000000000..4645be93d95fca6cfeb3a3f47cacbc17b9bb9acd Binary files /dev/null and b/new/gan-proj/img/969f8518-7d6b-42ce-9030-fdc7ca31818b.png differ diff --git a/new/gan-proj/img/96bdaff0-225d-4918-8f15-8a43dd6b9d7d.png b/new/gan-proj/img/96bdaff0-225d-4918-8f15-8a43dd6b9d7d.png new file mode 100644 index 0000000000000000000000000000000000000000..d1d1a8f4a6a9e6800e81af797148849e70f71275 Binary files /dev/null and b/new/gan-proj/img/96bdaff0-225d-4918-8f15-8a43dd6b9d7d.png differ diff --git a/new/gan-proj/img/96ef5573-9de4-4bf6-a6ec-848e6f3c622c.png b/new/gan-proj/img/96ef5573-9de4-4bf6-a6ec-848e6f3c622c.png new file mode 100644 index 0000000000000000000000000000000000000000..b9112432b04e42f3e4d61f54cffa8f3d26cb1edd Binary files /dev/null and b/new/gan-proj/img/96ef5573-9de4-4bf6-a6ec-848e6f3c622c.png differ diff --git a/new/gan-proj/img/981169b9-a3b7-435a-8188-2b6f42b10d30.png b/new/gan-proj/img/981169b9-a3b7-435a-8188-2b6f42b10d30.png new file mode 100644 index 0000000000000000000000000000000000000000..c89dc89d671236920ea27763951cffcf44b5e185 Binary files /dev/null and b/new/gan-proj/img/981169b9-a3b7-435a-8188-2b6f42b10d30.png differ diff --git a/new/gan-proj/img/98bda784-ced5-4608-8c1d-38477d40cb8f.png b/new/gan-proj/img/98bda784-ced5-4608-8c1d-38477d40cb8f.png new file mode 100644 index 0000000000000000000000000000000000000000..15bdeec6eb565a1208bf58937177d9a392293a68 Binary files /dev/null and b/new/gan-proj/img/98bda784-ced5-4608-8c1d-38477d40cb8f.png differ diff --git a/new/gan-proj/img/9a5468ed-810e-429c-8a0a-af8eb1d673b0.png b/new/gan-proj/img/9a5468ed-810e-429c-8a0a-af8eb1d673b0.png new file mode 100644 index 0000000000000000000000000000000000000000..0bc0f3362920383cf624bfae8f78f6cef2e9cf81 Binary files /dev/null and b/new/gan-proj/img/9a5468ed-810e-429c-8a0a-af8eb1d673b0.png differ diff --git a/new/gan-proj/img/9bd3d80d-905f-4e80-8eb0-ee3827a69a8c.png b/new/gan-proj/img/9bd3d80d-905f-4e80-8eb0-ee3827a69a8c.png new file mode 100644 index 0000000000000000000000000000000000000000..d82e7b96273dfdd7f63d8a16079a589457ea4e0a Binary files /dev/null and b/new/gan-proj/img/9bd3d80d-905f-4e80-8eb0-ee3827a69a8c.png differ diff --git a/new/gan-proj/img/9bdd11ae-60a0-4d18-b9ba-9e9a46e9514f.png b/new/gan-proj/img/9bdd11ae-60a0-4d18-b9ba-9e9a46e9514f.png new file mode 100644 index 0000000000000000000000000000000000000000..69424659327b578275336f63f0a39de4deebc5c6 Binary files /dev/null and b/new/gan-proj/img/9bdd11ae-60a0-4d18-b9ba-9e9a46e9514f.png differ diff --git a/new/gan-proj/img/9c07dac5-3b55-444a-b7a5-6119c5d9370b.png b/new/gan-proj/img/9c07dac5-3b55-444a-b7a5-6119c5d9370b.png new file mode 100644 index 0000000000000000000000000000000000000000..080944e050860a9dd827587ed1b46fa548072096 Binary files /dev/null and b/new/gan-proj/img/9c07dac5-3b55-444a-b7a5-6119c5d9370b.png differ diff --git a/new/gan-proj/img/a04311c3-8eb2-45e2-98c0-e691deb818e0.png b/new/gan-proj/img/a04311c3-8eb2-45e2-98c0-e691deb818e0.png new file mode 100644 index 0000000000000000000000000000000000000000..4831b1cf42324b768cdb87b10a769057c49a9045 Binary files /dev/null and b/new/gan-proj/img/a04311c3-8eb2-45e2-98c0-e691deb818e0.png differ diff --git a/new/gan-proj/img/a1fce921-d434-45e3-8f15-472f02dcebc5.png b/new/gan-proj/img/a1fce921-d434-45e3-8f15-472f02dcebc5.png new file mode 100644 index 0000000000000000000000000000000000000000..08e5bda993cb3f83202fd83305809b2c56390557 Binary files /dev/null and b/new/gan-proj/img/a1fce921-d434-45e3-8f15-472f02dcebc5.png differ diff --git a/new/gan-proj/img/a2a19bc3-64c1-48bd-b2d8-4b1303293dce.png b/new/gan-proj/img/a2a19bc3-64c1-48bd-b2d8-4b1303293dce.png new file mode 100644 index 0000000000000000000000000000000000000000..de1556e25cf1a6a5dcd449a46da44d32be5a9887 Binary files /dev/null and b/new/gan-proj/img/a2a19bc3-64c1-48bd-b2d8-4b1303293dce.png differ diff --git a/new/gan-proj/img/a37c8674-a39f-42a9-9f75-b61cf9a6e50f.png b/new/gan-proj/img/a37c8674-a39f-42a9-9f75-b61cf9a6e50f.png new file mode 100644 index 0000000000000000000000000000000000000000..cd00ca5f7cc4c61d23ae932d99f2c7c31891b71b Binary files /dev/null and b/new/gan-proj/img/a37c8674-a39f-42a9-9f75-b61cf9a6e50f.png differ diff --git a/new/gan-proj/img/a45ac6e1-28df-432c-a28c-ea1a2251e320.png b/new/gan-proj/img/a45ac6e1-28df-432c-a28c-ea1a2251e320.png new file mode 100644 index 0000000000000000000000000000000000000000..d61d9d5bb23993c30a087bfd472ea66ea1dd5ce9 Binary files /dev/null and b/new/gan-proj/img/a45ac6e1-28df-432c-a28c-ea1a2251e320.png differ diff --git a/new/gan-proj/img/a72a5335-9a86-417e-a15d-4d7b491b447d.png b/new/gan-proj/img/a72a5335-9a86-417e-a15d-4d7b491b447d.png new file mode 100644 index 0000000000000000000000000000000000000000..035808ffbe9fa09bd2424e21d66c948f290a4d3b Binary files /dev/null and b/new/gan-proj/img/a72a5335-9a86-417e-a15d-4d7b491b447d.png differ diff --git a/new/gan-proj/img/a77a78e0-3ea2-4ce3-8f96-deca552c9b9f.png b/new/gan-proj/img/a77a78e0-3ea2-4ce3-8f96-deca552c9b9f.png new file mode 100644 index 0000000000000000000000000000000000000000..95d448b01e85500769733f9b4082c83d329b4a4f Binary files /dev/null and b/new/gan-proj/img/a77a78e0-3ea2-4ce3-8f96-deca552c9b9f.png differ diff --git a/new/gan-proj/img/a86a4cb0-47d0-4123-8c40-207579ac79c4.png b/new/gan-proj/img/a86a4cb0-47d0-4123-8c40-207579ac79c4.png new file mode 100644 index 0000000000000000000000000000000000000000..4414dbda02b391fb816c70ca40ed8453ab864565 Binary files /dev/null and b/new/gan-proj/img/a86a4cb0-47d0-4123-8c40-207579ac79c4.png differ diff --git a/new/gan-proj/img/a8a70426-5d0b-4804-b69c-6bde1c0b0651.png b/new/gan-proj/img/a8a70426-5d0b-4804-b69c-6bde1c0b0651.png new file mode 100644 index 0000000000000000000000000000000000000000..1a20e96fde094ee5047815e3fef13c87fe1192fb Binary files /dev/null and b/new/gan-proj/img/a8a70426-5d0b-4804-b69c-6bde1c0b0651.png differ diff --git a/new/gan-proj/img/a8c04bff-bd5a-4350-8b08-bd2ef6ae193b.png b/new/gan-proj/img/a8c04bff-bd5a-4350-8b08-bd2ef6ae193b.png new file mode 100644 index 0000000000000000000000000000000000000000..385a32c9af378390e20f00717b0f997ffc782fd8 Binary files /dev/null and b/new/gan-proj/img/a8c04bff-bd5a-4350-8b08-bd2ef6ae193b.png differ diff --git a/new/gan-proj/img/aacb3462-7c27-4168-a445-52c2b2553c59.png b/new/gan-proj/img/aacb3462-7c27-4168-a445-52c2b2553c59.png new file mode 100644 index 0000000000000000000000000000000000000000..a8ae0e505f5e4c38c7c5444b7785d144bd26db84 Binary files /dev/null and b/new/gan-proj/img/aacb3462-7c27-4168-a445-52c2b2553c59.png differ diff --git a/new/gan-proj/img/adfe13fc-6e5c-4ffb-8230-ba4117391c77.png b/new/gan-proj/img/adfe13fc-6e5c-4ffb-8230-ba4117391c77.png new file mode 100644 index 0000000000000000000000000000000000000000..e3667936fd8de2740fc130212e3747d2d50e050a Binary files /dev/null and b/new/gan-proj/img/adfe13fc-6e5c-4ffb-8230-ba4117391c77.png differ diff --git a/new/gan-proj/img/aea66002-edd3-4c8f-bea7-91ebc8e0aaf4.png b/new/gan-proj/img/aea66002-edd3-4c8f-bea7-91ebc8e0aaf4.png new file mode 100644 index 0000000000000000000000000000000000000000..4f6f25d9d50af2a7aa1188fc15be7d2589ccc946 Binary files /dev/null and b/new/gan-proj/img/aea66002-edd3-4c8f-bea7-91ebc8e0aaf4.png differ diff --git a/new/gan-proj/img/af06eff5-c037-40a2-ac33-df687b277046.png b/new/gan-proj/img/af06eff5-c037-40a2-ac33-df687b277046.png new file mode 100644 index 0000000000000000000000000000000000000000..227f1261c903f9e547cd6b26c4750a6cafe3c0f8 Binary files /dev/null and b/new/gan-proj/img/af06eff5-c037-40a2-ac33-df687b277046.png differ diff --git a/new/gan-proj/img/b0e30420-c79d-434a-b6d9-6fcaaa2a61ad.png b/new/gan-proj/img/b0e30420-c79d-434a-b6d9-6fcaaa2a61ad.png new file mode 100644 index 0000000000000000000000000000000000000000..019f5133e2a3e9115085628360472277442ea70d Binary files /dev/null and b/new/gan-proj/img/b0e30420-c79d-434a-b6d9-6fcaaa2a61ad.png differ diff --git a/new/gan-proj/img/b1d25f9f-289b-40c8-a602-acf98b8637fe.png b/new/gan-proj/img/b1d25f9f-289b-40c8-a602-acf98b8637fe.png new file mode 100644 index 0000000000000000000000000000000000000000..d7c78dc18036514e1618f957ab4dd7af1acf9235 Binary files /dev/null and b/new/gan-proj/img/b1d25f9f-289b-40c8-a602-acf98b8637fe.png differ diff --git a/new/gan-proj/img/b202e2a1-36da-4851-8a99-1cf84585df1d.png b/new/gan-proj/img/b202e2a1-36da-4851-8a99-1cf84585df1d.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad178efc5749e90334234714c1b7be34bace35d Binary files /dev/null and b/new/gan-proj/img/b202e2a1-36da-4851-8a99-1cf84585df1d.png differ diff --git a/new/gan-proj/img/b2181e11-3658-4c93-8a03-365c47c6aae6.png b/new/gan-proj/img/b2181e11-3658-4c93-8a03-365c47c6aae6.png new file mode 100644 index 0000000000000000000000000000000000000000..f7b95defbc0d639e1e6281f254f53bbc4550e6db Binary files /dev/null and b/new/gan-proj/img/b2181e11-3658-4c93-8a03-365c47c6aae6.png differ diff --git a/new/gan-proj/img/b2a6e013-9ffb-4a51-93fb-91454976c9c4.png b/new/gan-proj/img/b2a6e013-9ffb-4a51-93fb-91454976c9c4.png new file mode 100644 index 0000000000000000000000000000000000000000..0773959297ef4be1ebc30f0b14b498406a94e44a Binary files /dev/null and b/new/gan-proj/img/b2a6e013-9ffb-4a51-93fb-91454976c9c4.png differ diff --git a/new/gan-proj/img/b2df0366-0c95-4e61-a490-7efa0da76e73.png b/new/gan-proj/img/b2df0366-0c95-4e61-a490-7efa0da76e73.png new file mode 100644 index 0000000000000000000000000000000000000000..1b933b4d08b26b0ed228c1410ae2e424aec92032 Binary files /dev/null and b/new/gan-proj/img/b2df0366-0c95-4e61-a490-7efa0da76e73.png differ diff --git a/new/gan-proj/img/b3edb215-2b94-4a1a-92f6-c97f1c779948.png b/new/gan-proj/img/b3edb215-2b94-4a1a-92f6-c97f1c779948.png new file mode 100644 index 0000000000000000000000000000000000000000..b5a1b9ec416e849f2ccbc6823dfab13554bbf382 Binary files /dev/null and b/new/gan-proj/img/b3edb215-2b94-4a1a-92f6-c97f1c779948.png differ diff --git a/new/gan-proj/img/b46d75ce-88c8-480a-ba3d-572b93f4c717.png b/new/gan-proj/img/b46d75ce-88c8-480a-ba3d-572b93f4c717.png new file mode 100644 index 0000000000000000000000000000000000000000..949d24ce1e26f778e899fe815d95ec706bd6daea Binary files /dev/null and b/new/gan-proj/img/b46d75ce-88c8-480a-ba3d-572b93f4c717.png differ diff --git a/new/gan-proj/img/b67a8b95-a1fe-4c06-be44-cec497dd35bf.png b/new/gan-proj/img/b67a8b95-a1fe-4c06-be44-cec497dd35bf.png new file mode 100644 index 0000000000000000000000000000000000000000..edfe9e82bf2ab61a66a156d71879d48579711b44 Binary files /dev/null and b/new/gan-proj/img/b67a8b95-a1fe-4c06-be44-cec497dd35bf.png differ diff --git a/new/gan-proj/img/b6e02c98-294c-42d4-b687-3c2c7e84644a.png b/new/gan-proj/img/b6e02c98-294c-42d4-b687-3c2c7e84644a.png new file mode 100644 index 0000000000000000000000000000000000000000..aa8920cff5e4d89a044e44169637584014ca22f0 Binary files /dev/null and b/new/gan-proj/img/b6e02c98-294c-42d4-b687-3c2c7e84644a.png differ diff --git a/new/gan-proj/img/b7167483-2ee2-469e-af9f-278f36699c79.png b/new/gan-proj/img/b7167483-2ee2-469e-af9f-278f36699c79.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad178efc5749e90334234714c1b7be34bace35d Binary files /dev/null and b/new/gan-proj/img/b7167483-2ee2-469e-af9f-278f36699c79.png differ diff --git a/new/gan-proj/img/b72c39e8-2d9b-487b-83df-3b73ab2c550f.png b/new/gan-proj/img/b72c39e8-2d9b-487b-83df-3b73ab2c550f.png new file mode 100644 index 0000000000000000000000000000000000000000..bcc2f159f5d8f5f24157cf598fe0f334cd6c7e6d Binary files /dev/null and b/new/gan-proj/img/b72c39e8-2d9b-487b-83df-3b73ab2c550f.png differ diff --git a/new/gan-proj/img/b951f896-fdf7-482f-81bd-8cf8cebd88d7.png b/new/gan-proj/img/b951f896-fdf7-482f-81bd-8cf8cebd88d7.png new file mode 100644 index 0000000000000000000000000000000000000000..878fdca7fd3acc5779da9e13653674feaad623ba Binary files /dev/null and b/new/gan-proj/img/b951f896-fdf7-482f-81bd-8cf8cebd88d7.png differ diff --git a/new/gan-proj/img/ba22d993-cf32-42a1-86b9-94c2b37311e8.png b/new/gan-proj/img/ba22d993-cf32-42a1-86b9-94c2b37311e8.png new file mode 100644 index 0000000000000000000000000000000000000000..3a5305a65d7acf25340f28e1e00a6f83b1d5557b Binary files /dev/null and b/new/gan-proj/img/ba22d993-cf32-42a1-86b9-94c2b37311e8.png differ diff --git a/new/gan-proj/img/ba528a08-5d57-47cf-8a7e-eb0974b86cdc.png b/new/gan-proj/img/ba528a08-5d57-47cf-8a7e-eb0974b86cdc.png new file mode 100644 index 0000000000000000000000000000000000000000..a37750443fcf57eba743c1027b77749b2937f6c6 Binary files /dev/null and b/new/gan-proj/img/ba528a08-5d57-47cf-8a7e-eb0974b86cdc.png differ diff --git a/new/gan-proj/img/bad2e9c8-167e-436b-9078-4fa53cffc0a2.png b/new/gan-proj/img/bad2e9c8-167e-436b-9078-4fa53cffc0a2.png new file mode 100644 index 0000000000000000000000000000000000000000..fefa46c193f61876b4f542e1920b4456144bde91 Binary files /dev/null and b/new/gan-proj/img/bad2e9c8-167e-436b-9078-4fa53cffc0a2.png differ diff --git a/new/gan-proj/img/bae8d146-d70d-479e-bc91-801a8140d502.png b/new/gan-proj/img/bae8d146-d70d-479e-bc91-801a8140d502.png new file mode 100644 index 0000000000000000000000000000000000000000..7439a37c92f958dd9215801d38c1f331dd1af38d Binary files /dev/null and b/new/gan-proj/img/bae8d146-d70d-479e-bc91-801a8140d502.png differ diff --git a/new/gan-proj/img/bb5729b4-1a7b-463f-a097-47e123ef56ab.png b/new/gan-proj/img/bb5729b4-1a7b-463f-a097-47e123ef56ab.png new file mode 100644 index 0000000000000000000000000000000000000000..5fcfa3a7ca33e0206102c82ae9a3f253724515d8 Binary files /dev/null and b/new/gan-proj/img/bb5729b4-1a7b-463f-a097-47e123ef56ab.png differ diff --git a/new/gan-proj/img/bcc5921a-1e7e-497a-b134-61a55d04cecb.png b/new/gan-proj/img/bcc5921a-1e7e-497a-b134-61a55d04cecb.png new file mode 100644 index 0000000000000000000000000000000000000000..5144828e7555072a08371a092ea52509126455e4 Binary files /dev/null and b/new/gan-proj/img/bcc5921a-1e7e-497a-b134-61a55d04cecb.png differ diff --git a/new/gan-proj/img/bd2a23f1-e11e-4c02-81ef-cbd6783ffc5d.png b/new/gan-proj/img/bd2a23f1-e11e-4c02-81ef-cbd6783ffc5d.png new file mode 100644 index 0000000000000000000000000000000000000000..be6b27915b6a7d0d8af1a7c5c213c79c5f1e5420 Binary files /dev/null and b/new/gan-proj/img/bd2a23f1-e11e-4c02-81ef-cbd6783ffc5d.png differ diff --git a/new/gan-proj/img/bd66dab8-77b6-42b8-bda8-3dd03f9285e8.png b/new/gan-proj/img/bd66dab8-77b6-42b8-bda8-3dd03f9285e8.png new file mode 100644 index 0000000000000000000000000000000000000000..bee98eb7b314651bf5043596b6ff1d748db8858d Binary files /dev/null and b/new/gan-proj/img/bd66dab8-77b6-42b8-bda8-3dd03f9285e8.png differ diff --git a/new/gan-proj/img/beb77a0a-2316-4a3e-9bf9-dead211714b4.png b/new/gan-proj/img/beb77a0a-2316-4a3e-9bf9-dead211714b4.png new file mode 100644 index 0000000000000000000000000000000000000000..80b7c16ac4ed82c8597aa3f0c4112f799d8aea56 Binary files /dev/null and b/new/gan-proj/img/beb77a0a-2316-4a3e-9bf9-dead211714b4.png differ diff --git a/new/gan-proj/img/bf736fcf-a789-4a17-b1a9-a452b23e07f9.png b/new/gan-proj/img/bf736fcf-a789-4a17-b1a9-a452b23e07f9.png new file mode 100644 index 0000000000000000000000000000000000000000..24a008b5615135d8a646f7c2605d8f794aa1b43e Binary files /dev/null and b/new/gan-proj/img/bf736fcf-a789-4a17-b1a9-a452b23e07f9.png differ diff --git a/new/gan-proj/img/c146fa61-f825-4089-840c-bb756be2579a.png b/new/gan-proj/img/c146fa61-f825-4089-840c-bb756be2579a.png new file mode 100644 index 0000000000000000000000000000000000000000..9250b898aa8b639b117b0792e51442ba48d3970d Binary files /dev/null and b/new/gan-proj/img/c146fa61-f825-4089-840c-bb756be2579a.png differ diff --git a/new/gan-proj/img/c1c39187-218d-46df-83a2-ab03483ff422.png b/new/gan-proj/img/c1c39187-218d-46df-83a2-ab03483ff422.png new file mode 100644 index 0000000000000000000000000000000000000000..87397ddc59eaaa8472a04bb9ed4205ea33e822f9 Binary files /dev/null and b/new/gan-proj/img/c1c39187-218d-46df-83a2-ab03483ff422.png differ diff --git a/new/gan-proj/img/c20b6dec-de99-467f-8b00-3a836163e900.png b/new/gan-proj/img/c20b6dec-de99-467f-8b00-3a836163e900.png new file mode 100644 index 0000000000000000000000000000000000000000..cd00ca5f7cc4c61d23ae932d99f2c7c31891b71b Binary files /dev/null and b/new/gan-proj/img/c20b6dec-de99-467f-8b00-3a836163e900.png differ diff --git a/new/gan-proj/img/c236f76b-da88-4ac1-9c4a-50cc4c3acb59.png b/new/gan-proj/img/c236f76b-da88-4ac1-9c4a-50cc4c3acb59.png new file mode 100644 index 0000000000000000000000000000000000000000..745223f8f943ae2646ba25da24508ee3ce6630c5 Binary files /dev/null and b/new/gan-proj/img/c236f76b-da88-4ac1-9c4a-50cc4c3acb59.png differ diff --git a/new/gan-proj/img/c23de9d4-5560-4c9f-a0bd-a3569bbf6f5a.png b/new/gan-proj/img/c23de9d4-5560-4c9f-a0bd-a3569bbf6f5a.png new file mode 100644 index 0000000000000000000000000000000000000000..57de05747d8b6cf9db0f23a5b301699be894fe77 Binary files /dev/null and b/new/gan-proj/img/c23de9d4-5560-4c9f-a0bd-a3569bbf6f5a.png differ diff --git a/new/gan-proj/img/c38be29e-d867-4b09-845b-a83600eebf1c.png b/new/gan-proj/img/c38be29e-d867-4b09-845b-a83600eebf1c.png new file mode 100644 index 0000000000000000000000000000000000000000..81c450a21eaa80d4ba3eefb49449247b459eb077 Binary files /dev/null and b/new/gan-proj/img/c38be29e-d867-4b09-845b-a83600eebf1c.png differ diff --git a/new/gan-proj/img/c420501d-952a-4e5b-84b0-0aa891624a10.png b/new/gan-proj/img/c420501d-952a-4e5b-84b0-0aa891624a10.png new file mode 100644 index 0000000000000000000000000000000000000000..87269f108b040a6a55919d071255677ff778680d Binary files /dev/null and b/new/gan-proj/img/c420501d-952a-4e5b-84b0-0aa891624a10.png differ diff --git a/new/gan-proj/img/c4ba3b4c-3d2b-4156-8e94-aea9a2e8c781.png b/new/gan-proj/img/c4ba3b4c-3d2b-4156-8e94-aea9a2e8c781.png new file mode 100644 index 0000000000000000000000000000000000000000..75d6cec9fa9c5a49355b040d77a0bfee5d53c465 Binary files /dev/null and b/new/gan-proj/img/c4ba3b4c-3d2b-4156-8e94-aea9a2e8c781.png differ diff --git a/new/gan-proj/img/c534d4df-c504-4ff3-83b6-61db0e52035b.png b/new/gan-proj/img/c534d4df-c504-4ff3-83b6-61db0e52035b.png new file mode 100644 index 0000000000000000000000000000000000000000..598f32e462af63144337058deff1be17b271a0f8 Binary files /dev/null and b/new/gan-proj/img/c534d4df-c504-4ff3-83b6-61db0e52035b.png differ diff --git a/new/gan-proj/img/c5dea8f8-7091-4c69-bfec-3987093fbaaa.png b/new/gan-proj/img/c5dea8f8-7091-4c69-bfec-3987093fbaaa.png new file mode 100644 index 0000000000000000000000000000000000000000..491626aa2657166a368fcea0c41f2a72100eb85a Binary files /dev/null and b/new/gan-proj/img/c5dea8f8-7091-4c69-bfec-3987093fbaaa.png differ diff --git a/new/gan-proj/img/c682e8fd-bd22-4998-968f-227c26898f62.png b/new/gan-proj/img/c682e8fd-bd22-4998-968f-227c26898f62.png new file mode 100644 index 0000000000000000000000000000000000000000..c7b21682667f05d4cc29bf2294d6f15315997a18 Binary files /dev/null and b/new/gan-proj/img/c682e8fd-bd22-4998-968f-227c26898f62.png differ diff --git a/new/gan-proj/img/c6b693e8-ae58-4258-bacf-0f7e31655099.png b/new/gan-proj/img/c6b693e8-ae58-4258-bacf-0f7e31655099.png new file mode 100644 index 0000000000000000000000000000000000000000..03670e1c03ac894af7e645f1042b69bda7c9a2a2 Binary files /dev/null and b/new/gan-proj/img/c6b693e8-ae58-4258-bacf-0f7e31655099.png differ diff --git a/new/gan-proj/img/c854836e-df68-4886-bb7c-8577a41d732c.png b/new/gan-proj/img/c854836e-df68-4886-bb7c-8577a41d732c.png new file mode 100644 index 0000000000000000000000000000000000000000..27b3b0c194c4bbdaebf34090bb17c6c14a31bdbd Binary files /dev/null and b/new/gan-proj/img/c854836e-df68-4886-bb7c-8577a41d732c.png differ diff --git a/new/gan-proj/img/c9a26056-2b9d-44f1-bc1b-4fbb29cfd2af.png b/new/gan-proj/img/c9a26056-2b9d-44f1-bc1b-4fbb29cfd2af.png new file mode 100644 index 0000000000000000000000000000000000000000..014905b2acf72d0fb284bf28d8346b66ec1bd5fe Binary files /dev/null and b/new/gan-proj/img/c9a26056-2b9d-44f1-bc1b-4fbb29cfd2af.png differ diff --git a/new/gan-proj/img/c9de7008-1df0-4381-92f5-fcfc5aa276a3.png b/new/gan-proj/img/c9de7008-1df0-4381-92f5-fcfc5aa276a3.png new file mode 100644 index 0000000000000000000000000000000000000000..258803c03358e6103ef1105b6f69a10328924fff Binary files /dev/null and b/new/gan-proj/img/c9de7008-1df0-4381-92f5-fcfc5aa276a3.png differ diff --git a/new/gan-proj/img/cb774d54-76ee-4c94-89a7-94e8b16c4493.png b/new/gan-proj/img/cb774d54-76ee-4c94-89a7-94e8b16c4493.png new file mode 100644 index 0000000000000000000000000000000000000000..035808ffbe9fa09bd2424e21d66c948f290a4d3b Binary files /dev/null and b/new/gan-proj/img/cb774d54-76ee-4c94-89a7-94e8b16c4493.png differ diff --git a/new/gan-proj/img/cd2083c4-1490-4eef-a931-c924f6366e86.png b/new/gan-proj/img/cd2083c4-1490-4eef-a931-c924f6366e86.png new file mode 100644 index 0000000000000000000000000000000000000000..2f0ee275e41035dddf6fb21ec620b3c2bfbd80e8 Binary files /dev/null and b/new/gan-proj/img/cd2083c4-1490-4eef-a931-c924f6366e86.png differ diff --git a/new/gan-proj/img/ce0afe99-4137-4673-985a-b073ab66c347.png b/new/gan-proj/img/ce0afe99-4137-4673-985a-b073ab66c347.png new file mode 100644 index 0000000000000000000000000000000000000000..bc8020878a614a913a067d7714187650703ceb94 Binary files /dev/null and b/new/gan-proj/img/ce0afe99-4137-4673-985a-b073ab66c347.png differ diff --git a/new/gan-proj/img/cf90c58f-3995-48a8-b78a-0ab81cb7ae9d.png b/new/gan-proj/img/cf90c58f-3995-48a8-b78a-0ab81cb7ae9d.png new file mode 100644 index 0000000000000000000000000000000000000000..763d54f6f1be3ddcb39e0cfaf932b4bdb5295f94 Binary files /dev/null and b/new/gan-proj/img/cf90c58f-3995-48a8-b78a-0ab81cb7ae9d.png differ diff --git a/new/gan-proj/img/d0ecd579-7d64-4b7c-a9c7-666c14c0e1cc.png b/new/gan-proj/img/d0ecd579-7d64-4b7c-a9c7-666c14c0e1cc.png new file mode 100644 index 0000000000000000000000000000000000000000..949d24ce1e26f778e899fe815d95ec706bd6daea Binary files /dev/null and b/new/gan-proj/img/d0ecd579-7d64-4b7c-a9c7-666c14c0e1cc.png differ diff --git a/new/gan-proj/img/d1541159-75a5-487e-805f-80a8c97436bd.png b/new/gan-proj/img/d1541159-75a5-487e-805f-80a8c97436bd.png new file mode 100644 index 0000000000000000000000000000000000000000..9a74ee0c38ea06e74a044b8ccbbc322a8b436989 Binary files /dev/null and b/new/gan-proj/img/d1541159-75a5-487e-805f-80a8c97436bd.png differ diff --git a/new/gan-proj/img/d399507e-b812-41a5-8731-9b7e2ebf48aa.png b/new/gan-proj/img/d399507e-b812-41a5-8731-9b7e2ebf48aa.png new file mode 100644 index 0000000000000000000000000000000000000000..ea9212edb0ce8bbfe822e78cbb00da77f7f5c7ad Binary files /dev/null and b/new/gan-proj/img/d399507e-b812-41a5-8731-9b7e2ebf48aa.png differ diff --git a/new/gan-proj/img/d58519f5-35a1-4b3b-831f-163b995eeabd.png b/new/gan-proj/img/d58519f5-35a1-4b3b-831f-163b995eeabd.png new file mode 100644 index 0000000000000000000000000000000000000000..7136b17a715de4d54355601417abdbed5adc5784 Binary files /dev/null and b/new/gan-proj/img/d58519f5-35a1-4b3b-831f-163b995eeabd.png differ diff --git a/new/gan-proj/img/d857d518-1a01-4b38-a794-c85183029413.png b/new/gan-proj/img/d857d518-1a01-4b38-a794-c85183029413.png new file mode 100644 index 0000000000000000000000000000000000000000..8a719c6f82cb1d979701c9ac712da614eb17e9bf Binary files /dev/null and b/new/gan-proj/img/d857d518-1a01-4b38-a794-c85183029413.png differ diff --git a/new/gan-proj/img/d86d468c-b92c-41c9-83f1-75baf727847c.png b/new/gan-proj/img/d86d468c-b92c-41c9-83f1-75baf727847c.png new file mode 100644 index 0000000000000000000000000000000000000000..014905b2acf72d0fb284bf28d8346b66ec1bd5fe Binary files /dev/null and b/new/gan-proj/img/d86d468c-b92c-41c9-83f1-75baf727847c.png differ diff --git a/new/gan-proj/img/dd18e210-40f0-4f5e-b898-37d023af1add.png b/new/gan-proj/img/dd18e210-40f0-4f5e-b898-37d023af1add.png new file mode 100644 index 0000000000000000000000000000000000000000..bf4d8b1b1968900f1c3ab6021bdf86e0978f7080 Binary files /dev/null and b/new/gan-proj/img/dd18e210-40f0-4f5e-b898-37d023af1add.png differ diff --git a/new/gan-proj/img/de2f63f2-3d6f-4cb6-90d0-9a9789508d31.png b/new/gan-proj/img/de2f63f2-3d6f-4cb6-90d0-9a9789508d31.png new file mode 100644 index 0000000000000000000000000000000000000000..96f0391c01be8f5b3172a4e2f29b1c2cf62317f7 Binary files /dev/null and b/new/gan-proj/img/de2f63f2-3d6f-4cb6-90d0-9a9789508d31.png differ diff --git a/new/gan-proj/img/de8c23e2-ee05-44ea-876e-adfe515c5b5b.png b/new/gan-proj/img/de8c23e2-ee05-44ea-876e-adfe515c5b5b.png new file mode 100644 index 0000000000000000000000000000000000000000..bee98eb7b314651bf5043596b6ff1d748db8858d Binary files /dev/null and b/new/gan-proj/img/de8c23e2-ee05-44ea-876e-adfe515c5b5b.png differ diff --git a/new/gan-proj/img/e08d1823-e871-40dc-b01f-d924c155a813.png b/new/gan-proj/img/e08d1823-e871-40dc-b01f-d924c155a813.png new file mode 100644 index 0000000000000000000000000000000000000000..1b9962d48ec7e0aecd8ebccd8fde73010d40ae69 Binary files /dev/null and b/new/gan-proj/img/e08d1823-e871-40dc-b01f-d924c155a813.png differ diff --git a/new/gan-proj/img/e0cef545-08a1-4089-a238-9b6945c1d821.png b/new/gan-proj/img/e0cef545-08a1-4089-a238-9b6945c1d821.png new file mode 100644 index 0000000000000000000000000000000000000000..dd7583849d98ac7f7d17d6ea6fbae72aa7fc40ae Binary files /dev/null and b/new/gan-proj/img/e0cef545-08a1-4089-a238-9b6945c1d821.png differ diff --git a/new/gan-proj/img/e196749b-1617-4fcb-bbc5-14c392c944f1.png b/new/gan-proj/img/e196749b-1617-4fcb-bbc5-14c392c944f1.png new file mode 100644 index 0000000000000000000000000000000000000000..8a11d780386829495ab3724f08672ce8b5e87f5e Binary files /dev/null and b/new/gan-proj/img/e196749b-1617-4fcb-bbc5-14c392c944f1.png differ diff --git a/new/gan-proj/img/e32e6af2-66f3-4230-a504-a531da126b72.png b/new/gan-proj/img/e32e6af2-66f3-4230-a504-a531da126b72.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad178efc5749e90334234714c1b7be34bace35d Binary files /dev/null and b/new/gan-proj/img/e32e6af2-66f3-4230-a504-a531da126b72.png differ diff --git a/new/gan-proj/img/e57cd8de-afc5-4e00-a0d4-b87786d7318f.png b/new/gan-proj/img/e57cd8de-afc5-4e00-a0d4-b87786d7318f.png new file mode 100644 index 0000000000000000000000000000000000000000..2bee10706d33e7379b2aae9338896dbe8af26549 Binary files /dev/null and b/new/gan-proj/img/e57cd8de-afc5-4e00-a0d4-b87786d7318f.png differ diff --git a/new/gan-proj/img/e5f5e27a-31d3-4fcc-a69c-3fd555c755b4.png b/new/gan-proj/img/e5f5e27a-31d3-4fcc-a69c-3fd555c755b4.png new file mode 100644 index 0000000000000000000000000000000000000000..05286cb6b8443b1d134e3b96d3e3d3a966e6a96d Binary files /dev/null and b/new/gan-proj/img/e5f5e27a-31d3-4fcc-a69c-3fd555c755b4.png differ diff --git a/new/gan-proj/img/e705b732-209a-407e-bbb4-5ddae7424011.png b/new/gan-proj/img/e705b732-209a-407e-bbb4-5ddae7424011.png new file mode 100644 index 0000000000000000000000000000000000000000..fe4734a8773a7cc85f7b5c16f88a7412df3eb365 Binary files /dev/null and b/new/gan-proj/img/e705b732-209a-407e-bbb4-5ddae7424011.png differ diff --git a/new/gan-proj/img/e726a3d7-269a-440f-97f9-d911339a21ba.png b/new/gan-proj/img/e726a3d7-269a-440f-97f9-d911339a21ba.png new file mode 100644 index 0000000000000000000000000000000000000000..9ea0138a105e0773565bb7b14df456050a2bd2da Binary files /dev/null and b/new/gan-proj/img/e726a3d7-269a-440f-97f9-d911339a21ba.png differ diff --git a/new/gan-proj/img/e7b17ae1-836c-4be6-a33c-f544a63d4952.png b/new/gan-proj/img/e7b17ae1-836c-4be6-a33c-f544a63d4952.png new file mode 100644 index 0000000000000000000000000000000000000000..bdd5de9c0818fc981aca8b7ea770447d41f9387e Binary files /dev/null and b/new/gan-proj/img/e7b17ae1-836c-4be6-a33c-f544a63d4952.png differ diff --git a/new/gan-proj/img/e81d7c4b-bd30-4887-9e84-303e9cea0a89.png b/new/gan-proj/img/e81d7c4b-bd30-4887-9e84-303e9cea0a89.png new file mode 100644 index 0000000000000000000000000000000000000000..783ccad8f9594be7d9454fcbdf820be466454f15 Binary files /dev/null and b/new/gan-proj/img/e81d7c4b-bd30-4887-9e84-303e9cea0a89.png differ diff --git a/new/gan-proj/img/e8c1a187-a93a-40e6-92a9-3c823a391fd9.png b/new/gan-proj/img/e8c1a187-a93a-40e6-92a9-3c823a391fd9.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed989ccd47dcfbb2c4ca2a0d637bd79359c4760 Binary files /dev/null and b/new/gan-proj/img/e8c1a187-a93a-40e6-92a9-3c823a391fd9.png differ diff --git a/new/gan-proj/img/f099cb7a-a2b7-476b-9476-f7e47eb947b4.png b/new/gan-proj/img/f099cb7a-a2b7-476b-9476-f7e47eb947b4.png new file mode 100644 index 0000000000000000000000000000000000000000..b2626cc618c53a723fd8399ab5480339b9a210ec Binary files /dev/null and b/new/gan-proj/img/f099cb7a-a2b7-476b-9476-f7e47eb947b4.png differ diff --git a/new/gan-proj/img/f0da2f12-07bd-477a-97a6-dc21589a0b1f.png b/new/gan-proj/img/f0da2f12-07bd-477a-97a6-dc21589a0b1f.png new file mode 100644 index 0000000000000000000000000000000000000000..39895e12be59fcbe4a49ec16422fef2420eb92c9 Binary files /dev/null and b/new/gan-proj/img/f0da2f12-07bd-477a-97a6-dc21589a0b1f.png differ diff --git a/new/gan-proj/img/f17e1ecf-417e-4b78-ad5c-6e5a25ae8e26.png b/new/gan-proj/img/f17e1ecf-417e-4b78-ad5c-6e5a25ae8e26.png new file mode 100644 index 0000000000000000000000000000000000000000..8ff1e5f49a46aa87165eaee9b6340093fd4fbb18 Binary files /dev/null and b/new/gan-proj/img/f17e1ecf-417e-4b78-ad5c-6e5a25ae8e26.png differ diff --git a/new/gan-proj/img/f278fd84-eb49-4d88-a98e-31396fe4ea3a.png b/new/gan-proj/img/f278fd84-eb49-4d88-a98e-31396fe4ea3a.png new file mode 100644 index 0000000000000000000000000000000000000000..0d46c42bf44d8795496c91a96c3e4d82ada76f78 Binary files /dev/null and b/new/gan-proj/img/f278fd84-eb49-4d88-a98e-31396fe4ea3a.png differ diff --git a/new/gan-proj/img/f2b9329d-27bc-421d-a8be-8852bea54071.png b/new/gan-proj/img/f2b9329d-27bc-421d-a8be-8852bea54071.png new file mode 100644 index 0000000000000000000000000000000000000000..5125ad72b8320f2dd691c374830742ffdb152c51 Binary files /dev/null and b/new/gan-proj/img/f2b9329d-27bc-421d-a8be-8852bea54071.png differ diff --git a/new/gan-proj/img/f5267071-9d06-4928-bc77-cfc450d6e3a6.png b/new/gan-proj/img/f5267071-9d06-4928-bc77-cfc450d6e3a6.png new file mode 100644 index 0000000000000000000000000000000000000000..d4b8c7ebc01226c257521ed1c75275ce500bda6d Binary files /dev/null and b/new/gan-proj/img/f5267071-9d06-4928-bc77-cfc450d6e3a6.png differ diff --git a/new/gan-proj/img/f542b337-7bc3-475e-b3ee-00a963352321.png b/new/gan-proj/img/f542b337-7bc3-475e-b3ee-00a963352321.png new file mode 100644 index 0000000000000000000000000000000000000000..a4570c21490c79ee9a192fbf1620df25323654af Binary files /dev/null and b/new/gan-proj/img/f542b337-7bc3-475e-b3ee-00a963352321.png differ diff --git a/new/gan-proj/img/f59e102b-292b-4043-bf0f-fb2aedc95ba9.png b/new/gan-proj/img/f59e102b-292b-4043-bf0f-fb2aedc95ba9.png new file mode 100644 index 0000000000000000000000000000000000000000..cd00ca5f7cc4c61d23ae932d99f2c7c31891b71b Binary files /dev/null and b/new/gan-proj/img/f59e102b-292b-4043-bf0f-fb2aedc95ba9.png differ diff --git a/new/gan-proj/img/f60e4201-0e24-4dab-b19c-c69d11994427.png b/new/gan-proj/img/f60e4201-0e24-4dab-b19c-c69d11994427.png new file mode 100644 index 0000000000000000000000000000000000000000..28dc6b399f80e8e35da82cee845422e7bca1da71 Binary files /dev/null and b/new/gan-proj/img/f60e4201-0e24-4dab-b19c-c69d11994427.png differ diff --git a/new/gan-proj/img/f87a45a6-3e9e-4ea6-988f-7117b3a91088.png b/new/gan-proj/img/f87a45a6-3e9e-4ea6-988f-7117b3a91088.png new file mode 100644 index 0000000000000000000000000000000000000000..d7c78dc18036514e1618f957ab4dd7af1acf9235 Binary files /dev/null and b/new/gan-proj/img/f87a45a6-3e9e-4ea6-988f-7117b3a91088.png differ diff --git a/new/gan-proj/img/fc0175ae-38b7-42aa-99ec-1bdbe36a8edf.png b/new/gan-proj/img/fc0175ae-38b7-42aa-99ec-1bdbe36a8edf.png new file mode 100644 index 0000000000000000000000000000000000000000..dbd29408d6e8cd79664ac90cf6b747ab90da6d0f Binary files /dev/null and b/new/gan-proj/img/fc0175ae-38b7-42aa-99ec-1bdbe36a8edf.png differ diff --git a/new/gan-proj/img/fcd5c713-f0e8-4b2b-b914-06b9cfdb3600.png b/new/gan-proj/img/fcd5c713-f0e8-4b2b-b914-06b9cfdb3600.png new file mode 100644 index 0000000000000000000000000000000000000000..33e46b5b7b14291e32effc08c82f7fe12bec4a30 Binary files /dev/null and b/new/gan-proj/img/fcd5c713-f0e8-4b2b-b914-06b9cfdb3600.png differ diff --git a/new/gan-proj/img/fd0ed18a-8cd7-4200-a3e9-81c6f771302a.png b/new/gan-proj/img/fd0ed18a-8cd7-4200-a3e9-81c6f771302a.png new file mode 100644 index 0000000000000000000000000000000000000000..c8e99122441c6624b91f703d757e040c6779e20e Binary files /dev/null and b/new/gan-proj/img/fd0ed18a-8cd7-4200-a3e9-81c6f771302a.png differ diff --git a/new/gan-proj/img/fd209aa6-8f96-45df-a754-7ae8cb364fa1.png b/new/gan-proj/img/fd209aa6-8f96-45df-a754-7ae8cb364fa1.png new file mode 100644 index 0000000000000000000000000000000000000000..67cd217eb696eaf892407116aa0d051658d4f2c8 Binary files /dev/null and b/new/gan-proj/img/fd209aa6-8f96-45df-a754-7ae8cb364fa1.png differ diff --git a/new/gan-proj/img/fe3eced0-c452-4b7a-9f6d-dd8228048ab9.png b/new/gan-proj/img/fe3eced0-c452-4b7a-9f6d-dd8228048ab9.png new file mode 100644 index 0000000000000000000000000000000000000000..3fc7403b730c2d78cce9df7ea42f4519c23e4ed6 Binary files /dev/null and b/new/gan-proj/img/fe3eced0-c452-4b7a-9f6d-dd8228048ab9.png differ diff --git a/new/gan-proj/img/fe7024ae-2f80-45fd-9592-45dfc1b32475.png b/new/gan-proj/img/fe7024ae-2f80-45fd-9592-45dfc1b32475.png new file mode 100644 index 0000000000000000000000000000000000000000..ee34798243670b3bac6fb3ba3625412ea8c3a854 Binary files /dev/null and b/new/gan-proj/img/fe7024ae-2f80-45fd-9592-45dfc1b32475.png differ diff --git a/new/gan-proj/img/ff705572-5fb8-4216-883c-63bbfb809286.png b/new/gan-proj/img/ff705572-5fb8-4216-883c-63bbfb809286.png new file mode 100644 index 0000000000000000000000000000000000000000..aec24695c2e5a04248d60cb4999a21ad4ca11e42 Binary files /dev/null and b/new/gan-proj/img/ff705572-5fb8-4216-883c-63bbfb809286.png differ