提交 926db5d5 编写于 作者: W wizardforcel

2020-09-06 20:36:59

上级 73066efe
...@@ -109,7 +109,7 @@ ReLu,或直线激活,简单定义为: ...@@ -109,7 +109,7 @@ ReLu,或直线激活,简单定义为:
进行梯度下降的另一种可能方法是一次使用一个数据点,并随着我们的更新网络权重。 此方法可能有助于加快网络可能停止收敛的鞍点附近的收敛速度。 当然,仅单个点的误差估计可能无法很好地近似于整个数据集的误差。 进行梯度下降的另一种可能方法是一次使用一个数据点,并随着我们的更新网络权重。 此方法可能有助于加快网络可能停止收敛的鞍点附近的收敛速度。 当然,仅单个点的误差估计可能无法很好地近似于整个数据集的误差。
解决此问题的最佳解决方案是使用小型批处理梯度下降,其中我们将采用称为小型批处理的数据的随机子集来计算误差并更新网络权重。 这几乎总是最好的选择。 它还有一个额外的好处,即可以将非常大的数据集自然地拆分为多个块,这些块可以更容易地在计算机的内存中甚至跨计算机的内存中进行管理。 解决此问题的最佳解决方案是使用小型批量梯度下降,其中我们将采用称为小型批量的数据的随机子集来计算误差并更新网络权重。 这几乎总是最好的选择。 它还有一个额外的好处,即可以将非常大的数据集自然地拆分为多个块,这些块可以更容易地在计算机的内存中甚至跨计算机的内存中进行管理。
这是对神经网络最重要部分之一的极高层次的描述,我们认为这与本书的实际性质相符。 实际上,大多数现代框架都为我们处理了这些步骤。 但是,至少在理论上,它们无疑是值得了解的。 我们鼓励读者在时间允许的情况下更深入地进行向前和向后传播。 这是对神经网络最重要部分之一的极高层次的描述,我们认为这与本书的实际性质相符。 实际上,大多数现代框架都为我们处理了这些步骤。 但是,至少在理论上,它们无疑是值得了解的。 我们鼓励读者在时间允许的情况下更深入地进行向前和向后传播。
...@@ -411,7 +411,7 @@ val 和测试数据来自同一数据集非常重要。 火车数据集匹配 va ...@@ -411,7 +411,7 @@ val 和测试数据来自同一数据集非常重要。 火车数据集匹配 va
* **高方差**:具有低偏差误差的网络很好地拟合了训练数据; 但是,如果验证误差大于测试误差,则网络已开始过度拟合训练数据。 减少差异的两种最佳方法是添加数据并向网络添加正则化。 * **高方差**:具有低偏差误差的网络很好地拟合了训练数据; 但是,如果验证误差大于测试误差,则网络已开始过度拟合训练数据。 减少差异的两种最佳方法是添加数据并向网络添加正则化。
添加数据很简单,但并非总是可能的。 在整本书中,我们将介绍适用的正则化技术。 我们将讨论的最常见的正则化技术是 L2 正则化,辍学和批处理归一化。 添加数据很简单,但并非总是可能的。 在整本书中,我们将介绍适用的正则化技术。 我们将讨论的最常见的正则化技术是 L2 正则化,辍学和批归一化。
# K 折交叉验证 # K 折交叉验证
......
...@@ -102,7 +102,7 @@ def create_callbacks(): ...@@ -102,7 +102,7 @@ def create_callbacks():
您可能已经注意到,我正在将 MLP 网络的 TensorBoard 回调的日志写入`~/ch_3_tb_log/mlp`,这将在我们为 TensorBoard 指定的目录下创建一个新的 Director `mlp`。 这是故意的。 我们将配置在第 2 章,“使用深度学习解决回归问题”训练的深度神经网络模型,以登录到单独的目录`~/ch_3_tb_log/dnn`。 这样做将使我们能够比较两个模型的运行。 您可能已经注意到,我正在将 MLP 网络的 TensorBoard 回调的日志写入`~/ch_3_tb_log/mlp`,这将在我们为 TensorBoard 指定的目录下创建一个新的 Director `mlp`。 这是故意的。 我们将配置在第 2 章,“使用深度学习解决回归问题”训练的深度神经网络模型,以登录到单独的目录`~/ch_3_tb_log/dnn`。 这样做将使我们能够比较两个模型的运行。
* `histogram_freq`:这指定我们将多长时间计算一次激活和权重的直方图(以时期为单位)。 它的默认值为 0,这会使日志更小,但不会生成直方图。 我们将介绍为什么以及何时您会对直方图感兴趣。 * `histogram_freq`:这指定我们将多长时间计算一次激活和权重的直方图(以时期为单位)。 它的默认值为 0,这会使日志更小,但不会生成直方图。 我们将介绍为什么以及何时您会对直方图感兴趣。
* `batch_size`:这是用于计算直方图的批处理大小。 默认为 32。 * `batch_size`:这是用于计算直方图的批大小。 默认为 32。
* `write_graph`:此函数为布尔值。 这将告诉 TensorBoard 可视化网络图。 这可能非常方便,但也会使日志变得很大。 * `write_graph`:此函数为布尔值。 这将告诉 TensorBoard 可视化网络图。 这可能非常方便,但也会使日志变得很大。
* `write_grads`:此函数也是布尔值。 这将告诉 TensorBoard 也计算梯度的直方图。 * `write_grads`:此函数也是布尔值。 这将告诉 TensorBoard 也计算梯度的直方图。
...@@ -148,7 +148,7 @@ tensorboard_callback = TensorBoard(log_dir='./ch3_tb_log/dnn', ...@@ -148,7 +148,7 @@ tensorboard_callback = TensorBoard(log_dir='./ch3_tb_log/dnn',
TensorBoard 可以容纳许多运行,并且您可以通过正则表达式过滤它们(例如`^dnn`将显示所有以`dnn`开头的运行)。 这意味着,如果您通过许多实验或运行(例如超参数优化)来搜索*最佳*模型,则可以在明确并一致地命名运行并包含有意义的超参数和体系结构信息的情况下快速浏览它们 以这个名字吧! TensorBoard 可以容纳许多运行,并且您可以通过正则表达式过滤它们(例如`^dnn`将显示所有以`dnn`开头的运行)。 这意味着,如果您通过许多实验或运行(例如超参数优化)来搜索*最佳*模型,则可以在明确并一致地命名运行并包含有意义的超参数和体系结构信息的情况下快速浏览它们 以这个名字吧!
这些图上的默认 **X** 比例尺是**历元****Y** 值是我们选择的**损失函数**,即 **MAE**。 您可以单击图形以浏览它们并拖动以缩放。 这些图上的默认 **X** 比例尺是**周期****Y** 值是我们选择的**损失函数**,即 **MAE**。 您可以单击图形以浏览它们并拖动以缩放。
看到这样的图,我们真的可以看到每个网络的相对偏差和方差。 虽然模型之间在火车损失方面有很好的分离,但深度神经网络在验证集上只得到了一点点改善,这表明我们已经进入了过度拟合的领域。 看到这样的图,我们真的可以看到每个网络的相对偏差和方差。 虽然模型之间在火车损失方面有很好的分离,但深度神经网络在验证集上只得到了一点点改善,这表明我们已经进入了过度拟合的领域。
......
...@@ -167,7 +167,7 @@ Hyperband 是一项超参数优化技术,由 Lisha Li,Kevin Jamieson,Guili ...@@ -167,7 +167,7 @@ Hyperband 是一项超参数优化技术,由 Lisha Li,Kevin Jamieson,Guili
想象一下,就像我们在`RandomSearchCV`中所做的那样,随机采样许多潜在的超参数集。 完成`RandomSearchCV`后,它将选择一个单一的超参数配置作为其采样的*最优值*。 Hyperband 利用这样的思想,即即使经过少量迭代,最佳的超参数配置也可能会胜过其他配置。 Hyperband 中的乐队来自土匪,指的是基于多臂土匪技术(用于优化竞争选择之间的资源分配以优化性能为目标的技术)的勘探与开发。 想象一下,就像我们在`RandomSearchCV`中所做的那样,随机采样许多潜在的超参数集。 完成`RandomSearchCV`后,它将选择一个单一的超参数配置作为其采样的*最优值*。 Hyperband 利用这样的思想,即即使经过少量迭代,最佳的超参数配置也可能会胜过其他配置。 Hyperband 中的乐队来自土匪,指的是基于多臂土匪技术(用于优化竞争选择之间的资源分配以优化性能为目标的技术)的勘探与开发。
使用 Hyperband,我们可以尝试一些可能的配置集(`n`),仅训练一次迭代。 作者将迭代一词留作多种可能的用途。 但是,我将时代作为迭代。 一旦完成第一个培训循环,就将根据性能对结果进行配置。 然后,对该列表的上半部分进行大量迭代的训练。 然后重复进行减半和剔除的过程,我们得到了一些非常小的配置集,我们将针对在搜索中定义的完整迭代次数进行训练。 与在每种可能的配置中搜索最大历元相比,此过程使我们在更短的时间内获得了*最佳*超参数集。 使用 Hyperband,我们可以尝试一些可能的配置集(`n`),仅训练一次迭代。 作者将迭代一词留作多种可能的用途。 但是,我将时代作为迭代。 一旦完成第一个培训循环,就将根据性能对结果进行配置。 然后,对该列表的上半部分进行大量迭代的训练。 然后重复进行减半和剔除的过程,我们得到了一些非常小的配置集,我们将针对在搜索中定义的完整迭代次数进行训练。 与在每种可能的配置中搜索最大周期相比,此过程使我们在更短的时间内获得了*最佳*超参数集。
在本章的 GitHub 存储库中,我在`hyperband.py`中包括了`hyperband`算法的实现。 此实现主要源自 FastML 的实现,您可以在[这个页面](http://fastml.com/tuning-hyperparams-fast-with-hyperband/)中找到。 要使用它,您需要首先实例化一个`hyperband`对象,如以下代码所示: 在本章的 GitHub 存储库中,我在`hyperband.py`中包括了`hyperband`算法的实现。 此实现主要源自 FastML 的实现,您可以在[这个页面](http://fastml.com/tuning-hyperparams-fast-with-hyperband/)中找到。 要使用它,您需要首先实例化一个`hyperband`对象,如以下代码所示:
......
...@@ -90,11 +90,11 @@ pool1 = MaxPooling2D(pool_size=(2, 2), name="pool_1") ...@@ -90,11 +90,11 @@ pool1 = MaxPooling2D(pool_size=(2, 2), name="pool_1")
# 批量标准化 # 批量标准化
处理规范化有助于我们的网络整体表现更好,学习速度更快。 批处理规范化在应用程序中也很容易理解。 但是,为什么它起作用,仍然受到研究人员的争议。 量规范化有助于我们的网络整体表现更好,学习速度更快。 批量规范化在应用程序中也很容易理解。 但是,为什么它起作用,仍然受到研究人员的争议。
使用批量归一化时,对于每个小批量,我们可以在每个非线性之后(或之前)对那个批量进行归一化,使其平均值为 0,单位方差。 这使每一层都可以从中学习标准化输入,从而使该层的学习效率更高。 使用批量归一化时,对于每个小批量,我们可以在每个非线性之后(或之前)对那个批量进行归一化,使其平均值为 0,单位方差。 这使每一层都可以从中学习标准化输入,从而使该层的学习效率更高。
批归一化层很容易在 Keras 中实现,本章的示例将在每个卷积层之后使用它们。 以下代码用于批处理规范化: 批归一化层很容易在 Keras 中实现,本章的示例将在每个卷积层之后使用它们。 以下代码用于批规范化:
```py ```py
from keras.layers import BatchNormalization from keras.layers import BatchNormalization
...@@ -130,7 +130,7 @@ output = Dense(10, activation="softmax", name="softmax")(d2) ...@@ -130,7 +130,7 @@ output = Dense(10, activation="softmax", name="softmax")(d2)
# 卷积层 # 卷积层
如果您开始怀疑此实现中是否会有任何不同之处,那就是这里。 我将使用两个卷积层,分别进行批处理规范化和最大池化。 这将要求我们做出很多选择,当然我们以后可以选择作为超参数进行搜索。 不过,最好先让某些东西开始工作。 正如 Donald Knuth 所说,过早的优化是万恶之源。 我们将使用以下代码片段定义两个卷积块: 如果您开始怀疑此实现中是否会有任何不同之处,那就是这里。 我将使用两个卷积层,分别进行批规范化和最大池化。 这将要求我们做出很多选择,当然我们以后可以选择作为超参数进行搜索。 不过,最好先让某些东西开始工作。 正如 Donald Knuth 所说,过早的优化是万恶之源。 我们将使用以下代码片段定义两个卷积块:
```py ```py
# convolutional block 1 # convolutional block 1
...@@ -144,9 +144,9 @@ batch2 = BatchNormalization(name="batch_norm_2")(conv2) ...@@ -144,9 +144,9 @@ batch2 = BatchNormalization(name="batch_norm_2")(conv2)
pool2 = MaxPooling2D(pool_size=(2, 2), name="pool_2")(batch2) pool2 = MaxPooling2D(pool_size=(2, 2), name="pool_2")(batch2)
``` ```
因此,很明显,我们在这里有两个卷积块,它们由一个卷积层,一个批处理规范化层和一个池化层组成。 因此,很明显,我们在这里有两个卷积块,它们由一个卷积层,一个批规范化层和一个池化层组成。
在第一块中,我使用具有`relu`激活功能的 64 个`3 x 3`过滤器。 我使用的是有效(无)填充,跨度为 1。批处理规范化不需要任何参数,并且实际上不是可训练的。 池化层使用`2 x 2`池化窗口,有效填充和跨度为 2(窗口尺寸)。 在第一块中,我使用具有`relu`激活功能的 64 个`3 x 3`过滤器。 我使用的是有效(无)填充,跨度为 1。批规范化不需要任何参数,并且实际上不是可训练的。 池化层使用`2 x 2`池化窗口,有效填充和跨度为 2(窗口尺寸)。
第二个块几乎相同。 但是,我将过滤器数量减半为 32。 第二个块几乎相同。 但是,我将过滤器数量减半为 32。
...@@ -313,6 +313,6 @@ model.fit_generator(data_generator.flow(data["train_X"], data["train_y"], batch_ ...@@ -313,6 +313,6 @@ model.fit_generator(data_generator.flow(data["train_X"], data["train_y"], batch_
# 摘要 # 摘要
在本章中,我们快速介绍了许多基础知识。 我们讨论了卷积层及其如何用于神经网络。 我们还介绍了批处理规范化,池化层和数据扩充。 最后,我们使用 Keras 从零开始训练卷积神经网络,然后使用数据增强对该网络进行改进。 在本章中,我们快速介绍了许多基础知识。 我们讨论了卷积层及其如何用于神经网络。 我们还介绍了批规范化,池化层和数据扩充。 最后,我们使用 Keras 从零开始训练卷积神经网络,然后使用数据增强对该网络进行改进。
我们还讨论了如何基于数据的渴望计算机视觉的深度神经网络问题。 在下一章中,我将向您展示**迁移学习**,这是我最喜欢的技术之一。 这将帮助您快速解决计算机视觉问题,并获得惊人的结果并且数据量更少。 我们还讨论了如何基于数据的渴望计算机视觉的深度神经网络问题。 在下一章中,我将向您展示**迁移学习**,这是我最喜欢的技术之一。 这将帮助您快速解决计算机视觉问题,并获得惊人的结果并且数据量更少。
\ No newline at end of file
...@@ -186,7 +186,7 @@ scores = model.evaluate_generator(val_generator, steps=val_generator.n // batch_ ...@@ -186,7 +186,7 @@ scores = model.evaluate_generator(val_generator, steps=val_generator.n // batch_
print("Step 1 Scores: Loss: " + str(scores[0]) + " Accuracy: " + str(scores[1])) print("Step 1 Scores: Loss: " + str(scores[0]) + " Accuracy: " + str(scores[1]))
``` ```
在前面的示例中,我们使用`ImageDataGenerator``n`属性来了解可用于生成器的图像总数,并将每个时期的步骤定义为该数目除以批处理大小。 在前面的示例中,我们使用`ImageDataGenerator``n`属性来了解可用于生成器的图像总数,并将每个时期的步骤定义为该数目除以批大小。
此代码的其余部分应该很熟悉。 此代码的其余部分应该很熟悉。
......
...@@ -39,7 +39,7 @@ NLP 领域广阔而复杂。 从技术上讲,人类语言与计算机科学之 ...@@ -39,7 +39,7 @@ NLP 领域广阔而复杂。 从技术上讲,人类语言与计算机科学之
**文档分类**与情感分析密切相关。 在这两种情况下,我们都使用文本将文档分类。 实际上,这只是改变的原因。 文档分类就是根据文档的类型对文档进行分类。 世界上最明显,最常见的文档分类系统是垃圾邮件过滤器,但它还有许多其他用途。 **文档分类**与情感分析密切相关。 在这两种情况下,我们都使用文本将文档分类。 实际上,这只是改变的原因。 文档分类就是根据文档的类型对文档进行分类。 世界上最明显,最常见的文档分类系统是垃圾邮件过滤器,但它还有许多其他用途。
我最喜欢的文档分类用途之一是解决*联邦主义者论文*的原始作者的辩论。 亚历山大·汉密尔顿(Alexander Hamilton),詹姆斯·麦迪逊(James Madison)和约翰·杰伊(John Jay)在 1787 年和 1788 年以化名 Publius 出版了 85 篇文章,支持批准美国宪法。 后来,汉密尔顿提供了一份清单,详细列出了每篇论文的作者在 1804 年与亚伦·伯尔(Aaron Burr)进行致命的对决之前。麦迪逊(Madison)在 1818 年提供了自己的清单,这在作者身份上引起了争执,此后学者一直在努力解决。 虽然大多数人都同意有争议的作品是麦迪逊的作品,但是关于两者之间的合作仍存在一些理论。 将这 12 个有争议的文档归类为 Madison 还是 Hamilton,已经成为许多数据科学博客的不二之选。 正式而言,Glenn Fung 的论文[《有争议的联邦主义者论文:通过凹面最小化进行 SVM 特征选择》](http://pages.cs.wisc.edu/~gfung/federalist.pdf) 涵盖了相当严格的主题。 我最喜欢的文档分类用途之一是解决“联邦主义者论文”的原始作者的辩论。 亚历山大·汉密尔顿(Alexander Hamilton),詹姆斯·麦迪逊(James Madison)和约翰·杰伊(John Jay)在 1787 年和 1788 年以化名 Publius 出版了 85 篇文章,支持批准美国宪法。 后来,汉密尔顿提供了一份清单,详细列出了每篇论文的作者在 1804 年与亚伦·伯尔(Aaron Burr)进行致命的对决之前。麦迪逊(Madison)在 1818 年提供了自己的清单,这在作者身份上引起了争执,此后学者一直在努力解决。 虽然大多数人都同意有争议的作品是麦迪逊的作品,但是关于两者之间的合作仍存在一些理论。 将这 12 个有争议的文档归类为 Madison 还是 Hamilton,已经成为许多数据科学博客的不二之选。 正式而言,Glenn Fung 的论文[《有争议的联邦主义者论文:通过凹面最小化进行 SVM 特征选择》](http://pages.cs.wisc.edu/~gfung/federalist.pdf) 涵盖了相当严格的主题。
文档分类的最后一个示例可能是围绕了解文档的内容并规定操作。 想象一下一个分类器,它可能会读取有关法律案件的一些信息,例如请愿/投诉和传票,然后向被告提出建议。 然后,我们的假想系统可能会说:*鉴于我在其他类似情况下的经验,您可能想解决* 文档分类的最后一个示例可能是围绕了解文档的内容并规定操作。 想象一下一个分类器,它可能会读取有关法律案件的一些信息,例如请愿/投诉和传票,然后向被告提出建议。 然后,我们的假想系统可能会说:*鉴于我在其他类似情况下的经验,您可能想解决*
...@@ -389,7 +389,7 @@ model.fit(x=data["X_train"], y=data["y_train"], ...@@ -389,7 +389,7 @@ model.fit(x=data["X_train"], y=data["y_train"],
像这样将我所有的训练参数和数据保存在一个字典中,实际上只是一个样式问题,而与功能无关。 您可能希望单独处理所有事情。 我喜欢对所有内容使用字典,因为它使我无法来回传递大量参数。 像这样将我所有的训练参数和数据保存在一个字典中,实际上只是一个样式问题,而与功能无关。 您可能希望单独处理所有事情。 我喜欢对所有内容使用字典,因为它使我无法来回传递大量参数。
由于我们使用的是无状态 LSTM,因此我们将在每个批次中重置单元存储器。 我的信念是,我们可以在不损失任何罚款的情况下重置文档之间的单元状态,因此批处理大小实际上与性能有关。 我在这里使用了 32 个观察批,但是只要 GPU 内存允许,128 个观察批会产生相似的结果,并且性能会有所提高。 由于我们使用的是无状态 LSTM,因此我们将在每个批次中重置单元存储器。 我的信念是,我们可以在不损失任何罚款的情况下重置文档之间的单元状态,因此批大小实际上与性能有关。 我在这里使用了 32 个观察批,但是只要 GPU 内存允许,128 个观察批会产生相似的结果,并且性能会有所提高。
# 性能 # 性能
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
![](img/e39144a7-9a97-4e55-8500-6f6a64fc8537.png) ![](img/e39144a7-9a97-4e55-8500-6f6a64fc8537.png)
动作会在原始状态*的*和下一个状态![](img/09ae8c4e-fc27-4814-a227-503b89567814.png)的代理之间进行转换,代理会在其中获得一些奖励 *r* 。 代理选择动作的方式称为**代理策略**,通常称为![](img/65b50102-86c9-4dab-9a73-246711c1ceee.png) 动作会在原始状态`s`和下一个状态`s'`的代理之间进行转换,代理会在其中获得一些奖励`r`。 代理选择动作的方式称为**代理策略**,通常称为`pi`
强化学习的目的是找到一系列动作,使行动者从一个州到另一个州,并获得尽可能多的报酬。 强化学习的目的是找到一系列动作,使行动者从一个州到另一个州,并获得尽可能多的报酬。
...@@ -38,10 +38,10 @@ ...@@ -38,10 +38,10 @@
我们构筑的这个世界恰好是**马尔可夫决策过程****MDP**),它具有以下属性: 我们构筑的这个世界恰好是**马尔可夫决策过程****MDP**),它具有以下属性:
* 它具有一组有限的状态, *S* * 它具有一组有限的状态,`S`
* 它具有一组有限的动作 *A* * 它具有一组有限的动作 `A`
* ![](img/02b74074-e384-4fa3-bccd-c0262c867f5b.png)是采取行动 *A* 将在状态 s 和状态![](img/eb953b39-cb00-4772-bc93-a344af6f04fd.png)之间转换的概率 * ![](img/02b74074-e384-4fa3-bccd-c0262c867f5b.png)是采取行动`A`将在状态`s`和状态![](img/eb953b39-cb00-4772-bc93-a344af6f04fd.png)之间转换的概率
* ![](img/4535c471-eb4b-493a-8248-21870b941877.png) s 和![](img/00a202fe-1100-4d7e-b183-32efc3c0dd7a.png)之间过渡的直接奖励。 * ![](img/4535c471-eb4b-493a-8248-21870b941877.png)`s`和![](img/00a202fe-1100-4d7e-b183-32efc3c0dd7a.png)之间过渡的直接奖励。
* ![](img/5c9c163f-374e-4d8f-a5f4-c434ce13a568.png)是折扣因子,这是我们相对于当前奖励对未来奖励的折扣程度(稍后会详细介绍) * ![](img/5c9c163f-374e-4d8f-a5f4-c434ce13a568.png)是折扣因子,这是我们相对于当前奖励对未来奖励的折扣程度(稍后会详细介绍)
一旦我们有了确定每个状态要采取的操作的策略功能![](img/2c3faff6-8c08-4086-9d0e-9ef3b6bf448e.png),MDP 就解决了,成为了马尔可夫链。 一旦我们有了确定每个状态要采取的操作的策略功能![](img/2c3faff6-8c08-4086-9d0e-9ef3b6bf448e.png),MDP 就解决了,成为了马尔可夫链。
...@@ -50,31 +50,31 @@ ...@@ -50,31 +50,31 @@
# Q 学习 # Q 学习
想象一下,我们有一些功能 *Q,*可以估算出采取行动的回报: 想象一下,我们有一些功能`Q`可以估算出采取行动的回报:
![](img/c0927993-35eb-4977-a4bc-ccd338f9fc95.png) ![](img/c0927993-35eb-4977-a4bc-ccd338f9fc95.png)
对于某些状态*的*以及动作*和*,它会根据状态为该动作生成奖励。 如果我们知道环境带来的所有回报,那么我们就可以遍历 *Q* 并选择能够为我们带来最大回报的行动。 但是,正如我们在上一节中提到的那样,我们的代理人不知道所有的奖励状态和状态概率。 因此,我们的 *Q* 函数需要尝试近似奖励。 对于某些状态`s`以及动作`a`,它会根据状态为该动作生成奖励。 如果我们知道环境带来的所有回报,那么我们就可以遍历`Q`并选择能够为我们带来最大回报的行动。 但是,正如我们在上一节中提到的那样,我们的代理人不知道所有的奖励状态和状态概率。 因此,我们的`Q`函数需要尝试近似奖励。
我们可以使用称为 **Bellman 公式**的递归定义的 *Q* 函数来近似此理想的 *Q* 函数: 我们可以使用称为 **Bellman 公式**的递归定义的`Q`函数来近似此理想的`Q`函数:
![](img/92cb692e-ef61-427e-bd69-0e698f08f007.png) ![](img/92cb692e-ef61-427e-bd69-0e698f08f007.png)
在这种情况下, *r* <sub>0</sub> 是下一个动作的奖励,然后在下一个动作上(递归地)递归使用 *Q* 函数 确定该行动的未来奖励。 为此,我们将伽马![](img/1a9812ab-15b6-46c8-84c2-e3c380e69973.png)作为相对于当前奖励的未来奖励的折扣。 只要伽玛小于 1,它就不会使我们的奖励序列变得无限大。 更明显地,与当前状态下的相同奖励相比,未来状态下的奖励的价值要低。 具体来说,如果有人今天给您 100 美元,明天给您 100 美元,您应该立即拿走这笔钱,因为明天不确定。 在这种情况下, `r[0]`是下一个动作的奖励,然后在下一个动作上(递归地)递归使用`Q`函数 确定该行动的未来奖励。 为此,我们将`γ`作为相对于当前奖励的未来奖励的折扣。 只要伽玛小于 1,它就不会使我们的奖励序列变得无限大。 更明显地,与当前状态下的相同奖励相比,未来状态下的奖励的价值要低。 具体来说,如果有人今天给您 100 美元,明天给您 100 美元,您应该立即拿走这笔钱,因为明天不确定。
如果我们尽最大的努力让我们的代理经历每种可能的状态转换,并使用此函数来估计我们的报酬,我们将得出我们试图近似的理想 *Q* 函数。 如果我们尽最大的努力让我们的代理经历每种可能的状态转换,并使用此函数来估计我们的报酬,我们将得出我们试图近似的理想`Q`函数。
# 无限状态空间 # 无限状态空间
*Q* 功能的讨论使我们陷入了传统强化学习的重要局限。 您可能还记得,它假设状态空间是有限且离散的。 不幸的是,这不是我们生活的世界,也不是我们的代理商在很多时候会发现自己的环境。 考虑一个可以打乒乓球的经纪人。 状态空间的重要组成部分是乒乓球的速度,它当然不是离散的。 像我们不久将要看到的那样,可以看到的特工会看到一个图像,该图像是一个很大的连续空间。 `Q`功能的讨论使我们陷入了传统强化学习的重要局限。 您可能还记得,它假设状态空间是有限且离散的。 不幸的是,这不是我们生活的世界,也不是我们的代理商在很多时候会发现自己的环境。 考虑一个可以打乒乓球的经纪人。 状态空间的重要组成部分是乒乓球的速度,它当然不是离散的。 像我们不久将要看到的那样,可以看到的特工会看到一个图像,该图像是一个很大的连续空间。
我们讨论的 Bellman 方程将要求我们在州与州之间转移时保持经验奖励的大矩阵。 但是,当面对连续的状态空间时,这是不可能的。 可能的状态本质上是无限的,我们不能创建无限大小的矩阵。 我们讨论的 Bellman 方程将要求我们在州与州之间转移时保持经验奖励的大矩阵。 但是,当面对连续的状态空间时,这是不可能的。 可能的状态本质上是无限的,我们不能创建无限大小的矩阵。
幸运的是,我们可以使用深度神经网络来近似 *Q* 函数。 这可能不会让您感到惊讶,因为您正在阅读一本深度学习书,因此您可能猜测深度学习必须在某个地方出现。 就是那个地方 幸运的是,我们可以使用深度神经网络来近似`Q`函数。 这可能不会让您感到惊讶,因为您正在阅读一本深度学习书,因此您可能猜测深度学习必须在某个地方出现。 就是那个地方
# 深度 Q 网络 # 深度 Q 网络
**深层 Q 网络****DQN**)是近似 *Q* 函数的神经网络。 他们将状态映射到动作,并学会估计每个动作的 *Q* 值,如下图所示: **深层 Q 网络****DQN**)是近似`Q`函数的神经网络。 他们将状态映射到动作,并学会估计每个动作的`Q`值,如下图所示:
![](img/33ff32cb-b608-417d-afd0-60e35ff0f6d4.png) ![](img/33ff32cb-b608-417d-afd0-60e35ff0f6d4.png)
...@@ -99,13 +99,13 @@ ...@@ -99,13 +99,13 @@
# 开发与探索 # 开发与探索
通常,我们希望代理遵循*贪婪*策略,这意味着我们希望代理采取具有最大 *Q* 值的操作。 在学习网络的同时,我们不希望它总是贪婪地表现。 如果这样做,它将永远不会探索新的选择,也不会学习新的东西。 因此,我们需要我们的代理偶尔执行不符合规定的政策。 通常,我们希望代理遵循*贪婪*策略,这意味着我们希望代理采取具有最大`Q`值的操作。 在学习网络的同时,我们不希望它总是贪婪地表现。 如果这样做,它将永远不会探索新的选择,也不会学习新的东西。 因此,我们需要我们的代理偶尔执行不符合规定的政策。
平衡这种探索的最佳方法是一个持续不断的研究主题,并且已经使用了很长时间。 但是,我们将使用的方法非常简单。 代理每次执行操作时,我们都会生成一个随机数。 如果该数字等于或小于某个阈值![](img/36beb758-d104-411f-8912-4fedf4502d67.png),则代理将采取随机措施。 这称为**∈-贪婪策略** 平衡这种探索的最佳方法是一个持续不断的研究主题,并且已经使用了很长时间。 但是,我们将使用的方法非常简单。 代理每次执行操作时,我们都会生成一个随机数。 如果该数字等于或小于某个阈值`ε`,则代理将采取随机措施。 这称为 **ε 贪婪策略**
代理第一次启动时,对世界了解不多,应该探索更多。 随着代理变得越来越聪明,它可能应该减少探索并更多地使用其对环境的了解。 为此,我们只需要在训练时逐渐降低![](img/0bf64549-9cd7-4e9e-9941-71b0a980e034.png)。 在我们的示例中,我们将每转降低ε的衰减率,以使它随每个动作线性减小。 代理第一次启动时,对世界了解不多,应该探索更多。 随着代理变得越来越聪明,它可能应该减少探索并更多地使用其对环境的了解。 为此,我们只需要在训练时逐渐降低`ε`。 在我们的示例中,我们将每转降低`ε`的衰减率,以使它随每个动作线性减小。
综上所述,我们有一个**线性退火ε-贪心 Q 策略**,说起来既简单又有趣。 综上所述,我们有一个**线性退火 ε - 贪心 Q 策略**,说起来既简单又有趣。
# 深心 # 深心
...@@ -164,8 +164,8 @@ next_state, reward, done, info = env.step(action) ...@@ -164,8 +164,8 @@ next_state, reward, done, info = env.step(action)
好消息,我们终于可以开始编码了。 在本部分中,我将演示两种名为 **CartPole****Lunar Lander** 的 Keras-RL 代理。 我选择这些示例是因为它们不会消耗您的 GPU 和云预算来运行。 它们可以很容易地扩展到 Atari 问题,我在本书的 Git 存储库中也包括了其中之一。 您可以照常在`Chapter12`文件夹中找到所有这些代码。 让我们快速讨论一下这两种环境: 好消息,我们终于可以开始编码了。 在本部分中,我将演示两种名为 **CartPole****Lunar Lander** 的 Keras-RL 代理。 我选择这些示例是因为它们不会消耗您的 GPU 和云预算来运行。 它们可以很容易地扩展到 Atari 问题,我在本书的 Git 存储库中也包括了其中之一。 您可以照常在`Chapter12`文件夹中找到所有这些代码。 让我们快速讨论一下这两种环境:
* **CartPole** :CartPole 环境由平衡在推车上的杆组成。 代理商必须学习如何在立柱下方的推车移动时垂直平衡立柱。 给智能体指定了推车的位置,推车的速度,杆的角度和杆的旋转速度作为输入。 代理可以在推车的任一侧施加力。 如果电线杆与垂直线的夹角下降超过 15 度,我们的经纪人就此告吹。 * **CartPole**:CartPole 环境由平衡在推车上的杆组成。 代理商必须学习如何在立柱下方的推车移动时垂直平衡立柱。 给智能体指定了推车的位置,推车的速度,杆的角度和杆的旋转速度作为输入。 代理可以在推车的任一侧施加力。 如果电线杆与垂直线的夹角下降超过 15 度,我们的经纪人就此告吹。
* **Lunar Lander** :Lunar Lander 的环境更具挑战性。 特工必须将月球着陆器降落在着陆垫上。 月亮的表面会发生变化,着陆器的方位也会在每个情节发生变化。 该代理将获得一个八维数组,用于描述每个步骤中的世界状态,并且可以在该步骤中执行四个操作之一。 代理可以选择不执行任何操作,启动其主引擎,启动其左向引擎或启动其右向引擎。 * **Lunar Lander**:Lunar Lander 的环境更具挑战性。 特工必须将月球着陆器降落在着陆垫上。 月亮的表面会发生变化,着陆器的方位也会在每个情节发生变化。 该代理将获得一个八维数组,用于描述每个步骤中的世界状态,并且可以在该步骤中执行四个操作之一。 代理可以选择不执行任何操作,启动其主引擎,启动其左向引擎或启动其右向引擎。
# 购物车杆 # 购物车杆
...@@ -188,7 +188,7 @@ def build_model(state_size, num_actions): ...@@ -188,7 +188,7 @@ def build_model(state_size, num_actions):
return model return model
``` ```
输入将是一个 1 x 状态空间向量,每个可能的动作都有一个输出神经元,它将预测每个步骤该动作的 Q 值。 通过获取输出的`argmax`,我们可以选择 *Q* 值最高的动作,但是我们不必自己做,因为 Keras-RL 会为我们做。 输入将是一个 1 x 状态空间向量,每个可能的动作都有一个输出神经元,它将预测每个步骤该动作的`Q`值。 通过获取输出的`argmax`,我们可以选择`Q`值最高的动作,但是我们不必自己做,因为 Keras-RL 会为我们做。
# 记忆 # 记忆
...@@ -223,7 +223,7 @@ dqn.compile(Adam(lr=1e-3), metrics=['mae']) ...@@ -223,7 +223,7 @@ dqn.compile(Adam(lr=1e-3), metrics=['mae'])
此时,其中两个参数`target_model_update``nb_steps_warmup`可能还不熟悉: 此时,其中两个参数`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`参数控制这种情况发生的频率。 * `target_model_update`:Q 函数是递归的,当代理更新它的网络以获取 Q(s,a)时,更新也影响其对 Q(s',a)所​​做的预测。 这会导致网络非常不稳定。 大多数深度 Q 网络实现解决此限制的方法是使用目标网络,该目标网络是未经训练的深度 Q 网络的副本,而经常被新副本替换。 `target_model_update`参数控制这种情况发生的频率。
# 训练 # 训练
...@@ -321,7 +321,7 @@ dqn.fit(env, nb_steps=1000000, ...@@ -321,7 +321,7 @@ dqn.fit(env, nb_steps=1000000,
callbacks=callbacks) callbacks=callbacks)
``` ```
您可以通过调整参数 gamma(默认值为 0.99)来进一步改进此示例。 如果您从 *Q* 函数中调用,此参数会减少或增加 *Q* 函数中将来奖励的影响。 您可以通过调整参数 gamma(默认值为 0.99)来进一步改进此示例。 如果您从`Q`函数中调用,此参数会减少或增加`Q`函数中将来奖励的影响。
# 结果 # 结果
...@@ -329,7 +329,7 @@ dqn.fit(env, nb_steps=1000000, ...@@ -329,7 +329,7 @@ dqn.fit(env, nb_steps=1000000,
![](img/a22a91ff-1c32-4a61-90bb-3b52d6555309.png) ![](img/a22a91ff-1c32-4a61-90bb-3b52d6555309.png)
希望这个例子可以说明,尽管深层 Q 网络并不是*火箭科学,但*仍可用于控制火箭。 希望这个例子可以说明,尽管深层 Q 网络并不是*火箭科学*,但仍可用于控制火箭。
# 摘要 # 摘要
......
# 生成对抗网络 # 生成对抗网络
尽管我在本书中花了很多时间谈论分类或估计的网络,但在本章中,我将向您展示一些具有创建能力的深度神经网络。 **生成对抗网络****GAN**)通过两个内部深层网络之间的内部竞争来学习如何做到这一点,我们将在下面讨论。 在**深度卷积通用对抗网络****DCGAN**)的情况下,这是我将在本章中重点介绍的 GAN 类型,该网络将学习创建类似于 训练数据集中的图像。 尽管我在本书中花了很多时间谈论分类或估计的网络,但在本章中,我将向您展示一些具有创建能力的深度神经网络。 **生成对抗网络****GAN**)通过两个内部深层网络之间的内部竞争来学习如何做到这一点,我们将在下面讨论。 在**深度卷积生成对抗网络****DCGAN**)的情况下,这是我将在本章中重点介绍的 GAN 类型,该网络将学习创建类似于 训练数据集中的图像。
我们将在本章介绍以下主题: 我们将在本章介绍以下主题:
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
# 深度卷积 GAN 架构 # 深度卷积 GAN 架构
关于 GAN 的论文很多,每篇都提出了新的新颖架构和调整。 但是,它们中的大多数至少在某种程度上基于**深卷积 GAN****DCGAN**)。 在本章的其余部分中,我们将重点介绍这种模型,因为当您采用此处未介绍的新的令人兴奋的 GAN 架构(例如**有条件 GAN****cGAN**),Stack GAN,InfoGAN 或 Wasserstein GAN,或者可能还有一些其他的新变种,您可能会选择接下来看看。 关于 GAN 的论文很多,每篇都提出了新的新颖架构和调整。 但是,它们中的大多数至少在某种程度上基于**深度卷积 GAN****DCGAN**)。 在本章的其余部分中,我们将重点介绍这种模型,因为当您采用此处未介绍的新的令人兴奋的 GAN 架构(例如**条件 GAN****CGAN**),Stack GAN,InfoGAN 或 Wasserstein GAN),或者可能还有一些其他的新变种,您可能会选择接下来看看。
DCGAN 由 Alex Radford,Luke Metz 和 Soumith Chintala 在论文[《深度卷积生成对抗网络》](https://arxiv.org/pdf/1511.06434.pdf)中提出。 DCGAN 由 Alex Radford,Luke Metz 和 Soumith Chintala 在论文[《深度卷积生成对抗网络》](https://arxiv.org/pdf/1511.06434.pdf)中提出。
...@@ -37,23 +37,23 @@ GAN 的整体体系结构如下图所示。 生成器和判别器分别是单独 ...@@ -37,23 +37,23 @@ GAN 的整体体系结构如下图所示。 生成器和判别器分别是单独
![](img/370d895f-0973-49cf-8e8a-a9746d082883.png) ![](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 之前,我们还要介绍更多细节。 接下来,让我们更深入地研究生成器。 当然,在您准备好构建自己的 GAN 之前,我们还要介绍更多细节。 接下来,让我们更深入地研究生成器。
# 发电机架构 # 发电机架构
在此示例中,我们使用适合于生成 *28 x 28* 灰度图像的图层大小,这正是我们稍后在 MNIST 示例中将要执行的操作。 如果您以前没有使用过生成器,那么生成器的算法可能会有些棘手,因此我们将在遍历每一层时进行介绍。 下图显示了体系结构: 在此示例中,我们使用适合于生成`28 x 28`灰度图像的图层大小,这正是我们稍后在 MNIST 示例中将要执行的操作。 如果您以前没有使用过生成器,那么生成器的算法可能会有些棘手,因此我们将在遍历每一层时进行介绍。 下图显示了体系结构:
![](img/6e7de048-2461-4be3-a702-f4d0a87d3c43.png) ![](img/6e7de048-2461-4be3-a702-f4d0a87d3c43.png)
生成器的输入只是 100 x 1 的随机向量,我们将其称为噪声向量。 当此噪声矢量是从正态分布生成时,GAN 往往工作得最好。 生成器的输入只是 100 x 1 的随机向量,我们将其称为噪声向量。 当此噪声矢量是从正态分布生成时,GAN 往往工作得最好。
网络的第一层是密集的并且完全连接。 它为我们提供了一种建立线性代数的方法,以便最终得到正确的输出形状。 对于每个卷积块,我们最终将第一轴和第二轴(最终将成为图像的高度和宽度的行和列)加倍,而通道数逐渐缩小到 1。我们最终需要高度和宽度 输出张数为 28。因此,我们将需要从 *7 x 7 x 128* 张量开始,以便它可以移动到 *14 x 14* ,然后最终是 *28 x 28* 。 为此,我们将密集层的大小设置为 *128 x 7 x 7* 神经元或 6,272 单位。 这使我们可以将密集层的输出重塑为 *7 x 7 x 128* 。 如果现在看来这还不算什么,请不用担心,在编写代码后,这才有意义。 网络的第一层是密集的并且完全连接。 它为我们提供了一种建立线性代数的方法,以便最终得到正确的输出形状。 对于每个卷积块,我们最终将第一轴和第二轴(最终将成为图像的高度和宽度的行和列)加倍,而通道数逐渐缩小到 1。我们最终需要高度和宽度 输出张数为 28。因此,我们将需要从`7 x 7 x 128`张量开始,以便它可以移动到`14 x 14`,然后最终是`28 x 28`。 为此,我们将密集层的大小设置为`128 x 7 x 7`神经元或 6,272 单位。 这使我们可以将密集层的输出重塑为`7 x 7 x 128`。 如果现在看来这还不算什么,请不用担心,在编写代码后,这才有意义。
在完全连接的层之后,事情变得更加简单。 就像我们一直一样,我们正在使用卷积层。 但是,这次我们反向使用它们。 我们不再使用最大池来缩减样本量。 取而代之的是,我们进行上采样,在学习视觉特征时使用卷积来构建我们的网络,并最终输出适当形状的张量。 在完全连接的层之后,事情变得更加简单。 就像我们一直一样,我们正在使用卷积层。 但是,这次我们反向使用它们。 我们不再使用最大池来缩减样本量。 取而代之的是,我们进行上采样,在学习视觉特征时使用卷积来构建我们的网络,并最终输出适当形状的张量。
...@@ -69,7 +69,7 @@ GAN 的整体体系结构如下图所示。 生成器和判别器分别是单独 ...@@ -69,7 +69,7 @@ GAN 的整体体系结构如下图所示。 生成器和判别器分别是单独
# 叠训 # 叠训
DCGAN 框架是使用迷你批处理来进行训练的,这与我之前在本书中对网络进行训练的方式相同。 但是,稍后在构建代码时,您会注意到我们正在构建一个训练循环,该循环明确控制每个更新批处理的情况,而不仅仅是调用`models.fit()`方法并依靠 Keras 为我们处理它。 我这样做是因为 GAN 训练需要多个模型来更新同一批次中的权重,所以它比我们以前所做的单个参数更新要稍微复杂一些。 DCGAN 框架是使用迷你批量来进行训练的,这与我之前在本书中对网络进行训练的方式相同。 但是,稍后在构建代码时,您会注意到我们正在构建一个训练循环,该循环明确控制每个更新批量的情况,而不仅仅是调用`models.fit()`方法并依靠 Keras 为我们处理它。 我这样做是因为 GAN 训练需要多个模型来更新同一批次中的权重,所以它比我们以前所做的单个参数更新要稍微复杂一些。
对 DCGAN 进行培训的过程分为两步,每批次进行一次。 对 DCGAN 进行培训的过程分为两步,每批次进行一次。
...@@ -85,7 +85,7 @@ DCGAN 框架是使用迷你批处理来进行训练的,这与我之前在本 ...@@ -85,7 +85,7 @@ DCGAN 框架是使用迷你批处理来进行训练的,这与我之前在本
![](img/c8cc7ae9-1801-46d0-af96-142955d6a9a2.png) ![](img/c8cc7ae9-1801-46d0-af96-142955d6a9a2.png)
鉴别器将提出一些预测,我们可以称之为![](img/d18fdfd8-3bd4-4f7d-b1f2-e0e754230335.png)。 此堆栈的`loss`函数将是二进制交叉熵,并且我们将`loss`函数的标签传递为 1,我们可以考虑 *y* 。 如您在本书前面所提到的, *y* 和![](img/d16a73a6-36ff-4092-ac21-700ea76f558c.png)之间的`loss`转换为梯度,然后通过鉴别器传给生成器。 这将更新生成器权重,使它可以从鉴别者对问题空间的了解中受益,以便它可以学习创建更逼真的生成图像。 鉴别器将提出一些预测,我们可以称之为![](img/d18fdfd8-3bd4-4f7d-b1f2-e0e754230335.png)。 此堆栈的`loss`函数将是二进制交叉熵,并且我们将`loss`函数的标签传递为 1,我们可以考虑`y`。 如您在本书前面所提到的, `y`和![](img/d16a73a6-36ff-4092-ac21-700ea76f558c.png)之间的`loss`转换为梯度,然后通过鉴别器传给生成器。 这将更新生成器权重,使它可以从鉴别者对问题空间的了解中受益,以便它可以学习创建更逼真的生成图像。
然后重复这两个步骤,直到生成器能够创建与训练集中的数据相似的数据,使得判别器无法再将两个数据集区分开,这成为了一个猜谜游戏。 鉴别器。 此时,发电机将不再能够改进。 当我们找到纳什均衡时,就对网络进行了训练。 然后重复这两个步骤,直到生成器能够创建与训练集中的数据相似的数据,使得判别器无法再将两个数据集区分开,这成为了一个猜谜游戏。 鉴别器。 此时,发电机将不再能够改进。 当我们找到纳什均衡时,就对网络进行了训练。
...@@ -109,8 +109,8 @@ DCGAN 框架是使用迷你批处理来进行训练的,这与我之前在本 ...@@ -109,8 +109,8 @@ DCGAN 框架是使用迷你批处理来进行训练的,这与我之前在本
我之前已经提到过 Soumith Chintala 的 [GAN hacks Git](https://github.com/soumith/ganhacks),当您试图使 GAN 稳定时,这是一个很好的起点。 既然我们已经讨论了训练稳定的 GAN 会有多么困难,让我们来谈谈一些安全的选择,这些选择可能会帮助您成功找到自己的地方。 尽管有很多技巧,但以下是本章中尚未涵盖的我的主要建议: 我之前已经提到过 Soumith Chintala 的 [GAN hacks Git](https://github.com/soumith/ganhacks),当您试图使 GAN 稳定时,这是一个很好的起点。 既然我们已经讨论了训练稳定的 GAN 会有多么困难,让我们来谈谈一些安全的选择,这些选择可能会帮助您成功找到自己的地方。 尽管有很多技巧,但以下是本章中尚未涵盖的我的主要建议:
* **批处理规范**:使用批处理规范化时,请为真实数据和伪数据构造不同的微型批处理,并分别进行更新。 * **批量规范**:使用批量规范化时,请为真实数据和伪数据构造不同的微型批量,并分别进行更新。
* **泄漏的 ReLU** :泄漏的 ReLU 是 ReLU 激活功能的变异。 回想一下 ReLU 函数是![](img/f11e5dcb-d17d-4280-9485-b003fd9e0d4b.png) * **泄漏的 ReLU**:泄漏的 ReLU 是 ReLU 激活功能的变异。 回想一下 ReLU 函数是![](img/f11e5dcb-d17d-4280-9485-b003fd9e0d4b.png)
但是,泄漏的 ReLU 可以表示为: 但是,泄漏的 ReLU 可以表示为:
...@@ -118,8 +118,8 @@ DCGAN 框架是使用迷你批处理来进行训练的,这与我之前在本 ...@@ -118,8 +118,8 @@ DCGAN 框架是使用迷你批处理来进行训练的,这与我之前在本
当设备不工作时,泄漏的 ReLU 允许非常小的非零梯度。 这可以消除消失的梯度,当我们像在鉴别器和生成器的组合中那样将多个层堆叠在一起时,这总是一个问题。 当设备不工作时,泄漏的 ReLU 允许非常小的非零梯度。 这可以消除消失的梯度,当我们像在鉴别器和生成器的组合中那样将多个层堆叠在一起时,这总是一个问题。
* **使用发生器中的压降**:这将产生噪声并防止模式崩溃。 * **在生成器中使用丢弃**:这将产生噪声并防止模式崩溃。
* **使用软标签**:对于真实示例,请使用介于 0.7 和 1 之间的标签,对于伪示例,请使用介于 0 和 0.3 之间的标签。 这种噪声有助于保持信息从鉴别器流向发生器。 * **使用软标签**:对于真实示例,请使用介于 0.7 和 1 之间的标签,对于伪示例,请使用介于 0 和 0.3 之间的标签。 这种噪声有助于保持信息从鉴别器流向生成器。
在本章的其他地方,我们还将介绍许多其他的 GAN 黑客。 但是,我认为在成功实施 GAN 时,这几项技巧是最重要的。 在本章的其他地方,我们还将介绍许多其他的 GAN 黑客。 但是,我认为在成功实施 GAN 时,这几项技巧是最重要的。
...@@ -143,7 +143,7 @@ def load_data(): ...@@ -143,7 +143,7 @@ def load_data():
return X_train return X_train
``` ```
您可能已经注意到,我没有返回任何标签或测试数据集。 我将只使用训练数据集。 不需要标签,因为我要使用的唯一标签是 *0* 代表假货, *1* 代表真货。 这些是真实的图像,因此将在鉴别器上将它们全部分配为标签 1。 您可能已经注意到,我没有返回任何标签或测试数据集。 我将只使用训练数据集。 不需要标签,因为我要使用的唯一标签是`0`代表假货,`1`代表真货。 这些是真实的图像,因此将在鉴别器上将它们全部分配为标签 1。
# 建造发电机 # 建造发电机
...@@ -171,9 +171,9 @@ def build_generator(noise_shape=(100,)): ...@@ -171,9 +171,9 @@ def build_generator(noise_shape=(100,)):
return model return model
``` ```
我们以前没有使用过`UpSampling2D`层。 该层将增加输入张量的行和列,从而使通道保持不变。 它通过重复输入张量中的值来实现。 默认情况下,它将使输入加倍。 如果给`UpSampling2D`层一个 *7 x 7 x 128* 输入,它将给我们一个 *14 x 14 x 128* 输出。 我们以前没有使用过`UpSampling2D`层。 该层将增加输入张量的行和列,从而使通道保持不变。 它通过重复输入张量中的值来实现。 默认情况下,它将使输入加倍。 如果给`UpSampling2D`层一个`7 x 7 x 128`输入,它将给我们一个`14 x 14 x 128`输出。
通常,当我们构建一个 CNN 时,我们从一个非常高和宽的图像开始,并使用卷积层来获得一个非常深但又不高又不宽的张量。 在这里,我将相反。 我将使用一个密集层并进行重塑,以 *7 x 7 x 128* 张量开始,然后将其加倍两次后,剩下 *28 x 28* 张量。 由于我需要灰度图像,因此可以使用具有单个单元的卷积层来获得 *28 x 28 x 1* 输出。 通常,当我们构建一个 CNN 时,我们从一个非常高和宽的图像开始,并使用卷积层来获得一个非常深但又不高又不宽的张量。 在这里,我将相反。 我将使用一个密集层并进行重塑,以`7 x 7 x 128`张量开始,然后将其加倍两次后,剩下`28 x 28`张量。 由于我需要灰度图像,因此可以使用具有单个单元的卷积层来获得`28 x 28 x 1`输出。
这种生成器运算法则有点令人反感,乍一看似乎很尴尬,但是经过几个小时的痛苦之后,您就会掌握它了! 这种生成器运算法则有点令人反感,乍一看似乎很尴尬,但是经过几个小时的痛苦之后,您就会掌握它了!
...@@ -208,9 +208,9 @@ def build_discriminator(img_shape): ...@@ -208,9 +208,9 @@ def build_discriminator(img_shape):
return model return model
``` ```
首先,您可能会注意到形状奇怪的`zeroPadding2D()`层。 第二次卷积后,我们的张量从 *28 x 28 x 3 变为 7 x 7 x 64* 。 这一层使我们回到偶数,在行和列的一侧都加零,这样我们的张量现在为 *8 x 8 x 64* 首先,您可能会注意到形状奇怪的`zeroPadding2D()`层。 第二次卷积后,我们的张量从`28 x 28 x 3`变为`7 x 7 x 64`。 这一层使我们回到偶数,在行和列的一侧都加零,这样我们的张量现在为`8 x 8 x 64`
更不寻常的是同时使用批处理规范化和辍学。 通常,这两层不能一起使用。 但是,就 GAN 而言,它们似乎确实使网络受益。 更不寻常的是同时使用批规范化和辍学。 通常,这两层不能一起使用。 但是,就 GAN 而言,它们似乎确实使网络受益。
# 建立堆叠模型 # 建立堆叠模型
...@@ -229,7 +229,7 @@ real = discriminator(img) ...@@ -229,7 +229,7 @@ real = discriminator(img)
combined = Model(z, real) combined = Model(z, real)
``` ```
注意,在建立模型之前,我们将鉴别器的训练属性设置为`False`。 这意味着对于该模型,在反向传播期间,我们将不会更新鉴别器的权重。 正如我们在*堆栈训练*部分中提到的,我们将冻结这些权重,仅将发电机的权重与堆栈一起移动。 鉴别器将单独训练。 注意,在建立模型之前,我们将鉴别器的训练属性设置为`False`。 这意味着对于该模型,在反向传播期间,我们将不会更新鉴别器的权重。 正如我们在“栈式训练”部分中提到的,我们将冻结这些权重,仅将发电机的权重与堆栈一起移动。 鉴别器将单独训练。
现在,所有模型都已构建,需要对其进行编译,如以下代码所示: 现在,所有模型都已构建,需要对其进行编译,如以下代码所示:
...@@ -253,7 +253,7 @@ combined.compile(loss='binary_crossentropy', optimizer=gen_optimizer) ...@@ -253,7 +253,7 @@ combined.compile(loss='binary_crossentropy', optimizer=gen_optimizer)
以前,我们曾很奢侈地在模型上调用`.fit()`,让 Keras 处理将数据分成小批和为我们训练的痛苦过程。 以前,我们曾很奢侈地在模型上调用`.fit()`,让 Keras 处理将数据分成小批和为我们训练的痛苦过程。
不幸的是,因为我们需要为一个批处理器对鉴别器和堆叠模型一起执行单独的更新,所以我们将不得不用老式的方式来做一些循环。 这就是过去一直做的事情,因此虽然可能需要做更多的工作,但它的确使我感到怀旧。 以下代码说明了训练技术: 不幸的是,因为我们需要为一个批器对鉴别器和堆叠模型一起执行单独的更新,所以我们将不得不用老式的方式来做一些循环。 这就是过去一直做的事情,因此虽然可能需要做更多的工作,但它的确使我感到怀旧。 以下代码说明了训练技术:
```py ```py
num_examples = X_train.shape[0] num_examples = X_train.shape[0]
...@@ -296,9 +296,9 @@ for epoch in range(epochs + 1): ...@@ -296,9 +296,9 @@ for epoch in range(epochs + 1):
fake_labels = np.zeros((half_batch, 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`的切片用作真实图像,如以下代码所示: 注意,这里的形状是`half_batch x 28 x 28 x 1``half_batch`正是您所想的。 我们将创建一半的生成图像,因为另一半将是真实数据,我们将在下一步进行组装。 要获取真实图像,我们将在`X_train`上生成一组随机索引,并将`X_train`的切片用作真实图像,如以下代码所示:
```py ```py
idx = np.random.randint(0, X_train.shape[0], half_batch) idx = np.random.randint(0, X_train.shape[0], half_batch)
...@@ -308,7 +308,7 @@ real_labels = np.ones((half_batch, 1)) ...@@ -308,7 +308,7 @@ real_labels = np.ones((half_batch, 1))
是的,在这种情况下,我们正在抽样更换。 它确实可以解决,但可能不是实施小批量培训的最佳方法。 但是,它可能是最简单,最常见的。 是的,在这种情况下,我们正在抽样更换。 它确实可以解决,但可能不是实施小批量培训的最佳方法。 但是,它可能是最简单,最常见的。
由于我们正在使用这些图像来训练鉴别器,并且由于它们是真实图像,因此我们将它们分配为 *1s* 作为标签,而不是 *0s* 。 现在我们已经组装了鉴别器训练集,我们将更新鉴别器。 还要注意,我们没有使用我们之前讨论的软标签。 那是因为我想让事情尽可能地容易理解。 幸运的是,在这种情况下,网络不需要它们。 我们将使用以下代码来训练鉴别器: 由于我们正在使用这些图像来训练鉴别器,并且由于它们是真实图像,因此我们将它们分配为`1`作为标签,而不是`0`。 现在我们已经组装了鉴别器训练集,我们将更新鉴别器。 还要注意,我们没有使用我们之前讨论的软标签。 那是因为我想让事情尽可能地容易理解。 幸运的是,在这种情况下,网络不需要它们。 我们将使用以下代码来训练鉴别器:
```py ```py
# Train the discriminator (real classified as ones and generated as zeros) # Train the discriminator (real classified as ones and generated as zeros)
...@@ -319,7 +319,7 @@ d_loss = 0.5 * np.add(d_loss_real, d_loss_fake) ...@@ -319,7 +319,7 @@ d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
请注意,这里我使用的是鉴别符的`train_on_batch()`方法。 这是我第一次在本书中使用此方法。 `train_on_batch()`方法正好执行一轮正向和反向传播。 每次我们调用它时,它都会从模型的先前状态更新一次模型。 请注意,这里我使用的是鉴别符的`train_on_batch()`方法。 这是我第一次在本书中使用此方法。 `train_on_batch()`方法正好执行一轮正向和反向传播。 每次我们调用它时,它都会从模型的先前状态更新一次模型。
另请注意,我正在分别对真实图像和伪图像进行更新。 这是我先前在 *Generator 体系结构*部分中引用的 GAN hack Git 上给出的建议。 尤其是在训练的早期阶段,当真实图像和伪图像来自完全不同的分布时,如果我们将两组数据放在同一更新中,则批量归一化将导致训练问题。 另请注意,我正在分别对真实图像和伪图像进行更新。 这是我先前在“生成器体系结构”部分中引用的 GAN hack Git 上给出的建议。 尤其是在训练的早期阶段,当真实图像和伪图像来自完全不同的分布时,如果我们将两组数据放在同一更新中,则批量归一化将导致训练问题。
现在,鉴别器已经更新,是时候更新生成器了。 这是通过更新组合堆栈间接完成的,如以下代码所示: 现在,鉴别器已经更新,是时候更新生成器了。 这是通过更新组合堆栈间接完成的,如以下代码所示:
...@@ -330,7 +330,7 @@ g_loss = combined.train_on_batch(noise, np.ones((batch_size, 1))) ...@@ -330,7 +330,7 @@ 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 ```py
print("Epoch %d Batch %d/%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % print("Epoch %d Batch %d/%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" %
...@@ -360,7 +360,7 @@ for j in range(c): ...@@ -360,7 +360,7 @@ for j in range(c):
plt.close() plt.close()
``` ```
它通过创建噪声矩阵并检索图像矩阵来仅使用生成器。 然后,使用`matplotlib.pyplot`将这些图像保存到 *5 x 5* 网格中的磁盘上。 它通过创建噪声矩阵并检索图像矩阵来仅使用生成器。 然后,使用`matplotlib.pyplot`将这些图像保存到`5 x 5`网格中的磁盘上。
# 模型评估 # 模型评估
...@@ -388,7 +388,7 @@ for j in range(c): ...@@ -388,7 +388,7 @@ for j in range(c):
虽然网络架构在很大程度上保持不变,但我认为有必要向您展示一个使用彩色图像的示例,并在 Git 中提供示例,以便在想要将 GAN 应用于您的 GAN 时有一些起点。 自己的数据。 虽然网络架构在很大程度上保持不变,但我认为有必要向您展示一个使用彩色图像的示例,并在 Git 中提供示例,以便在想要将 GAN 应用于您的 GAN 时有一些起点。 自己的数据。
`CIFAR-10`是一个著名的数据集,包含 60,000 张 *32 x 32 x 3* RGB 彩色图像,分布在 10 个类别中。 这些类别是飞机,汽车,鸟类,猫,鹿,狗,青蛙,马,船和卡车。 希望以后看到生成的图像时,您可能会看到一些可以想象的东西,就像那些对象。 `CIFAR-10`是一个著名的数据集,包含 60,000 张`32 x 32 x 3` RGB 彩色图像,分布在 10 个类别中。 这些类别是飞机,汽车,鸟类,猫,鹿,狗,青蛙,马,船和卡车。 希望以后看到生成的图像时,您可能会看到一些可以想象的东西,就像那些对象。
# 加载 CIFAR-10 # 加载 CIFAR-10
...@@ -404,7 +404,7 @@ def load_data(): ...@@ -404,7 +404,7 @@ def load_data():
# 建造发电机 # 建造发电机
生成器需要产生 *32 x 32 x 3* 图像。 这需要对我们的网络体系结构进行两项细微更改,您可以在此处看到它们: 生成器需要产生`32 x 32 x 3`图像。 这需要对我们的网络体系结构进行两项细微更改,您可以在此处看到它们:
```py ```py
input = Input(noise_shape) input = Input(noise_shape)
...@@ -424,13 +424,13 @@ out = Activation("tanh")(x) ...@@ -424,13 +424,13 @@ out = Activation("tanh")(x)
model = Model(input, out) model = Model(input, out)
``` ```
由于我们需要在 32 处结束,并且我们将两次上采样,因此我们应该从 8 开始。这可以通过将密集层及其相应的重塑层从 *128 * 7 * 7* 更改为*来轻松实现 128 * 8 * 8* 由于我们需要在 32 处结束,并且我们将两次上采样,因此我们应该从 8 开始。这可以通过将密集层及其相应的重塑层从`128 * 7 * 7`更改为`128 * 8 * 8`来轻松实现
由于我们的图像现在包含三个通道,因此最后的卷积层也需要包含三个通道,而不是一个。 这里的所有都是它的; 我们现在可以生成彩色图像! 由于我们的图像现在包含三个通道,因此最后的卷积层也需要包含三个通道,而不是一个。 这里的所有都是它的; 我们现在可以生成彩色图像!
# 建立鉴别器 # 建立鉴别器
鉴别符几乎完全不变。 输入层需要从 *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.
先完成此消息的编辑!
想要评论请 注册