提交 389d6b1a 编写于 作者: W wizardforcel

2020-09-15 22:46:25

上级 36cc08fb
# 一、TensorFlow 的设置和介绍 # 一、TensorFlow 的设置和介绍
TensorFlow 是 Google 创建的开源软件库,可让您构建和执行数据流图以进行数值计算。 在这些图中,每个节点表示要执行的某些计算或功能,连接节点的图边表示它们之间流动的数据。 在 TensorFlow 中,数据是称为**张量**的多维数组。 张量围绕图流动,因此命名为 TensorFlow。 TensorFlow 是 Google 创建的开源软件库,可让您构建和执行数据流图以进行数值计算。 在这些图中,每个节点表示要执行的某些计算或功能,连接节点的图边表示它们之间流动的数据。 在 TensorFlow 中,数据是称为**张量**的多维数组。 张量围绕图流动,因此命名为 TensorFlow。
**机器学习****ML**)模型,例如卷积神经网络,可以用这些图表示,而这正是 TensorFlow 最初设计的目的。 **机器学习****ML**)模型,例如卷积神经网络,可以用这些图表示,而这正是 TensorFlow 最初设计的目的。
...@@ -23,12 +23,12 @@ TensorFlow 是 Google 创建的开源软件库,可让您构建和执行数据 ...@@ -23,12 +23,12 @@ TensorFlow 是 Google 创建的开源软件库,可让您构建和执行数据
在 TensorFlow 中,计算图是安排成图结构的一系列 TensorFlow 操作。 TensorFlow 图包含两种主要类型的组件: 在 TensorFlow 中,计算图是安排成图结构的一系列 TensorFlow 操作。 TensorFlow 图包含两种主要类型的组件:
* **操作**:更通常称为 **ops**,这些是图中的节点。 操作执行需要在图中进行的任何计算。 通常,它们消耗并产生张量。 一些操作很特殊,运行时可能会有某些副作用。 * **操作**:更通常称为 **ops**,这些是图中的节点。 操作执行需要在图中进行的任何计算。 通常,它们消耗并产生张量。 一些操作很特殊,运行时可能会有某些副作用。
* **张量**:这是图形的边缘; 它们将节点连接起来并表示流经节点的数据。 大多数 TensorFlow 操作将产生并消耗这些`tf.Tensors` * **张量**:这是图的边; 它们将节点连接起来并表示流经节点的数据。 大多数 TensorFlow 操作将产生并消耗这些`tf.Tensors`
在 TensorFlow 中,您使用的主要对象称为张量。 张量是向量和矩阵的一般化。 即使向量是一维的,矩阵是二维的,张量也可以是`n`维。 TensorFlow 将张量表示为用户指定数据类型的`n`维数组,例如`float32` 在 TensorFlow 中,您使用的主要对象称为张量。 张量是向量和矩阵的一般化。 即使向量是一维的,矩阵是二维的,张量也可以是`n`维。 TensorFlow 将张量表示为用户指定数据类型的`n`维数组,例如`float32`
TensorFlow 程序通过首先构建计算图来工作。 该图将产生一些`tf.Tensor`输出。 要求值此输出,您必须通过在输出张量上调用`tf.Session.run`来在`tf.Session`中运行它。 当您执行此操作时,TensorFlow 将执行图中需要执行的所有部分,以评估您要求其运行的`tf.Tensor` TensorFlow 程序通过首先构建计算图来工作。 该图将产生一些`tf.Tensor`输出。 要求值此输出,您必须通过在输出张量上调用`tf.Session.run`来在`tf.Session`中运行它。 当您执行此操作时,TensorFlow 将执行图中需要执行的所有部分,以评估您要求其运行的`tf.Tensor`
# 设置和安装 TensorFlow # 设置和安装 TensorFlow
...@@ -102,7 +102,7 @@ Conda 环境允许您创建隔离的 Python 环境,该环境与系统 Python ...@@ -102,7 +102,7 @@ Conda 环境允许您创建隔离的 Python 环境,该环境与系统 Python
`**b'Tensorflow works!'**` `**b'Tensorflow works!'**`
您刚刚输入的是 TensorFlow 的`Hello World`。 您创建了一个包含单个`tf.constant`的图,该图只是一个常数张量。 将字符串传递给张量时,推断其为字符串类型。 然后,您创建了一个 TensorFlow 会话,这是运行图所必需的,并将您创建的张量上的会话告知`run`。 然后打印出会话运行的结果。 那里有一个额外的`b`,因为它是一个字节流。 您刚刚输入的是 TensorFlow 的`Hello World`。 您创建了一个包含单个`tf.constant`的图,该图只是一个常数张量。 将字符串传递给张量时,推断其为字符串类型。 然后,您创建了一个 TensorFlow 会话,这是运行图所必需的,并将您创建的张量上的会话告知`run`。 然后打印出会话运行的结果。 那里有一个额外的`b`,因为它是一个字节流。
如果没有看到上述内容并且出现一些错误,则最好的选择是检查以下页面,以获取安装时遇到的常见问题的解决方案: 如果没有看到上述内容并且出现一些错误,则最好的选择是检查以下页面,以获取安装时遇到的常见问题的解决方案:
...@@ -117,7 +117,7 @@ TensorFlow 提供了三层 API 抽象来帮助编写您的代码,这些可以 ...@@ -117,7 +117,7 @@ TensorFlow 提供了三层 API 抽象来帮助编写您的代码,这些可以
![](img/c1eed86e-95f5-4967-a2d3-eafe4e96d79a.png) ![](img/c1eed86e-95f5-4967-a2d3-eafe4e96d79a.png)
在最低级别上,您具有基本的 TensorFlow 操作,例如`tf.nn.conv2d``tf.nn.relu`。 使用 TensorFlow 时,这些低级原语为用户提供了最大的控制权。 但是,使用它们的代价是在构造图和编写更多样板代码时必须自己动手做很多事情。 在最低级别上,您具有基本的 TensorFlow 操作,例如`tf.nn.conv2d``tf.nn.relu`。 使用 TensorFlow 时,这些低级原语为用户提供了最大的控制权。 但是,使用它们的代价是在构造图和编写更多样板代码时必须自己动手做很多事情。
现在不用担心理解以下任何代码示例,我保证很快就会出现。 现在只是在这里展示 TensorFlow 中的不同 API 级别。 现在不用担心理解以下任何代码示例,我保证很快就会出现。 现在只是在这里展示 TensorFlow 中的不同 API 级别。
...@@ -149,7 +149,7 @@ def my_conv_2d(input, kernel_size, num_filters, strides): ...@@ -149,7 +149,7 @@ def my_conv_2d(input, kernel_size, num_filters, strides):
还有两个与各层一起工作的 API。 第一个是数据集 API,可轻松将数据加载和馈送到 TensorFlow 图。 第二个是指标 API,它提供工具来测试您训练有素的机器学习模型的运行状况。 我们将在本书的后面部分中学习所有这些内容。 还有两个与各层一起工作的 API。 第一个是数据集 API,可轻松将数据加载和馈送到 TensorFlow 图。 第二个是指标 API,它提供工具来测试您训练有素的机器学习模型的运行状况。 我们将在本书的后面部分中学习所有这些内容。
API 栈的最后一层是 TensorFlow 提供的最高抽象层,这称为估计器 API。 就像使用`tf.layers`来构造权重并为单个层添加偏差一样,估计器 API 封装了许多层的结构,以便我们可以将一个由多个不同层组成的整体模型定义为一个函数调用。 API 栈的最后一层是 TensorFlow 提供的最高抽象层,这称为估计器 API。 就像使用`tf.layers`来构造权重并为单个层添加偏差一样,估计器 API 封装了许多层的结构,以便我们可以将一个由多个不同层组成的整体模型定义为一个函数调用。
本书不会介绍估计器 API 的用法,但是如果读者希望了解有关估计器的更多信息,可以在 TensorFlow 网站上找到一些有用的教程。 本书不会介绍估计器 API 的用法,但是如果读者希望了解有关估计器的更多信息,可以在 TensorFlow 网站上找到一些有用的教程。
...@@ -163,7 +163,7 @@ API 堆栈的最后一层是 TensorFlow 提供的最高抽象层,这称为估 ...@@ -163,7 +163,7 @@ API 堆栈的最后一层是 TensorFlow 提供的最高抽象层,这称为估
事不宜迟,让我们开始在 TensorFlow 中构建您的第一个 ML 模型。 事不宜迟,让我们开始在 TensorFlow 中构建您的第一个 ML 模型。
我们将在本章中解决的问题是从四个给定的特征值正确识别鸢尾花的种类。 这是一个非常容易解决的经典 ML 问题,但它将为我们提供一种很好的方式来介绍在 TensorFlow 中构建图,馈送数据和训练 ML 模型的基础知识。 我们将在本章中解决的问题是从四个给定的特征值正确识别鸢尾花的种类。 这是一个非常容易解决的经典 ML 问题,但它将为我们提供一种很好的方式来介绍在 TensorFlow 中构建图,馈送数据和训练 ML 模型的基础知识。
鸢尾数据集由 150 个数据点组成,每个数据点具有四个相应的特征:长度,花瓣宽度,萼片长度和萼片宽度以及目标标签。 我们的任务是建立一个模型,仅给出这四个特征就可以推断出任何鸢尾的目标标签。 鸢尾数据集由 150 个数据点组成,每个数据点具有四个相应的特征:长度,花瓣宽度,萼片长度和萼片宽度以及目标标签。 我们的任务是建立一个模型,仅给出这四个特征就可以推断出任何鸢尾的目标标签。
...@@ -195,7 +195,7 @@ test_data = shuffled_data[105:] ...@@ -195,7 +195,7 @@ test_data = shuffled_data[105:]
test_labels = shuffled_labels[105:] test_labels = shuffled_labels[105:]
``` ```
让我们再次看一下这段代码,看看到目前为止我们做了什么。 导入 TensorFlow 和 Numpy 之后,我们将整个数据集加载到内存中。 我们的数据由表示为量的四个数值特征组成。 我们总共有 150 个数据点,因此我们的数据将是形状为`150 x 4`的矩阵,其中每一行代表不同的数据点,每一列代表不同的要素。 每个数据点还具有与之关联的目标标签,该目标标签存储在单独的标签向量中。 让我们再次看一下这段代码,看看到目前为止我们做了什么。 导入 TensorFlow 和 Numpy 之后,我们将整个数据集加载到内存中。 我们的数据由表示为量的四个数值特征组成。 我们总共有 150 个数据点,因此我们的数据将是形状为`150 x 4`的矩阵,其中每一行代表不同的数据点,每一列代表不同的要素。 每个数据点还具有与之关联的目标标签,该目标标签存储在单独的标签向量中。
接下来,我们重新整理数据集; 这一点很重要,因此,当我们将其分为训练集和测试集时,我们在这两个集之间平均分配,并且最终不会在一组集中获得所有一种类型的数据。 接下来,我们重新整理数据集; 这一点很重要,因此,当我们将其分为训练集和测试集时,我们在这两个集之间平均分配,并且最终不会在一组集中获得所有一种类型的数据。
...@@ -237,7 +237,7 @@ test_labels = shuffled_labels[105:] ...@@ -237,7 +237,7 @@ test_labels = shuffled_labels[105:]
我们如何在 TensorFlow 代码中全部写出来? 让我们开始创建权重和偏见。 在 TensorFlow 中,如果我们想创建一些可以被我们的代码操纵的张量,那么我们需要使用 TensorFlow 变量。 TensorFlow 变量是`tf.Variable`类的实例。 `tf.Variable`类表示`tf.Tensor`对象,可以通过在其上运行 TensorFlow 操作来更改其值。 变量是类似于张量的对象,因此它们可以以与张量相同的方式传递,并且可以与张量一起使用的任何操作都可以与变量一起使用。 我们如何在 TensorFlow 代码中全部写出来? 让我们开始创建权重和偏见。 在 TensorFlow 中,如果我们想创建一些可以被我们的代码操纵的张量,那么我们需要使用 TensorFlow 变量。 TensorFlow 变量是`tf.Variable`类的实例。 `tf.Variable`类表示`tf.Tensor`对象,可以通过在其上运行 TensorFlow 操作来更改其值。 变量是类似于张量的对象,因此它们可以以与张量相同的方式传递,并且可以与张量一起使用的任何操作都可以与变量一起使用。
要创建变量,我们可以使用`tf.get_variable()`。 调用此函数时,必须提供变量的名称。 此函数将首先检查图形上是否没有其他具有相同名称的变量,如果没有,则它将创建新变量并将其添加到 TensorFlow 图形 要创建变量,我们可以使用`tf.get_variable()`。 调用此函数时,必须提供变量的名称。 此函数将首先检查图上是否没有其他具有相同名称的变量,如果没有,则它将创建新变量并将其添加到 TensorFlow 图
您还必须指定变量要具有的形状,或者,可以使用`tf.constant`张量来初始化变量。 变量将采用您的常数张量的值,并且形状将自动推断。 例如,以下代码将产生一个包含值 21 和 25 的`1x2`张量: 您还必须指定变量要具有的形状,或者,可以使用`tf.constant`张量来初始化变量。 变量将采用您的常数张量的值,并且形状将自动推断。 例如,以下代码将产生一个包含值 21 和 25 的`1x2`张量:
...@@ -269,7 +269,7 @@ return linear_layer_out ...@@ -269,7 +269,7 @@ return linear_layer_out
在此处的代码中,我们创建了将存储权重和偏差的变量。 我们给他们起名字并提供所需的形状。 请记住,我们使用变量是因为我们想通过操作来操纵它们的值。 在此处的代码中,我们创建了将存储权重和偏差的变量。 我们给他们起名字并提供所需的形状。 请记住,我们使用变量是因为我们想通过操作来操纵它们的值。
接下来,我们创建一个`tf.matmul`节点,将我们的输入特征矩阵和权重矩阵作为参数。 可以通过我们的`linear_layer` Python 变量访问此操作的结果。 然后将该结果传递给另一个运算符`tf.nn.bias_add`。 该运算来自 **NN****神经网络**)模块,在我们希望向计算结果中添加偏差量时使用。 偏差必须是一维张量。 接下来,我们创建一个`tf.matmul`节点,将我们的输入特征矩阵和权重矩阵作为参数。 可以通过我们的`linear_layer` Python 变量访问此操作的结果。 然后将该结果传递给另一个运算符`tf.nn.bias_add`。 该运算来自 **NN****神经网络**)模块,在我们希望向计算结果中添加偏差量时使用。 偏差必须是一维张量。
# 使用占位符馈送数据 # 使用占位符馈送数据
...@@ -277,18 +277,18 @@ return linear_layer_out ...@@ -277,18 +277,18 @@ return linear_layer_out
它们就像张量一样,就像张量一样,意味着您可以将它们传递到放置张量的地方。 它们就像张量一样,就像张量一样,意味着您可以将它们传递到放置张量的地方。
通过使用占位符,我们可以向图形中提供外部输入,这些输入可能会在每次运行图形时更改。 它们的自然用法是将数据和标签提供到模型中的一种方式,因为每次我们要运行图形时,我们提供的数据和标签通常都会有所不同。 通过使用占位符,我们可以向图中提供外部输入,这些输入可能会在每次运行图时更改。 它们的自然用法是将数据和标签提供到模型中的一种方式,因为每次我们要运行图时,我们提供的数据和标签通常都会有所不同。
创建占位符时,我们必须提供将要填充的数据类型。 创建占位符时,我们必须提供将要填充的数据类型。
我们将使用两个占位符将数据和标签提供到图中。 我们还提供了馈入这些占位符的任何数据都必须采用的形状。 我们使用`None`表示该特定尺寸的大小可以为任何值。 这样,我们就可以批量输入大小不同的数据。 接下来,我们将看到如何在 TensorFlow 中为我们的问题定义占位符。 我们将使用两个占位符将数据和标签提供到图中。 我们还提供了馈入这些占位符的任何数据都必须采用的形状。 我们使用`None`表示该特定尺寸的大小可以为任何值。 这样,我们就可以批量输入大小不同的数据。 接下来,我们将看到如何在 TensorFlow 中为我们的问题定义占位符。
```py ```py
x = tf.placeholder(tf.float32, shape=[None, 4], name="data_in") x = tf.placeholder(tf.float32, shape=[None, 4], name="data_in")
y = tf.placeholder(tf.int32, shape=[None, 3], name="target_labels") y = tf.placeholder(tf.int32, shape=[None, 3], name="target_labels")
``` ```
现在,我们在图形中创建了占位符,因此我们也可以在图形上构造线性模型。 我们调用之前定义的函数,并提供数据占位符`x`作为输入。 请记住,占位符的行为类似于张量,因此它们也可以像它们一样被传递。 在以下代码中,我们使用占位符作为输入参数来调用`linear_model`函数。 现在,我们在图中创建了占位符,因此我们也可以在图上构造线性模型。 我们调用之前定义的函数,并提供数据占位符`x`作为输入。 请记住,占位符的行为类似于张量,因此它们也可以像它们一样被传递。 在以下代码中,我们使用占位符作为输入参数来调用`linear_model`函数。
```py ```py
model_out = linear_model(x) model_out = linear_model(x)
...@@ -300,7 +300,7 @@ model_out = linear_model(x) ...@@ -300,7 +300,7 @@ model_out = linear_model(x)
# 初始化变量 # 初始化变量
在我们能够在图形中使用变量之前,我们必须对其进行初始化。 我们需要创建一个图形节点来为我们做到这一点。 使用`tf.global_variables_initializer`将向我们的图形添加一个初始化器节点。 如果我们在会话中运行该节点,那么图形中的所有变量都将被初始化,以便我们可以使用它们。 因此,现在,让我们创建一个初始化器节点,如下所示: 在我们能够在图中使用变量之前,我们必须对其进行初始化。 我们需要创建一个图节点来为我们做到这一点。 使用`tf.global_variables_initializer`将向我们的图添加一个初始化器节点。 如果我们在会话中运行该节点,那么图中的所有变量都将被初始化,以便我们可以使用它们。 因此,现在,让我们创建一个初始化器节点,如下所示:
```py ```py
initializer = tf.global_variables_initializer() initializer = tf.global_variables_initializer()
...@@ -360,15 +360,15 @@ loss = tf.reduce_mean(tf.losses.hinge_loss(logits=model_out, labels=y)) ...@@ -360,15 +360,15 @@ loss = tf.reduce_mean(tf.losses.hinge_loss(logits=model_out, labels=y))
![](img/638e8ecb-3cc1-4a6d-a972-2f29b4a517b4.jpg) ![](img/638e8ecb-3cc1-4a6d-a972-2f29b4a517b4.jpg)
上图显示了椭圆抛物面的水平曲线。 这是一个碗形的表面,碗的底部位于中心。 从图中可以看出,在点`a`(黑色直箭头)处的梯度矢量垂直于通过`a`的水平曲线。 实际上,梯度矢量指向损失函数最大增加率的方向。 上图显示了椭圆抛物面的水平曲线。 这是一个碗形的表面,碗的底部位于中心。 从图中可以看出,在点`a`(黑色直箭头)处的梯度向量垂直于通过`a`的水平曲线。 实际上,梯度向量指向损失函数最大增加率的方向。
因此,如果我们从*点*开始并朝与相反的*方向*将权重更新为梯度量,那么我们将下降至`b`点,然后在下一次迭代到`c`,依此类推,直到达到最小值。 选择使损失函数最小的参数来表示最终的训练线性模型。 因此,如果我们从*点*开始并朝与相反的*方向*将权重更新为梯度量,那么我们将下降至`b`点,然后在下一次迭代到`c`,依此类推,直到达到最小值。 选择使损失函数最小的参数来表示最终的训练线性模型。
TensorFlow 的好处在于,它使用其内置的优化器(称为**自动微分**)为我们计算了所有所需的梯度。 我们要做的就是选择一个梯度下降优化器,并告诉它最小化我们的损失函数。 TensorFlow 将自动计算所有梯度,然后使用这些梯度为我们更新权重。 TensorFlow 的好处在于,它使用其内置的优化器(称为**自动微分**)为我们计算了所有所需的梯度。 我们要做的就是选择一个梯度下降优化器,并告诉它最小化我们的损失函数。 TensorFlow 将自动计算所有梯度,然后使用这些梯度为我们更新权重。
我们可以在`tf.train`模块中找到优化程序类。 现在,我们将使用`GradientDescentOptimizer`类,它只是基本的梯度下降优化算法。 创建优化器时,我们必须提供学习率。 学习速率的值是`hyperparameter`,用户必须通过反复试验和实验来对其进行调整。 0.5 的值应该可以很好地解决此问题。 我们可以在`tf.train`模块中找到优化程序类。 现在,我们将使用`GradientDescentOptimizer`类,它只是基本的梯度下降优化算法。 创建优化器时,我们必须提供学习率。 学习速率的值是`hyperparameter`,用户必须通过反复试验和实验来对其进行调整。 0.5 的值应该可以很好地解决此问题。
优化器节点具有一种称为`minimize`的方法。 在您提供的损失函数上调用此方法会做两件事。 首先,针对您的整个图计算与该损失有关的梯度。 其次,这些梯度用于更新所有相关变量。 优化器节点具有一种称为`minimize`的方法。 在您提供的损失函数上调用此方法会做两件事。 首先,针对您的整个图计算与该损失有关的梯度。 其次,这些梯度用于更新所有相关变量。
创建我们的优化器节点将如下所示: 创建我们的优化器节点将如下所示:
...@@ -380,7 +380,7 @@ optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.5).minimize(loss) ...@@ -380,7 +380,7 @@ optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.5).minimize(loss)
# 评估训练好的模型 # 评估训练好的模型
我们汇总了训练模型所需的所有零件。 开始训练之前的最后一件事是,我们想在图中创建一些节点,这些节点将使我们能够在完成训练后测试模型的执行情况。 我们汇总了训练模型所需的所有零件。 开始训练之前的最后一件事是,我们想在图中创建一些节点,这些节点将使我们能够在完成训练后测试模型的执行情况。
我们将创建一个节点来计算模型的准确性。 我们将创建一个节点来计算模型的准确性。
...@@ -398,7 +398,7 @@ accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) ...@@ -398,7 +398,7 @@ accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 会话 # 会话
现在,我们已经构造了计算图的所有部分。 我们需要做的最后一件事是创建一个`tf.Session`并运行我们的图。 TensorFlow 会话是一种将用 Python 编写的 TensorFlow 程序与为 TensorFlow 供电的 C++ 运行时连接的一种方式。 该会话还使 TensorFlow 可以访问本地或远程计算机上存在的设备,例如 CPU 和 GPU。 另外,该会话将缓存有关构造图的信息,因此可以有效地多次运行计算。 现在,我们已经构造了计算图的所有部分。 我们需要做的最后一件事是创建一个`tf.Session`并运行我们的图。 TensorFlow 会话是一种将用 Python 编写的 TensorFlow 程序与为 TensorFlow 供电的 C++ 运行时连接的一种方式。 该会话还使 TensorFlow 可以访问本地或远程计算机上存在的设备,例如 CPU 和 GPU。 另外,该会话将缓存有关构造图的信息,因此可以有效地多次运行计算。
创建会话的标准方法是使用 Python 上下文管理器:`with`语句块: 创建会话的标准方法是使用 Python 上下文管理器:`with`语句块:
...@@ -408,15 +408,15 @@ with tf.Session() as sess:. ...@@ -408,15 +408,15 @@ with tf.Session() as sess:.
原因是创建会话时,它可以控制计算机上的 CPU,内存和 GPU 资源。 使用完会话后,您希望再次释放所有这些资源,最简单的方法是使用`with`语句来确保这一点。 原因是创建会话时,它可以控制计算机上的 CPU,内存和 GPU 资源。 使用完会话后,您希望再次释放所有这些资源,最简单的方法是使用`with`语句来确保这一点。
创建会话后,我们要做的第一件事是运行初始化程序操作。 通过在要评估的图形对象上调用`tf.Session.run`,可以使用会话来求值图中的节点和张量。 当您将图形的一部分提供给`session.run`时,TensorFlow 将在整个图形中进行工作,求值所提供的图部分所依赖的所有内容以产生结果。 创建会话后,我们要做的第一件事是运行初始化器操作。 通过在要评估的图对象上调用`tf.Session.run`,可以使用会话来求值图中的节点和张量。 当您将图的一部分提供给`session.run`时,TensorFlow 将在整个图中进行工作,求值所提供的图部分所依赖的所有内容以产生结果。
因此,在我们的示例中,调用`sess.run(initializer)`将在图形中进行搜索,查找执行初始化程序所需的所有内容,然后按顺序执行这些节点。 在这种情况下,初始化程序节点没有任何连接,因此它将简单地执行一个初始化所有变量的节点。 因此,在我们的示例中,调用`sess.run(initializer)`将在图中进行搜索,查找执行初始化器所需的所有内容,然后按顺序执行这些节点。 在这种情况下,初始化器节点没有任何连接,因此它将简单地执行一个初始化所有变量的节点。
现在我们的变量已初始化,我们开始训练循环。 我们将训练 1000 个步骤或迭代,因此我们将在其中创建训练步骤的`for`循环。 训练的步骤数是`hyperparameter`。 这是我们在训练模型时需要决定的事情。 您可以选择与您选择的值进行权衡,这将在以后的章节中进行讨论。 对于此问题,1000 个步骤将足以获得所需的结果。 现在我们的变量已初始化,我们开始训练循环。 我们将训练 1000 个步骤或迭代,因此我们将在其中创建训练步骤的`for`循环。 训练的步骤数是`hyperparameter`。 这是我们在训练模型时需要决定的事情。 您可以选择与您选择的值进行权衡,这将在以后的章节中进行讨论。 对于此问题,1000 个步骤将足以获得所需的结果。
我们获取了一批训练数据和标签,并将它们输入到图中。 接下来,我们再次调用`session.run`。 这次,我们将其称为损失和优化器两件事。 通过将它们放在我们提供给`session.run`的列表中,我们可以提供想要求值的事物。 TensorFlow 将足够聪明,如果不需要,它不会多次求值图,并且它将重用已经计算出的结果。 我们提供的这个列表称为我们的提取; 它是我们要求值和获取的图中的节点。 我们获取了一批训练数据和标签,并将它们输入到图中。 接下来,我们再次调用`session.run`。 这次,我们将其称为损失和优化器两件事。 通过将它们放在我们提供给`session.run`的列表中,我们可以提供想要求值的事物。 TensorFlow 将足够聪明,如果不需要,它不会多次求值图,并且它将重用已经计算出的结果。 我们提供的这个列表称为我们的提取; 它是我们要求值和获取的图中的节点。
在提取列表之后,我们提供了`feed_dict`或馈送字典。 这是一个字典,其中的每个键都是图中的张量,我们将向该张量输入值(在本例中为占位符),而对应的值就是将被输入到它的值。 在提取列表之后,我们提供了`feed_dict`或馈送字典。 这是一个字典,其中的每个键都是图中的张量,我们将向该张量输入值(在本例中为占位符),而对应的值就是将被输入到它的值。
`session.run`的返回值对应于我们的提取列表中的每个值。 我们的第一个获取是图中的损失张量,因此第一个`return`参数来自此。 第二个获取是优化器节点。 我们不在乎从该节点返回什么,因为我们只在乎优化器节点的计算结果,因此我们将其对应的返回值留空: `session.run`的返回值对应于我们的提取列表中的每个值。 我们的第一个获取是图中的损失张量,因此第一个`return`参数来自此。 第二个获取是优化器节点。 我们不在乎从该节点返回什么,因为我们只在乎优化器节点的计算结果,因此我们将其对应的返回值留空:
......
...@@ -234,7 +234,7 @@ TensorFlow 的所有不同优化器都可以在`tf.train`类中找到。 例如 ...@@ -234,7 +234,7 @@ TensorFlow 的所有不同优化器都可以在`tf.train`类中找到。 例如
![](img/9b567add-923e-49f9-a994-3c69e7f8372c.png) ![](img/9b567add-923e-49f9-a994-3c69e7f8372c.png)
学习率的另一个重要方面是,随着训练的进行和错误的减少,您在训练开始时选择的学习率值可能会变得太大,因此您可能会开始超出最小值。 学习率的另一个重要方面是,随着训练的进行和误差的减少,您在训练开始时选择的学习率值可能会变得太大,因此您可能会开始超出最小值。
要解决此问题,您可以安排学习速率衰减,以免在训练时降低学习速率。 这个过程称为**学习率调度**,我们将在下一章中详细讨论几种流行的方法。 要解决此问题,您可以安排学习速率衰减,以免在训练时降低学习速率。 这个过程称为**学习率调度**,我们将在下一章中详细讨论几种流行的方法。
...@@ -265,7 +265,7 @@ TensorFlow 的所有不同优化器都可以在`tf.train`类中找到。 例如 ...@@ -265,7 +265,7 @@ TensorFlow 的所有不同优化器都可以在`tf.train`类中找到。 例如
在后面的章节中,我们将看到如何检测,避免和补救这些问题,但是仅出于介绍的目的,这些是解决这些问题的一些经典方法: 在后面的章节中,我们将看到如何检测,避免和补救这些问题,但是仅出于介绍的目的,这些是解决这些问题的一些经典方法:
* 获取更多数据 * 获取更多数据
* 当检测到测试数据的错误开始增长时停止(提前停止) * 当检测到测试数据的误差开始增长时停止(提前停止)
* 尽可能简单地开始模型设计,并且仅在检测到欠拟合时才增加复杂性 * 尽可能简单地开始模型设计,并且仅在检测到欠拟合时才增加复杂性
# 特征缩放 # 特征缩放
...@@ -296,7 +296,7 @@ dense_layer = tf.layers.dense(inputs=some_input_layer, units=1024, activation=tf ...@@ -296,7 +296,7 @@ dense_layer = tf.layers.dense(inputs=some_input_layer, units=1024, activation=tf
在这里,我们定义了一个具有 1,024 个输出的完全连接层,随后将激活 ReLU。 在这里,我们定义了一个具有 1,024 个输出的完全连接层,随后将激活 ReLU。
重要的是要注意,该层的输入必须仅具有二维,因此,如果您的输入是空间张量,例如形状为`[28 * 28 * 3]`的图像,则必须在输入之前将其重整为量: 重要的是要注意,该层的输入必须仅具有二维,因此,如果您的输入是空间张量,例如形状为`[28 * 28 * 3]`的图像,则必须在输入之前将其重整为量:
```py ```py
reshaped_input_to_dense_layer = tf.reshape(spatial_tensor_in, [-1, 28 * 28 * 3]) reshaped_input_to_dense_layer = tf.reshape(spatial_tensor_in, [-1, 28 * 28 * 3])
...@@ -304,7 +304,7 @@ reshaped_input_to_dense_layer = tf.reshape(spatial_tensor_in, [-1, 28 * 28 * 3]) ...@@ -304,7 +304,7 @@ reshaped_input_to_dense_layer = tf.reshape(spatial_tensor_in, [-1, 28 * 28 * 3])
# 针对 XOR 问题的 TensorFlow 示例 # 针对 XOR 问题的 TensorFlow 示例
在这里,我们将到目前为止已经了解的一些知识放在一起,并将使用 TensorFlow 解决布尔 XOR 问题。 在此示例中,我们将创建一个具有 S激活函数的三层神经网络。 我们使用对数丢失,因为网络 0 或 1 仅有两种可能的结果: 在这里,我们将到目前为止已经了解的一些知识放在一起,并将使用 TensorFlow 解决布尔 XOR 问题。 在此示例中,我们将创建一个具有 Sigmoid 激活函数的三层神经网络。 我们使用对数丢失,因为网络 0 或 1 仅有两种可能的结果:
```py ```py
import tensorflow as tf import tensorflow as tf
...@@ -357,7 +357,7 @@ for step in range(10000): ...@@ -357,7 +357,7 @@ for step in range(10000):
![](img/75567914-f4cf-459e-9955-ab4c20c97299.png) ![](img/75567914-f4cf-459e-9955-ab4c20c97299.png)
为了能够查看图,可以在脚本提示符下的命令提示符下运行以下命令。 这将为我们启动 tensorboard。 我们将在本章的后面找到关于 tensorboard 的更多信息。 为了能够查看图,可以在脚本提示符下的命令提示符下运行以下命令。 这将为我们启动 tensorboard。 我们将在本章的后面找到关于 tensorboard 的更多信息。
```py ```py
$ tensorboard --logdir=./logs/xor_logs $ tensorboard --logdir=./logs/xor_logs
...@@ -387,7 +387,7 @@ CNN 主要由称为**卷积层**的层组成,这些层对其层输入进行过 ...@@ -387,7 +387,7 @@ CNN 主要由称为**卷积层**的层组成,这些层对其层输入进行过
在 CNN 中,卷积层使用称为**内核**的小窗口,以类似于瓦片的方式过滤输入张量。 内核精确定义了卷积运算将要过滤的内容,并且在找到所需内容时会产生强烈的响应。 在 CNN 中,卷积层使用称为**内核**的小窗口,以类似于瓦片的方式过滤输入张量。 内核精确定义了卷积运算将要过滤的内容,并且在找到所需内容时会产生强烈的响应。
下图显示了将图像与称为 Sobel 过滤器的特定内核进行卷积的结果,该内核非常适合在图像中查找边 下图显示了将图像与称为 Sobel 过滤器的特定内核进行卷积的结果,该内核非常适合在图像中查找边:
![](img/263bc17f-fc97-42c2-a99a-8bc089aa9453.png) ![](img/263bc17f-fc97-42c2-a99a-8bc089aa9453.png)
...@@ -411,7 +411,7 @@ CNN 主要由称为**卷积层**的层组成,这些层对其层输入进行过 ...@@ -411,7 +411,7 @@ CNN 主要由称为**卷积层**的层组成,这些层对其层输入进行过
* **内核大小(K)**:滑动窗口的像素大小。 小通常更好,通常使用奇数,例如 1、3、5,有时很少使用 7。 * **内核大小(K)**:滑动窗口的像素大小。 小通常更好,通常使用奇数,例如 1、3、5,有时很少使用 7。
* **跨度(S)**:内核窗口在卷积的每个步骤中将滑动多少像素。 通常将其设置为 1,因此图像中不会丢失任何位置,但是如果我们想同时减小输入大小,则可以增加位置。 * **跨度(S)**:内核窗口在卷积的每个步骤中将滑动多少像素。 通常将其设置为 1,因此图像中不会丢失任何位置,但是如果我们想同时减小输入大小,则可以增加位置。
* **零填充(P)**:要放在图像边框上的零数量。 使用填充使内核可以完全过滤输入图像的每个位置,包括边 * **零填充(P)**:要放在图像边框上的零数量。 使用填充使内核可以完全过滤输入图像的每个位置,包括边。
* **过滤器数(F)**:我们的卷积层将具有多少个过滤器。 它控制卷积层将要查找的图案或特征的数量。 * **过滤器数(F)**:我们的卷积层将具有多少个过滤器。 它控制卷积层将要查找的图案或特征的数量。
在 TensorFlow 中,我们将在`tf.layers`模块中找到 2D 卷积层,可以将其添加到模型中,如下所示: 在 TensorFlow 中,我们将在`tf.layers`模块中找到 2D 卷积层,可以将其添加到模型中,如下所示:
...@@ -734,7 +734,7 @@ with tf.Session() as sess: ...@@ -734,7 +734,7 @@ with tf.Session() as sess:
"""Training loop""" """Training loop"""
``` ```
我们需要创建一个`tf.summar.FileWriter`,它负责创建一个目录,该目录将存储我们的摘要日志。 如果在创建`FileWriter`时传递图形,则该图形也将显示在 TensorBoard 中。 通过传入`sess.graph`,我们提供了会话正在使用的默认图。 在 TensorBoard 中显示图形的结果可能看起来像这样: 我们需要创建一个`tf.summar.FileWriter`,它负责创建一个目录,该目录将存储我们的摘要日志。 如果在创建`FileWriter`时传递图,则该图也将显示在 TensorBoard 中。 通过传入`sess.graph`,我们提供了会话正在使用的默认图。 在 TensorBoard 中显示图的结果可能看起来像这样:
![](img/a500985f-bedd-457d-82bc-f0dec7d4ebb3.png) ![](img/a500985f-bedd-457d-82bc-f0dec7d4ebb3.png)
......
# 三、TensorFlow 中的图像分类 # 三、TensorFlow 中的图像分类
图像分类是指根据图像内容将图像分类的问题。 让我们从分类的示例任务开始,其中图片可能是狗的图像,也可能不是。 某人可能要完成此任务的一种简单方法是,像在第 1 章中所做的那样,获取输入图像,将其重塑为量,然后训练线性分类器(或其他某种分类器)。 但是,您很快就会发现此主意不好,原因有几个。 除了不能很好地缩放到输入图像的大小之外,线性分类器将很难将一个图像与另一个图像分开。 图像分类是指根据图像内容将图像分类的问题。 让我们从分类的示例任务开始,其中图片可能是狗的图像,也可能不是。 某人可能要完成此任务的一种简单方法是,像在第 1 章中所做的那样,获取输入图像,将其重塑为量,然后训练线性分类器(或其他某种分类器)。 但是,您很快就会发现此主意不好,原因有几个。 除了不能很好地缩放到输入图像的大小之外,线性分类器将很难将一个图像与另一个图像分开。
与可以在图像中看到有意义的图案和内容的人类相反,计算机只能看到从 0 到 255 的数字数组。对于同一类的不同图像,这些数字在相同位置的广泛波动导致无法直接将它们使用为分类器的输入。 从**加拿大高级研究学院****CIFAR**)数据集中获取的这 10 张示例狗图像完美地说明了此问题。 狗的外观不仅有所不同,而且它们在镜头前的姿势和位置也有所不同。 对于机器来说,每个图像一目了然,完全没有共同点,而我们人类却可以清楚地看到它们都是狗: 与可以在图像中看到有意义的图案和内容的人类相反,计算机只能看到从 0 到 255 的数字数组。对于同一类的不同图像,这些数字在相同位置的广泛波动导致无法直接将它们使用为分类器的输入。 从**加拿大高级研究学院****CIFAR**)数据集中获取的这 10 张示例狗图像完美地说明了此问题。 狗的外观不仅有所不同,而且它们在镜头前的姿势和位置也有所不同。 对于机器来说,每个图像一目了然,完全没有共同点,而我们人类却可以清楚地看到它们都是狗:
...@@ -116,7 +116,7 @@ loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=model_logit ...@@ -116,7 +116,7 @@ loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=model_logit
Pascal **视觉对象类****VOC**)挑战成立于 2005 年。每年组织到 2012 年,它为*图像分类,对象检测,分割和操作分类*提供了广泛的自然图像的著名基准数据集。 它是一个多样化的数据集,包含来自各种大小,姿势,方向,照明和遮挡的 flickr 的图像。 从 2005 年(仅四个类别:自行车,汽车,摩托车和人,训练/验证/测试:包含 5 个图像的 2578 个注释对象的 1578 张图像)到 2012 年(二十个类别,训练/验证数据具有 11,530 张图片,包含 27,450 个 ROI 注释对象和 6,929 个分割)。 Pascal **视觉对象类****VOC**)挑战成立于 2005 年。每年组织到 2012 年,它为*图像分类,对象检测,分割和操作分类*提供了广泛的自然图像的著名基准数据集。 它是一个多样化的数据集,包含来自各种大小,姿势,方向,照明和遮挡的 flickr 的图像。 从 2005 年(仅四个类别:自行车,汽车,摩托车和人,训练/验证/测试:包含 5 个图像的 2578 个注释对象的 1578 张图像)到 2012 年(二十个类别,训练/验证数据具有 11,530 张图片,包含 27,450 个 ROI 注释对象和 6,929 个分割)。
重大变化来自 PASCAL(VOC)2007 挑战赛,当时班级的数量从 4 个增加到 20 个,并且此后一直固定。 分类任务的评估指标已更改为平均精度。 仅在 VOC 2007 挑战赛之前提供测试数据的注释。 重大变化来自 PASCAL(VOC)2007 挑战赛,当时的数量从 4 个增加到 20 个,并且此后一直固定。 分类任务的评估指标已更改为平均精度。 仅在 VOC 2007 挑战赛之前提供测试数据的注释。
随着更复杂的分类方法的出现,前面的数据集是不够的,以下几节中介绍的 ImageNet 数据集和 CIFAR 数据集成为分类测试的新标准。 随着更复杂的分类方法的出现,前面的数据集是不够的,以下几节中介绍的 ImageNet 数据集和 CIFAR 数据集成为分类测试的新标准。
...@@ -136,11 +136,11 @@ ImageNet 数据集由 Alex Berg(哥伦比亚大学),Jia Deng(普林斯 ...@@ -136,11 +136,11 @@ ImageNet 数据集由 Alex Berg(哥伦比亚大学),Jia Deng(普林斯
![](img/6436a6ce-fd4b-463c-b195-3c34040e17df.png) ![](img/6436a6ce-fd4b-463c-b195-3c34040e17df.png)
那么,算法的最终错误就是测试图像上出错的比例,如下所示: 那么,算法的最终误差就是测试图像上出错的比例,如下所示:
![](img/bd6af112-f4ec-4983-96cf-5b293eeb3e44.png) ![](img/bd6af112-f4ec-4983-96cf-5b293eeb3e44.png)
Imagenet 是近年来深度学习蓬勃发展的主要原因之一。 在深度学习开始流行之前,ILSVRC 的前五位错误率大约为 28%,并且丝毫没有下降太多。 但是,在 2012 年,挑战赛的冠军 SuperVision 将前 5 名的分类错误降低到了 16.4%。 团队模型(现在称为 AlexNet)是一个深度卷积神经网络。 这项巨大的胜利唤醒了人们使用 CNN 的力量,它成为许多现代 CNN 架构的垫脚石。 Imagenet 是近年来深度学习蓬勃发展的主要原因之一。 在深度学习开始流行之前,ILSVRC 的前五位错误率大约为 28%,并且丝毫没有下降太多。 但是,在 2012 年,挑战赛的冠军 SuperVision 将前 5 名的分类错误降低到了 16.4%。 团队模型(现在称为 AlexNet)是一个深度卷积神经网络。 这项巨大的胜利唤醒了人们使用 CNN 的力量,它成为许多现代 CNN 架构的垫脚石。
在接下来的几年中,CNN 模型继续占主导地位,前 5 个错误率持续下降。 2014 年冠军 GoogLeNet 将错误率降低到 6.7%,而 ResNet 在 2015 年将错误率再次降低了一半,降至 3.57%。 此后,2017 年的赢家“WMW 挤压和激励网络”产生了 2.25% 的误差,出现了较小的改进。 在接下来的几年中,CNN 模型继续占主导地位,前 5 个错误率持续下降。 2014 年冠军 GoogLeNet 将错误率降低到 6.7%,而 ResNet 在 2015 年将错误率再次降低了一半,降至 3.57%。 此后,2017 年的赢家“WMW 挤压和激励网络”产生了 2.25% 的误差,出现了较小的改进。
...@@ -266,7 +266,7 @@ if __name__ == '__main__': ...@@ -266,7 +266,7 @@ if __name__ == '__main__':
# 建立 CNN 图 # 建立 CNN 图
让我们通过`build_graph`函数进行详细介绍,该函数包含网络定义,损失函数和所使用的优化器。 首先,我们通过为输入定义占位符来启动函数。 我们将使用两个占位符在图表中提供数据和标签:`__x_``__y_`。 占位符`__x_`将保存我们输入的 RGB 图像,而占位符`__y_` 存储一个对应类别的热门标签。 在定义占位符形状的`N`部分时,我们使用`None`,因为这告诉 TensorFlow 该值可以是任何值,并且在执行图形时将提供该值: 让我们通过`build_graph`函数进行详细介绍,该函数包含网络定义,损失函数和所使用的优化器。 首先,我们通过为输入定义占位符来启动函数。 我们将使用两个占位符在图中提供数据和标签:`__x_``__y_`。 占位符`__x_`将保存我们输入的 RGB 图像,而占位符`__y_` 存储一个对应类别的热门标签。 在定义占位符形状的`N`部分时,我们使用`None`,因为这告诉 TensorFlow 该值可以是任何值,并且在执行图时将提供该值:
```py ```py
def build_graph(self): def build_graph(self):
...@@ -389,7 +389,7 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps) ...@@ -389,7 +389,7 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
self.__saver = tf.train.Saver(max_to_keep=None) self.__saver = tf.train.Saver(max_to_keep=None)
``` ```
另外,我们可以指定`tf.GPUOptions`要使用的 GPU 内存比例。 对象会话封装了执行操作和求值张量的环境。 创建`FileWriter`对象以将摘要和事件存储到文件后,`__session.run(init)`方法运行 TensorFlow 计算的一个步骤,方法是运行必要的图形片段以执行每个操作,并评估在`init`中初始化的每个张量作为图的一部分: 另外,我们可以指定`tf.GPUOptions`要使用的 GPU 内存比例。 对象会话封装了执行操作和求值张量的环境。 创建`FileWriter`对象以将摘要和事件存储到文件后,`__session.run(init)`方法运行 TensorFlow 计算的一个步骤,方法运行必要的图片段来执行每个操作,并评估在`init`中初始化的每个张量作为图的一部分:
```py ```py
# Avoid allocating the whole memory # Avoid allocating the whole memory
...@@ -469,7 +469,7 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps) ...@@ -469,7 +469,7 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
# 主要训练循环 # 主要训练循环
一旦检索到数据并构建了图,就可以开始我们的主要训练循环,该循环将继续进行 20,000 多次迭代。 在每次迭代中,都使用 CPU 设备获取一批训练数据,并调用`AdamOptimizer`对象的`__train_step.run`方法向前运行一次,向后运行一次。 每进行 100 次迭代,我们就会对当前的训练和测试批次进行一次前向传递,以收集训练和验证损失以及其他汇总数据。 然后,`FileWriter`对象的`add_summary`方法将提供的 TensorFlow 摘要:`summary_1``summary_2`包装在事件协议缓冲区中,并将其添加到事件文件中: 一旦检索到数据并构建了图,就可以开始我们的主要训练循环,该循环将继续进行 20,000 多次迭代。 在每次迭代中,都使用 CPU 设备获取一批训练数据,并调用`AdamOptimizer`对象的`__train_step.run`方法向前运行一次,向后运行一次。 每进行 100 次迭代,我们就会对当前的训练和测试批次进行一次前向传递,以收集训练和验证损失以及其他汇总数据。 然后,`FileWriter`对象的`add_summary`方法将提供的 TensorFlow 摘要:`summary_1``summary_2`包装在事件协议缓冲区中,并将其添加到事件文件中:
```py ```py
# Train Loop # Train Loop
...@@ -546,9 +546,9 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps) ...@@ -546,9 +546,9 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
# Xavier-Bengio 和初始化器 # Xavier-Bengio 和初始化器
在了解*训练深度前馈神经网络*的难度时,Xavier Glorot 和 Yoshua Bengio 表明,如果从均匀分布`U ~ [-1/√n, 1/√n]`初始化每一层的权重,其中`n`是上一层中的大小,对于乙状结肠激活函数,顶层(更靠近输出)的神经元迅速饱和为 0。我们知道,由于乙状结肠函数的形式,激活值 0 表示权重非常大,并且反向传播的梯度接近零。 较小的梯度值会减慢学习过程,因为早期层中的权重停止更新或停止学习。 在了解*训练深度前馈神经网络*的难度时,Xavier Glorot 和 Yoshua Bengio 表明,如果从均匀分布`U ~ [-1/√n, 1/√n]`初始化每一层的权重,其中`n`是上一层中的大小,对于 Sigmoid 激活函数,顶层(更靠近输出)的神经元迅速饱和为 0。我们知道,由于 Sigmoid 函数的形式,激活值 0 表示权重非常大,并且反向传播的梯度接近零。 较小的梯度值会减慢学习过程,因为早期层中的权重停止更新或停止学习。
因此,我们想要的是使权重在最初确定的时间间隔内均匀分布,也就是说,权重的方差应该在我们从底层移动到顶层时保持不变。 这将使错误平稳地流到顶层,从而使网络在训练期间收敛更快。 因此,我们想要的是使权重在最初确定的时间间隔内均匀分布,也就是说,权重的方差应该在我们从底层移动到顶层时保持不变。 这将使误差平稳地流到顶层,从而使网络在训练期间收敛更快。
为了实现这一点,Glorot 和 Bengio 证明了对于单位导数为 0 的对称激活函数`f`,每一层的权重方差必须如下: 为了实现这一点,Glorot 和 Bengio 证明了对于单位导数为 0 的对称激活函数`f`,每一层的权重方差必须如下:
...@@ -564,7 +564,7 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps) ...@@ -564,7 +564,7 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
因此,作者使用零均值高斯分布初始化其权重,其标准差(STD)为`√(2/n[in])`。 然后将该初始化称为 He 初始化。 因此,作者使用零均值高斯分布初始化其权重,其标准差(STD)为`√(2/n[in])`。 然后将该初始化称为 He 初始化。
默认情况下,TensorFlow 的大部分`tf.layers`都使用 Glorot(xavier)初始化程序,但是我们可以覆盖它并指定我们自己的初始化。 在这里,我们展示了一个如何覆盖`conv2d`层的默认初始化器的示例: 默认情况下,TensorFlow 的大部分`tf.layers`都使用 Glorot(xavier)初始化,但是我们可以覆盖它并指定我们自己的初始化。 在这里,我们展示了一个如何覆盖`conv2d`层的默认初始化器的示例:
```py ```py
conv1 = tf.layers.conv2d(inputs=self.__x_, filters=64, kernel_size=[5, 5], conv1 = tf.layers.conv2d(inputs=self.__x_, filters=64, kernel_size=[5, 5],
...@@ -590,7 +590,7 @@ bias_initializer=tf.zeros_initializer()) ...@@ -590,7 +590,7 @@ bias_initializer=tf.zeros_initializer())
创建更强大模型的第一种方法是使用 L1 或 L2 正则化。 到目前为止,这些是最常见的正则化方法。 基本思想是在训练模型时,我们积极尝试使用这些权重的 L1 或 L2 范数对模型权重的值施加一些约束。 创建更强大模型的第一种方法是使用 L1 或 L2 正则化。 到目前为止,这些是最常见的正则化方法。 基本思想是在训练模型时,我们积极尝试使用这些权重的 L1 或 L2 范数对模型权重的值施加一些约束。
为此,我们在使用的任何损失函数中增加了一个额外的项。 对于 L1 正则化,我们添加的术语是`λ |w|`,对于 L2 正则化,我们添加的术语是`0.5 λ w^2`。 在前面的术语中,`w`是我们网络中的所有权重,`λ`是称为**正则化强度**的超参数。 通过添加该术语,我们可以防止权重值变得太大。 为此,我们在使用的任何损失函数中增加了一个额外的项。 对于 L1 正则化,我们添加的项是`λ |w|`,对于 L2 正则化,我们添加的项是`0.5 λ w^2`。 在前面的项中,`w`是我们网络中的所有权重,`λ`是称为**正则化强度**的超参数。 通过添加该项,我们可以防止权重值变得太大。
因此,为了 L1 正则化,第 1 章,“TensorFlow 简介和设置”的 SVM 损失函数,即: 因此,为了 L1 正则化,第 1 章,“TensorFlow 简介和设置”的 SVM 损失函数,即:
...@@ -625,7 +625,7 @@ l2_reg = tf.contrib.layers.l2_regularizer(scale=0.001) ...@@ -625,7 +625,7 @@ l2_reg = tf.contrib.layers.l2_regularizer(scale=0.001)
reg_conv_layer = tf.layers.conv2d( inputs, filters, kernel_size, kernel_regularizer=l2_reg) reg_conv_layer = tf.layers.conv2d( inputs, filters, kernel_size, kernel_regularizer=l2_reg)
``` ```
要添加我们的正则化术语,我们首先需要将它们全部收集起来。 幸运的是,TensorFlow 会自动为我们将所有正则化术语放到一个集合中,以便我们可以轻松访问它们。 TensorFlow 在`tf.GraphKeys`内存储一些与您创建的图相关的重要集合,例如可训练变量,汇总和正则化损失。 我们可以使用`tf.get_collection()`访问这些集合,并提供要获取的集合的名称。 例如,为了得到正则化损失,我们将编写以下内容: 要添加我们的正则化项,我们首先需要将它们全部收集起来。 幸运的是,TensorFlow 会自动为我们将所有正则化项放到一个集合中,以便我们可以轻松访问它们。 TensorFlow 在`tf.GraphKeys`内存储一些与您创建的图相关的重要集合,例如可训练变量,汇总和正则化损失。 我们可以使用`tf.get_collection()`访问这些集合,并提供要获取的集合的名称。 例如,为了得到正则化损失,我们将编写以下内容:
```py ```py
reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES) reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
...@@ -653,7 +653,7 @@ combined_loss = tf.n_add(train_loss, reg_losses) ...@@ -653,7 +653,7 @@ combined_loss = tf.n_add(train_loss, reg_losses)
![](img/cce137ca-2838-44ac-b275-7e83d01a5ddb.png) ![](img/cce137ca-2838-44ac-b275-7e83d01a5ddb.png)
在下图中,我们显示了模型测试错误。 显然,通过辍学,测试集上的错误会减少。 请记住,与所有正则化一样,与不使用正则化相比,使用辍学会使您的训练损失增加,但是到最后,我们只对模型测试错误率降低(泛化)感兴趣: 在下图中,我们显示了模型测试误差。 显然,通过辍学,测试集上的误差会减少。 请记住,与所有正则化一样,与不使用正则化相比,使用辍学会使您的训练损失增加,但是到最后,我们只对模型测试错误率降低(泛化)感兴趣:
![](img/eef8f1a2-e6e1-40aa-9c0e-8ef692a60665.png) ![](img/eef8f1a2-e6e1-40aa-9c0e-8ef692a60665.png)
......
...@@ -354,7 +354,7 @@ def tf_iou_vectorized(self, box_vec_1, box_vec_2): ...@@ -354,7 +354,7 @@ def tf_iou_vectorized(self, box_vec_1, box_vec_2):
``` ```
我们也可以在 TensorFlow 上将其更改为量化形式,如下所示: 我们也可以在 TensorFlow 上将其更改为量化形式,如下所示:
```py ```py
def tf_iou_vectorized(self, box_vec_1, box_vec_2): def tf_iou_vectorized(self, box_vec_1, box_vec_2):
...@@ -456,9 +456,9 @@ Tensorflow 已经具有实现非最大值抑制算法的功能,称为`tf.image ...@@ -456,9 +456,9 @@ Tensorflow 已经具有实现非最大值抑制算法的功能,称为`tf.image
![](img/28ad37ab-56de-4f07-86d2-c4bcd0fc5dfb.png) ![](img/28ad37ab-56de-4f07-86d2-c4bcd0fc5dfb.png)
该损失是分类误差损失平方的总和。 同样,当单元上有一个对象时,术语`1[i]^(obj)`为 1,否则为 0。 我们的想法是,当存在对象时,我们不考虑分类错误。 该损失是分类误差损失平方的总和。 同样,当单元上有一个对象时,`1[i]^(obj)`为 1,否则为 0。 我们的想法是,当存在对象时,我们不考虑分类错误。
`1[i]^(obj), 1[ij]^(obj), 1[ij]^(noobj)`这些术语可以掩盖我们在真实情况上有一个对象而在特定单元的模型输出中有一个对象的情况下的损失。 当真实情况与模型输出不匹配时,也是如此。 `1[i]^(obj), 1[ij]^(obj), 1[ij]^(noobj)`这些可以掩盖我们在真实情况上有一个对象而在特定单元的模型输出中有一个对象的情况下的损失。 当真实情况与模型输出不匹配时,也是如此。
因此,例如,当特定单元格不匹配时,我们的损失将是: 因此,例如,当特定单元格不匹配时,我们的损失将是:
...@@ -468,7 +468,7 @@ Tensorflow 已经具有实现非最大值抑制算法的功能,称为`tf.image ...@@ -468,7 +468,7 @@ Tensorflow 已经具有实现非最大值抑制算法的功能,称为`tf.image
![](img/aea81ee0-d79f-4213-9343-be0e90e8c96e.png) ![](img/aea81ee0-d79f-4213-9343-be0e90e8c96e.png)
在实践中的实践中,您将尝试量化这种损失并避免`for`循环并提高性能,这对于 Tensorflow 之类的库尤其如此。 在实践中的实践中,您将尝试量化这种损失并避免`for`循环并提高性能,这对于 Tensorflow 之类的库尤其如此。
这是 YOLO 损失的 TensorFlow 实现: 这是 YOLO 损失的 TensorFlow 实现:
...@@ -566,7 +566,7 @@ def loss_layer(self, predicts, labels, scope='loss_layer'): ...@@ -566,7 +566,7 @@ def loss_layer(self, predicts, labels, scope='loss_layer'):
这个运算相当不好地称为反卷积,这意味着它是卷积的逆运算,但实际上并非如此。 更恰当的名称是转置卷积或分数步卷积。 这个运算相当不好地称为反卷积,这意味着它是卷积的逆运算,但实际上并非如此。 更恰当的名称是转置卷积或分数步卷积。
此层类型为您提供了一种对输入体积进行升采样的学习方法,并且可以在每次需要将输入要素地图智能地投影到更高的空间时使用。 一些用例包括以下内容: 此层类型为您提供了一种对输入体积进行升采样的学习方法,并且可以在每次需要将输入特征图智能地投影到更高的空间时使用。 一些用例包括以下内容:
* 上采样(条纹转置卷积)`== UNPOOL + CONV` * 上采样(条纹转置卷积)`== UNPOOL + CONV`
* 可视化显着图 * 可视化显着图
......
...@@ -301,7 +301,7 @@ GoogLeNet 的主要优点是,它比 VGG 更为准确,同时使用的参数 ...@@ -301,7 +301,7 @@ GoogLeNet 的主要优点是,它比 VGG 更为准确,同时使用的参数
# 残差网络 # 残差网络
在前面的部分中,已经证明了网络的深度是有助于提高准确性的关键因素(请参见 VGG)。 TensorFlow 中的第 3 章“图像分类”中也显示,可以通过正确的权重初始化和批量归一化来缓解深度网络中梯度消失或爆炸的问题。 但是,这是否意味着我们添加的层越多,我们得到的系统就越准确? 亚洲研究机构 Microsoft 的《用于图像识别的深度残差学习》的作者发现,只要网络深度达到 30 层,准确性就会达到饱和。 为了解决此问题,他们引入了一个称为残差块的新层块,该块将上一层的输出添加到下一层的输出中(请参见下图)。 残差网络或 ResNet 在非常深的网络(甚至超过 100 层!)中都显示了出色的结果,例如 152 层的 ResNet 赢得了 2015 LRVC 图像识别挑战,其前 5 个测试错误为 3.57。 事实证明,诸如 ResNets 之类的更深层网络要比包括 Inception 模块(例如 GoogLeNet)在内的更广泛的网络更好地工作。 在前面的部分中,已经证明了网络的深度是有助于提高准确性的关键因素(请参见 VGG)。 TensorFlow 中的第 3 章“图像分类”中也显示,可以通过正确的权重初始化和批量归一化来缓解深度网络中梯度消失或爆炸的问题。 但是,这是否意味着我们添加的层越多,我们得到的系统就越准确? 亚洲研究机构 Microsoft 的《用于图像识别的深度残差学习》的作者发现,只要网络深度达到 30 层,准确性就会达到饱和。 为了解决此问题,他们引入了一个称为残差块的新层块,该块将上一层的输出添加到下一层的输出中(请参见下图)。 残差网络或 ResNet 在非常深的网络(甚至超过 100 层!)中都显示了出色的结果,例如 152 层的 ResNet 赢得了 2015 LRVC 图像识别挑战,其前 5 个测试误差为 3.57。 事实证明,诸如 ResNets 之类的更深层网络要比包括 Inception 模块(例如 GoogLeNet)在内的更广泛的网络更好地工作。
![](img/d8ad63df-15dd-4d20-b918-a3f18767a9c8.png) ![](img/d8ad63df-15dd-4d20-b918-a3f18767a9c8.png)
...@@ -313,7 +313,7 @@ GoogLeNet 的主要优点是,它比 VGG 更为准确,同时使用的参数 ...@@ -313,7 +313,7 @@ GoogLeNet 的主要优点是,它比 VGG 更为准确,同时使用的参数
其中`g`是非线性激活函数,例如 ReLu 和`F(x) = W[l] g(W[l-1] x)`,即两层堆叠卷积。 ReLu 函数可以在添加`x`之前或之后添加。 剩余的块应由 2 层或更多层组成,因为一层的块没有明显的好处。 其中`g`是非线性激活函数,例如 ReLu 和`F(x) = W[l] g(W[l-1] x)`,即两层堆叠卷积。 ReLu 函数可以在添加`x`之前或之后添加。 剩余的块应由 2 层或更多层组成,因为一层的块没有明显的好处。
为了理解该概念背后的直觉,我们假设我们有一个经过训练的浅层 CNN,其更深的对应层具有与浅层 CNN 相同的层,并且在它们之间随机插入了一些层。 为了拥有一个与浅层模型至少具有相似表现的深层模型,附加层必须近似标识函数。 但是,要学习具有卷积层栈的标识函数比将残差函数推为零要困难得多。 换句话说,如果单位函数是最优解,则很容易实现`F(x)`,因此很容易实现`H(x) = x` 为了理解该概念背后的直觉,我们假设我们有一个经过训练的浅层 CNN,其更深的对应层具有与浅层 CNN 相同的层,并且在它们之间随机插入了一些层。 为了拥有一个与浅层模型至少具有相似表现的深层模型,附加层必须近似标识函数。 但是,要学习具有卷积层栈的标识函数比将残差函数推为零要困难得多。 换句话说,如果单位函数是最优解,则很容易实现`F(x)`,因此很容易实现`H(x) = x`
另一种思考的方式是,在训练期间,特定的层不仅会从上一层学习一个概念,还会从它之前的其他层学习一个概念。 这比只从上一层学习概念要好。 另一种思考的方式是,在训练期间,特定的层不仅会从上一层学习一个概念,还会从它之前的其他层学习一个概念。 这比只从上一层学习概念要好。
......
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
# 卷积自编码器示例 # 卷积自编码器示例
以下 TensorFlow 代码将为 MNIST 数据集构建卷积自编码器模型。 代码的第一部分将构建模型,编码器和解码器的图。 在代码中,我们突出显示模型的一部分,其输出将是我们的潜在向量: 以下 TensorFlow 代码将为 MNIST 数据集构建卷积自编码器模型。 代码的第一部分将构建模型,编码器和解码器的图。 在代码中,我们突出显示模型的一部分,其输出将是我们的潜在向量:
```py ```py
class CAE_CNN(object): class CAE_CNN(object):
...@@ -122,7 +122,7 @@ with tf.name_scope("Solver"): ...@@ -122,7 +122,7 @@ with tf.name_scope("Solver"):
我们第一个可以生成更多类似于训练数据的真实生成模型,将是**变分自编码器****VAE**)。 VAE 看起来像正常的自编码器,但有一个新的约束,它将迫使我们的压缩表示(潜伏空间)遵循零均值和单位方差高斯分布。 我们第一个可以生成更多类似于训练数据的真实生成模型,将是**变分自编码器****VAE**)。 VAE 看起来像正常的自编码器,但有一个新的约束,它将迫使我们的压缩表示(潜伏空间)遵循零均值和单位方差高斯分布。
在潜在空间上施加此约束的想法是,当我们想使用 VAE 生成新数据时,我们可以创建来自单位高斯分布的样本矢量,并将其提供给经过训练的解码器。 VAE 和常规自编码器之间的差异就是对潜在空间矢量的约束。 这个约束条件使我们可以创建一种新的潜在矢量,然后再将其馈送到解码器以生成数据。 在潜在空间上施加此约束的想法是,当我们想使用 VAE 生成新数据时,我们可以创建来自单位高斯分布的样本向量,并将其提供给经过训练的解码器。 VAE 和常规自编码器之间的差异就是对潜在空间向量的约束。 这个约束条件使我们可以创建一种新的潜在向量,然后再将其馈送到解码器以生成数据。
下图显示,VAE 在结构上与自编码器完全相同,除了对隐藏空间的约束之外: 下图显示,VAE 在结构上与自编码器完全相同,除了对隐藏空间的约束之外:
...@@ -160,13 +160,13 @@ KL 散度损失将产生一个数字,该数字指示两个分布彼此之间 ...@@ -160,13 +160,13 @@ KL 散度损失将产生一个数字,该数字指示两个分布彼此之间
# 训练 VAE # 训练 VAE
为了训练 VAE 并使用 KL 散度损失,我们首先需要研究如何生成潜矢量。 我们将使编码器产生两个向量,而不是让编码器直接精确地产生一个潜在向量。 第一个是平均值的向量`μ`,第二个是标准差值的向量`σ`。 根据这些,我们可以创建第三个向量,其中使用`μ``σ`从高斯分布中采样元素向量的第`i`个值作为该高斯分布的均值和标准差。 然后,该第三采样矢量被发送到解码器。 为了训练 VAE 并使用 KL 散度损失,我们首先需要研究如何生成潜向量。 我们将使编码器产生两个向量,而不是让编码器直接精确地产生一个潜在向量。 第一个是平均值的向量`μ`,第二个是标准差值的向量`σ`。 根据这些,我们可以创建第三个向量,其中使用`μ``σ`从高斯分布中采样元素向量的第`i`个值作为该高斯分布的均值和标准差。 然后,该第三采样向量被发送到解码器。
现在,我们的模型如下所示: 现在,我们的模型如下所示:
![](img/944095bc-4bde-4e04-86e3-1ccc3e0b1c04.png) ![](img/944095bc-4bde-4e04-86e3-1ccc3e0b1c04.png)
上图中的均值和标准差块将只是正常的全连接层,它们将通过 KL 损失函数来学习如何返回所需的值。 更改我们如何获得潜量的原因是因为它使我们能够轻松计算 KL 散度损失。 KL 损失现在如下:`latent_mean``μ``latent_stddev``σ` 上图中的均值和标准差块将只是正常的全连接层,它们将通过 KL 损失函数来学习如何返回所需的值。 更改我们如何获得潜量的原因是因为它使我们能够轻松计算 KL 散度损失。 KL 损失现在如下:`latent_mean``μ``latent_stddev``σ`
```py ```py
0.5 * tf.reduce_sum(tf.square(latent_mean) + tf.square(latent_stddev) - tf.log(tf.square(latent_stddev)) - 1, 1) 0.5 * tf.reduce_sum(tf.square(latent_mean) + tf.square(latent_stddev) - tf.log(tf.square(latent_stddev)) - 1, 1)
...@@ -176,13 +176,13 @@ KL 散度损失将产生一个数字,该数字指示两个分布彼此之间 ...@@ -176,13 +176,13 @@ KL 散度损失将产生一个数字,该数字指示两个分布彼此之间
# 重新参数化技巧 # 重新参数化技巧
重新参数化技巧的想法是从反向传播循环中取出随机样本节点。 它是通过从高斯分布中获取样本ε,然后将其乘以我们的标准差`σ`的结果,然后加上`μ`来实现的。 现在,我们的潜在向量的公式是: 重新参数化技巧的想法是从反向传播循环中取出随机样本节点。 它是通过从高斯分布中获取样本ε,然后将其乘以我们的标准差`σ`的结果,然后加上`μ`来实现的。 现在,我们的潜在向量的公式是:
![](img/16915d9b-677d-4f06-b381-1fe5e7353406.jpg) ![](img/16915d9b-677d-4f06-b381-1fe5e7353406.jpg)
![](img/ae58ed7b-25c5-447d-bb71-54d32dc9ed15.jpg) ![](img/ae58ed7b-25c5-447d-bb71-54d32dc9ed15.jpg)
产生的潜量将与以前相同,但是现在进行此更改可以使梯度流回到 VAE 的编码器部分。 下图显示了在进行重新参数化之前的 VAE 模型,在左侧进行了重新参数化之后。 蓝色框是损失函数的两个部分。 查看该图,您可以看到我们的渐变现在可以向后流动,因为我们不再具有红色框(示例节点)来挡路了: 产生的潜量将与以前相同,但是现在进行此更改可以使梯度流回到 VAE 的编码器部分。 下图显示了在进行重新参数化之前的 VAE 模型,在左侧进行了重新参数化之后。 蓝色框是损失函数的两个部分。 查看该图,您可以看到我们的渐变现在可以向后流动,因为我们不再具有红色框(示例节点)来挡路了:
![](img/412bfece-2f71-4956-a5fe-3ae19cce2b6b.png) ![](img/412bfece-2f71-4956-a5fe-3ae19cce2b6b.png)
...@@ -219,7 +219,7 @@ class VAE_CNN(object): ...@@ -219,7 +219,7 @@ class VAE_CNN(object):
filters=32, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) filters=32, kernel_size=[5, 5], padding="same", activation=tf.nn.relu)
``` ```
接下来是 VAE 的一部分,该部分负责使用我们之前的新重新参数化技巧来创建潜在量。 我们添加了对最终潜在向量的记录,以检查它是否按照我们期望的那样遵循单位高斯分布产生向量: 接下来是 VAE 的一部分,该部分负责使用我们之前的新重新参数化技巧来创建潜在量。 我们添加了对最终潜在向量的记录,以检查它是否按照我们期望的那样遵循单位高斯分布产生向量:
```py ```py
with tf.name_scope('LATENT'): with tf.name_scope('LATENT'):
...@@ -287,7 +287,7 @@ with tf.name_scope("Solver"): ...@@ -287,7 +287,7 @@ with tf.name_scope("Solver"):
# 产生新数据 # 产生新数据
训练完 VAE 模型后,我们可以将其解码器部分截断,并用作生成器为我们生成新数据。 它将通过向它提供来自单位高斯分布的新潜在量来工作。 训练完 VAE 模型后,我们可以将其解码器部分截断,并用作生成器为我们生成新数据。 它将通过向它提供来自单位高斯分布的新潜在量来工作。
我们在 TensorFlow 中提供负责构建此生成的 VAE 图的代码,如下所示: 我们在 TensorFlow 中提供负责构建此生成的 VAE 图的代码,如下所示:
...@@ -331,11 +331,11 @@ class VAE_CNN_GEN(object): ...@@ -331,11 +331,11 @@ class VAE_CNN_GEN(object):
![](img/98fcbf21-46e3-45f0-8a2d-a7004af773c3.png) ![](img/98fcbf21-46e3-45f0-8a2d-a7004af773c3.png)
判别器和发生器都将具有自己的损失函数,但是它们的损失都相互依赖。 判别器和生成器都将具有自己的损失函数,但是它们的损失都相互依赖。
让我们总结一下 GAN 模型的两个主要模块或网络: 让我们总结一下 GAN 模型的两个主要模块或网络:
* **生成器**:使用大小为 N 的一维量作为输入,创建类似于*真实图像*数据集的图像(选择 N 取决于我们) * **生成器**:使用大小为 N 的一维量作为输入,创建类似于*真实图像*数据集的图像(选择 N 取决于我们)
* **判别器**:验证提供给它的图像是真实的还是伪造的 * **判别器**:验证提供给它的图像是真实的还是伪造的
GAN 的一些实际用法如下: GAN 的一些实际用法如下:
...@@ -345,7 +345,7 @@ GAN 的一些实际用法如下: ...@@ -345,7 +345,7 @@ GAN 的一些实际用法如下:
* 将判别器用作损失函数(对于图像,可能优于 L1/L2),并且也可以在 VAE 中使用 * 将判别器用作损失函数(对于图像,可能优于 L1/L2),并且也可以在 VAE 中使用
* 通过将生成的数据与标记的数据混合来进行半监督学习 * 通过将生成的数据与标记的数据混合来进行半监督学习
现在我们将向您展示如何在 TensorFlow 中实现非常简单的 GAN。 一旦经过训练,我们的 GAN 的生成器部分就可以用于根据 100 个长随机噪声量创建 MNIST 手写数字。 让我们开始吧! 现在我们将向您展示如何在 TensorFlow 中实现非常简单的 GAN。 一旦经过训练,我们的 GAN 的生成器部分就可以用于根据 100 个长随机噪声量创建 MNIST 手写数字。 让我们开始吧!
# 判别器 # 判别器
...@@ -364,7 +364,7 @@ def discriminator(x): ...@@ -364,7 +364,7 @@ def discriminator(x):
# 生成器 # 生成器
现在我们创建生成器网络。 生成器的工作是将随机噪声的量作为输入,并从中生成输出图像。 在此示例中,我们再次使用全连接层,这些层最后将产生 784 个长向量的输出,我们可以对其进行整形以获得`28x28`的图像: 现在我们创建生成器网络。 生成器的工作是将随机噪声的量作为输入,并从中生成输出图像。 在此示例中,我们再次使用全连接层,这些层最后将产生 784 个长向量的输出,我们可以对其进行整形以获得`28x28`的图像:
```py ```py
def generator(z): def generator(z):
...@@ -385,7 +385,7 @@ def generator(z): ...@@ -385,7 +385,7 @@ def generator(z):
![](img/1d24b043-c4b8-42f0-8f9c-8f2c7db22ec3.png) ![](img/1d24b043-c4b8-42f0-8f9c-8f2c7db22ec3.png)
在这里,`D`是我们的判别器,`G`是我们的生成器,`z`是输入到生成器的随机量,`x`是真实图像。 尽管我们在此处给出了 GAN 损失的总和,但实际上更容易分别考虑这两种优化。 在这里,`D`是我们的判别器,`G`是我们的生成器,`z`是输入到生成器的随机量,`x`是真实图像。 尽管我们在此处给出了 GAN 损失的总和,但实际上更容易分别考虑这两种优化。
为了训练 GAN,我们将在判别器和生成器之间交替进行梯度步骤更新。 在更新判别器时,我们要尝试使**最大化**判别器做出**正确选择**的概率。 在更新生成器时,我们想尝试使**最小化**判别器做出**正确选择**的可能性。 为了训练 GAN,我们将在判别器和生成器之间交替进行梯度步骤更新。 在更新判别器时,我们要尝试使**最大化**判别器做出**正确选择**的概率。 在更新生成器时,我们想尝试使**最小化**判别器做出**正确选择**的可能性。
...@@ -399,11 +399,11 @@ def generator(z): ...@@ -399,11 +399,11 @@ def generator(z):
# 生成器损失 # 生成器损失
生成器想要欺骗判别器,换句话说,使判别器输出`q`用于生成的图像`G(z)`发生器损失只是施加到发生器结果的判别器输出的二项式交叉熵损失的负值。 请注意,由于生成器始终尝试生成“真实”图像,因此交叉熵损失可简化为: 生成器想要欺骗判别器,换句话说,使判别器输出`q`用于生成的图像`G(z)`生成器损失只是施加到生成器结果的判别器输出的二项式交叉熵损失的负值。 请注意,由于生成器始终尝试生成“真实”图像,因此交叉熵损失可简化为:
![](img/1761cebc-4eca-4a0f-81cf-9a128632ee81.png) ![](img/1761cebc-4eca-4a0f-81cf-9a128632ee81.png)
在这里,每个术语的含义如下: 在这里,每的含义如下:
* `m`:批量 * `m`:批量
* `D`:判别器 * `D`:判别器
...@@ -418,7 +418,7 @@ def generator(z): ...@@ -418,7 +418,7 @@ def generator(z):
![](img/6afbb7c8-d706-49dc-b74b-f8d9f894e8d3.png) ![](img/6afbb7c8-d706-49dc-b74b-f8d9f894e8d3.png)
此损失函数有两个术语 此损失函数有两
* 应用于判别器模型的二项式交叉熵产生了一些真实数据`x` * 应用于判别器模型的二项式交叉熵产生了一些真实数据`x`
* 将二项式交叉熵应用于所生成数据`G(z)`的判别器模型结果 * 将二项式交叉熵应用于所生成数据`G(z)`的判别器模型结果
...@@ -452,7 +452,7 @@ def gan_loss(logits_real, logits_fake): ...@@ -452,7 +452,7 @@ def gan_loss(logits_real, logits_fake):
return discriminator_loss , generator_loss return discriminator_loss , generator_loss
``` ```
您可能已经注意到,不可能同时最大化判别器损失和发生器损失。 这就是 GAN 的优点,因为在训练时,该模型有望达到某种平衡,在这种情况下,生成器必须生成真正高质量的图像,以欺骗判别器。 您可能已经注意到,不可能同时最大化判别器损失和生成器损失。 这就是 GAN 的优点,因为在训练时,该模型有望达到某种平衡,在这种情况下,生成器必须生成真正高质量的图像,以欺骗判别器。
TensorFlow 仅允许其优化器最小化而不是最大化。 结果,我们实际上采用了前面所述的损失函数的负值,这意味着我们从最大化损失变为最小化损失。 不过,我们无需执行任何其他操作,因为`tf.nn.sigmoid_cross_entropy_with_logits()`会为我们解决此问题。 TensorFlow 仅允许其优化器最小化而不是最大化。 结果,我们实际上采用了前面所述的损失函数的负值,这意味着我们从最大化损失变为最小化损失。 不过,我们无需执行任何其他操作,因为`tf.nn.sigmoid_cross_entropy_with_logits()`会为我们解决此问题。
...@@ -467,7 +467,7 @@ discriminator_solver = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.5) ...@@ -467,7 +467,7 @@ discriminator_solver = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.5)
generator_solver = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.5) generator_solver = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.5)
``` ```
接下来,创建一个随机噪声量; 这可以通过`tf.random_uniform`完成。 这被馈送到生成器网络以创建一批生成的图像: 接下来,创建一个随机噪声量; 这可以通过`tf.random_uniform`完成。 这被馈送到生成器网络以创建一批生成的图像:
```py ```py
z = tf.random_uniform(maxval=1,minval=-1,shape=[batch_size, dim]) z = tf.random_uniform(maxval=1,minval=-1,shape=[batch_size, dim])
...@@ -503,7 +503,7 @@ generator_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, 'generator' ...@@ -503,7 +503,7 @@ generator_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, 'generator'
generator_train_step = generator_solver.minimize(generator_loss , var_list=generator_vars ) generator_train_step = generator_solver.minimize(generator_loss , var_list=generator_vars )
``` ```
这些是训练 GAN 的主要步骤。 剩下的就是创建一个训练循环,遍历大量数据。 如果这样做,您应该能够像训练中那样输入任何随机噪声量,并生成图像。 这些是训练 GAN 的主要步骤。 剩下的就是创建一个训练循环,遍历大量数据。 如果这样做,您应该能够像训练中那样输入任何随机噪声量,并生成图像。
如下图所示,创建的图像开始类似于 MNIST 数字: 如下图所示,创建的图像开始类似于 MNIST 数字:
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
研究表明,在 ImageNet 上训练的卷积网络权重中的特征提取优于常规特征提取方法,例如 SURF,可变形部分描述符(**DPD**),**直方图定向梯度****HOG**)和**词袋****BoW**)。 这意味着无论常规视觉表示如何工作,卷积特征都可以同样好地使用,唯一的缺点是更深的架构可能需要更长的时间来提取特征。 研究表明,在 ImageNet 上训练的卷积网络权重中的特征提取优于常规特征提取方法,例如 SURF,可变形部分描述符(**DPD**),**直方图定向梯度****HOG**)和**词袋****BoW**)。 这意味着无论常规视觉表示如何工作,卷积特征都可以同样好地使用,唯一的缺点是更深的架构可能需要更长的时间来提取特征。
当在 ImageNet 上训练深层卷积神经网络时,第一层中的卷积过滤器的可视化(请参见下图)显示,他们学习了*低层*特征,类似于边检测过滤器,而卷积过滤器在最后一层学习*高级*功能,这些功能捕获特定于类的信息。 因此,如果我们在第一个池化层之后提取 ImageNet 的特征并将其嵌入 2D 空间(例如,使用 t-SNE),则可视化将显示数据中存在一些无中心状态,而如果在全连接层上执行相同操作,我们将注意到具有相同语义信息的数据被组织成簇。 这意味着网络可以在更高层次上很好地概括,并且有可能将这种知识转移到看不见的类别中。 当在 ImageNet 上训练深层卷积神经网络时,第一层中的卷积过滤器的可视化(请参见下图)显示,他们学习了*低层*特征,类似于边检测过滤器,而卷积过滤器在最后一层学习*高级*功能,这些功能捕获特定于类的信息。 因此,如果我们在第一个池化层之后提取 ImageNet 的特征并将其嵌入 2D 空间(例如,使用 t-SNE),则可视化将显示数据中存在一些无中心状态,而如果在全连接层上执行相同操作,我们将注意到具有相同语义信息的数据被组织成簇。 这意味着网络可以在更高层次上很好地概括,并且有可能将这种知识转移到看不见的类别中。
![](img/b7b186d6-7803-4517-add9-f559586b2576.png) ![](img/b7b186d6-7803-4517-add9-f559586b2576.png)
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
深度学习的另一种不太常见的方式是自己预先训练模型。 当可用的预训练网络不适合解决特定问题时,通常会发生这种情况,我们必须自己设计网络架构。 显然,这需要更多的时间和精力来设计模型和准备数据集。 在某些情况下,用于进行网络预训练的数据集甚至可以是合成的,可以从计算机图形引擎(例如 3D Studio Max 或 Unity)或其他卷积神经网络(例如 GAN)生成。 可以对虚拟数据进行预训练的模型在真实数据上进行微调,并且可以与仅对真实数据进行训练的模型一起很好地工作。 深度学习的另一种不太常见的方式是自己预先训练模型。 当可用的预训练网络不适合解决特定问题时,通常会发生这种情况,我们必须自己设计网络架构。 显然,这需要更多的时间和精力来设计模型和准备数据集。 在某些情况下,用于进行网络预训练的数据集甚至可以是合成的,可以从计算机图形引擎(例如 3D Studio Max 或 Unity)或其他卷积神经网络(例如 GAN)生成。 可以对虚拟数据进行预训练的模型在真实数据上进行微调,并且可以与仅对真实数据进行训练的模型一起很好地工作。
例如,如果我们想区分猫和狗,而我们没有足够的数据,则可以从“模型动物园”下载在 ImageNet 上训练的网络,并使用除最后一层以外的所有层的权重。 最后一层必须调整为具有与班级数量相同的大小,在本例中为两个,并且权重需要重新初始化和训练。 这样,通过将这些层的学习率设置为零或非常小的值(请参见下图),我们将冻结那些不需训练的层。 如果有更大的数据集,我们可以训练最后三个全连接层。 有时,预训练网络只能用于初始化权重,然后再进行正常训练。 例如,如果我们想区分猫和狗,而我们没有足够的数据,则可以从“模型动物园”下载在 ImageNet 上训练的网络,并使用除最后一层以外的所有层的权重。 最后一层必须调整为具有与数量相同的大小,在本例中为两个,并且权重需要重新初始化和训练。 这样,通过将这些层的学习率设置为零或非常小的值(请参见下图),我们将冻结那些不需训练的层。 如果有更大的数据集,我们可以训练最后三个全连接层。 有时,预训练网络只能用于初始化权重,然后再进行正常训练。
迁移学习之所以有效,是因为在初始层计算出的特征更通用并且看起来很相似。 在顶层提取的特征对于我们要解决的问题变得更加具体。 迁移学习之所以有效,是因为在初始层计算出的特征更通用并且看起来很相似。 在顶层提取的特征对于我们要解决的问题变得更加具体。
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
# 没有解码器的自编码器 # 没有解码器的自编码器
包含两个卷积层和一个完全连接层的编码器(不带解码器部分的自编码器)如下所示。 父自编码器在 MNIST 数据集上进行了训练。 因此,网络将大小为`28x28x1`的图像作为输入,并在潜在空间将其编码为 10 维量,每个类别一维: 包含两个卷积层和一个完全连接层的编码器(不带解码器部分的自编码器)如下所示。 父自编码器在 MNIST 数据集上进行了训练。 因此,网络将大小为`28x28x1`的图像作为输入,并在潜在空间将其编码为 10 维量,每个类别一维:
```py ```py
# Only half of the autoencoder changed for classification # Only half of the autoencoder changed for classification
...@@ -147,7 +147,7 @@ list_fc_linear = [v for v in tf.global_variables() if "fc" in v.name or "output" ...@@ -147,7 +147,7 @@ list_fc_linear = [v for v in tf.global_variables() if "fc" in v.name or "output"
saver = tf.train.Saver() saver = tf.train.Saver()
``` ```
然后,在使用`init = tf.global_variables_initializer()`初始化图并创建会话之后,我们可以使用`saver_load_autoencoder`从检查点恢复卷积层,如下所示: 然后,在使用`init = tf.global_variables_initializer()`初始化图并创建会话之后,我们可以使用`saver_load_autoencoder`从检查点恢复卷积层,如下所示:
```py ```py
# Restore only the weights (From AutoEncoder) # Restore only the weights (From AutoEncoder)
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
# 偏差和方差 # 偏差和方差
如第 2 章,“深度学习和卷积神经网络”中所讨论的,方差和偏差分别表示过拟合和欠拟合。 我们可以使用训练集,开发集和测试集错误来诊断“欠拟合”和“过拟合”的问题。 如第 2 章,“深度学习和卷积神经网络”中所讨论的,方差和偏差分别表示过拟合和欠拟合。 我们可以使用训练集,开发集和测试集误差来诊断“欠拟合”和“过拟合”的问题。
考虑以下场景,其中我们的数据来自两个不同的分布,分别称为分布 1 和分布 2。分布 2 表示我们关心的目标应用。 问题是,我们如何在这种分布上定义训练,开发和测试集。 考虑以下场景,其中我们的数据来自两个不同的分布,分别称为分布 1 和分布 2。分布 2 表示我们关心的目标应用。 问题是,我们如何在这种分布上定义训练,开发和测试集。
...@@ -72,7 +72,7 @@ ...@@ -72,7 +72,7 @@
![](img/43b92390-d3d8-4104-a0e8-0b0e03958557.png) ![](img/43b92390-d3d8-4104-a0e8-0b0e03958557.png)
下表可以更好地说明这一点。 在这些示例中,我们假设在所有情况下的最佳/人为错误均最小,即 1%。 通常,深度学习模型的准确性与人类相似,因此将其作为比较可帮助您找到良好的架构。 下表可以更好地说明这一点。 在这些示例中,我们假设在所有情况下的最佳/人为误差均最小,即 1%。 通常,深度学习模型的准确性与人类相似,因此将其作为比较可帮助您找到良好的架构。
* 高偏差/欠拟合 * 高偏差/欠拟合
...@@ -103,8 +103,8 @@ ...@@ -103,8 +103,8 @@
| | | | | |
| --- | --- | | --- | --- |
| 训练开发错误 | 2% | | 训练开发误差 | 2% |
| 开发错误 | 15% | | 开发误差 | 15% |
当模型很好地适合来自与训练集相同分布的开发集,并且对来自不同分布的开发集表现不佳时,这会导致数据不匹配问题,如本章前面所述。 当模型很好地适合来自与训练集相同分布的开发集,并且对来自不同分布的开发集表现不佳时,这会导致数据不匹配问题,如本章前面所述。
...@@ -277,13 +277,13 @@ NPV: TN / (TN + FN) = 0.99 ...@@ -277,13 +277,13 @@ NPV: TN / (TN + FN) = 0.99
# 代码结构最佳实践 # 代码结构最佳实践
在前面的章节中,我们将张量流图封装到一个类中,而无需进一步讨论。 这个想法本身已经是很好的编码实践。 有一个班级负责构建图形并仅公开对使用模型有用的东西(即输入/输出)是一种很好的编程习惯,可以节省大量时间。 在前面的章节中,我们将张量流图封装到一个类中,而无需进一步讨论。 这个想法本身已经是很好的编码实践。 有一个类负责构建图并仅公开对使用模型有用的东西(即输入/输出)是一种很好的编程习惯,可以节省大量时间。
# 单例模式 # 单例模式
使用设计模式来解决一些软件设计问题也是一种常见的做法。 python 中最简单,最有用的设计模式之一就是单例模式。 当您只想将一个类的实例强制仅用于一个对象时,可以使用它,因此,即使您在项目中的多个不同位置多次实例化该类,也将引用同一个对象。 在我们的情况下,如果我们要求 TensorFlow 创建具有相同名称的多个节点或图形,则会引发错误。 因此,我们在创建图形时使用单例模式,以避免生成两次。 使用设计模式来解决一些软件设计问题也是一种常见的做法。 python 中最简单,最有用的设计模式之一就是单例模式。 当您只想将一个类的实例强制仅用于一个对象时,可以使用它,因此,即使您在项目中的多个不同位置多次实例化该类,也将引用同一个对象。 在我们的情况下,如果我们要求 TensorFlow 创建具有相同名称的多个节点或图,则会引发错误。 因此,我们在创建图使用单例模式,以避免生成两次。
在下面的示例中,我们总结了一个简单的分类模型,同时还确保不会多次构建图(也称为单例模式)。 在下面的示例中,我们总结了一个简单的分类模型,同时还确保不会多次构建图(也称为单例模式)。
注意`__new__`类方法的定义。 在 Python 中,当我们创建一个类的新实例时,将调用`__new__` 注意`__new__`类方法的定义。 在 Python 中,当我们创建一个类的新实例时,将调用`__new__`
...@@ -348,10 +348,10 @@ class CAE_CNN_Encoder(object): ...@@ -348,10 +348,10 @@ class CAE_CNN_Encoder(object):
1. 使用内核大小为`3x3`的卷积层。 就参数和计算而言,较大的内核更昂贵。 最重要的是,如我们在前面的章节中所看到的,您可以堆叠卷积层以产生更大的感受域,并受益于更多的非线性激活。 1. 使用内核大小为`3x3`的卷积层。 就参数和计算而言,较大的内核更昂贵。 最重要的是,如我们在前面的章节中所看到的,您可以堆叠卷积层以产生更大的感受域,并受益于更多的非线性激活。
2. 第一层卷积通常应至少具有 32 个过滤器。 这样,更深的层不受第一层提取的特征数量的限制。 2. 第一层卷积通常应至少具有 32 个过滤器。 这样,更深的层不受第一层提取的特征数量的限制。
3. 尽可能避免使用池化层。 相反,请使用步长为 2 的卷积层。这将像池化那样对输入进行下采样,但它不会像池化那样丢弃宝贵的信息。 同样,使用跨步卷积就像将卷积和合并在一层中一样。 3. 尽可能避免使用池化层。 相反,请使用步长为 2 的卷积层。这将像池化那样对输入进行下采样,但它不会像池化那样丢弃宝贵的信息。 同样,使用跨步卷积就像将卷积和合并在一层中一样。
4. 减小要素地图的空间大小时,应增加使用的过滤器数量,以免丢失过多信息。 在深度网络中,请避免在第一层中过快减小空间大小。 4. 减小特征图的空间大小时,应增加使用的过滤器数量,以免丢失过多信息。 在深度网络中,请避免在第一层中过快减小空间大小。
5. 请遵循本章中有关从小规模开始网络设计,然后逐渐增加复杂性的建议,以避免出现过大的问题。 5. 请遵循本章中有关从小规模开始网络设计,然后逐渐增加复杂性的建议,以避免出现过大的问题。
6. 使用批量规范化。 确实有助于训练您的网络! 6. 使用批量规范化。 确实有助于训练您的网络!
7. 随着您对网络的深入了解,逐渐减小要素地图的空间大小。 7. 随着您对网络的深入了解,逐渐减小特征图的空间大小。
8. 最小化 FC 层的数量(在最后一层之前使用丢弃)。 仅在最终需要连接某些标量特征时才使用 FC。 (您甚至可以通过在输入通道上进行编码来避免这种情况) 8. 最小化 FC 层的数量(在最后一层之前使用丢弃)。 仅在最终需要连接某些标量特征时才使用 FC。 (您甚至可以通过在输入通道上进行编码来避免这种情况)
9. 如果您需要较大的感受域(对象大小接近总图像大小的检测或分类),请尝试对每层使用具有指数膨胀因子的膨胀卷积。 这样,您将在保持少量参数的同时非常迅速地扩大接收范围。 9. 如果您需要较大的感受域(对象大小接近总图像大小的检测或分类),请尝试对每层使用具有指数膨胀因子的膨胀卷积。 这样,您将在保持少量参数的同时非常迅速地扩大接收范围。
......
...@@ -70,7 +70,7 @@ for index in range(len(labels)): ...@@ -70,7 +70,7 @@ for index in range(len(labels)):
必须使用`tf.train.BytesList``tf.train.Int64List``tf.train.FloatList`将进入`tf.train.Feature`的数据转换为期望的正确类型。 必须使用`tf.train.BytesList``tf.train.Int64List``tf.train.FloatList`将进入`tf.train.Feature`的数据转换为期望的正确类型。
接下来,我们创建一个`tf.train.Example`协议缓冲区并将功能传递给它。 最后,我们将`Example`序列化为字符串并将其写入 TFRecord 文件。 一旦遍历了整个图像阵列,就必须记住关闭文件进行写入。 接下来,我们创建一个`tf.train.Example`协议缓冲区并将功能传递给它。 最后,我们将`Example`序列化为字符串并将其写入 TFRecord 文件。 一旦遍历了整个图像数组,就必须记住关闭文件进行写入。
# 存储编码图像 # 存储编码图像
...@@ -128,7 +128,7 @@ train_dataset = train_dataset.map(decode_tfrec, num_parallel_calls=4) ...@@ -128,7 +128,7 @@ train_dataset = train_dataset.map(decode_tfrec, num_parallel_calls=4)
# 映射转换的并行调用 # 映射转换的并行调用
默认情况下,您在数据集上调用的任何地图转换都仅作用于数据集的单个元素,并且将按顺序处理元素。 要加快速度并使用所有 CPU 功能,最简单的方法是将`num_parallel_calls`参数设置为可用的 CPU 内核数。 这样,我们就不会浪费任何可用的 CPU 能力。 但是,警告您不要将其设置为高于可用内核的数量,因为由于调度效率低下,这实际上可能会降低性能。 默认情况下,您在数据集上调用的任何映射转换都仅作用于数据集的单个元素,并且将按顺序处理元素。 要加快速度并使用所有 CPU 功能,最简单的方法是将`num_parallel_calls`参数设置为可用的 CPU 内核数。 这样,我们就不会浪费任何可用的 CPU 能力。 但是,警告您不要将其设置为高于可用内核的数量,因为由于调度效率低下,这实际上可能会降低性能。
您想要对数据进行的任何转换(例如数据扩充)也可以编写为函数,然后像以前一样传递给`map`方法,以将其应用于数据集。 例如,请注意以下代码: 您想要对数据进行的任何转换(例如数据扩充)也可以编写为函数,然后像以前一样传递给`map`方法,以将其应用于数据集。 例如,请注意以下代码:
...@@ -164,9 +164,9 @@ train_dataset= train_dataset.batch(128).prefetch(1) ...@@ -164,9 +164,9 @@ train_dataset= train_dataset.batch(128).prefetch(1)
# 追踪图 # 追踪图
TensorFlow 提供了一种很好的方式来分析并查看整个图形通过其时间轴跟踪工具执行所需的时间。 这是查看图表的哪些部分正在减慢训练速度并发现数据流水线中任何低效率的好工具。 TensorFlow 提供了一种很好的方式来分析并查看整个图通过其时间轴跟踪工具执行所需的时间。 这是查看图的哪些部分正在减慢训练速度并发现数据流水线中任何低效率的好工具。
我们将从为您提供如何跟踪图形的示例开始。 这非常简单:您只需在常规代码中添加几行,就会生成一个 JSON 文件,我们可以将该文件加载到 Google Chrome 浏览器中,以查看图形执行的所有时间: 我们将从为您提供如何跟踪图的示例开始。 这非常简单:您只需在常规代码中添加几行,就会生成一个 JSON 文件,我们可以将该文件加载到 Google Chrome 浏览器中,以查看图执行的所有时间:
```py ```py
from tensorflow.python.client import timeline from tensorflow.python.client import timeline
...@@ -186,11 +186,11 @@ with tf.Session() as sess: ...@@ -186,11 +186,11 @@ with tf.Session() as sess:
file.write(chome_readable_trace) file.write(chome_readable_trace)
``` ```
在此代码中,我们导入 TensorFlow 时间轴模块,然后设置两个选项以启用图跟踪并将其提供给`Session.run()`。 运行图之后,我们创建`Timeline`对象,该对象将包含对图执行进行性能分析的结果。 然后,我们将其转换为 Chrome 跟踪格式,最后将其写入 JSON 文件。 在此代码中,我们导入 TensorFlow 时间轴模块,然后设置两个选项以启用图跟踪并将其提供给`Session.run()`。 运行图之后,我们创建`Timeline`对象,该对象将包含对图执行进行性能分析的结果。 然后,我们将其转换为 Chrome 跟踪格式,最后将其写入 JSON 文件。
要查看结果,您需要打开一个新的 Chrome 窗口。 然后,在地址栏中输入`chrome://tracing`并按`Enter`。 左上角将有一个加载按钮。 使用它来加载刚刚保存的 JSON 文件。 要查看结果,您需要打开一个新的 Chrome 窗口。 然后,在地址栏中输入`chrome://tracing`并按`Enter`。 左上角将有一个加载按钮。 使用它来加载刚刚保存的 JSON 文件。
现在将显示跟踪图形的结果。 查看此内容将告诉您图形的每个部分执行所需的时间。 您应该特别注意存在大块空白的地方。 这些空白表示设备(例如您的 GPU)正坐在那里等待数据,以便它们可以执行计算。 您应该尝试通过优化数据馈送方式来消除这些问题。 现在将显示跟踪图的结果。 查看此内容将告诉您图的每个部分执行所需的时间。 您应该特别注意存在大块空白的地方。 这些空白表示设备(例如您的 GPU)正坐在那里等待数据,以便它们可以执行计算。 您应该尝试通过优化数据馈送方式来消除这些问题。
但是请注意,您的流水线可能已完全优化,但是您没有 CPU 周期来足够快地处理流水线。 检查您的 CPU 使用情况,看看是否是这种情况。 但是请注意,您的流水线可能已完全优化,但是您没有 CPU 周期来足够快地处理流水线。 检查您的 CPU 使用情况,看看是否是这种情况。
...@@ -216,9 +216,9 @@ with tf.Session() as sess: ...@@ -216,9 +216,9 @@ with tf.Session() as sess:
# 同步/异步 SGD # 同步/异步 SGD
如前所述,在数据并行性中,每个模型都会从训练集中获取一些数据并计算自己的梯度,但是考虑到每个工作人员都将拥有相同的模型,我们需要在更新模型之前以某种方式进行同步。 如前所述,在数据并行性中,每个模型都会从训练集中获取一些数据并计算自己的梯度,但是考虑到每个工作都将拥有相同的模型,我们需要在更新模型之前以某种方式进行同步。
在同步 SGD 中,所有工作人员都会计算一个梯度并等待计算所有梯度,然后将模型更新并再次分发给所有工作人员 在同步 SGD 中,所有工作器都会计算一个梯度并等待计算所有梯度,然后将模型更新并再次分发给所有工作器
![](img/f8da6b99-2533-4df2-87c9-2ba3c2165494.png) ![](img/f8da6b99-2533-4df2-87c9-2ba3c2165494.png)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册