赞
踩
原文:Generative Adversarial Networks Projects
译者:飞龙
本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。
不要担心自己的形象,只关心如何实现目标。——《原则》,生活原则 2.3.c
在本章中,我们将研究生成对抗网络(GAN)。 它们是一种深度神经网络架构,它使用无监督的机器学习来生成数据。 他们在 2014 年由 Ian Goodfellow,Yoshua Bengio 和 Aaron Courville 的论文中介绍,可在以下链接中找到。 GAN 具有许多应用,包括图像生成和药物开发。
本章将向您介绍 GAN 的核心组件。 它将带您了解每个组件的工作方式以及 GAN 背后的重要概念和技术。 它还将简要概述使用 GAN 的优缺点 ,并深入了解某些实际应用。
本章将通过探讨以下主题来涵盖所有这些要点:
GAN 是由两个网络(生成器网络和判别器网络)组成的深度神经网络架构。 通过生成和辨别的多个周期,两个网络互相训练,同时试图互相取胜。
生成器网络使用现有数据来生成新数据。 例如,它可以使用现有图像生成新图像。 生成器的主要目标是从随机生成的数字向量(称为潜在空间)生成数据(例如图像,视频,音频或文本)。 在创建生成器网络时,我们需要指定网络的目标。 这可能是图像生成,文本生成,音频生成,视频生成等。
判别器网络试图区分真实数据和生成器网络生成的数据。 判别器网络尝试将传入的数据放入预定义的类别。 它可以执行多类分类或二分类。 通常,在 GAN 中执行二分类。
在 GAN 中,网络是通过对抗性游戏来训练的:两个网络相互竞争。 例如,假设我们要让 GAN 伪造艺术品:
在此游戏中,同时训练两个网络。 当我们达到区分者无法区分真假艺术品的阶段时,网络便达到了称为纳什均衡的状态。 这将在本章稍后讨论。
GAN 具有一些相当有用的实际应用,其中包括:
GAN 的架构具有两个基本元素:生成器网络和判别器网络。 每个网络都可以是任何神经网络,例如人工神经网络(ANN),卷积神经网络(CNN), 循环神经网络(RNN),或长短期记忆(LSTM)。 判别器必须具有完全连接的层,最后是分类器。
让我们仔细看一下 GAN 架构的组件。 在此示例中,我们将想象正在创建一个虚拟 GAN。
虚拟 GAN 中的生成器网络是一个简单的前馈神经网络,具有五层:输入层,三个隐藏层和输出层。 让我们仔细看看生成器(虚拟)网络的配置:
编号 | 层名称 | 配置 |
---|---|---|
1 | 输入层 | input_shape=(batch_size, 100) ,output_shape=(batch_size, 100) |
2 | 密集层 | neurons=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-httkCl5a-1681652801299)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/9c07dac5-3b55-444a-b7a5-6119c5d9370b.png)]
生成器网络的架构。
让我们讨论一下前馈神经网络在数据正向传播期间如何处理信息:
输入层采用从高斯(正态)分布中采样的 100 维向量,并将张量直接传递给第一隐藏层。
三个隐藏层是分别具有 500、500 和 784 个单元的密集层。 第一隐藏层(密集层)将形状为[batch_size, 100]
的张量转换为形状为[batch_size, 500]
的张量。
第二密集层生成形状为[batch_size, 500]
的张量。
第三隐藏层生成[batch_size, 784]
形状的张量。
在最后一个输出层中,该张量从[batch_size, 784]
的形状改成[batch_size, 28, 28]
的形状。 这意味着我们的网络将生成一批图像,其中一个图像的形状为[28, 28]
。
我们 GAN 中的判别器是前馈神经网络,它具有五层,包括一个输入层和一个输出层,以及三个密集层。 判别器网络是一个分类器,与生成器网络略有不同。 它处理图像并输出该图像属于特定类别的概率。
下图显示了判别器网络中每一层的张量流以及张量的输入和输出形状:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zWqb8Pey-1681652801301)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/5577e65f-d54f-48ad-9f22-950a7abf3c36.png)]
判别器网络的架构。
让我们讨论判别器如何在网络训练期间以正向传播方式处理数据:
28x28
的输入。batch_sizex28x28
)的张量,然后将其直接传递到第一隐藏层。现在我们已经了解了 GAN 的架构,下面让我们看一下一些重要概念的简要概述。 我们首先来看 KL 散度。 理解 JS 差异非常重要,这是评估模型质量的重要措施。 然后,我们将研究纳什均衡,这是我们在训练中试图达到的状态。 最后,我们将仔细研究目标函数,了解这些目标函数对于很好地实现 GAN 至关重要。
Kullback-Leibler 散度(KL 散度),也称为相对熵,是一种用于识别两个概率分布之间相似性的方法。 它测量一个概率分布p
与第二个预期概率分布q
的差异。
用于计算两个概率分布p(x)
和q(x)
之间的 KL 散度的公式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fnMu768A-1681652801302)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/3ed12abb-fb0d-4205-848a-928127ec92ca.png)]
当p(x)
等于q(x)
时,KL 散度将为零或最小值。
由于 KL 散度的不对称性质,我们不应该使用它来度量两个概率分布之间的距离。 因此,不应将其用作距离度量。
Jensen-Shannon 散度(也称为信息半径(IRaD)或总散度与平均值)是两个概率分布之间相似度的另一种度量。 它基于 KL 散度。 但是,与 KL 散度不同,JS 散度本质上是对称的,可用于测量两个概率分布之间的距离。 如果我们采用 Jensen-Shannon 发散的平方根,则会得到 Jensen-Shannon 距离,因此它是距离度量。
以下等式表示两个概率分布p
和q
之间的 Jensen-Shannon 散度:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6B0QW1XI-1681652801302)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/ce0afe99-4137-4673-985a-b073ab66c347.png)]
在前面的等式中,p + q
是中点度量,而D[KL]
是 Kullback-Leibler 散度。
现在我们已经了解了 KL 散度和 Jenson-Shannon 散度,让我们讨论 GAN 的纳什均衡。
纳什均衡描述了博弈论中的特定状态。 在非合作游戏中可以达到这种状态,在这种游戏中,每个玩家都根据自己对其他玩家的期望,尝试选择最佳策略来为自己获得最佳结果。 最终,所有参与者都达到了根据其他参与者做出的决定为自己选择最佳策略的地步。 在游戏的这一点上,他们不会从改变策略中获得任何好处。 这种状态是纳什均衡。
关于如何达到纳什均衡的一个著名例子是《囚徒困境》。 在此示例中,两名犯罪分子(A 和 B)因犯罪而被捕。 两者都放置在单独的单元中,彼此之间无法通信。 检察官只有足够的证据将他们定罪为较小的罪行,而没有主要罪行,这将使他们长期入狱。 为了定罪,检察官向他们提出要约:
从这三种情况来看,很明显,A 和 B 的最佳可能结局是保持安静并在监狱服刑 1 年。 但是,保持安静的风险为 3 年,因为 A 和 B 都无法知道对方也将保持安静。 因此,他们将达到一种状态,在这种状态下,他们的实际最佳策略是供认,因为这是提供最高奖励和最低惩罚的选择。 当达到这种状态时,任何罪犯都不会通过改变策略来获得任何好处; 因此,他们将达到纳什均衡。
为了创建一个生成器网络,该生成器网络生成与真实图像相似的图像,我们尝试提高生成器生成的数据与实际数据之间的相似性。 为了测量相似度,我们使用目标函数。 这两个网络都有自己的目标函数,并且在训练过程中,它们会尽量减少各自的目标函数。 以下方程式表示 GAN 的最终目标函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fSRWpuW5-1681652801302)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/c682e8fd-bd22-4998-968f-227c26898f62.png)]
在前面的等式中,D(x)
是判别器模型,G(z)
是生成器模型,P(x)
是实际数据分布,P(z)
是生成器生成的数据的分布,E
是预期的输出。
在训练期间,D
(判别器)想要使整个输出最大化,而G
(生成器)希望使整个输出最小化,从而训练 GAN 使生成器和判别器网络达到平衡。 当它达到平衡时,我们说模型已经收敛。 这个平衡就是纳什平衡。 训练完成后,我们将获得一个生成器模型,该模型能够生成逼真的图像。
计算 GAN 的准确率很简单。 GAN 的目标函数不是特定函数,例如均方误差或交叉熵。 GAN 在训练期间学习目标函数。 研究人员提出了许多计分算法来衡量模型的拟合程度。 让我们详细了解一些评分算法。
初始分数是 GAN 使用最广泛的评分算法。 它使用预训练的 Inception V3 网络(在 Imagenet 上进行训练)来提取生成图像和真实图像的特征。 由 Shane Barrat 和 Rishi Sharma 在其论文《关于初始分数的注解》中提出。 初始分数或简称 IS,用于衡量所生成图像的质量和多样性。 让我们看一下IS
的等式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mxFU5gtU-1681652801303)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/0d33c46a-0a5f-4027-919c-30b910e6d93b.png)]
在前面的等式中,符号x
表示从分布中采样的样本。 P(g)
和x ~ P(g)
代表相同的概念。 P(y|x)
是条件类别分布,P(y)
是边际类别分布。
要计算初始分数,请执行以下步骤:
首先采样由模型生成的N
个图像,表示为(x^i)
然后,使用以下公式构建边际类分布:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bm1pI3Sf-1681652801304)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/f60e4201-0e24-4dab-b19c-c69d11994427.png)]
然后,使用以下公式计算 KL 散度和预期的改进:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNKVAcEV-1681652801304)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/0d33c46a-0a5f-4027-919c-30b910e6d93b.png)]
最后,计算结果的指数以得出初始分数。
如果模型的初始得分高,则其质量会很好。 尽管这是一项重要措施,但仍存在某些问题。 例如,即使模型每类生成一张图像,它也显示出很高的准确率,这意味着模型缺乏多样性。 为了解决此问题,提出了其他表现指标。 在下一节中,我们将介绍其中之一。
为了克服初始分数的各种缺点,Martin Heusel 等人在他们的论文中提出了 Fréchlet 初始距离(FID),《收敛到局部纳什均衡的两个时间维度更新规则所训练的 GAN》。
计算 FID 分数的公式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kCNWapw2-1681652801304)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/c23de9d4-5560-4c9f-a0bd-a3569bbf6f5a.png)]
前面的等式表示实际图像x
,与生成的图像g
之间的 FID 分数。 为了计算 FID 分数,我们使用 Inception 网络从 Inception 网络的中间层提取特征映射。 然后,我们对多元高斯分布建模,以学习特征映射的分布。 此多元高斯分布具有μ
的均值和∑
的协方差,我们用它们来计算 FID 得分。 FID 分数越低,模型越好,它越有能力生成更高质量的更多图像。 完美的生成模型的 FID 得分为零。 使用 FID 得分而不是 Inception 得分的优势在于它对噪声具有鲁棒性,并且可以轻松地测量图像的多样性。
学术界和工业界的研究人员最近提出了更多的评分算法。 我们不会在这里介绍所有这些内容。 在进一步阅读之前,请查看另一种称为模式得分的评分算法,有关该算法的信息可在以下链接中找到。
当前有成千上万种不同的 GAN,并且这个数字正以惊人的速度增长。 在本节中,我们将探索六种流行的 GAN 架构,我们将在本书的后续章节中对其进行更详细的介绍。
Alec Radford,Luke Metz 和 Soumith Chintala 在名为《使用深度卷积生成对抗网络的无监督表示学习》的论文中提出了深层卷积 GAN(DCGAN),可通过以下链接获得 。 朴素 GAN 通常在其网络中没有卷积神经网络(CNN)。 这是在 DCGAN 的引入下首次提出的。 我们将在第 3 章,“使用条件 GAN 进行人脸老化”中,学习如何使用 DCGAN 生成动漫人脸。
StackGAN 由 Han Zhang,Tao Xu,Li Hongsheng Li 等人在其题为《StackGAN:使用堆叠式生成对抗网络进行照片般逼真的文本到图像合成》的论文中提出,可从以下链接获得。 他们使用 StackGAN 来探索文本到图像的合成,并获得了令人印象深刻的结果。 StackGAN 是一对网络,当提供文本描述时,它们会生成逼真的图像。 我们将在第 6 章, “StackGAN – 文本到真实图像的图像合成”中,学习如何使用 StackGAN 从文本描述生成逼真的图像。。
CycleGAN 由朱俊彦,Taesung Park,Phillip Isola 和 Alexei A. Efros 在题为《使用循环生成对抗网络的不成对图像到图像翻译》的论文中提出。 在以下链接中。 CycleGAN 具有一些非常有趣的潜在用途,例如将照片转换为绘画,反之亦然,将夏天拍摄的照片转换为冬天,反之亦然,或者将马的图像转换为斑马的图像,反之亦然。 我们将在第 7 章, “CycleGAN - 将绘画变成照片”中学习如何使用 CycleGAN 将绘画变成照片。
3D-GAN 由 Wu Jiajun Wu,Zhengkai Zhang,薛天凡,William T. Freeman 和 Joshua B. Tenenbaum 在其名为《通过 3D 生成对抗建模来学习对象形状的概率潜在空间》的论文中提出,可通过以下链接获得。 在制造业和 3D 建模行业中,生成对象的 3D 模型具有许多用例。 一旦在对象的 3D 模型上进行训练,3D-GAN 网络便能够生成不同对象的新 3D 模型。 我们将在第 2 章, “使用 3D-GAN 生成形状”中学习如何使用 3D-GAN 生成对象的 3D 模型。
有条件 GAN 的人脸老化由 Grigory Antipov,Moez Baccouche 和 Jean-Luc Dugelay 在他们的题为《使用条件生成对抗网络的人脸老化》的论文中提出,可通过以下链接获得。 人脸老化具有许多行业用例,包括跨年龄的人脸识别,寻找迷路的孩子以及娱乐。 我们将在第 3 章,“使用条件 GAN 的人脸老化”中,学习如何训练条件 GAN 生成给定目标年龄的人脸。
pix2pix 网络是由 Phillip Isola,朱俊彦,周廷辉和 Alexei A. Efros 在他们的论文《使用条件对抗网络的图像到图像翻译》中介绍的,可通过以下链接获得。 pix2pix 网络具有与 CycleGAN 网络类似的用例。 它可以将建筑物标签转换为建筑物图片(我们将在 pix2pix 章节中看到一个类似的示例),黑白图像转换为彩色图像,将白天至夜晚的图像,草图转换为照片, 和航拍图像到类似地图的图像。
有关现有所有 GAN 的列表,请参阅 GAN Zoo,这是 Avinash Hindupur 的文章。
与其他有监督或无监督学习方法相比,GAN 具有某些优势:
GAN 是一种无监督的学习方法 :获取带标签的数据是一个手动过程,需要花费大量时间。 GAN 不需要标签数据; 当他们学习数据的内部表示时,可以使用未标记的数据来训练他们。
GAN 生成数据:关于 GAN 的最好的事情之一是,它们生成的数据类似于真实数据。 因此,它们在现实世界中有许多不同的用途。 它们可以生成与真实数据无法区分的图像,文本,音频和视频。 GAN 生成的图像可应用于营销,电子商务,游戏,广告和许多其他行业。
GAN 学习数据的密度分布:GAN 学习数据的内部表示。 如前所述,GAN 可以学习混乱而复杂的数据分布。 这可以用于许多机器学习问题。
受过训练的判别器是分类器:经过训练,我们得到了判别器和生成器。 判别器网络是分类器,可用于分类对象。
与任何技术一样,GAN 也有一些问题。 这些问题通常与训练过程有关,包括模式崩溃,内部协变量偏移和梯度消失。 让我们更详细地看看这些。
模式崩溃是指生成器网络生成变化不大的样本或模型开始生成相同图像的情况。 有时,概率分布实际上是多峰的,非常复杂。 这意味着它可能包含来自不同观察值的数据,并且对于样本的不同子图可能具有多个峰。 有时,GAN 不能为数据的多峰概率分布建模,并且会遭受模式崩溃。 所有生成的样本实际上都相同的情况称为完全崩溃。
我们可以使用许多方法来克服模式崩溃问题。 其中包括:
通过针对不同模式训练多个模型(GAN)
通过使用各种数据样本来训练 GAN
在反向传播期间,梯度从最后一层向第一层反向流动。 随着向后流动,它变得越来越小。 有时,梯度是如此之小,以至于初始层学习非常缓慢或完全停止学习。 在这种情况下,梯度完全不会改变初始层的权重值,因此有效地停止了网络中初始层的训练。 这被称为梯度消失问题。
如果我们使用基于梯度的优化方法训练更大的网络,此问题将变得更加严重。 当我们少量改变参数值时,基于梯度的优化方法通过计算网络输出的变化来优化参数值。 如果参数值的变化导致网络输出的微小变化,则权重变化将非常小,因此网络将停止学习。
当我们使用 Sigmoid 和 Tanh 等激活函数时,这也是一个问题。 Sigmoid 激活函数将值限制在 0 到 1 之间,将x
的大值转换为大约 1,将x
的小值或负值转换为大约零。 Tanh 激活函数将输入值压缩为 -1 和 1 之间的范围,将大输入值转换为大约 1,将小值转换为大约负 1。当应用反向传播时,我们使用微分链式规则, 有倍增作用。 当我们到达网络的初始层时,梯度(误差)呈指数下降,从而导致梯度消失。
为了克服这个问题,我们可以使用激活函数,例如 ReLU,LeakyReLU 和 PReLU。 这些激活函数的梯度在反向传播期间不会饱和,从而导致神经网络的有效训练。 另一种解决方案是使用批量规范化,该规范化将对网络隐藏层的输入规范化。
当我们网络的输入分配发生变化时,就会发生内部协变量偏移。 当输入分布更改时,隐藏层将尝试学习以适应新的分布。 这减慢了训练过程。 如果进程变慢,则需要很长时间才能收敛到全局最小值。 当网络输入的统计分布与以前看到的输入完全不同时,就会出现此问题。 批量规范化和其他归一化技术可以解决此问题。 我们将在以下各节中进行探讨。
训练稳定性是与 GAN 相关的最大问题之一。 对于某些数据集,由于此类问题,GAN 从未收敛。 在本节中,我们将研究一些可用于提高 GAN 稳定性的解决方案。
在 GAN 的训练过程中,我们使判别器网络的目标函数最大化,并使生成器网络的目标函数最小化。 这个目标函数有一些严重的缺陷。 例如,它没有考虑生成的数据和实际数据的统计信息。
特征匹配是 Tim Salimans,Ian Goodfellow 等人在其题为《GAN 训练的改进技术》的论文中提出的一种技术,旨在通过引入新的目标函数来改善 GAN 的收敛性。 生成器网络的新目标函数鼓励其生成具有统计信息的数据,该数据与真实数据相似。
要应用特征映射,网络不会要求判别器提供二进制标签。 相反,判别器网络提供从判别器网络的中间层提取的输入数据的激活或特征映射。 从训练的角度来看,我们训练判别器网络以学习真实数据的重要统计信息。 因此,目的在于通过学习那些判别特征,它应该能够从假数据中区分出真实数据。
为了从数学上理解这种方法,让我们首先看一下不同的表示法:
f(x)
:来自判别器网络中间层的真实数据的激活图或特征映射f(G(z))
:生成器网络从判别器网络中的中间层生成的数据的激活/特征映射。这个新的目标函数可以表示为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNmfEk9j-1681652801305)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/d1541159-75a5-487e-805f-80a8c97436bd.png)]
使用此目标函数可以获得更好的结果,但是仍然不能保证收敛。
小批量判别是稳定 GAN 训练的另一种方法。 它是由 Ian Goodfellow 等人在《GAN 训练的改进技术》中提出的,该技术可从这里获得。 为了理解这种方法,让我们首先详细研究问题。 在训练 GAN 时,当我们将独立的输入传递给判别器网络时,梯度之间的协调可能会丢失,这将阻止判别器网络学习如何区分由生成器网络生成的各种图像。 这是模式崩溃,这是我们之前讨论的问题。 为了解决这个问题,我们可以使用小批量判别。 下图很好地说明了此过程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y1ekTFa1-1681652801305)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/f542b337-7bc3-475e-b3ee-00a963352321.png)]
小批判别是一个多步骤的过程。 执行以下步骤,将小批量判别添加到您的网络:
T ∈ R^(A×B×C)
相乘,生成矩阵M[i] ∈ R^(A×B)
。M[i]
的行之间的 L1 距离:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xnqi95XT-1681652801306)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/4769bf48-3e9e-4976-999d-22513076b07d.png)]
x[i]
计算所有距离的总和:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UKGen7Lx-1681652801306)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/c236f76b-da88-4ac1-9c4a-50cc4c3acb59.png)]
o(x[i])
与f(x[i])
连接起来,并将其馈送到网络的下一层:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wHIbSBgi-1681652801307)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/d399507e-b812-41a5-8731-9b7e2ebf48aa.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5MwTvp7g-1681652801307)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/56ceb926-b75b-490c-91ff-9b736d389cb5.png)]
为了从数学上理解这种方法,让我们仔细看一下各种概念:
f(x[i])
:来自判别器网络中间层的第i
个样本的激活图或特征映射T ∈ R^(A×B×C)
:三维张量,我们乘以f(x[i])
M[i] ∈ R^(A×B)
:将张量T
和f(x[i])
相乘时生成的矩阵o(x[i])
:对于特定示例x[i]
,采用所有距离的总和后的输出小批量判别有助于防止模式崩溃,并提高训练稳定性的机会。
历史平均是一种获取过去参数平均值并将其添加到生成器和判别器网络的各个成本函数的方法。 它是由 Ian Goodfellow 等人在先前提到的《GAN 训练的改进技术》中提出的。
历史平均值可以表示为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3nlUhEH-1681652801307)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/04528291-1308-47df-8d56-5b242a809cb1.png)]
在前面的等式中,θ[i]
是在特定时间i
的参数值。 这种方法也可以提高 GAN 的训练稳定性。
之前,分类器的标签/目标值为 0 或 1; 0 代表假图片,1 代表真实图片。 因此,GAN 容易出现对抗性示例,其中是神经网络的输入,导致神经网络的输出不正确。 标签平滑是一种向判别器网络提供平滑标签的方法。 这意味着我们可以使用十进制值,例如 0.9(真实),0.8(真实),0.1(虚假)或 0.2(虚假),而不是将每个示例都标记为 1(真实)或 0(虚假)。 我们对真实图像和伪图像的目标值(标签值)进行平滑处理。 标签平滑可以降低 GAN 中对抗性示例的风险。 要应用标签平滑,请为图像分配标签 0.9、0.8 和 0.7、0.1、0.2 和 0.3。 要查找有关标签平滑的更多信息,请参阅以下论文。
批量规范化是一种对特征向量进行归一化以使其没有均值或单位方差的技术。 它用于稳定学习并处理较差的权重初始化问题。 这是预处理步骤,适用于网络的隐藏层,可帮助我们减少内部协变量偏移。
批量规范化由 Ioffe 和 Szegedy 在其 2015 年论文《批量规范化:通过减少内部协变量偏移来加速深度网络训练》中引入。可以在以下链接中找到。
The benefits of batch normalization are as follows:
在批量规范化中,我们将规范化应用于所有隐藏层,而不是仅将其应用于输入层。
如上一节所述,批量规范化仅通过利用来自该批量的信息来对一批样本进行归一化。 实例规范化是一种略有不同的方法。 在实例归一化中,我们仅通过利用来自该特征映射的信息来归一化每个特征映射。 实例规范化由 Dmitry Ulyanov 和 Andrea Vedaldi 在标题为《实例规范化:用于快速风格化的缺失成分》的论文中介绍,可通过以下链接获得。
在本章中,我们了解了 GAN 是什么以及组成标准 GAN 架构的组件。 我们还探讨了可用的各种 GAN。 建立 GAN 的基本概念后,我们继续研究 GAN 的构建和功能中涉及的基本概念。 我们了解了 GAN 的优缺点,以及有助于克服这些缺点的解决方案。 最后,我们了解了 GAN 的各种实际应用。
利用本章中 GAN 的基本知识,我们现在将进入下一章,在此我们将学习使用 GAN 生成各种形状。
3D-GAN 是用于 3D 形状生成的 GAN 架构。 由于处理 3D 图像涉及复杂性,因此 3D 形状生成通常是一个复杂的问题。 3D-GAN 是一种可以生成逼真的,变化的 3D 形状的解决方案,由吴嘉俊,张成凯,薛天凡等人在题为《通过 3D 生成对抗建模学习对象形状的概率潜在空间》的论文中介绍,可以在这个页面上找到该论文。 在本章中,我们将使用 Keras 框架实现 3D-GAN。
我们将涵盖以下主题:
3D 生成对抗网络(3D-GAN)是 GAN 的变体,就像 StackGAN,CycleGAN 和超分辨率生成对抗网络(SRGAN)一样 。 与朴素 GAN 相似,它具有生成器和判别器模型。 这两个网络都使用 3D 卷积层,而不是使用 2D 卷积。 如果提供足够的数据,它可以学习生成具有良好视觉质量的 3D 形状。
在仔细查看 3D-GAN 网络之前,让我们了解 3D 卷积。
简而言之,3D 卷积操作沿x
,y
和z
这三个方向对输入数据应用 3D 过滤器。 此操作将创建 3D 特征映射的堆叠列表。 输出的形状类似于立方体或长方体的形状。 下图说明了 3D 卷积操作。 左立方体的突出显示部分是输入数据。 内核位于中间,形状为(3, 3, 3)
。 右侧的块是卷积运算的输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XgsNw7Zh-1681652801308)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/9128ea2e-31f5-472a-8ebc-f3add1a716e0.png)]
现在,我们对 3D 卷积有了基本的了解,让我们继续看一下 3D-GAN 的架构。
3D-GAN 中的两个网络都是深度卷积神经网络。 生成器网络通常是一个上采样网络。 它对噪声向量(来自概率潜在空间的向量)进行上采样,以生成形状为的 3D 图像,该形状的长度,宽度,高度和通道与输入图像相似。 判别器网络是下采样网络。 使用一系列 3D 卷积运算和密集层,它可以识别提供给它的输入数据是真实的还是伪造的。
在接下来的两节中,我们将介绍生成器和判别器网络的架构。
生成器网络包含五个体积完全卷积的层,具有以下配置:
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, 1), (2, 2), (2, 2), (2, 2), (2, 2)
网络的输入和输出如下:
64x64x64
的 3D 图像下图显示了生成器的架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G8EmurNr-1681652801308)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/adfe13fc-6e5c-4ffb-8230-ba4117391c77.png)]
下图显示了判别器网络中张量的流动以及每个层的张量的输入和输出形状。 这将使您对网络有更好的了解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y478OqUj-1681652801309)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/fd209aa6-8f96-45df-a754-7ae8cb364fa1.png)]
完全卷积网络是在网络末端没有完全连接的密集层的网络。 相反,它仅由卷积层组成,并且可以进行端到端训练,就像具有完全连接的层的卷积网络一样。 生成器网络中没有池化层。
判别器网络包含五个具有以下配置的体积卷积层:
网络的输入和输出如下:
(64, 64, 64)
的 3D 图像下图显示了判别器网络中每一层的张量流以及张量的输入和输出形状。 这将使您对判别器网络有更好的了解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hRpXdBFt-1681652801309)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/7fc08303-569e-499f-bc91-e2ae9684f947.png)]
判别器网络主要镜像生成器网络。 一个重要的区别是它使用中的 LeakyReLU 代替了 ReLU 作为激活函数。 而且,网络末端的 Sigmoid 层用于二分类,并预测所提供的图像是真实的还是伪造的。 最后一层没有规范化层,但是其他层使用批量规范化输入。
目标函数是训练 3D-GAN 的主要方法。 它提供损失值,这些损失值用于计算梯度,然后更新权重值。 3D-GAN 的对抗损失函数如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FAVAKjJz-1681652801309)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/e8c1a187-a93a-40e6-92a9-3c823a391fd9.png)]
在这里, log(D(X))
是二进制交叉熵损失或分类损失,log(1 - D(G(Z)))
是对抗损失,z
是来自概率空间p(Z)
的潜向量, D(X)
是判别器网络的输出, G(Z)
是生成器网络的输出。
训练 3D-GAN 类似于训练朴素 GAN。 训练 3D-GAN 涉及的步骤如下:
我们将在后面的部分中详细探讨这些步骤。 让我们继续建立一个项目。
该项目的源代码可在 GitHub 上的以下链接中找到。
运行以下命令来设置项目:
cd Generative-Adversarial-Networks-Projects
Chapter02
目录:cd Chapter02
virtualenv venv
source venv/bin/activate
requirements.txt
文件中指示的所有要求:pip install -r requirements.txt
我们现在已经成功建立了该项目。 有关更多信息,请参见代码存储库中包含的 README.md
文件。
在本章中,我们将使用 3D ShapeNets 数据集,该数据集可从这个页面获得。 它由 Wu 和 Song 等人发行。 并包含 40 个对象类别的正确标注的 3D 形状。 我们将使用目录中可用的体积数据,我们将在本章稍后详细讨论。 在接下来的几节中,我们将下载,提取和浏览数据集。
3D ShapeNets 数据集仅用于学术用途。 如果您打算将数据集用于商业目的,请征求论文作者的许可,可以通过以下电子邮件地址与他们联系:shurans@cs.princeton.edu
.
运行以下命令以下载并提取数据集:
3DShapeNets
:wget http://3dshapenets.cs.princeton.edu/3DShapeNetsCode.zip
unzip 3DShapeNetsCode.zip
现在,我们已经成功下载并提取了数据集。 它包含.mat
(MATLAB)格式的图像。 每隔一张图像是 3D 图像。 在接下来的几节中,我们将学习体素,即 3D 空间中的点。
要了解数据集,我们需要可视化 3D 图像。 在接下来的几节中,我们将首先更详细地了解什么是体素。 然后,我们将加载并可视化 3D 图像。
体像素或体素是三维空间中的一个点。 体素在x
,y
和z
方向上定义了具有三个坐标的位置。 体素是表示 3D 图像的基本单位。 它们主要用于 CAT 扫描,X 射线和 MRI 中,以创建人体和其他 3D 对象的准确 3D 模型。 要处理 3D 图像,了解体素非常重要,因为这些是 3D 图像的组成。 包含下图,以使您了解 3D 图像中的体素是什么样的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P19Q015g-1681652801310)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/4a160433-e15b-440d-8a3f-91163db58421.png)]
在 3D 图像中的一系列体素。 阴影区域是单个体素。
前面的图像是体素的堆叠表示。 灰色长方体代表一个体素。 现在,您了解了什么是体素,让我们在下一部分中加载和可视化 3D 图像。
3D ShapeNets 数据集包含.mat
文件格式。 我们将这些.mat
文件转换为 NumPy N 维数组。 我们还将可视化 3D 图像,以直观了解数据集。
执行以下代码以从.mat
文件加载 3D 图像:
scipy
中的loadmat()
函数检索voxels
。 代码如下:import scipy.io as io
voxels = io.loadmat("path to .mat file")['instance']
30x30x30
。 我们的网络需要形状为64x64x64
的图像。 我们将使用 NumPy 的 pad()
方法将 3D 图像的大小增加到32x32x32
:import numpy as np
voxels = np.pad(voxels, (1, 1), 'constant', constant_values=(0, 0))
pad()
方法采用四个参数,它们是实际体素的 N 维数组,需要填充到每个轴边缘的值的数量,模式值(constant
)和constant_values
被填充。
scipy.ndimage
模块中的zoom()
函数将 3D 图像转换为大小为64x64x64
的 3D 图像。import scipy.ndimage as nd
voxels = nd.zoom(voxels, (2, 2, 2), mode='constant', order=0)
我们的网络要求图像的形状为64x64x64
,这就是为什么我们将 3D 图像转换为这种形状的原因。
让我们使用 matplotlib 可视化 3D 图像,如以下代码所示:
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal')
voxels
添加到绘图中:ax.voxels(voxels, edgecolor="red")
plt.show() plt.savefig(file_path)
第一个屏幕截图表示 3D 飞机上的飞机:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngCK0uCG-1681652801316)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/981169b9-a3b7-435a-8188-2b6f42b10d30.png)]
第二张屏幕截图表示 3D 平面中的表格:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ocHPqC5-1681652801317)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/25c92f62-a0a7-4c64-b768-577ac7dfe05b.png)]
第三个屏幕截图表示 3D 平面中的椅子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1naXJiI9-1681652801317)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/51177648-2247-4346-b270-5139a5dcab46.png)]
我们已经成功下载,提取和浏览了数据集。 我们还研究了如何使用体素。 在下一节中,我们将在 Keras 框架中实现 3D-GAN。
在本节中,我们将在 Keras 框架中实现生成器网络和判别器网络。 我们需要创建两个 Keras 模型。 这两个网络都有各自独立的权重值。 让我们从生成器网络开始。
为了实现生成器网络,我们需要创建 Keras 模型并添加神经网络层。 实现生成器网络所需的步骤如下:
z_size = 200 gen_filters = [512, 256, 128, 64, 1]
gen_kernel_sizes = [4, 4, 4, 4, 4]
gen_strides = [1, 2, 2, 2, 2]
gen_input_shape = (1, 1, 1, z_size)
gen_activations = ['relu', 'relu', 'relu', 'relu', 'sigmoid']
gen_convolutional_blocks = 5
input_layer = Input(shape=gen_input_shape)
# 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)
# 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)
model = Model(inputs=input_layer, outputs=a)
build_generator()
的函数内:def build_generator(): """ Create a Generator Model with hyperparameters values defined as follows :return: Generator network """ z_size = 200 gen_filters = [512, 256, 128, 64, 1] gen_kernel_sizes = [4, 4, 4, 4, 4] gen_strides = [1, 2, 2, 2, 2] gen_input_shape = (1, 1, 1, z_size) gen_activations = ['relu', 'relu', 'relu', 'relu', 'sigmoid'] gen_convolutional_blocks = 5 input_layer = Input(shape=gen_input_shape) # First 3D transpose convolution(or 3D deconvolution) block a = Deconv3D(filters=gen_filters[0], kernel_size=gen_kernel_sizes[0], strides=gen_strides[0])(input_layer) a = BatchNormalization()(a, training=True) a = Activation(activation='relu')(a) # Next 4 3D transpose convolution(or 3D deconvolution) blocks for i in range(gen_convolutional_blocks - 1): a = Deconv3D(filters=gen_filters[i + 1], kernel_size=gen_kernel_sizes[i + 1], strides=gen_strides[i + 1], padding='same')(a) a = BatchNormalization()(a, training=True) a = Activation(activation=gen_activations[i + 1])(a) gen_model = Model(inputs=input_layer, outputs=a) gen_model.summary() return gen_model
我们已经成功地为生成器网络创建了 Keras 模型。 接下来,为判别器网络创建 Keras 模型。
同样,要实现判别器网络,我们需要创建 Keras 模型并向其中添加神经网络层。 实现判别器网络所需的步骤如下:
dis_input_shape = (64, 64, 64, 1)
dis_filters = [64, 128, 256, 512, 1]
dis_kernel_sizes = [4, 4, 4, 4, 4]
dis_strides = [2, 2, 2, 2, 1]
dis_paddings = ['same', 'same', 'same', 'same', 'valid']
dis_alphas = [0.2, 0.2, 0.2, 0.2, 0.2]
dis_activations = ['leaky_relu', 'leaky_relu', 'leaky_relu', 'leaky_relu', 'sigmoid']
dis_convolutional_blocks = 5
64x64x64x1
的 3D 图像:dis_input_layer = Input(shape=dis_input_shape)
# 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)
# 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)
dis_model = Model(inputs=dis_input_layer, outputs=a)
def build_discriminator(): """ Create a Discriminator Model using hyperparameters values defined as follows :return: Discriminator network """ dis_input_shape = (64, 64, 64, 1) dis_filters = [64, 128, 256, 512, 1] dis_kernel_sizes = [4, 4, 4, 4, 4] dis_strides = [2, 2, 2, 2, 1] dis_paddings = ['same', 'same', 'same', 'same', 'valid'] dis_alphas = [0.2, 0.2, 0.2, 0.2, 0.2] dis_activations = ['leaky_relu', 'leaky_relu', 'leaky_relu', 'leaky_relu', 'sigmoid'] dis_convolutional_blocks = 5 dis_input_layer = Input(shape=dis_input_shape) # The first 3D Convolutional block a = Conv3D(filters=dis_filters[0], kernel_size=dis_kernel_sizes[0], strides=dis_strides[0], padding=dis_paddings[0])(dis_input_layer) a = BatchNormalization()(a, training=True) a = LeakyReLU(dis_alphas[0])(a) # Next 4 3D Convolutional Blocks for i in range(dis_convolutional_blocks - 1): a = Conv3D(filters=dis_filters[i + 1], kernel_size=dis_kernel_sizes[i + 1], strides=dis_strides[i + 1], padding=dis_paddings[i + 1])(a) a = BatchNormalization()(a, training=True) if dis_activations[i + 1] == 'leaky_relu': a = LeakyReLU(dis_alphas[i + 1])(a) elif dis_activations[i + 1] == 'sigmoid': a = Activation(activation='sigmoid')(a) dis_model = Model(inputs=dis_input_layer, outputs=a) print(dis_model.summary()) return dis_model
在本节中,我们为判别器网络创建了 Keras 模型。 我们现在准备训练 3D-GAN。
训练 3D-GAN 类似于训练朴素 GAN。 我们首先在生成的图像和真实图像上训练判别器网络,但是冻结生成器网络。 然后,我们训练生成器网络,但冻结判别器网络。 我们对指定的周期数重复此过程。 在一次迭代中,我们按顺序训练两个网络。 训练 3D-GAN 是一个端到端的训练过程。 让我们一步一步地进行这些步骤。
要训练 3D-GAN,请执行以下步骤:
gen_learning_rate = 0.0025 dis_learning_rate = 0.00001
beta = 0.5
batch_size = 32
z_size = 200
DIR_PATH = 'Path to the 3DShapenets dataset directory'
generated_volumes_dir = 'generated_volumes'
log_dir = 'logs'
# Create instances
generator = build_generator()
discriminator = build_discriminator()
# Specify optimizer
gen_optimizer = Adam(lr=gen_learning_rate, beta_1=beta)
dis_optimizer = Adam(lr=dis_learning_rate, beta_1=0.9)
# Compile networks
generator.compile(loss="binary_crossentropy", optimizer="adam")
discriminator.compile(loss='binary_crossentropy', optimizer=dis_optimizer)
我们正在使用Adam
优化器作为优化算法,并使用binary_crossentropy
作为损失函数。 在第一步中指定Adam
优化器的超参数值。
discriminator.trainable = False adversarial_model = Sequential()
adversarial_model.add(generator)
adversarial_model.add(discriminator) adversarial_model.compile(loss="binary_crossentropy", optimizer=Adam(lr=gen_learning_rate, beta_1=beta))
airplane
图像以进行训练:def getVoxelsFromMat(path, cube_len=64):
voxels = io.loadmat(path)['instance'] voxels = np.pad(voxels, (1, 1), 'constant', constant_values=(0, 0)) if cube_len != 32 and cube_len == 64:
voxels = nd.zoom(voxels, (2, 2, 2), mode='constant', order=0) return voxels
def get3ImagesForACategory(obj='airplane', train=True, cube_len=64, obj_ratio=1.0):
obj_path = DIR_PATH + obj + '/30/'
obj_path += 'train/' if train else 'test/'
fileList = [f for f in os.listdir(obj_path) if f.endswith('.mat')]
fileList = fileList[0:int(obj_ratio * len(fileList))]
volumeBatch = np.asarray([getVoxelsFromMat(obj_path + f, cube_len) for f in fileList], dtype=np.bool)
return volumeBatch
volumes = get3ImagesForACategory(obj='airplane', train=True, obj_ratio=1.0)
volumes = volumes[..., np.newaxis].astype(np.float)
TensorBoard
回调并添加generator
和discriminator
网络:tensorboard = TensorBoard(log_dir="{}/{}".format(log_dir, time.time()))
tensorboard.set_model(generator)
tensorboard.set_model(discriminator)
for epoch in range(epochs):
print("Epoch:", epoch)
# Create two lists to store losses
gen_losses = []
dis_losses = []
number_of_batches = int(volumes.shape[0] / batch_size)
print("Number of batches:", number_of_batches)
for index in range(number_of_batches):
print("Batch:", index + 1)
(1, 1, 1, 200)
:z_sample = np.random.normal(0, 0.33, size=[batch_size, 1, 1, 1,
z_size]).astype(np.float32)
volumes_batch = volumes[index * batch_size:(index + 1) * batch_size,
:, :, :]
z_sample
的一批噪声向量,并生成一批假图像:gen_volumes = generator.predict(z_sample,verbose=3)
# Make the discriminator network trainable discriminator.trainable = True
# Create fake and real labels
labels_real = np.reshape([1] * batch_size, (-1, 1, 1, 1, 1))
labels_fake = np.reshape([0] * batch_size, (-1, 1, 1, 1, 1))
# Train the discriminator network
loss_real = discriminator.train_on_batch(volumes_batch,
labels_real)
loss_fake = discriminator.train_on_batch(gen_volumes,
labels_fake)
# Calculate total discriminator loss
d_loss = 0.5 * (loss_real + loss_fake)
前面的代码训练了判别器网络并计算了总的判别器损失。
generator
和discriminator
网络的对抗模型:z = np.random.normal(0, 0.33, size=[batch_size, 1, 1, 1, z_size]).astype(np.float32)
# Train the adversarial model g_loss = adversarial_model.train_on_batch(z, np.reshape([1] * batch_size, (-1, 1, 1, 1, 1)))
另外,将损失添加到其各自的列表中,如下所示:
gen_losses.append(g_loss)
dis_losses.append(d_loss)
if index % 10 == 0:
z_sample2 = np.random.normal(0, 0.33, size=[batch_size, 1, 1, 1, z_size]).astype(np.float32)
generated_volumes = generator.predict(z_sample2, verbose=3)
for i, generated_volume in enumerate(generated_volumes[:5]):
voxels = np.squeeze(generated_volume)
voxels[voxels < 0.5] = 0.
voxels[voxels >= 0.5] = 1.
saveFromVoxels(voxels, "results/img_{}_{}_{}".format(epoch, index, i))
tensorboard
:# Save losses to Tensorboard write_log(tensorboard, 'g_loss', np.mean(gen_losses), epoch)
write_log(tensorboard, 'd_loss', np.mean(dis_losses), epoch)
我的建议是将其训练 100 个周期,以查找代码中的问题。 解决了这些问题后,您就可以在 100,000 个周期内训练网络。
训练完成后,通过添加以下代码来保存生成器和判别器模型的学习权重:
""" Save models """ generator.save_weights(os.path.join(generated_volumes_dir, "generator_weights.h5"))
discriminator.save_weights(os.path.join(generated_volumes_dir, "discriminator_weights.h5"))
要测试网络,请创建generator
和discriminator
网络。 然后,加载学习的权重。 最后,使用predict()
方法生成预测:
# Create models generator = build_generator()
discriminator = build_discriminator()
# Load model weights generator.load_weights(os.path.join(generated_volumes_dir, "generator_weights.h5"), True)
discriminator.load_weights(os.path.join(generated_volumes_dir, "discriminator_weights.h5"), True)
# Generate 3D images z_sample = np.random.normal(0, 0.33, size=[batch_size, 1, 1, 1, z_size]).astype(np.float32)
generated_volumes = generator.predict(z_sample, verbose=3)
在本节中,我们成功地训练了 3D-GAN 的生成器和识别器。 在下一节中,我们将探讨超参数调整和各种超参数优化选项。
要可视化训练的损失,请按如下所示启动tensorboard
服务器。
tensorboard --logdir=logs
现在,在浏览器中打开 localhost:6006
。 TensorBoard 的标量部分包含两种损失的图表:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r8BSrgMU-1681652801317)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/b67a8b95-a1fe-4c06-be44-cec497dd35bf.png)]
生成器网络的损失图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ak0QeZM6-1681652801318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/7937c970-9e91-4f47-bc05-2b230ae1b0c6.png)]
判别器网络的损失图
这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止训练,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 尝试使用超参数,然后选择一组您认为可能会提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。
TensorBoard 的GRAPHS
部分包含两个网络的图 。 如果网络表现不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的运算:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ik0IBSFg-1681652801318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/411f5f33-a51c-4f61-a795-4fb06231ea00.png)]
我们训练的模型可能不是一个完美的模型,但是我们可以优化超参数来改进它。 3D-GAN 中有许多可以优化的超参数。 其中包括:
我们还可以通过使用 Hyperopt 或 Hyperas 选择最佳的超参数集。
3D-GAN 可以广泛用于各种行业,如下所示:
制造业:3D-GAN 可以是帮助快速创建原型的创新工具。 他们可以提出创意,并可以帮助模拟和可视化 3D 模型。
3D 打印:3D-GAN 生成的 3D 图像可用于在 3D 打印中打印对象。 创建 3D 模型的手动过程非常漫长。
设计过程:3D 生成的模型可以很好地估计特定过程的最终结果。 他们可以向我们展示将要构建的内容。
新样本:与其他 GAN 相似,3D-GAN 可以生成图像来训练监督模型。
在本章中,我们探讨了 3D-GAN。 我们首先介绍了 3D-GAN,并介绍了架构以及生成器和判别器的配置。 然后,我们经历了建立项目所需的不同步骤。 我们还研究了如何准备数据集。 最后,我们在 Keras 框架中实现了 3D-GAN,并在我们的数据集中对其进行了训练。 我们还探讨了不同的超参数选项。 我们通过探索 3D-GAN 的实际应用来结束本章。
在下一章中,我们将学习如何使用条件生成对抗网络(cGAN)执行人脸老化。
条件 GAN(cGAN)是 GAN 模型的扩展。 它们允许生成具有特定条件或属性的图像,因此被证明比朴素 GAN 更好。 在本章中,我们将实现一个 cGAN,该 cGAN 一旦经过训练,就可以执行自动人脸老化。 Grigory Antipov,Moez Baccouche 和 Jean-Luc Dugelay 在其名为《使用条件生成对抗网络的人脸老化》的论文中,首先介绍了我们将要实现的 cGAN 网络。 可以在以下链接中找到。
在本章中,我们将介绍以下主题:
到目前为止,我们已经针对不同的用例实现了不同的 GAN 网络。 条件 GAN 扩展了普通 GAN 的概念,并允许我们控制生成器网络的输出。 人脸老化就是在不更改身份的情况下更改一个人的人脸年龄。 在大多数其他模型(包括 GAN)中,由于不考虑人脸表情和人脸配件(例如太阳镜或胡须),因此会使人的外观或身份损失 50%。 Age-cGAN 会考虑所有这些属性。 在本节中,我们将探索用于人脸老化的 cGAN。
cGAN 是 GAN 的一种,它取决于一些额外的信息。 我们将额外的y
信息作为额外的输入层提供给生成器。 在朴素 GAN 中,无法控制所生成图像的类别。 当我们向生成器添加条件y
时,我们可以使用y
生成特定类别的图像,该图像可以是任何类型的数据,例如类标签或整数数据 。朴素 GAN 只能学习一个类别,而为多个类别构造 GAN 则非常困难。 但是,可以使用 cGAN 生成针对不同类别具有不同条件的多模式模型。
下图显示了 cGAN 的架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yPtm31WZ-1681652801318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/7b4b89cd-9b42-4609-84e2-94086b34ac3a.png)]
cGAN 的训练目标函数可以表示为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKtYz0Os-1681652801318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/115b5d5f-6623-46dc-bed1-e99a6aa5910c.png)]
此处,G
是生成器网络,D
是判别器网络。 判别器的损失为log D(x|y)
, 生成器的损失为log(1 - D(G(z|y)))
。 我们可以说G(z|y)
在给定z
和y
情况下建模我们数据的分布。 在此,z
是从正态分布得出的大小为 100 的先验噪声分布。
用于人脸老化的 cGAN 的架构稍微复杂一些。 Age-cGan 由四个网络组成:编码器,FaceNet,生成器网络和判别器网络。 使用编码器,我们可以利用潜在的向量z[0]
来学习输入人脸图像和年龄条件的逆映射。 FaceNet 是人脸识别网络,用于学习输入图像x
与重构的图像x_tide
之间的差异。 我们有一个生成器网络,该网络使用由人脸图像和条件向量组成的隐藏表示并生成图像。 判别器网络将区分真实图像和伪图像。
cGAN 的问题在于它们无法学习将属性y
的输入图像x
逆映射到潜向量z
的任务。解决此问题的方法是使用编码器网络。 我们可以训练一个编码器网络来近似输入图像x
的逆映射。 在本节中,我们将探讨 Age-cGAN 涉及的网络。
编码器网络的主要目标是生成所提供图像的潜向量。 基本上,它会拍摄大小为(64, 64, 3)
的图像,并将其转换为 100 维向量。 编码器网络是一个深度卷积神经网络。 网络包含四个卷积块和两个密集层。 每个卷积块包含一个卷积层,一个批标准化层和一个激活函数。 在每个卷积块中,每个卷积层后面都有一个批量归一化层,但第一个卷积层除外。 Age-cGAN 部分的 “Keras 实现”将介绍编码器网络的配置。
生成器的主要目标是生成大小为(64, 64, 3)
的图像。 它需要一个 100 维的潜向量和一些额外的信息y
和尝试生成逼真的图像。 生成器网络也是一个深层卷积神经网络。 它由密集,上采样和卷积层组成。 它采用两个输入值:噪声向量和条件值。 条件值是提供给网络的附加信息。 对于 Age-cGAN,这就是年龄。 生成器网络的配置将在 Age-cGAN 部分的“Keras 实现”中进行介绍。
判别器网络的主要目标是识别所提供的图像是伪造的还是真实的。 它通过使图像经过一系列下采样层和一些分类层来实现。 换句话说,它可以预测图像是真实的还是伪造的。 像其他网络一样,判别器网络是另一个深度卷积网络。 它包含几个卷积块。 每个卷积块都包含一个卷积层,一个批量归一化层和一个激活函数,除了第一个卷积块之外,它没有批量归一化层。 判别器网络的配置将在 Age-cGAN 部分的“Keras 实现”中进行介绍。
人脸识别网络的主要目标是在给定图像中识别人的身份。 对于我们的任务,我们将使用没有完全连接的层的预训练的 Inception-ResNet-2 模型。 Keras 有一个很好的预训练模型库。 出于实验目的,您也可以使用其他网络,例如 Inception 或 ResNet-50。 要了解有关 Inception-ResNet-2 的更多信息,请访问链接。 一旦提供了图像,经过预训练的 Inception-ResNet-2 网络将返回相应的嵌入。 可以通过计算嵌入的欧几里德距离来计算针对真实图像和重建图像的提取的嵌入。 关于年龄识别网络的更多信息将在 Age-cGAN 部分的“Keras 实现”中进行介绍。
Age-cGAN 具有多个训练阶段。 如上一节所述,Age-cGAN 具有四个网络,并经过三个阶段的训练。 Age-cGAN 的训练包括三个阶段:
以下屏幕截图显示了 Age-cGAN 的各个阶段:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VneaMWyP-1681652801318)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/3019472f-d08b-4fa2-9ea1-071be37bc6bd.png)]
Age-cGAN 的各个阶段,资料来源:使用条件生成对抗网络进行人脸老化
我们将在以下部分介绍 Age- cGAN 的所有阶段。
在这个阶段,我们训练生成器网络和判别器网络。 经过训练后,生成器网络可以生成人脸的模糊图像。 此阶段类似于训练朴素 GAN,其中我们同时训练两个网络。
用于训练 cGAN 的训练目标函数可以表示为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9kUVMlic-1681652801319)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/0e7b1187-382e-4584-9c8b-b39c1db1fd91.png)]
训练 cGAN 网络涉及优化函数,ν(θ[G], θ[D])
。 训练 cGAN 可以看作是 minimax 游戏,其中同时对生成器和判别器进行训练。 在上式中, θ[G}
代表生成器网络的参数,θ[D]
代表G
和D
的参数 , log D(x, y)
是判别器模型的损失,log(1 - D(G(z|y_tide), y_tide))
是生成器模型的损失, P(data)
是所有可能的图像的分布。
初始潜在向量近似是一种近似潜在向量以优化人脸图像重建的方法。 为了近似一个潜在向量,我们有一个编码器网络。 我们在生成的图像和真实图像上训练编码器网络。 训练后,编码器网络将开始从学习到的分布中生成潜在向量。 用于训练编码器网络的训练目标函数是欧几里得距离损失。
在潜在向量优化过程中,我们同时优化了编码器网络和生成器网络。 我们用于潜在向量优化的方程式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JtkLbpdA-1681652801319)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/98bda784-ced5-4608-8c1d-38477d40cb8f.png)]
FR
是人脸识别网络。 该方程式表明,真实图像和重建图像之间的欧几里得距离应最小。 在此阶段,我们尝试最小化距离以最大程度地保留身份。
如果尚未使用所有章节的完整代码克隆存储库,请立即克隆存储库。 克隆的存储库有一个名为Chapter03
的目录,其中包含本章的全部代码。 执行以下命令来设置项目:
cd Generative-Adversarial-Networks-Projects
Chapter03
:cd Chapter03
virtualenv venv
virtualenv venv -p python3 # Create a virtual environment using python3 interpreter
virtualenv venv -p python2 # Create a virtual environment using python2 interpreter
我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。
source venv/bin/activate
激活虚拟环境后,所有其他命令将在此虚拟环境中执行。
requirements.txt
文件中提供的所有库:pip install -r requirements.txt
您可以参考 README.md
文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。
在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一部分中,我们将处理数据集。
在本章中,我们将使用Wiki-Cropped
数据集,其中包含 64 张以上 328 张不同人脸的图像。 作者还提供了数据集 ,该数据集仅包含已裁剪的人脸,因此我们无需裁剪人脸。
论文《没有人脸标志的单张图像的真实年龄和外表年龄的深度期望》,可在以下网址获得。作者从维基百科抓取了这些图片,并将其用于学术目的。 如果您打算将数据集用于商业目的,请通过以下方式与作者联系:rrothe@vision.ee.ethz.ch
。
您可以从以下链接手动下载数据集,并将所有压缩文件放置在 Age-cGAN 项目内的目录中。
执行以下步骤以下载和提取数据集。
要下载仅包含裁剪的面的数据集,请执行以下命令:
# Before download the dataset move to data directory
cd data
# Wikipedia : Download faces only
wget https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/static/wiki_crop.tar
下载数据集后,手动将文件提取到数据文件夹中,或执行以下命令来提取文件:
# Move to data directory
cd data
# Extract wiki_crop.tar
tar -xvf wiki_crop.tar
wiki_crop.tar
文件包含 62,328 张图像和一个包含所有标签的wiki.mat
文件。 scipy.io
库有一个称为loadmat
的方法,这是在 Python 中加载.mat
文件的非常方便的方法。 使用以下代码加载提取的.mat
文件:
def load_data(wiki_dir, dataset='wiki'): # Load the wiki.mat file meta = loadmat(os.path.join(wiki_dir, "{}.mat".format(dataset))) # Load the list of all files full_path = meta[dataset][0, 0]["full_path"][0] # List of Matlab serial date numbers dob = meta[dataset][0, 0]["dob"][0] # List of years when photo was taken photo_taken = meta[dataset][0, 0]["photo_taken"][0] # year # Calculate age for all dobs age = [calculate_age(photo_taken[i], dob[i]) for i in range(len(dob))] # Create a list of tuples containing a pair of an image path and age images = [] age_list = [] for index, image_path in enumerate(full_path): images.append(image_path[0]) age_list.append(age[index]) # Return a list of all images and respective age return images, age_list
photo_taken
变量是年份列表,dob
是 Matlab 列表中相应照片的序列号。 我们可以根据序列号和照片拍摄的年份来计算该人的年龄。 使用以下代码来计算年龄:
def calculate_age(taken, dob):
birth = datetime.fromordinal(max(int(dob) - 366, 1))
# assume the photo was taken in the middle of the year
if birth.month < 7:
return taken - birth.year
else:
return taken - birth.year - 1
现在,我们已经成功下载并提取了数据集。 在下一节中,让我们研究 Age-cGAN 的 Keras 实现。
像普通 GAN 一样,cGAN 的实现非常简单。 Keras 提供了足够的灵活性来编码复杂的生成对抗网络。 在本节中,我们将实现 cGAN 中使用的生成器网络,判别器网络和编码器网络。 让我们从实现编码器网络开始。
在开始编写实现之前,创建一个名为 main.py
的 Python 文件,并导入基本模块,如下所示:
import math import os import time from datetime import datetime import matplotlib.pyplot as plt import numpy as np import tensorflow as tf from keras import Input, Model from keras.applications import InceptionResNetV2 from keras.callbacks import TensorBoard from keras.layers import Conv2D, Flatten, Dense, BatchNormalization, Reshape, concatenate, LeakyReLU, Lambda, \ K, Conv2DTranspose, Activation, UpSampling2D, Dropout from keras.optimizers import Adam from keras.utils import to_categorical from keras_preprocessing import image from scipy.io import loadmat
编码器网络是卷积神经网络(CNN),可将图像(x
)编码为潜向量(z
)或潜在的向量表示。 让我们从在 Keras 框架中实现编码器网络开始。
执行以下步骤来实现编码器网络:
input_layer = Input(shape=(64, 64, 3))
32
5
2
same
LeakyReLU
,其中alpha
等于0.2
:# 1st Convolutional Block enc = Conv2D(filters=32, kernel_size=5, strides=2, padding='same')(input_layer) enc = LeakyReLU(alpha=0.2)(enc)
64
,128
,256
5
,5
,5
2
,2
,2
same
,same
,same
LealyReLU
,LeakyReLU
,LeakyReLU
,其中alpha
等于0.2
:# 2nd Convolutional Block enc = Conv2D(filters=64, kernel_size=5, strides=2, padding='same')(enc)
enc = BatchNormalization()(enc)
enc = LeakyReLU(alpha=0.2)(enc)
# 3rd Convolutional Block enc = Conv2D(filters=128, kernel_size=5, strides=2, padding='same')(enc)
enc = BatchNormalization()(enc)
enc = LeakyReLU(alpha=0.2)(enc)
# 4th Convolutional Block enc = Conv2D(filters=256, kernel_size=5, strides=2, padding='same')(enc)
enc = BatchNormalization()(enc)
enc = LeakyReLU(alpha=0.2)(enc)
# Flatten layer enc = Flatten()(enc)
将n
维张量转换为一维张量(数组)称为“扁平化”。
LeakyReLU
,其中alpha
等于0.2
:# 1st Fully Connected Layer enc = Dense(4096)(enc)
enc = BatchNormalization()(enc)
enc = LeakyReLU(alpha=0.2)(enc)
100
# Second Fully Connected Layer enc = Dense(100)(enc)
# Create a model model = Model(inputs=[input_layer], outputs=[enc])
编码器网络的完整代码如下所示:
def build_encoder(): """ Encoder Network :return: Encoder model """ input_layer = Input(shape=(64, 64, 3)) # 1st Convolutional Block enc = Conv2D(filters=32, kernel_size=5, strides=2, padding='same')(input_layer) enc = LeakyReLU(alpha=0.2)(enc) # 2nd Convolutional Block enc = Conv2D(filters=64, kernel_size=5, strides=2, padding='same')(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc) # 3rd Convolutional Block enc = Conv2D(filters=128, kernel_size=5, strides=2, padding='same')(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc) # 4th Convolutional Block enc = Conv2D(filters=256, kernel_size=5, strides=2, padding='same')(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc) # Flatten layer enc = Flatten()(enc) # 1st Fully Connected Layer enc = Dense(4096)(enc) enc = BatchNormalization()(enc) enc = LeakyReLU(alpha=0.2)(enc) # Second Fully Connected Layer enc = Dense(100)(enc) # Create a model model = Model(inputs=[input_layer], outputs=[enc]) return model
我们现在已经成功地为编码器网络创建了 Keras 模型。 接下来,为生成器网络创建 Keras 模型。
生成器网络是一个 CNN,它采用 100 维向量z
,并生成大小为(64, 64, 3)
的图像。 让我们在 Keras 框架中实现生成器网络。
执行以下步骤以实现生成器网络:
latent_dims = 100 num_classes = 6
# Input layer for vector z
input_z_noise = Input(shape=(latent_dims, ))
# Input layer for conditioning variable
input_label = Input(shape=(num_classes, ))
x = concatenate([input_z_noise, input_label])
上一步将生成级联张量。
2,048
LeakyReLU
的alpha
等于0.2
0.2
:x = Dense(2048, input_dim=latent_dims+num_classes)(x)
x = LeakyReLU(alpha=0.2)(x)
x = Dropout(0.2)(x)
alpha
等于0.2
的LeakyReLU
0.2
:x = Dense(256 * 8 * 8)(x)
x = BatchNormalization()(x)
x = LeakyReLU(alpha=0.2)(x)
x = Dropout(0.2)(x)
(8, 8, 256)
的三维张量:x = Reshape((8, 8, 256))(x)
该层将生成张量为(batch_size
,8, 8, 256
)的张量。
(2, 2)
128
5
same
momentum
等于0.8
LeakyReLU
,其中alpha
等于0.2
:x = UpSampling2D(size=(2, 2))(x)
x = Conv2D(filters=128, kernel_size=5, padding='same')(x)
x = BatchNormalization(momentum=0.8)(x)
x = LeakyReLU(alpha=0.2)(x)
Upsampling2D
is the process of repeating the rows a specified number of timesx
and repeating the columns a specified number of timesy
, respectively.
128
之外,该配置与上一个块类似:x = UpSampling2D(size=(2, 2))(x)
x = Conv2D(filters=64, kernel_size=5, padding='same')(x)
x = BatchNormalization(momentum=0.8)(x)
x = LeakyReLU(alpha=0.2)(x)
x = UpSampling2D(size=(2, 2))(x)
x = Conv2D(filters=3, kernel_size=5, padding='same')(x)
x = Activation('tanh')(x)
model = Model(inputs=[input_z_noise, input_label], outputs=[x])
生成器网络的完整代码如下所示:
def build_generator(): """ Create a Generator Model with hyperparameters values defined as follows :return: Generator model """ latent_dims = 100 num_classes = 6 input_z_noise = Input(shape=(latent_dims,)) input_label = Input(shape=(num_classes,)) x = concatenate([input_z_noise, input_label]) x = Dense(2048, input_dim=latent_dims + num_classes)(x) x = LeakyReLU(alpha=0.2)(x) x = Dropout(0.2)(x) x = Dense(256 * 8 * 8)(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x) x = Dropout(0.2)(x) x = Reshape((8, 8, 256))(x) x = UpSampling2D(size=(2, 2))(x) x = Conv2D(filters=128, kernel_size=5, padding='same')(x) x = BatchNormalization(momentum=0.8)(x) x = LeakyReLU(alpha=0.2)(x) x = UpSampling2D(size=(2, 2))(x) x = Conv2D(filters=64, kernel_size=5, padding='same')(x) x = BatchNormalization(momentum=0.8)(x) x = LeakyReLU(alpha=0.2)(x) x = UpSampling2D(size=(2, 2))(x) x = Conv2D(filters=3, kernel_size=5, padding='same')(x) x = Activation('tanh')(x) model = Model(inputs=[input_z_noise, input_label], outputs=[x]) return model
现在,我们已经成功创建了生成器网络。 接下来,我们将为判别器网络编写代码。
判别器网络是 CNN。 让我们在 Keras 框架中实现判别器网络。
执行以下步骤以实现判别器网络:
# Specify hyperparameters
# Input image shape
input_shape = (64, 64, 3)
# Input conditioning variable shape
label_shape = (6,)
# Two input layers
image_input = Input(shape=input_shape)
label_input = Input(shape=label_shape)
64
3
2
same
LeakyReLU
,其中alpha
等于0.2
:x = Conv2D(64, kernel_size=3, strides=2, padding='same')(image_input)
x = LeakyReLU(alpha=0.2)(x)
label_input
使其具有(32, 32, 6)
的形状:label_input1 = Lambda(expand_label_input)(label_input)
expand_label_input
函数如下:
# The expand_label_input function
def expand_label_input(x):
x = K.expand_dims(x, axis=1)
x = K.expand_dims(x, axis=1)
x = K.tile(x, [1, 32, 32, 1])
return x
前面的函数会将大小为(6, )
的张量转换为大小为(32, 32, 6)
的张量。
x = concatenate([x, label_input1], axis=3)
128
3
2
same
LeakyReLU
,其中alpha
等于0.2
:x = Conv2D(128, kernel_size=3, strides=2, padding='same')(x)
x = BatchNormalization()(x)
x = LeakyReLU(alpha=0.2)(x)
x = Conv2D(256, kernel_size=3, strides=2, padding='same')(x)
x = BatchNormalization()(x)
x = LeakyReLU(alpha=0.2)(x)
x = Conv2D(512, kernel_size=3, strides=2, padding='same')(x)
x = BatchNormalization()(x)
x = LeakyReLU(alpha=0.2)(x)
x = Flatten()(x)
x = Dense(1, activation='sigmoid')(x)
model = Model(inputs=[image_input, label_input], outputs=[x])
判别器网络的整个代码如下:
def build_discriminator(): """ Create a Discriminator Model with hyperparameters values defined as follows :return: Discriminator model """ input_shape = (64, 64, 3) label_shape = (6,) image_input = Input(shape=input_shape) label_input = Input(shape=label_shape) x = Conv2D(64, kernel_size=3, strides=2, padding='same')(image_input) x = LeakyReLU(alpha=0.2)(x) label_input1 = Lambda(expand_label_input)(label_input) x = concatenate([x, label_input1], axis=3) x = Conv2D(128, kernel_size=3, strides=2, padding='same')(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x) x = Conv2D(256, kernel_size=3, strides=2, padding='same')(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x) x = Conv2D(512, kernel_size=3, strides=2, padding='same')(x) x = BatchNormalization()(x) x = LeakyReLU(alpha=0.2)(x) x = Flatten()(x) x = Dense(1, activation='sigmoid')(x) model = Model(inputs=[image_input, label_input], outputs=[x]) return model
现在,我们已经成功创建了编码器,生成器和判别器网络。 在下一部分中,我们将组装所有内容并训练网络。
训练 cGAN 进行人脸老化的过程分为三个步骤:
我们将在以下各节中逐一介绍这些步骤。
这是训练过程的第一步。 在这一步中,我们训练生成器和判别器网络。 执行以下步骤:
# 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)
Adam
优化器。 初始化优化器,如以下代码所示:# Define optimizers
# Optimizer for the discriminator network dis_optimizer = Adam(lr=0.0002, beta_1=0.5, beta_2=0.999, epsilon=10e-8)
# Optimizer for the generator network
gen_optimizer = Adam(lr=0.0002, beta_1=0.5, beta_2=0.999, epsilon=10e-8)
# Optimizer for the adversarial network
adversarial_optimizer = Adam(lr=0.0002, beta_1=0.5, beta_2=0.999, epsilon=10e-8)
对于所有优化程序,请使用等于0.0002
的学习率,等于0.5
的beta_1
值,等于0.999
的beta_2
值以及等于10e-8
的epsilon
值。
# Build and compile the discriminator network discriminator = build_discriminator()
discriminator.compile(loss=['binary_crossentropy'], optimizer=dis_optimizer)
# Build and compile the generator network generator = build_generator1()
generator.compile(loss=['binary_crossentropy'], optimizer=gen_optimizer)
要编译网络,请使用binary_crossentropy
作为损失函数。
# Build and compile the adversarial model discriminator.trainable = False input_z_noise = Input(shape=(100,))
input_label = Input(shape=(6,))
recons_images = generator([input_z_noise, input_label])
valid = discriminator([recons_images, input_label])
adversarial_model = Model(inputs=[input_z_noise, input_label], outputs=[valid])
adversarial_model.compile(loss=['binary_crossentropy'], optimizer=gen_optimizer)
要编译对抗模型,请使用binary_crossentropy
作为损失函数,并使用gen_optimizer
作为优化器。
TensorBoard
如下:tensorboard = TensorBoard(log_dir="logs/{}".format(time.time()))
tensorboard.set_model(generator)
tensorboard.set_model(discriminator)
load_data
函数加载所有图像:images, age_list = load_data(wiki_dir=wiki_dir, dataset="wiki")
# Convert age to category
age_cat = age_to_category(age_list)
age_to_category
函数的定义如下:
# This method will convert age to respective category def age_to_category(age_list): age_list1 = [] for age in age_list: if 0 < age <= 18: age_category = 0 elif 18 < age <= 29: age_category = 1 elif 29 < age <= 39: age_category = 2 elif 39 < age <= 49: age_category = 3 elif 49 < age <= 59: age_category = 4 elif age >= 60: age_category = 5 age_list1.append(age_category) return age_list1
age_cat
的输出应如下所示:
[1, 2, 4, 2, 3, 4, 2, 5, 5, 1, 3, 2, 1, 1, 2, 1, 2, 2, 1, 5, 4 , .............]
将年龄类别转换为单热编码的向量:
# Also, convert the age categories to one-hot encoded vectors
final_age_cat = np.reshape(np.array(age_cat), [len(age_cat), 1])
classes = len(set(age_cat))
y = to_categorical(final_age_cat, num_classes=len(set(age_cat)))
将年龄类别转换为单热编码的向量后,y
的值应如下所示:
[[0\. 1\. 0\. 0\. 0\. 0.]
[0\. 0\. 1\. 0\. 0\. 0.]
[0\. 0\. 0\. 0\. 1\. 0.]
...
[0\. 0\. 0\. 1\. 0\. 0.]
[0\. 1\. 0\. 0\. 0\. 0.]
[0\. 0\. 0\. 0\. 1\. 0.]]
y
的形状应为(total_values
,5
)。
ndarray
:# Read all images and create an ndarray
loaded_images = load_images(wiki_dir, images, (image_shape[0], image_shape[1]))
load_images
函数的定义如下:
def load_images(data_dir, image_paths, image_shape): images = None for i, image_path in enumerate(image_paths): print() try: # Load image loaded_image = image.load_img(os.path.join(data_dir, image_path), target_size=image_shape) # Convert PIL image to numpy ndarray loaded_image = image.img_to_array(loaded_image) # Add another dimension (Add batch dimension) loaded_image = np.expand_dims(loaded_image, axis=0) # Concatenate all images into one tensor if images is None: images = loaded_image else: images = np.concatenate([images, loaded_image], axis=0) except Exception as e: print("Error:", i, e) return images
loaded_images
中的值应如下所示:
[[[[ 97\. 122\. 178.] [ 98\. 123\. 179.] [ 99\. 124\. 180.] ... [ 97\. 124\. 179.] [ 96\. 123\. 178.] [ 95\. 122\. 177.]] ... [[216\. 197\. 203.] [217\. 198\. 204.] [218\. 199\. 205.] ... [ 66\. 75\. 90.] [110\. 127\. 171.] [ 89\. 115\. 172.]]] [[[122\. 140\. 152.] [115\. 133\. 145.] [ 95\. 113\. 123.] ... [ 41\. 73\. 23.] [ 38\. 77\. 22.] [ 38\. 77\. 22.]] [[ 53\. 80\. 63.] [ 47\. 74\. 57.] [ 45\. 72\. 55.] ... [ 34\. 66...
for
循环,该循环应运行的次数由周期数指定,如下所示:for epoch in range(epochs):
print("Epoch:{}".format(epoch))
gen_losses = []
dis_losses = []
number_of_batches = int(len(loaded_images) / batch_size)
print("Number of batches:", number_of_batches)
num_batches
指定的次数,如下所示: for index in range(number_of_batches):
print("Batch:{}".format(index + 1))
我们用于判别器网络和对抗网络训练的整个代码将在此循环内。
images_batch = loaded_images[index * batch_size:(index + 1) * batch_size]
images_batch = images_batch / 127.5 - 1.0
images_batch = images_batch.astype(np.float32)
y_batch = y[index * batch_size:(index + 1) * batch_size]
image_batch
的形状应为[batch_size
和64, 64, 3
),y_batch
的形状应为(batch_size
和6
)。
z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))
initial_recon_images = generator.predict_on_batch([z_noise, y_batch])
生成器网络有两个输入z_noise
和y_batch
,它们是我们在步骤 11 和 12 中创建的。
d_loss_real = discriminator.train_on_batch([images_batch, y_batch], real_labels)
d_loss_fake = discriminator.train_on_batch([initial_recon_images, y_batch], fake_labels)
此代码应在一批图像上训练判别器网络。 在每个步骤中,将对一批样本进行判别。
# Again sample a batch of noise vectors from a Gaussian(normal) distribution
z_noise2 = np.random.normal(0, 1, size=(batch_size, z_shape))
# Samples a batch of random age values random_labels = np.random.randint(0, 6, batch_size).reshape(-1, 1)
# Convert the random age values to one-hot encoders
random_labels = to_categorical(random_labels, 6)
# Train the generator network
g_loss = adversarial_model.train_on_batch([z_noise2, sampled_labels], [1] * batch_size)
前面的代码将在一批输入上训练生成器网络。 对抗模型的输入是z_noise2
和random_labels
。
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
print("d_loss:{}".format(d_loss))
print("g_loss:{}".format(g_loss))
# Add losses to their respective lists
gen_losses.append(g_loss)
dis_losses.append(d_loss)
write_log(tensorboard, 'g_loss', np.mean(gen_losses), epoch)
write_log(tensorboard, 'd_loss', np.mean(dis_losses), epoch)
if epoch % 10 == 0:
images_batch = loaded_images[0:batch_size]
images_batch = images_batch / 127.5 - 1.0
images_batch = images_batch.astype(np.float32)
y_batch = y[0:batch_size]
z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))
gen_images = generator.predict_on_batch([z_noise, y_batch])
for i, img in enumerate(gen_images[:5]):
save_rgb_img(img, path="results/img_{}_{}.png".format(epoch, i))
将前面的代码块放入周期循环中。 每隔 10 个时间段,它将生成一批伪图像并将其保存到结果目录。 这里, save_rgb_img()
是效用函数,定义如下:
def save_rgb_img(img, path):
"""
Save a rgb image """ fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.imshow(img)
ax.axis("off")
ax.set_title("Image")
plt.savefig(path)
plt.close()
# Save weights only
generator.save_weights("generator.h5")
discriminator.save_weights("discriminator.h5")
# Save architecture and weights both
generator.save("generator.h5)
discriminator.save("discriminator.h5")
如果您已成功执行本节中给出的代码,则说明您已成功训练了生成器和判别器网络。 在此步骤之后,生成器网络将开始生成模糊的人脸图像。 在下一部分中,我们将训练编码器模型以进行初始潜在向量近似。
正如我们已经讨论过的,cGAN 不会学习从图像到潜在向量的反向映射。 取而代之的是,编码器学习了这种反向映射,并能够生成潜在向量,我们可以将其用于在目标年龄生成人脸图像。 让我们训练编码器网络。
我们已经定义了训练所需的超参数。 执行以下步骤来训练编码器网络:
# Build Encoder encoder = build_encoder()
encoder.compile(loss=euclidean_distance_loss, optimizer='adam')
我们尚未定义euclidean_distance_loss
。 在构建编码器网络之前,让我们对其进行定义并添加以下内容:
def euclidean_distance_loss(y_true, y_pred):
"""
Euclidean distance loss """ return K.sqrt(K.sum(K.square(y_pred - y_true), axis=-1))
generator.load_weights("generator.h5")
在这里,我们正在加载上一步的权重,在该步骤中,我们成功地训练并保存了生成器网络的权重。
z_i = np.random.normal(0, 1, size=(1000, z_shape))
y = np.random.randint(low=0, high=6, size=(1000,), dtype=np.int64)
num_classes = len(set(y))
y = np.reshape(np.array(y), [len(y), 1])
y = to_categorical(y, num_classes=num_classes)
您可以随意采样。 在我们的例子中,我们正在采样 1,000 个值。
for epoch in range(epochs):
print("Epoch:", epoch)
encoder_losses = []
number_of_batches = int(z_i.shape[0] / batch_size)
print("Number of batches:", number_of_batches)
for index in range(number_of_batches):
print("Batch:", index + 1)
z_batch = z_i[index * batch_size:(index + 1) * batch_size]
y_batch = y[index * batch_size:(index + 1) * batch_size]
generated_images = generator.predict_on_batch([z_batch, y_batch])
encoder_loss = encoder.train_on_batch(generated_images, z_batch)
write_log(tensorboard, "encoder_loss", np.mean(encoder_losses), epoch)
encoder.save_weights("encoder.h5")
如果您已成功执行了本节中给出的代码,则将成功地训练编码器模型。 现在,我们的编码器网络已准备好生成初始潜向量。 在下一节中,我们将学习如何执行优化的潜在向量近似。
在前面的两个步骤中,我们成功地训练了生成器网络,判别器网络和编码器网络。 在本节中,我们将改进编码器和生成器网络。 在这些步骤中,我们将使用人脸识别(FR)网络,该网络会生成输入给它的特定输入的 128 维嵌入,以改善生成器和编码器网络。
执行以下步骤:
encoder = build_encoder()
encoder.load_weights("encoder.h5")
# Load the generator network generator.load_weights("generator.h5")
(64, 64, 3)
形状调整为(192, 192, 3)
形状,如下所示:# Define all functions before you make a call to them
def build_image_resizer():
input_layer = Input(shape=(64, 64, 3))
resized_images = Lambda(lambda x: K.resize_images(x, height_factor=3, width_factor=3,
data_format='channels_last'))(input_layer)
model = Model(inputs=[input_layer], outputs=[resized_images])
return model
image_resizer = build_image_resizer()
image_resizer.compile(loss=loss, optimizer='adam')
要使用 FaceNet,我们图像的高度和宽度应大于 150 像素。 前面的网络将帮助我们将图像转换为所需的格式。
# Face recognition model fr_model = build_fr_model(input_shape=fr_image_shape)
fr_model.compile(loss=loss, optimizer="adam")
请参阅这里以获取build_fr_model()
函数。
# Make the face recognition network as non-trainable fr_model.trainable = False # Input layers input_image = Input(shape=(64, 64, 3)) input_label = Input(shape=(6,)) # Use the encoder and the generator network latent0 = encoder(input_image) gen_images = generator([latent0, input_label]) # Resize images to the desired shape resized_images = Lambda(lambda x: K.resize_images(gen_images, height_factor=3, width_factor=3, data_format='channels_last'))(gen_images) embeddings = fr_model(resized_images) # Create a Keras model and specify the inputs and outputs to the network fr_adversarial_model = Model(inputs=[input_image, input_label], outputs=[embeddings]) # Compile the model fr_adversarial_model.compile(loss=euclidean_distance_loss, optimizer=adversarial_optimizer)
epoch
循环和一个批量步骤循环,如下所示:for epoch in range(epochs):
print("Epoch:", epoch)
number_of_batches = int(len(loaded_images) / batch_size)
print("Number of batches:", number_of_batches)
for index in range(number_of_batches):
print("Batch:", index + 1)
# Sample and normalize
images_batch = loaded_images[index * batch_size:(index + 1) * batch_size]
images_batch = images_batch / 255.0
images_batch = images_batch.astype(np.float32)
# Sample a batch of age one-hot encoder vectors
y_batch = y[index * batch_size:(index + 1) * batch_size]
images_batch_resized = image_resizer.predict_on_batch(images_batch) real_embeddings = fr_model.predict_on_batch(images_batch_resized)
reconstruction_loss = fr_adversarial_model.train_on_batch([images_batch, y_batch], real_embeddings)
# Write the reconstruction loss to Tensorboard write_log(tensorboard, "reconstruction_loss", reconstruction_loss, index)
# Save improved weights for both of the networks generator.save_weights("generator_optimized.h5")
encoder.save_weights("encoder_optimized.h5")
恭喜你! 我们现在已经成功地训练了 Age-cGAN 进行人脸老化。
要可视化训练损失,请启动 Tensorboard 服务器,如下所示:
tensorboard --logdir=logs
现在,在浏览器中打开 localhost:6006
。 TensorBoard 的标量部分包含两种损失的图表,如以下屏幕截图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mi272u8K-1681652801320)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/e705b732-209a-407e-bbb4-5ddae7424011.png)]
这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止训练,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。
TensorBoard 的GRAPHS
部分包含两个网络的图。 如果网络表现不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QmxSTij3-1681652801320)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/63d2ac6d-4141-47c4-8807-19efe0bb901e.png)]
张量流和图内部的不同操作
年龄综合和年龄发展具有许多工业和消费者应用:
在本章中,我们介绍了年龄条件生成对抗网络(Age-cGAN)。 然后,我们研究了 Age-cGAN 的架构。 之后,我们学习了如何设置我们的项目,并研究了 Ages-cGAN 的 Keras 实现。 然后,我们在 Wiki 裁剪的数据集上训练了 Age-cGAN,并经历了 Age-cGAN 网络的所有三个阶段。 最后,我们讨论了 Age-cGAN 的实际应用。
在下一章中,我们将使用 GAN 的另一个变体生成动画角色:DCGAN。
众所周知,卷积层确实擅长处理图像。 他们能够学习重要的特征,例如边缘,形状和复杂的对象,有效的, ,例如神经网络,例如 Inception,AlexNet, 视觉几何组(VGG)和 ResNet。 Ian Goodfellow 等人在其名为《生成对抗网络》的论文中提出了具有密集层的生成对抗网络(GAN),该网络可在以下链接获得。 复杂的神经网络,例如卷积神经网络(CNN),循环神经网络(RNN)和长短期记忆(LSTM)最初并未在 GAN 中进行测试。 深度卷积生成对抗网络(DCGAN)的发展是使用 CNN 进行图像生成的重要一步。 DCGAN 使用卷积层而不是密集层。 它们是由研究人员 Alec Radford , Luke Metz , Soumith Chintala 等,在其论文《使用深度卷积生成对抗网络的无监督表示学习》中提出的,可以在以下链接中找到。 从那时起,DCGAN 被广泛用于各种图像生成任务。 在本章中,我们将使用 DCGAN 架构生成动漫角色。
在本章中,我们将介绍以下主题:
CNN 在计算机视觉任务中非常出色,无论是用于分类图像还是检测图像中的对象。 CNN 善于理解图像,以至于激发研究人员在 GAN 网络中使用 CNN。 最初,GAN 官方论文的作者介绍了仅具有密集层的深层神经网络(DNN)。 在 GAN 网络的原始实现中未使用卷积层。 在以前的 GAN 中,生成器和判别器网络仅使用密集的隐藏层。 相反,作者建议在 GAN 设置中可以使用不同的神经网络架构。
DCGAN 扩展了在判别器和生成器网络中使用卷积层的思想。 DCGAN 的设置类似于朴素 GAN。 它由两个网络组成:生成器和判别器。 生成器是具有卷积层的 DNN,而判别器是具有卷积层的 DNN。 训练 DCGAN 类似于训练普通 GAN 网络。 在第一章中,我们了解到网络参与了非合作博弈,其中判别器网络将其误差反向传播到生成器网络,生成器网络使用此误差来提高其权重。
在下一部分中,我们将探索两个网络的架构。
如前所述,DCGAN 网络在两个网络中都使用卷积层。 重申一下,CNN 是一个具有卷积层,紧随其后的归一化或池化层以及紧随其后的激活函数的网络。 在 DCGAN 中,判别器网络会拍摄图像,在卷积和池化层的帮助下对图像进行降采样,然后使用密集的分类层将图像分类为真实图像或伪图像。 生成器网络从潜在空间中获取随机噪声向量,使用上采样机制对其进行上采样,最后生成图像。 我们使用 Leaky ReLU 作为隐藏层的激活函数,并在 0.4 和 0.7 之间进行滤除以避免过拟合。
让我们看一下两个网络的配置。
在继续之前,让我们看一下生成器网络的架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyfiFi8d-1681652801321)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/7201db6b-98f4-473e-8ba3-615cd8d132f1.png)]
来源:arXiv:1511.06434 [cs.LG]
上图包含了生成器网络架构中的不同层,并显示了它如何生成分辨率为64 x 64 x 3
的图像。
DCGAN 的生成器网络包含 10 层。 它执行跨步卷积以增加张量的空间分辨率。 在 Keras 中,上采样和卷积层的组合等于跨步卷积层。 基本上,生成器网络会从均匀分布中获取采样的噪声向量,并不断对其进行转换,直到生成最终图像为止。 换句话说,它采取形状的张量(batch_size, 100
),并输出形状的张量(batch_size, 64, 64, 3
)。
让我们看一下生成器网络中的不同层:
编号 | 层名称 | 配置 |
---|---|---|
1 | 输入层 | input_shape=(batch_size, 100) ,output_shape=(batch_size, 100) |
2 | 密集层 | neurons=2048 ,input_shape=(batch_size, 100) ,output_shape=(batch_size, 2048) ,activation='relu' |
3. | 密集层 | neurons=16384 ,input_shape=(batch_size, 100) ,output_shape=(batch_size, 2048) ,batch_normalization=Yes ,activation='relu' |
4. | 重塑层 | input_shape=(batch_size=16384) ,outp ut_shape=(batch_size, 8, 8, 256) |
5. | 上采样层 | size=(2, 2) ,input_shape=(batch_size, 8, 8, 256) ,output_shape=(batch_size, 16, 16, 256) |
6. | 2D 卷积层 | filters=128 ,kernel_size=(5, 5) ,strides=(1, 1) ,padding='same' ,input_shape=(batch_size, 16, 16, 256) ,output_shape=(batch_size, 16, 16, 128) , activation='relu' |
7. | 上采样层 | size=(2, 2) ,input_shape=(batch_size, 16, 16, 128) ,output_shape=(batch_size, 32, 32, 128) |
8. | 2D 卷积层 | filters=64 ,kernel_size=(5, 5) ,strides=(1, 1) ,padding='same' ,activation=ReLU ,input_shape=(batch_size, 32, 32, 128) ,output_shape=(batch_size, 32, 32, 64) ,activation='relu' |
9. | 上采样层 | size=(2, 2) ,input_shape=(batch_size, 32, 32, 64) ,output_shape=(batch_size, 64, 64, 64) |
10. | 2D 卷积层 | filters=3 ,kernel_size=(5, 5) ,strides=(1, 1) ,padding='same' ,activation=ReLU ,input_shape=(batch_size, 64, 64, 64) ,output_shape=(batch_size, 64, 64, 3) ,activation='tanh' |
L 等人研究了张量如何从第一层流到最后一层。 下图显示了不同层的输入和输出形状:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tHKcTBz5-1681652801321)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/6cb9082b-a52f-48e7-8193-caf3c11e93ea.png)]
该配置对具有 TensorFlow 后端和channels_last
格式的 Keras API 有效。
在继续之前,让我们看一下判别器网络的架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uV0IsSKA-1681652801321)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/0bbb9055-1c43-4f7d-a900-fdf515b6d068.png)]
上图给出了生成器网络架构的顶层概述。
如前所述,判别器网络是一个包含 10 层的 CNN(您可以根据需要向网络添加更多层)。 基本上,它会拍摄大小为64 x 64 x 3
的图像,使用 2D 卷积层对其进行下采样,然后将其传递到完全连接的层进行分类。 它的输出是对给定图像是伪图像还是真实图像的预测。 可以为 0 或 1;可以为 0。 如果输出为 1,则传递到判别器的图像是真实的;如果输出为 0,则传递的图像是伪图像。
让我们看一下判别器网络中的各层:
编号 | 层名称 | 配置 |
---|---|---|
1. | 输入层 | input_shape=(batch_size, 64, 64, 3) ,output_shape=(batch_size, 64, 64, 3) |
2. | 2D 卷积层 | filters=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. | 2D 最大池化 | 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. | 2D 最大池化 | 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. | 2D 最大池化 | pool_size=(2, 2) ,input_shape=(batch_size, 13, 13, 512) ,output_shape=(batch_size, 6, 6, 512) |
8. | 展开层 | input_shape=(batch_size, 6, 6, 512) ,output_shape=(batch_size, 18432) |
9. | 密集层 | neurons=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-uAIlExaG-1681652801326)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/9a5468ed-810e-429c-8a0a-af8eb1d673b0.png)]
该配置对具有 TensorFlow 后端和channels_last
格式的 Keras API 有效。
我们已经克隆/下载了所有章节的完整代码。 下载的代码包含一个名为Chapter04
的目录,该目录包含本章的全部代码。 执行以下命令来设置项目:
cd Generative-Adversarial-Networks-Projects
Chapter04
:cd Chapter04
virtualenv venv
virtualenv venv -p python3 # Create a virtual environment using
python3 interpreter
virtualenv venv -p python2 # Create a virtual environment using
python2 interpreter
我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。
source venv/bin/activate
激活虚拟环境后,所有其他命令将在此虚拟环境中执行。
requirements.txt
文件中给出的所有要求:pip install -r requirements.txt
您可以参考 README.md
文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。
在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一部分中,我们将使用数据集,包括下载和清理数据集。
要训练 DCGAN 网络,我们需要一个动漫人物数据集,其中包含人物的裁剪面孔。 收集数据集有多种方法。 我们可以使用公开可用的数据集,也可以抓取一个,只要不违反网站的抓取策略即可。 在本章中,我们将仅出于教育和演示目的刮取图像。 我们使用名为 gallery-dl
的搜寻器工具从 pixiv.net
抓取了图像。 这是一个命令行工具,可用于从网站下载图像集,例如 pixiv.net,exhentai.org,danbooru.donmai.us 和更多。 它可通过以下链接获得。
在本节中,我们将介绍安装依赖项和下载数据集所需的不同步骤。 在执行以下命令之前,激活为此项目创建的虚拟环境:
gallery-dl
:pip install --upgrade gallery-dl
gallery-dl
的最新开发版本:pip install --upgrade https://github.com/mikf/gallery-dl/archive/master.zip
# Official gallery-dl Github repo
https://github.com/mikf/gallery-dl
gallery-dl.
从danbooru.donmai.us
下载图像:gallery-dl https://danbooru.donmai.us/posts?tags=face
下载图像需要您自担风险。 所提供的信息仅用于教育目的,我们不支持非法刮取。 我们没有图像的版权,因为图像由其各自所有者托管。 出于商业目的,请联系网站的各自所有者或您所使用的内容。
在裁剪或调整图像大小之前,请查看下载的图像:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SRwTHOso-1681652801326)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/503cd7f0-4105-49a5-9095-d400768e7eb9.png)]
如您所见,有些图像还包含其他身体部位,我们在训练图像中不需要这些部位。 在下一部分中,我们将仅从这些图像中裁剪出人脸。 此外,我们会将所有图像调整为训练所需的大小。
在本节中,我们将从图像中裁剪出面孔。 我们将使用python-animeface
从图像中裁剪出面孔。 这是一个开源 GitHub 存储库,可从命令行的图像中自动裁剪人脸。 它可以通过以下链接公开获得。
执行以下步骤来裁剪图像并调整其大小:
python-animeface
:pip install animeface
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/*.*')):
try:
# Open image
im = Image.open(filename)
# Detect faces
faces = animeface.detect(im)
except Exception as e:
print("Exception:{}".format(e))
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)
(64, 64)
的大小: # Resize image cropped_image = cropped_image.resize((64, 64), Image.ANTIALIAS)
cropped_image.save("/path/to/directory/to/store/cropped/images/filename.png"))
包装在 Python 函数中的完整代码如下所示:
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 是一个使用 TensorFlow 或 Teano 作为后端的元框架。 它提供了用于神经网络的高级 API。 与低级框架(如 TensorFlow)相比,它还具有预构建的神经网络层,优化器,正则化器,初始化器和数据预处理层,可轻松进行原型制作。 让我们开始编写生成器网络的实现。
如 DCGAN 部分的“架构”中所述,生成器网络由一些 2D 卷积层,上采样层,整形层和批归一化层组成 。 在 Keras 中,每个操作都可以指定为一个层。 甚至激活函数也是 Keras 中的层,可以像正常的密集层一样添加到模型中。
执行以下步骤来创建生成器网络:
Sequential
Keras 模型:gen_model = Sequential()
tanh
:gen_model.add(Dense(units=2048))
gen_model.add(Activation('tanh'))
batch normalization
层,其中default hyperparameters
和tanh
作为激活函数:gen_model.add(Dense(256`8`8))
gen_model.add(BatchNormalization())
gen_model.add(Activation('tanh'))
第二密集层的输出是大小为16384
的张量。 此处,(256, 8, 8)
是密集层中神经元的数量。
batch_size, 8, 8, 256
)**:**形状的张量# Reshape layer
gen_model.add(Reshape((8, 8, 256), input_shape=(256`8`8,)))
(8, 8, 256)
更改为(16, 16, 256)
。 上采样大小为(2, 2)
,这将张量的大小增加到其原始大小的两倍。 在这里,我们有 256 个张量为16 x 16
:的张量。gen_model.add(UpSampling2D(size=(2, 2)))
(5, 5)
形状的内核:gen_model.add(Conv2D(128, (5, 5), padding='same'))
gen_model.add(Activation('tanh'))
(batch_size, 16, 16, 64)
更改为(batch_size, 32, 32, 64)
:gen_model.add(UpSampling2D(size=(2, 2)))
2D 上采样层将张量的行和列分别以[0]
和[1]
的大小重复 。
64
过滤器和,将(5, 5)
的核大小tanh
作为激活函数:gen_model.add(Conv2D(64, (5, 5), padding='same'))
gen_model.add(Activation('tanh'))
(batch_size, 32, 32, 64)
更改为(batch_size, 64, 64, 64)
:gen_model.add(UpSampling2D(size=(2, 2)))
(5, 5)
,然后是tanh
作为激活函数:gen_model.add(Conv2D(3, (5, 5), padding='same'))
gen_model.add(Activation('tanh'))
生成器网络将输出(batch_size, 64, 64, 3)
形状的张量。 这批张量中的一个图像张量类似于具有三个通道的大小为64 x 64
的图像: 红色,绿色和蓝色(RGB)。
用 Python 方法包装的生成器网络的完整代码如下所示:
def get_generator(): gen_model = Sequential() gen_model.add(Dense(input_dim=100, output_dim=2048)) gen_model.add(LeakyReLU(alpha=0.2)) gen_model.add(Dense(256 * 8 * 8)) gen_model.add(BatchNormalization()) gen_model.add(LeakyReLU(alpha=0.2)) gen_model.add(Reshape((8, 8, 256), input_shape=(256 * 8 * 8,))) gen_model.add(UpSampling2D(size=(2, 2))) gen_model.add(Conv2D(128, (5, 5), padding='same')) gen_model.add(LeakyReLU(alpha=0.2)) gen_model.add(UpSampling2D(size=(2, 2))) gen_model.add(Conv2D(64, (5, 5), padding='same')) gen_model.add(LeakyReLU(alpha=0.2)) gen_model.add(UpSampling2D(size=(2, 2))) gen_model.add(Conv2D(3, (5, 5), padding='same')) gen_model.add(LeakyReLU(alpha=0.2)) return gen_model
现在我们已经创建了生成器网络,让我们开始创建判别器网络。
如 DCGAN 的架构中所述,判别器网络具有三个 2D 卷积层,每个层均具有激活函数,后跟两个最大合并层。 网络的尾部包含两个完全连接的(密集)层,用作分类层。 首先,让我们看一下判别器网络中的不同层:
LeakyReLU
作为激活函数,其alpha
值为 0.2(5, 5)
,(3, 3)
和(3, 3)
。LeakyReLU
作为激活函数,而第二层具有 Sigmoid 作为激活函数。 Sigmoid 激活用于二分类。 我们正在训练辨别器网络,以区分真实图像还是伪图像。执行以下步骤来创建判别器网络:
Sequential
Keras 模型:dis_model = Sequential()
(64, 64, 3)
的输入图像。 该层的超参数如下。 另外,添加具有0.2
的alpha
值的LeakyReLU
作为激活函数:
(5, 5)
dis_model.add(Conv2D(filters=128, kernel_size=5, padding='same',
input_shape=(64, 64, 3)))
dis_model.add(LeakyReLU(alpha=0.2))
(2, 2)
的 2D 最大池化层。 最大池用于对图像表示进行下采样,并通过在表示的非重叠子区域上使用最大过滤来应用它:dis_model.add(MaxPooling2D(pool_size=(2, 2)))
来自第一层的输出张量的形状将为(batch_size, 32, 32, 128)
。
(3, 3)
LeakyReLU
,具有alpha
0.2(2, 2)
dis_model.add(Conv2D(filters=256, kernel_size=3))
dis_model.add(LeakyReLU(alpha=0.2))
dis_model.add(MaxPooling2D(pool_size=(2, 2)))
该层的输出张量的形状将为 (batch_size, 30, 30, 256)
。
(3, 3)
LeakyReLU
,带有alpha
0.2(2, 2)
dis_model.add(Conv2D(512, (3, 3)))
dis_model.add(LeakyReLU(alpha=0.2))
dis_model.add(MaxPooling2D(pool_size=(2, 2)))
该层的输出张量的形状将为 (batch_size, 13, 13, 512)
。
dis_model.add(Flatten())
来自平坦化层的张量的输出形状将为(batch_size, 18432,)
。
1024
神经元和alpha
0.2 作为激活函数的LeakyReLU
的密集层:dis_model.add(Dense(1024))
dis_model.add(LeakyReLU(alpha=0.2))
dis_model.add(Dense(1))
dis_model.add(Activation('tanh'))
网络将生成形状为(batch_size, 1)
的输出张量。 输出张量包含类的概率。
包裹在 Python 方法中的判别器网络的完整代码如下:
def get_discriminator(): dis_model = Sequential() dis_model.add( Conv2D(128, (5, 5), padding='same', input_shape=(64, 64, 3)) ) dis_model.add(LeakyReLU(alpha=0.2)) dis_model.add(MaxPooling2D(pool_size=(2, 2))) dis_model.add(Conv2D(256, (3, 3))) dis_model.add(LeakyReLU(alpha=0.2)) dis_model.add(MaxPooling2D(pool_size=(2, 2))) dis_model.add(Conv2D(512, (3, 3))) dis_model.add(LeakyReLU(alpha=0.2)) dis_model.add(MaxPooling2D(pool_size=(2, 2))) dis_model.add(Flatten()) dis_model.add(Dense(1024)) dis_model.add(LeakyReLU(alpha=0.2)) dis_model.add(Dense(1)) dis_model.add(Activation('sigmoid')) return dis_model
在本节中,我们已成功实现了判别器和生成器网络。 在下一部分中,我们将在“下载和准备动漫角色数据集”部分中准备的数据集上训练模型。
同样,训练 DCGAN 类似于训练朴素 GAN 网络。 这是一个四步过程:
我们将在本节中一步一步地进行这些步骤。
让我们从定义变量和超参数开始:
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 网络,我们需要将数据集加载到内存中,并且需要定义一种机制来加载成批的内存。 执行以下步骤以加载数据集:
cropped
文件夹中的图像。 正确指定目录的路径,以便glob.glob
方法可以创建其中所有文件的列表。 要读取图像,请使用scipy.misc
模块中的imread
方法。 以下代码显示了加载目录中所有图像的不同步骤:# Loading images all_images = []
for index, filename in enumerate(glob.glob('/Path/to/cropped/images/directory/*.*')):
image = imread(filename, flatten=False, mode='RGB')
all_images.append(image)
ndarray
。 最终ndarray
的形状将为(total_num_images, 64, 64, 3)
。 此外,标准化所有图像:# Convert to Numpy ndarray
X = np.array(all_images)
X = (X - 127.5) / 127.5
现在我们已经加载了数据集,接下来我们将看到如何构建和编译网络。
在本节中,我们将构建和编译训练所需的网络:
# 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)
gen_model = build_generator()
gen_model.compile(loss='binary_crossentropy', optimizer=gen_optimizer)
使用binary_crossentropy
作为生成器网络的loss
函数,并使用gen_optimizer
作为优化器。
dis_model = build_discriminator()
dis_model.compile(loss='binary_crossentropy', optimizer=dis_optimizer)
同样,使用binary_crossentropy
作为判别器网络的损失函数,并使用dis_optimizer
作为优化器。
创建和编译对抗模型的代码如下:
adversarial_model = Sequential()
adversarial_model.add(gen_model)
dis_model.trainable = False adversarial_model.add(dis_model)
当我们训练该网络时,我们不想训练判别器网络,因此在将其添加到对抗模型之前,使其变为不可训练的。
编译对抗模型,如下所示:
adversarial_model.compile(loss='binary_crossentropy', optimizer=gen_optimizer)
使用binary_crossentropy
作为损失函数,使用gen_optimizer
作为对抗模型的优化器。
在开始训练之前,添加 TensorBoard 以可视化显示损失,如下所示:
tensorboard = TensorBoard(log_dir="logs/{}".format(time.time()), write_images=True, write_grads=True, write_graph=True)
tensorboard.set_model(gen_model)
tensorboard.set_model(dis_model)
我们将针对指定的迭代次数训练网络,因此创建一个应运行指定次数的循环。 在每个周期内,我们将在大小为 128 的微型批量上训练网络。 计算需要处理的批量数量:
for epoch in range(epcohs):
print("Epoch is", epoch)
number_of_batches = int(X.shape[0] / batch_size)
print("Number of batches", number_of_batches)
for index in range(number_of_batches):
现在,我们将仔细研究训练过程。 以下几点说明了 DCGAN 训练中涉及的不同步骤:
(100, )
的潜向量。 将此潜向量馈入未经训练的生成器网络。 生成器网络将生成伪样本,我们将其用于训练判别器网络。执行以下步骤来训练判别器网络:
z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))
要对值进行采样,请使用 Numpy 库中np.random
模块中的normal()
方法。
image_batch = X[index * batch_size:(index + 1) * batch_size]
generated_images = gen_model.predict_on_batch(z_noise)
y_real = np.ones(batch_size) - np.random.random_sample(batch_size) * 0.2 y_fake = np.random.random_sample(batch_size) * 0.2
dis_loss_real = dis_model.train_on_batch(image_batch, y_real)
dis_loss_fake = dis_model.train_on_batch(generated_images, y_fake)
d_loss = (dis_loss_real+dis_loss_fake)/2 print("d_loss:", d_loss)
到目前为止,我们一直在训练判别器网络。 在下一部分中,让我们训练生成器网络。
为了训练生成器网络,我们必须训练对抗模型。 当我们训练对抗模型时,它只训练生成器网络,而冻结判别器网络。 由于我们已经训练过判别器网络,因此我们不会对其进行训练。 执行以下步骤来训练对抗模型:
z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))
g_loss = adversarial_model.train_on_batch(z_noise, [1] * batch_size)
我们在一批噪声向量和实数标签上训练对抗模型。 在这里,实数标签是一个所有值均等于 1 的向量。我们还在训练生成器,以欺骗判别器网络。 为此,我们为它提供一个向量,该向量的所有值均等于 1。在此步骤中,生成器将接收来自生成器网络的反馈,并相应地进行改进。
print("g_loss:", g_loss)
有一种被动的方法可以评估训练过程。 每 10 个周期后,生成伪造图像并手动检查图像质量:
if epoch % 10 == 0:
z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))
gen_images1 = gen_model.predict_on_batch(z_noise)
for img in gen_images1[:2]:
save_rgb_img(img, "results/one_{}.png".format(epoch))
这些图像将帮助您决定是继续训练还是尽早停止训练。 如果生成的高分辨率图像的质量良好,请停止训练。 或者继续训练,直到您的模型变好为止。
我们已经成功地在动画角色数据集上训练了 DCGAN 网络。 现在我们可以使用该模型生成动漫人物图像。
为了生成图像,我们需要一个从潜在空间采样的噪声向量。 Numpy 有一种称为uniform()
的方法,可以根据均匀分布生成向量。 让我们看看如何在以下步骤中生成图像:
(batch_size, 100)
的噪声向量:z_noise = np.random.normal(0, 1, size=(batch_size, z_shape))
predict_on_batch
方法生成图像。 将上一步中创建的噪声向量馈入其中:gen_images = gen_model.predict_on_batch(z_noise)
results
的目录来存储生成的图像:imsave('results/image_{}.jpg'.format(epoch),gen_images[0])
现在,您可以打开这些生成的图像以测量生成的模型的质量。 这是一种评估模型表现的被动方法。
在 Keras 中保存模型只需要一行代码。 要保存生成器模型,请添加以下行:
# Specify the path for the generator model
gen_model.save("directory/for/the/generator/model.h5")
同样,通过添加以下行来保存判别器模型:
# Specify the path for the discriminator model
dis_model.save("directory/for/the/discriminator/model.h5")
在将网络训练了 100 个时间段后,生成器将开始生成合理的图像。 让我们看一下生成的图像。
在 100 个周期之后,图像显示如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ybF9YUlh-1681652801327)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/bad2e9c8-167e-436b-9078-4fa53cffc0a2.png)]
在 200 个周期之后,图像显示如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CaseySAv-1681652801327)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/82a9152b-d3fb-467b-837f-96af60995452.png)]
要生成非常好的图像,请将网络训练 10,000 个周期。
为了可视化训练的损失,请按以下方式启动 TensorBoard 服务器:
tensorboard --logdir=logs
现在,在浏览器中打开localhost:6006
。 Tensorboard 的标量部分包含两种损失的图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ObBR232L-1681652801327)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/590d57c0-43e3-4ca8-ad30-8bd20887ecc5.png)]
这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止训练,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。
Tensorboard 的GRAPHS
部分包含两个网络的图。 如果网络表现不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LXsBK9yD-1681652801327)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/9bdd11ae-60a0-4d18-b9ba-9e9a46e9514f.png)]
超参数是模型的属性,在模型训练期间是固定的。 不同的参数可以具有不同的精度。 让我们看一下一些常用的超参数:
在“使用 Keras 实现 DCGAN”的部分中,学习率是固定的:生成器模型为 0.0005,判别器模型为 0.0005。 批量大小为 128。调整这些值可能会导致我们创建更好的模型。 如果您的模型没有生成合理的图像,请尝试更改这些值,然后再次运行模型。
可以为不同的使用案例定制 DCGAN。 DCGAN 的各种实际应用包括:
动画角色的生成:目前,动画师使用计算机软件手动绘制字符,有时还绘制在纸上。 这是一个手动过程,通常需要很多时间。 使用 DCGAN,可以在更短的时间内生成新的动漫角色,从而改善了创作过程。
数据集的扩充:如果您想训练一个监督的机器学习模型,要训练一个好的模型,您将需要一个大的数据集。 DCGAN 可以通过扩展现有数据集来提供帮助,因此可以增加监督模型训练所需的数据集大小。
MNIST 字符的生成:MNIST 数据集包含 60,000 张手写数字图像。 要训练复杂的监督学习模型,MNIST 数据集是不够的。 DCGAN 一旦受过训练,将生成可以添加到原始数据集中的新数字。
人脸生成:DCGAN 使用卷积神经网络,非常擅长生成逼真的图像。
特征提取器:训练后,可以使用判别器从中间层提取特征。 这些提取的特征在样式迁移和人脸识别等任务中很有用。 样式迁移涉及生成图像的内部表示,用于计算样式和内容损失。 请参阅以下论文,以了解有关样式迁移的更多信息。
在本章中,我们介绍了深度卷积生成对抗网络。 我们从基本介绍 DCGAN 开始,然后深入探讨了 DCGAN 网络的架构。 之后,我们设置项目并安装必要的依赖项。 然后,我们研究了下载和准备数据集所需的不同步骤。 然后,我们准备了网络的 Keras 实现,并在我们的数据集中对其进行了训练。 经过训练后,我们将其用于生成新的动漫角色。 我们还探讨了 DCGAN 在实际用例中的不同应用。
在下一章中,我们将研究用于高分辨率图像生成的 SRGAN。
超分辨率生成对抗网络或 SRGAN ,是生成对抗网络(GAN), 低分辨率图像中的高分辨率图像,具有更好的细节和更高的质量 。 CNN 较早用于产生高分辨率图像,该图像可以更快地训练并达到高水平的精度。 但是,在某些情况下,它们无法恢复更精细的细节,并且通常会生成模糊的图像。 在本章中,我们将在 Keras 框架中实现一个 SRGAN 网络,该网络将能够生成高分辨率图像。 SRGAN 在标题为《使用生成对抗网络的逼真的单图像超分辨率》的论文中引入,作者是 Christian Ledig,Lucas Theis,Ferenc Huszar, Jose Caballero,Andrew Cunningham 等,可以在以下链接中找到。
在本章中,将涵盖以下主题:
与其他 GAN 一样,SRGAN 包含一个生成器网络和一个判别器网络。 两个网络都很深。 这两个网络的功能指定如下:
64x64x3
的低分辨率图像,并且在一系列卷积和上采样层之后, 生成形状为256x256x3
的超分辨率图片在 SRGAN 中,这两个网络都是深度卷积神经网络。 它们包含卷积层和上采样层。 每个卷积层之后是批量归一化操作和一个激活层。 我们将在以下各节中详细探讨网络。 下图显示了 SRGAN 的架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GOueF0Pp-1681652801327)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/fe3eced0-c452-4b7a-9f6d-dd8228048ab9.png)]
在以下各节中,让我们详细了解网络的架构。
如前一节所述,生成器网络是深度卷积神经网络。 生成器网络包含以下块:
让我们一一探讨这些块:
层名称 | 超参数 | 输入形状 | 输出形状 |
---|---|---|---|
2D 卷积层 | Filters=64, kernel_size=3, strides=1, padding='same', activation='relu' | (64, 64, 3) | (64, 64, 64) |
层名称 | 超参数 | 输入形状 | 输出形状 |
---|---|---|---|
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 个具有上述配置的残差块。
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 上采样层 | 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) |
(256, 256, 3)
的图像。 最后一层的配置如下:层名称 | 超参数 | 输入形状 | 输出形状 |
---|---|---|---|
2D 卷积层 | Filters=3, kernel_size=9, strides=1, padding='same', activation='tanh' | (256, 256, 256) | (256, 256, 3) |
这些超参数最适合 Keras 框架。 如果使用任何其他框架,请相应地对其进行修改。
判别器网络也是一个深度卷积网络。 它包含八个卷积块,后跟两个密集(完全连接)层。 每个卷积块后面都有一个批量归一化层。 网络的末端有两个密集层,它们充当分类块。 最后一层预测图像属于真实数据集或伪数据集的概率。 判别器网络的详细配置如下表所示:
层名称 | 超参数 | 输入形状 | 输出形状 |
---|---|---|---|
输入层 | 没有 | (256, 256, 3) | (256, 256, 3) |
2D 卷积层 | filter= 64, kernel_size = 3, stride= 1, padding='same', activcation='leakyrelu' | (256, 256, 3) | (256, 256, 64) |
2D 卷积层 | filter= 64, kernel_size = 3, stride= 2, padding='same', activcation='leakyrelu' | (256, 256, 64) | (128, 128, 64) |
批量规范化层 | momentum= 0.8 | (128, 128, 64) | (128, 128, 64) |
2D 卷积层 | filter= 128, kernel_size = 3, stride= 1, padding='same', activcation='leakyrelu' | (128, 128, 64) | (128, 128, 128) |
批量规范化层 | momentum= 0.8 | (128, 128, 128) | (128, 128, 128) |
2D 卷积层 | filter= 128, kernel_size = 3, stride= 2, padding='same', activcation='leakyrelu' | (128, 128, 128) | (64, 64, 128) |
批量规范化层 | momentum= 0.8 | (64, 64, 128) | (64, 64, 128) |
2D 卷积层 | filter= 256, kernel_size = 3, stride= 1, padding='same', activcation='leakyrelu' | (64, 64, 128) | (64, 64, 256) |
批量规范化层 | momentum= 0.8 | (64, 64, 256) | (64, 64, 256) |
2D 卷积层 | filter= 256, kernel_size = 3, stride= 2, padding='same', activcation='leakyrelu' | (64, 64, 256) | (32, 32, 256) |
现在,我们对这两个网络的架构有了清晰的了解。 在下一部分中,让我们看一下训练 SRGAN 所需的目标函数。
要训练 SRGAN,有一个目标函数或损失函数,我们需要将其最小化以训练模型。 SRGAN 的目标函数称为感知损失函数,它是两个损失函数的加权和,如下所示:
在以下各节中,让我们详细研究内容损失和对抗损失。
内容损失有两种类型,如下所示:
让我们详细讨论这些损失。
内容损失是在真实图像的每个像素值与生成的图像的每个像素值之间计算的均方误差。 像素级 MSE 损失计算出生成的图像与实际图像有多大差异。 像素级 MSE 损失的计算公式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HTs0aZcZ-1681652801328)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/3b2c1077-9f9a-4cda-8670-cd8d737041f8.png)]
在此,G[θ[G]](I^(LR))
表示由所生成的网络所生成的高分辨率图像。 I^(HR)
代表从真实数据集中采样的高分辨率图像。
VGG 损失是另一个内存损失函数,可应用于生成的图像和真实图像。 VGG19 是一种非常流行的深度神经网络,主要用于图像分类。 VGG19 由 Simonyan 和 Zisserman 在他们的论文《用于大规模图像识别的超深度卷积网络》中引入,可在这里获得。 预训练的 VGG19 网络的中间层用作特征提取器,可用于提取生成的图像和真实图像的特征映射。 VGG 损失基于这些提取的特征映射。 计算为生成的图像和真实图像的特征映射之间的欧几里得距离。 VGG 损失的公式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GlvrEO5U-1681652801328)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/c854836e-df68-4886-bb7c-8577a41d732c.png)]
在此,φ[i, j]
表示由 VGG19 网络生成的特征映射。 φ[i, j](I^(HR))
代表提取的真实图像特征映射,φ[i, j](G[θ[G]](I^(LR)))
代表提取的高分辨率图像生成的特征映射。 整个方程表示生成图像和真实图像的特征映射之间的欧式距离。
前述内容损失中的任何一种都可以用于训练 SRGAN。 对于我们的实现,我们将使用 VGG 损失。
根据判别器网络返回的概率计算对抗损失。 在对抗模型中,判别器网络被馈送有由生成的网络生成的生成的图像。 对抗损失可以用以下等式表示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gZ8PgVOg-1681652801328)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/cf90c58f-3995-48a8-b78a-0ab81cb7ae9d.png)]
这里,G[θG](I^(LR))
是所生成的图像 , D[θD](G[θG](I^(LR)))
表示所生成的图像是真实图像的概率。
知觉损失函数是内容损失和对抗损失的加权和,表示为以下方程式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k7AxCrt4-1681652801328)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/4e2039a0-5a7e-4038-b68d-f19fa142d2d8.png)]
在此,总的感知损失由l^(SR)
表示。 l^(SR)[X]
是内容损失,可以是像素级 MSE 损失或 VGG 损失。
通过最小化感知损失值,生成器网络试图欺骗判别器。 随着感知损失的值减小,生成器网络开始生成更逼真的图像。
现在开始进行该项目。
如果尚未使用所有章节的完整代码克隆存储库,请立即克隆存储库。 下载的代码有一个名为Chapter05
的目录,其中包含本章的全部代码。 执行以下命令来设置项目:
cd Generative-Adversarial-Networks-Projects
Chapter05
:cd Chapter05
virtualenv venv
virtualenv venv -p python3 # Create a virtual environment using
python3 interpreter
virtualenv venv -p python2 # Create a virtual environment using
python2 interpreter
我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。
source venv/bin/activate
激活虚拟环境后,所有其他命令将在此虚拟环境中执行。
requirements.txt
文件中提供的所有库:pip install -r requirements.txt
您可以参考 README.md
文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。
在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一节中,让我们研究数据集。 我们将探索下载和格式化数据集的各种步骤。
在本章中,我们将使用大型 CelebFaces 属性(CelebA)数据集,该数据集可从这里获得。 数据集包含 202、599 名名人的人脸图像。
该数据集仅可用于非商业研究目的,不能用于商业目的。 如果您打算将数据集用于商业目的,请寻求图像所有者的许可。
我们将使用 CelebA
数据集训练我们的 SRGAN 网络。 执行以下步骤下载和提取数据集:
https://www.dropbox.com/sh/8oqt9vytwxb3s4r/AAB06FXaQRUNtjW9ntaoPGvCa?dl=0
img_align_celeba.zip
中提取图像:unzip img_align_celeba.zip
现在,我们已经下载并提取了数据集。 现在,我们可以开始进行 SRGAN 的 Keras 实现。
正如我们所讨论的,SRGAN 在 Imagenet 数据集上具有三个神经网络,一个生成器,一个判别器和一个预训练的 VGG19 网络。 在本节中,我们将编写所有网络的实现。 让我们从实现生成器网络开始。
在开始编写实现之前,创建一个名为main.py
的 Python 文件并导入基本模块,如下所示:
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 中实现生成器网络:
residual_blocks = 16 momentum = 0.8 input_shape = (64, 64, 3)
input_layer = Input(shape=input_shape)
输入层获取形状为(64, 64, 3)
的输入图像,并将其传递到网络中的下一层。
64
9
1
same
relu:
gen1 = Conv2D(filters=64, kernel_size=9, strides=1, padding='same', activation='relu')(input_layer)
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
residual_block
函数添加 16 个残差块:res = residual_block(gen1)
for i in range(residual_blocks - 1):
res = residual_block(res)
前残差块的输出进入第一个残差块。 第一个残差块的输出将到达第二个残差块,依此类推,直到第 16 个残差块。
64
3
1
same
gen2 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(res)
gen2 = BatchNormalization(momentum=momentum)(gen2)
Add
层,以获取残留前块的输出gen1
和后残差块的输出gen2
之和。 该层生成另一个类似形状的张量。 有关更多详细信息,请参考“ 生成器网络的架构”部分。gen3 = Add()([gen2, gen1])
2
256
3
1
same
PReLU:
gen4 = UpSampling2D(size=2)(gen3)
gen4 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen4)
gen4 = Activation('relu')(gen4)
2
256
3
1
same
PReLU:
gen5 = UpSampling2D(size=2)(gen4)
gen5 = Conv2D(filters=256, kernel_size=3, strides=1, padding='same')(gen5)
gen5 = Activation('relu')(gen5)
3
(等于通道数)9
1
same
tanh:
gen6 = Conv2D(filters=3, kernel_size=9, strides=1, padding='same')(gen5)
output = Activation('tanh')(gen6)
一旦定义了网络中的所有层,就可以创建 Keras 模型。 我们已经使用 Keras 的函数式 API 定义了 Keras 顺序图。 让我们通过指定网络的输入和输出来创建 Keras 模型。
model = Model(inputs=[input_layer], outputs=[output], name='generator')
我们已经成功地为生成器网络创建了 Keras 模型。 现在,将生成器网络的整个代码包装在 Python 函数中,如下所示:
def build_generator(): """ Create a generator network using the hyperparameter values defined below :return: """ residual_blocks = 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 框架的函数式 API 创建 Keras 模型”中探讨了判别器网络的架构。 让我们首先在 Keras 框架中编写判别器网络的层。
执行以下步骤以在 Keras 中实现判别器网络:
leakyrelu_alpha = 0.2 momentum = 0.8 input_shape = (256, 256, 3)
input_layer = Input(shape=input_shape)
64
3
1
same
LeakyReLU
,alpha
等于 0.2:dis1 = Conv2D(filters=64, kernel_size=3, strides=1, padding='same')(input_layer)
dis1 = LeakyReLU(alpha=leakyrelu_alpha)(dis1)
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:# 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)
1024
LeakyReLU
,alpha
等于 0.2:dis9 = Dense(units=1024)(dis8)
dis9 = LeakyReLU(alpha=0.2)(dis9)
output = Dense(units=1, activation='sigmoid')(dis9)
model = Model(inputs=[input_layer], outputs=[output],
name='discriminator')
将判别器网络的整个代码如下包装在函数中:
def build_discriminator(): """ Create a discriminator network using the hyperparameter values defined below :return: """ leakyrelu_alpha = 0.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 网络的目的是提取生成的图像和真实图像的特征映射。 在本节中,我们将在 Keras 中使用预训练的权重构建和编译 VGG19 网络:
input_shape = (256, 256, 3)
vgg = VGG19(weights="imagenet")
vgg.outputs = [vgg.layers[9].output]
input_tensor
,这将是我们对 VGG19 网络的符号输入,如下所示:input_layer = Input(shape=input_shape)
features = vgg(input_layer)
model
并为网络指定inputs
和outputs
:model = Model(inputs=[input_layer], outputs=[features])
最后,将 VGG19 模型的整个代码包装在一个函数中,如下所示:
def build_vgg(): """ Build the VGG network to extract image features """ input_shape = (256, 256, 3) # Load a pre-trained VGG19 model trained on 'Imagenet' 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 的组合网络。 在本节中,我们将创建一个对抗网络。
执行以下步骤来创建对抗网络:
input_low_resolution = Input(shape=(64, 64, 3))
对抗网络将收到(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])
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 网络:
# 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)
beta_1
等于0.5
的 Adam 优化器:# Common optimizer for all networks common_optimizer = Adam(0.0002, 0.5)
在本节中,我们将完成构建和编译网络所需的不同步骤:
vgg = build_vgg()
vgg.trainable = False vgg.compile(loss='mse', optimizer=common_optimizer, metrics=
['accuracy'])
要编译 VGG19,请使用mse
作为损失,使用accuracy
作为度量,并使用common_optimizer
作为优化器。 编译网络之前,请先禁用训练,因为我们不想训练 VGG19 网络。
discriminator
网络,如下所示:discriminator = build_discriminator()
discriminator.compile(loss='mse', optimizer=common_optimizer,
metrics=['accuracy'])
要编译判别器网络,使用 mse
作为损失, accuracy
作为度量,并使用 common_optimizer
作为优化器。
generator = build_generator()
input_high_resolution = Input(shape=high_resolution_shape)
input_low_resolution = Input(shape=low_resolution_shape)
generated_high_resolution_images = generator(input_low_resolution)
使用 VGG19 提取生成图像的特征映射:
features = vgg(generated_high_resolution_images)
使判别器网络不可训练,因为我们不想在对抗模型训练期间训练判别器模型 :
discriminator.trainable = False
probs = discriminator(generated_high_resolution_images)
在此,probs
表示所生成的图像属于真实数据集的概率。
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]
作为损失权重。
Tensorboard
以可视化训练损失并可视化网络图:tensorboard = TensorBoard(log_dir="logs/".format(time.time()))
tensorboard.set_model(generator)
tensorboard.set_model(discriminator)
for epoch in range(epochs):
print("Epoch:{}".format(epoch))
完成此步骤后,所有代码都将在此for
循环内。
high_resolution_images, low_resolution_images =
sample_images(data_dir=data_dir,
batch_size=batch_size,low_resolution_shape=low_resolution_shape, high_resolution_shape=high_resolution_shape)
sample_images
函数的代码如下。 它具有很强的描述性,通过阅读可以理解。 它包含加载和调整图像大小并生成高分辨率和低分辨率图像的不同步骤:
def sample_images(data_dir, batch_size, high_resolution_shape, low_resolution_shape): # Make a list of all images inside the data 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)
[-1, 1]
之间的范围,如下所示: high_resolution_images = high_resolution_images / 127.5 - 1.
low_resolution_images = low_resolution_images / 127.5 - 1.
将像素值转换为 -1 到 1 的范围非常重要。我们的生成器网络的末尾有tanh
。 tanh
激活函数将值压缩到相同范围。 在计算损失时,必须使所有值都在同一范围内。
本节中给出的步骤显示了如何训练判别器网络。 这是最后一系列步骤的延续:
generator
网络生成伪造的高分辨率图像:generated_high_resolution_images =
generator.predict(low_resolution_images)
real_labels = np.ones((batch_size, 16, 16, 1))
fake_labels = np.zeros((batch_size, 16, 16, 1))
d_loss_real = discriminator.train_on_batch(high_resolution_images,
real_labels)
d_loss_fake = discriminator.train_on_batch(generated_high_resolution_images, fake_labels)
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
现在,我们添加了代码来训练判别器网络。 接下来,添加代码以训练对抗模型,从而训练生成器网络。
本节中给出的步骤显示了如何训练生成器网络。 这是最后一系列步骤的延续:
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.
image_features = vgg.predict(high_resolution_images)
g_loss = adversarial_model.train_on_batch([low_resolution_images, high_resolution_images],
[real_labels, image_features])
write_log(tensorboard, 'g_loss', g_loss[0], epoch)
write_log(tensorboard, 'd_loss', d_loss[0], epoch)
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 64 x 3
的低分辨率图像,并将其传递给generator.predict()
函数,该函数将生成高分辨率图像。
在 Keras 中保存模型只需要一行代码。 要保存generator
模型,请添加以下行:
# Specify the path for the generator model
gen_model.save("directory/for/the/generator/model.h5")
同样,通过添加以下行来保存discriminator
模型:
# Specify the path for the discriminator model
dis_model.save("directory/for/the/discriminator/model.h5")
经过大量时间后,生成器将开始生成良好的图像。 让我们看一下生成的图像:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BONfufBj-1681652801329)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/fcd5c713-f0e8-4b2b-b914-06b9cfdb3600.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xltyNNiI-1681652801329)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/f099cb7a-a2b7-476b-9476-f7e47eb947b4.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ubtPDh2y-1681652801329)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/96ef5573-9de4-4bf6-a6ec-848e6f3c622c.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ad5qSoYa-1681652801329)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/fd0ed18a-8cd7-4200-a3e9-81c6f771302a.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t4f0a9cr-1681652801330)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/d58519f5-35a1-4b3b-831f-163b995eeabd.png)]
要生成非常好的图像,请将网络训练 30,000-50,000 个周期。
要显示训练损失,请启动,,tensorboard
,,服务器,如下所示:
tensorboard --logdir=logs
现在,在浏览器中打开 localhost:6006
。 TensorBoard 的标量部分包含两种损失的图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4etk5xVC-1681652801330)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/777f3571-0412-4464-bb22-c1445c488875.png)]
这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止训练,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 尝试使用超参数,然后选择一组您认为可能会提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。
TensorBoard 的 GRAPHS 部分包含两个网络的图。 如果网络表现不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的运算:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CqU8hgv2-1681652801330)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/gan-proj/img/5d8ca1c2-04b5-4106-b5c6-5c96ca28ce46.png)]
现在,让我们看一下 SRGAN 的实际应用:
在本章中,我们首先介绍 SRGAN。 然后,我们研究了生成器和判别器网络的架构。 后来,我们执行了该项目所需的设置。 然后,我们收集并探索了数据集。 之后,我们在训练 SRGAN 之前先在 Keras 中实现了该项目,评估了训练后的 SRGAN 网络,并使用超参数优化技术对训练后的模型进行了优化。 最后,我们简要介绍了 SRGAN 的一些不同应用。
在下一章中,我们将介绍 StackGAN 及其不同的应用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。