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

2020-09-15 22:46:25

上级 36cc08fb
此差异已折叠。
......@@ -234,7 +234,7 @@ TensorFlow 的所有不同优化器都可以在`tf.train`类中找到。 例如
![](img/9b567add-923e-49f9-a994-3c69e7f8372c.png)
学习率的另一个重要方面是,随着训练的进行和错误的减少,您在训练开始时选择的学习率值可能会变得太大,因此您可能会开始超出最小值。
学习率的另一个重要方面是,随着训练的进行和误差的减少,您在训练开始时选择的学习率值可能会变得太大,因此您可能会开始超出最小值。
要解决此问题,您可以安排学习速率衰减,以免在训练时降低学习速率。 这个过程称为**学习率调度**,我们将在下一章中详细讨论几种流行的方法。
......@@ -265,7 +265,7 @@ TensorFlow 的所有不同优化器都可以在`tf.train`类中找到。 例如
在后面的章节中,我们将看到如何检测,避免和补救这些问题,但是仅出于介绍的目的,这些是解决这些问题的一些经典方法:
* 获取更多数据
* 当检测到测试数据的错误开始增长时停止(提前停止)
* 当检测到测试数据的误差开始增长时停止(提前停止)
* 尽可能简单地开始模型设计,并且仅在检测到欠拟合时才增加复杂性
# 特征缩放
......@@ -296,7 +296,7 @@ dense_layer = tf.layers.dense(inputs=some_input_layer, units=1024, activation=tf
在这里,我们定义了一个具有 1,024 个输出的完全连接层,随后将激活 ReLU。
重要的是要注意,该层的输入必须仅具有二维,因此,如果您的输入是空间张量,例如形状为`[28 * 28 * 3]`的图像,则必须在输入之前将其重整为量:
重要的是要注意,该层的输入必须仅具有二维,因此,如果您的输入是空间张量,例如形状为`[28 * 28 * 3]`的图像,则必须在输入之前将其重整为量:
```py
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 示例
在这里,我们将到目前为止已经了解的一些知识放在一起,并将使用 TensorFlow 解决布尔 XOR 问题。 在此示例中,我们将创建一个具有 S激活函数的三层神经网络。 我们使用对数丢失,因为网络 0 或 1 仅有两种可能的结果:
在这里,我们将到目前为止已经了解的一些知识放在一起,并将使用 TensorFlow 解决布尔 XOR 问题。 在此示例中,我们将创建一个具有 Sigmoid 激活函数的三层神经网络。 我们使用对数丢失,因为网络 0 或 1 仅有两种可能的结果:
```py
import tensorflow as tf
......@@ -357,7 +357,7 @@ for step in range(10000):
![](img/75567914-f4cf-459e-9955-ab4c20c97299.png)
为了能够查看图,可以在脚本提示符下的命令提示符下运行以下命令。 这将为我们启动 tensorboard。 我们将在本章的后面找到关于 tensorboard 的更多信息。
为了能够查看图,可以在脚本提示符下的命令提示符下运行以下命令。 这将为我们启动 tensorboard。 我们将在本章的后面找到关于 tensorboard 的更多信息。
```py
$ tensorboard --logdir=./logs/xor_logs
......@@ -387,7 +387,7 @@ CNN 主要由称为**卷积层**的层组成,这些层对其层输入进行过
在 CNN 中,卷积层使用称为**内核**的小窗口,以类似于瓦片的方式过滤输入张量。 内核精确定义了卷积运算将要过滤的内容,并且在找到所需内容时会产生强烈的响应。
下图显示了将图像与称为 Sobel 过滤器的特定内核进行卷积的结果,该内核非常适合在图像中查找边
下图显示了将图像与称为 Sobel 过滤器的特定内核进行卷积的结果,该内核非常适合在图像中查找边:
![](img/263bc17f-fc97-42c2-a99a-8bc089aa9453.png)
......@@ -411,7 +411,7 @@ CNN 主要由称为**卷积层**的层组成,这些层对其层输入进行过
* **内核大小(K)**:滑动窗口的像素大小。 小通常更好,通常使用奇数,例如 1、3、5,有时很少使用 7。
* **跨度(S)**:内核窗口在卷积的每个步骤中将滑动多少像素。 通常将其设置为 1,因此图像中不会丢失任何位置,但是如果我们想同时减小输入大小,则可以增加位置。
* **零填充(P)**:要放在图像边框上的零数量。 使用填充使内核可以完全过滤输入图像的每个位置,包括边
* **零填充(P)**:要放在图像边框上的零数量。 使用填充使内核可以完全过滤输入图像的每个位置,包括边。
* **过滤器数(F)**:我们的卷积层将具有多少个过滤器。 它控制卷积层将要查找的图案或特征的数量。
在 TensorFlow 中,我们将在`tf.layers`模块中找到 2D 卷积层,可以将其添加到模型中,如下所示:
......@@ -734,7 +734,7 @@ with tf.Session() as sess:
"""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)
......
# 三、TensorFlow 中的图像分类
图像分类是指根据图像内容将图像分类的问题。 让我们从分类的示例任务开始,其中图片可能是狗的图像,也可能不是。 某人可能要完成此任务的一种简单方法是,像在第 1 章中所做的那样,获取输入图像,将其重塑为量,然后训练线性分类器(或其他某种分类器)。 但是,您很快就会发现此主意不好,原因有几个。 除了不能很好地缩放到输入图像的大小之外,线性分类器将很难将一个图像与另一个图像分开。
图像分类是指根据图像内容将图像分类的问题。 让我们从分类的示例任务开始,其中图片可能是狗的图像,也可能不是。 某人可能要完成此任务的一种简单方法是,像在第 1 章中所做的那样,获取输入图像,将其重塑为量,然后训练线性分类器(或其他某种分类器)。 但是,您很快就会发现此主意不好,原因有几个。 除了不能很好地缩放到输入图像的大小之外,线性分类器将很难将一个图像与另一个图像分开。
与可以在图像中看到有意义的图案和内容的人类相反,计算机只能看到从 0 到 255 的数字数组。对于同一类的不同图像,这些数字在相同位置的广泛波动导致无法直接将它们使用为分类器的输入。 从**加拿大高级研究学院****CIFAR**)数据集中获取的这 10 张示例狗图像完美地说明了此问题。 狗的外观不仅有所不同,而且它们在镜头前的姿势和位置也有所不同。 对于机器来说,每个图像一目了然,完全没有共同点,而我们人类却可以清楚地看到它们都是狗:
......@@ -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)2007 挑战赛,当时班级的数量从 4 个增加到 20 个,并且此后一直固定。 分类任务的评估指标已更改为平均精度。 仅在 VOC 2007 挑战赛之前提供测试数据的注释。
重大变化来自 PASCAL(VOC)2007 挑战赛,当时的数量从 4 个增加到 20 个,并且此后一直固定。 分类任务的评估指标已更改为平均精度。 仅在 VOC 2007 挑战赛之前提供测试数据的注释。
随着更复杂的分类方法的出现,前面的数据集是不够的,以下几节中介绍的 ImageNet 数据集和 CIFAR 数据集成为分类测试的新标准。
......@@ -136,11 +136,11 @@ ImageNet 数据集由 Alex Berg(哥伦比亚大学),Jia Deng(普林斯
![](img/6436a6ce-fd4b-463c-b195-3c34040e17df.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% 的误差,出现了较小的改进。
......@@ -266,7 +266,7 @@ if __name__ == '__main__':
# 建立 CNN 图
让我们通过`build_graph`函数进行详细介绍,该函数包含网络定义,损失函数和所使用的优化器。 首先,我们通过为输入定义占位符来启动函数。 我们将使用两个占位符在图表中提供数据和标签:`__x_``__y_`。 占位符`__x_`将保存我们输入的 RGB 图像,而占位符`__y_` 存储一个对应类别的热门标签。 在定义占位符形状的`N`部分时,我们使用`None`,因为这告诉 TensorFlow 该值可以是任何值,并且在执行图形时将提供该值:
让我们通过`build_graph`函数进行详细介绍,该函数包含网络定义,损失函数和所使用的优化器。 首先,我们通过为输入定义占位符来启动函数。 我们将使用两个占位符在图中提供数据和标签:`__x_``__y_`。 占位符`__x_`将保存我们输入的 RGB 图像,而占位符`__y_` 存储一个对应类别的热门标签。 在定义占位符形状的`N`部分时,我们使用`None`,因为这告诉 TensorFlow 该值可以是任何值,并且在执行图时将提供该值:
```py
def build_graph(self):
......@@ -389,7 +389,7 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
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
# Avoid allocating the whole memory
......@@ -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
# Train Loop
......@@ -546,9 +546,9 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
# 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`,每一层的权重方差必须如下:
......@@ -564,7 +564,7 @@ decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
因此,作者使用零均值高斯分布初始化其权重,其标准差(STD)为`√(2/n[in])`。 然后将该初始化称为 He 初始化。
默认情况下,TensorFlow 的大部分`tf.layers`都使用 Glorot(xavier)初始化程序,但是我们可以覆盖它并指定我们自己的初始化。 在这里,我们展示了一个如何覆盖`conv2d`层的默认初始化器的示例:
默认情况下,TensorFlow 的大部分`tf.layers`都使用 Glorot(xavier)初始化,但是我们可以覆盖它并指定我们自己的初始化。 在这里,我们展示了一个如何覆盖`conv2d`层的默认初始化器的示例:
```py
conv1 = tf.layers.conv2d(inputs=self.__x_, filters=64, kernel_size=[5, 5],
......@@ -590,7 +590,7 @@ bias_initializer=tf.zeros_initializer())
创建更强大模型的第一种方法是使用 L1 或 L2 正则化。 到目前为止,这些是最常见的正则化方法。 基本思想是在训练模型时,我们积极尝试使用这些权重的 L1 或 L2 范数对模型权重的值施加一些约束。
为此,我们在使用的任何损失函数中增加了一个额外的项。 对于 L1 正则化,我们添加的术语是`λ |w|`,对于 L2 正则化,我们添加的术语是`0.5 λ w^2`。 在前面的术语中,`w`是我们网络中的所有权重,`λ`是称为**正则化强度**的超参数。 通过添加该术语,我们可以防止权重值变得太大。
为此,我们在使用的任何损失函数中增加了一个额外的项。 对于 L1 正则化,我们添加的项是`λ |w|`,对于 L2 正则化,我们添加的项是`0.5 λ w^2`。 在前面的项中,`w`是我们网络中的所有权重,`λ`是称为**正则化强度**的超参数。 通过添加该项,我们可以防止权重值变得太大。
因此,为了 L1 正则化,第 1 章,“TensorFlow 简介和设置”的 SVM 损失函数,即:
......@@ -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)
```
要添加我们的正则化术语,我们首先需要将它们全部收集起来。 幸运的是,TensorFlow 会自动为我们将所有正则化术语放到一个集合中,以便我们可以轻松访问它们。 TensorFlow 在`tf.GraphKeys`内存储一些与您创建的图相关的重要集合,例如可训练变量,汇总和正则化损失。 我们可以使用`tf.get_collection()`访问这些集合,并提供要获取的集合的名称。 例如,为了得到正则化损失,我们将编写以下内容:
要添加我们的正则化项,我们首先需要将它们全部收集起来。 幸运的是,TensorFlow 会自动为我们将所有正则化项放到一个集合中,以便我们可以轻松访问它们。 TensorFlow 在`tf.GraphKeys`内存储一些与您创建的图相关的重要集合,例如可训练变量,汇总和正则化损失。 我们可以使用`tf.get_collection()`访问这些集合,并提供要获取的集合的名称。 例如,为了得到正则化损失,我们将编写以下内容:
```py
reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
......@@ -653,7 +653,7 @@ combined_loss = tf.n_add(train_loss, reg_losses)
![](img/cce137ca-2838-44ac-b275-7e83d01a5ddb.png)
在下图中,我们显示了模型测试错误。 显然,通过辍学,测试集上的错误会减少。 请记住,与所有正则化一样,与不使用正则化相比,使用辍学会使您的训练损失增加,但是到最后,我们只对模型测试错误率降低(泛化)感兴趣:
在下图中,我们显示了模型测试误差。 显然,通过辍学,测试集上的误差会减少。 请记住,与所有正则化一样,与不使用正则化相比,使用辍学会使您的训练损失增加,但是到最后,我们只对模型测试错误率降低(泛化)感兴趣:
![](img/eef8f1a2-e6e1-40aa-9c0e-8ef692a60665.png)
......
......@@ -354,7 +354,7 @@ def tf_iou_vectorized(self, box_vec_1, box_vec_2):
```
我们也可以在 TensorFlow 上将其更改为量化形式,如下所示:
我们也可以在 TensorFlow 上将其更改为量化形式,如下所示:
```py
def tf_iou_vectorized(self, box_vec_1, box_vec_2):
......@@ -456,9 +456,9 @@ Tensorflow 已经具有实现非最大值抑制算法的功能,称为`tf.image
![](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
![](img/aea81ee0-d79f-4213-9343-be0e90e8c96e.png)
在实践中的实践中,您将尝试量化这种损失并避免`for`循环并提高性能,这对于 Tensorflow 之类的库尤其如此。
在实践中的实践中,您将尝试量化这种损失并避免`for`循环并提高性能,这对于 Tensorflow 之类的库尤其如此。
这是 YOLO 损失的 TensorFlow 实现:
......@@ -566,7 +566,7 @@ def loss_layer(self, predicts, labels, scope='loss_layer'):
这个运算相当不好地称为反卷积,这意味着它是卷积的逆运算,但实际上并非如此。 更恰当的名称是转置卷积或分数步卷积。
此层类型为您提供了一种对输入体积进行升采样的学习方法,并且可以在每次需要将输入要素地图智能地投影到更高的空间时使用。 一些用例包括以下内容:
此层类型为您提供了一种对输入体积进行升采样的学习方法,并且可以在每次需要将输入特征图智能地投影到更高的空间时使用。 一些用例包括以下内容:
* 上采样(条纹转置卷积)`== UNPOOL + CONV`
* 可视化显着图
......
......@@ -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)
......@@ -313,7 +313,7 @@ GoogLeNet 的主要优点是,它比 VGG 更为准确,同时使用的参数
其中`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 @@
# 卷积自编码器示例
以下 TensorFlow 代码将为 MNIST 数据集构建卷积自编码器模型。 代码的第一部分将构建模型,编码器和解码器的图。 在代码中,我们突出显示模型的一部分,其输出将是我们的潜在向量:
以下 TensorFlow 代码将为 MNIST 数据集构建卷积自编码器模型。 代码的第一部分将构建模型,编码器和解码器的图。 在代码中,我们突出显示模型的一部分,其输出将是我们的潜在向量:
```py
class CAE_CNN(object):
......@@ -122,7 +122,7 @@ with tf.name_scope("Solver"):
我们第一个可以生成更多类似于训练数据的真实生成模型,将是**变分自编码器****VAE**)。 VAE 看起来像正常的自编码器,但有一个新的约束,它将迫使我们的压缩表示(潜伏空间)遵循零均值和单位方差高斯分布。
在潜在空间上施加此约束的想法是,当我们想使用 VAE 生成新数据时,我们可以创建来自单位高斯分布的样本矢量,并将其提供给经过训练的解码器。 VAE 和常规自编码器之间的差异就是对潜在空间矢量的约束。 这个约束条件使我们可以创建一种新的潜在矢量,然后再将其馈送到解码器以生成数据。
在潜在空间上施加此约束的想法是,当我们想使用 VAE 生成新数据时,我们可以创建来自单位高斯分布的样本向量,并将其提供给经过训练的解码器。 VAE 和常规自编码器之间的差异就是对潜在空间向量的约束。 这个约束条件使我们可以创建一种新的潜在向量,然后再将其馈送到解码器以生成数据。
下图显示,VAE 在结构上与自编码器完全相同,除了对隐藏空间的约束之外:
......@@ -160,13 +160,13 @@ KL 散度损失将产生一个数字,该数字指示两个分布彼此之间
# 训练 VAE
为了训练 VAE 并使用 KL 散度损失,我们首先需要研究如何生成潜矢量。 我们将使编码器产生两个向量,而不是让编码器直接精确地产生一个潜在向量。 第一个是平均值的向量`μ`,第二个是标准差值的向量`σ`。 根据这些,我们可以创建第三个向量,其中使用`μ``σ`从高斯分布中采样元素向量的第`i`个值作为该高斯分布的均值和标准差。 然后,该第三采样矢量被发送到解码器。
为了训练 VAE 并使用 KL 散度损失,我们首先需要研究如何生成潜向量。 我们将使编码器产生两个向量,而不是让编码器直接精确地产生一个潜在向量。 第一个是平均值的向量`μ`,第二个是标准差值的向量`σ`。 根据这些,我们可以创建第三个向量,其中使用`μ``σ`从高斯分布中采样元素向量的第`i`个值作为该高斯分布的均值和标准差。 然后,该第三采样向量被发送到解码器。
现在,我们的模型如下所示:
![](img/944095bc-4bde-4e04-86e3-1ccc3e0b1c04.png)
上图中的均值和标准差块将只是正常的全连接层,它们将通过 KL 损失函数来学习如何返回所需的值。 更改我们如何获得潜量的原因是因为它使我们能够轻松计算 KL 散度损失。 KL 损失现在如下:`latent_mean``μ``latent_stddev``σ`
上图中的均值和标准差块将只是正常的全连接层,它们将通过 KL 损失函数来学习如何返回所需的值。 更改我们如何获得潜量的原因是因为它使我们能够轻松计算 KL 散度损失。 KL 损失现在如下:`latent_mean``μ``latent_stddev``σ`
```py
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 散度损失将产生一个数字,该数字指示两个分布彼此之间
# 重新参数化技巧
重新参数化技巧的想法是从反向传播循环中取出随机样本节点。 它是通过从高斯分布中获取样本ε,然后将其乘以我们的标准差`σ`的结果,然后加上`μ`来实现的。 现在,我们的潜在向量的公式是:
重新参数化技巧的想法是从反向传播循环中取出随机样本节点。 它是通过从高斯分布中获取样本ε,然后将其乘以我们的标准差`σ`的结果,然后加上`μ`来实现的。 现在,我们的潜在向量的公式是:
![](img/16915d9b-677d-4f06-b381-1fe5e7353406.jpg)
![](img/ae58ed7b-25c5-447d-bb71-54d32dc9ed15.jpg)
产生的潜量将与以前相同,但是现在进行此更改可以使梯度流回到 VAE 的编码器部分。 下图显示了在进行重新参数化之前的 VAE 模型,在左侧进行了重新参数化之后。 蓝色框是损失函数的两个部分。 查看该图,您可以看到我们的渐变现在可以向后流动,因为我们不再具有红色框(示例节点)来挡路了:
产生的潜量将与以前相同,但是现在进行此更改可以使梯度流回到 VAE 的编码器部分。 下图显示了在进行重新参数化之前的 VAE 模型,在左侧进行了重新参数化之后。 蓝色框是损失函数的两个部分。 查看该图,您可以看到我们的渐变现在可以向后流动,因为我们不再具有红色框(示例节点)来挡路了:
![](img/412bfece-2f71-4956-a5fe-3ae19cce2b6b.png)
......@@ -219,7 +219,7 @@ class VAE_CNN(object):
filters=32, kernel_size=[5, 5], padding="same", activation=tf.nn.relu)
```
接下来是 VAE 的一部分,该部分负责使用我们之前的新重新参数化技巧来创建潜在量。 我们添加了对最终潜在向量的记录,以检查它是否按照我们期望的那样遵循单位高斯分布产生向量:
接下来是 VAE 的一部分,该部分负责使用我们之前的新重新参数化技巧来创建潜在量。 我们添加了对最终潜在向量的记录,以检查它是否按照我们期望的那样遵循单位高斯分布产生向量:
```py
with tf.name_scope('LATENT'):
......@@ -287,7 +287,7 @@ with tf.name_scope("Solver"):
# 产生新数据
训练完 VAE 模型后,我们可以将其解码器部分截断,并用作生成器为我们生成新数据。 它将通过向它提供来自单位高斯分布的新潜在量来工作。
训练完 VAE 模型后,我们可以将其解码器部分截断,并用作生成器为我们生成新数据。 它将通过向它提供来自单位高斯分布的新潜在量来工作。
我们在 TensorFlow 中提供负责构建此生成的 VAE 图的代码,如下所示:
......@@ -331,11 +331,11 @@ class VAE_CNN_GEN(object):
![](img/98fcbf21-46e3-45f0-8a2d-a7004af773c3.png)
判别器和发生器都将具有自己的损失函数,但是它们的损失都相互依赖。
判别器和生成器都将具有自己的损失函数,但是它们的损失都相互依赖。
让我们总结一下 GAN 模型的两个主要模块或网络:
* **生成器**:使用大小为 N 的一维量作为输入,创建类似于*真实图像*数据集的图像(选择 N 取决于我们)
* **生成器**:使用大小为 N 的一维量作为输入,创建类似于*真实图像*数据集的图像(选择 N 取决于我们)
* **判别器**:验证提供给它的图像是真实的还是伪造的
GAN 的一些实际用法如下:
......@@ -345,7 +345,7 @@ GAN 的一些实际用法如下:
* 将判别器用作损失函数(对于图像,可能优于 L1/L2),并且也可以在 VAE 中使用
* 通过将生成的数据与标记的数据混合来进行半监督学习
现在我们将向您展示如何在 TensorFlow 中实现非常简单的 GAN。 一旦经过训练,我们的 GAN 的生成器部分就可以用于根据 100 个长随机噪声量创建 MNIST 手写数字。 让我们开始吧!
现在我们将向您展示如何在 TensorFlow 中实现非常简单的 GAN。 一旦经过训练,我们的 GAN 的生成器部分就可以用于根据 100 个长随机噪声量创建 MNIST 手写数字。 让我们开始吧!
# 判别器
......@@ -364,7 +364,7 @@ def discriminator(x):
# 生成器
现在我们创建生成器网络。 生成器的工作是将随机噪声的量作为输入,并从中生成输出图像。 在此示例中,我们再次使用全连接层,这些层最后将产生 784 个长向量的输出,我们可以对其进行整形以获得`28x28`的图像:
现在我们创建生成器网络。 生成器的工作是将随机噪声的量作为输入,并从中生成输出图像。 在此示例中,我们再次使用全连接层,这些层最后将产生 784 个长向量的输出,我们可以对其进行整形以获得`28x28`的图像:
```py
def generator(z):
......@@ -385,7 +385,7 @@ def generator(z):
![](img/1d24b043-c4b8-42f0-8f9c-8f2c7db22ec3.png)
在这里,`D`是我们的判别器,`G`是我们的生成器,`z`是输入到生成器的随机量,`x`是真实图像。 尽管我们在此处给出了 GAN 损失的总和,但实际上更容易分别考虑这两种优化。
在这里,`D`是我们的判别器,`G`是我们的生成器,`z`是输入到生成器的随机量,`x`是真实图像。 尽管我们在此处给出了 GAN 损失的总和,但实际上更容易分别考虑这两种优化。
为了训练 GAN,我们将在判别器和生成器之间交替进行梯度步骤更新。 在更新判别器时,我们要尝试使**最大化**判别器做出**正确选择**的概率。 在更新生成器时,我们想尝试使**最小化**判别器做出**正确选择**的可能性。
......@@ -399,11 +399,11 @@ def generator(z):
# 生成器损失
生成器想要欺骗判别器,换句话说,使判别器输出`q`用于生成的图像`G(z)`发生器损失只是施加到发生器结果的判别器输出的二项式交叉熵损失的负值。 请注意,由于生成器始终尝试生成“真实”图像,因此交叉熵损失可简化为:
生成器想要欺骗判别器,换句话说,使判别器输出`q`用于生成的图像`G(z)`生成器损失只是施加到生成器结果的判别器输出的二项式交叉熵损失的负值。 请注意,由于生成器始终尝试生成“真实”图像,因此交叉熵损失可简化为:
![](img/1761cebc-4eca-4a0f-81cf-9a128632ee81.png)
在这里,每个术语的含义如下:
在这里,每的含义如下:
* `m`:批量
* `D`:判别器
......@@ -418,7 +418,7 @@ def generator(z):
![](img/6afbb7c8-d706-49dc-b74b-f8d9f894e8d3.png)
此损失函数有两个术语
此损失函数有两
* 应用于判别器模型的二项式交叉熵产生了一些真实数据`x`
* 将二项式交叉熵应用于所生成数据`G(z)`的判别器模型结果
......@@ -452,7 +452,7 @@ def gan_loss(logits_real, logits_fake):
return discriminator_loss , generator_loss
```
您可能已经注意到,不可能同时最大化判别器损失和发生器损失。 这就是 GAN 的优点,因为在训练时,该模型有望达到某种平衡,在这种情况下,生成器必须生成真正高质量的图像,以欺骗判别器。
您可能已经注意到,不可能同时最大化判别器损失和生成器损失。 这就是 GAN 的优点,因为在训练时,该模型有望达到某种平衡,在这种情况下,生成器必须生成真正高质量的图像,以欺骗判别器。
TensorFlow 仅允许其优化器最小化而不是最大化。 结果,我们实际上采用了前面所述的损失函数的负值,这意味着我们从最大化损失变为最小化损失。 不过,我们无需执行任何其他操作,因为`tf.nn.sigmoid_cross_entropy_with_logits()`会为我们解决此问题。
......@@ -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)
```
接下来,创建一个随机噪声量; 这可以通过`tf.random_uniform`完成。 这被馈送到生成器网络以创建一批生成的图像:
接下来,创建一个随机噪声量; 这可以通过`tf.random_uniform`完成。 这被馈送到生成器网络以创建一批生成的图像:
```py
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'
generator_train_step = generator_solver.minimize(generator_loss , var_list=generator_vars )
```
这些是训练 GAN 的主要步骤。 剩下的就是创建一个训练循环,遍历大量数据。 如果这样做,您应该能够像训练中那样输入任何随机噪声量,并生成图像。
这些是训练 GAN 的主要步骤。 剩下的就是创建一个训练循环,遍历大量数据。 如果这样做,您应该能够像训练中那样输入任何随机噪声量,并生成图像。
如下图所示,创建的图像开始类似于 MNIST 数字:
......
......@@ -17,7 +17,7 @@
研究表明,在 ImageNet 上训练的卷积网络权重中的特征提取优于常规特征提取方法,例如 SURF,可变形部分描述符(**DPD**),**直方图定向梯度****HOG**)和**词袋****BoW**)。 这意味着无论常规视觉表示如何工作,卷积特征都可以同样好地使用,唯一的缺点是更深的架构可能需要更长的时间来提取特征。
当在 ImageNet 上训练深层卷积神经网络时,第一层中的卷积过滤器的可视化(请参见下图)显示,他们学习了*低层*特征,类似于边检测过滤器,而卷积过滤器在最后一层学习*高级*功能,这些功能捕获特定于类的信息。 因此,如果我们在第一个池化层之后提取 ImageNet 的特征并将其嵌入 2D 空间(例如,使用 t-SNE),则可视化将显示数据中存在一些无中心状态,而如果在全连接层上执行相同操作,我们将注意到具有相同语义信息的数据被组织成簇。 这意味着网络可以在更高层次上很好地概括,并且有可能将这种知识转移到看不见的类别中。
当在 ImageNet 上训练深层卷积神经网络时,第一层中的卷积过滤器的可视化(请参见下图)显示,他们学习了*低层*特征,类似于边检测过滤器,而卷积过滤器在最后一层学习*高级*功能,这些功能捕获特定于类的信息。 因此,如果我们在第一个池化层之后提取 ImageNet 的特征并将其嵌入 2D 空间(例如,使用 t-SNE),则可视化将显示数据中存在一些无中心状态,而如果在全连接层上执行相同操作,我们将注意到具有相同语义信息的数据被组织成簇。 这意味着网络可以在更高层次上很好地概括,并且有可能将这种知识转移到看不见的类别中。
![](img/b7b186d6-7803-4517-add9-f559586b2576.png)
......@@ -40,7 +40,7 @@
深度学习的另一种不太常见的方式是自己预先训练模型。 当可用的预训练网络不适合解决特定问题时,通常会发生这种情况,我们必须自己设计网络架构。 显然,这需要更多的时间和精力来设计模型和准备数据集。 在某些情况下,用于进行网络预训练的数据集甚至可以是合成的,可以从计算机图形引擎(例如 3D Studio Max 或 Unity)或其他卷积神经网络(例如 GAN)生成。 可以对虚拟数据进行预训练的模型在真实数据上进行微调,并且可以与仅对真实数据进行训练的模型一起很好地工作。
例如,如果我们想区分猫和狗,而我们没有足够的数据,则可以从“模型动物园”下载在 ImageNet 上训练的网络,并使用除最后一层以外的所有层的权重。 最后一层必须调整为具有与班级数量相同的大小,在本例中为两个,并且权重需要重新初始化和训练。 这样,通过将这些层的学习率设置为零或非常小的值(请参见下图),我们将冻结那些不需训练的层。 如果有更大的数据集,我们可以训练最后三个全连接层。 有时,预训练网络只能用于初始化权重,然后再进行正常训练。
例如,如果我们想区分猫和狗,而我们没有足够的数据,则可以从“模型动物园”下载在 ImageNet 上训练的网络,并使用除最后一层以外的所有层的权重。 最后一层必须调整为具有与数量相同的大小,在本例中为两个,并且权重需要重新初始化和训练。 这样,通过将这些层的学习率设置为零或非常小的值(请参见下图),我们将冻结那些不需训练的层。 如果有更大的数据集,我们可以训练最后三个全连接层。 有时,预训练网络只能用于初始化权重,然后再进行正常训练。
迁移学习之所以有效,是因为在初始层计算出的特征更通用并且看起来很相似。 在顶层提取的特征对于我们要解决的问题变得更加具体。
......@@ -58,7 +58,7 @@
# 没有解码器的自编码器
包含两个卷积层和一个完全连接层的编码器(不带解码器部分的自编码器)如下所示。 父自编码器在 MNIST 数据集上进行了训练。 因此,网络将大小为`28x28x1`的图像作为输入,并在潜在空间将其编码为 10 维量,每个类别一维:
包含两个卷积层和一个完全连接层的编码器(不带解码器部分的自编码器)如下所示。 父自编码器在 MNIST 数据集上进行了训练。 因此,网络将大小为`28x28x1`的图像作为输入,并在潜在空间将其编码为 10 维量,每个类别一维:
```py
# 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"
saver = tf.train.Saver()
```
然后,在使用`init = tf.global_variables_initializer()`初始化图并创建会话之后,我们可以使用`saver_load_autoencoder`从检查点恢复卷积层,如下所示:
然后,在使用`init = tf.global_variables_initializer()`初始化图并创建会话之后,我们可以使用`saver_load_autoencoder`从检查点恢复卷积层,如下所示:
```py
# Restore only the weights (From AutoEncoder)
......
......@@ -58,7 +58,7 @@
# 偏差和方差
如第 2 章,“深度学习和卷积神经网络”中所讨论的,方差和偏差分别表示过拟合和欠拟合。 我们可以使用训练集,开发集和测试集错误来诊断“欠拟合”和“过拟合”的问题。
如第 2 章,“深度学习和卷积神经网络”中所讨论的,方差和偏差分别表示过拟合和欠拟合。 我们可以使用训练集,开发集和测试集误差来诊断“欠拟合”和“过拟合”的问题。
考虑以下场景,其中我们的数据来自两个不同的分布,分别称为分布 1 和分布 2。分布 2 表示我们关心的目标应用。 问题是,我们如何在这种分布上定义训练,开发和测试集。
......@@ -72,7 +72,7 @@
![](img/43b92390-d3d8-4104-a0e8-0b0e03958557.png)
下表可以更好地说明这一点。 在这些示例中,我们假设在所有情况下的最佳/人为错误均最小,即 1%。 通常,深度学习模型的准确性与人类相似,因此将其作为比较可帮助您找到良好的架构。
下表可以更好地说明这一点。 在这些示例中,我们假设在所有情况下的最佳/人为误差均最小,即 1%。 通常,深度学习模型的准确性与人类相似,因此将其作为比较可帮助您找到良好的架构。
* 高偏差/欠拟合
......@@ -103,8 +103,8 @@
| | |
| --- | --- |
| 训练开发错误 | 2% |
| 开发错误 | 15% |
| 训练开发误差 | 2% |
| 开发误差 | 15% |
当模型很好地适合来自与训练集相同分布的开发集,并且对来自不同分布的开发集表现不佳时,这会导致数据不匹配问题,如本章前面所述。
......@@ -277,13 +277,13 @@ NPV: TN / (TN + FN) = 0.99
# 代码结构最佳实践
在前面的章节中,我们将张量流图封装到一个类中,而无需进一步讨论。 这个想法本身已经是很好的编码实践。 有一个班级负责构建图形并仅公开对使用模型有用的东西(即输入/输出)是一种很好的编程习惯,可以节省大量时间。
在前面的章节中,我们将张量流图封装到一个类中,而无需进一步讨论。 这个想法本身已经是很好的编码实践。 有一个类负责构建图并仅公开对使用模型有用的东西(即输入/输出)是一种很好的编程习惯,可以节省大量时间。
# 单例模式
使用设计模式来解决一些软件设计问题也是一种常见的做法。 python 中最简单,最有用的设计模式之一就是单例模式。 当您只想将一个类的实例强制仅用于一个对象时,可以使用它,因此,即使您在项目中的多个不同位置多次实例化该类,也将引用同一个对象。 在我们的情况下,如果我们要求 TensorFlow 创建具有相同名称的多个节点或图形,则会引发错误。 因此,我们在创建图形时使用单例模式,以避免生成两次。
使用设计模式来解决一些软件设计问题也是一种常见的做法。 python 中最简单,最有用的设计模式之一就是单例模式。 当您只想将一个类的实例强制仅用于一个对象时,可以使用它,因此,即使您在项目中的多个不同位置多次实例化该类,也将引用同一个对象。 在我们的情况下,如果我们要求 TensorFlow 创建具有相同名称的多个节点或图,则会引发错误。 因此,我们在创建图使用单例模式,以避免生成两次。
在下面的示例中,我们总结了一个简单的分类模型,同时还确保不会多次构建图(也称为单例模式)。
在下面的示例中,我们总结了一个简单的分类模型,同时还确保不会多次构建图(也称为单例模式)。
注意`__new__`类方法的定义。 在 Python 中,当我们创建一个类的新实例时,将调用`__new__`
......@@ -348,10 +348,10 @@ class CAE_CNN_Encoder(object):
1. 使用内核大小为`3x3`的卷积层。 就参数和计算而言,较大的内核更昂贵。 最重要的是,如我们在前面的章节中所看到的,您可以堆叠卷积层以产生更大的感受域,并受益于更多的非线性激活。
2. 第一层卷积通常应至少具有 32 个过滤器。 这样,更深的层不受第一层提取的特征数量的限制。
3. 尽可能避免使用池化层。 相反,请使用步长为 2 的卷积层。这将像池化那样对输入进行下采样,但它不会像池化那样丢弃宝贵的信息。 同样,使用跨步卷积就像将卷积和合并在一层中一样。
4. 减小要素地图的空间大小时,应增加使用的过滤器数量,以免丢失过多信息。 在深度网络中,请避免在第一层中过快减小空间大小。
4. 减小特征图的空间大小时,应增加使用的过滤器数量,以免丢失过多信息。 在深度网络中,请避免在第一层中过快减小空间大小。
5. 请遵循本章中有关从小规模开始网络设计,然后逐渐增加复杂性的建议,以避免出现过大的问题。
6. 使用批量规范化。 确实有助于训练您的网络!
7. 随着您对网络的深入了解,逐渐减小要素地图的空间大小。
7. 随着您对网络的深入了解,逐渐减小特征图的空间大小。
8. 最小化 FC 层的数量(在最后一层之前使用丢弃)。 仅在最终需要连接某些标量特征时才使用 FC。 (您甚至可以通过在输入通道上进行编码来避免这种情况)
9. 如果您需要较大的感受域(对象大小接近总图像大小的检测或分类),请尝试对每层使用具有指数膨胀因子的膨胀卷积。 这样,您将在保持少量参数的同时非常迅速地扩大接收范围。
......
......@@ -70,7 +70,7 @@ for index in range(len(labels)):
必须使用`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)
# 映射转换的并行调用
默认情况下,您在数据集上调用的任何地图转换都仅作用于数据集的单个元素,并且将按顺序处理元素。 要加快速度并使用所有 CPU 功能,最简单的方法是将`num_parallel_calls`参数设置为可用的 CPU 内核数。 这样,我们就不会浪费任何可用的 CPU 能力。 但是,警告您不要将其设置为高于可用内核的数量,因为由于调度效率低下,这实际上可能会降低性能。
默认情况下,您在数据集上调用的任何映射转换都仅作用于数据集的单个元素,并且将按顺序处理元素。 要加快速度并使用所有 CPU 功能,最简单的方法是将`num_parallel_calls`参数设置为可用的 CPU 内核数。 这样,我们就不会浪费任何可用的 CPU 能力。 但是,警告您不要将其设置为高于可用内核的数量,因为由于调度效率低下,这实际上可能会降低性能。
您想要对数据进行的任何转换(例如数据扩充)也可以编写为函数,然后像以前一样传递给`map`方法,以将其应用于数据集。 例如,请注意以下代码:
......@@ -164,9 +164,9 @@ train_dataset= train_dataset.batch(128).prefetch(1)
# 追踪图
TensorFlow 提供了一种很好的方式来分析并查看整个图形通过其时间轴跟踪工具执行所需的时间。 这是查看图表的哪些部分正在减慢训练速度并发现数据流水线中任何低效率的好工具。
TensorFlow 提供了一种很好的方式来分析并查看整个图通过其时间轴跟踪工具执行所需的时间。 这是查看图的哪些部分正在减慢训练速度并发现数据流水线中任何低效率的好工具。
我们将从为您提供如何跟踪图形的示例开始。 这非常简单:您只需在常规代码中添加几行,就会生成一个 JSON 文件,我们可以将该文件加载到 Google Chrome 浏览器中,以查看图形执行的所有时间:
我们将从为您提供如何跟踪图的示例开始。 这非常简单:您只需在常规代码中添加几行,就会生成一个 JSON 文件,我们可以将该文件加载到 Google Chrome 浏览器中,以查看图执行的所有时间:
```py
from tensorflow.python.client import timeline
......@@ -186,11 +186,11 @@ with tf.Session() as sess:
file.write(chome_readable_trace)
```
在此代码中,我们导入 TensorFlow 时间轴模块,然后设置两个选项以启用图跟踪并将其提供给`Session.run()`。 运行图之后,我们创建`Timeline`对象,该对象将包含对图执行进行性能分析的结果。 然后,我们将其转换为 Chrome 跟踪格式,最后将其写入 JSON 文件。
在此代码中,我们导入 TensorFlow 时间轴模块,然后设置两个选项以启用图跟踪并将其提供给`Session.run()`。 运行图之后,我们创建`Timeline`对象,该对象将包含对图执行进行性能分析的结果。 然后,我们将其转换为 Chrome 跟踪格式,最后将其写入 JSON 文件。
要查看结果,您需要打开一个新的 Chrome 窗口。 然后,在地址栏中输入`chrome://tracing`并按`Enter`。 左上角将有一个加载按钮。 使用它来加载刚刚保存的 JSON 文件。
现在将显示跟踪图形的结果。 查看此内容将告诉您图形的每个部分执行所需的时间。 您应该特别注意存在大块空白的地方。 这些空白表示设备(例如您的 GPU)正坐在那里等待数据,以便它们可以执行计算。 您应该尝试通过优化数据馈送方式来消除这些问题。
现在将显示跟踪图的结果。 查看此内容将告诉您图的每个部分执行所需的时间。 您应该特别注意存在大块空白的地方。 这些空白表示设备(例如您的 GPU)正坐在那里等待数据,以便它们可以执行计算。 您应该尝试通过优化数据馈送方式来消除这些问题。
但是请注意,您的流水线可能已完全优化,但是您没有 CPU 周期来足够快地处理流水线。 检查您的 CPU 使用情况,看看是否是这种情况。
......@@ -216,9 +216,9 @@ with tf.Session() as sess:
# 同步/异步 SGD
如前所述,在数据并行性中,每个模型都会从训练集中获取一些数据并计算自己的梯度,但是考虑到每个工作人员都将拥有相同的模型,我们需要在更新模型之前以某种方式进行同步。
如前所述,在数据并行性中,每个模型都会从训练集中获取一些数据并计算自己的梯度,但是考虑到每个工作都将拥有相同的模型,我们需要在更新模型之前以某种方式进行同步。
在同步 SGD 中,所有工作人员都会计算一个梯度并等待计算所有梯度,然后将模型更新并再次分发给所有工作人员
在同步 SGD 中,所有工作器都会计算一个梯度并等待计算所有梯度,然后将模型更新并再次分发给所有工作器
![](img/f8da6b99-2533-4df2-87c9-2ba3c2165494.png)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册