提交 d167b170 编写于 作者: W wizardforcel

2020-09-07 16:55:05

上级 56697002
......@@ -56,7 +56,7 @@
尽管此示例涵盖了机器翻译,但是序列到序列学习的其他应用程序却以相同的方式工作。
# 人物与文字
# 字符与文本
可以在字符级别或单词级别建立序列到序列模型。 单词级序列到序列模型将单词作为输入的原子单位,而字符级模型将字符作为输入的原子单位。
......@@ -66,7 +66,7 @@
对于本章中提出的问题,我将使用字符级模型,因为我重视您的 AWS 预算。 转换为单词非常简单,其中大部分复杂性都在数据准备中,这是留给读者的练习。
# 老师强迫
# 监督强迫
如上图所示,当预测序列`y[t(n)]`某个位置的输出时,我们使用`y[t(n-1)]`作为 LSTM 的输入。 然后,我们使用此时间步骤的输出来预测`y[t(n+1)]`
......@@ -120,7 +120,7 @@ Go now. Vas-y maintenant.
由于我们正在构建一个字符级序列到序列模型,因此需要将数据加载到内存中,然后对每个输入和输出在字符级进行热编码。 那是困难的部分。 让我们接下来做。
# 加载数据
# 加载数据
加载此数据涉及很多工作。 阅读本文时,您可能想参考代码块。
......@@ -132,7 +132,7 @@ Go now. Vas-y maintenant.
* 我们将每一行分为英语输入和其各自的法语翻译。 这些存储在列表`input_texts``target_texts`中。
* 最后,我们将输入文本和目标文本的每个字符添加到一个集合中。 这些集称为`input_characters``target_characters`。 当需要对短语进行热编码时,我们将使用这些集合。
循环完成后,我们会将字符集转换为排序列表。 我们还将创建名为`num_encoder_tokens``num_decoder_tokens`的变量,以保存每个列表的大小。 稍后我们也将需要这些以进行一种热编码。
循环完成后,我们会将字符集转换为排序列表。 我们还将创建名为`num_encoder_tokens``num_decoder_tokens`的变量,以保存每个列表的大小。 稍后我们也将需要这些以进行热编码。
为了将输入和目标输入矩阵,我们需要像上一章一样,将短语填充到最长短语的长度。 为此,我们需要知道最长的短语。 我们将其存储在`max_encoder_seq_length``max_decoder_seq_length`中,如以下代码所示:
......@@ -177,11 +177,11 @@ def load_data(num_samples=50000, start_char='\t', end_char='\n', data_path='data
加载数据后,我们将在字典中返回所有这些信息,这些信息可以传递给一个函数,该函数将对每个短语进行热编码。 让我们接下来做。
# 一种热编码
# 热编码
在此功能中,我们将使用刚刚构建的字典,并对每个短语的文本进行热编码。
一旦完成,我们将剩下三个字典。 它们每个的尺寸为`[文本数 * 最大序列长度 * 标记]`。 如果您停顿一下,回想一下第 10 章“使用单词嵌入从零开始训练 LSTM”的更简单的时间,您会发现这确实与我们在其他 NLP 模型中使用的相同,我们在输入端完成它。 我们将使用以下代码定义一种热编码:
一旦完成,我们将剩下三个字典。 它们每个的尺寸为`[文本数 * 最大序列长度 * 标记]`。 如果您停顿一下,回想一下第 10 章“使用单词嵌入从零开始训练 LSTM”的更简单的时间,您会发现这确实与我们在其他 NLP 模型中使用的相同,我们在输入端完成它。 我们将使用以下代码定义热编码:
```py
def one_hot_vectorize(data):
......@@ -237,7 +237,7 @@ def one_hot_vectorize(data):
终于完成了数据准备,因此我们可以开始构建序列到序列的网络架构。
# 训练网络架构
# 用于训练的网络架构
在此示例中,我们实际上将使用两种单独的架构,一种用于训练,另一种用于推理。 我们将从推理模型训练中使用训练过的层。 虽然实际上我们为每种架构使用了相同的部分,但是为了使事情更清楚,我将分别展示每个部分。 以下是我们将用来训练网络的模型:
......@@ -283,7 +283,7 @@ decoder_outputs, _, _ = LSTM(lstm_units, return_sequences=True,
最后,我们将定义训练模型,我将其创造性地称为`model`,该模型将`encoder_input_data``decoder_input`数据作为输入并预测`decoder_output_data`
# 网络架构(用于推理)
# 用于推理的网络架构
为了在给定输入序列的情况下预测整个序列,我们需要稍微重新安排一下架构。 我怀疑在 Keras 的未来版本中,这将变得更简单,但是从今天起这是必需的步骤。
......@@ -403,7 +403,7 @@ decoder_model.save('char_s2s_decoder.h5')
4. 将状态和`<SOS>`字符`'\t'`发送到解码器。
5. 循环,获取每个下一个字符,直到解码器生成`<EOS>``'\n'`
# 加载数据
# 加载数据
我们可以从训练脚本中导入`load_data``one_hot_vectorize`函数,以相同的方式调用这些方法,如以下代码所示:
......@@ -526,7 +526,7 @@ while not stop_condition:
我们的解码器将遵循此过程,直到生成解码序列为止。
# 翻译
# 翻译
只是为了好玩,我在这里提供了一些尝试的翻译。 所有这些都来自训练集的前面,这意味着我正在对`training`数据集进行预测,因此这些转换可能会使模型看起来比实际更好。
......
......@@ -86,20 +86,20 @@
这种天真的在线学习的缺点有些明显,有两个方面:
* 验之后,我们就会放弃经验。
* 验之后,我们就会放弃经验。
* 我们所经历的经验彼此高度相关,我们将过度适应最新的经验。 有趣的是,这也是人类遭受的苦难,称为可用性偏差。
我们可以通过使用内存和体验重播来解决这些问题。
我们可以通过使用内存和经验重放来解决这些问题。
# 记忆和体验重播
# 记忆和经验重放
当我们引入有限存储空间的概念时,可以找到针对这两个问题的巧妙解决方案,该存储空间用于存储代理具有的一组经验。 在每个状态下,我们都可以借此机会记住状态,行动和奖励。 然后,代理可以通过从内存中采样一个随机小批量并使用该小批量更新 DQN 权重,定期重播这些体验。
当我们引入有限存储空间的概念时,可以找到针对这两个问题的巧妙解决方案,该存储空间用于存储代理具有的一组经验。 在每个状态下,我们都可以借此机会记住状态,行动和奖励。 然后,代理可以通过从内存中采样一个随机小批量并使用该小批量更新 DQN 权重,定期重放这些经验。
这种重机制使代理能够以一般的方式从更长远的经验中学习,因为它是从内存中的那些经验中随机采样的,而不是仅使用最近的经验来更新整个网络。
这种重机制使代理能够以一般的方式从更长远的经验中学习,因为它是从内存中的那些经验中随机采样的,而不是仅使用最近的经验来更新整个网络。
# 开发与探索
# 利用与探索
通常,我们希望代理遵循*贪婪*策略,这意味着我们希望代理采取具有最大`Q`值的操作。 在学习网络的同时,我们不希望它总是贪婪地表现。 如果这样做,它将永远不会探索新的选择,也不会学习新的东西。 因此,我们需要我们的代理偶尔执行不符合规定的政策
通常,我们希望代理遵循*贪婪*策略,这意味着我们希望代理采取具有最大`Q`值的操作。 在学习网络的同时,我们不希望它总是贪婪地表现。 如果这样做,它将永远不会探索新的选择,也不会学习新的东西。 因此,我们需要我们的代理偶尔执行不符合规定的策略
平衡这种探索的最佳方法是一个持续不断的研究主题,并且已经使用了很长时间。 但是,我们将使用的方法非常简单。 代理每次执行操作时,我们都会生成一个随机数。 如果该数字等于或小于某个阈值`ε`,则代理将采取随机措施。 这称为 **ε 贪婪策略**
......@@ -107,7 +107,7 @@
综上所述,我们有一个**线性退火 ε - 贪心 Q 策略**,说起来既简单又有趣。
# 深心
# DeepMind
至少没有提到 Mnih 等人的论文[《和深度强化学习一起玩 Atari》](https://www.cs.toronto.edu/~vmnih/docs/dqn.pdf),就不会完成关于强化学习的讨论。 然后是 DeepMind,现在是 Google。 在这篇具有里程碑意义的论文中,作者使用了卷积神经网络来训练深度 Q 网络来玩 Atari 2600 游戏。 他们从 Atari 2600 游戏中获取原始像素输出,将其缩小一点,将其转换为灰度,然后将其用作网络的状态空间输入。 为了使计算机了解屏幕上对象的速度和方向,他们使用了四个图像缓冲区作为深度 Q 网络的输入。
......@@ -141,7 +141,7 @@ pip install gym[atari]
pip install gym[Box2D]
```
# 使用 OpenAI 体育馆
# 使用 OpenAI Gym
使用 OpenAI 体育场确实使深度强化学习变得容易。 Keras-RL 将完成大部分艰苦的工作,但是我认为值得单独走遍体育馆,这样您才能了解代理如何与环境互动。
......@@ -160,14 +160,14 @@ next_state, reward, done, info = env.step(action)
该代理可以通过使用循环与环境进行交互来播放整个情节。 此循环的每次迭代都对应情节中的单个步骤。 当代理从环境接收到“完成”信号时,情节结束。
# 在 Keras 中建立强化学习代理
# 在 Keras 中建立强化学习智能体
好消息,我们终于可以开始编码了。 在本部分中,我将演示两种名为 **CartPole****Lunar Lander** 的 Keras-RL 代理。 我选择这些示例是因为它们不会消耗您的 GPU 和云预算来运行。 它们可以很容易地扩展到 Atari 问题,我在本书的 Git 存储库中也包括了其中之一。 您可以照常在`Chapter12`文件夹中找到所有这些代码。 让我们快速讨论一下这两种环境:
* **CartPole**:CartPole 环境由平衡在推车上的杆组成。 代理商必须学习如何在立柱下方的推车移动时垂直平衡立柱。 给智能体指定了推车的位置,推车的速度,杆的角度和杆的旋转速度作为输入。 代理可以在推车的任一侧施加力。 如果电线杆与垂直线的夹角下降超过 15 度,我们的经纪人就此告吹。
* **Lunar Lander**:Lunar Lander 的环境更具挑战性。 特工必须将月球着陆器降落在着陆垫上。 月亮的表面会发生变化,着陆器的方位也会在每个情节发生变化。 该代理将获得一个八维数组,用于描述每个步骤中的世界状态,并且可以在该步骤中执行四个操作之一。 代理可以选择不执行任何操作,启动其主引擎,启动其左向引擎或启动其右向引擎。
# 购物车杆
# CartPole
CartPole 代理将使用一个相当适度的神经网络,即使没有 GPU,您也应该能够相当迅速地进行训练。 我们将一如既往地从模型架构开始。 然后,我们将定义网络的内存,探索策略,最后训练代理。
......@@ -198,11 +198,11 @@ Keras-RL 为我们提供了一个名为`rl.memory.SequentialMemory`的类,该
memory = SequentialMemory(limit=50000, window_length=1)
```
我们需要为此存储对象指定一个最大大小,它是一个超参数。 随着新的体验添加到该内存中并变得完整,旧的体验会被遗忘。
我们需要为此存储对象指定一个最大大小,它是一个超参数。 随着新的经验添加到该内存中并变得完整,旧的经验会被遗忘。
# 政策
# 策略
Keras-RL 提供了一个称为`rl.policy.EpsGreedyQPolicy`的 ε-贪婪 Q 策略,我们可以用来平衡勘探和开发。 当代理程序向世界前进时,我们可以使用`rl.policy.LinearAnnealedPolicy`来衰减`ε`,如以下代码所示:
Keras-RL 提供了一个称为`rl.policy.EpsGreedyQPolicy`的 ε-贪婪 Q 策略,我们可以用来平衡利用与探索。 当代理程序向世界前进时,我们可以使用`rl.policy.LinearAnnealedPolicy`来衰减`ε`,如以下代码所示:
```py
policy = LinearAnnealedPolicy(EpsGreedyQPolicy(), attr='eps', value_max=1., value_min=.1, value_test=.05, nb_steps=10000)
......@@ -210,7 +210,7 @@ policy = LinearAnnealedPolicy(EpsGreedyQPolicy(), attr='eps', value_max=1., valu
在这里我们要说的是,我们要从`ε`的值 1 开始,并且不小于 0.1,同时测试我们的随机数是否小于 0.05。 我们将步数设置为 .1 到 10,000 之间,Keras-RL 为我们处理衰减数学。
# 代理商
# 智能体
定义了模型,内存和策略后,我们现在就可以创建一个深度 Q 网络代理,并将这些对象发送给该代理。 Keras RL 提供了一个称为`rl.agents.dqn.DQNAgent`的代理类,我们可以为此使用它,如以下代码所示:
......@@ -223,7 +223,7 @@ dqn.compile(Adam(lr=1e-3), metrics=['mae'])
此时,其中两个参数`target_model_update``nb_steps_warmup`可能还不熟悉:
* `nb_steps_warmup`:确定我们开始进行体验重播之前需要等待的时间,如果您还记得的话,这是我们实际上开始训练网络的时间。 这使我们积累了足够的经验来构建适当的小批量生产。 如果您为此参数选择的值小于批量大小,则 Keras RL 将抽样替换。
* `nb_steps_warmup`:确定我们开始进行经验重放之前需要等待的时间,如果您还记得的话,这是我们实际上开始训练网络的时间。 这使我们积累了足够的经验来构建适当的小批量生产。 如果您为此参数选择的值小于批量大小,则 Keras RL 将抽样替换。
* `target_model_update``Q`函数是递归的,当代理更新它的网络以获取` Q(s, a)`时,更新也影响其对`Q(s', a)`所​​做的预测。 这会导致网络非常不稳定。 大多数深度 Q 网络实现解决此限制的方法是使用目标网络,该目标网络是未经训练的深度 Q 网络的副本,而经常被新副本替换。 `target_model_update`参数控制这种情况发生的频率。
# 训练
......@@ -266,7 +266,7 @@ dqn.test(env, nb_episodes=5, visualize=True)
我们走了,那是一根平衡杆! 好吧,我知道,我承认平衡手推车上的电线杆并不是那么酷,所以让我们再做一个轻量级的例子。 在此示例中,我们将把月球着陆器降落在月球上,希望它将给您留下深刻的印象。
# 月球着陆器
# Lunar Lander
感谢 Keras-RL,我们用于 Lunar Lander 的代理几乎与 CartPole 相同,除了实际的模型架构和一些超参数更改外。 Lunar Lander 的环境有八个输入而不是四个输入,我们的代理现在可以选择四个操作而不是两个。
......@@ -291,11 +291,11 @@ def build_model(state_size, num_actions):
在此问题的情况下,较小的架构会导致代理学习控制和悬停着陆器,但实际上并未着陆。 当然,由于我们要对每个情节的每个步骤进行小批量更新,因此我们需要仔细权衡复杂性与运行时和计算需求之间的关系。
# 记忆和政策
# 记忆和策略
CartPole 的内存和策略可以重复使用。 我相信,通过进一步调整**线性退火策略**中的步骤,可能会提高代理训练的速度,因为该代理需要采取更多的步骤来进行训练。 但是,为 CartPole 选择的值似乎可以很好地工作,因此这是留给读者的练习。
# 代理商
# 智能体
从以下代码中可以看出,Lunar Lander `DQNAgent`再次相同,只是学习率小得多。
......
......@@ -15,13 +15,13 @@
生成对抗网络都是关于生成新内容的。 GAN 能够学习一些分布并从该分布创建新样本。 该样本可能只是我们训练数据中未出现的直线上的新点,但也可能是非常复杂的数据集中的新点。 GAN 已用于生成新的音乐,声音和图像。 根据 Yann LeCun 所说,[《对抗训练是切片以来最酷的事情》](https://www.quora.com/session/Yann-LeCun/1)。 我不确定切片面包是否特别酷,但是 Yann LeCun 是​​一个非常酷的家伙,所以我会信守诺言。 无论如何,GAN 都非常受欢迎,虽然它可能不如我们在业务环境中涵盖的其他一些主题那么实用,但在我们对深度学习技术的调查中值得考虑。
2014 年,伊恩·古德费洛(Ian Goodfellow)等人。 撰写了一篇名为[**生成对抗网络**](https://arxiv.org/pdf/1406.2661.pdf)的论文,提出了使用两个深度网络进行对抗训练的框架,每个都尝试打败对方。 该框架由两个独立的网络组成:别器和生成器。
2014 年,伊恩·古德费洛(Ian Goodfellow)等人。 撰写了一篇名为[**生成对抗网络**](https://arxiv.org/pdf/1406.2661.pdf)的论文,提出了使用两个深度网络进行对抗训练的框架,每个都尝试打败对方。 该框架由两个独立的网络组成:别器和生成器。
别器正在查看来自训练集的真实数据和来自生成器的假数据。 它的工作是将每一个作为传入数据实例分类为真实还是伪造。
别器正在查看来自训练集的真实数据和来自生成器的假数据。 它的工作是将每一个作为传入数据实例分类为真实还是伪造。
生成器试图使别器误以为所生成的数据是真实的。
生成器试图使别器误以为所生成的数据是真实的。
生成器和鉴别器被锁定在一个游戏中,它们各自试图超越彼此。 这种竞争驱使每个网络不断改进,直到最终判别器将生成器的输出与训练集中的数据区分开。 当生成器和鉴别器都正确配置时,它们将达到纳什均衡,在纳什均衡中,两者都无法找到优势。
生成器和判别器被锁定在一个游戏中,它们各自试图超越彼此。 这种竞争驱使每个网络不断改进,直到最终判别器将生成器的输出与训练集中的数据区分开。 当生成器和判别器都正确配置时,它们将达到纳什均衡,在纳什均衡中,两者都无法找到优势。
# 深度卷积 GAN 架构
......@@ -37,11 +37,11 @@ GAN 的整体架构如下图所示。 生成器和判别器分别是单独的深
![](img/370d895f-0973-49cf-8e8a-a9746d082883.png)
给生成器一个随机噪声矢量(`z`),并创建一个输出`G(z)`(对于 DCGAN,这是一个图像),希望它能欺骗别器。
给生成器一个随机噪声矢量(`z`),并创建一个输出`G(z)`(对于 DCGAN,这是一个图像),希望它能欺骗别器。
别器既得到实际训练数据(`X`),又得到生成器输出`G(z)`。 要做的是确定其输入实际上是真实的概率`P(X)`
别器既得到实际训练数据(`X`),又得到生成器输出`G(z)`。 要做的是确定其输入实际上是真实的概率`P(X)`
鉴别器和生成器都在堆栈中一起训练。 随着一个方面的改进,另一个方面也有所改进,直到希望生成器产生如此好的输出,从而使鉴别器不再能够识别该输出与训练数据之间的差异。
判别器和生成器都在栈中一起训练。 随着一个方面的改进,另一个方面也有所改进,直到希望生成器产生如此好的输出,从而使判别器不再能够识别该输出与训练数据之间的差异。
当然,在您准备好构建自己的 GAN 之前,我们还要介绍更多细节。 接下来,让我们更深入地研究生成器。
......@@ -59,35 +59,35 @@ GAN 的整体架构如下图所示。 生成器和判别器分别是单独的深
通常,生成器中最后一层的激活是双曲正切,并且训练图像矩阵中的元素被归一化为 -1 和 1 之间。这是我将在整章中提到的众多 GAN 黑魔法之一。 研究人员已经发现了一些经验证明可以帮助构建稳定的 GAN 的黑魔法,Soumith Chintala 可以在此 Git 上找到大多数黑客,[而 Soumith Chintala 也是 DCGAN 原始论文的作者之一](https://github.com/soumith/ganhacks)。 深度学习研究的世界无疑是一个很小的领域。
# 别器架构
# 别器架构
鉴别器的架构更像我们在前几章中已经看到的。 它实际上只是一个典型的图像分类器,如下图所示。 输出是 Sigmoid 的,因为鉴别器将预测输入图像是真实图像集的成员的概率。 鉴别器正在解决二分类问题:
判别器的架构更像我们在前几章中已经看到的。 它实际上只是一个典型的图像分类器,如下图所示。 输出是 Sigmoid 的,因为判别器将预测输入图像是真实图像集的成员的概率。 判别器正在解决二分类问题:
![](img/68c3560e-2207-409d-94b3-aebcb4b7d247.png)
现在,我们已经介绍了 DCGAN 的架构以及它的各个层次,下面让我们看一下如何训练框架。
# 叠训
# DCGAN
DCGAN 框架是使用迷你批量来进行训练的,这与我之前在本书中对网络进行训练的方式相同。 但是,稍后在构建代码时,您会注意到我们正在构建一个训练循环,该循环明确控制每个更新批量的情况,而不仅仅是调用`models.fit()`方法并依靠 Keras 为我们处理它。 我这样做是因为 GAN 训练需要多个模型来更新同一批次中的权重,所以它比我们以前所做的单个参数更新要稍微复杂一些。
对 DCGAN 进行训练的过程分为两步,每批次进行一次。
# 步骤 1 –训练鉴别器
# 步骤 1 – 训练判别器
批量训练 DCGAN 的第一步是在实际数据和生成的数据上训练别器。 赋予真实数据的标签显然是`1`,而用于假数据的标签则是`0`
批量训练 DCGAN 的第一步是在实际数据和生成的数据上训练别器。 赋予真实数据的标签显然是`1`,而用于假数据的标签则是`0`
# 步骤 2 –训练堆
# 步骤 2 – 训练
鉴别器更新权重后,我们将鉴别器和生成器一起训练为一个模型。 这样做时,我们将使鉴别器的权重不可训练,将其冻结在适当的位置,但仍允许鉴别器将梯度反向传播到生成器,以便生成器可以更新其权重。
判别器更新权重后,我们将判别器和生成器一起训练为一个模型。 这样做时,我们将使判别器的权重不可训练,将其冻结在适当的位置,但仍允许判别器将梯度反向传播到生成器,以便生成器可以更新其权重。
对于训练过程中的这一步,我们将使用噪声矢量作为输入,这将导致生成器生成图像。 别器将显示该图像,并要求预测该图像是否真实。 下图说明了此过程:
对于训练过程中的这一步,我们将使用噪声矢量作为输入,这将导致生成器生成图像。 别器将显示该图像,并要求预测该图像是否真实。 下图说明了此过程:
![](img/c8cc7ae9-1801-46d0-af96-142955d6a9a2.png)
鉴别器将提出一些预测,我们可以称之为`y_hat`。 此堆栈的`loss`函数将是二进制交叉熵,并且我们将`loss`函数的标签传递为 1,我们可以考虑`y`。 如您在本书前面所提到的, `y``y_hat`之间的`loss`转换为梯度,然后通过鉴别器传给生成器。 这将更新生成器权重,使它可以从鉴别者对问题空间的了解中受益,以便它可以学习创建更逼真的生成图像。
判别器将提出一些预测,我们可以称之为`y_hat`。 此栈的`loss`函数将是二进制交叉熵,并且我们将`loss`函数的标签传递为 1,我们可以考虑`y`。 如您在本书前面所提到的, `y``y_hat`之间的`loss`转换为梯度,然后通过判别器传给生成器。 这将更新生成器权重,使它可以从判别者对问题空间的了解中受益,以便它可以学习创建更逼真的生成图像。
然后重复这两个步骤,直到生成器能够创建与训练集中的数据相似的数据,使得判别器无法再将两个数据集区分开,这成为了一个猜谜游戏。 别器。 此时,生成器将不再能够改进。 当我们找到纳什均衡时,就对网络进行了训练。
然后重复这两个步骤,直到生成器能够创建与训练集中的数据相似的数据,使得判别器无法再将两个数据集区分开,这成为了一个猜谜游戏。 别器。 此时,生成器将不再能够改进。 当我们找到纳什均衡时,就对网络进行了训练。
# GAN 如何失败
......@@ -95,15 +95,15 @@ DCGAN 框架是使用迷你批量来进行训练的,这与我之前在本书
# 稳定性
训练 GAN 需要在鉴别器和生成器之间进行仔细的平衡。 鉴别器和生成器都在争夺深度网络优势。 另一方面,他们也需要彼此学习和成长。 为了使它起作用,任何一个都不能压倒另一个。
训练 GAN 需要在判别器和生成器之间进行仔细的平衡。 判别器和生成器都在争夺深度网络优势。 另一方面,他们也需要彼此学习和成长。 为了使它起作用,任何一个都不能压倒另一个。
在不稳定的 GAN 中,鉴别器可能会使生成器过载,并绝对确定生成器是假的。 损失为零,并且没有可用于发送到生成器的梯度,因此它不再可以改善。 网络游戏结束。 解决此问题的最佳方法是降低鉴别器的学习率。 您也可以尝试减少整个鉴别器架构中神经元的数量。 但是,您可能会在训练过程的后期错过这些神经元。 最终,调整网络架构和超参数是避免这种情况的最佳方法。
在不稳定的 GAN 中,判别器可能会使生成器过载,并绝对确定生成器是假的。 损失为零,并且没有可用于发送到生成器的梯度,因此它不再可以改善。 网络游戏结束。 解决此问题的最佳方法是降低判别器的学习率。 您也可以尝试减少整个判别器架构中神经元的数量。 但是,您可能会在训练过程的后期错过这些神经元。 最终,调整网络架构和超参数是避免这种情况的最佳方法。
当然,这可能是相反的方式,如模式崩溃的情况。
# 模式崩溃
**模式崩溃**是 GAN 失败的类似且相关的方式。 在模式崩溃中,生成器在多模式分布中学习一种模式,并选择始终使用该方法来利用鉴别器。 如果您的训练集中有鱼和小猫,并且您的生成器仅生成奇怪的小猫而没有鱼,则您经历了模式崩溃。 在这种情况下,增加鉴别器的功能可能会有所帮助。
**模式崩溃**是 GAN 失败的类似且相关的方式。 在模式崩溃中,生成器在多模式分布中学习一种模式,并选择始终使用该方法来利用判别器。 如果您的训练集中有鱼和小猫,并且您的生成器仅生成奇怪的小猫而没有鱼,则您经历了模式崩溃。 在这种情况下,增加判别器的功能可能会有所帮助。
# GAN 的安全选择
......@@ -116,10 +116,10 @@ DCGAN 框架是使用迷你批量来进行训练的,这与我之前在本书
![](img/24a66098-c4cd-49c4-8322-9e65ae01d963.png)
当设备不工作时,泄漏的 ReLU 允许非常小的非零梯度。 这可以消除消失的梯度,当我们像在别器和生成器的组合中那样将多个层堆叠在一起时,这总是一个问题。
当设备不工作时,泄漏的 ReLU 允许非常小的非零梯度。 这可以消除消失的梯度,当我们像在别器和生成器的组合中那样将多个层堆叠在一起时,这总是一个问题。
* **在生成器中使用丢弃**:这将产生噪声并防止模式崩溃。
* **使用软标签**:对于真实示例,请使用介于 0.7 和 1 之间的标签,对于伪示例,请使用介于 0 和 0.3 之间的标签。 这种噪声有助于保持信息从别器流向生成器。
* **使用软标签**:对于真实示例,请使用介于 0.7 和 1 之间的标签,对于伪示例,请使用介于 0 和 0.3 之间的标签。 这种噪声有助于保持信息从别器流向生成器。
在本章的其他地方,我们还将介绍许多其他的 GAN 黑魔法。 但是,我认为在成功实施 GAN 时,这几项技巧是最重要的。
......@@ -143,9 +143,9 @@ def load_data():
return X_train
```
您可能已经注意到,我没有返回任何标签或测试数据集。 我将只使用训练数据集。 不需要标签,因为我要使用的唯一标签是`0`代表假货,`1`代表真货。 这些是真实的图像,因此将在别器上将它们全部分配为标签 1。
您可能已经注意到,我没有返回任何标签或测试数据集。 我将只使用训练数据集。 不需要标签,因为我要使用的唯一标签是`0`代表假货,`1`代表真货。 这些是真实的图像,因此将在别器上将它们全部分配为标签 1。
# 建造生成器
# 创建生成器
生成器使用了一些新的层,我们将在本节中讨论这些层。 首先,有机会略读以下代码:
......@@ -177,9 +177,9 @@ def build_generator(noise_shape=(100,)):
这种生成器运算法则有点令人反感,乍一看似乎很尴尬,但是经过几个小时的痛苦之后,您就会掌握它了!
# 建立鉴别器
# 创建判别器
鉴别符实际上在很大程度上与我之前谈到的任何其他 CNN 相同。 当然,我们应该谈论一些新事物。 我们将使用以下代码来构建鉴别器:
判别符实际上在很大程度上与我之前谈到的任何其他 CNN 相同。 当然,我们应该谈论一些新事物。 我们将使用以下代码来构建判别器:
```py
def build_discriminator(img_shape):
......@@ -212,9 +212,9 @@ def build_discriminator(img_shape):
更不寻常的是同时使用批量规范化和辍学。 通常,这两层不能一起使用。 但是,就 GAN 而言,它们似乎确实使网络受益。
# 建立堆叠模型
# 创建栈式模型
现在我们已经组装了`generator``discriminator`,我们需要组装第三个模型,这是两个模型的栈,在`discriminator`损失的情况下,我们可以用来训练生成器。
现在我们已经组装了`generator``discriminator`,我们需要组装第三个模型,这是两个模型的栈,在`discriminator`损失的情况下,我们可以用来训练生成器。
为此,我们可以创建一个新模型,这次使用以前的模型作为新模型中的图层,如以下代码所示:
......@@ -229,7 +229,7 @@ real = discriminator(img)
combined = Model(z, real)
```
注意,在建立模型之前,我们将鉴别器的训练属性设置为`False`。 这意味着对于该模型,在反向传播期间,我们将不会更新鉴别器的权重。 正如我们在“栈式训练”部分中提到的,我们将冻结这些权重,仅将生成器的权重与堆栈一起移动。 鉴别器将单独训练。
注意,在建立模型之前,我们将判别器的训练属性设置为`False`。 这意味着对于该模型,在反向传播期间,我们将不会更新判别器的权重。 正如我们在“栈式训练”部分中提到的,我们将冻结这些权重,仅将生成器的权重与栈一起移动。 判别器将单独训练。
现在,所有模型都已构建,需要对其进行编译,如以下代码所示:
......@@ -247,13 +247,13 @@ combined.compile(loss='binary_crossentropy', optimizer=gen_optimizer)
```
如果您会注意到,我们将创建两个自定义 **Adam 优化器**。 这是因为很多时候,我们只想更改别器或生成器的学习率,从而减慢一个或另一个的学习速度,以至于我们得到一个稳定的 GAN,而后者却无法胜任另一个。 您还会注意到我正在使用`beta_1 = 0.5`。 这是我发扬光大并取得成功的 DCGAN 原始论文的推荐。 从原始 DCGAN 论文中可以发现,0.0002 的学习率也是一个很好的起点。
如果您会注意到,我们将创建两个自定义 **Adam 优化器**。 这是因为很多时候,我们只想更改别器或生成器的学习率,从而减慢一个或另一个的学习速度,以至于我们得到一个稳定的 GAN,而后者却无法胜任另一个。 您还会注意到我正在使用`beta_1 = 0.5`。 这是我发扬光大并取得成功的 DCGAN 原始论文的推荐。 从原始 DCGAN 论文中可以发现,0.0002 的学习率也是一个很好的起点。
# 训练循环
以前,我们曾很奢侈地在模型上调用`.fit()`,让 Keras 处理将数据分成小批和为我们训练的痛苦过程。
不幸的是,因为我们需要为一个批量器对别器和堆叠模型一起执行单独的更新,所以我们将不得不用老式的方式来做一些循环。 这就是过去一直做的事情,因此虽然可能需要做更多的工作,但它的确使我感到怀旧。 以下代码说明了训练技术:
不幸的是,因为我们需要为一个批量器对别器和堆叠模型一起执行单独的更新,所以我们将不得不用老式的方式来做一些循环。 这就是过去一直做的事情,因此虽然可能需要做更多的工作,但它的确使我感到怀旧。 以下代码说明了训练技术:
```py
num_examples = X_train.shape[0]
......@@ -296,7 +296,7 @@ for epoch in range(epochs + 1):
fake_labels = np.zeros((half_batch, 1))
```
这段代码生成了一个噪声向量矩阵(我们之前将其称为`z`)并将其发送到生成器。 它返回了一组生成的图像,我称之为伪图像。 我们将使用它们来训练别器,因此我们要使用的标签为 0,表示这些实际上是生成的图像。
这段代码生成了一个噪声向量矩阵(我们之前将其称为`z`)并将其发送到生成器。 它返回了一组生成的图像,我称之为伪图像。 我们将使用它们来训练别器,因此我们要使用的标签为 0,表示这些实际上是生成的图像。
注意,这里的形状是`half_batch x 28 x 28 x 1``half_batch`正是您所想的。 我们将创建一半的生成图像,因为另一半将是真实数据,我们将在下一步进行组装。 要获取真实图像,我们将在`X_train`上生成一组随机索引,并将`X_train`的切片用作真实图像,如以下代码所示:
......@@ -308,7 +308,7 @@ real_labels = np.ones((half_batch, 1))
是的,在这种情况下,我们正在抽样更换。 它确实可以解决,但可能不是实施小批量训练的最佳方法。 但是,它可能是最简单,最常见的。
由于我们正在使用这些图像来训练鉴别器,并且由于它们是真实图像,因此我们将它们分配为`1`作为标签,而不是`0`。 现在我们已经组装了鉴别器训练集,我们将更新鉴别器。 还要注意,我们没有使用我们之前讨论的软标签。 那是因为我想让事情尽可能地容易理解。 幸运的是,在这种情况下,网络不需要它们。 我们将使用以下代码来训练鉴别器:
由于我们正在使用这些图像来训练判别器,并且由于它们是真实图像,因此我们将它们分配为`1`作为标签,而不是`0`。 现在我们已经组装了判别器训练集,我们将更新判别器。 还要注意,我们没有使用我们之前讨论的软标签。 那是因为我想让事情尽可能地容易理解。 幸运的是,在这种情况下,网络不需要它们。 我们将使用以下代码来训练判别器:
```py
# Train the discriminator (real classified as ones and generated as zeros)
......@@ -317,20 +317,20 @@ d_loss_fake = discriminator.train_on_batch(fake_images, fake_labels)
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
```
请注意,这里我使用的是别符的`train_on_batch()`方法。 这是我第一次在本书中使用此方法。 `train_on_batch()`方法正好执行一轮正向和反向传播。 每次我们调用它时,它都会从模型的先前状态更新一次模型。
请注意,这里我使用的是别符的`train_on_batch()`方法。 这是我第一次在本书中使用此方法。 `train_on_batch()`方法正好执行一轮正向和反向传播。 每次我们调用它时,它都会从模型的先前状态更新一次模型。
另请注意,我正在分别对真实图像和伪图像进行更新。 这是我先前在“生成器架构”部分中引用的 GAN 黑魔法 Git 上给出的建议。 尤其是在训练的早期阶段,当真实图像和伪图像来自完全不同的分布时,如果我们将两组数据放在同一更新中,则批量归一化将导致训练问题。
现在,鉴别器已经更新,是时候更新生成器了。 这是通过更新组合堆栈间接完成的,如以下代码所示:
现在,判别器已经更新,是时候更新生成器了。 这是通过更新组合栈间接完成的,如以下代码所示:
```py
noise = np.random.normal(0, 1, (batch_size, 100))
g_loss = combined.train_on_batch(noise, np.ones((batch_size, 1)))
```
为了更新组合模型,我们创建了一个新的噪声矩阵,这次它将与整个批次一样大。 我们将其用作堆栈的输入,这将使生成器生成图像,并使用鉴别器评估该图像。 最后,我们将使用`1`标签,因为我们想在实际图像和生成的图像之间反向传播错误。
为了更新组合模型,我们创建了一个新的噪声矩阵,这次它将与整个批次一样大。 我们将其用作栈的输入,这将使生成器生成图像,并使用判别器评估该图像。 最后,我们将使用`1`标签,因为我们想在实际图像和生成的图像之间反向传播错误。
最后,训练循环报告`epoch`/`batch`处的别器和生成器损失,然后每`epoch`中的每 50 批,我们将使用`save_imgs`生成示例图像并将其保存到磁盘,如以下代码所示:
最后,训练循环报告`epoch`/`batch`处的别器和生成器损失,然后每`epoch`中的每 50 批,我们将使用`save_imgs`生成示例图像并将其保存到磁盘,如以下代码所示:
```py
print("Epoch %d Batch %d/%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" %
......@@ -378,7 +378,7 @@ for j in range(c):
![](img/69d0a788-d965-4e70-a6f5-e8589b810c9f.png)
一个完整的时期后,这是我们的生成器。 我认为这些生成的数字看起来不错,而且我可以看到别符可能会被它们欺骗。 在这一点上,我们可能会继续改善一点,但是随着计算机生成一些令人信服的 MNIST 数字,我们的 GAN 似乎已经发挥了作用,如下图所示:
一个完整的时期后,这是我们的生成器。 我认为这些生成的数字看起来不错,而且我可以看到别符可能会被它们欺骗。 在这一点上,我们可能会继续改善一点,但是随着计算机生成一些令人信服的 MNIST 数字,我们的 GAN 似乎已经发挥了作用,如下图所示:
![](img/3b179ae1-40df-4859-8849-01c5011588f7.png)
......@@ -402,7 +402,7 @@ def load_data():
return X_train
```
# 建造生成器
# 创建生成器
生成器需要产生`32 x 32 x 3`图像。 这需要对我们的网络架构进行两项细微更改,您可以在此处看到它们:
......@@ -428,9 +428,9 @@ model = Model(input, out)
由于我们的图像现在包含三个通道,因此最后的卷积层也需要包含三个通道,而不是一个。 这里的所有都是它的; 我们现在可以生成彩色图像!
# 建立鉴别器
# 创建判别器
别符几乎完全不变。 输入层需要从`28 x 28 x 1`更改为`32 x 32 x 3`。 另外`ZeroPadding2D`可以毫无问题地删除,因为没有它的图层算术就可以工作。
别符几乎完全不变。 输入层需要从`28 x 28 x 1`更改为`32 x 32 x 3`。 另外`ZeroPadding2D`可以毫无问题地删除,因为没有它的图层算术就可以工作。
# 训练循环
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册