提交 06e1add7 编写于 作者: W wizardforcel

2020-09-07 22:51:57

上级 b6a2ae18
......@@ -17,7 +17,7 @@
深度神经网络架构的结构可能会因网络的应用而有很大差异,但它们都有一些基本组件。 在本节中,我们将简要讨论这些组件。
在本书中,我将深度神经网络定义为一个具有多个隐藏层的网络。 除此之外,我们不会尝试将会员资格限制为*深度学习俱乐部*。 因此,我们的网络可能只有不到 100 个神经元,甚至可能有数百万个。 我们可能会使用特殊的神经元层,包括卷积和递归层,但尽管如此,我们仍将所有这些都称为神经元。
在本书中,我将深度神经网络定义为一个具有多个隐藏层的网络。 除此之外,我们不会尝试将成员限制为*深度学习俱乐部*。 因此,我们的网络可能只有不到 100 个神经元,甚至可能有数百万个。 我们可能会使用特殊的神经元层,包括卷积和循环层,但尽管如此,我们仍将所有这些都称为神经元。
# 神经元
......@@ -35,7 +35,7 @@
# 神经元激活函数
神经元的第二个函数是激活函数,其任务是在神经元之间引入非线性。 乙状结肠激活是一种常用的激活,您可能会通过逻辑回归来熟悉它。 它将神经元的输出压缩到输出空间,其中`z`的非常大的值被驱动为 1,而`z`的非常小的值被驱动为 0。
神经元的第二个函数是激活函数,其任务是在神经元之间引入非线性。 Sigmoid 激活是一种常用的激活,您可能会通过逻辑回归来熟悉它。 它将神经元的输出压缩到输出空间,其中`z`的非常大的值被驱动为 1,而`z`的非常小的值被驱动为 0。
sigmoid 函数如下所示:
......@@ -45,7 +45,7 @@ sigmoid 函数如下所示:
事实证明,激活函数对于中间神经元非常重要。 没有它,可以证明一堆具有线性激活的神经元(实际上不是激活,或更正式地说是`z = z`的激活函数)实际上只是一个线性函数。
在这种情况下,单个线性函数是不理想的,因为在许多情况下,我们的网络可能未针对当前问题指定。 也就是说,由于输入要素和目标变量之间的非线性关系(我们正在预测),网络无法很好地对数据建模。
在这种情况下,单个线性函数是不理想的,因为在许多情况下,我们的网络可能未针对当前问题指定。 也就是说,由于输入特征和目标变量之间的非线性关系(我们正在预测),网络无法很好地对数据建模。
不能用线性函数建模的函数的典型示例是排他的`OR`函数,如下图所示:
......@@ -65,7 +65,7 @@ ReLu,或直线激活,简单定义为:
![](img/699dd350-149d-48aa-80b9-147894e8370c.png)
这是一个安全的赌注,我们在本书中的大部分时间都会使用它。 ReLu 不仅易于计算和分,而且还可以抵抗消失的梯度问题。 ReLu 的唯一缺点是它的一阶导数未精确定义为 0。包括泄漏的 ReLu 在内的变体在计算上更加困难,但针对此问题更健壮。
这是一个安全的赌注,我们在本书中的大部分时间都会使用它。 ReLu 不仅易于计算和分,而且还可以抵抗消失的梯度问题。 ReLu 的唯一缺点是它的一阶导数未精确定义为 0。包括泄漏的 ReLu 在内的变体在计算上更加困难,但针对此问题更健壮。
为了完整起见,以下是 ReLu 的一些明显图表:
......@@ -77,7 +77,7 @@ ReLu,或直线激活,简单定义为:
![](img/39a93548-a5c8-42d4-8696-f1cac167f5b9.png)
在这种情况下,损失函数为`(y_hat - y)^2`,这实际上是平方误差。 因此,我们的`cost`函数`J`实际上只是均方误差,或整个数据集的均方误差的平均值。 按照惯例,添加了术语 1/2 以使某些微积分更干净。
在这种情况下,损失函数为`(y_hat - y)^2`,这实际上是平方误差。 因此,我们的`cost`函数`J`实际上只是均方误差,或整个数据集的均方误差的平均值。 按照惯例,添加了 1/2 以使某些微积分更干净。
# 正向传播过程
......@@ -91,13 +91,13 @@ ReLu,或直线激活,简单定义为:
一旦`y``y_hat`都已知,就可以使用成本函数计算网络误差。 回想一下,代价函数是`loss`函数的平均值。
为了使学习在网络中发生,网络的错误信号必须从最后一层到最后一层通过网络层向后传播。 我们反向传播的目标是在网络中向后传播该误差信号,同时随着信号的传播使用误差信号来更新网络权重。 在数学上,要做到这一点,我们需要对权重进行微调,以使成本函数最小,从而最小化成本函数。 此过程称为梯度下降。
为了使学习在网络中发生,网络的误差信号必须从最后一层到最后一层通过网络层向后传播。 我们反向传播的目标是在网络中向后传播该误差信号,同时随着信号的传播使用误差信号来更新网络权重。 在数学上,要做到这一点,我们需要对权重进行微调,以使成本函数最小,从而最小化成本函数。 此过程称为梯度下降。
梯度是误差函数相对于网络内每个权重的偏导数。 可以使用链法则和上面各层的梯度逐层计算每个权重的梯度。
一旦知道了每一层的梯度,我们就可以使用梯度下降算法来最小化`cost`函数。
梯度下降将重复此更新,直到网络的错误最小化并且该过程收敛为止:
梯度下降将重复此更新,直到网络的误差最小化并且该过程收敛为止:
![](img/1e890c56-85ae-4f6c-82e5-4ae9de2f0fc8.png)
......@@ -165,7 +165,7 @@ Adam 是已知表现最好的**优化器**之一,这是我的首选。 它可
# 什么是 TensorFlow?
TensorFlow 是一个可用于快速构建深度神经网络的库。 在 TensorFlow 中,我们到目前为止已涵盖的数学运算被表示为节点。 这些节点之间的边缘是张量或多维数据阵列。 给定定义为图和损失函数的神经网络,TensorFlow 可以自动计算网络的梯度并优化图以最小化损失函数。
TensorFlow 是一个可用于快速构建深度神经网络的库。 在 TensorFlow 中,我们到目前为止已涵盖的数学运算被表示为节点。 这些节点之间的边缘是张量或多维数据数组。 给定定义为图和损失函数的神经网络,TensorFlow 可以自动计算网络的梯度并优化图以最小化损失函数。
TensorFlow 是 Google 在 2015 年发布的一个开源项目。此后,它已经获得了很大的关注,并拥有庞大的用户社区。 虽然 TensorFlow 提供 Java,C++,Go 和 Python 的 API,但我们仅介绍 Python API。 本书使用了 Python API,因为它既是最常用的,也是开发新模型时最常用的 API。
......@@ -201,7 +201,7 @@ TensorFlow 是 Google 在 2015 年发布的一个开源项目。此后,它已
在使用 GPU 之前,必须先安装 **NVidia CUDA 工具包****cuDNN** 。 我们将安装 CUDA Toolkit 8.0 和 cuDNN v6.0,建议与 TensorFlow v1.4 一起使用。 在您阅读完本段之前,很有可能会发布新版本,因此,请访问 [www.tensorflow.org](http://www.tensorflow.org) 以获取最新的必需版本。
我们将从在 Ubuntu 上安装`build-essential`软件包开始,该软件包包含编译 C++ 程序所需的大部分内容。 代码在这里给出:
我们将从在 Ubuntu 上安装`build-essential`包开始,该包包含编译 C++ 程序所需的大部分内容。 代码在这里给出:
```py
sudo apt-get update
......@@ -262,7 +262,7 @@ sudo cp cuda/lib64/* /usr/local/cuda/lib64
我们将使用`virtualenv`创建一个隔离的 Python 虚拟环境。 尽管这不是严格必要的,但这是一种极好的实践。 这样,我们会将该项目的所有 Python 库保存在一个独立的隔离环境中,该环境不会干扰系统 Python 的安装。 此外,`virtualenv`环境将使以后打包和部署我们的深度神经网络更加容易。
首先,使用 Ubuntu 中的 aptitude 软件包管理器安装`Python``pip``virtualenv`。 以下是代码:
首先,使用 Ubuntu 中的 aptitude 包管理器安装`Python``pip``virtualenv`。 以下是代码:
```py
sudo apt-get install python3-pip python3-dev python-virtualenv
......@@ -285,7 +285,7 @@ $source ~/deep-learn/bin/activate
此时,每次登录时都需要激活要使用的虚拟环境。如果您想始终输入刚刚创建的虚拟环境,可以将`source`命令添加到`~/.bash_profile`
现在我们已经配置了虚拟环境,我们可以根据需要在其中添加 Python 软件包。 首先,请确保我们具有 Python 包管理器`pip`的最新版本:
现在我们已经配置了虚拟环境,我们可以根据需要在其中添加 Python 包。 首先,请确保我们具有 Python 包管理器`pip`的最新版本:
```py
easy_install -U pip
......@@ -378,7 +378,7 @@ pip install keras
# 深度学习中的偏差和方差误差
您可能熟悉典型预测模型中的所谓偏差/方差折衷。 如果您不在,我们将在此处提供快速提醒。 在传统的预测模型中,当我们尝试从偏差中发现误差并从方差中发现误差时,通常会有一些折衷。 因此,让我们看看这两个错误是什么:
您可能熟悉典型预测模型中的所谓偏差/方差折衷。 如果您不在,我们将在此处提供快速提醒。 在传统的预测模型中,当我们尝试从偏差中发现误差并从方差中发现误差时,通常会有一些折衷。 因此,让我们看看这两个误差是什么:
* **偏差误差**:偏差误差是模型引入的误差。 例如,如果您尝试使用线性模型对非线性函数建模,则模型将在指定的下为,*并且偏差误差会很高*
* **方差误差**:方差误差是由训练数据中的随机性引起的误差。 当我们很好地拟合训练分布以至于我们的模型不再泛化时,我们就过拟合或引入了方差误差。
......@@ -391,31 +391,31 @@ pip install keras
![](img/b2cbcf2b-3fd0-4b84-b089-c10eca3b4ded.png)
火车数据集将按预期用于训练网络。
训练数据集将按预期用于训练网络。
验证数据集将用于查找理想的超参数并测量过拟合。 *在时期结束时*,即网络有机会观察训练集中的每个数据点时,我们将对验证集进行预测。 该预测将用于监视过拟合,并将帮助我们知道网络何时完成训练。 像这样在每个时期末尾使用验证设置与典型用法有些不同。 有关保留验证的更多信息,请参考 Hastie 和 Tibshirani 撰写的[《统计学习的要素](https://web.stanford.edu/~hastie/ElemStatLearn)
验证数据集将用于查找理想的超参数并测量过拟合。 *在周期结束时*,即网络有机会观察训练集中的每个数据点时,我们将对验证集进行预测。 该预测将用于监视过拟合,并将帮助我们知道网络何时完成训练。 像这样在每个周期末尾使用验证设置与典型用法有些不同。 有关保留验证的更多信息,请参考 Hastie 和 Tibshirani 撰写的[《统计学习的特征](https://web.stanford.edu/~hastie/ElemStatLearn)
一旦完成所有训练,就将使用测试数据集,以根据网络未看到的一组数据准确地测量模型表现。
验证和测试数据来自同一数据集非常重要。 火车数据集匹配验证和测试不太重要,尽管那仍然是理想的。 例如,如果使用图像增强(对训练图像进行较小的修改以尝试扩大训练集大小),则训练集分布可能不再与验证集分布匹配。 这是可以接受的,并且只要验证和测试来自同一分布,就可以充分测量网络表现。
验证和测试数据来自同一数据集非常重要。 训练数据集匹配验证和测试不太重要,尽管那仍然是理想的。 例如,如果使用图像增强(对训练图像进行较小的修改以尝试扩大训练集大小),则训练集分布可能不再与验证集分布匹配。 这是可以接受的,并且只要验证和测试来自同一分布,就可以充分测量网络表现。
在传统的机器学习应用中,习惯上将 10% 到 20% 的可用数据用于验证和测试。 在深度神经网络中,通常情况是我们的数据量很大,以至于我们可以用更小的验证和测试集来充分测量网络表现。 当数据量达到数以千万计的观测值时,将 98%,1%,1% 的拆分完全合适。
# 在深度神经网络中管理偏差和方差
现在,我们已经定义了如何构造数据并刷新偏见和方差,现在让我们考虑如何控制深度神经网络中的偏见和方差。
现在,我们已经定义了如何构造数据并刷新偏差和方差,现在让我们考虑如何控制深度神经网络中的偏差和方差。
* **高偏差**:在训练集上进行预测时,具有高偏差的网络将具有非常高的错误率。 该模型在拟合数据方面表现不佳。 为了减少偏差,您可能需要更改网络架构。 您可能需要添加层,神经元或两者。 使用卷积或循环网络可能可以更好地解决您的问题。
当然,有时由于信号不足或非常困难的问题而导致问题偏高,因此请务必以合理的速度校准您的期望(我喜欢从对人的准确进行校准开始)。
当然,有时由于信号不足或非常困难的问题而导致问题偏高,因此请务必以合理的速度校准您的期望(我喜欢从对人的准确进行校准开始)。
* **高方差**:具有低偏差误差的网络很好地拟合了训练数据; 但是,如果验证误差大于测试误差,则网络已开始过拟合训练数据。 减少差异的两种最佳方法是添加数据并向网络添加正则化。
添加数据很简单,但并非总是可能的。 在整本书中,我们将介绍适用的正则化技术。 我们将讨论的最常见的正则化技术是 L2 正则化,辍学和批量归一化。
添加数据很简单,但并非总是可能的。 在整本书中,我们将介绍适用的正则化技术。 我们将讨论的最常见的正则化技术是 L2 正则化,丢弃法和批量归一化。
# K 折交叉验证
如果您有机器学习的经验,您可能想知道为什么我会选择通过 K 折交叉验证而不是保留(火车/验证/测试)验证。 训练深度神经网络是一项非常昂贵的操作,并且非常简单地讲,针对每个我们想探索的超参数训练 K 个神经网络通常不太实用。
如果您有机器学习的经验,您可能想知道为什么我会选择通过 K 折交叉验证而不是保留(训练/验证/测试)验证。 训练深度神经网络是一项非常昂贵的操作,并且非常简单地讲,针对每个我们想探索的超参数训练 K 个神经网络通常不太实用。
我们可以确信,在给定的验证和测试集足够大的情况下,留出验证会做得很好。 在大多数情况下,我们希望在有大量数据的情况下应用深度学习,从而获得足够的值和测试集。
......
......@@ -118,7 +118,7 @@ def load_data():
![](img/15697b3b-001e-4120-a0d1-c4447a4dc47f.png)
很简单,MAE 是数据集中所有示例的平均无符号错误。 与 RMSE 非常相似; 但是,我们使用`y``y_hat`之间的差的绝对值代替平均平方误差的平方根:
很简单,MAE 是数据集中所有示例的平均无符号误差。 与 RMSE 非常相似; 但是,我们使用`y``y_hat`之间的差的绝对值代替平均平方误差的平方根:
![](img/f0a26b2f-9d67-4235-b6f1-c15e931a4efd.png)
......@@ -126,7 +126,7 @@ def load_data():
# 在 Keras 中建立 MLP
Keras 使用模型对象的实例来包含神经网络。 对于熟悉 scikit-learn 的人来说,这可能是相当熟悉的。 略有不同的是 Keras 模型包含一组层。 这一组层需要由我们定义。 只需很少的代码,就可以在网络架构中实现惊人的灵活性。
Keras 使用模型对象的实例来包含神经网络。 对于熟悉 scikit-learn 的人来说,这可能是相当熟悉的。 略有不同的是 Keras 模型包含一组层。 这一组层需要由我们定义。 只需很少的代码,就可以在网络架构中实现惊人的灵活性。
Keras 当前有两个用于构建模型的 API。 在我的示例中,我将使用函数式 API。 它稍微冗长一些,但可以提供更多的灵活性。 我建议尽可能使用函数式 API。
......@@ -200,12 +200,12 @@ model.compile(optimizer=adam_optimizer, loss='mean_absolute_error')
model.fit(x=data["train_X"], y=data["train_y"], batch_size=32, epochs=200, verbose=1, validation_data=(data["val_X"], data["val_y"]))
```
让我们来看一下 Keras `fit`方法所采用的一些重要论点。 我将假设您熟悉小批量梯度下降和训练纪元,但如果不熟悉,请查看第 1 章“深度学习的基础知识”, 概述。 Keras 拟合模型中的重要参数如下:
让我们来看一下 Keras `fit`方法所采用的一些重要参数。 我将假设您熟悉小批量梯度下降和训练周期,但如果不熟悉,请查看第 1 章“深度学习的基础知识”, 概述。 Keras 拟合模型中的重要参数如下:
* `batch_size`:Keras 的默认批次大小为 32。批次大小是 Keras 将使用的迷你批次的大小。 当然,这意味着 Keras 假设您要使用小批量梯度下降。 如果由于某种原因不想使用小批量梯度,可以设置`batch_size=None`
* `epochs`:一个时期只是整个训练集的单次通过。 在实践中,您需要在训练网络时对其进行监视,以了解网络何时收敛,因此`epochs`是一个易于学习的超参数。 稍后,我们将看到可以在每个时期甚至比最后一个时期更好的每个时期保存模型的权重。 一旦知道如何做到这一点,我们就可以选择我们认为最好的时期,并实施一种基于人的早期停止。
* `validation_data`:在这里,我们指定验证集。 在每个阶段结束时,Keras 将在验证集上测试模型,并使用损失函数和您指定的任何其他指标输出结果。 另外,您可以将`validation_split`设置为浮点值,以指定要用于验证的火车组的百分比。 这两个选项都可以正常工作,但是在数据集拆分方面,我希望讲得很明确。
* `verbose`:这有点不言而喻; 但是,值得一提。 `verbose=1`输出一个进度条,显示当前纪元的状态,在纪元结束时,Keras 将输出训练和验证损失。 也可以将`verbose`设置为 2(每个小批量输出丢失信息),将其设置为 0(使 Keras 保持静音)。
* `epochs`:一个周期只是整个训练集的单次通过。 在实践中,您需要在训练网络时对其进行监视,以了解网络何时收敛,因此`epochs`是一个易于学习的超参数。 稍后,我们将看到可以在每个周期甚至比最后一个周期更好的每个周期保存模型的权重。 一旦知道如何做到这一点,我们就可以选择我们认为最好的周期,并实现一种基于人的早期停止。
* `validation_data`:在这里,我们指定验证集。 在每个阶段结束时,Keras 将在验证集上测试模型,并使用损失函数和您指定的任何其他指标输出结果。 另外,您可以将`validation_split`设置为浮点值,以指定要用于验证的训练组的百分比。 这两个选项都可以正常工作,但是在数据集拆分方面,我希望讲得很明确。
* `verbose`:这有点不言而喻; 但是,值得一提。 `verbose=1`输出一个进度条,显示当前周期的状态,在周期结束时,Keras 将输出训练和验证损失。 也可以将`verbose`设置为 2(每个小批量输出损失信息),将其设置为 0(使 Keras 保持静音)。
# 评估模型的表现
......@@ -225,7 +225,7 @@ Model Val MAE: 0.213255747475
Model Test MAE: 0.199885450841
```
请记住,我们的数据已缩放为 0 均值和单位方差。 `Train MAE``0.19`,而我们的`Val MAE``0.21`。 这两个错误彼此之间非常接近,所以过分适合并不是我太在意的事情。 因为我预计会有一些我看不到的过拟合(通常是更大的问题),所以我认为此模型可能有太多偏差。 换句话说,我们可能无法足够紧密地拟合数据。 发生这种情况时,我们需要为我们的模型添加更多的层,更多的神经元或两者。 我们需要更深入。 让我们接下来做。
请记住,我们的数据已缩放为 0 均值和单位方差。 `Train MAE``0.19`,而我们的`Val MAE``0.21`。 这两个误差彼此之间非常接近,所以过分适合并不是我太在意的事情。 因为我预计会有一些我看不到的过拟合(通常是更大的问题),所以我认为此模型可能有太多偏差。 换句话说,我们可能无法足够紧密地拟合数据。 发生这种情况时,我们需要为我们的模型添加更多的层,更多的神经元或两者。 我们需要更深入。 让我们接下来做。
我们可以尝试通过以更多神经元的形式向网络添加参数来减少网络偏差。 虽然您可能会开始尝试优化优化器,但通常最好先找到自己熟悉的网络架构。
......@@ -257,7 +257,7 @@ def build_network(input_features=None):
# 测量深度神经网络表现
在这个问题上,深层网络真的比 MLP 好吗? 让我们找出答案! 训练了 500 个纪元后,模型的效果如下:
在这个问题上,深层网络真的比 MLP 好吗? 让我们找出答案! 训练了 500 个周期后,模型的效果如下:
```py
Model Train MAE: 0.0753991873787
......@@ -265,15 +265,15 @@ Model Val MAE: 0.189703853999
Model Test MAE: 0.190189985043
```
我们可以看到`Train MAE`现在从`0.19`减少到`0.075`。 我们大大降低了网络的偏
我们可以看到`Train MAE`现在从`0.19`减少到`0.075`。 我们大大降低了网络的偏
但是,我们的差异增加了。 训练误差和验证误差之间的差异要大得多。 我们的`Val`设置错误确实略有下降,这很好; 但是,训练误差和验证误差之间的巨大差距表明我们开始过度适应训练集。
但是,我们的差异增加了。 训练误差和验证误差之间的差异要大得多。 我们的`Val`集误差确实略有下降,这很好; 但是,训练误差和验证误差之间的巨大差距表明我们开始过度适应训练集。
在这种情况下,减少差异的最直接方法是添加其他训练数据或应用诸如 L2 正则化或辍学之类的正则化技术,我们将在下一章中介绍。
在这种情况下,减少差异的最直接方法是添加其他训练数据或应用诸如 L2 正则化或丢弃法之类的正则化技术,我们将在下一章中介绍。
对于高方差网络,更多的数据通常是最佳解决方案。 如果有可能收集更多数据,那可能就是花费时间的最佳位置。
建立网络后,我想直观地检查错误,以了解网络对验证集分布进行建模的程度。 这通常会带来见解,这将有助于我改进模型。 对于回归模型,我想绘制验证集的预测值和实际值的直方图。 让我们看看我的表现如何。 该图如下,供您参考:
建立网络后,我想直观地检查误差,以了解网络对验证集分布进行建模的程度。 这通常会带来见解,这将有助于我改进模型。 对于回归模型,我想绘制验证集的预测值和实际值的直方图。 让我们看看我的表现如何。 该图如下,供您参考:
![](img/f254ee63-9487-4f22-93f4-70090f208fe6.jpg)
......@@ -290,7 +290,7 @@ Model Test MAE: 0.190189985043
* 试验隐藏层的数量。 看来五个可能太多,而一个可能还不够。
* 试验每个隐藏层相对于层数的神经元数量。
* 尝试添加丢弃或正则化。
* 尝试通过尝试使用 SGD 或 RMS 属性而不是 Adam 或通过对 Adam 使用不同的学习率来进一步减少模型错误
* 尝试通过尝试使用 SGD 或 RMS 属性而不是 Adam 或通过对 Adam 使用不同的学习率来进一步减少模型误差
深度神经网络有许多活动部分,有时要达到最佳状态是一个疲惫的概念。 您必须确定您的模型是否足够好。
......
......@@ -15,7 +15,7 @@ TensorBoard 是一个基于 Web 的应用,可以帮助您可视化 TensorFlow
正如您现在可能已经猜到的那样,深度神经网络可能变得相当复杂。 不幸的是,这意味着很多事情可能出错。 众所周知,我时不时地会犯一个错误,而当错误发生在一个深度神经网络内部时,该深度神经网络位于一个框架内,该框架在另一个框架上运行,在一个 GPU 上运行,很难找到这些错误。 他们。 TensorBoard 可能是您需要在其他本来很暗的房间中发现问题的手电筒。 TensorBoard 将允许您在训练网络时监视指标和参数的变化,这可以大大加快故障排除速度。
TensorBoard 也非常适合优化。 借助 TensorBoard,您可以直观地比较多个模型运行。 这使您可以试验不断变化的架构和超参数,然后相对于网络的其他运行评估那些变化。 所有这一切都可能在每个期发生,因此如果您愿意,您可以取消效果不佳的模型运行,从而节省了时间和金钱。 您可以在[这个页面](https://www.tensorflow.org/programmers_guide/summaries_and_tensorboard)上阅读有关 TensorBoard 的更多信息。
TensorBoard 也非常适合优化。 借助 TensorBoard,您可以直观地比较多个模型运行。 这使您可以试验不断变化的架构和超参数,然后相对于网络的其他运行评估那些变化。 所有这一切都可能在每个期发生,因此如果您愿意,您可以取消效果不佳的模型运行,从而节省了时间和金钱。 您可以在[这个页面](https://www.tensorflow.org/programmers_guide/summaries_and_tensorboard)上阅读有关 TensorBoard 的更多信息。
# 设置 TensorBoard
......@@ -69,11 +69,11 @@ tensorboard --logdir ~/ch3_tb_log --port 6006
# 引入 Keras 回调
Keras 中的回调是可以在训练过程中运行的函数。 他们可以做各种伟大的事情,例如在某个时代之后节省模型权重,记录事情,更改超参数或方便地编写 TensorBoard 日志文件。 您甚至可以创建自己的自定义回调。
Keras 中的回调是可以在训练过程中运行的函数。 他们可以做各种伟大的事情,例如在某个周期之后节省模型权重,记录事情,更改超参数或方便地编写 TensorBoard 日志文件。 您甚至可以创建自己的自定义回调。
在下一节中,我们将使用 TensorBoard 回调。 但是,我鼓励您在[这个页面](https://keras.io/callbacks)上查看 Keras 中可用的所有回调。
TensorBoard 回调是可以在模型训练之前进行配置和实例化的对象。 我们将创建这些回调的列表。 一旦创建了要用于深度神经网络的回调列表,我们就可以将该列表作为参数传递给模型的`.fit()`方法。 然后,将在每个期或 Keras 适当时使用这些回调。 在我们继续下一个示例时,这将更有意义。
TensorBoard 回调是可以在模型训练之前进行配置和实例化的对象。 我们将创建这些回调的列表。 一旦创建了要用于深度神经网络的回调列表,我们就可以将该列表作为参数传递给模型的`.fit()`方法。 然后,将在每个期或 Keras 适当时使用这些回调。 在我们继续下一个示例时,这将更有意义。
# 创建一个 TensorBoard 回调
......@@ -101,7 +101,7 @@ def create_callbacks():
您可能已经注意到,我正在将 MLP 网络的 TensorBoard 回调的日志写入`~/ch_3_tb_log/mlp`,这将在我们为 TensorBoard 指定的目录下创建一个新的目录`mlp`。 这是故意的。 我们将配置在第 2 章,“使用深度学习解决回归问题”训练的深度神经网络模型,以登录到单独的目录`~/ch_3_tb_log/dnn`。 这样做将使我们能够比较两个模型的运行。
* `histogram_freq`:这指定我们将多长时间计算一次激活和权重的直方图(以期为单位)。 它的默认值为 0,这会使日志更小,但不会生成直方图。 我们将介绍为什么以及何时您会对直方图感兴趣。
* `histogram_freq`:这指定我们将多长时间计算一次激活和权重的直方图(以期为单位)。 它的默认值为 0,这会使日志更小,但不会生成直方图。 我们将介绍为什么以及何时您会对直方图感兴趣。
* `batch_size`:这是用于计算直方图的批量大小。 默认为 32。
* `write_graph`:此函数为布尔值。 这将告诉 TensorBoard 可视化网络图。 这可能非常方便,但也会使日志变得很大。
* `write_grads`:此函数也是布尔值。 这将告诉 TensorBoard 也计算梯度的直方图。
......@@ -119,7 +119,7 @@ model.fit(x=data["train_X"], y=data["train_y"], batch_size=32,
data["val_y"]), callbacks=callbacks)
```
为了清楚起见,我将新论点加粗了。
为了清楚起见,我将新参数加粗了。
在继续使用 TensorBoard 之前,我将以与检测`mlp`相同的方式来检测深度神经网络。 唯一的代码更改是我们将 TensorBoard 日志写入的目录。 下面给出了实现该方法的方法,供您参考:
......@@ -150,7 +150,7 @@ TensorBoard 可以容纳许多运行,并且您可以通过正则表达式过
这些图上的默认 **X** 比例尺是**周期****Y** 值是我们选择的**损失函数**,即 **MAE**。 您可以单击图形以浏览它们并拖动以缩放。
看到这样的图,我们真的可以看到每个网络的相对偏差和方差。 虽然模型之间在火车损失方面有很好的分离,但深度神经网络在验证集上只得到了一点点改善,这表明我们已经进入了过拟合的领域。
看到这样的图,我们真的可以看到每个网络的相对偏差和方差。 虽然模型之间在训练损失方面有很好的分离,但深度神经网络在验证集上只得到了一点点改善,这表明我们已经进入了过拟合的领域。
# 可视化网络图
......@@ -178,18 +178,18 @@ TensorBoard 是一个出色的故障排除工具。 为了证明这一点,我
显示两个网络中隐藏层 1 的权重直方图的屏幕截图
对于标记为 dnn 的网络的偏差和权重,您将看到权重分布在整个图中。 您甚至可以说每个分布都可能是正态分布。
对于标记为 dnn 的网络的偏差和权重,您将看到权重分布在整个图中。 您甚至可以说每个分布都可能是正态分布。
您也可以在“分布”部分比较权重和偏差。 两者都以略有不同的方式呈现大多数相同的信息。
现在,看看我们破碎的网络的权重和偏。 并不是*这样分散*,实际上的权重基本上是相同的。 网络并不是真正的学习。 该层中的每个神经元看起来或多或少都是相同的。 如果您查看其他隐藏层,则会看到更多相同的层。
现在,看看我们破碎的网络的权重和偏。 并不是*这样分散*,实际上的权重基本上是相同的。 网络并不是真正的学习。 该层中的每个神经元看起来或多或少都是相同的。 如果您查看其他隐藏层,则会看到更多相同的层。
您可能想知道我是怎么做到的。 您很幸运,我会分享我的秘密。 毕竟,您永远都不知道何时需要断开自己的网络。 为了解决问题,我将网络中的每个神经元初始化为完全相同的值。 发生这种情况时,每个神经元在反向传播期间收到的错误是完全相同的,并且更改的方式也完全相同。 网络然后无法破坏对称性。 以随机方式将权重初始化到深度神经网络非常重要,如果您违反了该规则,就会发生这种情况!
您可能想知道我是怎么做到的。 您很幸运,我会分享我的秘密。 毕竟,您永远都不知道何时需要断开自己的网络。 为了解决问题,我将网络中的每个神经元初始化为完全相同的值。 发生这种情况时,每个神经元在反向传播期间收到的误差是完全相同的,并且更改的方式也完全相同。 网络然后无法破坏对称性。 以随机方式将权重初始化到深度神经网络非常重要,如果您违反了该规则,就会发生这种情况!
遇到问题时,可以像这样完全使用 TensorBoard。 请记住,我们的深度神经网络有 4033,在深度学习领域中,它仍然可以算作*很小的*。 使用 TensorBoard,我们能够直观地检查 4033 个参数并确定问题。 TensorBoard 是一个用于深度学习的暗室中的神奇手电筒。
# 总结
在本章中,我们讨论了如何安装,配置和使用 TensorBoard。 我们讨论了如何使用 TensorBoard 在 TensorBoard 的`SCALARS`部分中的每个期检查模型的损失函数,从而直观地比较模型。 然后,我们使用 TensorsBoard 的`GRAPHS`部分来可视化网络结构。 最后,我们通过查看直方图向您展示了如何使用 TensorBoard 进行故障排除。
在本章中,我们讨论了如何安装,配置和使用 TensorBoard。 我们讨论了如何使用 TensorBoard 在 TensorBoard 的`SCALARS`部分中的每个期检查模型的损失函数,从而直观地比较模型。 然后,我们使用 TensorsBoard 的`GRAPHS`部分来可视化网络结构。 最后,我们通过查看直方图向您展示了如何使用 TensorBoard 进行故障排除。
在下一章中,我们将研究如何使用 Keras 和 TensorFlow 解决二分类问题,从而扩展我们的深度学习技巧。
\ No newline at end of file
......@@ -84,9 +84,9 @@ def load_data():
# 成本函数
我们需要分类器来预测癫痫发作的可能性,即类别 1。这意味着我们的输出将被限制为`[0, 1]`,就像在传统的逻辑回归模型中一样。 在这种情况下,我们的成本函数将是二进制交叉熵,也称为**对数损失**。 如果您以前使用过分类器,那么您可能很熟悉此数学运算; 但是,作为复习,我将在这里包括。
我们需要分类器来预测癫痫发作的可能性,即类别 1。这意味着我们的输出将被限制为`[0, 1]`,就像在传统的逻辑回归模型中一样。 在这种情况下,我们的成本函数将是二交叉熵,也称为**对数损失**。 如果您以前使用过分类器,那么您可能很熟悉此数学运算; 但是,作为复习,我将在这里包括。
日志丢失的完整公式如下所示:
对数损失的完整公式如下所示:
![](img/0b843338-e484-47d0-9bec-6b9db1209675.png)
......@@ -100,15 +100,15 @@ def load_data():
`y[i] = 0`
对数函数在这里用于产生单调函数(一个一直在增加或减少的函数),我们可以轻松分它。 与所有成本函数一样,我们将调整网络参数以最小化网络成本。
对数函数在这里用于产生单调函数(一个一直在增加或减少的函数),我们可以轻松分它。 与所有成本函数一样,我们将调整网络参数以最小化网络成本。
# 使用指标评估表现
除了`loss`函数之外,Keras 还使我们可以使用度量标准来帮助判断模型的表现。 虽然最大程度地降低损失是有好处的,但在给定`loss`函数的情况下,我们如何期望模型执行效果并不是特别明显。 度量标准并不用于训练模型,它们只是用来帮助我们了解当前状态。
尽管损失对我们而言并不重要,但准确性却对我们而言意义重大。 我们人类非常了解准确性
尽管损失对我们而言并不重要,但准确率却对我们而言意义重大。 我们人类非常了解准确率
Keras 定义二进制精度如下:
Keras 定义二精度如下:
```py
def binary_accuracy(y_true, y_pred):
......@@ -118,7 +118,7 @@ def binary_accuracy(y_true, y_pred):
这实际上只是将正确答案的数量除以总答案的一种聪明方法,这是我们自从上学初期就可能一直在做的一项工作,目的是计算出考试的成绩。
您可能想知道我们的数据集是否平衡,因为准确对于不平衡的数据集而言效果很差。 实际上这是不平衡的。 只有五分之一的数据集是类 1。我们将 ROC AUC 分数作为自定义回调来计算,以解决此问题。 在 Keras 中未将 ROC 用作度量标准,因为度量标准是针对每个小型批次计算的,并且 ROC AUC 分数并非真正由小型批次定义。
您可能想知道我们的数据集是否平衡,因为准确对于不平衡的数据集而言效果很差。 实际上这是不平衡的。 只有五分之一的数据集是类 1。我们将 ROC AUC 分数作为自定义回调来计算,以解决此问题。 在 Keras 中未将 ROC 用作度量标准,因为度量标准是针对每个小型批次计算的,并且 ROC AUC 分数并非真正由小型批次定义。
# 在 Keras 中建立二分类器
......@@ -147,7 +147,7 @@ def build_network(input_features=None):
如果我们增加许多层,我们的梯度将变得越来越小,直到前几层几乎没有训练为止,这就是**梯度消失问题**。 我们离那还很遥远,但是我们稍后会讨论。
用说唱传奇克里斯托弗·华莱士(又名臭名昭著的 B.I.G.)的话来说,我们遇到的神经元越多,看到的问题就越多。 话虽如此,方差可以通过辍学,正则化和提早停止进行管理,GPU 计算的进步使更深层次的网络成为可能。
用说唱传奇克里斯托弗·华莱士(又名臭名昭著的 B.I.G.)的话来说,我们遇到的神经元越多,看到的问题就越多。 话虽如此,方差可以通过丢弃法,正则化和提早停止进行管理,GPU 计算的进步使更深层次的网络成为可能。
如果我必须在神经元太多或太少的网络之间进行选择,而我只能尝试一个实验,那么我宁愿选择稍微过多的神经元。
......@@ -164,13 +164,13 @@ def build_network(input_features=None):
因此,既然我们了解选择太多参数而不是选择太多参数的价格和行为,那么从哪里开始呢? 据我所知,剩下的只是实验。
测量这些实验可能很棘手。 如果像我们的早期网络一样,您的网络训练很快,那么可以在多种架构中实诸如交叉验证之类的东西,以评估每种架构的多次运行。 如果您的网络需要很长时间进行训练,则可能会留下一些统计上不太复杂的信息。 我们将在第 6 章“超参数优化”中介绍网络优化。
测量这些实验可能很棘手。 如果像我们的早期网络一样,您的网络训练很快,那么可以在多种架构中实诸如交叉验证之类的东西,以评估每种架构的多次运行。 如果您的网络需要很长时间进行训练,则可能会留下一些统计上不太复杂的信息。 我们将在第 6 章“超参数优化”中介绍网络优化。
一些书籍提供了选择神经网络架构的经验法则。 我对此表示怀疑和怀疑,您当然不会在这里找到一个。
# 为我们的示例编码隐藏层
对于我们的示例问题,我将使用五个隐藏层,因为我认为要素之间存在许多交互。 我的直觉主要基于领域知识。 阅读数据描述后,我知道这是时间序列的横截面切片,并且可能是自动相关的。
对于我们的示例问题,我将使用五个隐藏层,因为我认为特征之间存在许多交互。 我的直觉主要基于领域知识。 阅读数据描述后,我知道这是时间序列的横截面切片,并且可能是自动相关的。
我将从第一层的 128 个神经元开始(略小于我的输入大小),然后在接近输出时减半到 16 个神经元。 这完全不是凭经验,它仅基于我自己的经验。 我们将使用以下代码定义隐藏层:
......@@ -223,11 +223,11 @@ model = build_network(input_features=input_features)
model.fit(x=data["train_X"], y=data["train_y"], batch_size=32, epochs=20, verbose=1, validation_data=(data["val_X"], data["val_y"]), callbacks=callbacks)
```
如果您已经阅读第 2 章“使用深度学习解决回归问题”,则应该看起来很熟悉。 在大多数情况下,实际上是相同的。 回调列表包含 TensorBoard 回调,因此让我们观看我们的网络训练 20 个纪元,看看会发生什么:
如果您已经阅读第 2 章“使用深度学习解决回归问题”,则应该看起来很熟悉。 在大多数情况下,实际上是相同的。 回调列表包含 TensorBoard 回调,因此让我们观看我们的网络训练 20 个周期,看看会发生什么:
![](img/33cc8da8-37ba-4743-8e74-3bffbc02c050.png)
尽管我们的火车损失继续下降,但我们可以看到`val_loss`到处都在跳跃。 大约在第八个时代之后,我们就过拟合了。
尽管我们的训练损失继续下降,但我们可以看到`val_loss`到处都在跳跃。 大约在第八个周期之后,我们就过拟合了。
有几种方法可以减少网络差异并管理这种过拟合,下一章将介绍大多数方法。 但是,在开始之前,我想向您展示一些有用的东西,称为**检查点回调**
......@@ -243,7 +243,7 @@ checkpoint_callback = ModelCheckpoint(filepath="./model-weights.{epoch:02d}-{val
`ModelCheckpoint`将为我们执行的工作是按计划的时间间隔保存模型。 在这里,我们告诉`ModelCheckpoint`每当我们达到新的最佳验证精度(`val_acc`)时都要保存模型的副本。 我们也可以监视验证损失或我们指定的任何其他指标。
文件名字符串将包含纪元编号和运行的验证准确性
文件名字符串将包含周期编号和运行的验证准确率
当我们再次训练模型时,我们可以看到正在创建以下文件:
......@@ -257,13 +257,13 @@ model-weights.05-0.985217.hdf5
这里有一些大的假设,将第 5 期称为最佳。 您可能需要多次运行网络,尤其是在您的数据集相对较小的情况下,就像本书中的早期示例一样。 我们可以肯定地说,这个结果将是不稳定的。
顺便说一下,这是防止过拟合的非常简单的方法。 我们可以选择使用方差太大之前发生的模型检查点。 这是做类似提前停止的一种方法,这意味着当我们看到模型没有改善时,我们会在指定的期数之前停止训练。
顺便说一下,这是防止过拟合的非常简单的方法。 我们可以选择使用方差太大之前发生的模型检查点。 这是做类似提前停止的一种方法,这意味着当我们看到模型没有改善时,我们会在指定的期数之前停止训练。
# 在自定义回调中测量 ROC AUC
让我们再使用一个回调。 这次,我们将构建一个自定义的回调,以在每个纪元结束时在训练集和测试集上计算曲线下的接收器工作特征区域(ROC AUC)。
让我们再使用一个回调。 这次,我们将构建一个自定义的回调,以在每个周期结束时在训练集和测试集上计算曲线下的接收器工作特征区域(ROC AUC)。
在 Keras 中创建自定义回调实际上非常简单。 我们需要做的就是创建一个固有的`Callback`类,并覆盖所需的方法。 由于我们想在每个期结束时计算 ROC AUC 分数,因此我们将在`_epoch_end`上覆盖:
在 Keras 中创建自定义回调实际上非常简单。 我们需要做的就是创建一个固有的`Callback`类,并覆盖所需的方法。 由于我们想在每个期结束时计算 ROC AUC 分数,因此我们将在`_epoch_end`上覆盖:
```py
from keras.callbacks import Callback
......@@ -302,7 +302,7 @@ def create_callbacks(data):
return [tensorboard_callback, roc_auc_callback, checkpoint_callback]
```
这里的所有都是它的! 您可以用相同的方式实其他任何指标。
这里的所有都是它的! 您可以用相同的方式实其他任何指标。
# 测量精度,召回率和 f1 得分
......
......@@ -48,7 +48,7 @@
我们的输出层将为每个类包含一个神经元。 每个类别的关联神经元将经过训练,以将该类别的概率预测为介于 0 和 1 之间的值。我们将使用一种称为 **softmax** 的特殊激活,以确保所有这些输出总和为 1,我们将介绍 softmax 的详细信息。
这意味着我们将需要为我们的类创建一个二进制/分类编码。 例如,如果我们使`y = [0, 3, 2, 1]`并对其进行分类编码,则将具有如下矩阵`y`
这意味着我们将需要为我们的类创建一个二/分类编码。 例如,如果我们使`y = [0, 3, 2, 1]`并对其进行分类编码,则将具有如下矩阵`y`
![](img/557558d7-5f13-4821-96c1-7d2d8fe578b6.png)
......@@ -56,7 +56,7 @@
# 成本函数
我们将使用的成本函数称为**多项式交叉熵**。 多项式交叉熵实际上只是在第 4 章“使用 Keras 进行二分类”中看到的二进制交叉熵函数的概括。
我们将使用的成本函数称为**多项式交叉熵**。 多项式交叉熵实际上只是在第 4 章“使用 Keras 进行二分类”中看到的二交叉熵函数的概括。
让我们一起看看它们,而不只是显示分类交叉熵。 我要断言它们是平等的,然后解释原因:
......@@ -64,15 +64,15 @@
前面的等式是正确的(`m = 2`时)
好吧,别害怕。 我知道,这是一堆数学。 绝对交叉熵方程是一直存在于右边的方程。 二进制交叉熵紧随其后。 现在,设想`m = 2`的情况。 在这种情况下,您可能会发现,`j = 0``j = 1``y[ij]log(p[ij])`的和,对于`i`中的每个值,等于来自二元交叉熵的结果。 希望这种减少足以使分类交叉熵有意义。 如果没有,我建议选择一些值并进行编码。 只需一秒钟,稍后您将感谢我!
好吧,别害怕。 我知道,这是一堆数学。 绝对交叉熵方程是一直存在于右边的方程。 二交叉熵紧随其后。 现在,设想`m = 2`的情况。 在这种情况下,您可能会发现,`j = 0``j = 1``y[ij]log(p[ij])`的和,对于`i`中的每个值,等于来自二元交叉熵的结果。 希望这种减少足以使分类交叉熵有意义。 如果没有,我建议选择一些值并进行编码。 只需一秒钟,稍后您将感谢我!
# 指标
分类交叉熵是一个很好的成本函数,但实际上并不能告诉我们很多我们可以从网络中获得的预测质量。 不幸的是,像 ROC AUC 这样的二分类指标也对我们没有太大帮助,因为我们超越了二分类 AUC 的定义并没有。
鉴于缺少更好的指标,我将使用准确性作为人类可以理解的训练指标。 幸运的是,在这种情况下,我的数据集是平衡的。 正如您所期望的那样,准确性是指真实值与预测值的匹配次数除以数据集的总大小。
鉴于缺少更好的指标,我将使用准确率作为人类可以理解的训练指标。 幸运的是,在这种情况下,我的数据集是平衡的。 正如您所期望的那样,准确率是指真实值与预测值的匹配次数除以数据集的总大小。
训练结束后,我将使用 scikit-learn 的分类报告向我们显示每个班级的精确度和召回率。 如果您愿意,也可以为此使用混淆矩阵。
训练结束后,我将使用 scikit-learn 的分类报告向我们显示每个的精确度和召回率。 如果您愿意,也可以为此使用混淆矩阵。
# 在 Keras 中建立多分类器
......@@ -221,7 +221,7 @@ model.fit(x=data["train_X"], y=data["train_y"],
我正在使用与以前相同的回调。 我没有使用我们在第 4 章“使用 Keras 进行二分类”中构建的 ROC AUC 回调,因为 ROC AUC 没有为多分类器明确定义。
存在一些针对该问题的创造性解决方案。 例如,[**通过成对分析近似多类 ROC**](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.108.3250&rep=rep1&type=pdf)[**ROC 表面下体积**](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.2427&rep=rep1&type=pdf)都是出色的论文,都可以解决这个问题。 但是,实际上,这些方法及其度量标准很少在 R 中使用,最常在 R 中实现。因此,到目前为止,让我们坚持使用多类准确,并且远离 R。
存在一些针对该问题的创造性解决方案。 例如,[**通过成对分析近似多类 ROC**](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.108.3250&rep=rep1&type=pdf)[**ROC 表面下体积**](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.2427&rep=rep1&type=pdf)都是出色的论文,都可以解决这个问题。 但是,实际上,这些方法及其度量标准很少在 R 中使用,最常在 R 中实现。因此,到目前为止,让我们坚持使用多类准确,并且远离 R。
让我们观看 TensorBoard 在我们的模型训练中:
......@@ -229,11 +229,11 @@ model.fit(x=data["train_X"], y=data["train_y"],
在阅读下一段之前,请花点时间思考一下这些图形在告诉我们什么。 得到它了? 好的,让我们继续。
因此,这是一个熟悉的情况。 我们的训练损失正在继续下降,而我们的验证损失正在上升。 我们过拟合。 虽然当然可以选择提前停止,但让我向您展示一些处理过拟合的新技巧。 让我们在下一部分中查看辍学和 l2 正则化。 但是,在进行此操作之前,我们应该研究如何使用多类网络来测量准确性和进行预测。
因此,这是一个熟悉的情况。 我们的训练损失正在继续下降,而我们的验证损失正在上升。 我们过拟合。 虽然当然可以选择提前停止,但让我向您展示一些处理过拟合的新技巧。 让我们在下一部分中查看丢弃法和 l2 正则化。 但是,在进行此操作之前,我们应该研究如何使用多类网络来测量准确率和进行预测。
# 在多类模型中使用 scikit-learn 指标
和以前一样,我们可以借鉴 scikit-learn 的指标来衡量我们的模型。 但是,为此,我们需要从模型的`y`的分类输出中进行一些简单的转换,因为 scikit-learn 需要使用类标签,而不是二进制类指示器。
和以前一样,我们可以借鉴 scikit-learn 的指标来衡量我们的模型。 但是,为此,我们需要从模型的`y`的分类输出中进行一些简单的转换,因为 scikit-learn 需要使用类标签,而不是二类指示器。
为了取得飞跃,我们将使用以下代码开始进行预测:
......@@ -262,13 +262,13 @@ print(classification_report(test_y, y_hat))
减少深度神经网络过拟合的一种非常好的方法是采用一种称为**丢弃法**的技术。 丢弃法完全按照其说的去做,它使神经元脱离隐藏层。 运作方式如下。
通过每个小批量,我们将随机选择关闭每个隐藏层中的节点。 想象一下,我们在某个隐藏层中实了丢弃,并且我们选择了丢弃率为 0.5。 这意味着,对于每个小批量,对于每个神经元,我们都掷硬币以查看是否使用该神经元。 这样,您可能会随机关闭该隐藏层中大约一半的神经元:
通过每个小批量,我们将随机选择关闭每个隐藏层中的节点。 想象一下,我们在某个隐藏层中实了丢弃,并且我们选择了丢弃率为 0.5。 这意味着,对于每个小批量,对于每个神经元,我们都掷硬币以查看是否使用该神经元。 这样,您可能会随机关闭该隐藏层中大约一半的神经元:
![](img/6bff492d-dbf6-4c35-aaef-2e8283c4afed.png)
如果我们一遍又一遍地执行此操作,就好像我们正在训练许多较小的网络。 模型权重保持相对较小,每个较小的网络不太可能过拟合数据。 这也迫使每个神经元减少对其他神经元的依赖。
辍学效果惊人,可以很好地解决您可能遇到的许多(如果不是大多数)深度学习问题的过拟合问题。 如果您具有高方差模型,则辍学是减少过拟合的好选择。
丢弃法效果惊人,可以很好地解决您可能遇到的许多(如果不是大多数)深度学习问题的过拟合问题。 如果您具有高方差模型,则丢弃是减少过拟合的好选择。
Keras 包含一个内置的`Dropout`层,我们可以轻松地在网络中使用它来实现`Dropout``Dropout`层将简单地随机关闭前一层神经元的输出,以使我们轻松地改造网络以使用`Dropout`。 要使用它,除了我们正在使用的其他层类型之外,我们还需要首先导入新层,如以下代码所示:
......@@ -276,7 +276,7 @@ Keras 包含一个内置的`Dropout`层,我们可以轻松地在网络中使
from keras.layers import Input, Dense, Dropout
```
然后,我们只需将`Dropout`层插入模型,如以下代码所示:
然后,我们只需将`Dropout`层插入模型,如以下代码所示:
```py
def build_network(input_features=None):
......@@ -295,17 +295,17 @@ def build_network(input_features=None):
return model
```
这是我们先前使用的确切模型; 但是,我们在每个`Dense`层之后都插入了`Dropout`层,这是我通常在实现辍学时开始的方式。 像其他模型架构决策一样,您可以选择仅在某些层,所有层或没有层中实施辍学。 您还可以选择更改退出/保留概率; 但是,我确实建议从 0.5 开始,因为它通常效果很好。
这是我们先前使用的确切模型; 但是,我们在每个`Dense`层之后都插入了`Dropout`层,这是我通常在实现丢弃时开始的方式。 像其他模型架构决策一样,您可以选择仅在某些层,所有层或没有层中实现丢弃。 您还可以选择更改退出/保留概率; 但是,我确实建议从 0.5 开始,因为它通常效果很好。
一个安全的选择是在每一层都退出,保持概率为 0.5。 不错的第二种尝试是仅在第一层使用辍学
一个安全的选择是在每一层都退出,保持概率为 0.5。 不错的第二种尝试是仅在第一层使用丢弃
让我们用辍学训练我们的新模型,看看它与我们的第一次尝试相比如何:
让我们用丢弃法训练我们的新模型,看看它与我们的第一次尝试相比如何:
![](img/d2c65c07-5c92-4ad2-8428-71c70a27552f.png)
首先让我们看一下验证准确性。 使用辍学模型的训练速度与未规范模型的训练速度一样快,但是在这种情况下,它的确似乎很快就开始加速。 看看在第 44 个时期的验证准确性。它比非正规模型略好。
首先让我们看一下验证准确率。 使用丢弃模型的训练速度与未规范模型的训练速度一样快,但是在这种情况下,它的确似乎很快就开始加速。 看看在第 44 个周期的验证准确率。它比非正规模型略好。
现在,让我们看看验证损失。 您可以看到辍学对模型过拟合的影响,而且确实非常明显。 虽然仅转换为最终产品的少量改进,但辍学表现相当不错,可以防止我们的验证丢失
现在,让我们看看验证损失。 您可以看到丢弃法对模型过拟合的影响,而且确实非常明显。 虽然仅转换为最终产品的少量改进,但丢弃法表现相当不错,可以防止我们的验证损失提升
# 通过正则化控制方差
......@@ -340,7 +340,7 @@ def build_network(input_features=None):
![](img/ca428764-c8dd-4e96-8726-49b4bd1b6b9b.png)
不幸的是,我们的新 L2 正则化网络很容易找到。 在这种情况下,似乎 L2 正则化效果很好。 我们的网络现在偏严重,对其他两个方面的了解还不够。
不幸的是,我们的新 L2 正则化网络很容易找到。 在这种情况下,似乎 L2 正则化效果很好。 我们的网络现在偏严重,对其他两个方面的了解还不够。
如果我真的确定要使用正则化来解决此问题,那么我将首先更改正则化率并尝试找到更合适的值,但我们相距甚远,我对此表示怀疑,我们会做得比我们更好 `dropout`模型。
......
......@@ -12,7 +12,7 @@
# 是否应该将网络架构视为超参数?
在构建最简单的网络时,我们必须对网络架构做出各种选择。 我们应该使用 1 个隐藏层还是 1,000 个? 每层应包含多少个神经元? 他们都应该使用`relu`激活函数还是`tanh`? 我们应该在每个隐藏层上还是仅在第一层上使用辍学? 在设计网络架构时,我们必须做出许多选择。
在构建最简单的网络时,我们必须对网络架构做出各种选择。 我们应该使用 1 个隐藏层还是 1,000 个? 每层应包含多少个神经元? 他们都应该使用`relu`激活函数还是`tanh`? 我们应该在每个隐藏层上还是仅在第一层上使用丢弃? 在设计网络架构时,我们必须做出许多选择。
在最典型的情况下,我们穷举搜索每个超参数的最佳值。 但是,要穷举搜索网络架构并不容易。 实际上,我们可能没有时间或计算能力。 我们很少看到研究人员通过穷举搜索来寻找最佳架构,因为选择的数量非常多,而且存在不只一个正确的答案。 取而代之的是,我们看到该领域的研究人员通过实验尝试建立已知的架构,以尝试创建新的新颖架构并改善现有架构。
......@@ -30,8 +30,8 @@
希望通过寻找类似问题的架构,您至少接近适合您的架构。 您如何做才能进一步优化网络架构?
* 在多个实验运行中,添加层和/或神经元,直到您的网络开始针对问题过拟合。 在深度学习中,添加单,直到您不再具有高偏差模型为止。
* 一旦开始过拟合,您就会发现一些网络架构能够很好地拟合训练数据,甚至可能拟合得很好。 在这一点上,您应该集中精力通过使用辍学,正则化,提早停止等方法来减少方差。
* 在多个实验运行中,添加层和/或神经元,直到您的网络开始针对问题过拟合。 在深度学习中,添加单,直到您不再具有高偏差模型为止。
* 一旦开始过拟合,您就会发现一些网络架构能够很好地拟合训练数据,甚至可能拟合得很好。 在这一点上,您应该集中精力通过使用丢弃,正则化,提早停止等方法来减少方差。
这种方法通常归因于著名的神经网络研究员 Geoffrey Hinton。 这是一个有趣的想法,因为它使过拟合不是要避免的事情,而是构建网络架构的良好第一步。
......@@ -49,10 +49,10 @@
* 每个优化器都有一组我们可能需要调整的超参数,例如学习率,动量和衰减。
* 网络权重初始化。
* 神经元激活。
* 正则化参数(例如辍学概率)或 12 正则化中使用的正则化参数。
* 正则化参数(例如丢弃概率)或 12 正则化中使用的正则化参数。
* 批次大小。
如上所述,这不是详尽的清单。 当然,您可以尝试更多的选择,包括在每个隐藏层中引入可变数量的神经元,每层中丢概率的变化等等。 就像我们一直暗示的那样,超参数的可能组合是无限的。 这些选择也很可能并非独立于网络架构,添加和删除层可能会为这些超参数中的任何一个带来新的最佳选择。
如上所述,这不是详尽的清单。 当然,您可以尝试更多的选择,包括在每个隐藏层中引入可变数量的神经元,每层中丢概率的变化等等。 就像我们一直暗示的那样,超参数的可能组合是无限的。 这些选择也很可能并非独立于网络架构,添加和删除层可能会为这些超参数中的任何一个带来新的最佳选择。
# 超参数优化策略
......@@ -135,7 +135,7 @@ hyperparameters = create_hyperparameters()
search = RandomizedSearchCV(estimator=model, param_distributions=hyperparameters, n_iter=10, n_jobs=1, cv=3, verbose=1)
```
拟合此`RandomizedSearchCV`对象后,它将从参数分布中随机选择值并将其应用于模型。 它将执行 10 次(`n_iter=10`),并且将尝试每种组合 3 次,因为我们使用了 3 倍交叉验证。 这意味着我们将总共拟合模型 30 次。 使用每次运行的平均准确,它将返回最佳模型作为类属性`.best_estimator`,并且将返回最佳参数作为`.best_params_`
拟合此`RandomizedSearchCV`对象后,它将从参数分布中随机选择值并将其应用于模型。 它将执行 10 次(`n_iter=10`),并且将尝试每种组合 3 次,因为我们使用了 3 倍交叉验证。 这意味着我们将总共拟合模型 30 次。 使用每次运行的平均准确,它将返回最佳模型作为类属性`.best_estimator`,并且将返回最佳参数作为`.best_params_`
为了适合它,我们只需调用它的`fit`方法,就好像它是一个模型一样,如以下代码所示:
......@@ -167,7 +167,7 @@ Hyperband 是一项超参数优化技术,由 Lisha Li,Kevin Jamieson,Guili
想象一下,就像我们在`RandomSearchCV`中所做的那样,随机采样许多潜在的超参数集。 完成`RandomSearchCV`后,它将选择一个单一的超参数配置作为其采样的*最优值*。 Hyperband 利用这样的思想,即即使经过少量迭代,最佳的超参数配置也可能会胜过其他配置。 Hyperband 中的乐队来自土匪,指的是基于多臂土匪技术(用于优化竞争选择之间的资源分配以优化表现为目标的技术)的勘探与开发。
使用 Hyperband,我们可以尝试一些可能的配置集(`n`),仅训练一次迭代。 作者将迭代一词留作多种可能的用途。 但是,我将时代作为迭代。 一旦完成第一个训练循环,就将根据表现对结果进行配置。 然后,对该列表的上半部分进行大量迭代的训练。 然后重复进行减半和剔除的过程,我们得到了一些非常小的配置集,我们将针对在搜索中定义的完整迭代次数进行训练。 与在每种可能的配置中搜索最大周期相比,此过程使我们在更短的时间内获得了*最佳*超参数集。
使用 Hyperband,我们可以尝试一些可能的配置集(`n`),仅训练一次迭代。 作者将迭代一词留作多种可能的用途。 但是,我将周期作为迭代。 一旦完成第一个训练循环,就将根据表现对结果进行配置。 然后,对该列表的上半部分进行大量迭代的训练。 然后重复进行减半和剔除的过程,我们得到了一些非常小的配置集,我们将针对在搜索中定义的完整迭代次数进行训练。 与在每种可能的配置中搜索最大周期相比,此过程使我们在更短的时间内获得了*最佳*超参数集。
在本章的 GitHub 存储库中,我在`hyperband.py`中包括了`hyperband`算法的实现。 此实现主要源自 FastML 的实现,您可以在[这个页面](http://fastml.com/tuning-hyperparams-fast-with-hyperband/)中找到。 要使用它,您需要首先实例化一个`hyperband`对象,如以下代码所示:
......@@ -176,7 +176,7 @@ from hyperband import Hyperband
hb = Hyperband(data, get_params, try_params)
```
Hyperband 构造函数需要三个参数:
Hyperband 构造需要三个参数:
* `data`:到目前为止,我在示例中一直在使用的数据字典
* `get_params`:用于从我们正在搜索的超参数空间中采样的函数的名称
......
......@@ -10,7 +10,7 @@
# 卷积介绍
经过训练的卷积层由称为过滤器的许多特征检测器组成,这些特征检测器在输入图像上滑动作为移动窗口。 稍后我们将讨论过滤器内部的内容,但现在它可能是一个黑匣子。 想象一个已经训练过的过滤器。 也许该过滤器已经过训练,可以检测图像中的边缘,您可能会认为这是黑暗与明亮之间的过渡。 当它经过图像时,其输出表示它检测到的特征的存在和位置,这对于第二层过滤器很有用。 稍微扩展一下我们的思想实验,现在想象第二个卷积层中的一个过滤器,它也已经被训练过了。 也许这个新层已经学会了检测直角,其中存在由上一层找到的两个边缘。 不断地我们去; 随着我们添加层,可以了解更多复杂的特征。 特征层次结构的概念对于卷积神经网络至关重要。 下图来自 Honglak Lee 等人的《使用卷积深度信念网络的无监督学习层次表示》[2011],非常好地说明了特征层次结构的概念:
经过训练的卷积层由称为过滤器的许多特征检测器组成,这些特征检测器在输入图像上滑动作为移动窗口。 稍后我们将讨论过滤器内部的内容,但现在它可能是一个黑匣子。 想象一个已经训练过的过滤器。 也许该过滤器已经过训练,可以检测图像中的边缘,您可能会认为这是黑暗与明亮之间的过渡。 当它经过图像时,其输出表示它检测到的特征的存在和位置,这对于第二层过滤器很有用。 稍微扩展一下我们的思想实验,现在想象第二个卷积层中的一个过滤器,它也已经被训练过了。 也许这个新层已经学会了检测直角,其中存在由上一层找到的两个边缘。 不断地我们去; 随着我们添加层,可以了解更多复杂的特征。 特征层次结构的概念对于卷积神经网络至关重要。 下图来自 Honglak Lee 等人的《使用卷积深度信念网络的无监督学习层次表示》[2011],非常好地说明了特征层次结构的概念:
![](img/c63ca1b1-5e19-423c-8174-d62c87d452bc.png)
......@@ -53,7 +53,7 @@ Conv2D(64, kernel_size=(3,3), activation="relu", name="conv_1")
因此,现在您希望对卷积层的工作原理有所了解,让我们讨论为什么我们要进行所有这些疯狂的数学运算。 为什么我们要使用卷积层而不是以前使用的普通层?
假设我们确实使用了普通层,以得到与之前讨论的相同的输出形状。 我们从`32 x 32 x 3`图像开始,所以总共有 3,072 个值。 我们剩下一个`30 x 30 x 64`矩阵。 总共有 57,600 个值。 如果我们要使用完全连接的层来连接这两个矩阵,则该层将具有 176,947,200 个可训练参数。 那是 1.76 亿。
假设我们确实使用了普通层,以得到与之前讨论的相同的输出形状。 我们从`32 x 32 x 3`图像开始,所以总共有 3,072 个值。 我们剩下一个`30 x 30 x 64`矩阵。 总共有 57,600 个值。 如果我们要使用完全连接的层来连接这两个矩阵,则该层将具有 176,947,200 个可训练参数。 那是 1.76 亿。
但是,当我们使用上面的卷积层时,我们使用了 64 个`3 x 3 x 3`过滤器,这将导致 1,728 个可学习权重加 64 个偏差(总共 1,792 个参数)。
......@@ -126,7 +126,7 @@ output = Dense(10, activation="softmax", name="softmax")(d2)
# 成本函数和指标
在第 5 章中,我们使用分类交叉熵作为多分类器的损失函数。 这只是另一个多分类器,我们可以继续使用分类交叉熵作为我们的损失函数,并使用准确作为度量。 我们已经开始使用图像作为输入,但是幸运的是我们的成本函数和指标保持不变。
在第 5 章中,我们使用分类交叉熵作为多分类器的损失函数。 这只是另一个多分类器,我们可以继续使用分类交叉熵作为我们的损失函数,并使用准确作为度量。 我们已经开始使用图像作为输入,但是幸运的是我们的成本函数和指标保持不变。
# 卷积层
......@@ -158,7 +158,7 @@ pool2 = MaxPooling2D(pool_size=(2, 2), name="pool_2")(batch2)
我们希望在这些卷积层中提取此`6 x 6 x 32`张量表示的相关图像特征。 为了使用这些特征对图像进行分类,在进入最终输出层之前,我们将将该张量连接到几个完全连接的层。
在此示例中,我将使用 512 神经元完全连接层,256 神经元完全连接层以及最后的 10 神经元输出层。 我还将使用辍学来帮助防止过拟合,但只有一点点! 该过程的代码如下,供您参考:
在此示例中,我将使用 512 神经元完全连接层,256 神经元完全连接层以及最后的 10 神经元输出层。 我还将使用丢弃法来帮助防止过拟合,但只有一点点! 该过程的代码如下,供您参考:
```py
from keras.layers import Flatten, Dense, Dropout
......@@ -257,7 +257,7 @@ model.fit(x=data["train_X"], y=data["train_y"],
# 使用数据增强
数据增强是一种将变换应用于图像并使用原始图像和变换后的图像进行训练的技术。 想象一下,我们有一个训练,里面有一只猫:
数据增强是一种将变换应用于图像并使用原始图像和变换后的图像进行训练的技术。 想象一下,我们有一个训练,里面有一只猫:
![](img/f2fc9117-dda1-40a5-b677-7b5bdaddb3fb.jpg)
......@@ -286,7 +286,7 @@ def create_datagen(train_X):
return data_generator
```
在此示例中,我同时使用了移位,旋转和水平翻转。 我只使用很小的班次。 通过实验,我发现更大的变化太多了,而且我的网络实际上无法学到任何东西。 您的经验会随着您的问题而变化,但是我希望较大的图像更能容忍移动。 在这种情况下,我们使用 32 个像素的图像,这些图像非常小。
在此示例中,我同时使用了移位,旋转和水平翻转。 我只使用很小的移位。 通过实验,我发现更大的变化太多了,而且我的网络实际上无法学到任何东西。 您的经验会随着您的问题而变化,但是我希望较大的图像更能容忍移动。 在这种情况下,我们使用 32 个像素的图像,这些图像非常小。
# 用生成器训练
......@@ -303,7 +303,7 @@ model.fit_generator(data_generator.flow(data["train_X"], data["train_y"], batch_
callbacks=callbacks)
```
在这里,我们用生成器替换了传统的`x``y`参数。 最重要的是,请注意`steps_per_epoch`参数。 您可以从训练集中任意采样替换次数,并且每次都可以应用随机变换。 这意味着我们每个期可以使用的迷你批数比数据还多。 在这里,我将仅根据观察得到的样本数量进行采样,但这不是必需的。 如果可以,我们可以并且应该将这个数字提高。
在这里,我们用生成器替换了传统的`x``y`参数。 最重要的是,请注意`steps_per_epoch`参数。 您可以从训练集中任意采样替换次数,并且每次都可以应用随机变换。 这意味着我们每个期可以使用的迷你批数比数据还多。 在这里,我将仅根据观察得到的样本数量进行采样,但这不是必需的。 如果可以,我们可以并且应该将这个数字提高。
在总结之前,让我们看一下这种情况下图像增强的好处:
......
......@@ -11,9 +11,9 @@
# 迁移学习概述
在第 7 章和“卷积神经网络”中,我们训练了约 50,000 个观测值的卷积神经网络,并且由于网络和问题的复杂性,在开始训练的短短几个期后,我们过拟合了。 如果您还记得的话,我曾评论说我们的训练集中有 50,000 个观察结果对于计算机视觉问题不是很大。 确实如此。 计算机视觉问题喜欢数据,而我们可以提供给他们的数据越多,它们的表现就越好。
在第 7 章和“卷积神经网络”中,我们训练了约 50,000 个观测值的卷积神经网络,并且由于网络和问题的复杂性,在开始训练的短短几个期后,我们过拟合了。 如果您还记得的话,我曾评论说我们的训练集中有 50,000 个观察结果对于计算机视觉问题不是很大。 确实如此。 计算机视觉问题喜欢数据,而我们可以提供给他们的数据越多,它们的表现就越好。
我们可能认为计算机视觉技术最先进的深度神经网络通常在称为 **ImageNet** 的数据集上进行训练。 [`ImageNet`数据集](http://www.image-net.org/)是包含 120 万张图像的 1,000 个分类器。 这还差不多! 如此庞大的数据集使研究人员能够构建真正复杂的深度神经网络,以检测复杂的特征。 当然,在 120 万张图像上训练有时具有 100 多个层的模型的价格很高。 训练可能需要数周和数月,而不是数小时。
我们可能认为计算机视觉技术最先进的深度神经网络通常在称为 **ImageNet** 的数据集上进行训练。 [`ImageNet`数据集](http://www.image-net.org/)是包含 120 万张图像的 1,000 个分类器。 这还差不多! 如此庞大的数据集使研究人员能够构建真正复杂的深度神经网络,以检测复杂的特征。 当然,在 120 万张图像上训练有时具有 100 多个层的模型的价格很高。 训练可能需要数周和数月,而不是数小时。
但是,如果我们可以从一个最先进的,多层的,经过数百万张图像训练的网络开始,然后仅使用少量数据将该网络应用于我们自己的计算机视觉问题,该怎么办? 那就是**迁移学习**
......@@ -53,13 +53,13 @@
# 更多数据总是有益的
Google 研究人员在《重新研究深度学习时代数据的不合理有效性》中进行的几次实验中,构建了一个内部数据集,其中包含 3 亿个观测值,显然比`ImageNet`大得多。 然后,他们在该数据集上训练了几种最先进的架构,从而使模型显示的数据量从 1000 万增加到 3000 万,1 亿,最后是 3 亿。 通过这样做,他们表明模型表现随用于训练的观察次数的对数线性增加,这表明在源域中,更多的数据总是有帮助。
Google 研究人员在《重新研究深度学习周期数据的不合理有效性》中进行的几次实验中,构建了一个内部数据集,其中包含 3 亿个观测值,显然比`ImageNet`大得多。 然后,他们在该数据集上训练了几种最先进的架构,从而使模型显示的数据量从 1000 万增加到 3000 万,1 亿,最后是 3 亿。 通过这样做,他们表明模型表现随用于训练的观察次数的对数线性增加,这表明在源域中,更多的数据总是有帮助。
但是目标域呢? 我们使用了一些类似于我们在迁移学习过程中可能使用的类型的数据集重复了 Google 实验,包括我们将在本章稍后使用的`Dogs versus Cats`数据集。 我们发现,在目标域中,模型的表现随用于训练的观察次数的对数线性增加,就像在源域中一样。 更多数据总是有帮助的。
# 源/目标域相似度
迁移学习的独特之处在于您担心源域和目标域之间的相似度。 经过训练以识别人脸的分类器可能不会轻易移到识别各种架构的目标领域。 我们进行了源和目标尽可能不同的实验,以及源和目标域非常相似的实验。 毫不奇怪,当迁移学习应用中的源域和目标域非常不同时,与相似时相比,它们需要更多的数据。 它们也需要更多的微调,因为当域在视觉上非常不同时,特征提取层需要大量的学习。
迁移学习的独特之处在于您担心源域和目标域之间的相似度。 经过训练以识别人脸的分类器可能不会轻易移到识别各种架构的目标领域。 我们进行了源和目标尽可能不同的实验,以及源和目标域非常相似的实验。 毫不奇怪,当迁移学习应用中的源域和目标域非常不同时,与相似时相比,它们需要更多的数据。 它们也需要更多的微调,因为当域在视觉上非常不同时,特征提取层需要大量的学习。
# Keras 的迁移学习
......@@ -67,7 +67,7 @@ Google 研究人员在《重新研究深度学习时代数据的不合理有效
# 目标域概述
在本章的示例中,我将使用 Kaggle 的`Dogs versus Cats`数据集。 该数据集包含 25,000 张猫和狗的图像。 每个班级之间达到完美平衡,每个班级 12,500。 可以从[这里](https://www.kaggle.com/c/dogs-vs-cats/data)下载数据集。
在本章的示例中,我将使用 Kaggle 的`Dogs versus Cats`数据集。 该数据集包含 25,000 张猫和狗的图像。 每个类别之间达到完美平衡,每个类别 12,500。 可以从[这里](https://www.kaggle.com/c/dogs-vs-cats/data)下载数据集。
这是一个二分类问题。 每张照片都包含狗或猫,但不能同时包含两者。
......@@ -115,7 +115,7 @@ model = Model(inputs=base_model.input, outputs=predictions)
注意,我们在这里使用`GlobalAveragePooling2D`层。 该层将前一层的 4D 输出平坦化为 2D 层,通过求平均将其适合于我们的全连接层。 通过指定`pooling='avg' or 'max'`来加载基本模型时,也可以完成此操作。 这是您如何处理此问题的电话。
至此,我们已经准备好训练网络。 但是,在执行此操作之前,我们需要记住冻结基本模型中的图层,以免新的完全连接的图层疯狂地试图学习时它们的权重不变。 为此,我们可以使用以下代码遍历各层并将其设置为不可训练:
至此,我们已经准备好训练网络。 但是,在执行此操作之前,我们需要记住冻结基本模型中的层,以免新的完全连接的层疯狂地试图学习时它们的权重不变。 为此,我们可以使用以下代码遍历各层并将其设置为不可训练:
```py
for layer in base_model.layers:
......@@ -162,7 +162,7 @@ validation_generator = val_datagen.flow_from_directory(
# 训练(特征提取)
对于此模型,我们将训练两次。 对于第一轮训练,我们将通过冻结网络的训练来进行 10 个时期的特征提取,仅调整完全连接的层权重,如我们在“迁移网络架构”部分中所讨论的。 然后,在下一部分中,我们将解冻某些图层并再次进行训练,对另外 10 个纪元进行微调,如以下代码所示:
对于此模型,我们将训练两次。 对于第一轮训练,我们将通过冻结网络的训练来进行 10 个周期的特征提取,仅调整完全连接的层权重,如我们在“迁移网络架构”部分中所讨论的。 然后,在下一部分中,我们将解冻某些层并再次进行训练,对另外 10 个周期进行微调,如以下代码所示:
```py
data_dir = "data/train/"
......@@ -186,19 +186,19 @@ scores = model.evaluate_generator(val_generator, steps=val_generator.n // batch_
print("Step 1 Scores: Loss: " + str(scores[0]) + " Accuracy: " + str(scores[1]))
```
在前面的示例中,我们使用`ImageDataGenerator``n`属性来了解可用于生成器的图像总数,并将每个期的步骤定义为该数目除以批量大小。
在前面的示例中,我们使用`ImageDataGenerator``n`属性来了解可用于生成器的图像总数,并将每个期的步骤定义为该数目除以批量大小。
此代码的其余部分应该很熟悉。
如前所述,我们只需要训练大约 10 个纪元。 现在,让我们看一下 TensorBoard 中的训练过程:
如前所述,我们只需要训练大约 10 个周期。 现在,让我们看一下 TensorBoard 中的训练过程:
![](img/ba99854e-0291-4744-b2ed-1b099411fa0c.png)
如您所见,即使经过一个期,网络的表现仍然非常好。 直到大约第 7 个阶段,我们都取得了非常微弱的表现提升。在第 7 个阶段,我们达到了最佳表现,导致 0.9828 的精度和 0.0547 的损失。
如您所见,即使经过一个期,网络的表现仍然非常好。 直到大约第 7 个阶段,我们都取得了非常微弱的表现提升。在第 7 个阶段,我们达到了最佳表现,导致 0.9828 的精度和 0.0547 的损失。
# 训练(微调)
为了微调网络,我们需要解冻一些冻结的层。 您可以解冻多少层,并且可以解冻任意数量的网络。 实际上,在大多数情况下,我们仅看到解冻最顶层的好处。 在这里,我仅解冻最后一个初始块,该块从图`249`层开始。 以下代码描述了此技术:
为了微调网络,我们需要解冻一些冻结的层。 您可以解冻多少层,并且可以解冻任意数量的网络。 实际上,在大多数情况下,我们仅看到解冻最顶层的好处。 在这里,我仅解冻最后一个初始块,该块从图的`249`层开始。 以下代码描述了此技术:
```py
def build_model_fine_tuning(model, learning_rate=0.0001, momentum=0.9):
......
# 九、从头开始训练 RNN
**循环神经网络****RNN**)是为建模顺序数据而构建的一组神经网络。 在最后几章中,我们研究了使用卷积层从图像中学习特征。 当我们想从所有相关的值中学习特征时,递归层同样有用: `x[t]``x[t-1]``x[t-2]``x[t-3]`
**循环神经网络****RNN**)是为建模顺序数据而构建的一组神经网络。 在最后几章中,我们研究了使用卷积层从图像中学习特征。 当我们想从所有相关的值中学习特征时,循环层同样有用: `x[t]``x[t-1]``x[t-2]``x[t-3]`
在本章中,我们将讨论如何将 RNN 用于时间序列问题,这无疑是涉及按时间或时间顺序排列的一系列数据点的问题。
......@@ -31,7 +31,7 @@
![](img/c3a17aee-28bb-49e5-b4fc-70be936df1ad.jpg)
如果我们将这个图跨时间展平,它将看起来更像下图。 网络通知本身的想法是“递归”一词的来源,尽管作为 CS 专业,我始终将其视为循环神经网络。
如果我们将这个图跨时间展平,它将看起来更像下图。 网络通知本身的想法是“循环”一词的来源,尽管作为 CS 专业,我始终将其视为循环神经网络。
![](img/78f99d5b-c72c-4dd8-a83f-e000b7e711c5.jpg)
......@@ -47,7 +47,7 @@
此处`t`是前一个时间步输出和当前时间步输入的线性组合,均由权重矩阵`W``U`进行参数化。 一旦计算出`t`,它就具有非线性函数,最常见的是双曲正切`h[t]`。 最后,神经元的输出`o[t]``h[t]`与权重矩阵结合在一起,`V``a`偏置,`c`偏置。
当您查看此结构时,请尝试想象一下一种情况,在该情况下,您很早就需要一些非常重要的信息。 随着序列的延长,重要的早期信息被遗忘的可能性就更高,因为新信号会轻易地压倒旧信息。 从数学上讲,单的梯度将消失或爆炸。
当您查看此结构时,请尝试想象一下一种情况,在该情况下,您很早就需要一些非常重要的信息。 随着序列的延长,重要的早期信息被遗忘的可能性就更高,因为新信号会轻易地压倒旧信息。 从数学上讲,单的梯度将消失或爆炸。
这是标准 RNN 的主要缺点。 在实践中,传统的 RNN 难以按顺序学习真正的长期交互作用。 他们很健忘!
......@@ -103,17 +103,17 @@ LSTM 的另一个关键特性是内部自循环,使设备可以长期积累信
与正常反向传播一样,BPTT 的目标是使用整体网络误差,通过梯度来调整每个神经元/单元对它们对整体误差的贡献的权重。 总体目标是相同的。
但是,当使用 BPTT 时,我们对错误的定义会稍有变化。 正如我们刚刚看到的,可以通过几个时间步长展开神经元循环。 我们关心所有这些时间步长的预测质量,而不仅仅是终端时间步长,因为 RNN 的目标是正确预测序列,因为逻辑单元错误定义为所有时间步长上展开的错误之和。
但是,当使用 BPTT 时,我们对误差的定义会稍有变化。 正如我们刚刚看到的,可以通过几个时间步长展开神经元循环。 我们关心所有这些时间步长的预测质量,而不仅仅是终端时间步长,因为 RNN 的目标是正确预测序列,因为逻辑单元误差定义为所有时间步长上展开的误差之和。
使用 BPTT 时,我们需要总结所有时间步骤中的错误。 然后,在计算完该总体误差后,我们将通过每个时间步的梯度来调整单位的权重。
使用 BPTT 时,我们需要总结所有时间步骤中的误差。 然后,在计算完该总体误差后,我们将通过每个时间步的梯度来调整单元的权重。
这迫使我们明确定义将展开 LSTM 的程度。 在下面的示例中,您将看到这一点,当我们创建一组特定的时间步长时,将为每个观察值进行训练。
您选择反向传播的步骤数当然是超参数。 如果您需要从序列中很远的地方学习一些东西,显然您必须在序列中包括很多滞后。 您需要能够捕获相关期间。 另一方面,捕获太多的时间步长也不可取。 该网络将变得非常难以训练,因为随着时间的流逝,梯度会变得非常小。 这是前面几章中描述的梯度消失问题的另一个实例。
如您想象的那样,您可能想知道是否选择太大的时间步会使程序崩溃。 如果渐变驱动得太小以至于变为 NaN,那么我们将无法完成更新操作。 解决此问题的一种常见且简便的方法是在某些上下阈值之间固定梯度,我们将其称为梯度裁剪。 默认情况下,所有 **Keras** 优化器均已启用渐变剪切。 如果您的梯度被剪裁,则在该时间范围内网络可能不会学到很多东西,但是至少您的程序不会崩溃。
如您想象的那样,您可能想知道是否选择太大的时间步会使程序崩溃。 如果梯度驱动得太小以至于变为 NaN,那么我们将无法完成更新操作。 解决此问题的一种常见且简便的方法是在某些上下阈值之间固定梯度,我们将其称为梯度裁剪。 默认情况下,所有 **Keras** 优化器均已启用梯度剪切。 如果您的梯度被剪裁,则在该时间范围内网络可能不会学到很多东西,但是至少您的程序不会崩溃。
如果 BPTT 看起来确实令人困惑,请想象一下 LSTM 处于展开状态,其中每个时间步都有一个单。 对于该网络结构,该算法实际上与标准反向传播几乎相同,不同之处在于所有展开的层均共享权重。
如果 BPTT 看起来确实令人困惑,请想象一下 LSTM 处于展开状态,其中每个时间步都有一个单。 对于该网络结构,该算法实际上与标准反向传播几乎相同,不同之处在于所有展开的层均共享权重。
# 时间序列问题回顾
......@@ -129,7 +129,7 @@ LSTM 的另一个关键特性是内部自循环,使设备可以长期积累信
在计量经济学时间序列中,数量通常被定义为**库存****流量**。 库存度量是指特定时间点的数量。 例如,2008 年 12 月 31 日的 SP500 的值是库存测量值。 流量测量是一段时间间隔内的速率。 美国股票市场从 2009 年到 2010 年的增长率是一种流量度量。
最经常进行预测时,我们会关注预测流量。 如果我们将预测想象为一种特定的回归,那么我们偏爱流量的第一个也是最明显的原因是,流量估更有可能是插值而不是外推,而且插值几乎总是更安全。 此外,大多数时间序列模型都具有平稳性的假设。 固定时间序列是其统计属性(均值,方差和自相关)随时间恒定的序列。 如果我们使用一定数量的库存测量,则会发现大多数现实世界中的问题远非静止不动。
最经常进行预测时,我们会关注预测流量。 如果我们将预测想象为一种特定的回归,那么我们偏爱流量的第一个也是最明显的原因是,流量估更有可能是插值而不是外推,而且插值几乎总是更安全。 此外,大多数时间序列模型都具有平稳性的假设。 固定时间序列是其统计属性(均值,方差和自相关)随时间恒定的序列。 如果我们使用一定数量的库存测量,则会发现大多数现实世界中的问题远非静止不动。
使用 LSTM 进行时间序列分析时,虽然没有假设(读取规则)需要平稳性,但根据实际经验,我发现对相对固定的数据进行训练的 LSTM 更加健壮。 使用 LSTM 进行时间序列预测时,几乎在所有情况下,一阶差分就足够了。
......@@ -139,7 +139,7 @@ LSTM 的另一个关键特性是内部自循环,使设备可以长期积累信
例如,如果我们在三月份拥有价值 80 美元的股票,而在四月份突然价值 100 美元,则该股票的流率将为 20 美元。
一阶微分不能保证平稳的时间序列。 我们可能还需要删除季节或趋势。 趋势消除是专业预测员日常生活的重要组成部分。 如果我们使用传统的统计模型进行预测,则需要做更多的工作。 虽然我们没有涵盖这些内容的页面,但我们可能还需要执行二阶差分,季节性趋势下降或更多操作。 **增强 Dickey-Fuller****ADF**)测试是一种统计测试,通常用于确定我们的时间序列是否实际上是静止的。 如果您想知道时间序列是否稳定,[可以使用增强的 Dickey-Fuller 检验来检查](https://en.wikipedia.org/wiki/Augmented_Dickey%E2%80%93Fuller_test)。 但是,对于 LSTM,一阶微分通常可能就足够了。 只需了解网络最肯定会学习您数据集中剩余的季节和期。
一阶微分不能保证平稳的时间序列。 我们可能还需要删除季节或趋势。 趋势消除是专业预测员日常生活的重要组成部分。 如果我们使用传统的统计模型进行预测,则需要做更多的工作。 虽然我们没有涵盖这些内容的页面,但我们可能还需要执行二阶差分,季节性趋势下降或更多操作。 **增强 Dickey-Fuller****ADF**)测试是一种统计测试,通常用于确定我们的时间序列是否实际上是静止的。 如果您想知道时间序列是否稳定,[可以使用增强的 Dickey-Fuller 检验来检查](https://en.wikipedia.org/wiki/Augmented_Dickey%E2%80%93Fuller_test)。 但是,对于 LSTM,一阶微分通常可能就足够了。 只需了解网络最肯定会学习您数据集中剩余的季节和期。
# ARIMA 和 ARIMAX 预测
......@@ -175,8 +175,8 @@ ARIMAX 模型允许在时间序列模型中包含一个或多个协变量。 您
在继续之前,让我们看一下将要进行的数据准备的总体流程。 为了使用此数据集训练 LSTM,我们需要:
1. 加载数据集并将纪元时间转换为熊猫日期时间。
2. 通过对日期范围进行切片来创建火车和测试集。
1. 加载数据集并将周期时间转换为熊猫日期时间。
2. 通过对日期范围进行切片来创建训练和测试集。
3. 差分我们的数据集。
4. 将差异缩放到更接近我们的激活函数的程度。 我们将使用 -1 到 1,因为我们将使用`tanh`作为激活
5. 创建一个训练集,其中每个目标`x[t]`都有一系列与之相关的滞后`x[t-1], ..., x[t-n]`。 在此训练集中,您可以将`x[t]`视为我们的典型因变量`y`。 滞后序列`x[t-1], ..., x[t-n]`可以看作是典型的`X`训练矩阵。
......@@ -185,7 +185,7 @@ ARIMAX 模型允许在时间序列模型中包含一个或多个协变量。 您
# 加载数据集
从磁盘加载数据集是一项相当简单的工作。 如前所述,我们将按日期对数据进行切片。 为此,我们需要将数据集中的 Unix 纪元时间转换为可分割的日期。 可以通过`pandas to_datetime()`方法轻松实现,如以下代码所示:
从磁盘加载数据集是一项相当简单的工作。 如前所述,我们将按日期对数据进行切片。 为此,我们需要将数据集中的 Unix 周期时间转换为可分割的日期。 可以通过`pandas to_datetime()`方法轻松实现,如以下代码所示:
```py
def read_data():
......@@ -243,7 +243,7 @@ def scale_data(df, scaler=None):
return scaler, scaled_df
```
请注意,此函数可以选择使用已经适合的缩放器。 这使我们能够将火车定标器应用到我们的测试仪上。
请注意,此函数可以选择使用已经适合的缩放器。 这使我们能够将训练定标器应用到我们的测试仪上。
# 创建滞后的训练集
......@@ -346,7 +346,7 @@ output = Dense(1, activation='tanh', name='output')(lstm2)
# 网络架构
我们的网络将使用两个 Keras LSTM 层,每个层具有 100 个 LSTM 单
我们的网络将使用两个 Keras LSTM 层,每个层具有 100 个 LSTM 单
```py
inputs = Input(batch_shape=(batch_shape, sequence_length,
......@@ -368,7 +368,7 @@ output = Dense(1, activation='tanh', name='output')(lstm2)
默认为无状态配置。 使用无状态 LSTM 配置时,每批 LSTM 单元存储器都会重置。 这使得批量大小成为非常重要的考虑因素。 当您正在学习的序列彼此不依赖时,无状态效果最佳。 下一个单词的句子级预测可能是何时使用无状态的一个很好的例子。
有状态配置会在每个期重置 LSTM 单元存储器。 当训练集中的每个序列取决于其之前的序列时,最常使用此配置。 如果句子级别的预测对于无状态配置可能是一项好任务,那么文档级别的预测对于有状态模型可能是一项好任务。
有状态配置会在每个期重置 LSTM 单元存储器。 当训练集中的每个序列取决于其之前的序列时,最常使用此配置。 如果句子级别的预测对于无状态配置可能是一项好任务,那么文档级别的预测对于有状态模型可能是一项好任务。
最终,这种选择取决于问题,并且可能需要在测试每个选项时进行一些试验。
......@@ -395,7 +395,7 @@ model.save("lstm_model.h5")
在准备好数据之后,我们使用我们已经遍历的架构实例化一个网络,然后按预期对其进行拟合。
在这里,我使用的是有状态的 LSTM。 有状态 LSTM 的一个实际好处是,与无状态 LSTM 相比,它们倾向于在更少的时间进行训练。 如果要将其重构为无状态 LSTM,则在网络完成学习之前可能需要 100 个纪元,而此处我们仅使用 10 个纪元
在这里,我使用的是有状态的 LSTM。 有状态 LSTM 的一个实际好处是,与无状态 LSTM 相比,它们倾向于在更少的时间进行训练。 如果要将其重构为无状态 LSTM,则在网络完成学习之前可能需要 100 个周期,而此处我们仅使用 10 个周期
# 测量表现
......
此差异已折叠。
# 十一、训练 Seq2Seq 模型
在上一章中,我们讨论了文档分类以及文档分类的一种特殊情况,称为**情感分类**。 这样做时,我们不得不谈论很多关于量化的知识。
在上一章中,我们讨论了文档分类以及文档分类的一种特殊情况,称为**情感分类**。 这样做时,我们不得不谈论很多关于量化的知识。
在本章中,我们将继续谈论解决 NLP 问题,但是除了分类之外,我们将生成新的单词序列。
......@@ -80,7 +80,7 @@
# 注意
注意是可以在序列到序列模型中实现的另一种有用的训练技巧。 注意使解码器在输入序列的每个步骤中都能看到隐藏状态。 这使网络可以专注于(或关注)特定的输入,这可以加快训练速度并可以提高模型的准确。 注意通常是一件好事。 但是,在撰写本文时,Keras 尚未内置注意力。尽管如此,Keras 目前确实有一个拉取请求正在等待自定义注意层。 我怀疑很快就会在 Keras 中建立对关注的支持。
注意是可以在序列到序列模型中实现的另一种有用的训练技巧。 注意使解码器在输入序列的每个步骤中都能看到隐藏状态。 这使网络可以专注于(或关注)特定的输入,这可以加快训练速度并可以提高模型的准确。 注意通常是一件好事。 但是,在撰写本文时,Keras 尚未内置注意力。尽管如此,Keras 目前确实有一个拉取请求正在等待自定义注意层。 我怀疑很快就会在 Keras 中建立对关注的支持。
# 翻译指标
......@@ -287,7 +287,7 @@ decoder_outputs, _, _ = LSTM(lstm_units, return_sequences=True,
为了在给定输入序列的情况下预测整个序列,我们需要稍微重新安排一下架构。 我怀疑在 Keras 的未来版本中,这将变得更简单,但是从今天起这是必需的步骤。
为什么需要有所不同? 因为我们没有推断的`decoder_input_data`教师量。 我们现在独自一人。 因此,我们将必须进行设置,以便我们不需要该向量。
为什么需要有所不同? 因为我们没有推断的`decoder_input_data`教师量。 我们现在独自一人。 因此,我们将必须进行设置,以便我们不需要该向量。
让我们看一下这种推理架构,然后逐步执行代码:
......@@ -385,11 +385,11 @@ decoder_model.save('char_s2s_decoder.h5')
训练模型适合后,我将保存所有三个模型,并将它们再次加载到为推理而构建的单独程序中。 我这样做是为了使代码保持简洁,因为推理代码本身非常复杂。
让我们来看看这个模型的 100 个时代的模型训练:
让我们来看看这个模型的 100 个周期的模型训练:
![](img/d6844cf9-3d55-4e3f-9722-4c54b3be05b5.png)
如您所见,我们在第 20 个时期开始过拟合。虽然损失持续减少,但`val_loss`却在增加。 在这种情况下,模型检查指向可能无法正常工作,因为在训练结束之前我们不会序列化推理模型。 因此,理想情况下,我们应该再训练一次,将训练的时期数设置为略大于 TensorBoard 中观察到的最小值。
如您所见,我们在第 20 个周期开始过拟合。虽然损失持续减少,但`val_loss`却在增加。 在这种情况下,模型检查指向可能无法正常工作,因为在训练结束之前我们不会序列化推理模型。 因此,理想情况下,我们应该再训练一次,将训练的周期数设置为略大于 TensorBoard 中观察到的最小值。
# 推理
......@@ -397,7 +397,7 @@ decoder_model.save('char_s2s_decoder.h5')
总体而言,推理步骤如下:
1. 加载数据并再次量化(我们需要字符到索引的映射以及一些转换进行测试)
1. 加载数据并再次量化(我们需要字符到索引的映射以及一些转换进行测试)
2. 使用字符对字典进行索引,我们将创建字符字典的反向索引,因此一旦我们预测了正确的字符,我们就可以从数字返回到字符
3. 选择一些输入序列进行翻译,然后通过编码器运行,获取状态
4. 将状态和`<SOS>`字符`'\t'`发送到解码器。
......
# 十二、深度强化学习
在本章中,我们将以略有不同的方式使用深度神经网络。 我们将要构建一个智能体,而不是预测一个类的成员,估一个值,甚至生成一个序列。 尽管机器学习和人工智能这两个术语经常互换使用,但在本章中,我们将讨论人工智能作为一种可以感知其环境的智能体,并采取步骤在该环境中实现某些目标。
在本章中,我们将以略有不同的方式使用深度神经网络。 我们将要构建一个智能体,而不是预测一个类的成员,估一个值,甚至生成一个序列。 尽管机器学习和人工智能这两个术语经常互换使用,但在本章中,我们将讨论人工智能作为一种可以感知其环境的智能体,并采取步骤在该环境中实现某些目标。
想象一个可以玩象棋或围棋之类策略游戏的特工。 构建神经网络来解决此类游戏的一种非常幼稚的方法可能是使用一种网络架构,在该架构中,我们对每个可能的棋盘/棋子组合进行热编码,然后预测每个可能的下一个动作。 尽管该网络庞大而复杂,但可能做得并不好。 要很好地玩国际象棋,您不仅要考虑下一步,而且还要考虑接下来的步伐。 在不确定的情况下,我们的智能体将需要考虑给定未来行动的最佳下一步行动。
......@@ -32,7 +32,7 @@
动作会在原始状态`s`和下一个状态`s'`的智能体之间进行转换,智能体会在其中获得一些奖励`r`。 智能体选择动作的方式称为**智能体策略**,通常称为`pi`
强化学习的目的是找到一系列动作,使行动者从一个州到另一个州,并获得尽可能多的报酬。
强化学习的目的是找到一系列动作,使行动者从一个状态到另一个状态,并获得尽可能多的报酬。
# 马尔可夫决策过程
......@@ -50,7 +50,7 @@
# Q 学习
想象一下,我们有一些函数`Q`,可以估出采取行动的回报:
想象一下,我们有一些函数`Q`,可以估出采取行动的回报:
![](img/c0927993-35eb-4977-a4bc-ccd338f9fc95.png)
......@@ -68,7 +68,7 @@
`Q`函数的讨论使我们陷入了传统强化学习的重要局限。 您可能还记得,它假设状态空间是有限且离散的。 不幸的是,这不是我们生活的世界,也不是我们的智能体在很多时候会发现自己的环境。 考虑一个可以打乒乓球的经纪人。 状态空间的重要组成部分是乒乓球的速度,它当然不是离散的。 像我们不久将要看到的那样,可以看到的特工会看到一个图像,该图像是一个很大的连续空间。
我们讨论的 Bellman 方程将要求我们在州与州之间转移时保持经验奖励的大矩阵。 但是,当面对连续的状态空间时,这是不可能的。 可能的状态本质上是无限的,我们不能创建无限大小的矩阵。
我们讨论的 Bellman 方程将要求我们在状态与状态之间转移时保持经验奖励的大矩阵。 但是,当面对连续的状态空间时,这是不可能的。 可能的状态本质上是无限的,我们不能创建无限大小的矩阵。
幸运的是,我们可以使用深度神经网络来近似`Q`函数。 这可能不会让您感到惊讶,因为您正在阅读一本深度学习书,因此您可能猜测深度学习必须在某个地方出现。 就是那个地方
......@@ -82,7 +82,7 @@
# 在线学习
当我们的智能体通过采取行动从一个州过渡到另一个州时,它会得到奖励。 智能体可以通过使用每个状态,动作和奖励作为训练输入来在线学习。 在执行每个操作后,该智能体将更新其神经网络权重,并希望在此过程中变得更聪明。 这是在线学习的基本思想。 智能体就像您和我一样,不断学习。
当我们的智能体通过采取行动从一个状态过渡到另一个状态时,它会得到奖励。 智能体可以通过使用每个状态,动作和奖励作为训练输入来在线学习。 在执行每个操作后,该智能体将更新其神经网络权重,并希望在此过程中变得更聪明。 这是在线学习的基本思想。 智能体就像您和我一样,不断学习。
这种朴素的在线学习的缺点有些明显,有两个方面:
......@@ -117,7 +117,7 @@
在这一点上,我们应该有足够的背景知识来开始建立深层的 Q 网络,但是仍然需要克服很大的障碍。
利用深度强化学习的智能体可能是一个很大的挑战,但是最初由 Matthias Plappert 编写的 Keras-RL 库使其变得更加容易。 我将使用他的库来为本章介绍的智能体提供支持。
利用深度强化学习的智能体可能是一个很大的挑战,但是最初由 Matthias Plappert 编写的 Keras-RL 库使其变得更加容易。 我将使用他的库来为本章介绍的智能体提供支持。
当然,如果没有环境,我们的经纪人将不会有太多的乐趣。 我将使用 OpenAI 体育馆,该体育馆提供许多环境,包括状态和奖励函数,我们可以轻松地使用它们来构建供智能体探索的世界。
......@@ -325,7 +325,7 @@ dqn.fit(env, nb_steps=1000000,
# 结果
我在 Git 一章中包含了 Lunar Lander 的权重,并创建了一个脚本,该脚本在启用可视化的情况下运行这些权重`dqn_lunar_lander_test.py`。 它加载经过训练的模型权重并运行 10 集。 在大多数情况下,特工能够以惊人的技能和准确将月球着陆器降落在其着陆板上,如以下屏幕截图所示:
我在 Git 一章中包含了 Lunar Lander 的权重,并创建了一个脚本,该脚本在启用可视化的情况下运行这些权重`dqn_lunar_lander_test.py`。 它加载经过训练的模型权重并运行 10 集。 在大多数情况下,特工能够以惊人的技能和准确将月球着陆器降落在其着陆板上,如以下屏幕截图所示:
![](img/a22a91ff-1c32-4a61-90bb-3b52d6555309.png)
......
......@@ -37,7 +37,7 @@ GAN 的整体架构如下图所示。 生成器和判别器分别是单独的深
![](img/370d895f-0973-49cf-8e8a-a9746d082883.png)
给生成器一个随机噪声量(`z`),并创建一个输出`G(z)`(对于 DCGAN,这是一个图像),希望它能欺骗判别器。
给生成器一个随机噪声量(`z`),并创建一个输出`G(z)`(对于 DCGAN,这是一个图像),希望它能欺骗判别器。
判别器既得到实际训练数据(`X`),又得到生成器输出`G(z)`。 要做的是确定其输入实际上是真实的概率`P(X)`
......@@ -47,13 +47,13 @@ GAN 的整体架构如下图所示。 生成器和判别器分别是单独的深
# 生成器架构
在此示例中,我们使用适合于生成`28 x 28`灰度图像的层大小,这正是我们稍后在 MNIST 示例中将要执行的操作。 如果您以前没有使用过生成器,那么生成器的算法可能会有些棘手,因此我们将在遍历每一层时进行介绍。 下图显示了架构:
在此示例中,我们使用适合于生成`28 x 28`灰度图像的层大小,这正是我们稍后在 MNIST 示例中将要执行的操作。 如果您以前没有使用过生成器,那么生成器的算法可能会有些棘手,因此我们将在遍历每一层时进行介绍。 下图显示了架构:
![](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`。 如果现在看来这还不算什么,请不用担心,在编写代码后,这才有意义。
在完全连接的层之后,事情变得更加简单。 就像我们一直一样,我们正在使用卷积层。 但是,这次我们反向使用它们。 我们不再使用最大池来缩减样本量。 取而代之的是,我们进行上采样,在学习视觉特征时使用卷积来构建我们的网络,并最终输出适当形状的张量。
......@@ -81,11 +81,11 @@ DCGAN 框架是使用迷你批量来进行训练的,这与我之前在本书
判别器更新权重后,我们将判别器和生成器一起训练为一个模型。 这样做时,我们将使判别器的权重不可训练,将其冻结在适当的位置,但仍允许判别器将梯度反向传播到生成器,以便生成器可以更新其权重。
对于训练过程中的这一步,我们将使用噪声量作为输入,这将导致生成器生成图像。 判别器将显示该图像,并要求预测该图像是否真实。 下图说明了此过程:
对于训练过程中的这一步,我们将使用噪声量作为输入,这将导致生成器生成图像。 判别器将显示该图像,并要求预测该图像是否真实。 下图说明了此过程:
![](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`转换为梯度,然后通过判别器传给生成器。 这将更新生成器权重,使它可以从判别者对问题空间的了解中受益,以便它可以学习创建更逼真的生成图像。
然后重复这两个步骤,直到生成器能够创建与训练集中的数据相似的数据,使得判别器无法再将两个数据集区分开,这成为了一个猜谜游戏。 判别器。 此时,生成器将不再能够改进。 当我们找到纳什均衡时,就对网络进行了训练。
......@@ -121,7 +121,7 @@ DCGAN 框架是使用迷你批量来进行训练的,这与我之前在本书
* **在生成器中使用丢弃**:这将产生噪声并防止模式崩溃。
* **使用软标签**:对于真实示例,请使用介于 0.7 和 1 之间的标签,对于伪示例,请使用介于 0 和 0.3 之间的标签。 这种噪声有助于保持信息从判别器流向生成器。
在本章的其他地方,我们还将介绍许多其他的 GAN 黑魔法。 但是,我认为在成功实 GAN 时,这几项技巧是最重要的。
在本章的其他地方,我们还将介绍许多其他的 GAN 黑魔法。 但是,我认为在成功实 GAN 时,这几项技巧是最重要的。
# 使用 Keras GAN 生成 MNIST 图像
......@@ -210,13 +210,13 @@ def build_discriminator(img_shape):
首先,您可能会注意到形状奇怪的`zeroPadding2D()`层。 第二次卷积后,我们的张量从`28 x 28 x 3`变为`7 x 7 x 64`。 这一层使我们回到偶数,在行和列的一侧都加零,这样我们的张量现在为`8 x 8 x 64`
更不寻常的是同时使用批量规范化和辍学。 通常,这两层不能一起使用。 但是,就 GAN 而言,它们似乎确实使网络受益。
更不寻常的是同时使用批量规范化和丢弃法。 通常,这两层不能一起使用。 但是,就 GAN 而言,它们似乎确实使网络受益。
# 创建栈式模型
现在我们已经组装了`generator``discriminator`,我们需要组装第三个模型,这是两个模型的栈,在`discriminator`损失的情况下,我们可以用来训练生成器。
为此,我们可以创建一个新模型,这次使用以前的模型作为新模型中的层,如以下代码所示:
为此,我们可以创建一个新模型,这次使用以前的模型作为新模型中的层,如以下代码所示:
```py
discriminator = build_discriminator(img_shape=(28, 28, 1))
......@@ -288,7 +288,7 @@ for epoch in range(epochs + 1):
save_imgs(generator, epoch, batch)
```
可以肯定,这里发生了很多事情。 和以前一样,让我们​​逐个细分。 首先,让我们看一下生成噪声量的代码:
可以肯定,这里发生了很多事情。 和以前一样,让我们​​逐个细分。 首先,让我们看一下生成噪声量的代码:
```py
noise = np.random.normal(0, 1, (half_batch, 100))
......@@ -306,7 +306,7 @@ real_images = X_train[idx]
real_labels = np.ones((half_batch, 1))
```
是的,在这种情况下,我们正在抽样更换。 它确实可以解决,但可能不是实小批量训练的最佳方法。 但是,它可能是最简单,最常见的。
是的,在这种情况下,我们正在抽样更换。 它确实可以解决,但可能不是实小批量训练的最佳方法。 但是,它可能是最简单,最常见的。
由于我们正在使用这些图像来训练判别器,并且由于它们是真实图像,因此我们将它们分配为`1`作为标签,而不是`0`。 现在我们已经组装了判别器训练集,我们将更新判别器。 还要注意,我们没有使用我们之前讨论的软标签。 那是因为我想让事情尽可能地容易理解。 幸运的是,在这种情况下,网络不需要它们。 我们将使用以下代码来训练判别器:
......@@ -328,7 +328,7 @@ 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`生成示例图像并将其保存到磁盘,如以下代码所示:
......@@ -366,7 +366,7 @@ for j in range(c):
当您构建深层神经网络来创建图像时,好坏有点主观。 让我们看一下训练过程的一些示例,以便您可以亲自了解 GAN 如何开始学习如何生成 MNIST。
这是第一个时代的第一批网络。 显然,此时生成器对生成 MNIST 并不了解。 只是噪音,如下图所示:
这是第一个周期的第一批网络。 显然,此时生成器对生成 MNIST 并不了解。 只是噪音,如下图所示:
![](img/100f93bf-4aee-47f2-9b5f-542ffd5ac42a.png)
......@@ -374,11 +374,11 @@ for j in range(c):
![](img/6ccd70b9-a564-4bc3-9d65-99301b571751.png)
在 200 个批次的期 0 之后,我们几乎可以看到数字,如下图所示:
在 200 个批次的期 0 之后,我们几乎可以看到数字,如下图所示:
![](img/69d0a788-d965-4e70-a6f5-e8589b810c9f.png)
一个完整的期后,这是我们的生成器。 我认为这些生成的数字看起来不错,而且我可以看到判别符可能会被它们欺骗。 在这一点上,我们可能会继续改善一点,但是随着计算机生成一些令人信服的 MNIST 数字,我们的 GAN 似乎已经发挥了作用,如下图所示:
一个完整的期后,这是我们的生成器。 我认为这些生成的数字看起来不错,而且我可以看到判别符可能会被它们欺骗。 在这一点上,我们可能会继续改善一点,但是随着计算机生成一些令人信服的 MNIST 数字,我们的 GAN 似乎已经发挥了作用,如下图所示:
![](img/3b179ae1-40df-4859-8849-01c5011588f7.png)
......@@ -430,7 +430,7 @@ 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`可以毫无问题地删除,因为没有它的层算术就可以工作。
# 训练循环
......@@ -444,15 +444,15 @@ discriminator = build_discriminator(img_shape=(32, 32, 3))
# 模型评估
`CIFAR-10`数据集当然更加复杂,并且网络具有更多的参数。 因此,事情将需要更长的时间。 这是在期 0(批次 300)中我们的图像的样子:
`CIFAR-10`数据集当然更加复杂,并且网络具有更多的参数。 因此,事情将需要更长的时间。 这是在期 0(批次 300)中我们的图像的样子:
![](img/09db5d5d-b9fb-4d13-a89e-6f83e4c55da2.png)
我可能开始看到一些边缘,但是看起来并不像什么。 但是,如果我们等待几个纪元,我们显然处在松鼠和怪异的鱼类地区。 我们可以看到一些东西正在成形,只是有些模糊,如下图所示:
我可能开始看到一些边缘,但是看起来并不像什么。 但是,如果我们等待几个周期,我们显然处在松鼠和怪异的鱼类地区。 我们可以看到一些东西正在成形,只是有些模糊,如下图所示:
![](img/72ffa712-8e86-49e4-8dc5-46daebb79db0.png)
下图显示了 12 个纪元后的生成器:
下图显示了 12 个周期后的生成器:
![](img/7a6bfedb-d613-4204-badf-c62e7e5f00f4.png)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册