提交 62f5ead4 编写于 作者: W wizardforcel

2020-09-02 08:20:29

上级 18eddf38
# 前言
*用于计算机视觉的深度学习*是一本书,适合希望学习基于深度学习的计算机视觉技术用于各种应用的读者。 本书将为读者提供开发基于计算机视觉的产品的工具和技术。 书中涵盖了许多遵循该理论的实际例子。
# 这本书是给谁的
读者想知道如何将深度学习应用于计算机视觉问题,例如分类,检测,检索,分割,生成,字幕和视频分类。 读者还希望了解如何在各种约束下(例如更少的数据,不平衡的类别和噪声)获得良好的准确性。 然后,读者还想知道如何在各种平台(AWS,Google Cloud,Raspberry Pi 和移动电话)上部署经过训练的模型。 读完本书后,读者应该能够开发出有关人员检测,面部识别,产品搜索,医学图像分割,图像生成,图像字幕,视频分类等问题的代码。
# 本书涵盖的内容
[第 1 章](../Text/01.html)*入门* 介绍了深度学习的基础知识,并使读者熟悉该词汇表。 读者将安装遵循其余各章所必需的软件包。
[第 2 章](../Text/02.html)*图像分类* 讨论图像分类问题,该问题将整个图像标记为图像。 读者将学习图像分类技术,并训练用于宠物分类的深度学习模型。 他们还将学习提高准确性的方法,并深入研究各种先进的体系结构。
[第 3 章](../Text/03.html)*图像检索* 涵盖了深层功能和图像检索。 读者将学习获得模型可视化,视觉特征,使用 TensorFlow 进行推理以及服务和使用视觉特征进行产品检索的各种方法。
[第 4 章](../Text/04.html)*对象检测* 讨论了检测图像中的对象。 读者将学习各种物体检测技术,并将其应用于行人检测。 本章将使用用于对象检测的 TensorFlow API。
[第 5 章](../Text/05.html)*语义分割* 涵盖了 像素级图像分割。 读者将获得有关分割技术的知识,并训练用于医学图像分割的模型。
[第 6 章](../Text/06.html)*相似性学习*讨论了关于相似性学习的。 读者将学习相似度匹配以及如何训练人脸识别模型。 示出了训练面部地标的模型。
[第 7 章](../Text/07.html)*图像字幕* 是关于生成或选择图像字幕的 。 读者将学习自然语言处理技术以及如何使用这些技术为图像生成字幕。
[第 8 章](../Text/08.html)*生成模型* 讨论了关于出于各种目的生成合成图像的问题。 读者将了解什么是生成模型,并将其用于图像生成应用,例如样式转换,训练数据等。
[第 9 章](../Text/09.html)*视频分类* 涵盖了用于视频数据的 计算机视觉技术。 读者将了解解决视频与图像问题之间的主要区别,并实施视频分类技术。
[第 10 章](../Text/10.html)*部署* , 讨论了深度学习模型的部署步骤。 读者将学习如何在各种平台上部署训练有素的模型并优化速度。
# 充分利用这本书
本书涵盖的示例可以在 Windows,Ubuntu 或 Mac 上运行。 涵盖了所有安装说明。 需要具备 Python 和机器学习的基础知识。 读取器最好具有 GPU 硬件,但这不是必需的。
# 使用约定
本书中使用了许多文本约定。
`CodeInText`:表示文本中的词,数据库表名称,文件夹名称,文件名,文件扩展名,路径名,伪 URL,用户输入和 Twitter 句柄。 这里是一个示例:“ 请注意,图形是用 `summary_writer`编写的。
代码块设置如下:
```py
merged_summary_operation = tf.summary.merge_all()
train_summary_writer = tf.summary.FileWriter('/tmp/train', session.graph)
test_summary_writer = tf.summary.FileWriter('/tmp/test')
```
任何命令行输入或输出的编写方式如下:
```py
wget http://www.robots.ox.ac.uk/~vgg/data/pets/daimg.tar.gz
wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz
```
**粗体**:表示您在屏幕上看到的新术语,重要单词或顺序。 例如,菜单或对话框中的单词会出现在这样的文本中。 这是一个示例:“完成后,通过单击操作|实例状态|终端 来终止实例。”
警告或重要提示如下所示。
提示和技巧如下所示。
# 入门
计算机视觉是理解或操纵图像和视频的科学。 计算机视觉具有许多应用,包括自动驾驶,工业检查和增强现实。 深度学习在计算机视觉中的使用可以分为多个类别:图像和视频中的分类,检测,分割和生成。 在本书中,您将学习如何为计算机视觉应用程序训练深度学习模型并将其部署在多个平台上。 我们将在本书中使用 **TensorFlow** ,这是一个用于深入学习的流行 python 库,用于示例。 在本章中,我们将介绍以下主题:
* 深度学习的基础知识和词汇
* 深度学习如何满足计算机视觉?
* 设置将用于本书所涵盖示例的开发环境
* 体验 TensorFlow 及其强大的工具,例如 TensorBoard 和 TensorFlow Serving
# 了解深度学习
计算机视觉作为一个领域有着悠久的历史。 随着深度学习的出现,计算机视觉已被证明可用于各种应用。 深度学习是来自**人工神经网络****ANN** )的技术的集合,这是机器学习的一个分支。 人工神经网络以人脑为模型。 有彼此链接的节点,它们彼此传递信息。 在以下各节中,我们将通过了解常用的基本术语来详细讨论深度学习的工作原理。
# 感知器
人工神经元或感知器接受多个输入并执行加权求和以产生输出。 感知器的重量是在训练过程中确定的,并基于训练数据。 以下是感知器的 图:
![](img/1e292483-bf0f-4474-9ee4-9f18966861b6.png)
如上图所示,对输入进行加权和求和。 然后,对于二进制分类问题,该总和然后通过单位步长函数传递。 感知器只能通过从示例中学习权重来学习简单功能。 学习权重的过程称为训练。 可以通过基于梯度的方法对感知器进行训练,这将在后面的部分中进行介绍。 感知器的输出可以通过 `activation` 函数或 `transfer` 函数传递,这将在下一部分中进行说明。
# 激活功能
`activation`函数使**神经网络**成为非线性。 激活功能决定是否应触发感知器。 在训练激活期间,功能在调整梯度中起着重要作用。 下一节所示的`activation`功能(如 S 型)会衰减较大幅度的值。 `activation`函数的这种非线性行为为学习复杂函数提供了深层网络。 `activation`的大多数功能是连续和微分功能,但整流单元为 0 除外。输入的每一个小变化,连续功能的输出都会有很小的变化。 微分函数在域中的每个点都有一个导数。
为了训练神经网络,功能必须是可微的。 以下是一些`activation`功能。
如果您不了解诸如连续性和可区分性之类的术语,请不要担心。 在各章中将变得更加清楚。
# 乙状结肠
Sigmoid 可以看作是平滑的阶跃函数,因此可以微分。 Sigmoid 可用于将任何值转换为概率,并可用于二进制分类。 S 型映射将输入映射到 0 到 1 范围内的值,如下图所示:
![](img/f171c6d5-67e5-4e9c-a168-be794c8e444b.png)
相对于 *X**Y* 值的变化将很小,因此,梯度将消失。 经过一番学习之后,变化可能很小。 在下一节中介绍的另一个称为`tanh`的激活函数是 S 型曲线的缩放比例版本,避免了梯度消失的问题。
# 双曲正切函数
双曲正切函数或 `tanh` 是 S 型曲线的缩放形式。 像乙状结肠一样,它是光滑且可区分的。 `tanh`将输入映射到-1 到 1 的值,如下图所示:
![](img/623bcab3-b4f7-4ce7-b817-a3f1bad2944d.png)
梯度比 S 形更稳定,因此减少了消失的梯度问题。 乙状结肠和`tanh`一直在发射,这使 ANN 变得很沉重。 下一节中介绍的**整流线性单元****ReLU** )激活功能通过不触发而避免了这种陷阱。
# 整流线性单位(ReLU)
ReLu 可以让大量数字通过。 这会使一些神经元陈旧,并且它们不会发射。 这增加了稀疏性,因此很好。 `ReLU`将输入 *x* 映射到最大值( *0**x* ),即,它们将负输入映射为 0,而正输入为 输出无任何变化,如下图所示:
![](img/f869a450-2d05-4d78-9ad6-8cc941adcfff.png)
由于 ReLU 不会一直触发,因此可以更快地进行训练。 由于功能简单,因此在计算上最便宜。 选择`activation`功能在很大程度上取决于应用程序。 尽管如此,ReLU 在许多问题上都运行良好。 在下一节中,您将学习如何将几个感知器堆叠在一起,这些感知器可以学习比感知器更复杂的功能。
# 人工神经网络(ANN)
ANN 是感知器和[H​​TG0]功能的集合。 感知器连接形成隐藏的层或单元。 隐藏的单元形成了将低层空间中的输入层映射到输出层的非线性基础,这也称为人工神经网络。 ANN 是从输入到输出的映射。 该图是通过将输入与偏差进行加权相加来计算的。 权重和偏差值以及体系结构称为`model`
训练过程确定这些权重和偏差的值。 在训练开始时,使用随机值初始化模型值。 通过使用损失函数将误差与基本事实进行对比来计算误差。 根据计算出的损耗,在每一步调整权重。 如果无法进一步减少错误,则停止训练。 训练过程会在训练过程中学习功能。 这些特征比原始图像更好地表示。 以下是人工神经网络或多层感知器的示意图:
![](img/386212ca-7e62-482c-b6b3-d0d30426ca19.png)
*x* 的多个输入通过感知器的隐藏层传递并求和。 通用逼近定理表明,这样的神经网络可以逼近任何函数。 隐藏层也可以称为密集层。 每个层都可以具有上一节中描述的`activation`功能之一。 可以根据问题选择隐藏层和感知器的数量。 还有更多的事情可以使此多层感知器适用于多类分类问题。 一个多类别的分类问题试图区分十多个类别。 我们将在以下各节中探讨这些术语。
# 一键编码
一次热编码是在出现分类问题时表示目标变量或类的一种方式。 目标变量可以从字符串标签转换为一键编码的向量。 一个热门向量在目标类别的索引处填充有 *1* ,但在其他所有地方填充有 *0* 。 例如,如果目标类别是猫和狗,则可以用[ *1**0* ]和[ *0**1* ], 分别。 对于 1,000 个类别,一热向量的大小为 1,000 整数,全为零,但 *1* 。 它不假设目标变量的相似性。 通过在下一节中说明的一键编码和 softmax 的组合,可以在 ANN 中实现多类分类。
# 软最大
Softmax 是一种强制神经网络输出 1 之和的方法。因此,`softmax`函数的输出值可以视为概率分布的一部分。 这在多类分类问题中很有用。 Softmax 是一种`activation`函数,其特征是输出求和为 1。通过将输出除以所有其他值的总和,可以将输出转换为概率。 欧几里德距离可以在 softmax 概率和一键编码之间进行计算,以进行优化。 但是下一部分将说明的交叉熵是一个更好的成本函数,可以进行优化。
# 交叉熵
交叉熵比较 softmax 和一键编码输出之间的距离。 交叉熵是一种损失函数,必须将其误差降至最低。 神经网络估计每个类别给定数据的概率。 必须将概率最大化到正确的目标标签。 交叉熵是负对数概率的总和。 对数值用于数值稳定性。 最大化一个功能等同于最小化相同功能的负数。 在下一节中,我们将看到以下正则化方法,以避免 ANN 的过拟合:
* 退出
* 批量标准化
* L1 和 L2 归一化
# 退出
辍学是规整神经网络以避免 ANN 过度拟合的有效方法。 在训练期间,辍学层通过随机删除隐藏的单元来破坏神经网络,如下图所示:
![](img/9eae1f1c-0308-4516-9cda-3e9531220150.png)
请注意如何随机训练神经元。 辍学也是组合多个神经网络的有效方法。 对于每个训练案例,我们随机选择一些隐藏的单元,以便最终为每个案例使用不同的体系结构。 这是装袋和模型平均的极端情况。 推断期间不应使用辍学层,因为没有必要。
# 批量标准化
批处理规范化或批处理规范可提高神经网络训练的稳定性和性能。 它将平均值为零且标准偏差为 1 的图层的输出归一化。这减少了过拟合,并使网络训练更快。 这对于训练复杂的神经网络非常有用。
# L1 和 L2 正则化
L1 惩罚权重的绝对值,并趋于使权重为零。 L2 惩罚权重的平方值,并且在训练过程中倾向于使权重更小。 两个正则化均假设权重较小的模型更好。
# 训练神经网络
训练 ANN 非常棘手,因为它包含多个要优化的参数。 权重的更新过程称为反向传播。 最小化错误的过程称为优化。 我们将在下一节中详细介绍这两个方面。
# 反向传播
反向传播算法通常用于训练人工神经网络。 权重根据计算的误差从后向更新,如下图所示:
![](img/91e3b72c-6258-41b8-a8dc-3a471bd1da38.png)
在计算了误差之后,可以使用梯度下降来计算权重更新,如下一节中所述。
# 梯度下降
梯度下降算法执行多维优化。 目标是达到全球最高水平。 梯度下降是许多机器学习模型中使用的一种流行的优化技术。 它用于改善或优化模型预测。 梯度下降的一种实现称为**随机梯度下降****SGD** ),在神经网络中正变得越来越流行(在下一节中说明)。 优化涉及计算误差值并更改权重以实现最小误差。 找到最小值的方向是`loss`函数的梯度的负值。 下图定性显示了梯度下降过程:
![](img/13375e63-ff4f-46e6-a64b-4f01435f18e1.png)
学习速度决定了每个步骤应该多大。 注意,具有非线性激活的 ANN 将具有局部最小值。 SGD 在实践中更好地用于优化非凸成本函数。
# 随机梯度下降
SGD 与梯度下降相同,区别在于 SGD 每次仅用于部分数据训练。 该参数称为小批量大小。 从理论上讲,甚至可以使用一个示例进行训练。 在实践中,最好尝试各种数字。 在下一部分中,我们将讨论比标准 ANN 在图像数据上更有效的卷积神经网络。
访问 [https://yihui.name/animation/example/grad-desc/](https://yihui.name/animation/example/grad-desc/) ,可以很好地看到凸面和非凸面的梯度下降情况。
# 在 TensorFlow 游乐场玩
TensorFlow 游乐场是神经网络的交互式可视化。 访问 [http://playground.tensorflow.org/](http://playground.tensorflow.org/) ,方法是通过更改参数来查看前面提到的术语如何协同工作。 这是操场的屏幕截图:
![](img/2a57f080-1ddb-4c9d-b3d0-201bc792caad.png)
TensorFlow 游乐场中的仪表板
如前所示,读者可以更改学习率,激活,正则化,隐藏单元和层,以了解其如何影响训练过程。 您可以花费一些时间来调整参数,以直观了解神经网络如何处理各种数据。
# 卷积神经网络
**卷积神经网络****CNN** )与前面各节中描述的神经网络相似。 CNN 具有权重,偏差和通过非线性激活产生的输出。 规则的神经网络接受输入,并且神经元完全连接到下一层。 同一层中的神经元不共享任何连接。 如果我们对图像使用常规的神经网络,由于神经元数量众多,它们的大小将非常大,从而导致过拟合。 我们不能将其用于图像,因为图像尺寸较大。 增加模型大小,因为它需要大量的神经元。 可以将图像视为具有高度,宽度和深度尺寸的体积。 深度是图像的通道,它是红色,蓝色和绿色。 CNN 的神经元以体积方式排列,以利用体积。 每个图层都将输入体积转换为输出体积,如下图所示:
![](img/42b70af2-d682-4f56-8b6c-fe90ac4a02c9.jpeg)
卷积神经网络滤波器通过变换进行编码。 学到的滤镜可以检测图像中的特征或图案。 层越深,图案越抽象。 一些分析表明,这些层具有检测边缘,角和图案的能力。 CNN 层中的可学习参数小于上一节中描述的密集层。
# 核心
内核是用于对图像进行卷积的参数卷积层。 卷积操作如下图所示:
![](img/0b5c0e8a-edbf-42ed-b858-2788b7e281b9.png)
内核有两个参数,称为步幅和大小。 大小可以是矩形的任何尺寸。 步幅是每次移动的像素数。 长度为 1 的步幅产生几乎相同大小的图像,长度为 2 的步幅产生一半大小。 填充图像将有助于获得相同的输入大小。
# 最大池
池化层位于卷积层之间。 合并层通过采样减小了跨层图像的大小。 通过在窗口中选择最大值来完成采样。 窗口中的平均池平均值。 池化还可以作为一种正则化技术来避免过度拟合。 在功能的所有通道上进行池化。 合并也可以进行各种步骤。
窗口的大小是 CNN 接收场的量度。 下图显示了最大池化的示例:
![](img/9fb61e22-8dbd-46cc-87a2-c325197bf504.png)
CNN 是任何计算机视觉深度学习模型中最重要的组成部分。 毫不夸张地说,没有 CNN,任何计算机都不可能拥有视觉。 在下一部分中,我们将讨论几个可用于一些应用程序的高级层。
访问 [https://www.youtube.com/watch?v=jajksuQW4mc](https://www.youtube.com/watch?v=jajksuQW4mc) ,以获取有关 CNN 和最大池操作的出色可视化。
# 递归神经网络(RNN)
**递归神经网络****RNN** )可以对顺序信息进行建模。 他们不假定数据点密集。 它们从一系列序列数据的先前数据的输出执行相同的任务。 这也可以被视为记忆。 RNN 无法记住更长的序列或更长的时间。 在培训过程中将其展开,如下图所示:
![](img/74621d85-d8ff-4532-a793-8eda53ef7651.png)
如上图所示,该步骤每次都展开和训练。 在反向传播期间,梯度会随着时间消失。 为了克服此问题,可以使用较长的短期记忆来记忆较长的时间。
# 长短期记忆(LSTM)
**长短期存储器****LSTM** )可以存储较长时间的信息,因此,它可以有效地捕获长期效率。 下图说明了如何设计 LSTM 单元:
![](img/e166af85-c2c6-4046-bf30-4984795eff8f.png)
LSTM 有几个门:忘记,输入和输出。 忘记门保持信息先前的状态。 输入门使用输入更新当前状态。 输出门决定将信息传递到下一个状态。 忘记和保留重要内容的能力使 LSTM 可以在更长的时间内记住。 您已经学习了将在整本书中使用的深度学习词汇。 在下一节中,我们将了解如何在计算机视觉的背景下使用深度学习。
# 用于计算机视觉的深度学习
计算机视觉在计算机上实现了人类视觉的特性。 计算机可以是智能手机,无人机,闭路电视,MRI 扫描仪等形式,并带有各种感知传感器。 传感器产生数字形式的图像,必须由计算机解释。 下一部分将说明这种解释或智能的基本构成部分。 使用深度学习技术可以有效解决计算机视觉中出现的各种问题。
# 分类
图像分类是充满信心地用对象或概念标记整个图像的任务。 这些应用程序包括给定人脸图像的性别分类,识别宠物的类型,为照片添加标签等。 以下是此类分类任务的输出:
![](img/0ac5cf28-7641-47ed-a19f-af886508aae7.png)
[第 2 章](../Text/02.html)*图像分类*详细介绍了可用于分类任务的方法,在[第 3 章](../Text/03.html)[HTG8 图像检索,我们使用分类模型对深度学习模型进行可视化并检索相似的图像。
# 检测或定位和细分
检测或定位是一项在图像中找到对象并使用边界框定位该对象的任务。 这项任务有许多应用,例如为自动驾驶汽车寻找行人和招牌。 下图是检测的示意图:
![](img/eebafaa9-de29-4acc-aa17-e39010013809.png)
分割是进行像素分类的任务。 这样可以很好地分离对象。 这对于处理医学图像和卫星图像很有用。 更多示例和说明可以在[第 4 章](../Text/04.html),对象检测和[第 5 章](../Text/05.html)*图像分割*中找到。
# 相似学习
相似性学习是学习两个图像如何相似的过程。 可以基于语义含义在两个图像之间计算分数,如下图所示:
![](img/55f91a2f-3104-4dee-8feb-81aebcea95c3.png)
从发现相似产品到执行面部识别,此方法有多种应用。 [第 6 章](../Text/06.html)*相似性学习*处理相似性学习技术。
# 图片字幕
图像标题是用文字描述图像的任务,如下所示:
![](img/407ec761-30e5-41da-8b72-69b17212196c.png)
经 Vinyals 等人许可复制。
[第 8 章](../Text/07.html)*图像字幕*详细介绍了图像字幕。 这是将**自然语言处理****NLP** )和计算机视觉技术相结合的独特情况。
# 生成模型
生成模型在生成图像时非常有趣。 以下是样式转换应用程序的示例,其中使用该图像的内容和其他图像的样式生成图像:
![](img/1a581deb-0956-44e2-bf7c-fc63e591160a.jpeg)
经 Gatys 等人许可复制。
可以出于其他目的生成图像,例如新的训练示例,超分辨率图像等。 [第 7 章](../Text/08.html)*生成模型*详细介绍了生成模型。
# 视频分析
与以前的情况相比,视频分析处理的是整个视频,而不是图像。 它具有多种应用,例如运动跟踪,入侵检测和监视摄像机。 [第 9 章](../Text/09.html)*视频分类*处理特定于视频的应用程序。 时间数据的新维度带来了许多有趣的应用。 在下一节中,我们将看到如何设置开发环境。
# 开发环境设置
在本节中,我们将设置编程环境,该环境对于遵循本书其余部分中的示例非常有用。 读者可以选择以下操作系统:
* **开发操作系统****OS** ),例如 Mac,Ubuntu 或 Windows
* **部署操作系统**(例如 Mac,Windows,Android,iO 或 Ubuntu)安装在云平台(例如 **Amazon Web Services****AWS** ), **云平台****GCP** ),Azure,Tegra,Raspberry Pi
无论使用哪种平台,本书中开发的所有代码均应运行无任何问题。 在本章中,我们将介绍开发环境的安装过程。 在[第 10 章](../Text/10.html)*部署*中,我们将介绍在各种其他环境(例如 AWS,GCP,Azure,Tegra 和 Raspberry Pi)中的部署安装。
# 硬件和操作系统-OS
对于开发环境,您需要具有很多计算能力,因为培训在计算上非常昂贵。 Mac 用户相当受限于计算能力。 Windows 和 Ubuntu 用户可以使用更多处理器和**通用图形处理单元****GP-GPU** ),来增强其开发环境。 下一节将对此进行说明。
# 通用-图形处理单元(GP-GPU)
GP-GPU 是一种特殊的硬件,可加快训练深度学习模型的训练过程。 NVIDIA 公司提供的 GP-GPU 由于具有完善的软件和社区支持,因此在深度学习培训和部署中非常受欢迎。 读者可以设置带有此类 GP-GPU 的机器以进行更快的培训。 有很多选择,读者可以根据预算选择一个。 选择与 GP-GPU 功率相对应的 RAM,CPU 和硬盘也很重要。 安装硬件后,必须安装以下驱动程序和库。 使用 Mac 或不使用 GP-GPU 的 Windows / Ubuntu 的阅读器可以跳过安装。
以下是设置环境所需的库:
* **计算机统一设备体系结构****CUDA**
* **CUDA 深度神经网络****CUDNN**
# 计算机统一设备架构-CUDA
CUDA 是 NVIDIA 使用 GPU 的并行特性提供的 API 层。 安装此驱动程序后,还将安装硬件驱动程序。 首先,从 NVIDIA 门户网站下载`CUDA`库: [https://developer.nvidia.com/cuda-downloads](https://developer.nvidia.com/cuda-downloads)
按照页面上的说明进行操作,下载驱动程序,然后按照安装说明进行操作。 这是 Ubuntu CUDA 的屏幕截图和安装说明:
![](img/e042367c-385d-481f-a9f0-7b9e64db394f.png)
这些命令将安装所需的`cuda-drivers`和其他 CUDA API。
您可以通过在命令提示符下键入`nvidia-smi`来检查驱动程序是否正确安装。
# CUDA 深度神经网络-CUDNN
`CUDNN`库为深度学习算法提供了原语。 由于此软件包由 NVIDIA 提供,因此对其硬件进行了高度优化,并且运行速度更快。 该软件包提供了几种用于深度学习的标准例程。 著名的深度学习库(例如`tensorflow``caffe`等)使用这些软件包。 在下一节中,将提供安装`CUDNN`的说明。 您可以从 NVIDIA 门户网站 [https://developer.nvidia.com/rdp/cudnn-download](https://developer.nvidia.com/rdp/cudnn-download) 下载`CUDNN`
用户帐户是必需的(免费注册)。
将相关文件复制到`CUDA`文件夹,使其更快地在 GPU 上运行。 我们不会直接使用`CUDA``CUDNN`库。 Tensorflow 使用这些来优化例程在 GP-GPU 上工作。
# 安装软件包
训练有素的深度学习模型需要几个库。 我们将安装以下库,并查看在竞争软件包中选择以下软件包的原因:
* Python 和其他依赖项
* OpenCV 的
* TensorFlow
*
# 蟒蛇
Python 是任何数据科学应用程序的实际选择。 它拥有最大的社区和图书馆支持生态系统。 用于 Python 的 TensorFlow API 是最完整的,因此,Python 是首选的自然语言。 Python 有两个版本-Python2.x 和 Python3.x。 在本书中,我们将讨论 Python3.x。 这种选择有几个原因:
* 到 2020 年,Python 2.x 的开发将停止,因此,Python3.x 是 Python 的未来
* Python 3.x 避免了原始实现中的许多设计缺陷
* 与普遍的看法相反,Python3.x 具有与 Python 2.x 一样多的数据科学支持库。
在本书中,我们将使用 Python 版本 3。 转到 [https://www.python.org/downloads/](https://www.python.org/downloads/) ,然后根据操作系统下载版本 3。 按照下载链接中给出的步骤安装 Python。 安装 Python 后,必须安装 **pip3** ,以方便安装 Python 软件包。 然后通过输入以下命令安装几个 Python 软件包,以便以后可以安装`OpenCV``tensorflow`
```py
sudo pip3 install numpy scipy scikit-learn pillow h5py
```
先前安装的软件包的说明如下:
* `numpy` 是高度优化的数值计算程序包。 它具有强大的 N 维封装数组对象,并且`numpy`库的矩阵运算针对速度进行了高度优化。 图像可以存储为 3 维`numpy`对象。
* `scipy` 有一些用于科学和工程计算的例程。 在本书的后面,我们将使用一些优化程序包。
* `scikit-learn` 是一个机器学习库,我们将使用其中的许多辅助函数。
* `Ppillow`对于图像加载和基本操作很有用。
* `H5py`程序包是 HDF5 二进制数据格式的 Pythonic 接口。 这是存储使用 Keras 训练的模型的格式。
# 开放式计算机视觉-OpenCV
`OpenCV`是著名的计算机视觉库。 该库中有许多可用的图像处理例程,这些例程很有用。 以下是在 Ubuntu 中安装 OpenCV 的步骤。
```py
sudo apt-get install python-opencv
```
对于其他操作系统,可以在 [https://opencv.org/](https://opencv.org/) 找到类似的步骤。 它是跨平台的,针对 CPU 密集型应用程序进行了优化。 它具有多种编程语言的接口,并且受 Windows,Ubuntu 和 Mac 支持。
# TensorFlow 库
`tensorflow`是一个用于开发和部署深度学习模型的开源库。 TensorFlow 使用计算图进行数据流和数值计算。 换句话说,数据或张量流经图形,因此名称为`tensorflow`。 该图具有可进行任何数值计算的节点,因此适用于深度学习操作。 它为各种平台和硬件提供了一个 API。 TensorFlow 在后端处理扩展和优化的所有复杂性。 它最初是为在 Google 上进行研究而开发的。 它是最著名的深度学习库,拥有大型社区,并提供用于生产中的可视化和部署的工具。
# 安装 TensorFlow
使用以下命令,使用 pip3 为 CPU 安装`tensorflow`
```py
sudo pip3 install tensorflow
```
如果您使用的是 GPU 硬件,并且已安装`CUDA``CUDNN`,请使用以下命令安装`tensorflow`的 GPU 版本:
```py
sudo pip3 install tensorflow-gpu
```
现在`tensorflow`已安装并可以使用。 我们将尝试一些示例以了解 TensorFlow 的工作原理。
# TensorFlow 示例打印 Hello,TensorFlow
我们将直接在 Python Shell 中使用 TensorFlow 进行示例。 在此示例中,我们将使用 TensorFlow 打印您好,TensorFlow 。
1. 通过在命令提示符下键入以下命令,从 shell 调用 Python:
```py
python3
```
2. 通过输入以下命令导入`tensorflow`库:
```py
>>> import tensorflow as tf
```
3. 接下来,使用字符串`Hello, TensorFlow`定义一个常量。 这与通常的 Python 赋值操作不同,因为该值尚未初始化:
```py
>>> hello = tf.constant('Hello, TensorFlow!')
```
4. 创建一个会话以初始化计算图,并为该会话命名:
```py
>>> session = tf.Session()
```
可以使用变量`hello`作为参数运行会话。
5. 现在,该图执行并返回打印的特定变量:
```py
>>> print(session.run(hello))
```
它应该打印以下内容:
```py
Hello, TensorFlow!
```
让我们再看一个示例,以了解会话和图形的工作方式。
访问 [https://github.com/rajacheers/DeepLearningForComputerVision](https://github.com/rajacheers/DeepLearningForComputerVision) 获取本书中所有示例的代码。 该代码将根据章节进行组织。 您可以提出问题并在存储库中获得帮助。
# TensorFlow 示例,将两个数字相加
这是如何使用 TensorFlow 将两个数字相加的另一个简单示例。
1. 创建一个 Python 文件并使用以下代码导入`tensorflow`
```py
import tensorflow as tf
```
对于所有后面的示例,前面的导入都是必需的。 假定读者已经为所有示例导入了库。 可以通过以下方式定义`placeholder`。 占位符在分配时不会加载。 在此,将变量定义为类型为`float32``placeholder``placeholder`是一个空声明,并且在运行会话时可以采用值。
2. 现在我们定义一个`placeholder`,如以下代码所示:
```py
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
```
3. 现在,可以将占位符的求和运算定义为通常的加法运算。 在这里,不执行操作,而只是使用以下代码定义:
```py
z = x + y
```
4. 可以如前面的示例所示创建会话。 如下所示定义时,该图即可执行计算:
```py
session = tf.Session()
```
5. 以字典格式定义`placeholder`的值:
```py
values = {x: 5.0, y: 4.0}
```
6. 使用变量`c`和值运行会话。 该图将值提供给适当的占位符,并为变量`c`取回值:
```py
result = session.run([z], values) print(result)
```
作为添加结果,该程序应打印[ 9.0 ]。
可以理解,这不是将两个数字相加的最佳方法。 该示例旨在了解 TensorFlow 中如何定义张量和操作。 想象一下使用一万亿个数字并将它们相加会多么困难。 TensorFlow 可以使用相同的 API 轻松实现这种扩展。 在下一节中,我们将看到如何安装和使用 TensorBoard 和 TensorFlow 服务。
# 张量板
TensorBoard 是一套可视化工具,用于使用 TensorFlow 训练基于深度学习的模型。 可以在 TensorBoard 中可视化以下数据:
* **图**:计算图,设备位置和张量详细信息
* **标量**:指标,例如损耗,迭代精度
* **图像**:用于查看带有相应标签的图像
* **音频**:用于收听训练或生成的音频
* **分布**:用于查看某些标量的分布
* **直方图**:包括权重和偏差的直方图
* **投影仪**:帮助可视化 3 维空间中的数据
* **文本**:打印训练文本数据
* **配置文件**:查看用于培训的硬件资源
Tensorboard 与 TensorFlow 一起安装。 转到 python3 提示符并输入以下命令(类似于上一个示例)以开始使用 Tensorboard:
```py
x = tf.placeholder(tf.float32, name='x')
y = tf.placeholder(tf.float32, name='y')
z = tf.add(x, y, name='sum')
```
请注意,已将参数名称作为占位符和操作的附加参数提供。 这些是我们可视化图形时可以看到的名称。 现在我们可以在 TensorBoard 中使用以下命令将图形写入特定的文件夹:
```py
session = tf.Session()
summary_writer = tf.summary.FileWriter('/tmp/1', session.graph)
```
此命令将图形写入磁盘到参数中给定的特定文件夹中。 现在可以使用以下命令调用 Tensorboard:
```py
tensorboard --logdir=/tmp/1
```
可以将任何目录作为`logdir`选项的参数传递,该选项用于存储文件。 转到浏览器并粘贴以下 URL 以开始可视化以访问 TensorBoard:
```py
http://localhost:6006/
```
浏览器应显示如下内容:
![](img/5c0dfa38-1cd4-4e1c-8d90-6e9bbc31129e.png)
浏览器窗口中的 TensorBoard 可视化
显示加法图,并为占位符指定名称。 单击它们时,我们可以在右侧看到该操作的所有张量细节。 使自己熟悉选项卡和选项。 此窗口有几个部分。 我们将在不同的章节中了解它们。 TensorBoard 是 TensorFlow 中最好的区分工具之一,这使其比其他任何深度学习框架都更好。
# TensorFlow 服务工具
TensorFlow Serving 是 TensorFlow 中的工具,专为灵活的部署环境而开发,可提供高延迟和吞吐量的环境。 使用 TensorFlow 训练的任何深度学习模型都可以与服务一起部署。 通过运行以下命令来安装 Serving:
```py
sudo apt-get install tensorflow-model-server
```
有关如何使用服务的逐步说明,将在[第 3 章](../Text/03.html)*图像检索*中进行介绍。 请注意,仅在 Ubuntu 中即可轻松安装 Serving; 对于其他操作系统,请参考 [https://www.tensorflow.org/serving/setup](https://www.tensorflow.org/serving/setup) 。 下图说明了 TensorFlow Serving 和 TensorFlow 在生产环境中如何交互:
![](img/54fe6552-5ba5-4270-ab85-74a753389c05.png)
训练过程可以产生许多模型,Serving 会无缝切换它们,而不会造成任何停机。 除了[第 3 章](../Text/03.html)*图像检索*[第 10 章](../Text/10.html)*部署*之外,以下各章均不需要 TensorFlow Serving。
# 凯拉斯图书馆
`Keras`是一个用 Python 编写的用于深度学习的开源库。 它提供了一个简单的界面来使用 TensorFlow 作为后端。 Keras 还可以与 Theano,深度学习 4j 或 CNTK 一起用作后端。 Keras 通过专注于友好性,模块化和可扩展性而设计用于轻松快速地进行实验。 它是一个独立的框架,可以在 CPU 和 GPU 之间无缝运行。 Keras 可以单独安装,也可以使用`tf.keras` API 在 TensorFlow 本身中使用。 在本书中,我们将使用`tf.keras` API。 我们已经看到了安装开发环境所需的库的步骤。 顺利安装 CUDA,CUDNN,OpenCV,TensorFlow 和 Keras 并对以下章节至关重要。
# 摘要
在本章中, e 涵盖了深度学习的基础知识。 本章介绍的词汇将在整本书中使用,因此,您可以经常参考本章。 示例还显示了计算机视觉的应用。 还介绍了用于开发环境的各种平台的所有软件包的安装。
在下一章中,我们将讨论如何在数据集上使用 Keras 和 TensorFlow 训练分类模型。 我们将研究如何使用更大的模型和其他技术(例如增强和微调)来提高准确性。 然后,我们将看到由世界各地的几个人提出的几种先进模型,它们在比赛中达到了最佳准确性。
\ No newline at end of file
# 影像分类
图像分类是将整个图像分类为单个标签的任务。 例如,给定图像是狗还是猫,图像分类任务可以将图像标记为狗或猫。 在本章中,我们将了解如何使用 TensorFlow 建立这样的图像分类模型,并学习提高准确性的技术。
我们将在本章介绍以下主题:
* 在 TensorFlow 中训练 MNIST 模型
* 在 Keras 中训练 MNIST 模型
* 其他流行的图像测试数据集
* 更大的深度学习模型
* 训练猫与狗的模型
* 开发实际应用
# 在 TensorFlow 中训练 MNIST 模型
在本节中,我们将了解**修改后的美国国家标准技术学院****MNIST** )数据库[​​HTG5]的数据,并建立一个简单的分类模型。 本部分的目的是学习深度学习的通用框架,并将其用于 TensorFlow。 首先,我们将建立一个感知器或逻辑回归模型。 然后,我们将训练 CNN 以获得更高的准确性。 我们还将看到 TensorBoard 如何帮助可视化培训过程并了解参数。
# MNIST 数据集
`MNIST`数据具有从 0 到 9 的手写数字,其中 60,000 张用于训练的图像和 10,000 张用于测试的图像。 该数据库被广泛用于尝试使用最少预处理的算法。 这是一个学习机器学习算法的好而紧凑的数据库。 这是最著名的图像分类问题数据库。 这里显示了一些示例:
![](img/fe1a0746-bfbb-445f-949c-8787df44e2ec.png)
从上图中可以看出,这些手写字符有 10 个标签。 将图像标准化为 28 个图像像素乘以 28 个图像像素的尺寸,转换为灰度尺寸,并居中为固定尺寸。 这是一个很小的数据集,可以在上面快速测试算法。 在下一节中,我们将看到如何加载此数据集以在 TensorFlow 中使用。
# 加载 MNIST 数据
直接从 TensorFlow 加载`MNIST`数据。 请注意,在加载数据时,我们指定一种热编码作为参数。 标签存储为整数,但为了进行训练,应将其加载为一键编码。 从现在开始,假设读者正在使用导入了 TensorFlow `tf`的编辑器运行代码。 以下是要加载`MNIST_data`的代码段:
```py
from tensorflow.examples.tutorials.mnist import input_data
mnist_data = input_data.read_data_sets('MNIST_data', one_hot=True)
```
对于首次运行,将下载数据,并且可能需要一些时间。 从第二次运行开始,将使用缓存的数据。 在下一节中,我们将构建一个感知器来对数字进行分类。
# 建立一个感知器
感知器是单层神经网络。 本章介绍的概念(例如,完全连接的图层,`activation`函数,随机梯度下降,`logits`,一种热编码,softmax 和交叉熵)在这里将很有用。 您将学习如何在 TensorFlow 中定义神经网络的这些组件,并使用该网络训练`MNIST`数据。
# 为输入数据和目标定义占位符
占位符是传递数据的张量。 占位符不是特定值,但将在计算过程中接收输入。 首先声明感知器的输入大小,类数,批处理大小以及迭代或批处理的总数。 `x_input`是稍后将在其中输入图像的输入。 `y_input`是占位符,将在其中提供一键式标签或目标,如下所示:
```py
input_size = 784 no_classes = 10 batch_size = 100 total_batches = 200 x_input = tf.placeholder(tf.float32, shape=[None, input_size])
y_input = tf.placeholder(tf.float32, shape=[None, no_classes])
```
shape 参数中的`None`表示它可以是任意大小,因为我们尚未定义批量大小。 第二个参数是`x_input`的张量大小和`y_input`的类数。 根据占位符的类型,我们以浮点数形式发送了数据。 接下来,我们可以定义感知器。
# 定义完全连接层的变量
让我们通过解释`weights``bias`等变量来定义一个简单的线性分类器或感知器。 这些变量的值将在计算过程中获悉。 这些也称为模型参数。 权重变量使用具有输入大小和类数形状的正态随机分布进行初始化。 由于图像被整形为单个矢量,因此输入大小为`784`。 类的数量是 10,它等于数据集中的位数。 偏差变量还使用大小等于类数的随机正态分布进行初始化。 `weights``bias`定义如下:
```py
weights = tf.Variable(tf.random_normal([input_size, no_classes]))
bias = tf.Variable(tf.random_normal([no_classes]))
```
变量的初始化可以为零,但随机正态分布可提供稳定的训练。 然后对输入进行加权,并加上偏置以产生`logits`,如下所示:
```py
logits = tf.matmul(x_input, weights) + bias
```
必须将感知器产生的`logits`与单热标签`y_input`进行比较。 正如在[第 1 章](../Text/01.html)*入门*中所了解的,最好使用 softmax 和交叉熵来比较`logits`和单热标签。
TensorFlow 的 `tf.nn.softmax_cross_entropy_with_logits` API 为我们做到了。 可以通过平均交叉熵来计算损耗。 然后,交叉熵通过`tf.train.GradientDescentOptimizer`完成的梯度下降优化得到馈送。 优化器接受损失,并以`0.5`的学习率将其最小化。 接下来显示 softmax,交叉熵,损耗,优化的计算:
```py
softmax_cross_entropy = tf.nn.softmax_cross_entropy_with_logits(
labels=y_input, logits=logits)
loss_operation = tf.reduce_mean(softmax_cross_entropy)
optimiser = tf.train.GradientDescentOptimizer(
learning_rate=0.5).minimize(loss_operation)
```
softmax 和交叉熵是从`tf.nn`程序包一起计算的,该程序包还有其他几种有用的方法。 `tf.train`有几个优化器,在这里,我们使用香草梯度下降。 您可以访问 TensorFlow API 文档以了解其他可选参数。 到目前为止,已定义了占位符,变量和操作,但尚未填充张量。
阅读 TensorFlow 中提供的优化程序列表,网址为 [https://www.tensorflow.org/api_guides/python/train](https://www.tensorflow.org/api_guides/python/train) 。 Adam 优化器对于计算机视觉应用程序特别有用。 它通常会收敛得更快,因此我们不需要定义学习率。 有关优化器的理论总结,请访问 [http://ruder.io/optimizing-gradient-descent](http://ruder.io/optimizing-gradient-descent)
# 用数据训练模型
现在,您已经定义了模型和训练操作。 下一步是开始使用数据训练模型。 在训练过程中,计算梯度并更新权重。 变量尚未初始化。 接下来,启动会话并使用全局变量初始化程序初始化变量:
```py
session = tf.Session()
session.run(tf.global_variables_initializer())
```
本书中的大多数示例都需要前两行。 假定读者将在需要的地方使用这两行。 现在,该图已准备好填充数据并开始训练。 通过循环,批量读取数据并训练模型。 通过使用所需的张量运行会话来进行模型训练。 为了使图形更新权重,必须调用优化器:
```py
for batch_no in range(total_batches):
mnist_batch = mnist_data.train.next_batch(batch_size)
_, loss_value = session.run([optimiser, loss_operation], feed_dict={
x_input: mnist_batch[0],
y_input: mnist_batch[1]
})
print(loss_value)
```
`run`方法的第一个参数可以具有一个数组,要求为其提供值的输出。 我们通过损失是因为打印损失会告诉我们模型是否正在训练中。 随着我们将损失降至最低,预计损失将减少。 feed dict 是一个 Python 字典,用于直接将输入和目标标签提供给占位符。 一旦该循环结束,损耗通常应低于 0.46。 接下来,我们可以通过计算精度来评估模型的工作效果,如下所示:
```py
predictions = tf.argmax(logits, 1)
correct_predictions = tf.equal(predictions, tf.argmax(y_input, 1))
accuracy_operation = tf.reduce_mean(tf.cast(correct_predictions,
tf.float32))
test_images, test_labels = mnist_data.test.images, mnist_data.test.labels
accuracy_value = session.run(accuracy_operation, feed_dict={
x_input: test_images,
y_input: test_labels
})
print('Accuracy : ', accuracy_value)
session.close()
```
该预测应该是最大激活的索引。 应该将其与 MNIST 标签上的基本事实进行比较,以进行正确的预测。 使用正确预测的平均值计算准确性。 可以通过将测试数据作为提要字典运行会话来评估数据的准确性。 当整个程序运行时,最终应产生 90%左右的精度。 如果没有用于培训和测试的更简单的 API,该模型的定义似乎太明确了。 此基本定义水平为 TensorFlow 赋予了表达能力。 在下一部分中,我们将看到更高级别的 API。 感知器获得的精度不是很高,在下一节中,我们将使用具有卷积层的更深的网络来提高精度。
# 建立多层卷积网络
在本节中,我们将看到如何在 TensorFlow 中创建多层卷积网络,并观察更深的网络如何提高分类准确性。 我们将使用 TensorFlow 层的 API 定义层,而不是从头开始定义它们。 最佳实践方法已根植于这些方法中。 可以从上一节开始导入库,数据集和占位符。 这次,我们将使用 TensorBoard 可视化培训过程。 为了可视化变量的统计信息,必须将变量统计信息的值添加到`tf.summary`中。
摘要将被写入 TensorBoard 可以解释的文件夹中。 我们定义一个函数来编写摘要,以便可以使用 TensorBoard 可视化它们:
```py
def add_variable_summary(tf_variable, summary_name):
with tf.name_scope(summary_name + '_summary'):
mean = tf.reduce_mean(tf_variable)
tf.summary.scalar('Mean', mean)
with tf.name_scope('standard_deviation'):
standard_deviation = tf.sqrt(tf.reduce_mean(
tf.square(tf_variable - mean)))
tf.summary.scalar('StandardDeviation', standard_deviation)
tf.summary.scalar('Maximum', tf.reduce_max(tf_variable))
tf.summary.scalar('Minimum', tf.reduce_min(tf_variable))
tf.summary.histogram('Histogram', tf_variable)
```
变量`summary`函数写入变量的摘要。 摘要中添加了五个统计量:平均值,标准差,最大值,最小值和直方图。 汇总可以是`scalar``histogram`。 当记录多个变量时,我们将看到如何在 TensorBoard 中可视化这些值。 与以前的模型不同,我们将`MNIST`数据的大小调整为一个正方形,并像二维图像一样使用它。 以下是将图像整形为 28 个图像像素乘 28 个图像像素的命令:
```py
x_input_reshape = tf.reshape(x_input, [-1, 28, 28, 1],
name='input_reshape')
```
尺寸`-1`表示批量大小可以是任何数字。 请注意,有一个名为`name`的自变量会在 TensorBoard 图形中反映出来,以便于理解。 我们将定义一个 2D 卷积层,其中定义了输入,过滤器,内核和激活。 可以在任何地方调用此方法以获取更多示例,并且在激活功能必须具有**整流线性单元****ReLU** )的情况下很有用。 `convolution`功能层定义如下:
```py
def convolution_layer(input_layer, filters, kernel_size=[3, 3],
activation=tf.nn.relu):
layer = tf.layers.conv2d(
inputs=input_layer,
filters=filters,
kernel_size=kernel_size,
activation=activation,
)
add_variable_summary(layer, 'convolution')
return layer
```
`kernel_size``activation`的默认参数。 汇总将添加到函数中的层,然后返回该层。 每当调用该函数时,都必须将`input_layer`作为参数传递。 这个定义将使我们的其他代码变得简单而小巧。 以非常相似的方式,我们将为`pooling_layer`定义一个函数,如下所示:
```py
def pooling_layer(input_layer, pool_size=[2, 2], strides=2):
layer = tf.layers.max_pooling2d(
inputs=input_layer,
pool_size=pool_size,
strides=strides
)
add_variable_summary(layer, 'pooling')
return layer
```
该层的`pool_size``strides`的默认参数分别为`[2, 2]``2`。 这些参数通常工作良好,但可以在必要时进行更改。 也为该层添加了摘要。 接下来,我们将定义一个密集层,如下所示:
```py
def dense_layer(input_layer, units, activation=tf.nn.relu):
layer = tf.layers.dense(
inputs=input_layer,
units=units,
activation=activation
)
add_variable_summary(layer, 'dense')
return layer
```
定义的密集层具有用于激活的默认参数,并且还添加了变量摘要。 `pooling_layer`从卷积层获取特征图,并通过使用池大小和跨距进行跳过来将其缩小为一半。 所有这些层均以图形方式连接,并且已被定义。 没有一个值被初始化。 可以添加另一个卷积层,以将采样特征从第一卷积层转换为更好的特征。 合并后,我们可以将激活重塑为线性形式,以便通过密集的层进行馈送:
```py
convolution_layer_1 = convolution_layer(x_input_reshape, 64)
pooling_layer_1 = pooling_layer(convolution_layer_1)
convolution_layer_2 = convolution_layer(pooling_layer_1, 128)
pooling_layer_2 = pooling_layer(convolution_layer_2)
flattened_pool = tf.reshape(pooling_layer_2, [-1, 5 * 5 * 128],
name='flattened_pool')
dense_layer_bottleneck = dense_layer(flattened_pool, 1024)
```
卷积层之间的唯一区别是滤波器的大小。 重要的是,各层之间的尺寸必须适当地变化。 选择内核和步幅的参数是任意的,这些数字是根据经验选择的。 定义了两个卷积层,然后可以是一个完全连接的层。 d ense-layer API 可以采用单个维的任何矢量并将其映射到任意数量的隐藏单元,如本例中的`1024`。 隐藏层之后是 ReLU 激活 ,以使其成为非线性计算。 也为此层添加了变量摘要。 接下来是具有退出率的退出层。 保持较高水平将阻止网络学习。 根据使用的时间,可以将训练模式设置为`True``False`。 在训练中,我们将其设置为`True`(默认为`False`)。 在计算准确性时,我们将不得不更改此设置。 因此,为此保留了一个布尔值,将在训练过程中喂入:
```py
dropout_bool = tf.placeholder(tf.bool)
dropout_layer = tf.layers.dropout(
inputs=dense_layer_bottleneck,
rate=0.4,
training=dropout_bool
)
```
辍学层再次被馈送到一个密实层,这称为 logits。 Logits 是最后一层,激活会导致类数增加。 激活将针对特定类别(即目标类别)加标,并且最多可以获得这 10 个激活的最大值:
```py
logits = dense_layer(dropout_layer, no_classes)
```
logits 的输出与上一节中创建的模型非常相似。 现在,对数可以通过 softmax 层传递,然后像以前一样进行交叉熵计算。 在这里,我们添加了一个作用域名称,以在 TensorBoard 中获得更好的可视化效果,如下所示:
```py
with tf.name_scope('loss'):
softmax_cross_entropy = tf.nn.softmax_cross_entropy_with_logits(
labels=y_input, logits=logits)
loss_operation = tf.reduce_mean(softmax_cross_entropy, name='loss')
tf.summary.scalar('loss', loss_operation)
```
可以使用`tf.train` API 的方法优化此`loss`函数。 在这里,我们将使用`Adamoptimiser`。 学习率无需定义,并且在大多数情况下效果良好:
```py
with tf.name_scope('optimiser'):
optimiser = tf.train.AdamOptimizer().minimize(loss_operation)
```
像以前一样计算准确性,但是为正确的预测和准确性计算添加了名称范围:
```py
with tf.name_scope('accuracy'):
with tf.name_scope('correct_prediction'):
predictions = tf.argmax(logits, 1)
correct_predictions = tf.equal(predictions, tf.argmax(y_input, 1))
with tf.name_scope('accuracy'):
accuracy_operation = tf.reduce_mean(
tf.cast(correct_predictions, tf.float32))
tf.summary.scalar('accuracy', accuracy_operation)
```
还添加了精度的标量摘要。 下一步是启动会话并初始化变量,如上一节所述。 这些行在这里不再重复。 必须合并这些摘要,并且必须定义用于编写培训和测试摘要的文件:
```py
merged_summary_operation = tf.summary.merge_all()
train_summary_writer = tf.summary.FileWriter('/tmp/train', session.graph)
test_summary_writer = tf.summary.FileWriter('/tmp/test')
```
注意,该图形只用`summary_writer`写入一次。 训练与之前非常相似,除了训练时的精度计算和值被添加到摘要中。 接下来,可以批量加载数据并可以开始训练:
```py
test_images, test_labels = mnist_data.test.images, mnist_data.test.labels
for batch_no in range(total_batches):
mnist_batch = mnist_data.train.next_batch(batch_size)
train_images, train_labels = mnist_batch[0], mnist_batch[1]
_, merged_summary = session.run([optimiser, merged_summary_operation],
feed_dict={
x_input: train_images,
y_input: train_labels,
dropout_bool: True
})
train_summary_writer.add_summary(merged_summary, batch_no)
if batch_no % 10 == 0:
merged_summary, _ = session.run([merged_summary_operation,
accuracy_operation], feed_dict={
x_input: test_images,
y_input: test_labels,
dropout_bool: False
})
test_summary_writer.add_summary(merged_summary, batch_no)
```
每次迭代都会返回摘要以获取训练数据,并将其添加到编写器中。 对于第十次迭代,将添加测试摘要。 请注意,仅在训练期间而不是在测试期间启用辍学。 我们已经完成了定义以及网络摘要,可以运行该网络。 要查看培训过程,我们可以按照[第 1 章](../Text/01.html)*入门*中所述前往 TensorBoard。
# 在深度学习中使用 TensorBoard
在浏览器中打开 TensorBoard 后,转到图表选项卡。 应该显示我们定义并接受训练的图形。 右键单击节点,我们可以选择要从主图中删除的操作。 对齐后,图形应如下所示:
![](img/c07978e2-e383-4124-9f9a-2be7bf0c7899.png)
该图说明了在先前实例中经过训练和定义的图形
注意我们定义的所有图层的显示效果如何。 这对于检查体系结构的定义非常有用。 图形的方向与所有细节都很好地可视化了。 通过单击每个节点,您可以看到该节点的详细信息,例如输入和输出张量形状,如下所示:
![](img/e96a21f1-a15b-4962-8aec-62216cd004e1.png)
这些值可用于交叉检查图层参数的定义。 请注意左下方的图例,以使自己熟悉此页面,如下所示:
![](img/58e1619e-46b3-4d78-9076-dfcfad01b5be.png)
名称范围已分组,可以通过单击节点上的加号来查看各个组件。 节点按颜色排列。 现在我们可以移至标量页面。 通过在页面上四处移动,可以发现精度图,如下图所示:
![](img/dc143414-d4bc-471b-ae2f-f6831f6030db.png)
橙色线用于训练数据,蓝色线用于测试数据。 他们大致遵循相同的模式。 表示原始值的亮线稍微少一些,而亮线是平滑的曲线。 可以在 UI 中选择平滑系数。 测试数据的准确性已达到 97%以上。 以下是损失摘要中的图:
![](img/590361f9-9421-41be-b2a2-d2aafe0e1d6d.png)
在培训过程中,培训和测试数据的损失都在稳步减少,这是一个好兆头。 在训练过程中将刷新所有摘要的数据,我们可以见证准确性的提高和损失的减少,从而获得 97.38%的出色测试精度。
这可以帮助您查看模型是否正在学习并且正在朝着更好的方向发展。 其他汇总(例如最小值,最大值,平均值和标准偏差)也很有用。 以下是密集层的图形:
![](img/c57d3b2a-1f10-49e0-9daf-08e6ce170d0b.png)
这些摘要对于注意权重的变化很有用。 这些分布也可以显示为直方图,如下所示:
![](img/04d8485e-8078-4516-8203-27c99d3f2e66.png)
这些是对数的权重分布。 这些是 TensorBoard 可能提供的美丽可视化效果,并且在培训中非常有帮助。 通过使模型更深入,我们可以见证准确性的巨大提高。 在下一节中,我们将看到如何使用 Keras API 训练相同的模型。 现在您可以看到 TensorBoard 在检查深度学习模型和训练过程中的功能。
# 在 Keras 中训练 MNIST 模型
在本节中,我们将使用通过 `tf.keras` API 定义的与上一节相同的模型。 最好从 TensorFlow 学习 Keras 和 Layer 包,因为它们可以在几个开源代码中看到。 本书的目的是使您了解 TensorFlow 的各种产品,以便您可以在其之上构建产品。
<q class="calibre46">“读取代码的次数多于写入代码的次数。”</q>
牢记前面的引用,向您展示了如何使用各种 AP​​I 实现相同的模型。 任何最新算法实现的开放源代码都将是这些 API 的组合。 接下来,我们将从 Keras 实现开始。
# 准备数据集
Keras 提供`MNIST`数据。 首先,导入`tensorflow`。 然后定义一些常量,例如批处理大小,类和时期数。 可以根据计算机上可用的 RAM 选择批次大小。 批处理大小越大,所需的 RAM 越多。 批次大小对准确性的影响很小。 此处的类数等于 10,并且针对不同的问题而有所不同。 时期的数量决定了训练必须经过整个数据集的次数。 如果在所有时期结束时减少损失,则可以将其设置为较高的数字。 在某些情况下,训练时间较长可以提高准确性。 现在让我们看一下创建数据集的步骤:
1. 设置输入图像的尺寸,如下所示:
```py
batch_size = 128
no_classes = 10
epochs = 2
image_height, image_width = 28, 28
```
2. 使用 Keras 实用程序将数据从磁盘加载到内存:
```py
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
```
3. 将向量重塑为图像格式,并使用给定的代码定义卷积的输入尺寸:
```py
x_train = x_train.reshape(x_train.shape[0], image_height, image_width, 1)
x_test = x_test.reshape(x_test.shape[0], image_height, image_width, 1)
input_shape = (image_height, image_width, 1)
```
4. 如下将数据类型转换为`float`
```py
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
```
5. 通过减去数据均值来归一化数据:
```py
x_train /= 255
x_test /= 255
```
6. 将分类标签转换为一次性编码:
```py
y_train = tf.keras.utils.to_categorical(y_train, no_classes)
y_test = tf.keras.utils.to_categorical(y_test, no_classes)
```
这与 TensorFlow 编写代码的方式非常不同。 数据已加载到内存中,此处`Placeholders`的概念均不存在。
# 建立模型
在本节中,我们将使用一些卷积层,然后是全连接层,以训练前面的数据集。 构造一个简单的顺序模型,该模型具有两个卷积层,然后是池化层,丢失层和密集层。 顺序模型具有`add`方法,可以将多个层堆叠在一起。 第一层具有 64 个过滤器,第二层具有 128 个过滤器。 所有过滤器的内核大小均为 3。 在卷积层之后应用最大池。 卷积层的输出被展平,并通过丢包连接连接到一对完全连接的层。
最后一层连接到 softmax,因为这是一个多类分类问题。 以下代码显示了如何定义模型:
```py
def simple_cnn(input_shape):
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Conv2D(
filters=64,
kernel_size=(3, 3),
activation='relu',
input_shape=input_shape
))
model.add(tf.keras.layers.Conv2D(
filters=128,
kernel_size=(3, 3),
activation='relu'
))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Dropout(rate=0.3))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(units=1024, activation='relu'))
model.add(tf.keras.layers.Dropout(rate=0.3))
model.add(tf.keras.layers.Dense(units=no_classes, activation='softmax'))
model.compile(loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=['accuracy'])
return model
simple_cnn_model = simple_cnn(input_shape)
```
该模型刚刚定义,必须进行编译。 在编译丢失期间,必须定义优化器和指标。 损失将是交叉熵,并通过 Adam 算法进行了优化,我们将以准确性作为度量标准。 使用加载的数据,训练和评估数据。 使用训练参数加载训练数据并拟合模型:
```py
simple_cnn_model.fit(x_train, y_train, batch_size, epochs, (x_test, y_test))
train_loss, train_accuracy = simple_cnn_model.evaluate(
x_train, y_train, verbose=0)
print('Train data loss:', train_loss)
print('Train data accuracy:', train_accuracy)
```
使用 Keras A PI 时,不会创建会话。 然后按以下方式评估测试数据:
```py
test_loss, test_accuracy = simple_cnn_model.evaluate(
x_test, y_test, verbose=0)
print('Test data loss:', test_loss)
print('Test data accuracy:', test_accuracy)
```
评估也可以在不显式创建会话的情况下创建。 运行完成后,结果应类似于以下内容:
```py
Loss for train data: 0.0171295607952
Accuracy of train data: 0.995016666667
Loss for test data: 0.0282736890309
Accuracy of test data: 0.9902
```
这样可以使测试数据的准确度达到 99%。 请注意,训练精度高于测试数据,并且始终打印它们都是一个好习惯。 精度的差异是由于迭代次数造成的。 由于数据集的差异,准确性比 TensorFlow 中创建的先前模型要高一些。
# 其他流行的图像测试数据集
`MNIST`数据集是用于测试算法的最常用数据集。 但是还有其他数据集可用于测试图像分类算法。
# CIFAR 数据集
加拿大高级研究机构**( **CIFAR** )-10 数据集包含 60,000 张图像,其中 50,000 张图像用于训练,10,000 张图像用于测试。 类的数量是 10。图像尺寸是 32 像素 x 32 像素。 以下是从每个类别中随机选择的图像:**
`![](img/50a4ca9c-f6dc-413c-9cb1-1691ccc0680c.png)`
这些图像很小,仅包含一个对象。 `CIFAR-100`数据集包含相同数量的图像,但具有 100 个类别。 因此,每个类别只有 600 张图像。 每个图像都带有一个超级标签和一个精美标签。 如果您想进行实验,可以在`tf.keras.datasets`上找到此数据集。
# Fashion-MNIST 数据集
`Fashion-MNIST`是替代`MNIST`数据集而创建的数据集。 创建为`MNIST`的此数据集被认为太简单了,可以直接用`MNIST`代替。
以下是在执行**主成分分析****PCA** )之后从数据集中随机选择的示例:
![](img/da904c50-2447-4537-833a-d31218cbfe5a.png)
数据集大小,标签数量和图像大小类似于`MNIST`。 可以在 [https://github.com/zalandoresearch/fashion-mnist](https://github.com/zalandoresearch/fashion-mnist) 上找到更多详细信息。 您可以运行先前学习的模型并检查准确性。
# ImageNet 数据集和竞争
ImageNet 是具有 14,197,122 图像,21,841 个同义词集索引的计算机视觉数据集。 同义词集是 WordNet 层次结构中的一个节点,而节点又是一组同义词。 每年都会举办一次比赛,其中有 1000 个此类数据集。 它已成为评估图像分类算法性能的标准基准。
在 2013 年,基于深度学习的计算机视觉模型获得了第一名。 从那时起,只有深度学习模型赢得了竞争。 以下是多年来在比赛中排名前五位的错误率:
![](img/06f1198b-fb2d-44ab-baed-b2cff9e113ed.png)
您会注意到,多年来精度以及层的深度一直在增加。 接下来,我们将了解该图中存在的模型。
# 更大的深度学习模型
我们将审视几种模型定义,这些模型定义在 ImageNet 竞赛中取得了最新的成果。 我们将在以下主题中单独研究它们。
# AlexNet 模型
**AlexNet** 是第一本引起人们对计算机视觉深度学习的广泛兴趣的出版物。 Krizhevsky 等。 ( [https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf](https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf) )提出了 AlexNet,它一直是先驱和影响力 在这个领域里。 该模型赢得了 ImageNet 2013 挑战。 错误率是 15.4%,明显优于下一个。 该模型是具有五个卷积层的相对简单的体系结构。 面临的挑战是对 1,000 种对象进行分类。 图像和数据包含 1500 万条带注释的图像,其中包含 22,000 多个类别。 其中,只有 1,000 个类别用于比赛。 AlexNet 使用 ReLU 作为激活功能,发现它的训练速度比其他激活功能快几倍。 该模型的架构如下所示:
![](img/ee056ac4-3d0d-47df-ac96-5e2f6988c91b.png)
复制自 Krizhevsky 等人。
本文还使用了数据增强技术,例如图像翻译,水平翻转和随机裁剪。 漏失层防止过度拟合 。 该模型使用香草**随机梯度下降****SGD** )进行训练。 仔细选择 SGD 的参数进行训练。 学习率在一组固定的训练迭代中变化。 动量和重量衰减采用固定值进行训练。 本文介绍了一种称为**本地响应规范化****LRN** )的概念。 LRN 层对滤镜上的每个像素进行归一化,以避免在特定滤镜中发生巨大的激活。
不再使用该层,因为最近的研究表明,由于 LRN,没有太大的改进。 AlexNet 总共有 6000 万个参数。
# VGG-16 模型
**VGG** 模型代表牛津大学的, **视觉几何组** 。 该模型非常简单,并且比 AlexNet 具有更大的深度。 该纸有两个模型,深度分别为 16 和 19 层。 所有的 CNN 层都使用 3 x 3 步幅的滤镜和 1 尺寸的垫,以及 2 步幅的最大合并尺寸 2。这导致参数数量减少。 尽管由于最大池化而减小了大小,但过滤器的数量却随着层的增加而增加。 16 层深度模型的体系结构如下:
![](img/776596c6-d2f7-4656-bb91-85d84253f33c.png)
该模型具有 1.38 亿个参数,是此处描述的所有模型中最大的。 但是参数的一致性很好。 其特征是,随着网络的深入,图像的尺寸越小,滤波器的数量就越多。 所使用的数据增强技术之一是规模抖动。 比例抖动是一种增强技术,其中具有随机大小的一侧被认为会改变比例。
# Google Inception-V3 模型
**Inception-V3** 是 Szegedy 等人提出的。 ( [https://arxiv.org/pdf/1409.4842.pdf](https://arxiv.org/pdf/1409.4842.pdf) ),并介绍了具有更好泛化方法的初始概念。 该架构在 2014 年赢得了 ImageNet 竞赛的冠军。它旨在提高速度和尺寸的效率。 它的参数比 AlexNet 小 12 倍。 初始阶段是构建宏体系结构的微体系结构。 每个隐藏层都有一个较高级别的图像表示。 在每一层,我们可以选择使用池化或其他层。 初始使用多个内核,而不是使用一种类型的内核。 平均池之后是各种大小的卷积,然后将它们合并在一起。
可以基于数据学习内核参数。 使用多个内核,该模型可以检测较小的特征以及较高的抽象度。 1 x 1 卷积将减少特征,从而减少计算量。 这将在推理过程中占用较少的 RAM。 以下是最简单形式的启动模块,其中包含具有各种内核大小和池化的卷积选项:
![](img/e38ca5d2-f9c5-4581-b5be-1a5a30c5d57b.png)
请注意,与 AlexNet 或 VGG 相反,操作是并行进行的。 输出量巨大,因此引入了 1 x 1 的滤波器以降低尺寸。 将缩小的尺寸添加到体系结构后,它将变为:
![](img/cc7e385f-0826-4d07-99d7-9116a649e402.png)
该模型的整个体系结构如下,包括所有的风吹草动:
![](img/bef2324f-3a22-4f92-8af9-a6e90300e66c.png)
该图说明了 Google Inception V3 模型架构[经 Szegedy 等人的许可复制]
有 9 个初始模块,共 100 层,它们具有良好的性能。
# Microsoft ResNet-50 模型
**ResNet** 是 He 等人提出的。 ( [https://arxiv.org/pdf/1512.03385.pdf](https://arxiv.org/pdf/1512.03385.pdf) ),并在 2015 年赢得了 ImageNet 竞赛。此方法表明可以训练更深的网络。 网络越深,精度变得越饱和。 这甚至不是由于过拟合或由于存在大量参数,而是由于减少了训练误差。 这是由于无法反向传播梯度。 可以通过以下方法将梯度直接发送到带有残差块的更深层来克服:
![](img/ff1c06f1-c254-4602-9b50-91cf9402df05.png)
每两层相连,形成一个残留块。 您可以看到训练是在各层之间传递的。 通过这种技术,反向传播会将错误带到较早的层。
可以从 [https://github.com/tensorflow/tensorflow/tree/r1.4/tensorflow/python/keras/_impl/keras/applications](https://github.com/tensorflow/tensorflow/tree/r1.4/tensorflow/python/keras/_impl/keras/applications) 使用模型定义。 定义了模型中的每一层,并且`ImageNet`数据集上的预训练权重可用。
# SqueezeNet 模型
Iandola 等人介绍了 **SqueezeNet** 模型。 ( [https://arxiv.org/pdf/1602.07360.pdf](https://arxiv.org/pdf/1602.07360.pdf) ),以减少模型尺寸和参数数量。
通过使用 1 x 1 过滤器替换 3 x 3 过滤器,使网络变得更小,如下所示:
![](img/61941eba-0b03-4a6d-93ce-69132bedbe96.png)
经 Iandola 等人许可复制。
3 x 3 滤波器的输入数量也减少了在较高级别发生时各层的下采样,从而提供了较大的激活图:
![](img/12771464-5c49-48ae-b9e4-f512ed8f42aa.png)
经 Iandola 许可复制 等。
# 空间变压器网络
Jaderberg 等人提出的**空间变压器网络**。 ( [https://arxiv.org/pdf/1506.02025.pdf](https://arxiv.org/pdf/1506.02025.pdf) )尝试在传递到 CNN 之前对图像进行转换。 这与其他网络不同,因为它尝试在卷积之前修改图像。 该网络学习参数以变换图像。 学习用于 **仿射变换**的参数。 通过应用仿射变换,可以实现 **空间不变性** 。 在以前的网络中,空间不变性是通过最大池化层实现的。 空间变压器网络的位置如下所示:
![](img/177e14e5-4bc8-4b5c-9e0f-754188739941.png)
经 Jaderberg 许可复制 等。
# DenseNet 模型
DenseNet 是 Huang 等人提出的 ResNet 的扩展。 ( [https://arxiv.org/pdf/1608.06993.pdf](https://arxiv.org/pdf/1608.06993.pdf) )。 在 ResNet 块中,上一层通过求和合并到下一层。 在 DenseNet 中,上一层通过串联合并到下一层。 DenseNet 将所有层连接到上一层,将当前层连接到下一层。
在下图中,可以看出要素图是如何作为输入提供给其他层的:
![](img/2ff8e417-48ae-491e-9abc-78bccdcdc61a.png)
经 Huang 许可复制 等。
这样,它提供了多个优点,例如更平滑的渐变,特征变换等。 这也减少了参数的数量:
![](img/feec4f72-5ef2-4eb0-a647-21f56d254fef.png)
经 Huang 许可复制 等。
我们已经介绍了图像分类任务的所有最新算法。 任何体系结构均可用于图像分类任务。 在下一节中,我们将看到如何使用这些先进的体系结构训练模型来预测宠物,并提高准确性。
# 训练猫与狗的模型
在本部分中,我们将准备和训练用于预测猫与狗的模型,并了解一些可提高准确性的技术。 大多数图像分类问题都属于这种范例。 本节介绍的技术,例如扩充和迁移学习,对于一些问题很有用。
# 准备数据
为了进行分类,我们将从 **kaggle** 下载数据并以适当的格式存储。 注册并登录 [www.kaggle.com](http://www.kaggle.com) 并转到 [https://www.kaggle.com/c/dogs-vs-cats/data](https://www.kaggle.com/c/dogs-vs-cats/data) 。 从该页面下载`train.zip``test1.zip`文件。 `train.zip`文件包含 25,000 张宠物数据图像。 我们将仅使用部分数据来训练模型。 具有更多计算能力的读者,例如**图形处理单元****GPU** ),可以使用比建议的更多的数据。 运行以下脚本以重新排列图像并创建必要的文件夹:
```py
import os
import shutil
work_dir = '' # give your correct directory
image_names = sorted(os.listdir(os.path.join(work_dir, 'train')))
def copy_files(prefix_str, range_start, range_end, target_dir):
image_paths = [os.path.join(work_dir, 'train', prefix_str + '.' + str(i) + '.jpg')
for i in range(range_start, range_end)]
dest_dir = os.path.join(work_dir, 'data', target_dir, prefix_str)
os.makedirs(dest_dir)
for image_path in image_paths:
shutil.copy(image_path, dest_dir)
copy_files('dog', 0, 1000, 'train')
copy_files('cat', 0, 1000, 'train')
copy_files('dog', 1000, 1400, 'test')
copy_files('cat', 1000, 1400, 'test')
```
对于我们的实验,我们将仅使用 1000 张猫和狗的图像。 因此,将图像 0–999 从下载的文件夹复制到`cats`下新创建的`train` 文件夹。 同样,将 1,000–1,400 复制到`data/test/cat`,将`train/dogs`中的 1 0–999 和`data/test/dog`中的 1,000–1,400 复制,这样我们每个班级都有 1,000 个训练示例 每个类别 400 个验证示例。
# 使用简单的 CNN 进行基准测试
让我们在该数据集上运行先前的`simple_cnn`模型,并查看其性能。 该模型的性能将成为我们判断其他技术的基本基准。 我们将为数据加载和训练定义一些变量,如下所示:
```py
image_height, image_width = 150, 150
train_dir = os.path.join(work_dir, 'train')
test_dir = os.path.join(work_dir, 'test')
no_classes = 2
no_validation = 800
epochs = 2
batch_size = 200
no_train = 2000
no_test = 800
input_shape = (image_height, image_width, 3)
epoch_steps = no_train // batch_size
test_steps = no_test // batch_size
```
该常数用于本节中的训练猫和狗模型的讨论中讨论的技术。 在这里,我们正在使用 2,800 张图像进行训练和测试,这对于个人计算机的 RAM 是合理的。 但这对于更大的数据集是不可持续的。 最好一次只加载一批图像进行培训和测试。 为此,`tf.keras`具有称为`ImageDataGenerator`的类,可在必要时读取图像。 假定从上一节中导入了`simple_cnn`模型。 以下是使用生成器加载图像的示例:
```py
generator_train = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1\. / 255)
generator_test = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1\. / 255)
```
加载时,此定义还会重新缩放图像。 接下来,我们可以使用`flow_from_directory`方法从目录中读取图像,如下所示:
```py
train_images = generator_train.flow_from_directory(
train_dir,
batch_size=batch_size,
target_size=(image_width, image_height))
test_images = generator_test.flow_from_directory(
test_dir,
batch_size=batch_size,
target_size=(image_width, image_height))
```
加载图像的目录,批量大小和图像的目标大小作为参数传递。 此方法执行重新缩放,并分批传递数据以拟合模型。 该生成器可直接用于拟合模型。 该模型的方法 `fit_generator`可以按以下方式使用:
```py
simple_cnn_model.fit_generator(
train_images,
steps_per_epoch=epoch_steps,
epochs=epochs,
validation_data=test_images,
validation_steps=test_steps)
```
该模型适合来自训练图像生成器的数据。 从训练中定义时期数,并传递验证数据以获取模型过度训练的性能。 该`fit_generator`支持并行处理数据和模型训练。 CPU 执行重新缩放,而 GPU 可以执行模型训练。 这使得计算资源的效率很高。 经过 50 个纪元后,该模型的准确度应为 60%。 接下来,我们将看到如何扩充数据集以获得改进的性能。
# 扩充数据集
数据扩充提供了增加数据集大小的方法。 数据扩充会在训练期间引入噪声,从而在模型中为各种输入生成鲁棒性。 该技术在数据集较小且可以组合并与其他技术一起使用的情况下很有用。 接下来,我们将看到不同类型的扩充。
# 技术提升
可以通过多种方式来增强图像,如下所述:
* **翻转**:图像在水平或垂直方向上被镜像或翻转
* **随机裁剪**:裁剪随机部分,因此该模型可以处理遮挡
* **剪切**:图像变形以影响物体的形状
* **缩放**:训练图像的缩放部分以处理不同比例的图像
* **旋转**:旋转对象以处理对象中各种程度的变化
* **增白**:增白是通过仅保留重要数据的主成分分析完成的
* **归一化**:通过标准化均值和方差来归一化像素
* **通道偏移**:更改颜色通道以使模型对各种伪像引起的颜色变化具有鲁棒性
所有这些技术都在`ImageDataGenerator`中实现,以增加数据集的大小。 以下是`generator_train`的修改版本,其中包含前面讨论的一些增强技术:
```py
generator_train = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1\. / 255,
horizontal_flip=True,
zoom_range=0.3,
shear_range=0.3,)
```
替换前面代码中的`generator_train`将使精度提高到 90%。 更改扩充的参数,并注意更改。 在下一节中,我们将讨论一种称为转移学习的技术,该技术有助于以更少的数据训练更大的模型。
# 转移模型的学习或微调
转移学习是从预先训练的模型中学习的过程,该模型在较大的数据集上进行了训练。 用随机初始化训练模型通常需要时间和精力才能获得结果。 使用预训练的模型初始化模型可以加快收敛速度​​,并节省时间和能源。 这些经过预训练的模型通常使用精心选择的超参数进行训练。
可以直接使用预训练模型的几层,而无需进行任何修改,也可以对其进行位训练以适应变化。 在本节中,我们将学习如何对在`ImageNet`数据集上具有数百万个类别的模型进行调整或转移学习。
# 瓶颈功能培训
上一节中介绍的模型很简单,因此准确性可能较低。 应该从它们构建复杂的模型。 它们不能从头开始构建。 因此,提取瓶颈特征并对它们进行分类器训练。 瓶颈功能是训练数百万张图像的复杂体系结构所产生的功能。 图像是通过前进完成的,并存储了最终图层的特征。 从这些中,训练了一个简单的逻辑分类器进行分类。 提取瓶颈层,如下所示:
```py
generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1\. / 255)
model = tf.keras.applications.VGG16(include_top=False)
train_images = generator.flow_from_directory(
train_dir,
batch_size=batch_size,
target_size=(image_width, image_height),
class_mode=None,
shuffle=False
)
train_bottleneck_features = model.predict_generator(train_images, epoch_steps)
test_images = generator.flow_from_directory(
test_dir,
batch_size=batch_size,
target_size=(image_width, image_height),
class_mode=None,
shuffle=False
)
test_bottleneck_features = model.predict_generator(test_images, test_steps)
```
将采用 VGG 模型并将其用于预测图像。 标签分配如下:
```py
train_labels = np.array([0] * int(no_train / 2) + [1] * int(no_train / 2))
test_labels = np.array([0] * int(no_test / 2) + [1] * int(no_test / 2))
```
使用瓶颈功能构建,编译和训练具有两层的顺序模型,并且可以使用以下给出的代码来实现:
```py
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Flatten(input_shape=train_bottleneck_features.shape[1:]))
model.add(tf.keras.layers.Dense(1024, activation='relu'))
model.add(tf.keras.layers.Dropout(0.3))
model.add(tf.keras.layers.Dense(1, activation='softmax'))
model.compile(loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=['accuracy'])
```
使用以下所示的代码对这些瓶颈特征进行模型训练:
```py
model.fit(
train_bottleneck_features,
train_labels,
batch_size=batch_size,
epochs=epochs,
validation_data=(test_bottleneck_features, test_labels))
```
这提供了一种不同的方法来训练模型,并且在训练数据较少时很有用。 这通常是训练模型的更快方法。 仅使用预训练模型的最终激活来适应新任务。 这个想法可以扩展为微调几层,如下所示:
# 在深度学习中微调几层
可以加载预训练的模型,并且仅可以训练几层。 当给定的问题与模型所训练的图像非常不同时,此方法会更好地工作。 **微调**是深度学习中的常见做法。 当数据集较小时,这具有优势。 优化也可以更快地获得。
在小型数据集上训练深度网络会导致过度拟合。 使用微调程序也可以避免这种过拟合。 在较大的数据集上训练的模型也应该相似,因为我们希望激活和特征与较小的数据集相似。 您可以从存储的权重路径开始,如下所示:
```py
top_model_weights_path = 'fc_model.h5'
```
加载**视觉几何组****VGG** )模型,并将初始层设置为不可训练。 下一部分将详细介绍 VGG 模型。 目前,将 VGG 视为适用于图像数据的大型深度学习模型。 使用以下给出的代码,用新的可训练层替换完全连接的层:
```py
model = tf.keras.applications.VGG16(include_top=False)
```
可以在 VGG 模型的顶部构建一个小型的两层前馈网络,通常具有隐藏的单元,激活和退出,如下所示:
```py
model_fine_tune = tf.keras.models.Sequential()
model_fine_tune.add(tf.keras.layers.Flatten(input_shape=model.output_shape))
model_fine_tune.add(tf.keras.layers.Dense(256, activation='relu'))
model_fine_tune.add(tf.keras.layers.Dropout(0.5))
model_fine_tune.add(tf.keras.layers.Dense(no_classes, activation='softmax'))
```
顶级型号还必须装有经过充分培训的砝码。 然后可以将顶级模型添加到卷积基础中:
```py
model_fine_tune.load_weights(top_model_weights_path)
model.add(model_fine_tune)
```
我们可以将前 25 个层设置为不可训练,直到最后一个卷积块,这样它们的权重才会被更新。 仅其余层将被更新:
```py
for vgg_layer in model.layers[:25]:
vgg_layer.trainable = False
```
使用梯度下降优化器以缓慢的学习速率(4 量级)编译模型:
```py
model.compile(loss='binary_crossentropy',
optimizer=tf.keras.optimizers.SGD(lr=1e-4, momentum=0.9),
metrics=['accuracy'])
```
我们可以将之前介绍的增强技术与剪切,缩放和翻转结合使用。 可以从目录中将流与火车和验证数据集一起添加到生成器。 现在可以将模型与数据增强结合起来进行微调。 这种训练方式比以前的所有方法都具有更好的准确性。 以下是转学的指南:
| **数据大小** | **相似数据集** | **不同的数据集** |
| 较小的数据 | 微调输出层 | 微调更深层 |
| 更大的数据 | 微调整个模型 | 从头开始训练 |
根据数据大小,可以确定要微调的层数。 数据越少,需要调整的层数就越少。 我们已经看到了如何使用转移学习技术来提高模型的准确性。
# 开发实际应用
识别猫和狗是一个很酷的问题,但不太可能是重要的问题。 产品中使用的图像分类的实际应用可能会有所不同。 您可能有不同的数据,目标等。 在本节中,您将学习解决这些不同设置的提示和技巧。 解决新问题时应考虑的因素如下:
* 目标数量。 是 10 类问题还是 10,000 类问题?
* 类内差异有多大? 例如,是否必须在一个类别标签下标识不同类型的猫?
* 类间差异有多大? 例如,是否需要识别不同的猫?
* 数据有多大?
* 数据的平衡程度如何?
* 是否已经有一个训练有很多图像的模型?
* 部署推断时间和模型大小需要什么? 在 iPhone 上是 50 毫秒还是在 Google Cloud Platform 上是 10 毫秒? 可以消耗多少 RAM 来存储模型?
处理图像分类问题时,请尝试回答这些问题。 根据答案,您可以设计训练体系结构并提高准确性,如下一节所述。
# 选择合适的模型
体系结构有很多选择。 根据部署的灵活性,可以选择模型。 请记住,卷积较小且较慢,但是密集层较大且较快。 在大小,运行时间和准确性之间需要权衡。 建议在最终决定之前测试所有架构。 根据应用程序,某些模型可能比其他模型更好。 您可以减小输入大小以加快推理速度。 可以根据以下部分所述的指标来选择体系结构。
# 解决欠拟合和过拟合的方案
对于该问题,模型有时可能太大或太小。 可以将其分别分类为欠拟合或过度拟合。 当模型太小时会发生拟合不足,而在训练精度较低时可以进行测量。 当模型太大并且训练和测试精度之间存在较大差距时,就会发生过度拟合。 拟合不足可以通过以下方法解决:
* 获取更多数据
* 尝试更大的模型
* 如果数据很小,请尝试使用转移学习技术或进行数据扩充
过度拟合可以通过以下方法解决:
* 使用辍学和批处理规范化等技术进行正则化
* 扩充数据集
时刻提防损失。 损耗应随着迭代次数的减少而减少。 如果损失没有减少,则表明训练已停止。 一种解决方案是尝试使用其他优化器。 类别失衡可以通过加权损失函数来解决。 始终使用 **TensorBoard** 观看摘要。 很难估计需要多少数据。 本部分是培训任何深度学习模型的最佳课程。 接下来,我们将介绍一些特定于应用程序的指南。
# 面部性别和年龄检测
应用程序可能需要从面部检测性别和年龄。 脸部图像可以是通过脸部检测器获取的 。 可以将经过裁剪的脸部图像作为训练数据提供,并且应该给出相似的经过裁剪的脸部以进行推断。 根据所需的推理时间,可以选择 OpenCV 或 CNN 面部检测器。 对于培训,可以使用 Inception 或 ResNet。 如果由于是视频而所需的推理时间要少得多,则最好使用三个卷积,然后是两个完全连接的层。 请注意,年龄数据集通常存在巨大的类别失衡,因此使用不同的度量标准(如准确性和召回率)将有所帮助。
# 服装模型的微调
服装模型的微调是一个不错的选择。 在这里,具有多个对属性进行分类的 softmax 层将很有用。 这些属性可以是图案,颜色等。
# 品牌安全
使用**支持向量机****SVM** )来训练瓶颈层是一个不错的选择,因为各个类别的图像可能会完全不同。 通常将其用于内容审核,以帮助避免显示露骨的图像。 您已经了解了如何解决图像分类中的新问题。
# 摘要
我们已经介绍了用于训练分类任务的基本但有用的模型。 我们看到了使用 Keras 和 TensorFlow API 的 MNIST 数据集的简单模型。 我们还看到了如何利用 TensorBoard 观看培训过程。 然后,我们讨论了一些特定应用程序的最新体系结构。 还介绍了几种提高准确性的方法,例如数据增强,瓶颈层训练和微调预训练模型。 还介绍了为新模型训练模型的提示和技巧。
在下一章中,我们将看到如何可视化深度学习模型。 我们还将在本章中部署经过训练的模型以进行推断。 我们还将看到如何将训练有素的图层用于通过应用程序进行图像搜索。 然后,我们将了解自动编码器的概念并将其用于特征的维数。
\ No newline at end of file
# 图像检索
深度学习也可以称为**表示学习**,因为模型的特征或表示是在训练期间学习的。 在隐藏层的训练过程中生成的**视觉特征**可用于计算距离度量。 这些模型学习如何根据分类任务在各个层上检测边缘,图案等。 在本章中,我们将研究以下内容:
* 如何从经过分类训练的模型中提取特征
* 如何使用 TensorFlow Serving 在生产系统中进行更快的推断
* 如何使用这些功能计算查询图像和目标集之间的相似度
* 使用分类模型进行排名
* 如何提高检索系统的速度
* 从整体上看系统的架构
* 当目标图像过多时,使用自动编码器学习紧凑的描述符
* 训练去噪自动编码器
# 了解视觉特征
深度学习模型经常因无法解释而受到批评。 基于神经网络的模型通常被认为像黑匣子,因为人类很难推理出深度学习模型的工作原理。 由于激活函数,深度学习模型对图像进行的层转换是非线性的,因此不容易可视化。 已经开发出了通过可视化深层网络的层来解决对不可解释性的批评的方法。 在本节中,我们将研究可视化深层的尝试,以便了解模型的工作原理。
可视化可以使用模型的激活和渐变来完成。 可以使用以下技术可视化激活:
* **最近的邻居**:可以对图像进行层激活,并且可以一起看到该激活的最近图像。
* **降维**:激活的尺寸可以通过**主成分分析****PCA** )或 **t 分布随机邻居** **嵌入****t-SNE** ),可在二维或三维中可视化。 PCA 通过将值投影到最大方差方向来减小尺寸。 t-SNE 通过将最接近的点映射到三个维度来减小维度。 降维的使用及其技术超出了本书的范围。 建议您参考基本的机器学习材料,以了解有关降维的更多信息。
Wikipedia is a good source for understanding dimensionality reduction techniques. Here are a few links that you can refer to:
* [https://zh.wikipedia.org/wiki/Dimensionality_reduction](https://en.wikipedia.org/wiki/Dimensionality_reduction)
* [https://zh.wikipedia.org/wiki/Principal_component_analysis](https://en.wikipedia.org/wiki/Principal_component_analysis)
* [https://zh.wikipedia.org/wiki/T-distributed_stochastic_neighbor_embedding](https://en.wikipedia.org/wiki/T-distributed_stochastic_neighbor_embedding)
* [https://en.wikipedia.org/wiki/Locality-sensitive_hashing](https://en.wikipedia.org/wiki/Locality-sensitive_hashing)
* **最大补丁**:激活一个神经元,并捕获最大激活的相应补丁。
* **遮挡**:在各个位置遮挡(遮挡)图像,并且激活以热图显示,以了解图像的哪些部分很重要。
在以下各节中,我们将看到如何实现这些功能的可视化。
# 可视化深度学习模型的激活
任何层的过滤器都可以可视化任何模型架构。 使用该技术只能理解初始层。 最后一层对于最近的邻居方法很有用。 当`ImageNet`数据集与最近的邻居排列在一起时,其外观如下:
![](img/f13aef48-ee95-48ba-a9d8-87149edc5ea7.jpg)
查看此图像,您可以看到相同的对象一起出现。 有趣的事情之一是,诸如狗,猴子和猎豹之类的动物虽然没有经过一个标签的训练却同时出现。 当对象相似时,图像的最近邻居可视化非常有用,因此,我们可以了解模型的预测。 最后一层也可以通过降维技术(例如主成分分析和 t-SNE)进行可视化。 在下一节中,我们将看到使用降维的可视化实现。
# 嵌入可视化
可以使用 TensorBoard 以二维或三维可视化嵌入层(即预最终层)。 假定本节中的代码段位于图像分类一章中训练的卷积神经网络模型之后。 首先,我们需要一个元数据文件,它是一个制表符分隔的文件。 元数据文件的每一行都应具有将要可视化的图像标签。 需要一个新变量来存储在会话创建和初始化之间定义的嵌入,如以下代码所示:
```py
no_embedding_data = 1000 embedding_variable = tf.Variable(tf.stack(
mnist.test.images[:no_embedding_data], axis=0), trainable=False)
```
我们将获取 MNIST 测试数据,并创建用于可视化的元数据文件,如下所示:
```py
metadata_path = '/tmp/train/metadata.tsv' with open(metadata_path, 'w') as metadata_file:
for i in range(no_embedding_data):
metadata_file.write('{}\n'.format(
np.nonzero(mnist.test.labels[::1])[1:][0][i]))
```
如上代码所示,应通过设置参数使嵌入变量不可训练。 接下来,必须定义投影仪配置。 它必须具有`tensor_name`,它是嵌入变量名称,元数据文件的路径和子画面图像。 子画面图像是一个带有小图像的图像,表示要通过嵌入可视化的标签。 以下是用于定义嵌入投影的代码:
```py
from tensorflow.contrib.tensorboard.plugins import projector
projector_config = projector.ProjectorConfig()
embedding_projection = projector_config.embeddings.add()
embedding_projection.tensor_name = embedding_variable.name
embedding_projection.metadata_path = metadata_path
embedding_projection.sprite.image_path = os.path.join(work_dir + '/mnist_10k_sprite.png')
embedding_projection.sprite.single_image_dim.extend([28, 28])
```
必须指定子画面图像尺寸。 然后,可以使用投影机通过摘要编写器和配置来可视化嵌入,如以下代码所示:
```py
projector.visualize_embeddings(train_summary_writer, projector_config)
tf.train.Saver().save(session, '/tmp/train/model.ckpt', global_step=1)
```
然后,将模型与会话一起保存。 然后转到 TensorBoard 查看以下可视化效果:
![](img/12a15ca8-cf56-4557-8f9c-6ee86903ed4f.png)
TensorBoard 说明了代码的输出
您必须通过按钮选择 T-SNE 和颜色,如屏幕截图所示,以获得类似的可视化效果。 您可以看到数字如何一起出现。 该可视化对于检查数据和经过训练的嵌入非常有用。 这是 TensorBoard 的另一个强大功能。 在下一部分中,我们将实现可视化的引导反向传播。
# 引导反向传播
直接将特征可视化可能会减少信息量。 因此,我们使用反向传播的训练过程来激活滤镜以实现更好的可视化。 由于我们选择了要激活的神经元以进行反向传播,因此称为引导反向传播。 在本节中,我们将实现引导式反向传播以可视化功能。
我们将定义大小并加载 VGG 模型,如下所示:
```py
image_width, image_height = 128, 128 vgg_model = tf.keras.applications.vgg16.VGG16(include_top=False)
```
图层由以图层名称作为键的字典组成,模型中的图层以权重作为键值,以方便访问。 现在,我们将从第五个块 `block5_conv1` 中获取第一卷积层,以计算 可视化效果。 输入和输出在此处定义:
```py
input_image = vgg_model.input
vgg_layer_dict = dict([(vgg_layer.name, vgg_layer) for vgg_layer in vgg_model.layers[1:]])
vgg_layer_output = vgg_layer_dict['block5_conv1'].output
```
我们必须定义损失函数。 损失函数将最大化特定层的激活。 这是一个梯度上升过程,而不是通常的梯度下降过程,因为我们正在尝试使损失函数最大化。 对于梯度上升,平滑梯度很重要。 因此,在这种情况下,我们通过归一化像素梯度来平滑梯度。 该损失函数快速收敛而不是。
应该对图像的输出进行归一化以可视化,在优化过程中使用 g 辐射上升来获得函数的最大值。 现在,我们可以通过定义评估器和梯度来开始梯度上升优化,如下所示。 现在,必须定义损失函数,并要计算的梯度。 迭代器通过迭代计算损耗和梯度值,如下所示:
```py
filters = []
for filter_idx in range(20):
loss = tf.keras.backend.mean(vgg_layer_output[:, :, :, filter_idx])
gradients = tf.keras.backend.gradients(loss, input_image)[0]
gradient_mean_square = tf.keras.backend.mean(tf.keras.backend.square(gradients))
gradients /= (tf.keras.backend.sqrt(gradient_mean_square) + 1e-5)
evaluator = tf.keras.backend.function([input_image], [loss, gradients])
```
输入是随机的灰度图像,并添加了一些噪声。 如此处所示,将生成随机图像并完成缩放。
```py
gradient_ascent_step = 1.
input_image_data = np.random.random((1, image_width, image_height, 3))
input_image_data = (input_image_data - 0.5) * 20 + 128
```
现在开始对损失函数进行优化,对于某些滤波器,损失值可能为 0,应将其忽略,如下所示:
```py
for i in range(20):
loss_value, gradient_values = evaluator([input_image_data])
input_image_data += gradient_values * gradient_ascent_step
# print('Loss :', loss_value)
if loss_value <= 0.:
break
```
优化之后,通过均值减去并调整标准偏差来完成归一化。 然后,可以按比例缩小滤镜并将其裁剪到其渐变值,如下所示:
```py
if loss_value > 0:
filter = input_image_data[0]
filter -= filter.mean()
filter /= (filter.std() + 1e-5)
filter *= 0.1
filter += 0.5
filter = np.clip(filter, 0, 1)
filter *= 255
filter = np.clip(filter, 0, 255).astype('uint8')
filters.append((filter, loss_value))
```
这些过滤器是随机选择的,并在此处可视化:
![](img/f741f405-d2e9-4598-822b-d269d004e882.png)
如图所示,用于缝合图像并产生输出的代码与代码束一起提供。 由于修道院的接受区域变大,因此可视化在以后的层变得复杂。 一些滤镜看起来很相似,但只是旋转而已。 在这种情况下,可视化的层次结构可以清楚地看到,如 Zeiler 等人所示。 ( [https://arxiv.org/pdf/1412.6572.pdf](https://arxiv.org/pdf/1412.6572.pdf) )。 下图显示了不同层的直接可视化:
![](img/810e2adf-2cf5-44d2-b729-91dfd56cadb1.png)
经 Zeiler 等人许可复制。
前两层看起来像边缘和角落检测器。 类似于 Gabor 的滤镜仅出现在第三层中。 Gabor 滤波器是线性的,传统上用于纹理分析。 我们已经直接通过引导反向传播看到了特征的可视化。 接下来,我们将看到如何实现 DeepDream 进行可视化。
# 深梦
可以在网络中的某些层上放大神经元激活,而不是合成图像。 放大原始图像以查看特征效果的概念称为 **DeepDream** 。 创建 DeepDream 的步骤是:
1. 拍摄图像并从 CNN 中选择一个图层。
2. 在特定的层进行激活。
3. 修改渐变,以使渐变和激活相等。
4. 计算图像和反向传播的梯度。
5. 必须使用正则化对图像进行抖动和归一化。
6. 像素值应修剪。
7. 为了实现分形效果,对图像进行了多尺度处理。
让我们从导入相关的包开始:
```py
import os
import numpy as np
import PIL.Image
import urllib.request
from tensorflow.python.platform import gfile
import zipfile
```
初始模型在`Imagenet`数据集和 Google 提供的模型文件上进行了预训练。 我们可以下载该模型并将其用于本示例。 模型文件的 ZIP 归档文件已下载并解压缩到一个文件夹中,如下所示:
```py
model_url = 'https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip' file_name = model_url.split('/')[-1]
file_path = os.path.join(work_dir, file_name)
if not os.path.exists(file_path):
file_path, _ = urllib.request.urlretrieve(model_url, file_path)
zip_handle = zipfile.ZipFile(file_path, 'r')
zip_handle.extractall(work_dir)
zip_handle.close()
```
这些命令应该在工作目录中创建了三个新文件。 可以将此预训练的模型加载到会话中,如下所示:
```py
graph = tf.Graph()
session = tf.InteractiveSession(graph=graph)
model_path = os.path.join(work_dir, 'tensorflow_inception_graph.pb')
with gfile.FastGFile(model_path, 'rb') as f:
graph_defnition = tf.GraphDef()
graph_defnition.ParseFromString(f.read())
```
会话从图形初始化开始。 然后,将下载的模型的图形定义加载到内存中。 作为预处理步骤,必须从输入中减去`ImageNet`平均值,如下所示。 预处理后的图像随后被馈送到该图,如下所示:
```py
input_placeholder = tf.placeholder(np.float32, name='input')
imagenet_mean_value = 117.0 preprocessed_input = tf.expand_dims(input_placeholder-imagenet_mean_value, 0)
tf.import_graph_def(graph_defnition, {'input': preprocessed_input})
```
现在,会话和图形已准备好进行推断。 双线性插值需要`resize_image`功能。 可以添加`resize`函数方法,该函数通过 TensorFlow 会话来调整图像的大小,如下所示:
```py
def resize_image(image, size):
resize_placeholder = tf.placeholder(tf.float32)
resize_placeholder_expanded = tf.expand_dims(resize_placeholder, 0)
resized_image = tf.image.resize_bilinear(resize_placeholder_expanded, size)[0, :, :, :]
return session.run(resized_image, feed_dict={resize_placeholder: image})
```
可以将工作目录中的图像加载到内存中并转换为浮点值,如下所示:
```py
image_name = 'mountain.jpg' image = PIL.Image.open(image_name)
image = np.float32(image)
```
此处显示了已加载的图像,供您参考:
![](img/a6cf4c5d-de28-4bec-995d-07b57bfbe560.jpg)
音阶空间的八度音阶数,大小和音阶在此处定义:
```py
no_octave = 4 scale = 1.4 window_size = 51
```
这些值在此处显示的示例中效果很好,因此需要根据其大小调整其他图像。 可以选择一个层来做梦,该层的平均平均值将是`objective`函数,如下所示:
```py
score = tf.reduce_mean(objective_fn)
gradients = tf.gradients(score, input_placeholder)[0]
```
计算图像的梯度以进行优化。 可以通过将图像调整为各种比例并找到差异来计算八度图像,如下所示:
```py
octave_images = []
for i in range(no_octave - 1):
image_height_width = image.shape[:2]
scaled_image = resize_image(image, np.int32(np.float32(image_height_width) / scale))
image_difference = image - resize_image(scaled_image, image_height_width)
image = scaled_image
octave_images.append(image_difference)
```
现在可以使用所有八度图像运行优化。 窗口在图像上滑动,计算渐变激活以创建梦,如下所示:
```py
for octave_idx in range(no_octave):
if octave_idx > 0:
image_difference = octave_images[-octave_idx]
image = resize_image(image, image_difference.shape[:2]) + image_difference
for i in range(10):
image_heigth, image_width = image.shape[:2]
sx, sy = np.random.randint(window_size, size=2)
shifted_image = np.roll(np.roll(image, sx, 1), sy, 0)
gradient_values = np.zeros_like(image)
for y in range(0, max(image_heigth - window_size // 2, window_size), window_size):
for x in range(0, max(image_width - window_size // 2, window_size), window_size):
sub = shifted_image[y:y + window_size, x:x + window_size]
gradient_windows = session.run(gradients, {input_placeholder: sub})
gradient_values[y:y + window_size, x:x + window_size] = gradient_windows
gradient_windows = np.roll(np.roll(gradient_values, -sx, 1), -sy, 0)
image += gradient_windows * (1.5 / (np.abs(gradient_windows).mean() + 1e-7))
```
现在,创建 DeepDream 的优化已完成,可以通过剪切值来保存,如下所示:
```py
image /= 255.0 image = np.uint8(np.clip(image, 0, 1) * 255)
PIL.Image.fromarray(image).save('dream_' + image_name, 'jpeg')
```
在本节中,我们已经看到了创建 DeepDream 的过程。 结果显示在这里:
![](img/1c9b8a0a-3621-4644-97e5-1c0158a051dd.jpg)
如我们所见,狗到处都被激活。 您可以尝试其他各种层并查看结果。 这些结果可用于艺术目的。 类似地,可以激活其他层以产生不同的伪像。 在下一节中,我们将看到一些对抗性示例,这些示例可能会欺骗深度学习模型。
# 对抗性例子
在几个数据集上,图像分类算法已达到人类水平的准确性。 但是它们可以被对抗性例子轻易地欺骗。 对抗示例是合成图像,它们使模型无法产生所需的结果。 拍摄任何图像,然后选择不正确的随机目标类别。 可以用噪声修改该图像,直到网络被 Goodfellow 等人所欺骗。 ( [https://arxiv.org/pdf/1412.6572.pdf](https://arxiv.org/pdf/1412.6572.pdf) )。 该模型的对抗攻击示例如下所示:
![](img/5fc3a7dd-4e49-4215-a821-e026811ca3fd.png)
经 Goodfellow 等人许可复制。
在此图中,左侧显示的图像具有特定标签的 58%可信度。 左边的图像与中间显示的噪声结合在一起时,在右边形成图像。 对于人来说,带有噪点的图像看起来还是一样。 但是带有噪点的图像可以通过具有 97%置信度的其他标签来预测。 尽管图像具有非常不同的对象,但仍将高置信度分配给特定示例。 这是深度学习模型的问题,因此,您应该了解这在哪里适用:
* 甚至可以在不访问模型的情况下生成对抗性示例。 您可以训练自己的模型,生成对抗性示例,但仍然可以欺骗其他模型。
* 在实践中这种情况很少发生,但是当有人试图欺骗系统来发送垃圾邮件或崩溃时,这将成为一个真正的问题。
* 所有机器学习模型都容易受到此问题的影响,而不仅仅是深度学习模型。
您应该考虑对抗性示例,了解在安全关键系统上部署深度学习模型的后果。 在下一节中,我们将看到如何利用 TensorFlow Serving 获得更快的推断。
# 模型推论
任何新数据都可以传递给模型以获取结果。 从图像获取分类结果或特征的过程称为 **推论**。 训练和推理通常在不同的计算机上和不同的时间进行。 我们将学习如何存储模型,运行推理以及如何使用 TensorFlow Serv 作为具有良好延迟和吞吐量的服务器。
# 导出模型
训练后的模型必须导出并保存。 权重,偏差和图形都存储用于推断。 我们将训练 MNIST 模型并将其存储。 首先使用以下代码定义所需的常量:
```py
work_dir = '/tmp' model_version = 9 training_iteration = 1000 input_size = 784 no_classes = 10 batch_size = 100 total_batches = 200
```
`model_version`可以是一个整数,用于指定我们要导出以供服务的模型。 `feature config`存储为具有占位符名称及其对应数据类型的字典。 应该映射预测类及其标签。 身份占位符可与 API 配合使用:
```py
tf_example = tf.parse_example(tf.placeholder(tf.string, name='tf_example'),
{'x': tf.FixedLenFeature(shape=[784], dtype=tf.float32), })
x_input = tf.identity(tf_example['x'], name='x')
```
可以使用以下代码使用权重,偏差,对数和优化器定义一个简单的分类器:
```py
y_input = tf.placeholder(tf.float32, shape=[None, no_classes])
weights = tf.Variable(tf.random_normal([input_size, no_classes]))
bias = tf.Variable(tf.random_normal([no_classes]))
logits = tf.matmul(x_input, weights) + bias
softmax_cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=y_input, logits=logits)
loss_operation = tf.reduce_mean(softmax_cross_entropy)
optimiser = tf.train.GradientDescentOptimizer(0.5).minimize(loss_operation)
```
训练模型,如以下代码所示:
```py
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
for batch_no in range(total_batches):
mnist_batch = mnist.train.next_batch(batch_size)
_, loss_value = session.run([optimiser, loss_operation], feed_dict={
x_input: mnist_batch[0],
y_input: mnist_batch[1]
})
print(loss_value)
```
定义预测签名,并导出模型。 将模型保存到持久性存储中,以便可以在以后的时间点进行推理。 这将通过反序列化导出数据,并将其存储为其他系统可以理解的格式。 具有不同变量和占位符的多个图可用于导出。 它还支持`signature_defs` 和资产。 `signature_defs`指定了输入和输出,因为将从外部客户端访问输入和输出。 资产是将用于推理的非图形组件,例如词汇表等。
分类签名使用对 TensorFlow 分类 API 的访问权限。 输入是强制性的,并且有两个可选输出(预测类别和预测概率),其中至少一个是强制性的。 预测签名提供输入和输出数量的灵活性。 可以定义多个输出并从客户端显式查询。 `signature_def`显示在此处:
```py
signature_def = (
tf.saved_model.signature_def_utils.build_signature_def(
inputs={'x': tf.saved_model.utils.build_tensor_info(x_input)},
outputs={'y': tf.saved_model.utils.build_tensor_info(y_input)},
method_name="tensorflow/serving/predict"))
```
最后,使用预测签名将元图和变量添加到构建器中:
```py
model_path = os.path.join(work_dir, str(model_version))
saved_model_builder = tf.saved_model.builder.SavedModelBuilder(model_path)
saved_model_builder.add_meta_graph_and_variables(
session, [tf.saved_model.tag_constants.SERVING],
signature_def_map={
'prediction': signature_def
},
legacy_init_op=tf.group(tf.tables_initializer(), name='legacy_init_op'))
saved_model_builder.save()
```
该构建器已保存,可以由服务器使用。 所示示例适用于任何模型,并可用于导出。 在下一部分中,我们将服务并查询导出的模型。
# 服务训练过的模型
可以使用以下命令通过 TensorFlow Serving 服务上一节中导出的模型:
```py
tensorflow_model_server --port=9000 --model_name=mnist --model_base_path=/tmp/mnist_model/
```
`model_base_path` 指向导出模型的目录。 现在可以与客户端一起测试服务器。 请注意,这不是 HTTP 服务器,因此需要此处显示的客户端而不是 HTTP 客户端。 导入所需的库:
```py
from grpc.beta import implementations
import numpy
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2
```
添加并发常数,测试数量和工作目录。 定义了一个类,用于对返回的结果进行计数。 定义了**远程过程调用****RPC** )回调,并带有用于对预测计数的计数器,如下所示:
```py
concurrency = 1 num_tests = 100 host = '' port = 8000 work_dir = '/tmp' def _create_rpc_callback():
def _callback(result):
response = numpy.array(
result.result().outputs['y'].float_val)
prediction = numpy.argmax(response)
print(prediction)
return _callback
```
根据您的要求修改 `host``port``_callback`方法定义了从服务器返回响应时所需的步骤。 在这种情况下,将计算最大概率。 通过调用服务器来运行推断:
```py
test_data_set = mnist.test
test_image = mnist.test.images[0]
predict_request = predict_pb2.PredictRequest()
predict_request.model_spec.name = 'mnist' predict_request.model_spec.signature_name = 'prediction' predict_channel = implementations.insecure_channel(host, int(port))
predict_stub = prediction_service_pb2.beta_create_PredictionService_stub(predict_channel)
predict_request.inputs['x'].CopyFrom(
tf.contrib.util.make_tensor_proto(test_image, shape=[1, test_image.size]))
result = predict_stub.Predict.future(predict_request, 3.0)
result.add_done_callback(
_create_rpc_callback())
```
反复调用推理以评估准确性,延迟和吞吐量。 推断错误率应该在 90%左右,并且并发性应该很高。 导出和客户端方法可用于任何模型,以从模型获得结果和特征。 在下一节中,我们将构建检索管道。
# 基于内容的图像检索
**基于内容的图像检索****CBIR** )的技术将查询图像作为输入,并对目标图像数据库中的图像进行排名,从而产生输出。 CBIR 是具有特定目标的图像到图像搜索引擎。 要检索需要目标图像数据库。 返回距查询图像最小距离的目标图像。 我们可以直接将图像用于相似性,但是问题如下:
* 图像尺寸巨大
* 像素中有很多冗余
* 像素不携带语义信息
因此,我们训练了一个用于对象分类的模型,并使用该模型中的特征进行检索。 然后,我们通过相同的模型传递查询图像和目标数据库以获得特征。 这些模型也可以称为**编码器**,因为它们对特定任务的图像信息进行编码。 编码器应该能够捕获全局和局部特征。 我们可以使用我们在图像分类一章中研究过的模型,这些模型经过训练可以进行分类任务。 由于强力扫描或线性扫描速度较慢,因此图像搜索可能会花费大量时间。 因此,需要一些用于更快检索的方法。 以下是一些加快匹配速度的方法:
* **局部敏感哈希****LSH** ):L SH 将要素投影到其子空间,并可以向候选对象提供列表,并在以后进行精细特征排名。 这也是我们本章前面介绍的降维技术,例如 PCA 和 t-SNE。 它具有较小尺寸的铲斗。
* **多索引哈希**:此方法对功能进行哈希处理,就像信鸽拟合一样,可以使其更快。 它使用汉明距离来加快计算速度。 汉明距离不过是以二进制表示的数字的位置差异的数量。
这些方法更快,需要更少的内存,但要权衡准确性。 这些方法也没有捕获语义上的差异。 可以根据查询对匹配结果进行重新排名以获得更好的结果。 重新排序可以通过对返回的目标图像重新排序来改善结果。 重新排序可以使用以下技术之一:
* **几何验证**:此方法将几何图形和目标图像与仅返回相似几何图形的目标图像进行匹配。
* **查询扩展**:这将扩展目标图像列表并详尽搜索它们。
* **相关性反馈**:此方法从使用中获取反馈并返回结果。 根据用户输入,将进行重新排名。
这些技术已针对文本进行了很好的开发,可用于图像。 在本章中,我们将重点介绍提取特征并将其用于 CBIR。 在下一节中,我们将学习如何进行模型推断。
# 建立检索管道
从查询图像的目标图像中获得最佳匹配的步骤序列称为**检索管道**。 检索管道具有多个步骤或组件。 图像数据库的功能必须脱机提取并存储在数据库中。 对于每个查询图像,必须提取特征并且必须在所有目标图像之间计算相似度。 然后,可以对图像进行排名以最终输出。 检索管道如下所示:
![](img/c9ea2c8e-b149-4890-8a5e-ba4d03ade21e.png)
特征提取步骤必须快速,为此可以使用 TensorFlow Serving。 您可以根据应用程序选择使用哪些功能。 例如,当需要基于纹理的匹配时可以使用初始层,而当必须在对象级别进行匹配时可以使用更高的层。 在下一部分中,我们将看到如何从预训练的初始模型中提取特征。
# 提取图像的瓶颈特征
瓶颈要素是在预分类层中计算的值。 在本节中,我们将看到如何使用 TensorFlow 从预训练的模型中提取 b ottleneck 特征。 首先,使用以下代码导入所需的库:
```py
import os
import urllib.request
from tensorflow.python.platform import gfile
import tarfile
```
然后,我们需要下载带有图形定义及其权重的预训练模型。 TensorFlow 已使用初始架构在`ImageNet`数据集上训练了一个模型,并提供了该模型。 我们将使用以下代码下载该模型并将其解压缩到本地文件夹中:
```py
model_url = 'http://download.tensorflow.org/models/image/imagenet/inception-2015-12-05.tgz' file_name = model_url.split('/')[-1]
file_path = os.path.join(work_dir, file_name)
if not os.path.exists(file_path):
file_path, _ = urllib.request.urlretrieve(model_url, file_path)
tarfile.open(file_path, 'r:gz').extractall(work_dir)
```
仅当模型不存在时,这会创建一个文件夹并下载模型。 如果重复执行代码,则不会每次都下载模型。 该图形以**协议缓冲区****protobuf** )格式存储在文件中。 必须将其读取为字符串,然后传递给`tf.GraphDef()`对象以将其带入内存:
```py
model_path = os.path.join(work_dir, 'classify_image_graph_def.pb')
with gfile.FastGFile(model_path, 'rb') as f:
graph_defnition = tf.GraphDef()
graph_defnition.ParseFromString(f.read())
```
在初始模型中,瓶颈层名为`pool_3/_reshape:0`,并且该层的尺寸为 2,048 。 输入的占位符名称为`DecodeJpeg/contents:0`,调整大小张量名称为`ResizeBilinear:0`。 我们可以使用`tf.import_graph_def`和所需的返回张量导入图形定义,以进行进一步的操作:
```py
bottleneck, image, resized_input = (
tf.import_graph_def(
graph_defnition,
name='',
return_elements=['pool_3/_reshape:0',
'DecodeJpeg/contents:0',
'ResizeBilinear:0'])
)
```
进行查询和目标图像并将其加载到内存中。 `gfile`功能提供了一种更快的方式将图像加载到内存中。
```py
query_image_path = os.path.join(work_dir, 'cat.1000.jpg')
query_image = gfile.FastGFile(query_image_path, 'rb').read()
target_image_path = os.path.join(work_dir, 'cat.1001.jpg')
target_image = gfile.FastGFile(target_image_path, 'rb').read()
```
让我们定义一个使用`session`和图像从图像中提取瓶颈特征的函数:
```py
def get_bottleneck_data(session, image_data):
bottleneck_data = session.run(bottleneck, {image: image_data})
bottleneck_data = np.squeeze(bottleneck_data)
return bottleneck_data
```
启动会话,并传递图像以运行前向推理,以从预先训练的模型中获取瓶颈值:
```py
query_feature = get_bottleneck_data(session, query_image)
print(query_feature)
target_feature = get_bottleneck_data(session, target_image)
print(target_feature)
```
运行上面的代码应显示如下:
```py
[ 0.55705792 0.36785451 1.06618118 ..., 0.6011821 0.36407694
0.0996572 ]
[ 0.30421323 0.0926369 0.26213276 ..., 0.72273785 0.30847171
0.08719242]
```
该计算特征的过程可以按比例缩放以获取更多目标图像。 使用这些值,可以在查询图像和目标数据库之间计算相似度,如以下部分所述。
# 计算查询图像与目标数据库之间的相似度
NumPy 的`linalg.norm`可用于计算**欧几里德距离**。 可以通过计算要素之间的欧几里得距离来计算查询图像与目标数据库之间的相似度,如下所示:
```py
dist = np.linalg.norm(np.asarray(query_feature) - np.asarray(target_feature))
print(dist)
```
运行此命令应打印以下内容:
```py
16.9965
```
这是可用于相似度计算的度量。 查询与目标图像之间的欧几里得距离越小,图像越相似。 因此,计算欧几里得距离是相似度的量度。 使用特征来计算欧几里得距离是基于这样的假设:在训练模型的过程中学习了这些特征。 将这种计算扩展成数百万个图像效率不高。 在生产系统中,期望以毫秒为单位返回结果。 在下一节中,我们将看到如何提高检索效率。
# 高效检索
检索可能很慢,因为它是蛮力方法。 使用近似最近的邻居可以使匹配更快。 维度的诅咒也开始出现,如下图所示:
![](img/d3c91098-f5f8-42ed-9fac-3ee18ba2baee.png)
随着维数的增加,复杂度也从二维维增加到三个维。 距离的计算也变慢。 为了使距离搜索更快,我们将在下一部分中讨论一种近似方法。
# 使用近似最近的邻居更快地匹配
**近似最近邻居****ANNOY** )是一种用于更快进行最近邻居搜索的方法。 ANNOY 通过随机投影来构建树。 树结构使查找最接近的匹配更加容易。 您可以创建`ANNOYIndex`以便快速检索,如下所示:
```py
def create_annoy(target_features):
t = AnnoyIndex(layer_dimension)
for idx, target_feature in enumerate(target_features):
t.add_item(idx, target_feature)
t.build(10)
t.save(os.path.join(work_dir, 'annoy.ann'))
create_annoy(target_features)
```
创建索引需要要素的尺寸。 然后将项目添加到索引并构建树。 树木的数量越多,在时间和空间复杂度之间进行权衡的结果将越准确。 可以创建索引并将其加载到内存中。 可以查询 ANNOY,如下所示:
```py
annoy_index = AnnoyIndex(10)
annoy_index.load(os.path.join(work_dir, 'annoy.ann'))
matches = annoy_index.get_nns_by_vector(query_feature, 20)
```
匹配项列表可用于检索图像详细信息。 项目的索引将被返回。
请访问 [https://github.com/spotify/annoy](https://github.com/spotify/annoy) 以获取`ANNOY`的完整实现,以及其在准确性和速度方面与其他近似最近邻算法的基准比较。
# ANNOY 的优点
使用 ANNOY 的原因很多。 主要优点如下:
* 具有内存映射的数据结构,因此对 RAM 的占用较少。 因此,可以在多个进程之间共享同一文件。
* 可以使用曼哈顿,余弦或欧几里得等多种距离来计算查询图像和目标数据库之间的相似度。
# 原始图像的自动编码器
自动编码器是一种用于生成有效编码的无监督算法。 输入层和目标输出通常相同。 减少和增加之间的层以下列方式:
![](img/2cc688ac-9e9f-447e-b46e-295eea60e0e9.png)
**瓶颈**层是尺寸减小的中间层。 瓶颈层的左侧称为**编码器**,右侧称为**解码器**。 编码器通常减小数据的尺寸,而解码器增大尺寸。 编码器和解码器的这种组合称为自动编码器。 整个网络都经过重建错误训练。 从理论上讲,可以存储瓶颈层,并可以通过解码器网络重建原始数据。 如下所示,这可以减小尺寸并易于编程。 使用以下代码定义卷积,解卷积和完全连接的层:
```py
def fully_connected_layer(input_layer, units):
return tf.layers.dense(
input_layer,
units=units,
activation=tf.nn.relu
)
def convolution_layer(input_layer, filter_size):
return tf.layers.conv2d(
input_layer,
filters=filter_size,
kernel_initializer=tf.contrib.layers.xavier_initializer_conv2d(),
kernel_size=3,
strides=2
)
def deconvolution_layer(input_layer, filter_size, activation=tf.nn.relu):
return tf.layers.conv2d_transpose(
input_layer,
filters=filter_size,
kernel_initializer=tf.contrib.layers.xavier_initializer_conv2d(),
kernel_size=3,
activation=activation,
strides=2
)
```
定义具有五层卷积的会聚编码器,如以下代码所示:
```py
input_layer = tf.placeholder(tf.float32, [None, 128, 128, 3])
convolution_layer_1 = convolution_layer(input_layer, 1024)
convolution_layer_2 = convolution_layer(convolution_layer_1, 512)
convolution_layer_3 = convolution_layer(convolution_layer_2, 256)
convolution_layer_4 = convolution_layer(convolution_layer_3, 128)
convolution_layer_5 = convolution_layer(convolution_layer_4, 32)
```
通过展平第五个卷积层来计算瓶颈层。 再次将瓶颈层重新成形为卷积层,如下所示:
```py
convolution_layer_5_flattened = tf.layers.flatten(convolution_layer_5)
bottleneck_layer = fully_connected_layer(convolution_layer_5_flattened, 16)
c5_shape = convolution_layer_5.get_shape().as_list()
c5f_flat_shape = convolution_layer_5_flattened.get_shape().as_list()[1]
fully_connected = fully_connected_layer(bottleneck_layer, c5f_flat_shape)
fully_connected = tf.reshape(fully_connected,
[-1, c5_shape[1], c5_shape[2], c5_shape[3]])
```
计算可以重建图像的发散或解码器部分,如以下代码所示:
```py
deconvolution_layer_1 = deconvolution_layer(fully_connected, 128)
deconvolution_layer_2 = deconvolution_layer(deconvolution_layer_1, 256)
deconvolution_layer_3 = deconvolution_layer(deconvolution_layer_2, 512)
deconvolution_layer_4 = deconvolution_layer(deconvolution_layer_3, 1024)
deconvolution_layer_5 = deconvolution_layer(deconvolution_layer_4, 3,
activation=tf.nn.tanh)
```
该网络经过培训,可以快速收敛。 传递图像特征时可以存储瓶颈层。 这有助于减少可用于检索的数据库的大小。 仅需要编码器部分即可为特征建立索引。 自动编码器是一种有损压缩算法。 它与其他压缩算法不同,因为它从数据中学习压缩模式。 因此,自动编码器模型特定于数据。 自动编码器可以与 t-SNE 结合使用以获得更好的可视化效果。 自动编码器学习的瓶颈层可能对其他任务没有用。 瓶颈层的大小可以大于以前的层。 在这种分叉和收敛连接的情况下,稀疏的自动编码器就会出现。 在下一节中,我们将学习自动编码器的另一种应用。
# 使用自动编码器进行降噪
自动编码器也可以用于图像去噪。 去噪是从图像中去除噪点的过程。 去噪编码器可以无监督的方式进行训练。 可以在正常图像中引入噪声,并针对原始图像训练自动编码器。 以后,可以使用完整的自动编码器生成无噪声的图像。 在本节中,我们将逐步说明如何去噪 MNIST 图像。 导入所需的库并定义占位符,如下所示:
```py
x_input = tf.placeholder(tf.float32, shape=[None, input_size])
y_input = tf.placeholder(tf.float32, shape=[None, input_size])
```
`x_input``y_input`的形状与自动编码器中的形状相同。 然后,定义一个密集层,如下所示,默认激活为`tanh`激活功能。 `add_variable_summary`方法是从图像分类章节示例中导入的。 密集层的定义如下所示:
```py
def dense_layer(input_layer, units, activation=tf.nn.tanh):
layer = tf.layers.dense(
inputs=input_layer,
units=units,
activation=activation
)
add_variable_summary(layer, 'dense')
return layer
```
接下来,可以定义自动编码器层。 该自动编码器仅具有完全连接的层。 编码器部分具有减小尺寸的三层。 解码器部分具有增加尺寸的三层。 编码器和解码器都是对称的,如下所示:
```py
layer_1 = dense_layer(x_input, 500)
layer_2 = dense_layer(layer_1, 250)
layer_3 = dense_layer(layer_2, 50)
layer_4 = dense_layer(layer_3, 250)
layer_5 = dense_layer(layer_4, 500)
layer_6 = dense_layer(layer_5, 784)
```
隐藏层的尺寸是任意选择的。 接下来,定义`loss``optimiser`。 这里我们使用 Sigmoid 代替 softmax 作为分类,如下所示:
```py
with tf.name_scope('loss'):
softmax_cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits(
labels=y_input, logits=layer_6)
loss_operation = tf.reduce_mean(softmax_cross_entropy, name='loss')
tf.summary.scalar('loss', loss_operation)
with tf.name_scope('optimiser'):
optimiser = tf.train.AdamOptimizer().minimize(loss_operation)
```
TensorBoard 提供了另一种称为`image,`的摘要,可用于可视化图像。 我们将使用输入`layer_6`并将其重塑形状以将其添加到摘要中,如下所示:
```py
x_input_reshaped = tf.reshape(x_input, [-1, 28, 28, 1])
tf.summary.image("noisy_images", x_input_reshaped)
y_input_reshaped = tf.reshape(y_input, [-1, 28, 28, 1])
tf.summary.image("original_images", y_input_reshaped)
layer_6_reshaped = tf.reshape(layer_6, [-1, 28, 28, 1])
tf.summary.image("reconstructed_images", layer_6_reshaped)
```
图像数量默认限制为三张,并且可以更改。 这是为了限制其将所有图像都写入摘要文件夹。 接下来,合并所有摘要,并将图添加到摘要编写器,如下所示:
```py
merged_summary_operation = tf.summary.merge_all()
train_summary_writer = tf.summary.FileWriter('/tmp/train', session.graph)
```
可以将正常的随机噪声添加到图像中并作为输入张量馈入。 添加噪声后,多余的值将被裁剪。 目标将是原始图像本身。 此处显示了噪声和训练过程的附加信息:
```py
for batch_no in range(total_batches):
mnist_batch = mnist_data.train.next_batch(batch_size)
train_images, _ = mnist_batch[0], mnist_batch[1]
train_images_noise = train_images + 0.2 * np.random.normal(size=train_images.shape)
train_images_noise = np.clip(train_images_noise, 0., 1.)
_, merged_summary = session.run([optimiser, merged_summary_operation],
feed_dict={
x_input: train_images_noise,
y_input: train_images,
})
train_summary_writer.add_summary(merged_summary, batch_no)
```
开始此培训后,可以在 TensorBoard 中查看结果。 损失显示在此处:
![](img/2ed9d697-3908-4169-927f-99769d2f7bdf.png)
Tensorboard 说明了输出图
损耗稳步下降,并将在迭代过程中保持缓慢下降。 这显示了自动编码器如何快速收敛。 接下来,原始图像显示三位数:
![](img/4e4b465b-0039-41c7-826e-67ba07141257.png)
以下是添加了噪点的相同图像:
![](img/16852acb-354f-43a2-832c-78fb55edb1ff.png)
您会注意到有很大的噪音,这是作为输入给出的。 接下来,是使用去噪自动编码器重建的相同编号的图像:
![](img/fcaa79e6-c455-46a6-8d49-1788aa7be7b6.png)
您会注意到,去噪自动编码器在消除噪声方面做得非常出色。 您可以在测试图像上运行它,并可以看到质量得到保持。 对于更复杂的数据集,可以使用卷积神经网络以获得更好的结果。 该示例展示了计算机视觉深度学习的强大功能,因为它是在无人监督的情况下进行训练的。
# 摘要
在本章中,您学习了如何从图像中提取特征并将其用于 CBIR。 您还学习了如何使用 TensorFlow Serving 来推断图像特征。 我们看到了如何利用近似最近邻或更快的匹配而不是线性扫描。 您了解了散列如何仍可以改善结果。 引入了自动编码器的概念,我们看到了如何训练较小的特征向量以进行搜索。 还显示了使用自动编码器进行图像降噪的示例。 我们看到了使用基于位的比较的可能性,该比较可以将其扩展到数十亿张图像。
在下一章中,我们将看到如何训练对象检测问题的模型。 我们将利用开源模型来获得良好的准确性,并了解其背后的所有算法。 最后,我们将使用所有想法来训练行人检测模型。
\ No newline at end of file
# 物体检测
对象检测是在图像中找到对象位置的动作。 在本章中,我们将通过了解以下主题来学习对象检测技术和实现行人检测:
* 基础知识以及定位和检测之间的区别
* 各种数据集及其描述
* 用于对象定位和检测的算法
* TensorFlow API 用于对象检测
* 训练新的物体检测模型
* 基于 YOLO 算法的移动汽车行人检测
# 检测图像中的物体
近年来,对象检测在应用和研究方面都出现了爆炸式增长。 对象检测是计算机视觉中的重要问题。 与图像分类任务相似,更深的网络在检测方面表现出更好的性能。 目前,这些技术的准确性非常好。 因此,它被用于许多应用中。
图像分类将图像标记为一个整体。 除了标记对象外,找到对象的位置也称为**对象定位**。 通常,对象的位置由直角坐标定义。 在图像中使用直角坐标查找多个对象称为检测。 这是对象检测的示例:
![](img/07fc811c-af39-4bb1-b78d-16a2914667c2.png)
该图显示了带有边界框的四个对象。 我们将学习可以执行查找框任务的算法。 这些应用在自动驾驶汽车和工业物体等机器人视觉领域具有巨大的应用前景。 我们可以将定位和检测任务概括为以下几点:
* 本地化检测标签内图像中的一个对象
* 检测可找到图像中的所有对象以及标签
区别在于对象的数量。 在检测中,存在可变数量的对象。 在设计与定位或检测有关的深度学习模型的体系结构时,此小差异会带来很大的不同。 接下来,我们将看到可用于任务的各种数据集。
# 探索数据集
可用于对象定位和检测的数据集很多。 在本节中,我们将探索研究社区用来评估算法的数据集。 有些数据集带有不同数量的对象,这些对象中标注的范围从 20 到 200 不等,这使得对象检测变得困难。 与其他数据集(每个图像仅包含一个对象)相比,某些数据集在一个图像中包含的对象太多。 接下来,我们将详细查看数据集。
# ImageNet 数据集
ImageNet 具有用于评估分类,本地化和检测任务的数据。 [第 2 章](../Text/02.html)*图像分类*详细讨论了分类数据集。 与分类数据类似,本地化任务有 1,000 个类别。 准确度是根据前五次检测得出的。 所有图像中至少会有一个边界框。 有 470,000 张图像的检测问题有 200 个对象,每个图像平均有 1.1 个对象。
# PASCAL VOC 挑战
PASCAL VOC 挑战赛于 2005 年至 2012 年进行。该挑战赛被认为是物体检测技术的基准。 数据集中有 20 个类别。 该数据集包含用于训练和验证的 11,530 张图像,以及针对感兴趣区域的 27,450 条注释。 以下是数据集中存在的二十个类:
* 人: P 人
* 动物: B ird,猫,牛,狗,马,绵羊
* 车辆: A 飞机,自行车,轮船,公共汽车,汽车,摩托车,火车
* 室内: B 水壶,椅子,餐桌,盆栽,沙发,电视/显示器
您可以从 [http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar](http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar) 下载数据集。 每个图像平均有 2.4 个对象。
# 可可物体检测挑战
上下文( **COCO** )数据集中的**通用对象具有 200,000 张图像,其中 80 个类别的注释超过 500,000 个。 它是最广泛的公开可用的对象检测数据库。 下图显示了数据集中存在的对象的列表:**
![](img/277bcdb3-8b17-4c4a-829a-e79e857a354c.png)
每个图像的平均对象数为 7.2。 这些是物体检测挑战的著名数据集。 接下来,我们将学习如何针对这些数据集评估算法。
# 使用指标评估数据集
指标对于深度学习任务中的理解至关重要。 由于人工注释,对象检测和定位的度量是特殊的。 人类可能已经注释了一个名为 **Ground-truth** 的框。 真实性不一定是绝对真理。 此外,盒子的像素可能因人而异。 因此,算法很难检测到人类绘制的确切边界框。 **联合交叉口****IoU** )用于评估定位任务。 **平均精确度平均值****mAP** )用于评估检测任务。 我们将在下一部分中看到指标的描述。
# 联合路口
IoU 是**地面实况**与预测面积的重叠面积与总面积之比。 这是该指标的直观说明:
![](img/6d0305cb-779a-42f0-8bfe-6834eecdca89.png)
这两个正方形代表地面实况和预测的边界框。 IoU 计算为重叠面积与并集面积之比。 这是给定地面真理和预测边界框的 IoU 计算脚本:
```py
def calculate_iou(gt_bb, pred_bb):
'''
:param gt_bb: ground truth bounding box :param pred_bb: predicted bounding box ''' gt_bb = tf.stack([
gt_bb[:, :, :, :, 0] - gt_bb[:, :, :, :, 2] / 2.0,
gt_bb[:, :, :, :, 1] - gt_bb[:, :, :, :, 3] / 2.0,
gt_bb[:, :, :, :, 0] + gt_bb[:, :, :, :, 2] / 2.0,
gt_bb[:, :, :, :, 1] + gt_bb[:, :, :, :, 3] / 2.0])
gt_bb = tf.transpose(gt_bb, [1, 2, 3, 4, 0])
pred_bb = tf.stack([
pred_bb[:, :, :, :, 0] - pred_bb[:, :, :, :, 2] / 2.0,
pred_bb[:, :, :, :, 1] - pred_bb[:, :, :, :, 3] / 2.0,
pred_bb[:, :, :, :, 0] + pred_bb[:, :, :, :, 2] / 2.0,
pred_bb[:, :, :, :, 1] + pred_bb[:, :, :, :, 3] / 2.0])
pred_bb = tf.transpose(pred_bb, [1, 2, 3, 4, 0])
area = tf.maximum(
0.0,
tf.minimum(gt_bb[:, :, :, :, 2:], pred_bb[:, :, :, :, 2:]) -
tf.maximum(gt_bb[:, :, :, :, :2], pred_bb[:, :, :, :, :2]))
intersection_area= area[:, :, :, :, 0] * area[:, :, :, :, 1]
gt_bb_area = (gt_bb[:, :, :, :, 2] - gt_bb[:, :, :, :, 0]) * \
(gt_bb[:, :, :, :, 3] - gt_bb[:, :, :, :, 1])
pred_bb_area = (pred_bb[:, :, :, :, 2] - pred_bb[:, :, :, :, 0]) * \
(pred_bb[:, :, :, :, 3] - pred_bb[:, :, :, :, 1])
union_area = tf.maximum(gt_bb_area + pred_bb_area - intersection_area, 1e-10)
iou = tf.clip_by_value(intersection_area / union_area, 0.0, 1.0)
return iou
```
地面真相和预测的边界框堆叠在一起。 然后在处理负面积的情况下计算面积。 当边界框坐标不正确时,可能会出现负区域。 框的右侧坐标很多发生在从左到左的坐标上。 由于没有保留边界框的结构,因此必然会出现负区域。 计算联合和交叉区域,然后进行最终的 IoU 计算,该计算是与 **地面 真相** 的预测面积与 总面积。 IoU 计算可以与算法结合使用,以训练定位问题。
# 平均平均精度
mAP 用于评估检测算法。 mAP 度量是检测到的边界框的精度和召回率的乘积。 mAP 值的范围是 0 到 100。数字越大,则越好。 可以通过分别为每个类别计算**平均精度****AP** ),然后计算该类别的平均值来计算 mAP。 仅当 mAP 高于 0.5 时,检测结果才被视为真正的阳性。 通过绘制每个类别的绘制精度/召回曲线,可以合并来自测试图像的所有检测。 曲线下的最终区域可用于算法比较。 mAP 是衡量网络灵敏度的一种很好的方法,同时不会引发很多错误警报。 我们已经了解了数据集的评估算法。 接下来,我们将研究本地化任务的算法。
# 本地化算法
定位算法是在[第 2 章](../Text/02.html)*图像分类*[第 3 章](../Text/03.html)*图像检索*中学习的材料的扩展。 在图像分类中,图像经过 CNN(卷积神经网络)的多层。 CNN 的最后一层输出属于每个标签的概率值。 可以扩展它以定位对象。 我们将在以下各节中看到这些想法。
# 使用滑动窗口定位对象
定位的一种直观方法是使用对象预测图像的多个裁剪部分。 可以通过在图像上移动一个窗口并为每个窗口进行预测来完成图像的裁剪。 移动比图像小的窗口并根据窗口大小裁剪图像的方法称为**滑动窗口**。 可以对图像的每个裁剪窗口进行预测,这称为滑动窗口对象检测。
可以通过针对紧密裁剪的图像进行图像分类问题训练的深度学习模型来完成预测。 近距离修剪意味着在整个图像中只会找到一个对象。 窗口在整个图像上的移动必须一致。 图像的每个部分都会通过模型以找到分类。 这种方法有两个问题。
* 它只能找到与窗口大小相同的对象。 如果对象大小大于窗口大小,则滑动窗口将丢失对象。 为了克服这个问题,我们将使用**标度空间** 的概念。
* 另一个问题是,将窗口移到像素上方可能会导致丢失一些对象。 在每个像素上移动窗口会导致大量额外的计算,因此会降低系统速度。 为避免这种情况,我们将在卷积层中加入一个技巧。
在下一节中,我们将介绍这两种技术。
# 比例空间概念
比例空间是使用各种大小的图像的概念。 图像会缩小为较小的尺寸,因此可以在相同尺寸的窗口中检测到较大的物体。 可以使用减小的尺寸将图像调整为某些尺寸。 通过删除替代像素或插值来调整图像大小可能会留下一些伪像。 因此,图像被平滑并迭代调整大小。 通过平滑和调整大小获得的图像是比例空间。
窗口在每个刻度上滑动以定位对象。 运行多个比例相当于使用更大的窗口运行图像。 在多个规模上运行的计算复杂度很高。 可以通过以准确性为代价进行快速移动来加快本地化速度。 复杂性使解决方案无法在生产中使用。 滑动窗口的思想可以通过完全卷积的滑动窗口实现而变得高效。
# 将完全连接的层训练为卷积层
滑动窗口的问题是计算复杂度。 复杂性是因为对每个窗口都进行了预测。 已针对重叠区域的每个窗口计算了深度学习功能。 可以减少裁剪窗口中重叠区域的特征计算。 解决方案是使用仅计算一次特征的全卷积网络。 为了理解全卷积网络,让我们首先看看如何将全连接层转换为`convolution_layer`。 内核更改为相同的大小,并使用与神经元数量相同的过滤器数量。 也可以对其他层重复此操作。 更改内核大小是将完全连接的层转换为`convolution_layer`的简便方法:
```py
convolution_layer_1 = convolution_layer(x_input_reshape, 64)
pooling_layer_1 = pooling_layer(convolution_layer_1)
convolution_layer_2 = convolution_layer(pooling_layer_1, 128)
pooling_layer_2 = pooling_layer(convolution_layer_2)
dense_layer_bottleneck = convolution_layer(pooling_layer_2, 1024, [5, 5])
logits = convolution_layer(dense_layer_bottleneck, no_classes, [1, 1])
logits = tf.reshape(logits, [-1, 10])
```
密集层表示为卷积层。 这个想法在各种情况下都是强大而有用的。 我们将扩展此思想,以将滑动窗口表示为完整的卷积网络。
# 滑动窗口的卷积实现
在这种技术中,最终目标不是滑动,而是变成一些需要深度的目标,并需要多个框作为窗口。 Sermanet 等。 ( [https://arxiv.org/pdf/1312.6229.pdf](https://arxiv.org/pdf/1312.6229.pdf) )使用完全卷积实现来克服滑动窗口的这一问题。 这是滑动窗口的这种卷积实现的说明:
![](img/615888e7-8aac-425c-83be-0802095b2cd8.png)
经 Sermanet 等人许可复制。
在示例的上部,常规分类表示为完全卷积层。 在该图的下部,相同的内核应用于更大的图像,最后生成 2x2 而不是 1。最后一层表示这些边界框的输出中的四个。 具有用于预测的体积可以提高效率,但是盒子仍然存在准确定位的问题。 因此不需要滑动窗口,因此解决了复杂性。 纵横比总是在变化,必须在多个比例尺上看到。 通过完全卷积方法生成的边界框不是很准确。 仅针对额外区域进行额外计算。 可以想像,这些盒子仅限于经过训练的盒子的数量。 接下来,我们将看到一种更准确地检测边界框位置的方法。
# 将本地化视为回归问题
思考本地化的一种基本方法是将问题建模为回归问题。 边界框是四个数字,因此可以通过回归设置以直接方式进行预测。 我们还需要预测标签,这是一个分类问题。
有不同的参数化可用于定义边界框。 边界框通常有四个数字。 表示形式之一是坐标的中心以及边界框的高度和宽度。 通过删除完全连接的层并用回归编码器替换它,可以使用预训练的模型。 必须使用 L2 损失对回归进行正则化,而 L2 损失在异常值方面表现不佳。 L1 的损失比 L1 好。 用平滑化的正则化交换回归更好。 对模型进行微调可提供良好的准确性,而对整个网络进行训练只会带来微不足道的性能改善。 在训练时间和准确性之间进行权衡。 接下来,我们将看到使用卷积网络进行回归的不同应用。
# 将回归应用于其他问题
回归图像坐标适用于其他几种应用程序,例如**姿态检测****基准点检测**。 姿势检测是在人体中发现关节位置的动作,如下所示:
![](img/9e37fff6-eae3-4729-8cf6-c015a4e0b85f.png)
在上一个图像中,检测到多个位置,例如头部,颈部,肩膀,脚踝和手。 这可以扩展到所有人类部分。 我们了解到的回归可以用于此应用程序。 这是基准点检测的示例:
![](img/6bfd58bb-e261-4dee-8b30-25c3f884e729.png)
基准点是脸部相对于眼睛,鼻子和嘴唇的位置的地标。 找到这些地标对于基于面部的增强现实应用至关重要。 人脸识别中还有更多地标可用,将在[第 6 章](../Text/06.html)*相似性学习*中详细介绍。
# 结合回归与滑动窗口
为滑动窗口方法或完全卷积方法中的每个窗口计算分类分数,以了解该窗口中存在哪些对象。 代替预测用于检测对象的每个窗口的分类得分,可以利用分类得分来预测每个窗口本身。 结合滑动窗口,比例空间,完全卷积和回归之类的所有想法,比任何单独的方法都具有更好的结果。 以下是各种网络使用回归方法在`ImageNet`数据集上获得的前五个定位错误率:
![](img/e4493c04-62ab-4564-9465-cb134209a626.png)
上图显示网络越深,结果越好。 对于 AlexNet,本地化方法未在本文中描述。 OverFeat 使用带有框合并的多尺度卷积回归。 VGG 使用了本地化,但比例尺和位置较少。 这些收益归因于深层特征。 ResNet 使用不同的本地化方法和更深入的功能。
回归编码器和分类编码器独立运行。 因此,有可能预测边界框的标签不正确。 通过在不同的图层上附加回归编码器可以解决此问题。 该方法也可以用于多个物体,从而解决了物体检测问题。 给定一个图像,找到其中的所有实例。 很难将检测视为回归,因为输出的数量是可变的。 一个图像可能有两个对象,而另一个可能有三个或更多。 在下一节中,我们将看到更有效地处理检测问题的算法。
# 检测物体
对象检测算法有多种变体。 这里讨论了对象检测 API 附带的一些算法。
# 卷积神经网络(R-CNN)的区域
该系列的第一个工作是 Girshick 等人提出的 CNN 区域( [https://arxiv.org/pdf/1311.2524.pdf](https://arxiv.org/pdf/1311.2524.pdf) )。 它提出了一些框,并检查是否有任何框对应于基本事实。 **选择性搜索**用于这些地区提案。 选择性搜索通过对各种大小的窗口的颜色/纹理进行分组来建议区域。 选择性搜索寻找斑点样的结构。 它以一个像素开始,并在更大范围内产生斑点。 它产生了大约 2,000 个区域提案。 与所有可能的滑动窗口相比,该区域建议更少。
调整提案的大小并通过标准的 CNN 体​​系结构,例如 Alexnet / VGG / Inception / ResNet。 CNN 的最后一层是通过 SVM 进行训练的,该 SVM 使用无对象类来标识对象。 通过拉紧图像周围的框可以进一步改善框。 使用对象区域建议训练用于预测更近边界框的线性回归模型。 R-CNN 的架构如下所示:
![](img/4f45a709-739b-41a7-969e-56589fecb96f.png)
经 Girshick 等人许可复制。
编码器可以是标准深度学习模型的预训练模型。 从训练数据为所有区域计算特征。 存储功能,然后训练 SVM。 接下来,用标准化坐标训练边界框。 在图像坐标之外可能会有一些建议,因此将其标准化以进行训练和推理。
这种方法的缺点是:
* 通过选择性搜索形成了多个建议,因此必须计算许多推论,通常约为 2,000
* 必须对三个分类器进行训练,这会增加参数的数量
* 没有端到端的培训
# 快速 R-CNN
Girshick 等人提出的 Fast R-CNN。 ( [https://arxiv.org/pdf/1504.08083.pdf](https://arxiv.org/pdf/1504.08083.pdf) )方法仅运行一次 CNN 推理,因此减少了计算量。 CNN 的输出用于建议网络并选择边界框。 它介绍了一种称为**感兴趣区域池**的技术。 感兴趣区域池采用 CNN 功能,并根据区域将它们合并在一起。 合并使用 CNN 进行推理后获得的特征,并选择区域,如下图所示:
![](img/9607861d-014a-4574-980d-90fb23f91ae4.png)
经 Girshick 等人许可复制。
这样,执行端到端训练,避免了多个分类器。 请注意,SVM 被 softmax 层替换,并且框回归器由边界框回归器代替。 仍然存在的缺点是选择性搜索,这需要一些时间。
# 更快的 R-CNN
Ren 等人提出了更快的 R-CNN。 ( [https://arxiv.org/pdf/1506.01497.pdf](https://arxiv.org/pdf/1506.01497.pdf) )。 Faster R-CNN 和 Fast R-CNN 方法之间的区别在于,Faster R-CNN 使用诸如 VGG 和 Inception 等体系结构的 CNN 功能来提案而不是选择性搜索。 CNN 功能进一步通过区域提议网络传递。 滑动窗口通过具有潜在边界框和分数的要素传递,并输出一些直观的长宽比,模型输出边界框和分数:
![](img/ea103e8f-b553-4d9d-89cd-077888fb7db0.png)
经 Ren 等人许可复制。
更快的 R-CNN 比快速 R-CNN 更快,因为它通过仅计算一次功能来节省计算量。
# 单发多盒探测器
SSD(单发多盒)是所有方法中最快的。 此方法同时预测对象并找到边界框。 在培训期间,可能会有很多负面因素,因此很难否定地挖掘班级失衡。 CNN 的输出具有各种大小的功能。 这些被传递到 3x3 卷积滤波器以预测边界框。
此步骤将预测对象和边界框:
![](img/28e92ddf-38ea-4b41-80e6-add3753d03d2.png)
经 Liu 等人许可复制。
这些是可用于对象检测的算法,我们将在下一节中学习如何实现它们。
# 对象检测 API
Google 发布了经过预先训练的模型,并在`COCO`数据集上对各种算法进行了训练,以供公众使用。 该 API 建立在 TensorFlow 之上,旨在用于构建,训练和部署对象检测模型。 这些 API 支持对象检测和本地化任务。 预训练模型的可用性可对新数据进行微调,从而加快训练速度。 这些不同的模型在速度和准确性之间进行权衡。
# 安装与设定
使用以下命令安装协议缓冲区 ***(*** **protobuf)**编译器。 为 protobuf 创建目录并直接下载该库:
```py
mkdir protoc_3.3
cd protoc_3.3
wget [https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_64.zip](https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_64.zip)
```
更改文件夹的权限并提取内容,如下所示:
```py
chmod 775 protoc-3.3.0-linux-x86_64.zip
unzip protoc-3.3.0-linux-x86_64.zip
```
协议缓冲区(protobuf)是 Google 的语言无关,平台无关的可扩展机制,用于序列化结构化数据。 它用于 XML 的使用,但是更加简单快捷。 模型通常在 TensorFlow 中导出为这种格式。 一个人可以定义一次数据结构,但可以用多种语言进行读写。 然后运行以下命令来编译 protobuf。 返回到工作文件夹,然后 c 从 [https://github.com/tensorflow/models.git](https://github.com/tensorflow/models.git) 克隆存储库,并将它们移至以下文件夹:
```py
git clone https://github.com/tensorflow/models.git
```
现在,使用以下代码将模型移至研究文件夹:
```py
cd models/research/
~/protoc_3.3/bin/protoc object_detection/protos/*.proto --python_out=.
```
TensorFlow 对象检测 API 使用 protobuf 导出模型权重和训练参数。 TensorFlow ,模型,研究和苗条目录应通过以下命令附加到`PYTHONPATH`
```py
export PYTHONPATH=.:./slim/
```
使用前面的命令添加到 python 路径仅一次。 对于下一个,该命令必须再次运行。 可以通过运行以下代码来测试安装:
```py
python object_detection/builders/model_builder_test.py
```
此代码的输出在此处给出:
```py
Ran 7 tests in 0.022s
OK
```
可以从 [https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/installation.md](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/installation.md) 获得有关安装的更多信息。 现在安装已完成并经过测试。
# 预训练模型
有几种模型已经过预先训练并可以使用。 所有这些模型都在`COCO`数据集上进行了训练,可用于检测`COCO`数据集中可用的对象,例如人和汽车。 这些模型对于新任务(例如交通标志检测)的转移学习也很有用。 此处显示了经过预训练的模型的表格,其中包含`COCO`数据集上的相对速度和 mAP。 使用不同的 CNN 训练了各种算法,并在名称中进行了描述:
| **型号名称** | **速度** | **可可地图** |
| `ssd_mobilenet_v1_coco` | 快速 | 21 |
| `ssd_inception_v2_coco` | 快速 | 24 |
| `rfcn_resnet101_coco` | 中 | 30 |
| `faster_rcnn_resnet101_coco` | 中 | 32 |
| `faster_rcnn_inception_resnet_v2_atrous_coco` | 慢 | 37 |
根据需求,可以从模型中进行选择。 下载在 Mobilenet 上训练的 SSD 模型,并通过转到工作目录将其提取,如下所示:
```py
mkdir Chapter04 && cd Chapter04
wget http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_11_06_2017.tar.gz
tar -xzvf ssd_mobilenet_v1_coco_11_06_2017.tar.gz
```
`Chapter04`文件夹中将包含各种文件,这些文件在此处列出:
* 这是图形的原始定义-`graph.pbtxt`
* 图的权重已冻结,可以用于推断-`frozen_inference_graph.pb`
* 检查点文件
* `model.ckpt.data-00000-of-00001`
* `model.ckpt.meta`
* `model.ckpt.index`
下一部分将使用此模型执行检测任务。
# 重新训练物体检测模型
使用相同的 API,我们可以为自定义数据集重新训练模型。 定制数据的训练涉及数据集的准备,选择算法以及执行微调。 整个管道可以作为参数传递给训练脚本。 训练数据必须转换为 TensorFlow 记录。 TensorFlow 记录是 Google 提供的一种文件格式,可以使数据读取比常规文件更快。 现在,我们将逐步进行培训。
# Pet 数据集的数据准备
本示例使用 Oxford-IIIT `Pet`数据集。 使用这些命令从`Chapter04`目录下载图像和注释。
```py
wget http://www.robots.ox.ac.uk/~vgg/data/pets/daimg.tar.gz
wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz
```
提取图像和注释,如下所示:
```py
tar -xvf images.tar.gz
tar -xvf annotations.tar.gz
```
创建`pet_tf`记录文件以在`tf`记录中创建数据集,因为它们是对象检测训练器的必需输入。 可在`object_detection/data/pet_label_map.pbtxt`找到`Pet`数据集的`label_map`。 移至`research`文件夹并运行以下命令:
```py
python object_detection/create_pet_tf_record.py \
--label_map_path=object_detection/data/pet_label_map.pbtxt \
--data_dir=~/chapter4/. \
--output_dir=~/chapter4/.
```
您可以在研究目录`pet_train.record``pet_val.record`中看到两个`.record`文件。
# 目标检测训练流水线
培训 protobuf 必须配置为进行培训。 在此过程中,以下五件事很重要:
* 具有型号类型的型号配置
* `train_config`用于标准训练参数
* 必须报告的指标的`eval_config`
* 数据集的`train_input_`配置
* 评估数据集的`eval_input_`配置
我们将使用 [https://github.com/tensorflow/models/blob/master/research/object_detection/samples/configs/ssd_mobilenet_v1_pets.config](https://github.com/tensorflow/models/blob/master/research/object_detection/samples/configs/ssd_mobilenet_v1_pets.config) 中的配置文件。 通过运行以下命令将其下载到`Chapter04`文件夹。 打开`config`文件并编辑以下行:
```py
fine_tune_checkpoint: "~/Chapter04/ssd_mobilenet_v1_coco_11_06_2017/model.ckpt"
train_input_reader: {
tf_record_input_reader {
input_path: "~/Chapter04/pet_train.record"
}
label_map_path: "~/model/research/object_detection/data/pet_label_map.pbtxt"
}
eval_input_reader: {
tf_record_input_reader {
input_path: "~/Chapter04/pet_val.record"
}
label_map_path: "~/model/research/object_detection/data/pet_label_map.pbtxt"
}
```
保存`config`文件。 文件中有各种参数会影响模型的准确性。
# 训练模型
现在,API,数据和配置文件已准备好进行重新培训。 可以通过以下命令触发训练:
```py
PYTHONPATH=.:./slim/. python object_detection/train.py \
--logtostderr \
--pipeline_config_path=~/chapter4/ssd_mobilenet_v1_pets.config \
--train_dir=~/Chapter04
```
培训将从大约 140 的损失开始,并将持续减少。 培训将永远进行,必须使用 *Ctrl* + *C* 命令手动将其终止。 训练期间创建的检查点可在以后用于推理。
# 使用 TensorBoard 监控损失和准确性
训练损失和准确性可以使用 TensorBoard 进行监视。 使用以下命令运行 TensorBoard:
```py
tensorboard --logdir=/home/ubuntu/Chapter04
```
培训和评估都可以在 TensorBoard 中可视化。
# 训练自动驾驶汽车的行人检测
可以在 [http://pascal.inrialpes.fr/data/human/](http://pascal.inrialpes.fr/data/human/) 中找到用于训练行人物体检测的数据集。 可以在 [https://github.com/diegocavalca/machine-learning/blob/master/supervisioned/object.detection_tensorflow/simple.detection.ipynb](https://github.com/diegocavalca/machine-learning/blob/master/supervisioned/object.detection_tensorflow/simple.detection.ipynb) 中找到检测行人的步骤。 可以从 [http://www.vision.ee.ethz.ch/~timofter/traffic_signs/](http://www.vision.ee.ethz.ch/~timofter/traffic_signs/)[http://btsd.ethz.ch/下载用于训练符号检测器的数据集。 shareddata /](http://btsd.ethz.ch/shareddata/) 。 对于无人驾驶汽车,图像中将有四个类别用于标记:行人,汽车,摩托车和背景。 当不存在任何后台类时,必须检测到该后台类。 训练深度学习分类模型的一个假设是,至少一个对象将出现在图像中。 通过添加`background`类,我们克服了这个问题。 神经网络还可以根据标签生成对象的边界框。
# YOLO 对象检测算法
最近的对象检测算法是**。您只看过一次****YOLO** )。 图像分为多个网格。 图像的每个网格单元都运行相同的算法。 让我们通过定义带有初始化器的层来开始实现:
```py
def pooling_layer(input_layer, pool_size=[2, 2], strides=2, padding='valid'):
layer = tf.layers.max_pooling2d(
inputs=input_layer,
pool_size=pool_size,
strides=strides,
padding=padding
)
add_variable_summary(layer, 'pooling')
return layer
def convolution_layer(input_layer, filters, kernel_size=[3, 3], padding='valid',
activation=tf.nn.leaky_relu):
layer = tf.layers.conv2d(
inputs=input_layer,
filters=filters,
kernel_size=kernel_size,
activation=activation,
padding=padding,
weights_initializer=tf.truncated_normal_initializer(0.0, 0.01),
weights_regularizer=tf.l2_regularizer(0.0005)
)
add_variable_summary(layer, 'convolution')
return layer
def dense_layer(input_layer, units, activation=tf.nn.leaky_relu):
layer = tf.layers.dense(
inputs=input_layer,
units=units,
activation=activation,
weights_initializer=tf.truncated_normal_initializer(0.0, 0.01),
weights_regularizer=tf.l2_regularizer(0.0005)
)
add_variable_summary(layer, 'dense')
return layer
```
可以注意到,激活层为`leaky_relu`,权重以截断的正态分布初始化。 这些修改的图层可用于构建模型。 该模型如下创建:
```py
yolo = tf.pad(images, np.array([[0, 0], [3, 3], [3, 3], [0, 0]]), name='pad_1')
yolo = convolution_layer(yolo, 64, 7, 2)
yolo = pooling_layer(yolo, [2, 2], 2, 'same')
yolo = convolution_layer(yolo, 192, 3)
yolo = pooling_layer(yolo, 2, 'same')
yolo = convolution_layer(yolo, 128, 1)
yolo = convolution_layer(yolo, 256, 3)
yolo = convolution_layer(yolo, 256, 1)
yolo = convolution_layer(yolo, 512, 3)
yolo = pooling_layer(yolo, 2, 'same')
yolo = convolution_layer(yolo, 256, 1)
yolo = convolution_layer(yolo, 512, 3)
yolo = convolution_layer(yolo, 256, 1)
yolo = convolution_layer(yolo, 512, 3)
yolo = convolution_layer(yolo, 256, 1)
yolo = convolution_layer(yolo, 512, 3)
yolo = convolution_layer(yolo, 256, 1)
yolo = convolution_layer(yolo, 512, 3)
yolo = convolution_layer(yolo, 512, 1)
yolo = convolution_layer(yolo, 1024, 3)
yolo = pooling_layer(yolo, 2)
yolo = convolution_layer(yolo, 512, 1)
yolo = convolution_layer(yolo, 1024, 3)
yolo = convolution_layer(yolo, 512, 1)
yolo = convolution_layer(yolo, 1024, 3)
yolo = convolution_layer(yolo, 1024, 3)
yolo = tf.pad(yolo, np.array([[0, 0], [1, 1], [1, 1], [0, 0]]))
yolo = convolution_layer(yolo, 1024, 3, 2)
yolo = convolution_layer(yolo, 1024, 3)
yolo = convolution_layer(yolo, 1024, 3)
yolo = tf.transpose(yolo, [0, 3, 1, 2])
yolo = tf.layers.flatten(yolo)
yolo = dense_layer(yolo, 512)
yolo = dense_layer(yolo, 4096)
dropout_bool = tf.placeholder(tf.bool)
yolo = tf.layers.dropout(
inputs=yolo,
rate=0.4,
training=dropout_bool
)
yolo = dense_layer(yolo, output_size, None)
```
堆叠了几个卷积层,生成了 YOLO 网络。 该网络用于创建用于实时检测的对象检测算法。
# 摘要
在本章中,我们了解了对象定位和检测任务之间的区别。 讨论了几个数据集和评估标准。 讨论了各种解决定位问题和算法的方法,例如用于检测的 R-CNN 和 SSD 模型的变体。 涵盖了在开源存储库中执行检测的过程。 我们使用该技术训练了行人检测模型。 我们还了解了在训练此类模型时需要进行的各种取舍。
在下一章中,我们将学习语义分割算法。 我们将使用该知识来实现​​医学成像和卫星成像问题的分割算法。
\ No newline at end of file
# 语义分割
在本章中,我们将学习各种语义分割技术并为其训练模型。 分割是逐像素分类任务。 解决分割问题的思想是对对象检测问题的扩展。 分割在医学和卫星图像理解等应用中非常有用。
本章将涵盖以下主题:
* 学习语义分割和实例分割之间的区别
* 细分数据集和指标
* 语义分割算法
* 分割在医学和卫星图像中的应用
* 实例分割算法
# 预测像素
图像分类是预测标签或类别的任务。 对象检测是预测几种基于深度学习的算法及其相应边界框的列表的任务。 边界框可能在其中包含除检测到的对象以外的其他对象。 在某些应用中,将每个像素标记到标签很重要,而不是可能包含多个对象的边框。 **语义分割**是预测逐像素标签的任务。
这是图像及其对应的语义分割的示例:
![](img/ecd4a853-47b0-4b64-959d-e659b7a506a8.jpeg) ![](img/f3d07c15-d6fe-4677-9b43-9628b38bc9d4.jpg) >
如图中所示,使用每个像素的标签预测输入图像。 标签可以是天空,树木,人,山和桥。 标签不是分配给整个图像,而是分配给每个像素。 语义分割独立标记像素。 您会注意到每个人都没有区别。 图像中的所有人员均以相同的方式标记。
这是区分相同标签的每个实例的示例:
![](img/1c6a1b46-8d59-4882-99a5-cdf1ddfc4ef0.jpg)
用像素标记分割每个实例的任务称为**实例分割**。 实例分割可以被认为是具有像素级标签的对象检测的扩展。 语义分段和实例分段的应用非常广泛,下一部分将提供一些应用。
# 诊断医学影像
可以使用分割技术来诊断医学图像。 现代医学成像技术,例如**磁共振成像****MRI** ),**计算机断层扫描****CT** )和**视网膜病变**创建高质量的图像。 可以将通过这种技术生成的图像划分为多个区域,以检测来自脑部扫描的肿瘤或来自视网膜扫描的斑点。 一些设备提供体积图像,这些图像也可以通过分段进行分析。 分割视频以进行机器人手术,使医生能够在机器人协助的手术中仔细查看区域。 在本章的后面,我们将看到如何分割医学图像。
# 通过卫星图像了解地球
卫星图像最近变得越来越丰富。 卫星捕获的图像提供了地球整个表面的高分辨率视图。 通过分析卫星图像,我们可以了解有关地球的几件事,例如:
* 衡量与经济增长相关的国家的建筑率
* 测量油箱
* 规划和组织交通
* 计算森林砍伐及其影响
* 通过对动物进行计数并跟踪其运动来帮助保护野生动植物
* 发现考古遗址
* 绘制自然灾害造成的损坏区域
卫星图像还有更多的应用可能。 对于上述大多数问题,解决方案始于卫星图像的分割。 在本章的后面,我们将看到如何分割卫星图像。
# 使机器人能够看到
分割场景对于机器人看清周围世界并与之互动至关重要。 工业和家用机器人必须处理这些物体。 一旦根据对象跨越了机器人的视野,就可以进行处理。 还有更多值得一提的应用程序:
* 对缺陷进行细分的工具的工业检查
* 时装行业的色彩诊断; 可以将图像与各种时尚对象进行分割并将其用于颜色解析
* 区分背景与前景以应用人像效果
在下一部分中,我们将学习一些用于评估细分算法的公共数据集。
# 数据集
[第 4 章](../Text/04.html)*对象检测*中提到的`PASCAL``COCO`数据集也可以用于分割任务。 注释是不同的,因为它们是按像素标记的。 新算法通常以`COCO`数据集为基准。 `COCO`还具有诸如草,墙和天空之类的东西数据集。 像素精度属性可用作评估算法的指标。
除了上面提到的那些以外,在医学影像和卫星影像领域还有其他几个数据集。 这里提供了指向其中一些链接的供您参考:
* [http://www.cs.bu.edu/~betke/BiomedicalImageSegmentation](http://www.cs.bu.edu/~betke/BiomedicalImageSegmentation/)
* [https://www.kaggle.com/c/intel-mobileodt-cervical-cancer-screening/data](https://www.kaggle.com/c/intel-mobileodt-cervical-cancer-screening/data)
* [https://www.kaggle.com/c/diabetic-retinopathy-detection](https://www.kaggle.com/c/diabetic-retinopathy-detection)
* [https://grand-challenge.org/all_challenges](https://grand-challenge.org/all_challenges/)
* [http://www.via.cornell.edu/databases](http://www.via.cornell.edu/databases/)
* [https://www.kaggle.com/c/dstl-satellite-imagery-feature-detection](https://www.kaggle.com/c/dstl-satellite-imagery-feature-detection)
* [https://aws.amazon.com/public-datasets/spacenet](https://aws.amazon.com/public-datasets/spacenet/)
* [https://www.iarpa.gov/challenges/fmow.html](https://www.iarpa.gov/challenges/fmow.html)
* [https://www.kaggle.com/c/planet-understanding-the-amazon-from-space](https://www.kaggle.com/c/planet-understanding-the-amazon-from-space)
为分割任务创建训练数据非常昂贵。 有在线工具可用于注释数据集。 麻省理工学院 **MIT 大学**提供的 **LabelMe** 移动应用程序非常适合注释,可以从[下载 http://labelme.csail.mit.edu/Release3.0](http://labelme.csail.mit.edu/Release3.0)
# 语义分割算法
提出了几种基于深度学习的算法来解决图像分割任务。 可以在像素级别应用滑动窗口方法进行分割。 滑动窗口方法会拍摄图像并将图像分成较小的作物。 图像的每种裁剪都被分类为标签。 这种方法昂贵且效率低下,因为它不会重用重叠补丁之间的共享功能。 在以下各节中,我们将讨论一些可以克服此问题的算法。
# 完全卷积网络
**全卷积网络****FCN** )引入了端到端卷积网络的思想。 通过删除完全连接的层,可以将任何标准的 CNN 架构用于 FCN,其实现在[第 4 章](../Text/04.html)*对象检测*中进行了显示。 完全连接的层被卷积层代替。 最终层的深度较高,尺寸较小。 因此,可以执行一维卷积以达到所需的标签数量。 但是对于分割,必须保留空间尺寸。 因此,构建完整的卷积网络时没有最大池,如下所示:
![](img/3b75d8a1-168a-4625-b727-169ebb0425f4.png)
该网络的损耗是通过平均每个像素和小批量的交叉熵损耗来计算的。 最后一层的深度等于类数。 FCN 与对象检测相似,只是保留了空间尺寸。 由于某些像素可能会被错误预测,因此该架构产生的输出将很粗糙。 计算量很大,在下一节中,我们将看到如何解决此问题。
# SegNet 架构
**SegNet** 具有编码器和解码器方法。 编码器具有各种卷积层,而解码器具有各种解卷积层。 SegNet 改进了 FCN 产生的粗略输出。 因此,它的内存占用较少。 当特征尺寸减小时,通过反卷积将其再次上采样至图像大小,从而反转了卷积效果。 反卷积学习用于上采样的参数。 由于池层中信息的丢失,这种体系结构的输出将很粗糙。
![](img/0bca20d8-04c9-48cb-8029-e8af3258c09f.png)
现在,让我们学习几个新概念,这些概念称为上采样,无规则卷积和转置卷积,它们将帮助我们更好地理解该网络。
# 通过合并对图层进行上采样
[第 1 章](../Text/01.html)*入门*中,我们讨论了最大池化。 最大池化是一种从窗口中选取最大值的采样策略。 对于上采样,可以相反。 每个值都可以用零包围,以对该层进行上采样,如下所示:
![](img/6c6fa429-432a-4bc4-af17-015e872d5490.png)
将零添加到与上采样数字相同的位置。 通过记住下采样的位置并将其用于上采样,可以改善解池功能,如下所示:
![](img/7793c888-12eb-40b1-944f-85470767c43c.png)
从索引角度来看,上采样比附加零产生更好的结果。 这种通过池对层进行上采样的方法是无法学到的,并且可以按原样工作。 接下来,我们将看到如何使用可学习的参数进行上采样和下采样。
# 通过卷积对图层进行采样
可以使用卷积直接对图层进行升采样或降采样。 可以增加用于卷积的步幅以引起下采样,如下所示:
![](img/d2cd8c90-7707-4786-a5bc-e40b08a8a35d.png)
通过卷积的下采样被称为**无孔卷积****扩张卷积****大卷积**。 类似地,可以通过学习内核将其反转为升采样,如下所示:
![](img/9e542e78-5c13-4d7f-8d96-e5cedc35bdec.png)
直接使用卷积的上采样可以称为**转置卷积**。 其他一些同义词是**反卷积****分数步卷积****上卷积**。 现在,了解了升采样的过程。 这是描述先前算法的代码片段:
```py
input_height = 360 input_width = 480 kernel = 3 filter_size = 64 pad = 1 pool_size = 2
```
输入之后,它遵循大小逐渐减小的通常的卷积神经网络,可以称为编码器。 以下代码可用于定义编码器:
```py
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Layer(input_shape=(3, input_height, input_width)))
# encoder model.add(tf.keras.layers.ZeroPadding2D(padding=(pad, pad)))
model.add(tf.keras.layers.Conv2D(filter_size, kernel, kernel,
border_mode='valid'))
model.add(tf.keras.layers.BatchNormalization())
model.add(tf.keras.layers.Activation('relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(pool_size, pool_size)))
model.add(tf.keras.layers.ZeroPadding2D(padding=(pad, pad)))
model.add(tf.keras.layers.Conv2D(128, kernel, kernel, border_mode='valid'))
model.add(tf.keras.layers.BatchNormalization())
model.add(tf.keras.layers.Activation('relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(pool_size, pool_size)))
model.add(tf.keras.layers.ZeroPadding2D(padding=(pad, pad)))
model.add(tf.keras.layers.Conv2D(256, kernel, kernel, border_mode='valid'))
model.add(tf.keras.layers.BatchNormalization())
model.add(tf.keras.layers.Activation('relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(pool_size, pool_size)))
model.add(tf.keras.layers.ZeroPadding2D(padding=(pad, pad)))
model.add(tf.keras.layers.Conv2D(512, kernel, kernel, border_mode='valid'))
model.add(tf.keras.layers.BatchNormalization())
model.add(tf.keras.layers.Activation('relu'))
```
可以使用以下代码将编码器的输出以增大的尺寸馈入解码器:
```py
# decoder model.add(tf.keras.layers.ZeroPadding2D(padding=(pad, pad)))
model.add(tf.keras.layers.Conv2D(512, kernel, kernel, border_mode='valid'))
model.add(tf.keras.layers.BatchNormalization())
model.add(tf.keras.layers.UpSampling2D(size=(pool_size, pool_size)))
model.add(tf.keras.layers.ZeroPadding2D(padding=(pad, pad)))
model.add(tf.keras.layers.Conv2D(256, kernel, kernel, border_mode='valid'))
model.add(tf.keras.layers.BatchNormalization())
model.add(tf.keras.layers.UpSampling2D(size=(pool_size, pool_size)))
model.add(tf.keras.layers.ZeroPadding2D(padding=(pad, pad)))
model.add(tf.keras.layers.Conv2D(128, kernel, kernel, border_mode='valid'))
model.add(tf.keras.layers.BatchNormalization())
model.add(tf.keras.layers.UpSampling2D(size=(pool_size, pool_size)))
model.add(tf.keras.layers.ZeroPadding2D(padding=(pad, pad)))
model.add(tf.keras.layers.Conv2D(filter_size, kernel, kernel, border_mode='valid'))
model.add(tf.keras.layers.BatchNormalization())
model.add(tf.keras.layers.Conv2D(nClasses, 1, 1, border_mode='valid', ))
```
解码图像的大小与输入的大小相同,并且可以使用以下代码来训练整个模型:
```py
model.outputHeight = model.output_shape[-2]
model.outputWidth = model.output_shape[-1]
model.add(tf.keras.layers.Reshape((nClasses, model.output_shape[-2] * model.output_shape[-1]),
input_shape=(nClasses, model.output_shape[-2], model.output_shape[-1])))
model.add(tf.keras.layers.Permute((2, 1)))
model.add(tf.keras.layers.Activation('softmax'))
model.compile(loss="categorical_crossentropy", optimizer=tf.keras.optimizers.Adam, metrics=['accuracy'])
```
这种对图像进行编码和解码的方式克服了基于 FCN 的模型的缺点。 接下来,我们将看到具有膨胀卷积的不同概念。
# 跳过连接以进行更好的培训
分割输出的粗糙程度可以通过跳过体系结构来限制,并且可以获得更高的分辨率。 另一种替代方法是按比例放大最后三层并将其平均,如下所示:
![](img/1cf06efe-233a-40d0-9fa5-7f53ab8f18dc.png)
此算法将在后面的部分中用作卫星图像的示例。
# 膨胀卷积
逐像素分类和图像分类在结构上不同。 因此,减少信息的池化层将产生粗略的分段。 但是请记住,池化对于拥有更广阔的视野并允许采样至关重要。 引入了一种名为**扩张卷积**的新想法,以解决该问题,从而在进行更广阔视野的同时减少损耗。 扩大的卷积本质上是通过跳过窗口中的每个像素来进行卷积,如下所示:
![](img/1caea247-6635-401e-a844-b023c12f9366.png)
膨胀距离随层而变化。 这样的分割结果的输出被放大以得到更精细的分辨率。 训练了一个单独的网络以进行多尺度聚合。
# 深度实验室
Chen 等人提出的 DeepLab。 ( [https://arxiv.org/pdf/1606.00915.pdf](https://arxiv.org/pdf/1606.00915.pdf) )在多个尺度上执行卷积,并使用来自各种尺度的特征获得分数图。 然后对得分图进行插值,并通过**条件随机字段****CRF** )进行最终分割。 图像的这种比例处理可以通过使用其自己的 CNN 处理各种大小的图像,或者通过具有不同水平的卷积卷积的并行卷积来执行。
![](img/c958122d-5ce1-4d41-89a1-d2203bb2c7f5.png)
经 Chen 等人许可复制。
# 互联网
膨胀卷积需要更大的输入,因此占用大量内存。 使用高分辨率图片时,这会带来计算问题。 里德等。 ( [https://arxiv.org/pdf/1611.06612.pdf](https://arxiv.org/pdf/1611.06612.pdf) )提出了一种称为 RefiNet 的方法来克服此问题,如下所示:
![](img/db354e6d-4923-4bbc-947a-44c5ba5be7c7.png)
经 Reid 等人许可复制。
RefiNet 使用编码器,然后使用解码器。 CNN 的编码器输出。 解码器连接各种大小的功能:
![](img/836b137d-c3de-4c2c-baa2-c6e86dd1b6fc.png)
经 Reid 等人许可复制。
串联完成后会放大低维特征。
# PSP 网
Zhoa 等人介绍的 PSPnet 中使用了全局内容。 (https://arxiv.org/pdf/1612.01105.pdf),方法是增加池化层的内核大小。 汇集以金字塔的方式进行。 金字塔同时覆盖图像的各个部分和大小。 架构之间存在损失,因此无法进行适当的监管。
![](img/1a1c9433-d75c-4c5d-add8-1a1c9fc9ec8b.png)
经 Zhao 等人许可复制。
# 大内核很重要
Peng 等。 ( [https://arxiv.org/pdf/1703.02719.pdf](https://arxiv.org/pdf/1703.02719.pdf) )展示了大内核的重要性。 大内核比小内核具有更大的接受范围。 这些大内核的计算复杂度可用于以较小的内核来克服。 最后有一个边界优化网络。
![](img/3f5d488c-04a0-4396-a2a8-c1fb2bcbf1aa.png)
经 Peng 等人许可复制。
# DeepLab v3
Chen 等人在论文中使用批处理归一化。 ( [https://arxiv.org/pdf/1706.05587.pdf](https://arxiv.org/pdf/1706.05587.pdf) )以提高性能。 功能的多尺度以级联方式编码,以提高性能:
![](img/301dc866-0bfd-4010-a5f1-df4e33e12e34.png)
经 Chen 等人许可复制。
![](img/65958e56-9dfc-4075-8de4-d31f9bd2662e.png)
经 Chen 等人许可复制。
我们已经看到了几种架构可以使用深度学习提高图像分割的准确性。 接下来,我们将看到在医学成像中的应用。
# 超神经分割
Kaggler 是一个组织进行预测建模和分析竞赛的组织。 Kagglers 曾经受到挑战,要从颈部超声图像中分割神经结构。 可以从 [https://www.kaggle.com/c/ultrasound-nerve-segmentation](https://www.kaggle.com/c/ultrasound-nerve-segmentation) 下载有关该数据的数据。 Ronneberger 等人提出的 UNET 模型。 ( [https://arxiv.org/pdf/1505.04597.pdf](https://arxiv.org/pdf/1505.04597.pdf) )类似于自动编码器,但具有卷积而不是完全连接的层。 这里有一个编码部分,其卷积减小,而解码器部分的卷积增大,如下所示:
![](img/d5f0233d-03ef-4826-9cba-8a3bbf2f4eb3.png)
该图说明了 UNET 模型的体系结构[经 Ronneberger 等人许可复制]
相似大小的编码器和解码器部分的卷积通过跳过连接来学习。 模型的输出是一个介于 0 到 1 之间的掩码。让我们从导入函数开始,借助以下代码:
```py
import os
from skimage.transform import resize
from skimage.io import imsave
import numpy as np
from data import load_train_data, load_test_data
```
在所有导入之后,我们现在将使用以下代码定义大小:
```py
image_height, image_width = 96, 96 smoothness = 1.0 work_dir = ''
```
现在我们将定义`dice_coefficient`及其损失函数。 在这种情况下,`dice_coefficient`也是度量标准:
```py
def dice_coefficient(y1, y2):
y1 = tf.flatten(y1)
y2 = tf.flatten(y2)
return (2\. * tf.sum(y1 * y2) + smoothness) / (tf.sum(y1) + tf.sum(y2) + smoothness)
def dice_coefficient_loss(y1, y2):
return -dice_coefficient(y1, y2)
```
UNET 模型可以定义如下:
```py
def preprocess(imgs):
imgs_p = np.ndarray((imgs.shape[0], image_height, image_width), dtype=np.uint8)
for i in range(imgs.shape[0]):
imgs_p[i] = resize(imgs[i], (image_width, image_height), preserve_range=True)
imgs_p = imgs_p[..., np.newaxis]
return imgs_p
def covolution_layer(filters, kernel=(3,3), activation='relu', input_shape=None):
if input_shape is None:
return tf.keras.layers.Conv2D(
filters=filters,
kernel=kernel,
activation=activation)
else:
return tf.keras.layers.Conv2D(
filters=filters,
kernel=kernel,
activation=activation,
input_shape=input_shape)
def concatenated_de_convolution_layer(filters):
return tf.keras.layers.concatenate([
tf.keras.layers.Conv2DTranspose(
filters=filters,
kernel=(2, 2),
strides=(2, 2),
padding='same'
)],
axis=3
)
```
所有层都已连接并使用,如以下代码所示:
```py
unet = tf.keras.models.Sequential()
inputs = tf.keras.layers.Input((image_height, image_width, 1))
input_shape = (image_height, image_width, 1)
unet.add(covolution_layer(32, input_shape=input_shape))
unet.add(covolution_layer(32))
unet.add(pooling_layer())
unet.add(covolution_layer(64))
unet.add(covolution_layer(64))
unet.add(pooling_layer())
unet.add(covolution_layer(128))
unet.add(covolution_layer(128))
unet.add(pooling_layer())
unet.add(covolution_layer(256))
unet.add(covolution_layer(256))
unet.add(pooling_layer())
unet.add(covolution_layer(512))
unet.add(covolution_layer(512))
```
这些层是连接在一起的,并使用了反卷积层:
```py
unet.add(concatenated_de_convolution_layer(256))
unet.add(covolution_layer(256))
unet.add(covolution_layer(256))
unet.add(concatenated_de_convolution_layer(128))
unet.add(covolution_layer(128))
unet.add(covolution_layer(128))
unet.add(concatenated_de_convolution_layer(64))
unet.add(covolution_layer(64))
unet.add(covolution_layer(64))
unet.add(concatenated_de_convolution_layer(32))
unet.add(covolution_layer(32))
unet.add(covolution_layer(32))
unet.add(covolution_layer(1, kernel=(1, 1), activation='sigmoid'))
unet.compile(optimizer=tf.keras.optimizers.Adam(lr=1e-5),
loss=dice_coefficient_loss,
metrics=[dice_coefficient])
```
接下来,可以通过使用以下代码对模型进行图像训练:
```py
x_train, y_train_mask = load_train_data()
x_train = preprocess(x_train)
y_train_mask = preprocess(y_train_mask)
x_train = x_train.astype('float32')
mean = np.mean(x_train)
std = np.std(x_train)
x_train -= mean
x_train /= std
y_train_mask = y_train_mask.astype('float32')
y_train_mask /= 255. unet.fit(x_train, y_train_mask, batch_size=32, epochs=20, verbose=1, shuffle=True,
validation_split=0.2)
x_test, y_test_mask = load_test_data()
x_test = preprocess(x_test)
x_test = x_test.astype('float32')
x_test -= mean
x_test /= std
y_test_pred = unet.predict(x_test, verbose=1)
for image, image_id in zip(y_test_pred, y_test_mask):
image = (image[:, :, 0] * 255.).astype(np.uint8)
imsave(os.path.join(work_dir, str(image_id) + '.png'), image)
```
图像可以进行预处理和使用。 现在可以进行图像的训练和测试了。 训练模型后,分割会产生良好的结果,如下所示:
![](img/92227668-9c13-4160-b879-f011ded4bb4c.png)
我们已经训练了可以分割医学图像的模型。 该算法可以在几种用例中使用。 在下一节中,我们将看到如何分割卫星图像。
# 分割卫星图像
在本节中,我们将使用**国际摄影测量与遥感学会****ISPRS** )提供的数据集。 数据集包含 5 毫米分辨率的德国波茨坦的卫星图像。 这些图像带有红外和图像高度轮廓的附加数据。 与图像相关联的六个标签是:
* 建造
* 植被
* 树木
* 驾驶室
* 杂乱无章
* 不透水的
总共提供了 8,000 x 6,000 色块的 38 张图像。 请转到页面 [http://www2.isprs.org/commissions/comm3/wg4/data-request-form2.html](http://www2.isprs.org/commissions/comm3/wg4/data-request-form2.html) 并填写表格。 之后,在表单上选择以下选项:
![](img/3272ab24-2a2e-4ebe-bf40-91d3fd5d9eb0.png)
发布表格后,将向您发送电子邮件,从中可以下载数据。
# 为细分建模 FCN
导入库并获得输入的形状。 标签数定义为`6`
```py
from .resnet50 import ResNet50
nb_labels = 6 img_height, img_width, _ = input_shape
input_tensor = tf.keras.layers.Input(shape=input_shape)
weights = 'imagenet'
```
在 ImageNet 上预先训练的`ResNet`模型将用作基本模型。 以下代码可用于使用`ResNet`定义基本模型:
```py
resnet50_model = ResNet50(
include_top=False, weights='imagenet', input_tensor=input_tensor)
```
现在,我们将使用以下代码从`ResNet`中获取最后三层:
```py
final_32 = resnet50_model.get_layer('final_32').output
final_16 = resnet50_model.get_layer('final_16').output
final_x8 = resnet50_model.get_layer('final_x8').output
```
必须压缩每个跳过连接以匹配等于标签数的通道:
```py
c32 = tf.keras.layers.Conv2D(nb_labels, (1, 1))(final_32)
c16 = tf.keras.layers.Conv2D(nb_labels, (1, 1))(final_16)
c8 = tf.keras.layers.Conv2D(nb_labels, (1, 1))(final_x8)
```
可以使用双线性插值来调整压缩跳过连接的输出大小。 可以通过使用可以计算 TensorFlow 操作的`Lambda`层来实现插值。 以下代码段可用于使用 lambda 层进行插值:
```py
def resize_bilinear(images):
return tf.image.resize_bilinear(images, [img_height, img_width])
r32 = tf.keras.layers.Lambda(resize_bilinear)(c32)
r16 = tf.keras.layers.Lambda(resize_bilinear)(c16)
r8 = tf.keras.layers.Lambda(resize_bilinear)(c8)
```
使用以下代码,可以通过添加三个值来合并我们定义的三层:
```py
m = tf.keras.layers.Add()([r32, r16, r8])
```
可以使用 softmax 激活来应用模型的概率。 在应用 softmax 之前和之后调整模型大小:
```py
x = tf.keras.ayers.Reshape((img_height * img_width, nb_labels))(m)
x = tf.keras.layers.Activation('img_height')(x)
x = tf.keras.layers.Reshape((img_height, img_width, nb_labels))(x)
fcn_model = tf.keras.models.Model(input=input_tensor, output=x)
```
已经定义了一个简单的 FCN 层,经过训练后,它会产生以下结果:
![](img/c09f3fc7-b5d0-4eb1-9b22-e68707be3f96.png)
您会看到六个标签的预测是合理的。 接下来,我们将学习分割实例。
# 分割实例
在分析图像时,我们的兴趣只会吸引到图像中的某些实例。 因此,它不得不从图像的其余部分中分割这些实例。 从其余信息中分离所需信息的过程被广泛称为**分割实例**。 在此过程中,首先拍摄输入图像,然后将边界框与对象一起定位,最后,将为每个类别预测逐像素掩码。 对于每个对象,都将计算像素级精度。 有几种用于分割实例的算法。 最近的算法之一是 He 等人提出的 **Mask RCNN** 算法。 ( [https://arxiv.org/pdf/1703.06870.pdf](https://arxiv.org/pdf/1703.06870.pdf) )。 下图描绘了 Mask R-CNN 的体系结构:
![](img/608bc4e7-8b4b-46b9-98d9-1626226bc859.png)
经 He 等人许可复制。
该架构看起来与 R-CNN 类似,但增加了分段功能。 这是一个具有端到端培训的多阶段网络。 学习了区域提案。 该网络分为两个部分,一个用于检测,另一个用于分类评分。 结果非常好,如下所示:
![](img/a0697d7c-9d03-46fd-9ae2-ff00ff587825.png)
该图说明了分割实例的过程,请注意,准确地检测了对象并进行了相应的分割。
同一网络还可以预测人的姿势。 分割和检测这两个任务是并行处理的。
# 摘要
在本章中,我们学习了各种分割算法。 我们还看到了用于基准测试的数据集和指标。 我们将学到的技术应用于卫星和医学图像的细分。 最后,我们谈到了 Mask R-CNN 算法的实例分割。
在下一章中,我们将学习相似性学习。 相似性学习模型学习两个图像之间的比较机制。 对于面部识别等多种应用很有用。 我们将学习几种可用于相似性学习的模型架构。
\ No newline at end of file
# 相似学习
在本章中,我们将学习相似性学习并学习相似性学习中使用的各种损失函数。 当每个班级的数据集都很小时,相似性学习对我们很有用。 我们将了解可用于面部分析的不同数据集,并建立用于面部识别,界标检测的模型。 我们将在本章介绍以下主题:
* 相似性学习的不同算法
* 用于相似度学习的各种损失函数
* 可以使用此类模型的各种方案
* 人脸识别的完整过程
# 相似性学习算法
**相似性学习**是训练度量以计算两个实体之间的相似性的过程。 由于学习了相似性,这也可以称为度量学习。 度量可以是欧几里得或余弦或其他自定义距离函数。 实体可以是任何数据,例如图像,视频,文本或表格。 为了计算度量,需要图像的矢量表示。 此表示可以是 CNN 计算的特征,如[第 3 章](../Text/03.html)*图像检索*中所述。 为对象分类而学习的 CNN 可以用作计算度量的向量。 为图像分类而获得的特征向量将不是手头任务的最佳表示。 在相似性学习中,我们发现有关 CNN 的信息,这些 CNN 会为相似性学习任务生成经过训练的特征。 这里给出了相似学习的一些应用:
* 用于生物识别的人脸验证以比较两个人脸
* 视觉搜索现实世界中的物体以在线查找类似产品
* 视觉推荐某些属性相似的产品
在本章中,我们将详细了解人脸验证。 因此,让我们从可用于相似性学习的算法开始。
# 连体网络
顾名思义,暹罗网络是一种神经网络模型,其中训练该网络以区分两个输入。 暹罗网络可以训练 CNN,以通过两个编码器产生嵌入。 每个编码器被馈送正对或负对中的一个图像。 暹罗网络所需的数据少于其他深度学习算法。 最初引入暹罗网络来比较签名。 下图显示了一个暹罗网络。 权重在网络之间共享:
![](img/451420ac-8f1b-415c-b8d7-f893cf701fee.png)
连体网络的另一种用途是一次性学习。 **单次学习**是仅举一个示例的学习技术。 在这种情况下,可以显示图像,并判断它们是否相似。 对于大多数相似性学习任务,需要一对正负对进行训练。 可以将此类数据集与可用于分类任务的任何数据集一起形成,前提是它们是欧几里得距离。 这些算法与前几章中的算法之间的主要区别在于,这些编码器试图将一个编码器与另一个编码器区分开。
# 对比损失
对比损失通过相似度区分图像。 使用相似性度量比较特征或潜在层,并与目标一起训练相似性得分。 在正对的情况下,目标将为 0,因为两个输入相同。 对于负数对,在余弦距离或正则欧几里得距离的情况下,潜对之间的距离最大为 0。 损耗可以由`contrastive_loss`定义,在以下代码中进行解释:
```py
def contrastive_loss(model_1, model_2, label, margin=0.1):
distance = tf.reduce_sum(tf.square(model_1 - model_2), 1)
loss = label * tf.square(
tf.maximum(0., margin - tf.sqrt(distance))) + (1 - label) * distance
loss = 0.5 * tf.reduce_mean(loss)
return loss
```
比较两个模型的距离并计算损失。 现在,我们将定义和训练一个暹罗网络。 对于暹罗网络,我们将需要两个相同的模型。 接下来,借助以下代码,为具有给定输入的简单 CNN 定义一个函数:
```py
def get_model(input_):
input_reshape = tf.reshape(input_, [-1, 28, 28, 1],
name='input_reshape')
convolution_layer_1 = convolution_layer(input_reshape, 64)
pooling_layer_1 = pooling_layer(convolution_layer_1)
convolution_layer_2 = convolution_layer(pooling_layer_1, 128)
pooling_layer_2 = pooling_layer(convolution_layer_2)
flattened_pool = tf.reshape(pooling_layer_2, [-1, 5 * 5 * 128],
name='flattened_pool')
dense_layer_bottleneck = dense_layer(flattened_pool, 1024)
return dense_layer_bottleneck
```
定义的模型将使用两次来定义暹罗网络所需的编码器。 接下来,定义两个模型的占位符。 对于每一对,输入的相似性也作为输入提供。 定义的模型相同。 还可以定义模型,以便共享权重。 此处定义了左右两个模型:
```py
left_input = tf.placeholder(tf.float32, shape=[None, input_size])
right_input = tf.placeholder(tf.float32, shape=[None, input_size])
y_input = tf.placeholder(tf.float32, shape=[None, no_classes])
left_bottleneck = get_model(left_input)
right_bottleneck = get_model(right_input)
```
瓶颈层是从模型中获取的,并被串联在一起。 这对于相似性学习问题至关重要。 可以创建任意数量的模型,并且可以连接最后的图层,如下所示:
```py
dense_layer_bottleneck = tf.concat([left_bottleneck, right_bottleneck], 1)
```
接下来,添加一个辍学层,并从级联层中计算出 logit。 然后,该过程类似于任何其他网络,如下所示:
```py
dropout_bool = tf.placeholder(tf.bool)
dropout_layer = tf.layers.dropout(
inputs=dense_layer_bottleneck,
rate=0.4,
training=dropout_bool
)
logits = dense_layer(dropout_layer, no_classes)
with tf.name_scope('loss'):
softmax_cross_entropy = tf.nn.softmax_cross_entropy_with_logits(
labels=y_input, logits=logits)
loss_operation = tf.reduce_mean(softmax_cross_entropy, name='loss')
tf.summary.scalar('loss', loss_operation)
with tf.name_scope('optimiser'):
optimiser = tf.train.AdamOptimizer().minimize(loss_operation)
with tf.name_scope('accuracy'):
with tf.name_scope('correct_prediction'):
predictions = tf.argmax(logits, 1)
correct_predictions = tf.equal(predictions, tf.argmax(y_input, 1))
with tf.name_scope('accuracy'):
accuracy_operation = tf.reduce_mean(
tf.cast(correct_predictions, tf.float32))
tf.summary.scalar('accuracy', accuracy_operation)
session = tf.Session()
session.run(tf.global_variables_initializer())
merged_summary_operation = tf.summary.merge_all()
train_summary_writer = tf.summary.FileWriter('/tmp/train', session.graph)
test_summary_writer = tf.summary.FileWriter('/tmp/test')
test_images, test_labels = mnist_data.test.images, mnist_data.test.labels
```
数据必须分别输入左右模型,如下所示:
```py
for batch_no in range(total_batches):
mnist_batch = mnist_data.train.next_batch(batch_size)
train_images, train_labels = mnist_batch[0], mnist_batch[1]
_, merged_summary = session.run([optimiser, merged_summary_operation],
feed_dict={
left_input: train_images,
right_input: train_images,
y_input: train_labels,
dropout_bool: True
})
train_summary_writer.add_summary(merged_summary, batch_no)
if batch_no % 10 == 0:
merged_summary, _ = session.run([merged_summary_operation,
accuracy_operation], feed_dict={
left_input: test_images,
right_input: test_images,
y_input: test_labels,
dropout_bool: False
})
test_summary_writer.add_summary(merged_summary, batch_no)
```
我们已经看到了如何定义一个暹罗网络。 定义了两个编码器,并连接了潜在空间以形成训练损失。 左右模型分别提供数据。 接下来,我们将看到如何在单个网络中执行相似性学习。
# 面对网
Schroff 等人提出的 FaceNet 模型。 ( [https://arxiv.org/pdf/1503.03832.pdf](https://arxiv.org/pdf/1503.03832.pdf) )解决了面部验证问题。 它学习一个深层的 CNN,然后将面部图像转换为嵌入图像。 嵌入可用于比较人脸以查看其相似程度,并可通过以下三种方式使用:
* **人脸验证**考虑两个人脸,并确定它们是否相似。 面部验证可以通过计算距离度量来完成。
* **人脸识别**是用于用名字标记人脸的分类问题。 嵌入向量可用于训练最终标签。
* **人脸聚类**将相似的人脸分组,就像照片应用程序将同一个人的照片聚在一起的方式一样。 诸如 K-means 之类的聚类算法用于对人脸进行分组。
下图显示了 FaceNet 架构:
![](img/b1d11aa0-8cd0-498c-873c-edc53897f6c9.png)
经 Schroff 等人许可复制。
FaceNet 会获取一批面部图像并进行训练。 在那一批中,将有几个正对。 在计算损耗时,考虑正对和最接近的几个负对。 挖掘选择性对可实现平稳训练。 如果所有负面因素一直都被推开,则训练不稳定。 比较三个数据点称为**三重态损失**。 在计算损耗时,图像被认为具有正负匹配。 底片仅被推动一定的幅度。 在此详细说明三重态损失。
# 三重损失
三元组损失学习图像的得分向量。 人脸描述符的得分向量可用于验证欧几里得空间中的人脸。 在学习投影的意义上,三元组损失类似于度量学习,因此可以区分输入。 这些投影或描述符或分数矢量是紧凑的表示形式,因此可以视为降维技术。 一个三元组由一个锚点,正负面组成。 锚可以是任何人的面孔,正面是同一个人的图像。 负片图像可能来自另一个人。 显然,对于给定的锚点,将会有很多负面的面孔。 通过选择当前更靠近锚点的底片,编码器将很难区分面部,从而使其学习效果更好。 此过程称为**硬底开采**。 可以在欧氏空间中使用阈值获得更接近的负值。 下图描述了三重态损失模型:
![](img/861242f9-1c52-49ef-a891-0377251b7745.png)
经 Schroff 等人许可复制。
TensorFlow 中的损失计算如下所示:
```py
def triplet_loss(anchor_face, positive_face, negative_face, margin):
def get_distance(x, y):
return tf.reduce_sum(tf.square(tf.subtract(x, y)), 1)
positive_distance = get_distance(anchor_face, positive_face)
negative_distance = get_distance(anchor_face, negative_face)
total_distance = tf.add(tf.subtract(positive_distance, negative_distance), margin)
return tf.reduce_mean(tf.maximum(total_distance, 0.0), 0)
```
三胞胎的开采是一项艰巨的任务。 每个点都必须与其他点进行比较,以获得适当的锚点和正对。 三元组的挖掘如下所示:
```py
def mine_triplets(anchor, targets, negative_samples):
distances = cdist(anchor, targets, 'cosine')
distances = cdist(anchor, targets, 'cosine').tolist()
QnQ_duplicated = [
[target_index for target_index, dist in enumerate(QnQ_dist) if dist == QnQ_dist[query_index]]
for query_index, QnQ_dist in enumerate(distances)]
for i, QnT_dist in enumerate(QnT_dists):
for j in QnQ_duplicated[i]:
QnT_dist.itemset(j, np.inf)
QnT_dists_topk = QnT_dists.argsort(axis=1)[:, :negative_samples]
top_k_index = np.array([np.insert(QnT_dist, 0, i) for i, QnT_dist in enumerate(QnT_dists_topk)])
return top_k_index
```
由于距离计算发生在 CPU 中,因此这可能会使在 GPU 机器上的训练变慢。 FaceNet 模型是训练人脸相似模型的最新方法。
# DeepNet 模型
DeepNet 模型用于学习用于面部验证任务(例如 FaceNet)的面部嵌入。 这是对上一部分中讨论的 FaceNet 方法的改进。 它需要对同一张脸进行多次裁剪,并通过多个编码器才能获得更好的嵌入效果。 与 FaceNet 相比,此方法具有更高的准确性,但需要更多时间进行处理。 面部裁切在相同区域进行,并通过其各自的编码器。 然后将所有层连接起来以进行三重态损失的训练。
# 深排
Wang 等人提出的 DeepRank。 ( [https://users.eecs.northwestern.edu/~jwa368/pdfs/deep_ranking.pdf](https://users.eecs.northwestern.edu/~jwa368/pdfs/deep_ranking.pdf) )用于根据相似度对图像进行排名。 图像通过不同的模型传递,如下所示:
![](img/85e5d360-602e-4bf4-b596-09e44f152e27.png)
经王等人许可转载。
在此也计算了三重态损耗,并且反向传播更加顺利。 然后可以将图像转换为线性嵌入以进行排名,如下所示:
![](img/e87e214e-cc3c-45bd-8629-75a2beeef0ea.png)
经 Wang 等人许可复制。
该算法对于排名目的非常有用。
# 视觉推荐系统
视觉推荐系统非常适合获取给定图像的推荐。 推荐模型提供具有相似属性的图像。 根据 Shankar 等人提出的以下模型。 ( [https://arxiv.org/pdf/1703.02344.pdf](https://arxiv.org/pdf/1703.02344.pdf) ),您可以了解相似图像的嵌入,并提供以下建议:
![](img/7337cf27-f5a5-4292-b4b2-d967bd5382f9.png)
图(a)显示了深度排名架构,图(b)显示了 VisNet 架构[经 Shankar 等人许可复制。
这些是用于相似性学习的一些算法。 在下一节中,我们将看到如何将这些技术应用于面部。
# 人脸分析
可以使用计算机视觉以多种方式分析人脸。 为此,需要考虑以下几个因素:
* **人脸检测**:找到人脸位置的边界框
* **面部标志检测**:查找鼻子,嘴巴等面部特征的空间点
* **人脸对齐**:将人脸转换成正面人脸以进行进一步分析
* **属性识别**:查找诸如性别,微笑等属性
* **情绪分析**:分析人的情绪
* **人脸验证**:查找两个图像是否属于同一个人
* **人脸识别**:为人脸识别
* **人脸聚类**:将同一个人的脸部分组在一起
在以下各节中,让我们详细了解这些任务的数据集和实现。
# 人脸检测
人脸检测类似于对象检测,我们在[第 4 章](../Text/04.html)*对象检测*中讨论过。 必须从图像中检测出面部的位置。 可以从 [http://vis-www.cs.umass.edu/fddb/](http://vis-www.cs.umass.edu/fddb/) 下载名为**人脸检测数据集和基准****FDDB** )的数据集。 。 它具有 2,845 张带有 5,171 张脸的图像。 可以从 Yang 等人提出的 [http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/](http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/) 下载另一个称为**宽脸**的数据集。 它具有 32,203 张图像和 393,703 张面孔。 这是来自更广泛的面部数据集的图像示例:
![](img/92e946e7-2fb7-468a-aacd-77039c30fbb4.jpg)
由杨等人提出。 并转载自 http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/support/intro.jpg
数据集的比例,姿势,遮挡,表情,妆容和照明度都有很好的变化。 另一个名为 **多属性标签的面孔****MALF** )的数据集包含 5,250 张图像,其中包含 11,931 张面孔。 可以从 [http://www.cbsr.ia.ac.cn/faceevaluation/](http://www.cbsr.ia.ac.cn/faceevaluation/) 链接访问 MALF。 在对象检测中使用的相同技术也可以应用于面部检测。
# 面对地标和属性
脸部地标是人脸的空间点。 空间点对应于各种面部特征的位置,例如眼睛,眉毛,鼻子,嘴巴和下巴。 点数可能会从 5 到 78 不等,具体取决于注释。 脸部界标也称为**基准点****脸部关键点****脸部姿势**。 人脸标志具有许多应用,如下所示:
* 脸部对齐以更好地进行脸部验证或识别
* 跟踪视频中的人脸
* 面部表情或情绪可以测量
* 有助于诊断疾病
接下来,我们将看到一些带有基准点注释的数据库。
# 多任务面部地标(MTFL)数据集
`MTFL`数据集由 Zhang 等人提出。 并带有五个面部标志以及性别,微笑,眼镜和头部姿势注释。 数据库中存在 12,995 张面孔。 可以从 [http://mmlab.ie.cuhk.edu.hk/projects/TCDCN/data/MTFL.zip](http://mmlab.ie.cuhk.edu.hk/projects/TCDCN/data/MTFL.zip) 下载`MTFL`
这是`MTFL`中存在的图像的示例:
![](img/de1113bf-8f62-4752-a656-e0f79ca4b1a4.jpg)
由张等人提出。 并转载自 http://mmlab.ie.cuhk.edu.hk/projects/TCDCN/img/1.jpg
面部在年龄,照度,情绪等方面有很多变化。 **头姿势**是脸部方向的角度,以度为单位。 眼镜,微笑,性别属性等都用二进制标签注释。
# Kaggle 关键点数据集
Kaggle 关键点数据集带有 15 个面部标志。 数据集中存在 8,832 张图像。 可以从链接 [https://www.kaggle.com/c/facial-keypoints-detection/data](https://www.kaggle.com/c/facial-keypoints-detection/data) 下载。 图像尺寸为 96 像素 x 96 像素。
# 多属性人脸地标(MAFL)数据集
Zhang 等人提出的`MAFL`数据集。 带有 5 种具有 40 种不同面部属性的面部地标。 数据库中存在 20,000 张面孔。 可以从 [https://github.com/zhzhanp/TCDCN-face-alignment](https://github.com/zhzhanp/TCDCN-face-alignment) 下载`MAFL`。 这是`MAFL`中存在的图像的示例:
![](img/0c8f1dd3-a580-402f-a9e2-66ccff7c19b1.png)
由 Liu 等人提出。 并转载自 http://mmlab.ie.cuhk.edu.hk/projects/celeba/overview.png
注释的属性包括尖头,带子,小胡子,卷发,戴帽子等。 这些图像也包含在`CelebA`数据集中,稍后将详细讨论。
# 学习面部关键点
如先前主题中所述,在计算关键面部点时,需要定义一些参数。 我们将使用以下代码来定义这些参数:
```py
image_size = 40 no_landmark = 10 no_gender_classes = 2 no_smile_classes = 2 no_glasses_classes = 2 no_headpose_classes = 5 batch_size = 100 total_batches = 300
```
接下来,为各种输入保留一些占位符。
```py
image_input = tf.placeholder(tf.float32, shape=[None, image_size, image_size])
landmark_input = tf.placeholder(tf.float32, shape=[None, no_landmark])
gender_input = tf.placeholder(tf.float32, shape=[None, no_gender_classes])
smile_input = tf.placeholder(tf.float32, shape=[None, no_smile_classes])
glasses_input = tf.placeholder(tf.float32, shape=[None, no_glasses_classes])
headpose_input = tf.placeholder(tf.float32, shape=[None, no_headpose_classes])
```
接下来,使用四个卷积层构造主模型,如以下代码所示:
```py
image_input_reshape = tf.reshape(image_input, [-1, image_size, image_size, 1],
name='input_reshape')
convolution_layer_1 = convolution_layer(image_input_reshape, 16)
pooling_layer_1 = pooling_layer(convolution_layer_1)
convolution_layer_2 = convolution_layer(pooling_layer_1, 48)
pooling_layer_2 = pooling_layer(convolution_layer_2)
convolution_layer_3 = convolution_layer(pooling_layer_2, 64)
pooling_layer_3 = pooling_layer(convolution_layer_3)
convolution_layer_4 = convolution_layer(pooling_layer_3, 64)
flattened_pool = tf.reshape(convolution_layer_4, [-1, 5 * 5 * 64],
name='flattened_pool')
dense_layer_bottleneck = dense_layer(flattened_pool, 1024)
dropout_bool = tf.placeholder(tf.bool)
dropout_layer = tf.layers.dropout(
inputs=dense_layer_bottleneck,
rate=0.4,
training=dropout_bool
)
```
接下来,我们将使用以下代码为所有不同的任务创建一个 logit 分支:
```py
landmark_logits = dense_layer(dropout_layer, 10)
smile_logits = dense_layer(dropout_layer, 2)
glass_logits = dense_layer(dropout_layer, 2)
gender_logits = dense_layer(dropout_layer, 2)
headpose_logits = dense_layer(dropout_layer, 5)
```
损耗是针对所有面部特征单独计算的,如以下代码所示:
```py
landmark_loss = 0.5 * tf.reduce_mean(
tf.square(landmark_input, landmark_logits))
gender_loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(
labels=gender_input, logits=gender_logits))
smile_loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(
labels=smile_input, logits=smile_logits))
glass_loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(
labels=glasses_input, logits=glass_logits))
headpose_loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(
labels=headpose_input, logits=headpose_logits))
loss_operation = landmark_loss + gender_loss + \
smile_loss + glass_loss + headpose_loss
```
现在,我们将初始化优化器并开始训练,如以下代码所示:
```py
optimiser = tf.train.AdamOptimizer().minimize(loss_operation)
session = tf.Session()
session.run(tf.initialize_all_variables())
fiducial_test_data = fiducial_data.test
for batch_no in range(total_batches):
fiducial_data_batch = fiducial_data.train.next_batch(batch_size)
loss, _landmark_loss, _ = session.run(
[loss_operation, landmark_loss, optimiser],
feed_dict={
image_input: fiducial_data_batch.images,
landmark_input: fiducial_data_batch.landmarks,
gender_input: fiducial_data_batch.gender,
smile_input: fiducial_data_batch.smile,
glasses_input: fiducial_data_batch.glasses,
headpose_input: fiducial_data_batch.pose,
dropout_bool: True
})
if batch_no % 10 == 0:
loss, _landmark_loss, _ = session.run(
[loss_operation, landmark_loss],
feed_dict={
image_input: fiducial_test_data.images,
landmark_input: fiducial_test_data.landmarks,
gender_input: fiducial_test_data.gender,
smile_input: fiducial_test_data.smile,
glasses_input: fiducial_test_data.glasses,
headpose_input: fiducial_test_data.pose,
dropout_bool: False
})
```
此过程可用于检测面部特征以及界标。
# 人脸识别
**面部识别****面部识别**是从数字图像或视频中识别人物的过程。 让我们在下一部分中了解可用于面部识别的数据集。
# 野生(LFW)数据集中的带标签的面孔
`LFW`数据集包含 13,233 张面孔和 5,749 位独特的人,被视为评估面孔验证数据集的标准数据集。 精度度量可用于评估算法。 可以在链接 [http://vis-www.cs.umass.edu/lfw/](http://vis-www.cs.umass.edu/lfw/) 中访问数据集。
# YouTube 面孔数据集
YouTube `faces`数据集包含 3,425 个视频剪辑,其中包含 1,595 个独特的人。 这些视频是从 YouTube 收集的。 数据集每人至少有两个视频。 该数据集被视为视频中人脸验证的标准数据集。 可以在链接 [https://www.cs.tau.ac.il/~wolf/ytfaces/](https://www.cs.tau.ac.il/~wolf/ytfaces/) 中访问数据集。
# CelebFaces 属性数据集(CelebA)
`CelebA` 数据集带有人物身份以及 5 个面部标志和 40 个属性的注释。 数据库中有 10,177 位独特的人,拥有 202,599 张面部图像。 它是可用于面部验证,检测,界标和属性识别问题的大型数据集之一。 图像具有带有各种注释的良好人脸变化。 可以在链接 [http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html](http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html) 中访问数据集。
# CASIA 网络人脸数据库
`CASIA`数据集带有 1 个 0,575 个独特的人注释,总共有 494,414 张图像。 该数据集可以从 [http://www.cbsr.ia.ac.cn/english/CASIA-WebFace-Database.html](http://www.cbsr.ia.ac.cn/english/CASIA-WebFace-Database.html) 获得。 这是可用于人脸验证和识别问题的第二大公共数据集。
# VGGFace2 数据集
Cao 等人提出的`VGGFace2`数据集。 被 9,131 位独特的人注解,具有 331 万张图片。 数据集可从 [http://www.robots.ox.ac.uk/~vgg/data/vgg_face2/](http://www.robots.ox.ac.uk/~vgg/data/vgg_face2/) 获得。 变化包括年龄,种族,姿势,职业和照度。 这是可用于面部验证的最大数据集。
这是数据集中存在的图像的示例:
![](img/d2c45b27-6fc0-409c-8aa6-196b8569aec2.png)
由曹等人提出。 并转载自 http://www.robots.ox.ac.uk/~vgg/data/vgg_face2/web_page_img.png
每个唯一的人的最小,平均和最大图像数分别是 87、362.6 和 843。
# 计算脸部之间的相似度
人脸相似度的计算是一个多步骤问题。 必须检测面部,然后找到基准点。 面可以与基准点对齐。 对齐的面可以用于比较。 如前所述,人脸检测类似于物体检测。 因此,为了找到面孔之间的相似性,我们将首先通过以下代码导入所需的库,以及`facenet`库:
```py
from scipy import misc
import tensorflow as tf
import numpy as np
import os
import facenet
print facenet
from facenet import load_model, prewhiten
import align.detect_face
```
可以如下所示加载和对齐图像:
```py
def load_and_align_data(image_paths, image_size=160, margin=44, gpu_memory_fraction=1.0):
minsize = 20
threshold = [0.6, 0.7, 0.7]
factor = 0.709 print('Creating networks and loading parameters')
with tf.Graph().as_default():
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=gpu_memory_fraction)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False))
with sess.as_default():
pnet, rnet, onet = align.detect_face.create_mtcnn(sess, None)
nrof_samples = len(image_paths)
img_list = [None] * nrof_samples
for i in range(nrof_samples):
img = misc.imread(os.path.expanduser(image_paths[i]), mode='RGB')
img_size = np.asarray(img.shape)[0:2]
bounding_boxes, _ = align.detect_face.detect_face(img, minsize, pnet, rnet, onet, threshold, factor)
det = np.squeeze(bounding_boxes[0, 0:4])
bb = np.zeros(4, dtype=np.int32)
bb[0] = np.maximum(det[0] - margin / 2, 0)
bb[1] = np.maximum(det[1] - margin / 2, 0)
bb[2] = np.minimum(det[2] + margin / 2, img_size[1])
bb[3] = np.minimum(det[3] + margin / 2, img_size[0])
cropped = img[bb[1]:bb[3], bb[0]:bb[2], :]
aligned = misc.imresize(cropped, (image_size, image_size), interp='bilinear')
prewhitened = prewhiten(aligned)
img_list[i] = prewhitened
images = np.stack(img_list)
return images
```
现在,我们将处理图像路径以获取嵌入。 相同的代码在这里给出:
```py
def get_face_embeddings(image_paths, model='/20170512-110547/'):
images = load_and_align_data(image_paths)
with tf.Graph().as_default():
with tf.Session() as sess:
load_model(model)
images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
feed_dict = {images_placeholder: images, phase_train_placeholder: False}
emb = sess.run(embeddings, feed_dict=feed_dict)
return emb
```
现在,我们将使用以下代码来计算嵌入之间的距离:
```py
def compute_distance(embedding_1, embedding_2):
dist = np.sqrt(np.sum(np.square(np.subtract(embedding_1, embedding_2))))
return dist
```
此函数将计算嵌入之间的**欧几里德**距离。
# 寻找最佳阈值
使用前面的功能,可以计算出该系统的精度。 以下代码可用于计算最佳阈值:
```py
import sys
import argparse
import os
import re
from sklearn.metrics import classification_report
from sklearn.metrics import accuracy_score
```
现在,使用以下代码从文件夹中获取图像路径:
```py
def get_image_paths(image_directory):
image_names = sorted(os.listdir(image_directory))
image_paths = [os.path.join(image_directory, image_name) for image_name in image_names]
return image_paths
```
通过嵌入时,将获得图像的距离,如以下代码所示:
```py
def get_labels_distances(image_paths, embeddings):
target_labels, distances = [], []
for image_path_1, embedding_1 in zip(image_paths, embeddings):
for image_path_2, embedding_2 in zip(image_paths, embeddings):
if (re.sub(r'\d+', '', image_path_1)).lower() == (re.sub(r'\d+', '', image_path_2)).lower():
target_labels.append(1)
else:
target_labels.append(0)
distances.append(compute_distance(embedding_1, embedding_2)) # Replace distance metric here
return target_labels, distances
```
阈值随以下代码所示而变化,并相应打印各种度量:
```py
def print_metrics(target_labels, distances):
accuracies = []
for threshold in range(50, 150, 1):
threshold = threshold/100.
predicted_labels = [1 if dist <= threshold else 0 for dist in distances]
print("Threshold", threshold)
print(classification_report(target_labels, predicted_labels, target_names=['Different', 'Same']))
accuracy = accuracy_score(target_labels, predicted_labels)
print('Accuracy: ', accuracy)
accuracies.append(accuracy)
print(max(accuracies))
```
现在,借助以下代码将图像路径传递给嵌入:
```py
def main(args):
image_paths = get_image_paths(args.image_directory)
embeddings = get_face_embeddings(image_paths) # Replace your embedding calculation here
target_labels, distances = get_labels_distances(image_paths, embeddings)
print_metrics(target_labels, distances)
```
最后,图像目录作为这些方法的主要参数传递,如以下代码所示:
```py
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('image_directory', type=str, help='Directory containing the images to be compared')
parsed_arguments = parser.parse_args(sys.argv[1:])
main(parsed_arguments)
```
在此示例中,我们采用了预先训练的模型并将其用于构建人脸验证方法。 ;
# 人脸聚类
**人脸聚类**是将同一个人的图像分组在一起以形成相册的过程。 可以提取人脸的嵌入,并且可以使用诸如 K 均值的聚类算法将同一个人的人脸合并在一起。 TensorFlow 为 K-means 算法提供了一个称为 `tf.contrib.learn.KmeansClustering` 的 API。 K-均值算法将数据点分组在一起。 借助这种 K-means 算法,可以提取专辑的嵌入内容,并且可以一起找到个人的面孔,或者换句话说,可以将聚在一起。
# 摘要
在本章中,我们介绍了相似性学习的基础知识。 我们研究了度量学习,暹罗网络和 FaceNet 等算法。 我们还介绍了损失函数,例如对比损失和三重损失。 还涵盖了两个不同的领域,即排名和推荐。 最后,通过理解几个步骤(包括检测,基准点检测和相似性评分)涵盖了面部识别的分步演练。
在下一章中,我们将了解递归神经网络及其在自然语言处理问题中的使用。 稍后,我们将使用语言模型和图像模型来对图像进行字幕。 我们将针对该问题访问几种算法,并查看两种不同类型数据的实现。
\ No newline at end of file
# 图片字幕
在本章中,我们将处理字幕图像的问题。 这涉及到检测对象,并且还提出了图像的文本标题。 图像字幕也可以称为**图像文本转换**。 曾经被认为是一个非常棘手的问题,我们现在在此方面取得了相当不错的成绩。 对于本章,需要具有相应标题的图像数据集。 在本章中,我们将详细讨论图像字幕的技术和应用。
我们将在本章介绍以下主题:
* 了解用于评估它们的不同数据集和指标
* 了解用于自然语言处理问题的一些技巧
* 向量模型的不同词
* 几种用于图像字幕的算法
* 不良结果和改进范围
# 了解问题和数据集
自动生成图像标题的过程是一项重要的深度学习任务,因为它结合了语言和视觉这两个世界。 该问题的独特性使其成为计算机视觉中的主要问题之一。 用于图像字幕的深度学习模型应该能够识别图像中存在的对象,并能够以自然语言生成表示对象与动作之间关系的文本。 此问题的数据集很少。 其中最著名的数据集是[第 4 章](../Text/04.html)*对象检测*中对象检测中涵盖的 COCO 数据集的扩展。
# 了解用于图像字幕的自然语言处理
由于必须从图像中生成自然语言,因此熟悉**自然语言处理****NLP** )变得很重要。 NLP 的概念是一个广泛的主题,因此我们将范围限制为与图像字幕相关的主题。 自然语言的一种形式是**文本**。 文本是单词或字符的序列。 文本的原子元素称为**令牌**,它是**字符**的序列。 字符是文本的原子元素。
为了处理文本形式的任何自然语言,必须通过删除标点符号,方括号等对文本进行预处理。 然后,必须通过将文本分隔为空格来将文本标记为单词。 然后,必须将单词转换为向量。 接下来,我们将看到矢量转换如何提供帮助。
# 以矢量形式表达单词
矢量形式的单词可以帮助自己执行算术运算。 向量必须紧凑,尺寸较小。 同义词应具有相似的向量,而反义词应具有不同的向量。 可以将单词转换为向量,以便可以如下所示比较关系:
![](img/d4061008-c857-4130-bb3e-6c1f418ea379.png)
该向量算法使得能够在不同实体之间的语义空间中进行比较。 接下来,我们将看到如何创建可将单词转换为矢量表示的深度学习模型。
# 将单词转换为矢量
通过在大型文本语料库上训练模型,可以将单词转换为向量。 训练模型,使得给定一个单词,该模型可以预测附近的单词。 在预测附近单词的单次热编码之前,首先对单词进行单次热编码,然后进行隐藏层。 以这种方式进行训练将创建单词的紧凑表示。 可以通过两种方式获得单词的上下文,如下所示:
* **跳过图**:给定一个单词,尝试预测几个接近的单词
* **连续词袋****CBOW** ):通过给定一组词来预测一个词,从而跳过跳跃语法
下图说明了这些过程:
![](img/2b34c3e0-28a8-4d55-8c1b-70ea045357c5.png)
两种方法均显示出良好的结果。 单词在嵌入空间中转换为向量。 接下来,我们将看到训练嵌入空间的详细信息。
# 训练嵌入
可以使用如下所示的模型来训练嵌入:
![](img/82b25c21-48e2-4de6-b9c0-03e6e245a825.png)
如上图所示,目标词是根据上下文或历史预测的。 该预测基于 **Softmax 分类器**。 隐藏层将嵌入作为紧凑的表示形式学习。 请注意,这不是完整的深度学习模型,但它仍然可以正常工作。 这是嵌入的低维可视化:
![](img/1dc5ed0c-353d-4884-8251-522e97b6bc8e.png)
使用 Softmax 分类器的嵌入的低维可视化
该可视化使用 TensorBoard 生成。 具有相似语义或不同词性的单词会一起出现。
我们已经学习了如何训练用于生成文本的紧凑表示。 接下来,我们将看到图像字幕的方法。
# 图像字幕处理方法及相关问题
已经提出了几种对图像进行字幕的方法。 直观地,将图像转换为视觉特征,并从这些特征生成文本。 生成的文本将采用词嵌入的形式。 生成文本的一些主要方法涉及 LSTM 和关注。 让我们从使用旧的生成文本的方法开始。
# 使用条件随机字段链接图像和文本
Kulkarni 等人在论文 [http://www.tamaraberg.com/papers/generation_cvpr11.pdf](http://www.tamaraberg.com/papers/generation_cvpr11.pdf) 中,提出了一种从图像中查找对象和属性并使用它来生成文本的方法。 **条件随机字段****CRF** )。 传统上,CRF 用于结构化预测,例如文本生成。 生成文本的流程如下所示:
![](img/114a59b6-d1fe-4f78-b9c6-32c7d416cd3a.png)
该图说明了使用 CRF 生成文本的过程[摘自 Kulkarni 等人]
CRF 的使用在以适当的前置词以连贯的方式生成文本方面存在局限性。 结果显示在这里:
![](img/29a03016-9c2c-4712-9dc8-cf412fb617b7.png)
复制自 Kulkarni 等。
结果对对象和属性具有正确的预测,但无法生成良好的描述。
# 在 CNN 功能上使用 RNN 生成字幕
Vinyals 等人在论文 [https://arxiv.org/pdf/1411.4555.pdf](https://arxiv.org/pdf/1411.4555.pdf) 中提出了一种端到端可训练的深度学习用于图像字幕的方法,该方法将 CNN 和 RNN 背靠背地堆叠在一起。 这是一个端到端的可训练模型。 结构如下所示:
![](img/5bf3b8f9-fa55-4a3f-a84c-b0818fcc856a.png)
转载自 Vinyals 等。 (2015 年)
该模型可以生成以自然语言完成的句子。 CNN 和 **LSTM** 的展开图如下所示:
![](img/04f54858-0741-427c-9c36-4c627f70ba34.png)
该图说明了 CNN 和 LSTM 架构[摘自 Vinyals 等人]
这是 **LSTM** 的展开视图。 此处显示了一组选择性的结果:
![](img/bc22e56f-e384-499e-be63-4e036405ee7c.png)
转载自 Vinyals 等。 (2015 年)
在此过程中,CNN 将图像编码为特征,RNN 从中生成句子。
# 使用图像排名创建字幕
Ordonez 等人在论文 [http://papers.nips.cc/paper/4470-im2text-describing-images-using-1-million-captioned-photographs.pdf](http://papers.nips.cc/paper/4470-im2text-describing-images-using-1-million-captioned-photographs.pdf) 中,提出了一种方法 对图像进行排名,然后生成标题。 此过程的流程如下所示:
![](img/5b285990-9992-4773-833a-720904a28188.png)
复制自 Ordonez 等。 (2015)
从排名图像中提取的高级信息可用于生成文本。 下图显示,可用于排名的图像越多,结果将越好:
![](img/fd60d31e-2040-4634-8f2b-245e258740ea.png)
复制自 Ordonez 等。 (2015)
# 从图像检索字幕和从字幕检索图像
Chen 等人在论文 [https://www.cs.cmu.edu/~xinleic/papers/cvpr15_rnn.pdf](https://www.cs.cmu.edu/~xinleic/papers/cvpr15_rnn.pdf) 中,提出了一种从文本中检索图像和从图像中检索文本的方法。 这是双向映射。 下图显示了一个用自然语言解释图像的人和另一个在视觉上思考它的人:
![](img/226bae31-12c2-4ec6-aa6a-5f54db2f7d6e.png)
转载自 Chen 等。 (2015)
检索字幕可以通过以下方式通过潜在空间连接图像和文本的编码器来实现:
![](img/f4ca10b9-38aa-46a9-9839-d7844a8401c1.png)
转载自 Chen 等。 (2015)
图像中的第一个模型是用于训练的完整模型。 如图中所示,视觉功能也可以用于生成句子,反之亦然。
# 密集字幕
Johnson 等人在论文 [https://www.cv-foundation.org/openaccess/content_cvpr_2016/papers/Johnson_DenseCap_Fully_Convolutional_CVPR_2016_paper.pdf](https://www.cv-foundation.org/openaccess/content_cvpr_2016/papers/Johnson_DenseCap_Fully_Convolutional_CVPR_2016_paper.pdf) 中,提出了一种用于密集字幕的方法。 首先,让我们看一些结果,以了解任务:
![](img/29744e79-12f1-49c0-a1bb-cd99c44a87d2.png)
转载自 Johnson 等。
如您所见,为图像中的对象和动作生成了单独的标题; 由此得名; **密集字幕**。 这是 Johnson 等人提出的架构:
![](img/b4d4bca7-5ea6-48a2-ad5c-c5063af9ebe5.png)
转自 Johnson 等。
该体系结构实质上是 Faster-RCNN 和 **LSTM** 的组合。 产生该区域以产生对象检测结果,并且使用该区域的视觉特征来产生字幕。
# 使用 RNN 字幕
Donahue 等人在论文 [https://arxiv.org/pdf/1411.4389.pdf](https://arxiv.org/pdf/1411.4389.pdf) 中,提出了**长期递归卷积架构****LRCN** ) 用于图像字幕的任务。 此模型的体系结构如下所示:
![](img/b0a60d74-0013-443b-b167-4cc77a74bfcb.png)
转载自 Donahue 等。
图中显示了 CNN 和 LSTM 在整个时间上的权重,这使得该方法可扩展到任意长序列。
# 使用多峰度量空间
Mao 等人在论文 [https://arxiv.org/pdf/1412.6632.pdf](http://papers.nips.cc/paper/4470-im2text-describing-images-using-1-million-captioned-photographs.pdf) 中提出了一种使用**多模态嵌入空间**生成字幕的方法。 下图说明了这种方法:
![](img/11930a42-1972-4cbb-a1d7-417207484a6c.png)
转自毛等人。
Kiros 等人在论文 [https://arxiv.org/pdf/1411.2539.pdf](https://arxiv.org/pdf/1411.2539.pdf) 中提出了另一种生成字幕的多模态方法,该方法可以将图像和文本嵌入同一多模态空间。 下图说明了这种方法:
![](img/c7396971-eb91-40ba-8e8e-9ea6ed8eef77.png)
复制自 Kiros 等。
两种多模式方法都给出了良好的结果。
# 使用注意力网络进行字幕
Xu 等人在论文 [https://arxiv.org/pdf/1502.03044.pdf](https://arxiv.org/pdf/1502.03044.pdf) 中,提出了一种使用**注意机制**进行图像字幕的方法。 注意机制对图像的某些区域比其他区域赋予更多权重。 注意还可以实现可视化,向我们展示模型生成下一个单词时所关注的位置。 建议的模型如下所示:
![](img/16daa7fa-b24c-4855-b677-fdb975865573.png)
转载自徐等人。
首先,从图像中提取 CNN 特征。 然后,将关注的 RNN 应用于生成单词的图像。
# 知道什么时候看
Lu 等。 ( [https://arxiv.org/pdf/1612.01887.pdf](https://arxiv.org/pdf/1612.01887.pdf) )提出了一种引起关注的方法,可提供出色的结果。 知道何时看待注意力捕获的区域会产生更好的结果。 流程如下所示:
![](img/94576f82-a74c-4394-bc55-78c027c88424.png)
摘自 Lu 等。
注意机制如下所示:
![](img/15065730-7c08-4996-9a4d-efd4eaa6b377.png)
摘自 Lu 等。
结果重点突出的区域如下:
![](img/aeb23046-5281-48a0-8f43-19852a14bc89.png)
摘自 Lu 等。
生成字幕时注意力的释放在此处可视化:
![](img/5dc70bd2-6e0f-4e12-a79b-564d36959461.png)
摘自 Lu 等。
我们已经看到,用于生成字幕几种方法。 接下来,我们将看到一个实现。
# 实施基于注意力的图像字幕
让我们使用以下代码从 VGG 和 LSTM 模型定义 CNN:
```py
vgg_model = tf.keras.applications.vgg16.VGG16(weights='imagenet',
include_top=False,
input_tensor=input_tensor,
input_shape=input_shape)
word_embedding = tf.keras.layers.Embedding(
vocabulary_size, embedding_dimension, input_length=sequence_length)
embbedding = word_embedding(previous_words)
embbedding = tf.keras.layers.Activation('relu')(embbedding)
embbedding = tf.keras.layers.Dropout(dropout_prob)(embbedding)
cnn_features_flattened = tf.keras.layers.Reshape((height * height, shape))(cnn_features)
net = tf.keras.layers.GlobalAveragePooling1D()(cnn_features_flattened)
net = tf.keras.layers.Dense(embedding_dimension, activation='relu')(net)
net = tf.keras.layers.Dropout(dropout_prob)(net)
net = tf.keras.layers.RepeatVector(sequence_length)(net)
net = tf.keras.layers.concatenate()([net, embbedding])
net = tf.keras.layers.Dropout(dropout_prob)(net)
```
现在,我们已经定义了 CNN,接下来使用以下代码定义关注层:
```py
h_out_linear = tf.keras.layers.Convolution1D(
depth, 1, activation='tanh', border_mode='same')(h)
h_out_linear = tf.keras.layers.Dropout(
dropout_prob)(h_out_linear)
h_out_embed = tf.keras.layers.Convolution1D(
embedding_dimension, 1, border_mode='same')(h_out_linear)
z_h_embed = tf.keras.layers.TimeDistributed(
tf.keras.layers.RepeatVector(num_vfeats))(h_out_embed)
Vi = tf.keras.layers.Convolution1D(
depth, 1, border_mode='same', activation='relu')(V)
Vi = tf.keras.layers.Dropout(dropout_prob)(Vi)
Vi_emb = tf.keras.layers.Convolution1D(
embedding_dimension, 1, border_mode='same', activation='relu')(Vi)
z_v_linear = tf.keras.layers.TimeDistributed(
tf.keras.layers.RepeatVector(sequence_length))(Vi)
z_v_embed = tf.keras.layers.TimeDistributed(
tf.keras.layers.RepeatVector(sequence_length))(Vi_emb)
z_v_linear = tf.keras.layers.Permute((2, 1, 3))(z_v_linear)
z_v_embed = tf.keras.layers.Permute((2, 1, 3))(z_v_embed)
fake_feat = tf.keras.layers.Convolution1D(
depth, 1, activation='relu', border_mode='same')(s)
fake_feat = tf.keras.layers.Dropout(dropout_prob)(fake_feat)
fake_feat_embed = tf.keras.layers.Convolution1D(
embedding_dimension, 1, border_mode='same')(fake_feat)
z_s_linear = tf.keras.layers.Reshape((sequence_length, 1, depth))(fake_feat)
z_s_embed = tf.keras.layers.Reshape(
(sequence_length, 1, embedding_dimension))(fake_feat_embed)
z_v_linear = tf.keras.layers.concatenate(axis=-2)([z_v_linear, z_s_linear])
z_v_embed = tf.keras.layers.concatenate(axis=-2)([z_v_embed, z_s_embed])
z = tf.keras.layers.Merge(mode='sum')([z_h_embed,z_v_embed])
z = tf.keras.layers.Dropout(dropout_prob)(z)
z = tf.keras.layers.TimeDistributed(
tf.keras.layers.Activation('tanh'))(z)
attention= tf.keras.layers.TimeDistributed(
tf.keras.layers.Convolution1D(1, 1, border_mode='same'))(z)
attention = tf.keras.layers.Reshape((sequence_length, num_vfeats))(attention)
attention = tf.keras.layers.TimeDistributed(
tf.keras.layers.Activation('softmax'))(attention)
attention = tf.keras.layers.TimeDistributed(
tf.keras.layers.RepeatVector(depth))(attention)
attention = tf.keras.layers.Permute((1,3,2))(attention)
w_Vi = tf.keras.layers.Add()([attention,z_v_linear])
sumpool = tf.keras.layers.Lambda(lambda x: K.sum(x, axis=-2),
output_shape=(depth,))
c_vec = tf.keras.layers.TimeDistributed(sumpool)(w_Vi)
atten_out = tf.keras.layers.Merge(mode='sum')([h_out_linear,c_vec])
h = tf.keras.layers.TimeDistributed(
tf.keras.layers.Dense(embedding_dimension,activation='tanh'))(atten_out)
h = tf.keras.layers.Dropout(dropout_prob)(h)
predictions = tf.keras.layers.TimeDistributed(
tf.keras.layers.Dense(vocabulary_size, activation='softmax'))(h)
```
在前面的代码的帮助下,我们定义了一个深度学习模型,该模型将 CNN 功能与 RNN 结合在一起,并借助注意力机制。 目前,这是生成字幕的最佳方法。
# 摘要
在本章中,我们已经了解了与图像标题相关的问题。 我们看到了一些涉及自然语言处理和各种`word2vec`模型(例如`GLOVE`)的技术。 我们了解了`CNN2RNN`,度量学习和组合目标等几种算法。 后来,我们实现了一个结合了 CNN 和 LSTM 的模型。
在下一章中,我们就来了解生成模型。 我们将从头开始学习和实现样式算法,并介绍一些最佳模型。 我们还将介绍很酷的**生成对抗网络****GAN** )及其各种应用。
\ No newline at end of file
# 生成模型
生成模型已经成为计算机视觉中的重要应用。 与前几章讨论的应用程序根据图像进行预测不同,生成模型可以为特定目标创建图像。 在本章中,我们将了解:
* 生成模型的应用
* 样式转移算法
* 训练超分辨率图像模型
* 生成模型的实施和培训
* 当前模型的缺点
在本章的最后,您将能够实现一些出色的应用程序来传递样式,并理解与生成模型相关的可能性和困难。
# 生成模型的应用
让我们从生成模型的可能应用开始本章。 应用程序是巨大的。 我们将看到其中一些应用程序,以了解动机和可能性。
# 艺术风格转移
艺术风格转移是将艺术风格转移到任何图像的过程。 例如,可以使用一幅图像的艺术风格和另一幅图​​像的内容来创建图像。 Gatys 等人在此显示了一个结合了几种不同样式的图像示例。 ( [https://www.cv-foundation.org/openaccess/content_cvpr_2016/papers/Gatys_Image_Style_Transfer_CVPR_2016_paper.pdf](https://www.cv-foundation.org/openaccess/content_cvpr_2016/papers/Gatys_Image_Style_Transfer_CVPR_2016_paper.pdf) )。 图像 **A** 是应用了样式的照片,其结果显示在其他图像中:
![](img/0e36da01-457e-4388-8cec-85994bf97af2.png)
转载自盖蒂斯等。
此应用程序引起了公众的注意,并且市场上有几种提供此功能的移动应用程序。
# 预测视频中的下一帧
可以使用生成模型从合成视频集中预测未来的帧。 在下面由 Lotter 等人提出的图像中。 ( [https://arxiv.org/pdf/1511.06380.pdf](https://arxiv.org/pdf/1511.06380.pdf) )左侧的图像是前一帧的模型,而右侧的图像相对于前一帧有两种算法 基本事实:
![](img/af300a68-236d-43de-b849-f7fbad8dd6b8.png)
复制自 Lotter 等。
生成模型生成的框架将是现实的。
# 图像超分辨率
**超分辨率**是从较小的图像创建高分辨率图像的过程。 传统上,插值用于创建更大的图像。 但是插值通过提供平滑效果而错过了高频细节。 经过训练的生成模型针对此超分辨率的特定目的而创建的图像具有出色的细节。 以下是 Ledig 等人提出的此类模型的示例。 ( [https://arxiv.org/pdf/1609.04802.pdf](https://arxiv.org/pdf/1609.04802.pdf) )。 左侧是通过 **4 倍缩放**生成的,看起来与右侧的原件没有区别:
![](img/96bebdcb-ce44-486b-9b87-f3f48a39b789.png)
转自 Ledig 等。
超分辨率对于在高质量的显示器或打印件上呈现低分辨率的图像很有用。 另一个应用可能是重建高质量的压缩图像。
# 交互式图像生成
生成模型可用于通过**交互作用**创建图像。 用户可以添加编辑内容,并且可以生成图像,以反映编辑内容,如 Zhu 等人建议的那样。 ( [https://arxiv.org/pdf/1609.03552v2.pdf](https://arxiv.org/pdf/1609.03552v2.pdf) ):
![](img/76957f5e-d2e5-4573-a6c7-3aa162253546.png)
复制自 Zhu 等。
如图所示,图像是根据编辑的形状和颜色生成的。 底部的绿色笔触创建了草原,矩形创建了摩天大楼,依此类推。 图像将被生成并通过用户的进一步输入进行微调。 生成的图像还可以用于检索可以利用的最相似的真实图像。 提供交互式图像生成是一种直观搜索图像的全新方法。
# 图像到图像的翻译
图像可用于生成具有特定目标的其他图像,因此此过程称为**图像到图像的转换**。 此处显示了此类翻译的一些示例,以及由 Isola 等人提出的相应标准。 ( [https://arxiv.org/pdf/1611.07004.pdf](https://arxiv.org/pdf/1611.07004.pdf) ):
![](img/a289fe70-1518-455e-91f5-22ae6a0a1de4.png)
复制自 Isola 等。
带有标签的图形可以转换为逼真的图像以用于创意目的。 黑白图像可以转换为彩色图像。 这样的翻译对于照片编辑应用程序,为旧电影着色,服装设计等非常有用。
# 文字到图像的生成
可以从文本描述中生成图像,其步骤类似于图像到图像的翻译。 以下是一些由 Reed 等人展示的自然文本描述生成的示例。 ( [https://arxiv.org/pdf/1605.05396.pdf](https://arxiv.org/pdf/1605.05396.pdf) ):
![](img/5f66cccf-b9c9-4609-b224-06e95897407b.png)
复制自 Reed 等。
当前,此模型仅适用于少数几个对象。 从文本生成图像还不够实际,无法在应用程序中使用。
# 修补
修复是填充图像中的间隙的过程,如下所示:
![](img/c3919cea-ec35-4cff-b352-15975acdb6f9.jpg)
资料来源:https://www.flickr.com/photos/littleredelf/4756059924/
左侧的图像是正常图像,右侧的图像是经过处理的图像。 从图像中可以看到,不需要的东西已从图片中删除。 修补对于从图像中删除不需要的对象以及填充扫描图稿的空间很有用。
# 调和
融合是将图像的一部分平滑地粘贴到另一个图像上而没有任何伪影的过程。 此处显示的图像**和**表示一种图像放置在另一图像上的情况,给人留下不好的印象。 图像 **b****c** 代表传统的混合技术,例如**修正的 Poisson 方法****多样条方法[**
最终图像或图像 **d** 显示了混合生成方法的结果,该方法比 Wu 等人的其他方法提供了更好的结果。 ( [https://arxiv.org/pdf/1703.07195.pdf](https://arxiv.org/pdf/1703.07195.pdf) ):
![](img/85081b77-9fc2-436d-8205-13ffb13b374a.png)
摘自 Wu 等。
混合对于照片编辑和电影行业中的特殊效果非常有用。
# 转换属性
可以使用生成模型来更改图像的属性。 兰珀(Lample)等人在此显示,可以修改人的脸部以反映不同的属性,例如性别,眼镜,年龄等。 ( [https://research.fb.com/wp-content/uploads/2017/11/fader_networks__conditional_attribute_based_image_generation_by_disentangling_in_latent_space.pdf](https://research.fb.com/wp-content/uploads/2017/11/fader_networks__conditional_attribute_based_image_generation_by_disentangling_in_latent_space.pdf) ):
![](img/e700b8c7-e5a1-4914-8f0a-afded0129609.png)
转载于 Lample 等人。
更改属性既可以用于创意应用程序,也可以用于娱乐,也可以用于生成更多具有变化的训练数据。
# 创建训练数据
生成模型可用于大规模生成训练,甚至可用于完善为训练而创建的合成图像。 这是使用 Wang 等人的生成模型为交通标志识别创建的合成图像。 ( [https://arxiv.org/pdf/1707.03124.pdf](https://arxiv.org/pdf/1707.03124.pdf)
![](img/b4123a9e-ccd5-4c45-9348-0935c45e318f.png)
转载自 Wang 等。
使用这些图像可以使分类更加准确。
# 创建新的动画角色
生成模型可用于创建具有各种条件的新动画角色,例如面部表情,发型,服装等,如 Jin 等人所示。 ( [https://arxiv.org/pdf/1708.05509.pdf](https://arxiv.org/pdf/1708.05509.pdf) ):
![](img/e92bfe6e-601b-49e4-a8df-72cb19f7c73e.png)
转载自 Jin 等。
创建具有不同属性的新角色可以彻底改变动画产业。
# 照片的 3D 模型
我们可以使用生成模型从 2D 图像创建 3D 模型,如 Wu 等人所示。 ( [https://arxiv.org/pdf/1610.07584.pdf](https://arxiv.org/pdf/1610.07584.pdf) ):
![](img/08cdcdde-c328-4fec-84bc-42c38bd0a7d7.png)
摘自 Wu 等。
从图像创建 3D 模型对于机器人技术,增强现实和动画行业很有用。 在以下各节中,我们将学习它们背后的算法。 在下一节中,我们将实现神经艺术风格的转换。
# 神经艺术风格的转移
我们将要实现的第一个应用程序是**神经艺术风格转换**。 在这里,我们将 **Van Gogh** 艺术的样式转移到图像上。 图像可以视为样式和内容的组合。 艺术风格转换技术将图像转换为看起来像具有特定绘画风格的绘画。 我们将看到如何编写这个想法。 `loss`功能将比较生成的图像与照片内容和绘画风格。 因此,针对图像像素而不是针对网络权重执行优化。 通过将照片的内容与生成的图像相比较,然后是绘画风格和生成的图像,可以计算出两个值。
# 内容丢失
由于像素不是一个好的选择,我们将使用各个图层的 CNN 功能,因为它们可以更好地表示内容。 如 [第 3 章](../Text/03.html)*图像检索*, 所示,初始层具有高频,例如边缘,拐角和纹理。 后面的层代表对象,因此更适合内容。 后者可以比像素更好地将*对象与*对象进行比较。 但是为此,我们需要先使用以下代码导入所需的库:
```py
import numpy as np
from PIL import Image
from scipy.optimize import fmin_l_bfgs_b
from scipy.misc import imsave
from vgg16_avg import VGG16_Avg
from keras import metrics
from keras.models import Model
from keras import backend as K
```
现在,让我们使用以下命令加载所需的图像:
```py
content_image = Image.open(work_dir + 'bird_orig.png')
```
我们将在此实例中使用以下图片:
![](img/c5f130bb-71bd-45ba-8bea-d224f13820de.png)
当我们使用 VGG 架构提取特征时,必须从所有图像中减去所有`ImageNet`图像的均值,如以下代码所示:
```py
imagenet_mean = np.array([123.68, 116.779, 103.939], dtype=np.float32)
def subtract_imagenet_mean(image):
return (image - imagenet_mean)[:, :, :, ::-1]
```
请注意,通道是不同的。 `preprocess`函数拍摄生成的图像并减去平均值,然后反转通道。 `deprocess`函数由于进行了预处理步骤而使效果相反,如以下代码所示:
```py
def add_imagenet_mean(image, s):
return np.clip(image.reshape(s)[:, :, :, ::-1] + imagenet_mean, 0, 255)
```
首先,我们将了解如何使用其他图像中的内容创建图像。 这是根据**随机噪声** 创建图像的过程。 此处使用的内容是某层中**激活**的总和。 我们将使随机噪声和图像之间的内容损失最小化,这被称为内容损失。 该损耗类似于逐像素损耗,但应用于层激活,因此将捕获内容而没有噪声。 任何 CNN 体系结构都可以用来转发内容图像和随机噪声。 比较这两个输出的激活,进行激活并计算均方误差。
冻结 CNN 权重时,将更新随机图像的像素。 在这种情况下,我们将冻结 VGG 网络。 现在,可以加载 VGG 模型。 生成图像对子采样技术(例如 **max pooling** )非常敏感。 无法从最大池中取回像素值。 因此,**平均池**比最大池更平滑。 使用平均池转换 VGG 模型的功能用于加载模型,如下所示:
```py
vgg_model = VGG16_Avg(include_top=False)
```
请注意,即使合并类型已更改,此模型的权重也与原始模型相同。 ResNet 和 Inception 模型不适合此操作,因为它们无法提供各种抽象。 我们将从模型冻结的最后一个 VGG 模型的卷积层`block_conv1`中获取激活。 这是 VGG 的第三层,具有广阔的接收范围。 这里给出了相同的代码供您参考:
```py
content_layer = vgg_model.get_layer('block5_conv1').output
```
现在,使用截断的 VGG 创建新模型,直到具有良好功能的图层。 因此,该图像现在可以加载,并且可以用于执行前向推断,以获得**实际激活的层**。 使用以下代码创建 TensorFlow 变量以捕获激活:
```py
content_model = Model(vgg_model.input, content_layer)
content_image_array = subtract_imagenet_mean(np.expand_dims(np.array(content_image), 0))
content_image_shape = content_image_array.shape
target = K.variable(content_model.predict(content_image_array))
```
让我们定义一个评估器类,以计算图像的损耗和梯度。 下列类在迭代的任意点返回损耗和梯度值:
```py
class ConvexOptimiser(object):
def __init__(self, cost_function, tensor_shape):
self.cost_function = cost_function
self.tensor_shape = tensor_shape
self.gradient_values = None def loss(self, point):
loss_value, self.gradient_values = self.cost_function([point.reshape(self.tensor_shape)])
return loss_value.astype(np.float64)
def gradients(self, point):
return self.gradient_values.flatten().astype(np.float64)
```
损失函数可以定义为特定卷积层的激活值之间的均方误差。 损失将在生成的图像和原始内容照片的层之间进行计算,如下所示:
```py
mse_loss = metrics.mean_squared_error(content_layer, target)
```
可以通过考虑模型的输入来计算损耗的梯度,如下所示:
```py
grads = K.gradients(mse_loss, vgg_model.input)
```
函数的输入是模型的输入,输出将是损耗和梯度值的数组,如下所示:
```py
cost_function = K.function([vgg_model.input], [mse_loss]+grads)
```
此函数是确定性的要优化的,因此不需要 **SGD**
```py
optimiser = ConvexOptimiser(cost_function, content_image_shape)
```
可以使用简单的优化程序来优化此功能,因为它是凸的,因此是确定性的。 我们还可以在迭代的每个步骤中保存图像。 我们将以可访问渐变的方式进行定义,就像我们使用 scikit-learn 的优化程序进行最终优化一样。 注意,该损失函数是凸的,因此,简单的优化器足以满足计算要求。 可以使用以下代码定义优化器:
```py
def optimise(optimiser, iterations, point, tensor_shape, file_name):
for i in range(iterations):
point, min_val, info = fmin_l_bfgs_b(optimiser.loss, point.flatten(),
fprime=optimiser.gradients, maxfun=20)
point = np.clip(point, -127, 127)
print('Loss:', min_val)
imsave(work_dir + 'gen_'+file_name+'_{i}.png', add_imagenet_mean(point.copy(), tensor_shape)[0])
return point
```
优化器采用`loss`函数,点和渐变,然后返回更新。 需要使用以下代码生成随机图像,以使内容损失最小化:
```py
def generate_rand_img(shape):
return np.random.uniform(-2.5, 2.5, shape)/1 generated_image = generate_rand_img(content_image_shape)
```
这是创建的随机图像:
![](img/bb7d0bd4-7167-43d7-938f-163330b4bfa5.png)
该优化可以运行 10 次迭代以查看结果,如下所示:
```py
iterations = 10 generated_image = optimise(optimiser, iterations, generated_image, content_image_shape, 'content')
```
如果一切顺利,那么在迭代过程中,损失应如下图所示:
```py
Current loss value: 73.2010421753
Current loss value: 22.7840042114
Current loss value: 12.6585302353
Current loss value: 8.53817081451
Current loss value: 6.64649534225
Current loss value: 5.56395864487
Current loss value: 4.83072710037
Current loss value: 4.32800722122
Current loss value: 3.94804215431
Current loss value: 3.66387653351
```
这是生成的图像,现在,它看起来几乎像只鸟。 可以运行优化以进行进一步的迭代以完成此操作:
![](img/6f9d1c9b-34ce-4c4b-ab82-4a67f76fb906.png)
优化器拍摄图像并更新像素,以使内容相同。 虽然效果较差,但可以在一定程度上重现图像内容。 通过迭代获得的所有图像都很好地说明了图像的生成方式。 此过程不涉及批处理。 在下一节中,我们将看到如何以绘画风格创建图像。
# 使用 Gram 矩阵的样式损失
创建具有原始图像内容的图像后,我们将看到如何仅使用样式创建图像。 样式可以认为是图像颜色和纹理的混合。 为此,我们将定义样式丢失。 首先,我们将覆盖图像并将其转换为数组,如以下代码所示:
```py
style_image = Image.open(work_dir + 'starry_night.png')
style_image = style_image.resize(np.divide(style_image.size, 3.5).astype('int32'))
```
这是我们加载的样式图像:
![](img/1f67b6de-e5e1-461d-b515-a96915351fd5.png)
现在,我们将使用以下代码通过更改通道对该图像进行预处理:
```py
style_image_array = subtract_imagenet_mean(np.expand_dims(style_image, 0)[:, :, :, :3])
style_image_shape = style_image_array.shape
```
为此,我们将考虑以下几层,就像我们在以下代码中所做的那样:
```py
model = VGG16_Avg(include_top=False, input_shape=shp[1:])
outputs = {l.name: l.output for l in model.layers}
```
现在,我们将使用以下代码将多层作为前四个块的数组输出:
```py
layers = [outputs['block{}_conv1'.format(o)] for o in range(1,3)]
```
现在创建一个新模型,该模型可以使用以下代码输出所有这些层并分配目标变量:
```py
layers_model = Model(model.input, layers)
targs = [K.variable(o) for o in layers_model.predict(style_arr)]
```
使用 **Gram 矩阵**计算样式损失。 革兰氏矩阵是矩阵及其转置的乘积。 激活值可以简单地转置和相乘。 然后将此矩阵用于计算样式和随机图像之间的误差。 克矩阵会丢失位置信息,但会保留纹理信息。 我们将使用以下代码定义 Gram 矩阵:
```py
def grammian_matrix(matrix):
flattened_matrix = K.batch_flatten(K.permute_dimensions(matrix, (2, 0, 1)))
matrix_transpose_dot = K.dot(flattened_matrix, K.transpose(flattened_matrix))
element_count = matrix.get_shape().num_elements()
return matrix_transpose_dot / element_count
```
您可能现在已经知道,它是一对列之间的相关性的度量。 高度和宽度尺寸被展平。 这不包括任何本地信息,因为坐标信息被忽略。 样式损失计算输入图像的 Gram 矩阵与目标之间的均方误差,如以下代码所示:
```py
def style_mse_loss(x, y):
return metrics.mse(grammian_matrix(x), grammian_matrix(y))
```
现在,我们使用以下代码通过汇总各层的所有激活来计算损失:
```py
style_loss = sum(style_mse_loss(l1[0], l2[0]) for l1, l2 in zip(style_features, style_targets))
grads = K.gradients(style_loss, vgg_model.input)
style_fn = K.function([vgg_model.input], [style_loss]+grads)
optimiser = ConvexOptimiser(style_fn, style_image_shape)
```
然后,通过创建随机图像,以与以前相同的方式解决它。 但是这次,我们还将应用高斯滤波器,如以下代码所示:
```py
generated_image = generate_rand_img(style_image_shape)
```
生成的随机图像如下所示:
![](img/6bf68978-98b1-449a-a813-639c4bda8b31.png)
优化可以运行 10 次迭代以查看结果,如下所示:
```py
generated_image = optimise(optimiser, iterations, generated_image, style_image_shape)
```
如果一切顺利,求解器应打印类似于以下的损耗值:
```py
Current loss value: 5462.45556641
Current loss value: 189.738555908
Current loss value: 82.4192581177
Current loss value: 55.6530838013
Current loss value: 37.215713501
Current loss value: 24.4533748627
Current loss value: 15.5914745331
Current loss value: 10.9425945282
Current loss value: 7.66888141632
Current loss value: 5.84042310715
```
这是生成的图像:
![](img/097eec9d-b9f7-42b3-81db-c4d68781126e.png)
在这里,我们从随机噪声中创建了具有特定绘画风格的图像,而没有任何位置信息。 在下一节中,我们将看到如何结合使用-内容损失和样式损失。
# 风格转移
现在,我们知道了如何重建图像,以及如何构建捕获原始图像样式的图像。 显而易见的想法可能是通过加权并添加两个`loss`函数来将这两种方法结合起来,如以下代码所示:
```py
w,h = style.size
src = img_arr[:,:h,:w]
```
和以前一样,我们将获取一系列图层输出以计算样式损失。 但是,我们仍然只需要一层输出来计算内容损失。 我们如何知道要抓哪一层? 如前所述,层越低,内容重构将越精确。 在将内容重建与样式相结合时,我们可以预期,对内容进行更宽松的重建将为样式带来更大的影响空间(例如:灵感)。 此外,即使没有相同的细节,后面的图层也可以确保图像看起来像相同的主题。 以下代码用于此过程:
```py
style_layers = [outputs['block{}_conv2'.format(o)] for o in range(1,6)]
content_name = 'block4_conv2'
content_layer = outputs[content_name]
```
现在,使用以下代码使用所需的输出层创建一个单独的样式模型:
```py
style_model = Model(model.input, style_layers)
style_targs = [K.variable(o) for o in style_model.predict(style_arr)]
```
我们还将使用以下代码为具有内容层的内容创建另一个模型:
```py
content_model = Model(model.input, content_layer)
content_targ = K.variable(content_model.predict(src))
```
现在,两种方法的合并就像合并它们各自的损失函数一样简单。 请注意,与我们之前的功能相反,此功能将产生三种不同类型的输出:
* 一个用于原始图像
* 一个用于模仿我们风格的图片
* 一个用于训练像素的随机图像
我们调整重建方式的一种方法是更改​​内容损失系数,此处为 1/10。 如果增加分母,则样式将对图像产生较大影响,而如果太大,则非结构化样式将掩盖图像的原始内容。 同样,如果它太小,则图像将没有足够的样式。 我们将在此过程中使用以下代码:
```py
style_wgts = [0.05,0.2,0.2,0.25,0.3]
```
`loss`函数同时包含样式和内容层,如下所示:
```py
loss = sum(style_loss(l1[0], l2[0])*w
for l1,l2,w in zip(style_layers, style_targs, style_wgts))
loss += metrics.mse(content_layer, content_targ)/10
grads = K.gradients(loss, model.input)
transfer_fn = K.function([model.input], [loss]+grads) evaluator = Evaluator(transfer_fn, shp)
```
我们将使用以下代码像以前一样运行求解器 10 次迭代:
```py
iterations=10
x = rand_img(shp) x = solve_image(evaluator, iterations, x)
```
损耗值应按如下所示打印:
```py
Current loss value: 2557.953125
Current loss value: 732.533630371
Current loss value: 488.321166992
Current loss value: 385.827178955
Current loss value: 330.915924072
Current loss value: 293.238189697
Current loss value: 262.066864014
Current loss value: 239.34185791
Current loss value: 218.086700439
Current loss value: 203.045211792
```
这些结果是惊人的。 他们每个人都以艺术家的风格来完成原始图像的复制工作。 生成的图像如下所示:
![](img/fe01f255-d682-4f67-8612-b692af48dd95.png)
现在,我们将结束样式转换部分。 此操作确实很慢,但可以处理任何图像。 在下一节中,我们将看到如何使用类似的想法来创建超分辨率网络。 有几种方法可以改善这种情况,例如:
* 将 高斯 滤镜添加到随机图像
* 为图层添加不同的权重
* 可以使用不同的图层和权重来满足
* 初始化图像而不是随机图像
* 颜色可以保存
* 掩码可以用于 的,用于指定所需的内容
* 任何草图都可以转换为绘画
* 绘制草图并创建图像
通过训练 CNN 输出任何图像,都可以将其转换为艺术风格。
# 生成对抗网络
**生成对抗网络****GAN** )由 **Ian Goodfellow** 于 2014 年发明。这是一种无监督算法,其中两个神经网络被训练为鉴别器和生成器。 , 同时。 该技术可以根据随机噪声生成图像,鉴别器可以评估是否为原始图像。 经过进一步培训后,生成器网络可以生成逼真的图像。 生成器网络通常是反卷积神经网络,而鉴别器是卷积神经网络。
理解这一点的一个很好的类比是,将生成器看作是伪造钱币的人,而将鉴别器看作是确定钱币是否为假币的警察。 生成器会根据警察的反馈不断提高伪钞的质量,直到警察无法区分伪钞和伪钞。 现在,让我们从实现开始。
# 甘香草
原始 GAN 称为**香草 GAN** 。 在构建模型之前,让我们定义一些对本章其余部分有用的层。 以下是`convolutional_layers`,其中添加了泄漏激活和正则化:
```py
def convolution_layer(input_layer,
filters,
kernel_size=[4, 4],
activation=tf.nn.leaky_relu):
layer = tf.layers.conv2d(
inputs=input_layer,
filters=filters,
kernel_size=kernel_size,
activation=activation,
kernel_regularizer=tf.nn.l2_loss,
bias_regularizer=tf.nn.l2_loss,
)
add_variable_summary(layer, 'convolution')
return layer
```
接下来,我们将使用以下代码定义与带有正则化的`convolution_layer`相反的`transpose_convolution_layer`
```py
def transpose_convolution_layer(input_layer,
filters,
kernel_size=[4, 4],
activation=tf.nn.relu,
strides=2):
layer = tf.layers.conv2d_transpose(
inputs=input_layer,
filters=filters,
kernel_size=kernel_size,
activation=activation,
strides=strides,
kernel_regularizer=tf.nn.l2_loss,
bias_regularizer=tf.nn.l2_loss,
)
add_variable_summary(layer, 'convolution')
return layer
```
接下来,我们将使用以下代码定义一个具有非线性激活的密集层:
```py
def dense_layer(input_layer,
units,
activation=tf.nn.relu):
layer = tf.layers.dense(
inputs=input_layer,
units=units,
activation=activation
)
add_variable_summary(layer, 'dense')
return layer
```
现在,我们将定义一个生成器,该生成器将噪声作为输入并变为图像。 发生器由几个完全连接的层组成,然后是转置卷积层以对噪声进行上采样。 最后,提出了卷积层以使噪声成为单个通道。 每层之间都有批量归一化层,以使梯度平滑流动。 我们将使用以下代码定义生成器:
```py
def get_generator(input_noise, is_training=True):
generator = dense_layer(input_noise, 1024)
generator = tf.layers.batch_normalization(generator, training=is_training)
generator = dense_layer(generator, 7 * 7 * 256)
generator = tf.layers.batch_normalization(generator, training=is_training)
generator = tf.reshape(generator, [-1, 7, 7, 256])
generator = transpose_convolution_layer(generator, 64)
generator = tf.layers.batch_normalization(generator, training=is_training)
generator = transpose_convolution_layer(generator, 32)
generator = tf.layers.batch_normalization(generator, training=is_training)
generator = convolution_layer(generator, 3)
generator = convolution_layer(generator, 1, activation=tf.nn.tanh)
return generator
```
现在,我们将定义 GAN 的**鉴别器**部分,该部分可拍摄图像并尝试区分假冒商品和真实形象。 鉴别器是一个规则的卷积网络,上面有几个`convolutional_layers`,其后是致密层。 批归一化层位于层之间。 我们将使用以下代码来定义鉴别符:
```py
def get_discriminator(image, is_training=True):
x_input_reshape = tf.reshape(image, [-1, 28, 28, 1],
name='input_reshape')
discriminator = convolution_layer(x_input_reshape, 64)
discriminator = convolution_layer(discriminator, 128)
discriminator = tf.layers.flatten(discriminator)
discriminator = dense_layer(discriminator, 1024)
discriminator = tf.layers.batch_normalization(discriminator, training=is_training)
discriminator = dense_layer(discriminator, 2)
return discriminator
```
创建鉴别器后,我们将使用以下代码创建一个噪声矢量,该噪声矢量将作为生成器的输入:
```py
input_noise = tf.random_normal([batch_size, input_dimension])
```
可以使用 TensorFlow 中的`tf.contrib.gan`模块创建 GAN 模型。 它采用了 generator 和 discriminator 方法及其相应的输入,如下所示:
```py
gan = tf.contrib.gan.gan_model(
get_generator,
get_discriminator,
real_images,
input_noise)
```
现在,可以使用以下代码从`gan_train`方法开始训练,该方法将`gan_train_ops`方法带给生成器和鉴别器以损失,并对优化器进行优化,并使用以下代码:
```py
tf.contrib.gan.gan_train(
tf.contrib.gan.gan_train_ops(
gan,
tf.contrib.gan.gan_loss(gan),
tf.train.AdamOptimizer(0.001),
tf.train.AdamOptimizer(0.0001)))
```
通过运行此命令,将创建可从随机矢量输出图像的 GAN 模型。 生成的图像不受限制,可以来自任何标签。 在下一节中,我们将使用条件 GAN 生成所需的输出。
# Conditional GAN
有条件的 GAN 生成带有所需标签的图像。 例如,我们可以要求模型生成数字 8,而模型将生成数字 8。为此,需要标签以及使用模型训练的噪声,如下所示:
```py
gan = tf.contrib.gan.gan_model(
get_generator,
get_discriminator,
real_images,
(input_noise, labels))
```
其余的培训与香草 GAN 相似。 接下来,我们将使用 GAN 压缩图像。
# 对抗损失
对抗性损失是来自发电机的损失。 该损失可以与伪图像和真实图像之间的逐像素损失相结合,以形成组合的对抗性损失。 GAN 模型必须随`real_images`一起提供给生成器和鉴别器,如下所示:
```py
gan = tf.contrib.gan.gan_model(
get_autoencoder,
get_discriminator,
real_images,
real_images)
```
生成器是一个自动编码器。 可以在[第 3 章](../Text/03.html)*图像检索*中找到该实现。 此后,我们将使用以下代码定义损失:
```py
loss = tf.contrib.gan.gan_loss(
gan, gradient_penalty=1.0)
l1_pixel_loss = tf.norm(gan.real_data - gan.generated_data, ord=1)
loss = tf.contrib.gan.losses.combine_adversarial_loss(
loss, gan, l1_pixel_loss, weight_factor=1)
```
GAN 损失的梯度是不利的。 然后,计算逐像素损失并将其添加到损失的损失中。 训练此模型将创建一个功能强大的自动编码器,可用于图像压缩。
# 图片翻译
正如我们在应用程序部分中所了解的,可以将一个图像转换为另一个图像。 输入图像被提供给鉴别器,而目标图像被提供给生成器,同时创建 GAN 模型,如下所示:
```py
gan = tf.contrib.gan.gan_model(
get_generator,
get_discriminator,
real_images,
input_images)
```
除像素级损失外,最小二乘损失也用于训练模型。 可以使用以下代码进行计算:
```py
loss = tf.contrib.gan.gan_loss(
gan,
tf.contrib.gan.losses.least_squares_generator_loss,
tf.contrib.gan.losses.least_squares_discriminator_loss)
l1_loss = tf.norm(gan.real_data - gan.generated_data, ord=1)
gan_loss = tf.contrib.gan.losses.combine_adversarial_loss(
loss, gan, l1_loss, weight_factor=1)
```
使用此技术,可以将一个图像转换为另一个图像。
# InfoGAN
InfoGAN 无需任何明确的监督培训即可生成所需标签的图像。 `infogan_model`接受非结构化和结构化的输入,如以下代码所示:
```py
info_gan = tf.contrib.gan.infogan_model(
get_generator,
get_discriminator,
real_images,
unstructured_input,
structured_input)
loss = tf.contrib.gan.gan_loss(
info_gan,
gradient_penalty_weight=1,
gradient_penalty_epsilon=1e-10,
mutual_information_penalty_weight=1)
```
由于训练不稳定,因此将损失定义为罚款。 增加罚分可以在训练过程中提供更大的稳定性。
# GAN 的缺点
GAN 生成的图像具有一些缺点,例如计数,透视图和全局结构。 当前正在广泛研究以改进模型。
# 视觉对话模型
**视觉对话模型****VDM** )可以基于图像进行聊天。 VDM 应用了计算机视觉,**自然语言处理****NLP** )和聊天机器人的技术。 它发现了主要的应用程序,例如向盲人解释图像,向医生解释医学扫描,虚拟伴侣等。 接下来,我们将看到解决这一难题的算法。
# VDM 算法
**Lu et al**[https://research.fb.com/wp-content/uploads/2017/11/camera_ready_nips2017.pdf)](https://research.fb.com/wp-content/uploads/2017/11/camera_ready_nips2017.pdf))提出了此处讨论的算法。 Lu 等人提出了基于 GAN 的 VDM。 生成器生成答案,鉴别器对这些答案进行排名。 以下是该过程的示意图:
![](img/d64d01f5-a996-4063-aac0-a64fec3d0e15.png)
基于 GAN 技术的 VDM 架构[摘自 Lu 等人]
聊天历史,当前问题和图像将作为输入提供给生成器。 接下来,我们将看到生成器如何工作。
# 发电机
生成器具有编码器和解码器。 编码器将图像,问题和历史记录作为输入。 编码器首先关注 **LSTM** 的历史记录,并关注图像的输出。 流程如下所示:
![](img/f1aed34d-d664-451a-a18e-50148eb5990b.png)
转载自 Lu 等。
整个历史记录都可用,并且 **LSTM** 记录了聊天的历史记录。 输出伴随有产生嵌入的图像。 编码器生成的嵌入被解码器用来创建答案。 解码器由 RNN 制成。 编码器和解码器一起形成生成器,生成可能的答案。 接下来,我们将了解鉴别器的工作原理。
# 鉴别器
鉴别器从生成器获取生成的序列并对其进行排序。 排名是通过对 n 对损失学习的嵌入完成的。 n 对损失类似于三重态损失,不同之处在于使用几对正负对进行比较。 这是该模型产生的一些结果。
![](img/981124a1-6d73-4233-a453-f44e7861a98e.png)
转载自 Lu 等。
结果是合理的,并且比简单的鉴别器产生的结果更好。
# 摘要
在本章中,我们了解了生成模型和大量应用程序。 我们实施它们是为了在保留内容的同时将样式从一种转换为另一种。 我们看到了 GAN 背后的直觉和经过训练的模型可以做到这一点。 最后,我们了解了视觉对话系统。
在下一章中,我们将学习用于视频分析的深度学习方法。 我们将看到如何通过摄像机,文件等访问视频内容。 我们将通过在帧级别和整个视频上应用分类来实现视频分类。 稍后,我们将看到如何跟踪视频中的对象。
\ No newline at end of file
# 视频分类
在本章中,我们将看到如何训练视频数据的深度学习模型。 我们将开始按帧对视频进行分类。 然后,我们将使用时间信息以获得更好的准确性。 稍后,我们将图像的应用扩展到视频,包括姿势估计,字幕和生成视频。
在本章中,我们将涵盖的以下主题:
* 视频分类的数据集和算法
* 将视频分成帧并分类视频
* 在单个框架级别上训练视觉特征模型 0
* 了解 3D 卷积及其在视频中的使用
* 在视频上合并运动矢量
* 利用时间信息进行目标跟踪
* 人体姿势估计和视频字幕等应用
# 了解视频并对其分类
视频不过是一系列图像。 视频沿时间方向为图像带来了新的维度。 图像的空间特征和视频的时间特征可以放在一起,比仅图像提供更好的结果。 额外的维度还导致大量空间,因此增加了训练和推理的复杂性。 用于处理视频的计算需求非常高。 视频还改变了深度学习模型的架构,因为我们必须考虑时间特征。
视频分类是用类别标记视频的任务。 类别可以在帧级别,也可以在整个视频中。 视频中可能有执行的动作或任务。 因此,视频分类可以标记视频中存在的对象或标记视频中发生的动作。 在下一部分中,我们将看到用于视频分类任务的可用数据集。
# 探索视频分类数据集
视频分类是视频数据研究的主要问题。 拍摄了几个视频,并标记了与数据相关的各种对象或动作。 数据集根据大小,质量和标签类型而有所不同。 有些甚至包括多个视频标签。 这些视频通常很短。 长视频可能会执行各种操作,因此可以在分别对剪切的视频片段或摘要进行分类之前在时间上进行分割。 接下来,我们将考虑一些特定数据集的细节。
# UCF101
佛罗里达中部**大学****UCF101** )是用于动作识别的数据集。 这些视频是在 YouTube 上收集的,由逼真的动作组成。 此数据集中有 101 个操作类别。 还有另一个名为 **UCF50** 的数据集,它具有 50 个类别。 整个动作中该数据集中有 13,320 个视频。 这些视频具有背景,比例,姿势,遮挡和照明条件的多种变化。 动作类别分为 25 个,它们具有相似的变化,例如背景,姿势,比例,视点,照明等。
动作和每个动作的视频数显示如下:
![](img/fa184ce6-4c4f-4e4e-9eb0-46906bd0b009.png)
来源:http://crcv.ucf.edu/data/UCF101/Number%20of%20Videos%202.jpg
所有 101 个动作都分为五种类型的动作,如下所示:人与物体的交互,身体动作,人与人的交互,演奏乐器和运动。 数据集和注释可从 [http://crcv.ucf.edu/data/UCF101.php](http://crcv.ucf.edu/data/UCF101.php) 下载。
接下来,我们将了解 YouTube-8M 数据集。
# YouTube-8M
**YouTube-8M** 数据集用于视频分类问题。 数据集包含带有标签和视觉功能的视频 URL。 以下是有关数据集的一些统计信息:
* **视频 URL 的数量**:700 万
* **影片剪辑的时长**:450,000
* **类标签的数量**:4,716
* **每个视频的平均标签数**:3.4
以下是各种类型的数据集摘要:
![](img/53ee0eb1-cef3-4906-84ef-e972b7792a36.png)
来源:https://research.google.com/youtube8m/vertical-videos.png
前面的图像可以让您一眼看出数据集中可用的标签类型。 视频数据很大,因此视觉特征被计算并随数据集一起提供。 可以通过以下链接访问数据集: [https://research.google.com/youtube8m/。](https://research.google.com/youtube8m/.)
# 其他数据集
还有更多的数据集可用于视频分类问题。 以下是更多数据集的详细信息:
* **Sports-1M****Sports-1 百万**):拥有 1,133,158 个具有 487 个课程的视频。 注释是自动完成的。 数据集可以从以下位置下载: [http://cs.stanford.edu/people/karpathy/deepvideo/](http://cs.stanford.edu/people/karpathy/deepvideo/)
* **UCF-11****中佛罗里达大学-11 动作**):拥有 1,600 部视频,包含 11 动作。 视频的速度为 29.97 fps(每秒帧数)。 数据集可以与`UCF101`一起下载。
* **HMDB-51****人体运动数据库-51 个动作**):包含 5,100 个具有 51 个动作的视频。 数据集链接为: [http://serre-lab.clps.brown.edu/resource/hmdb-a-large-human-motion-database](http://serre-lab.clps.brown.edu/resource/hmdb-a-large-human-motion-database)
* **Hollywood2** :拥有 12 个动作的 1,707 个视频。 数据集链接为: [http://www.di.ens.fr/~laptev/actions/hollywood2](http://www.di.ens.fr/~laptev/actions/hollywood2)
我们已经看到了可用于视频分类任务的数据集,以及描述和访问链接。 接下来,我们将看到如何加载视频并将其拆分为帧以进行进一步处理。
# 将视频分成帧
视频可以转换为帧并保存在目录中以备将来使用。 分成帧可以通过在训练过程之前对视频进行解压缩来帮助我们节省时间。 首先,让我们看一下将视频转换为帧的代码片段:
```py
import cv2
video_handle = cv2.VideoCapture(video_path)
frame_no = 0 while True:
eof, frame = video_handle.read()
if not eof:
break
cv2.imwrite("frame%d.jpg" % frame_no, frame)
frame_no += 1
```
使用此代码段,所有前面的数据集都可以转换为帧。 请注意,这将需要大量的硬盘空间。
# 视频分类方法
视频必须针对几种应用进行分类。 由于视频中包含大量数据,因此还必须考虑训练和推理计算。 所有视频分类方法均受图像分类算法启发。 VGG,Inception 等标准体系结构用于帧级别的特征计算,然后进行进一步处理。 诸如 **CNN****注意** ,先前章节中学习的和 **LSTM** 之类的概念将在此处有用。 直观地,以下方法可用于视频分类:
* 提取帧并使用在[第 2 章](../Text/02.html)*图像分类*中学习的模型,以帧为基础进行分类。
* 提取在[第 3 章](../Text/03.html)*图像检索*中学习的图像特征,并且可以按照[第 7 章](../Text/07.html)*图像说明中的描述,使用这些特征训练 RNN。*
* 在整个视频上训练 **3D 卷积**网络。 3D 卷积是 2D 卷积的扩展; 我们将在以下各节中详细了解 3D 卷积的工作原理。
* 使用视频的**光流**可以进一步提高精度。 光流是物体运动的模式,我们将在接下来的部分中详细介绍。
我们将看到几种算法,它们在各种计算复杂性上都具有良好的准确性。 可以通过将数据集转换为帧并将其子采样为相同的长度来准备它。 一些预处理会有所帮助,例如减去 Imagenet 的均值。
# 融合并行 CNN 进行视频分类
就帧而言,由于图像的下采样,视频的预测可能不会产生良好的结果,从而丢失了精细的细节。 使用高分辨率的 CNN 将增加推理时间。 因此,Karpathy 等。 ( [https://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/42455.pdf](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42455.pdf) )建议融合两个流 并行运行进行视频分类。 进行逐帧预测有两个问题,即:
* 由于较大的 CNN 架构,预测可能需要很长时间
* 独立的预测会沿时间维度丢失信息
使用更少的参数和两个并行运行的较小编码器可以简化体系结构。 视频同时通过两个 CNN 编码器传递。 一个编码器需要较低的分辨率并要处理高分辨率。 编码器具有交替的卷积,规范化和合并层。 两个编码器的最后一层通过完全连接的层连接。 另一个编码器具有相同的大小,但仅进行中心裁剪,如下所示:
![](img/1f34b3d0-4e01-4ed2-a5fa-da513c7a1155.png)
转载自 Karpathy 等。
帧的并行处理通过对视频进行下采样来加快运行时间。 CNN 体系结构的参数减半,同时保持相同的精度。 这两个流称为,,**中央凹**,,和**上下文**。 以下代码段显示了流:
```py
high_resolution_input = tf.placeholder(tf.float32, shape=[None, input_size])
low_resolution_input = tf.placeholder(tf.float32, shape=[None, input_size])
y_input = tf.placeholder(tf.float32, shape=[None, no_classes])
high_resolution_cnn = get_model(high_resolution_input)
low_resolution_cnn = get_model(low_resolution_input)
dense_layer_1 = tf.concat([high_resolution_cnn, low_resolution_cnn], 1)
dense_layer_bottleneck = dense_layer(dense_layer_1, 1024)
logits = dense_layer(dense_layer_bottleneck, no_classes)
```
下图显示了跨时间维度进行处理的帧:
![](img/d2211390-ff79-43d4-9b7e-842c29b2b66c.png)
转自 Karpathy 等。
可以在不同的时间观看视频,而不是通过固定大小的剪辑。 在前面的图像中介绍了连接时间信息的三种方式。 后期融合需要更长的时间框架,而早期融合则需要几个帧。 慢速融合将后期融合和早期融合结合在一起,可获得良好效果。 该模型在`Sports1M`数据集上进行了训练,该数据集具有 487 个类别,并达到了 50%的准确性。 将同一模型应用于`UCF101`时,可达到 60%的精度。
# 长时间对视频进行分类
融合方法适用于短视频片段。 分类较长的视频很困难,因为必须计算和记住很多帧。 Ng 等。 ( [https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Ng_Beyond_Short_Snippets_2015_CVPR_paper.pdf](https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Ng_Beyond_Short_Snippets_2015_CVPR_paper.pdf) )提出了两种对较长视频进行分类的方法:
* 第一种方法是在时间上合并卷积特征。 最大池用作功能`aggregation`方法。
* 第二种方法是使用 LSTM 连接处理各种可变长度视频的卷积功能。
下图显示了这两种方法:
![](img/1fc508d4-5f43-4908-aa11-13d9e0a2b336.png)
摘自 Ng 等。
可以提取 CNN 功能并将其馈送到小型 LSTM 网络,如以下代码所示:
```py
net = tf.keras.models.Sequential()
net.add(tf.keras.layers.LSTM(2048,
return_sequences=False,
input_shape=input_shape,
dropout=0.5))
net.add(tf.keras.layers.Dense(512, activation='relu'))
net.add(tf.keras.layers.Dropout(0.5))
net.add(tf.keras.layers.Dense(no_classes, activation='softmax'))
```
添加 LSTM 进行功能池可提供更好的性能。 功能以各种方式合并,如下图所示:
![](img/72c1430c-6604-4c17-abe6-133fe2d4daa9.png)
摘自 Ng 等。
如图所示,卷积特征可以几种不同的方式聚合。 池在完全连接的层之前完成。 该方法在`Sports1M`数据集和`UCF101`数据集中的准确率分别为 73.1%和 88.6%。 下图显示了 LSTM 方法:
![](img/bc029064-479f-4788-8aec-077b933fe468.png)
摘自 Ng 等。
该模型的计算量很高,因为使用了多个 LSTM。
# 流式传输两个 CNN 以进行动作识别
视频中对象的运动具有有关视频中执行的动作的非常好的信息。 物体的运动可以通过光流来量化。 Simonyan 和 Zisserman( [http://papers.nips.cc/paper/5353-two-stream-convolutional-networks-for-action-recognition-in-videos.pdf](http://papers.nips.cc/paper/5353-two-stream-convolutional-networks-for-action-recognition-in-videos.pdf) 提出了一种用于动作识别的方法,该方法使用来自图像和光流的两个流。
光流通过量化观察者与场景之间的相对运动来测量运动。 可以在 [https://www.youtube.com/watch?v=5VyLAH8BhF8](https://www.youtube.com/watch?v=5VyLAH8BhF8) 上找到有关光流的详细讲座。 通过运行以下命令可以获得光流:
```py
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
```
一个流采用单个帧并使用常规 CNN 预测动作。 另一个流获取多个帧并计算光流。 光流通过 CNN 进行预测。 下图显示了这两个预测:
![](img/ef8e4e07-e018-48ba-b82d-53613dddb8e8.png)
转载自 Simonyan 和 Zisserman
两种预测都可以与最终预测结合。
# 使用 3D 卷积进行时间学习
可以使用 3D 卷积对视频进行分类。 3D 卷积运算将体积作为输入并输出,而 2D 卷积可以将 2D 或体积输出并输出 2D 图像。 区别如下所示:
![](img/0e8faa5c-11ae-4631-b717-bcab34d2e075.png)
复制自 Tran 等。
前两个图像属于 2D 卷积。 输出始终是图像。 同时,3D 卷积输出一个体积。 区别在于内核在 3 个方向上进行卷积运算。 Tran 等。 ( [https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Tran_Learning_Spatiotemporal_Features_ICCV_2015_paper.pdf](https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Tran_Learning_Spatiotemporal_Features_ICCV_2015_paper.pdf) )将 3D 卷积用于视频分类。 3D 卷积模型如下所示:
![](img/f2f9bf9d-7ffd-4229-b5cf-0b90cbf076a1.png)
复制自 Tran 等。
以下是使用 3D 卷积的模型代码片段:
```py
net = tf.keras.models.Sequential()
net.add(tf.keras.layers.Conv3D(32,
kernel_size=(3, 3, 3),
input_shape=(input_shape)))
net.add(tf.keras.layers.Activation('relu'))
net.add(tf.keras.layers.Conv3D(32, (3, 3, 3)))
net.add(tf.keras.layers.Activation('softmax'))
net.add(tf.keras.layers.MaxPooling3D())
net.add(tf.keras.layers.Dropout(0.25))
net.add(tf.keras.layers.Conv3D(64, (3, 3, 3)))
net.add(tf.keras.layers.Activation('relu'))
net.add(tf.keras.layers.Conv3D(64, (3, 3, 3)))
net.add(tf.keras.layers.Activation('softmax'))
net.add(tf.keras.layers.MaxPool3D())
net.add(tf.keras.layers.Dropout(0.25))
net.add(tf.keras.layers.Flatten())
net.add(tf.keras.layers.Dense(512, activation='sigmoid'))
net.add(tf.keras.layers.Dropout(0.5))
net.add(tf.keras.layers.Dense(no_classes, activation='softmax'))
net.compile(loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(), metrics=['accuracy'])
```
3D 卷积需要大量的计算能力。 3D 卷积在`Sports1M`数据集上达到 90.2%的精度。
# 使用轨迹进行分类
Wang 等。 ( [https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Wang_Action_Recognition_With_2015_CVPR_paper.pdf](https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Wang_Action_Recognition_With_2015_CVPR_paper.pdf) )使用身体各部分的轨迹对所执行的动作进行分类。 这项工作结合了手工制作和深度学习的功能,可以进行最终预测。 以下是分类的表示形式:
![](img/fa390d0d-4a3e-48af-8fe6-4896c092d2e9.png)
转载自 Wang 等。
手工制作的特征是 **Fisher 向量**,这些特征来自 CNN。 下图演示了轨迹和特征图的提取:
![](img/389aa340-de61-4803-baaf-6e3b78b659f3.png)
转载自 Wang 等。
轨迹和特征图都在时间上组合在一起,以形成关于时间片段的最终预测。
# 多峰融合
杨等。 ( [http://research.nvidia.com/sites/default/files/pubs/2016-10_Multilayer-and-Multimodal/MM16.pdf](http://research.nvidia.com/sites/default/files/pubs/2016-10_Multilayer-and-Multimodal/MM16.pdf) )提出了一种具有 4 个模型的多模态视频融合 分类。 这四个模型分别是 3D 卷积特征,2D 光流,3D 光流和 2D 卷积特征。
该方法的数据流如下所示:
![](img/9b6f98f2-2e75-4ebb-b40e-ac265467545f.png)
转载自 Yang 等。
现在,让我们了解 Convlet 。 Convlet 是来自单个内核的小卷积输出。 下图显示了 convlet 对卷积层中空间权重的学习:
![](img/e258e5ea-f039-40f2-b79f-4fa0df3a7b55.png)
转载自 Yang 等。
空间权重指示卷积层中局部空间区域的区分度或重要性。 下图是在多层卷积层和完全连接层上完成的多层表示融合的图示:
![](img/b4160338-5686-48dd-a31a-f6c07404edcc.png)
转载自 Yang 等。
增强机制用于组合预测。 **Boosting** 是一种可以将多个模型预测组合为最终预测的机制。
# 参加区域分类
注意机制可以用于分类。 注意机制复制了人类专注于识别活动区域的行为。 注意机制赋予某些区域比其他区域更多的权重。 训练时从数据中学习权重方法。 注意机制主要有两种,即:
* **柔和的注意力**:性格确定,因此可以通过反向传播来学习。
* **刻苦关注**:性质随机,这需要复杂的学习机制。 由于需要采样数据,因此也很昂贵。
以下是软关注的可视化:
![](img/5c514fb5-7f01-4436-90e9-65ced3ea1f90.png)
转载自 Sharma 等。
根据注意,计算并加权 **CNN** 功能。 对某些区域的关注或权重可以用于可视化。 Sharma 等。 ( [https://arxiv.org/pdf/1511.04119.pdf](https://arxiv.org/pdf/1511.04119.pdf) )使用此想法对视频进行分类。 **LSTM** 被用作卷积特征。 **LSTM** 通过注意以下帧来预测区域,如下图所示:
![](img/c835ef24-72c0-4144-9fd8-890500f2c8ac.png)
转自 Sharma 等人。
每个 **LSTM** 堆栈都会预测位置和标签。 每个堆栈具有三个 **LSTM****LSTM** 堆栈的输入是卷积特征立方体和位置。 位置概率是注意权重。 注意的使用提高了准确性以及可视化预测的方法。
我们已经看到了各种视频分类方法。 接下来,我们将学习视频中的其他应用程序。
# 将基于图像的方法扩展到视频
图像可用于姿势估计,样式转换,图像生成,分割,字幕等等。 同样,这些应用程序也在视频中找到位置。 使用时间信息可以改善来自图像的预测,反之亦然。 在本节中,我们将看到如何将这些应用程序扩展到视频。
# 退缩人体姿势
人体姿势估计是视频数据的重要应用,可以改善其他任务,例如动作识别。 首先,让我们看一下可用于姿势估计的数据集的描述:
* **摆在野生的** **数据集**中:包含 30 个带有人体姿势注释的视频。 数据集链接为: [https://lear.inrialpes.fr/research/posesinthewild/](https://lear.inrialpes.fr/research/posesinthewild/) 。 该数据集带有人类上半身关节的注释。
* **电影院中标记的帧****FLIC** ):从 30 部电影中获得的人体姿势数据集,可在以下位置找到: [https://bensapp.github.io/flic-dataset.html](https://bensapp.github.io/flic-dataset.html)
Pfister 等。 ( [https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Pfister_Flowing_ConvNets_for_ICCV_2015_paper.pdf](https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Pfister_Flowing_ConvNets_for_ICCV_2015_paper.pdf) )提出了一种预测视频中人体姿势的方法。 以下是回归人体姿势的管道:
![](img/59d379eb-94cf-453c-aeac-28b1a04e6e44.png)
复制自 Pfister 等。
视频中的帧被获取并通过卷积网络。 融合图层,并获得姿势热图。 姿势热图与光流结合以获得扭曲的热图。 合并时间范围内的扭曲热图,以生成合并的热图,得到最终姿势。
# 跟踪面部标志
视频中的人脸分析需要人脸检测,界标检测,姿势估计,验证等。 计算地标对于捕获面部动画,人机交互和人类活动识别尤其重要。 除了在帧上进行计算外,还可以在视频上进行计算。 Gu 等。 ( [http://research.nvidia.com/sites/default/files/pubs/2017-07_Dynamic-Facial-Analysis/rnnface.pdf](http://research.nvidia.com/sites/default/files/pubs/2017-07_Dynamic-Facial-Analysis/rnnface.pdf) )提出了一种使用联合估计的检测和跟踪方法 使用 RNN 的视频中的面部地标。 结果优于逐帧预测和其他先前模型。 地标由 CNN 计算,时间方面在 RNN 中编码。 综合数据用于训练。
# 分割影片
使用时间信息时,可以更好地分割视频。 加德(Gadde)等人。 ( [https://ps.is.tuebingen.mpg.de/uploads_file/attachment/attachment/386/gadde2017videocnns.pdf](https://ps.is.tuebingen.mpg.de/uploads_file/attachment/attachment/386/gadde2017videocnns.pdf) )提出了一种通过扭曲来组合时间信息的方法。 下图演示了该解决方案,该方法将两个帧分段并且结合了变形:
![](img/0d1ae328-95c8-4794-b8a4-196046e5f578.png)
转载自 Gadde 等。
下图显示了翘曲网:
![](img/8c759f74-da66-4844-a5d5-66369605ba25.png)
转载自 Gadde 等。
在两个帧之间计算光流,将它们与变形结合在一起。 变形模块获取光流,对其进行转换,然后将其与变形的表示相结合。
# 字幕视频
[第 7 章](../Text/07.html)*图像字幕*说明了几种组合文本和图像的方法。 同样,可以为视频生成字幕,以描述上下文。 让我们看一下可用于字幕视频的数据集列表:
* **Microsoft Research-视频转文本****MSR-VTT** )具有 200,000 个视频剪辑和句子对。 可以从以下网站获取更多详细信息: [https://www.microsoft.com/zh-cn/research/publication/msr-vtt-a-large-video-description-dataset-for-bridging-video-and- 语言/](https://www.microsoft.com/en-us/research/publication/msr-vtt-a-large-video-description-dataset-for-bridging-video-and-language/)
* **MPII 电影描述语料库****MPII-MD** )可以从以下网站获取: [https://www.mpi-inf.mpg.de/departments/computer-vision-and 多模态计算/研究/视觉和语言/ mpii 电影描述数据集](https://www.mpi-inf.mpg.de/departments/computer-vision-and-multimodal-computing/research/vision-and-language/mpii-movie-description-dataset)。 它有 68,000 个句子和 94 部电影。
* **蒙特利尔视频注释数据集****M-VAD** )可从以下网站获得: [https://mila.quebec/zh/publications/public-datasets / m-vad /](https://mila.quebec/en/publications/public-datasets/m-vad/) 和有 49,000 个剪辑。
* **YouTube2Text** 包含 1,970 个视频,包含 80,000 个描述。
姚等。 ( [https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Yao_Describing_Videos_by_ICCV_2015_paper.pdf](https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Yao_Describing_Videos_by_ICCV_2015_paper.pdf) )提出了一种为视频添加字幕的方法。 经过训练以进行动作识别的 3D 卷积网络用于提取局部时间特征。 然后在特征上使用注意力机制以使用 RNN 生成文本。 该过程如下所示:
![](img/71cfe3d2-2787-4234-a683-8ca292510133.png)
转载自 Yao 等。
Donahue 等。 ( [https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Donahue_Long-Term_Recurrent_Convolutional_2015_CVPR_paper.pdf](https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Donahue_Long-Term_Recurrent_Convolutional_2015_CVPR_paper.pdf) )提出了另一种视频字幕或描述方法,该方法将 **LSTM** 与 卷积功能。
这类似于前面的方法,除了我们在此处使用 2D 卷积功能,如下图所示:
![](img/fe273a12-ded0-4027-b3d1-0657dee35178.png)
摘自 Donahue 等。
我们有几种将文本与图像结合起来的方法,例如活动识别,图像描述和视频描述技术。 下图说明了这些技术:
![](img/37ba89c5-80e9-4655-8e8f-9e664ddd5b29.png)
摘自 Donahue 等。
Venugopalan 等。 ( [https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Venugopalan_Sequence_to_Sequence_ICCV_2015_paper.pdf](https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Venugopalan_Sequence_to_Sequence_ICCV_2015_paper.pdf) )提出了一种使用编码器-解码器方法进行视频字幕的方法。 以下是他提出的技术的可视化:
![](img/6935b3c7-f3bb-4876-a3d5-f6f6559d7e05.png)
复制自 Venugopalan 等。
对于此方法,可以在图像的帧或光流上计算 **CNN**
# 产生影片
可以使用生成模型以无监督的方式生成视频。 可以使用当前帧预测未来的帧。 Ranzato 等。 ( [https://arxiv.org/pdf/1412.6604.pdf](https://arxiv.org/pdf/1412.6604.pdf) )提出了一种受语言模型启发的视频生成方法。 RNN 模型用于拍摄图像补丁并预测下一个补丁。
# 摘要
在本章中,我们涵盖了与视频分类有关的各种主题。 我们看到了如何将视频拆分为帧,以及如何将图像中的深度学习模型用于各种任务。 我们介绍了一些特定于视频的算法,例如跟踪对象。 我们看到了如何将基于视频的解决方案应用于各种场景,例如动作识别,手势识别,安全应用程序和入侵检测。
在下一章中,我们将学习如何将上一章中训练有素的模型部署到各种云和移动平台上的生产环境中。 我们将看到不同的硬件如何影响延迟和吞吐量方面的性能。
\ No newline at end of file
# 部署方式
在本章中,我们将学习如何在各种平台上部署经过训练的模型,以实现最大吞吐量和最小延迟。 我们将了解 GPU 和 CPU 等各种硬件的性能。 我们将遵循在 Amazon Web Services,Google Cloud Platform 等平台以及 Android,iOS 和 Tegra 等移动平台上部署 TensorFlow 的步骤。
我们将在本章介绍以下主题:
* 了解影响深度学习模型训练和推理性能的因素
* 通过各种方法提高性能
* 查看各种硬件的基准并学习调整它们以实现最佳性能的步骤
* 使用各种云平台进行部署
* 使用各种移动平台进行部署
# 模型表现
性能对于深度学习模型的培训和部署都很重要。 由于大数据或大模型架构,培训通常需要更多时间。 结果模型可能更大,因此在 RAM 受限的移动设备中使用时会出现问题。 更多的计算时间导致更多的基础架构成本。 推理时间在视频应用中至关重要。 由于前面提到了性能的重要性,因此在本节中,我们将研究提高性能的技术。 降低模型复杂度是一个简单的选择,但会导致精度降低。 在这里,我们将重点介绍一些方法,这些方法可以提高性能,而准确性却没有明显的下降。 在下一节中,我们将讨论量化选项。
# 量化模型
深度学习模型的权重具有 32 位浮点值。 当权重量化为 8 位时,精度下降很小,因此在部署中不会注意到。 结果权重的精度似乎对深度学习模型的精度性能影响较小。 这个想法对深度学习很有趣,并且在模型大小变得至关重要时很有用。 通过用 8 位值替换 32 位浮点值,可以显着减小模型大小并提高推理速度。 实施模型量化时有很多选择。 权重可以存储在 8 位中,但推理操作可以以 32 位浮点值执行。 架构的每个组件在量化大小上的行为可能有所不同,因此,取决于层,可以选择 32 或 16 或 8 位值。
量化工作有多种原因。 通常,深度学习模型经过训练可以解决图像中的噪声,因此可以被认为是健壮的。 推理计算可以具有冗余信息,并且可以由于量化而去除冗余信息。
最新的 CPU 和 RAM 硬件已针对浮点计算进行了调整,因此在此类硬件中量化效果可能不太明显。 随着为此目的引入越来越多的硬件,这种情况正在改变。 在 GPU 中,由于内存和速度现已适应较低的精确浮点运算,因此它们在内存和速度上存在明显差异。 还有其他特殊硬件可用于运行不太精确的浮动操作。
# 移动网
霍华德(Howard)和其他人( [https://arxiv.org/pdf/1704.04861.pdf](https://arxiv.org/pdf/1704.04861.pdf) )引入了一种称为 **MobileNets** 的新型模型,可用于移动和嵌入式应用程序。 MobileNets 可以用于不同的应用程序,例如对象检测,地标识别,人脸属性,细粒度分类,如下所示:
![](img/f363f9cf-4786-41e0-9812-d49b4c26039a.png)
转载自霍华德等人
MobileNets 通过用深度( **b** )和点向卷积( **c** )替换标准卷积滤波器( **a** )和点卷积( **c** )来减少模型的大小和计算量,如下所示:
![](img/dab9c6a5-cbd9-485e-8e96-832c17bedf0a.png)
转载自霍华德等人
批量归一化和激活层被添加到深度和点积卷积中,如下所示:
![](img/4ee8ae67-9658-480e-a775-5b6f45033be8.png)
转载自霍华德等人
有两个参数会影响模型的选择:
* **乘法和加法次数**:精度和多加法之间的权衡如下所示:
![](img/9f6fad9e-9ab5-405c-a5ac-8958cb342906.png)
转载自霍华德等人
* **模型**中的参数数量:此处显示权衡:
![](img/5a370687-9b2b-4f57-80fd-87792fdfae85.png)
转载自霍华德等人
MobileNets 已显示,可以在移动和嵌入式设备上使用的精度有所降低的情况下,可以减少模型的计算和尺寸。 在霍华德等人的文章中可以看到模型与精度之间的确切权衡。
# 云端部署
必须将这些模型部署在云中以用于多个应用程序。 我们将为此目的寻找主要的云服务提供商。
# AWS
Amazon Web Services(AWS)将支持扩展到基于 TensorFlow 的模型的开发和部署。 在 [https://aws.amazon.com/](https://aws.amazon.com/) 上注册 AWS,然后选择 **Amazon Machine Images****AMI** )之一。 AMI 是安装了所有必需软件的计算机的映像。 您不必担心安装软件包。 **AWS 提供了深度学习 AMI****DLAMI** ),以简化培训和部署深度学习模型。 有几种选择。 在这里,我们将使用 Conda,因为它带有运行 TensorFlow 所需的几个软件包。 Python 有两个选项:版本 2 和版本 3。以下代码将在 CUDA 8 的 Python 3 上使用 Keras 2 激活 TensorFlow:
```py
source activate tensorflow_p36
```
以下代码将在 CUDA 8 的 Python 2 上使用 Keras 2 激活 TensorFlow:
```py
source activate tensorflow_p27
```
您可以访问 [https://aws.amazon.com/tensorflow/](https://aws.amazon.com/tensorflow/) 了解更多详细信息和教程。
还可以通过执行以下给定的步骤来启动**虚拟机****VM** ):
1. 转到 [aws.amazon.com](https://aws.amazon.com/) ,然后使用您的 Amazon 帐户登录。
2. 从登录页面选择启动虚拟机:
![](img/d8d35771-0127-4f5f-9f7a-37edc6eb0c1c.png)
3. 在下一个窗口中,单击入门,选择 EC2 实例,如下所示:
![](img/cf4ce914-ae52-4a17-968d-206854461304.png)
4. 为 EC2 实例命名:
![](img/afc41e92-9559-4fb0-9214-91b3d9902bf4.png)
5. 选择操作系统的类型:
![](img/5d91face-5671-4648-93fd-69708c41002b.png)
6. 选择实例类型。 实例类型指示 RAM 和 CPU 大小不同的配置类型。 也有两个选项可供选择。 选择实例类型,然后单击“下一步”按钮:
![](img/39eeaa24-3a0a-47d7-8e72-87048ae2d5f3.png)
7. 创建一个隐私增强型邮件安全证书(PEM)文件,该文件将用于登录,如下所示:
![](img/25ab45d2-9f98-4720-a642-03263e11295b.png)
8. 创建实例将花费一些时间,最后,将显示完成状态:
![](img/06ba2432-c401-46c9-9046-5097bad8cbfc.png)
9. 接下来,单击进入 EC2 控制台按钮:
![](img/973f1291-4651-47fd-b924-e999c5f583a6.png)
10. 现在将创建实例; 单击连接按钮,如下所示:
![](img/9c23756d-5135-44af-9f23-d59513e9d581.png)
11. 接下来,必须将实例连接到虚拟机的命令提示符。 连接所需的说明在此步骤中给出。 您需要在之前的步骤中下载“ pem”文件。 按照显示的说明连接到系统:
![](img/13ef9b43-6bd6-4ef3-a9e9-526f0a69c84e.png)
12. 完成后,通过单击操作|实例状态|终止来终止实例:
![](img/c7b106a4-3f86-44a5-94fb-e8795643e0d2.png)
安装和执行步骤可以遵循[第 1 章](../Text/01.html)*入门*
# Google Cloud Platform
Google Cloud Platform(GCP)是 Google 提供的云平台,具有与 AWS 类似的功能。 通过执行以下步骤,可以使用一个简单的虚拟机来训练诸如 AWS 之类的模型:
1. 使用 [cloud.google.com](https://cloud.google.com/) 转到 Google Cloud Platform,然后使用您的 Gmail 帐户登录到该平台。
2. 现在,通过单击转到控制台按钮进入控制台:
![](img/b38e13e2-2b2a-45e4-841c-fc5feed34fcd.png)
3. 进入控制台后,通过单击 Compute Engine |进入 **VM 创建页面**。 右上角菜单中的 VM 实例,如以下屏幕截图所示:
![](img/0a5622e5-c108-4b28-8960-1eab8e3d9574.png)
4. 然后单击 CREATE INSTANCE 按钮,以创建所需的实例:
![](img/0671e492-4e4b-4edd-8622-8208b096056e.png)
5. 接下来,可以通过配置选择实例类型。 Zone 参数通知区域将部署实例。 通过选择靠近用户的区域,可以节省等待时间。 可以使用所需的 RAM 和 CPU 定制机器类型。 还可以选择 GPU,以进行更快的训练。 选择实例的大小,然后单击“创建”按钮,如以下屏幕截图所示:
![](img/ae5b0924-3d17-4afa-9e63-baa8859245ba.png)
6. 创建实例将需要几分钟。 然后,单击实例的 SSH 下拉列表,然后选择“在浏览器窗口中打开”选项,如下所示,以在浏览器中打开控制台:
![](img/783c512d-4692-4eeb-914d-9ff3f2d430b1.png)
使用该外壳,您可以安装 TensorFlow 并可以训练或部署模型。 有许多选项可从虚拟机的配置中选择。 根据成本和时间的权衡,可以选择配置。
GCP 具有**云机器学习引擎**,可在使用 TensorFlow 时为我们提供帮助。 GCP 的三个组件可以一起用于构建培训和部署基础架构:
1. 用于 的 Cloud DataFlow 预处理图像
2. 用于 和 培训和部署模型的云机器学习引擎
3. Google Cloud Storage 用于存储培训数据,代码和结果
可以在 [https://cloud.google.com/ml-engine/docs 上找到 使用云机器学习引擎建立自定义图像分类模型的出色教程。 / flowers-tutorial](https://cloud.google.com/ml-engine/docs/flowers-tutorial)
# 在设备中部署模型
TensorFlow 模型也可以部署在移动设备中。 移动设备包括智能手机,无人机,家用机器人等。 数十亿智能手机可以具有可以使用深度学习的计算机视觉应用程序。 可以拍照并搜索,流化带有标记场景的视频等。 在移动设备中进行部署意味着深度学习模型存在于设备上,并且推断发生在设备上。 设备上部署的模型有助于解决隐私问题。 在以下主题中,我们将讨论如何在各种移动平台上部署它们。
# 杰特逊 TX2
Jetson TX2 是由 NVIDIA 提供的嵌入式设备,专门用于高效 AI 计算。 Jetson TX2 轻巧,紧凑,因此适合在无人机,公共场所等中部署。 它还附带预装的 TensorRT,这是 TensorFlow 的运行时。 您可以购买 Jetson 并在安装 TensorFlow 之前快速安装 Ubuntu,CUDA,CUDNN。 克隆 [https://github.com/jetsonhacks/installTensorFlowTX2](https://github.com/jetsonhacks/installTensorFlowTX2) ,然后在命令提示符下输入以下命令。
1. 首先,在以下代码的帮助下安装必备组件:
```py
./installPrerequisites.sh
```
2. 现在,使用以下代码克隆 TensorFlow:
```py
./cloneTensorFlow.sh
```
3. 接下来,使用 以下代码设置所需的环境变量:
```py
./setTensorFlowEV.sh
```
4. 现在我们将使用 以下代码构建 TensorFlow :
```py
./buildTensorFlow.sh
```
5. 现在,我们将使用以下代码 使用 将打包文件处理为 Wheel 文件:
```py
./packageTensorFlow.sh
```
6. 现在,我们将使用以下代码安装 Tensorflow:
```py
pip install $HOME/tensorflow-1.0.1-cp27-cp27mu-linux_aarch64.whl
```
借助这些步骤,我们可以在 Jetson TX2 中安装 TensorFlow。
# 安卓系统
任何 Android 应用程序都可以使用 TensorFlow,其构建细节可以在 [https://www.tensorflow.org/mobile/android_build](https://www.tensorflow.org/mobile/android_build) 中找到。 关于此的官方示例可以在 [https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/android](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/android) 中找到。 假设读者具有 Android 编程经验,则在 Android 设备中实现 Tensorflow 的步骤如下:
1. 使用[第 3 章](../Text/03.html)*图像检索*中介绍的步骤,将 TensorFlow 模型导出到`.pb`文件。
2. 生成二进制文件`.so``.jar`
3. 编辑`gradle`文件以启用库加载。
4. 加载并运行 Android 应用文件
# 苹果手机
苹果使用 CoreML 框架将机器学习集成到 iPhone 应用程序中。 Apple 提供了可以直接集成到应用程序中的标准模型列表。 您可以使用 TensorFlow 训练自定义深度学习模型并将其在 iPhone 中使用。 为了部署自定义模型,您必须在 CoreML 框架模型中隐藏 TensorFlow。 谷歌发布了 [https://github.com/tf-coreml/tf-coreml](https://github.com/tf-coreml/tf-coreml) ,用于将 TensorFlow 模型转换为 CoreML 模型。 可以使用以下代码安装 TFcoreML:
```py
pip install -U tfcoreml
```
可以使用以下代码导出模型:
```py
import tfcoreml as tf_converter
tf_converter.convert(tf_model_path='tf_model_path.pb',
mlmodel_path='mlmodel_path.mlmodel',
output_feature_names=['softmax:0'],
input_name_shape_dict={'input:0': [1, 227, 227, 3]})
```
iPhone 可以使用导出的模型进行预测。
# 摘要
在本章中,我们了解了如何在各种平台和设备上部署经过训练的深度学习模型。 我们已经介绍了为这些平台获得最佳性能的步骤和准则。 我们已经看到了 MobileNets 的优势,它以很小的精度权衡来减少推理时间。
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册