TensorFlow2-神经网络应用指南-全-

TensorFlow2 神经网络应用指南(全)

原文:Applied Neural Networks with TensorFlow 2

协议:CC BY-NC-SA 4.0

一、简介

在本书中,我们深入到深度学习(DL)的领域,并涵盖了几个深度学习概念以及几个案例研究。这些案例研究的范围从图像识别到推荐系统,从艺术生成到对象聚类。深度学习是更广泛的机器学习(ML)方法家族的一部分,基于具有表示学习的人工神经网络(ann)。这些神经网络模仿人类的脑细胞,或神经元,进行算法学习,它们的学习速度比人类的学习速度快得多。几种深度学习方法为不同类型的机器学习问题提供了解决方案:(I)监督学习,(ii)非监督学习,(iii)半监督学习,以及(iv)强化学习。

这本书的结构也包括机器学习学科的介绍,这样读者可以熟悉机器学习的一般规则和概念。然后,提供深度学习的详细介绍,让读者熟悉深度学习的子学科。

在涵盖了深度学习的基础知识之后,这本书涵盖了不同类型的人工神经网络及其潜在的现实生活应用(即案例研究)。因此,在每一章,这本书(I)介绍了一个特定的神经网络结构的概念,并详细介绍了它的组成部分,然后(ii)提供了一个如何应用这种网络结构来解决一个特定的人工智能(AI)问题的教程。

由于本书的目标是为深度学习应用程序提供案例研究,因此寻求几种技术和库的能力以获得满意的学习体验。

在深入机器学习和深度学习之前,我们先介绍一下本书中使用的技术。这个介绍包括最新的发展和为什么选择这些技术的理由。最后,本章还介绍了如何安装这些技术,并以最少的麻烦为您的环境做准备。本书的核心技术如下:

  • 我们选择的编程语言: Python 3.x

  • 我们选择的深度学习框架: TensorFlow 2.x

  • 我们的开发环境: Google Colab ( 用 Jupyter 笔记本替代)

Note

显示如何使用 TensorFlow 的 TensorFlow 流水线指南可在第五章中找到,而与 TensorFlow 一起使用的相关库在第四章中介绍。

请注意,本书假设您使用 Google Colab,这几乎不需要环境设置。如果您喜欢本地环境,本章还包括本地 Jupyter 笔记本安装指南。如果你决定使用 Google Colab,你可以跳过 Jupyter 笔记本安装部分。

Note

当学习一门新的编程学科或技术时,最令人沮丧的任务之一是环境设置过程。因此,尽可能简化这一过程非常重要。因此,本章的设计考虑到了这一原则。

Python 作为编程语言

Python 是一种编程语言,由吉多·范·罗苏姆作为附带项目创建,最初发布于 1991 年。Python 支持面向对象编程(OOP),这是一种基于对象概念的范式,可以包含字段形式的数据。Python 优先考虑程序员的体验。因此,程序员可以为小型和大型项目编写清晰的逻辑代码。它还包含对函数式编程的支持。Python 是动态类型化的,是垃圾回收的。

Python 也被认为是一种解释语言,因为它通过一个解释器,将你写的代码转换成你的计算机处理器可以理解的语言。解释器“一条一条”地执行代码语句。另一方面,在编译语言中,编译器完全执行代码并一次列出所有可能的错误。就速度和性能而言,编译的代码比解释的代码更有效。然而,脚本语言(如 Python)只显示一条错误消息,即使您的代码有多个错误。这个特性可以帮助程序员快速清除错误,提高开发速度。

Python 的时间轴

我们来看看 Python 的时间轴:

  • 在 20 世纪 80 年代末,Python 被认为是 ABC 语言的继承者。

  • 1989 年 12 月,吉多·范·罗苏姆开始实施 Python。

  • 1994 年 1 月,Python 版发布。包括的主要新特性是函数式编程工具 lambda、map、filter 和 reduce。

  • 2000 年 10 月,Python 2.0 发布了主要的新特性,包括循环检测垃圾收集器和对 Unicode 的支持。

  • Python 3.0 于 2008 年 12 月 3 日发布。这是该语言的一个主要修订版,只是部分向后兼容。它的许多主要特性被移植到 Python 2.6.x 和 2.7.x 版本系列。Python 3 的发行版包括 2 到 3 实用程序,它自动(至少部分地)将 Python 2 代码翻译成 Python 3。

  • 截至 2020 年 1 月 1 日,Python 2 没有新的 bug 报告、修复或更改,不再支持Python 2

Python 2 对 Python 3

一个新的深度学习程序员可能会有一个常见的问题,就是使用 Python 2.x 还是 Python 3.x,因为有许多过时的博客帖子和网络文章比较两个主要版本。截至 2020 年,可以肯定地说,这些比较是不相关的。正如您在前面的时间表中看到的,Python 2.x 的延迟弃用最终发生在 2020 年 1 月 1 日。因此,程序员可能再也找不到对 Python 2.x 版本的官方支持了。

程序员的一项基本技能是掌握最新技术,因此,本书仅使用 Python 3.x 版本。对于只熟悉 Python 2.x 版本的读者来说,这种偏好应该不会造成问题,因为本书中针对 Python 2.x 和 Python 3.x 使用的语法之间的差异并不明显。因此,Python 2.x 程序员可能会立即熟悉本书中的源代码。

为什么是 Python?

与其他编程语言相比,Python 在数据科学家和机器学习工程师中受欢迎有几个原因。2019 年 Kaggle 机器学习和数据科学调查显示,Python 是迄今为止最受数据科学和机器学习欢迎的编程语言;见图 1-1 。

img/501289_1_En_1_Fig1_HTML.jpg

图 1-1

2019 年 Kaggle 机器学习和数据科学调查

与其他语言相比,Python 受欢迎有几个原因。下面是 Python 的一个非详尽的优点列表。

易于学习

新手选择 Python 作为主要编程语言的主要原因之一是它的易学性。与其他编程语言相比,Python 提供了更短的学习曲线,因此程序员可以在短时间内达到良好的能力水平。与其他流行的编程语言相比,Python 的语法更容易学习,代码可读性更好。显示这一点的一个常见例子是不同编程语言打印“Hello,World!”所需的代码量。例如,能够打印出 Hello,World!在 Java 中,您需要以下代码:

你好,世界!在 Java 中

public class Main {
   public static void main(String[] args) {
       System.out.println("Hello, World!");
   }
}

使用 Python 中的一行代码也可以获得相同的结果:

Hello, World! in Python
print("Hello, World!")

各种可用的数据科学库

与其他编程语言相比,Python 的另一个强大特性是它的各种各样的数据科学库。Pandas、NumPy、SciPy 和 scikit-learn 等数据科学库通过其标准化的函数和逻辑与数学运算模块,减少了为模型训练准备数据的时间。此外,由于 Python 开发人员的活跃社区,一旦开发人员检测到一个常见问题,就会立即设计并发布一个新的库来解决这个问题。

社区支持

强大的社区支持是 Python 相对于其他编程语言的另一个优势。越来越多的志愿者正在发布 Python 库,这种实践使 Python 成为具有现代和强大库的语言。此外,大量经验丰富的 Python 程序员随时准备帮助其他程序员解决在线社区渠道上的问题,如堆栈溢出。

可视化选项

数据可视化是从原始数据中提取洞察力的重要学科,Python 提供了几个有用的可视化选项。优秀的 Matplotlib 总是有最可定制的选项。此外,Seaborn 和 Pandas Plot API 是强大的库,可以简化数据科学家使用的最常见的可视化任务。此外,Plotly 和 Dash 等库允许用户创建交互式绘图和复杂的仪表板,并在 Web 上提供服务。有了这些库,数据科学家可以轻松地创建图表,绘制图形,并促进特征提取。

既然我们讨论了为什么数据科学家最喜欢的语言是 Python,我们可以继续讨论为什么我们使用 TensorFlow 作为我们的机器学习框架。

TensorFlow 作为深度学习框架

TensorFlow 是一个开源的机器学习平台,特别关注神经网络,由谷歌大脑团队开发。尽管最初用于内部目的,但谷歌在 2015 年 11 月发布了 Apache License 2.0 下的库,这使其成为一个开源库。 1 虽然 TensorFlow 的用例并不局限于机器学习应用,但是机器学习才是我们看到 TensorFlow 实力的领域。

具有稳定和官方 TensorFlow APIs 的两种编程语言是 Python 和 C。此外,C++、Java、JavaScript、Go 和 Swift 是开发人员可能会发现有限到广泛 TensorFlow 兼容性的其他编程语言。最后,还有 C#、Haskell、Julia、MATLAB、R、Scala、Rust、OCaml、Crystal 的第三方 TensorFlow APIs。

TensorFlow 时间线

虽然这本书重点介绍了使用 Python API 的 TensorFlow 2.x,但是 Google 也发布了几个补充的 TensorFlow 库。了解 TensorFlow 平台的发展对于了解全貌至关重要。作为 TensorFlow 项目的一部分,Google 实现的里程碑的时间表可以总结如下:

  • 2011 年,Google Brain 使用深度学习神经网络构建了一个名为 DistBelief 的机器学习系统。

  • 2015 年 11 月,谷歌在 Apache License 2.0 下发布了 TensorFlow 库,并将其开源,以加速人工智能的进步。

  • 2016 年 5 月,谷歌公布了一款为机器学习打造、为 TensorFlow 量身定制的专用集成电路(ASIC),名为张量处理单元(TPU)

  • 2017 年 2 月,谷歌发布了 TensorFlow 1.0.0

  • 2017 年 5 月,谷歌宣布 TensorFlow Lite 一个用于移动设备中机器学习开发的库。

  • 2017 年 12 月,谷歌推出了 Kubeflow ,允许在 Kubernetes 上运行和部署 TensorFlow。

  • 2018 年 3 月,谷歌公布了用 JavaScript 进行机器学习的tensor flow . js1.0 版本。

  • 2018 年 7 月,谷歌公布了 Edge TPU 。Edge TPU 是谷歌专门打造的 ASIC 芯片,旨在智能手机上运行 TensorFlow Lite 机器学习(ML)模型。

  • 2019 年 1 月,谷歌宣布 TensorFlow 2.0 将于 2019 年 9 月正式上市。

  • 2019 年 5 月,谷歌公布了用于计算机图形学深度学习的 TensorFlow Graphics

  • 2019 年 9 月,TensorFlow 团队发布了 TensorFlow 2.0 ,库的新主要版本。

这个时间线表明 TensorFlow 平台正在走向成熟。特别是随着 TensorFlow 2.0 的发布,Google 显著提高了 TensorFlow APIs 的用户友好性。此外,TensorFlow 团队宣布,他们不打算引入任何其他重大变化。因此,可以有把握地假设,本书中包含的方法和语法将长期保持其相关性。

为什么选择 TensorFlow?

由科技巨头、科技基金会和学术机构开发的二十多个深度学习库对公众开放。虽然每个框架在深度学习的特定子学科中都有其优势,但这本书主要关注带有 Keras API 的 TensorFlow。选择 TensorFlow 而不是其他深度学习框架的主要原因是它的受欢迎程度。另一方面,这种说法并不意味着其他框架比 TensorFlow 更好,也没有 tensor flow 受欢迎。特别是随着 2.0 版本的推出,TensorFlow 通过解决深度学习社区提出的问题加强了自己的力量。今天,TensorFlow 可能被视为最受欢迎的深度学习框架,它非常强大且易于使用,并具有出色的社区支持。

TensorFlow 2.x 中的新增功能

自 2015 年推出以来,TensorFlow 已经发展成为市场上最先进的机器学习平台之一。研究人员、开发人员和公司广泛采用了 TensorFlow 团队引入的技术。2019 年 9 月,TensorFlow 2.0 在 4 岁生日前后发布。TensorFlow 团队通过清理废弃的 API 和减少重复来简化 API。TensorFlow 团队引入了几项更新,以实现 TensorFlow 2.0 的简单易用。这些更新可能如下所列:

  1. 使用 Keras 和热切的执行轻松构建模型

  2. 在任何平台上的生产级别中实现强大的模型部署

  3. 强大的研究实验

  4. 由于清理和减少重复,简化了 API

使用 Keras 和热切的执行轻松构建模型

TensorFlow 团队进一步简化了模型构建体验,通过新的或改进的模块(如tf.datatf.kerastf.estimatorsDistribution Strategy API)来响应预期。

使用 tf.data 加载数据

在 TensorFlow 2.0 中,使用通过tf.data模块创建的输入流水线读取训练数据。tf.feature_column模块用于定义特征特性。对新来者有用的是新的数据集模块。TensorFlow 2.0 提供了一个单独的数据集模块,该模块提供了一系列流行的数据集,并允许开发人员试验这些数据集。

使用 tf.keras 构建、训练和验证您的模型,或者使用预制的估计器

在 TensorFlow 1.x 中,开发人员可以使用之前版本的tf.contribtf.layerstf.kerastf.estimators来构建模型。为同一个问题提供四种不同的选择让新来者感到困惑,并赶走了其中一些人,尤其是 PyTorch。TensorFlow 2.0 通过将选项限制为两个改进的模块来简化模型构建:tf.keras (TensorFlow Keras API)和tf.estimators (Estimator API)。TensorFlow Keras API 提供了一个高级接口,使模型构建变得简单,这对于概念验证(POC) 尤其有用。另一方面,Estimator API 更适合需要扩展服务和增加定制能力的生产级模型。

以急切的执行方式运行和调试,然后使用 AutoGraph API 获得图形的好处

TensorFlow 1.x 版本对 TensorFlow 图进行了优先排序,这对新来者并不友好。尽管 TensorFlow 2.0 保留了这种复杂的方法,但急切执行对比概念被默认。谷歌用以下声明解释了这一变化的最初原因:

急切执行是一个命令性的、由运行定义的接口,当从 Python 中调用操作时,立即执行操作。这样更容易上手 TensorFlow,可以让研发更直观。 2

热切的执行使模型建立更容易。它提供了快速调试功能,可以立即处理运行时错误,并与 Python 工具集成,这使得 TensorFlow 对初学者更加友好。另一方面,图执行对于分布式培训、性能优化和生产部署具有优势。为了填补这一空白,TensorFlow 引入了名为 via tf.function decorator 的 AutoGraph API。这本书将急切执行优先于图形执行,为读者提供了一个陡峭的学习曲线。

使用分布式策略进行分布式培训

使用大型数据集的模型训练需要使用多个处理器(如 CPU、GPU 或 TPU)进行分布式训练。尽管 TensorFlow 1.x 支持分布式训练,但分布式策略 API 优化并简化了跨多个 GPU、多台机器或 TPU 的分布式训练。TensorFlow 还提供了在本地或云环境中的 Kubernetes 集群上部署培训的模板,这使得培训更具成本效益。

导出到保存模型

训练模型后,开发人员可以导出到 SavedModel。API 可用于构建一个完整的带权重和计算的 TensorFlow 程序。这种标准化的 SavedModel 可以在不同的 TensorFlow 部署库中互换使用,例如(i) TensorFlow Serving,(ii) TensorFlow Lite,(iii) TensorFlow.js,以及(iv) TensorFlow Hub。

在任何平台上的生产中实现强大的模型部署

TensorFlow 一直致力于在不同设备上提供直接的生产路径。已经有几个库可以用来在专用环境中为训练好的模型提供服务。

TensorFlow 服务

TensorFlow Serving 是一个灵活的高性能 TensorFlow 库,允许通过 HTTP/REST 或 gRPC/协议缓冲区为模型提供服务。这个平台是平台和语言中立的,因为您可以使用任何编程语言进行 HTTP 调用。

TensorFlow Lite

TensorFlow Lite 是一个轻量级深度学习框架,用于将模型部署到移动设备(iOS 和 Android)或嵌入式设备(Raspberry Pi 或 Edge TPUs)。开发人员可能会选择一个经过训练的模型,将该模型转换为压缩的 fat 缓冲区,并使用 TensorFlow Lite 部署到移动或嵌入式设备上。

TensorFlow.js

TensorFlow.js 使开发人员能够将其模型部署到 web 浏览器或 Node.js 环境中。开发人员还可以使用类似 Keras 的 API 在浏览器中用 JavaScript 构建和训练模型。

在 TensorFlow 2.0 中,通过标准化的交换格式和一致的 API,跨平台和组件的能力和奇偶校验得到了极大的提高。TensorFlow 团队在图 1-2 中展示了 TensorFlow 2.0 新的简化架构。

img/501289_1_En_1_Fig2_HTML.jpg

图 1-2

TensorFlow 2.0 架构的简化图 3

改善研究人员的实验体验

研究人员通常需要一个易于使用的工具来将他们的研究想法从概念转化为代码。一个概念的证明可能只有在几次迭代之后才能实现,而这个概念可能在几次实验之后才能发表。TensorFlow 2.0 旨在让这一过程更容易实现。Keras Functional API 与模型子类 API 配合使用,提供了构建复杂模型的足够能力。tf.GradientTapetf.custom_gradient是生成自定义训练逻辑的关键。

任何机器学习项目都始于概念证明(POC)。开发人员需要采用敏捷的方法,并使用易于使用的工具来将新想法从概念转化为有证据支持的出版物。最后,TensorFlow 2.0 提供了强大的扩展,如参差张量、TensorFlow Probability 和 Tensor2Tensor,以确保灵活性和增强的实验能力。

TensorFlow 竞争对手

尽管这本书使用 TensorFlow 作为主要的深度学习框架,但对竞争的深度学习框架和库进行简要介绍是必不可少的。虽然深度学习框架的总数超过 20 个,但其中许多框架目前都不是由其设计者维护的。因此,我们只能谈一谈极少数积极可靠的深度学习框架,涵盖如下。

Keras 是一个用 Python 编写的开源神经网络库,可以运行在 TensorFlow、微软认知工具包(CNTK)、Theano、R 和 PlaidML 之上。谷歌工程师弗朗索瓦·乔莱(Franç ois Chollet)设计了 Keras,以实现神经网络的快速实验。它非常用户友好,模块化,可扩展。Keras 还以简单、灵活和强大而自豪。由于这些特性,Keras 被新人视为首选的深度学习库。

Keras 应该被视为 TensorFlow 的补充选项,而不是竞争对手的库,因为它依赖于现有的深度学习框架。2017 年,谷歌的 TensorFlow 团队同意在其核心库中支持 Keras。有了 TensorFlow 2.0,Keras API 变得更加精简和集成。这本书利用了 TensorFlow Keras API,这使得创建神经网络变得容易得多。

Keras 官网: www.keras.io

PyTorch

PyTorch 是一个开源神经网络库,主要由脸书的人工智能研究实验室(FAIR)开发和维护,最初于 2016 年 10 月发布。FAIR 在 Torch library 的基础上构建了 PyTorch,这是另一个开源机器学习库,一个科学计算框架,以及一个基于 Lua 编程语言的脚本语言,最初由 Ronan Collobert,Samy Bengio 和 Johnny Mariéthoz 设计。

由于 PyTorch 是由脸书开发的,并提供了一个易于使用的界面,它的流行程度在最近几年有所增加,特别是在学术界。PyTorch 是 TensorFlow 的主要竞争对手。在 TensorFlow 2.0 之前,尽管其 API 的易用性存在问题,但由于其社区支持、生产性能和额外的用例解决方案,TensorFlow 一直保持着流行。此外,TensorFlow 2.0 的最新改进引入了对 TensorFlow 1.x 缺点的补救措施。因此,尽管 PyTorch 越来越受欢迎,TensorFlow 很可能会保持其地位。

PyTorch 官网: www.pytorch.org

Apache MXNet

img/501289_1_En_1_Figd_HTML.gif MXNet 是 Apache 基金会推出的开源深度学习框架。这是一个灵活、可扩展、快速的深度学习框架。它支持多种编程语言(包括 C++、Python、Java、Julia、MATLAB、JavaScript、Go、R、Scala、Perl 和 Wolfram 语言)。

MXNet 由亚马逊、英特尔、百度、微软、Wolfram Research、卡内基梅隆大学、麻省理工学院和华盛顿大学使用和支持。虽然一些受尊敬的机构和技术公司支持 MXNet,但 MXNet 的社区支持是有限的。因此,与 TensorFlow、Keras 和 PyTorch 相比,它仍然不太受欢迎。

MXNet 官网:mxnet.apache.org

微软认知工具包

img/501289_1_En_1_Fige_HTML.jpg微软于 2016 年 1 月发布了作为其开源深度学习框架的 CNTK。CNTK 也称为微软认知工具包,支持 Python、C++、C#和 Java 等流行编程语言。微软在其流行的应用程序和产品(如 Skype、Xbox 和 Cortana)中使用了 CNTK,特别是在语音、手写和图像识别方面。然而,截至 2019 年 1 月,微软停止发布微软认知工具包的新更新。因此,CNTK 被认为是不推荐使用的。

微软认知工具包官网: www.cntk.ai

最终评估

上述深度学习框架的设计者和维护者明显显示了深度学习框架开发的转变。深度学习最初是大学里的一个学术研究领域,很少甚至没有现实生活中的应用。然而,随着计算能力的提高、处理成本的降低以及互联网的兴起,这种情况已经发生了变化。越来越多的深度学习应用的现实生活用例一直在满足大型科技公司的胃口。Torch、Caffe 和 Theano 等早期的学术项目为 TensorFlow、Keras 和 PyTorch 等深度学习库的开发铺平了道路。谷歌、亚马逊和脸书等行业参与者已经为他们自己的开源深度学习框架雇佣了这些早期项目的维护者。因此,对早期项目的支持非常有限,而新一代框架变得越来越强大。

截至 2020 年,可以肯定地说,TensorFlow 和 PyTorch 之间正在进行真正的竞争。由于其成熟性、对多种编程语言的广泛支持、在就业市场中的受欢迎程度、广泛的社区支持和支持技术,TensorFlow 占据了上风。2018 年,Jeff Hale 为市场上的深度学习框架开发了一个权力排名。他权衡了在网上工作列表、相关文章和博客帖子以及 GitHub 上发现的提及。他的结果也支持前面的评估;参见图 1-3 。

img/501289_1_En_1_Fig3_HTML.jpg

图 1-3

深度学习框架 Power Scores 2018 作者杰夫·黑尔 4

因此,由于其技术进步和在技术社区中的受欢迎程度,TensorFlow 是本书中使用的单一深度学习框架。在下一节中,我们将了解 TensorFlow 2.0 引入的新功能。

最终考虑

提供 PyTorch 等易用模块的竞争对手库越来越受欢迎,这表明 TensorFlow 1.x 没有走上正轨。Keras library 的兴起(其唯一目的是促进 TensorFlow 以及其他一些的使用)是另一个迹象,表明 TensorFlow 必须简化其工作流程以保持其现有的用户群。TensorFlow 2.0 的引入是为了缓解这个问题,看起来大多数批评都是通过新引入的 API 和改进的现有 API 来解决的。

安装和环境设置

既然我们解决了为什么 TensorFlow 是本书选择的深度学习框架,为什么 Python 是选择的编程语言的问题,那么是时候为深度学习建立一个编程环境了。

机器学习任务需要持续的测试和概念验证工作。传统的 Python 运行环境可能会阻碍测试的速度。因此,开发人员通常求助于交互式运行环境来进行数据清理、模型构建和训练。使用交互式环境有几个优点:

  • 在交互式运行环境中,开发人员可以运行部分代码,而输出仍然保存在内存中。

  • 代码的下一部分可能仍然使用代码的前一部分的输出。

  • 代码的一部分中出现的错误可能会被修复,而代码的其余部分可能仍会运行。

  • 一个大的代码文件可能被分成几个部分,这使得调试变得非常简单。

我们几乎可以说,使用交互式编程环境已经成为深度学习研究的行业标准。因此,对于贯穿本书的深度学习项目,我们也将遵循这种做法。

对于 Python TensorFlow 程序员来说,在交互式编程环境中构建和训练模型有几种可行的选择。然而,我们将深入探讨为用户提供不同好处的最受欢迎的选项:(i) Jupyter Notebook 和(ii) Google Colab

交互式编程环境:IPython、Jupyter Notebook 和 Google Colab

Python 交互式编程环境中使用了多种工具。使交互成为可能的核心技术是 IPython。IPython 是 Python 的一个改进的 shell 和 read–eval–print 循环(REPL)。“IPython Notebook”是使用 IPython 开发的产品,可通过网络浏览器作为“笔记本”访问。IPython 处理两个基本角色:

  • 作为 REPL 的终端 IPython

  • IPython 内核,提供计算和与前端接口(如 IPython Notebook)的通信

开发人员可以编写代码,做笔记,并将媒体上传到他们的 IPython 笔记本上。IPython Notebook 项目的发展导致了 Project Jupyter 的创建,它包含了 Notebook 工具和其他用于多种语言(Julia、Python 和 R)的交互式工具。Jupyter Notebook 及其灵活的界面将笔记本从代码扩展到可视化、多媒体、协作和许多其他功能,为数据科学家和机器学习专家创造了一个舒适的环境。

如果你想让你的开发体验更上一层楼,谷歌云,这是一个基于云的 Jupyter 笔记本环境,是一个终极工具。此外,Google Colab 还提供协作选项、使用 Google 的计算能力和基于云的托管功能。IPython、Jupyter Notebook、Google Colab 之间的关系如图 1-4 所示。

img/501289_1_En_1_Fig4_HTML.png

图 1-4

IPython、Jupyter Notebook 和 Google Colab 之间的关系

在接下来的部分,我们将深入 IPython、Jupyter Notebook 和 Google Colab 的细节。我们还将(I)安装带有 Anaconda 发行版的 Jupyter 笔记本,以及(ii)安装 Google Colab。

伊普提洪伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁

IPython 是一个命令外壳和一个内核,它支持交互式 Python 笔记本。IPython 允许程序员在笔记本环境中快速运行他们的代码。IPython 提供了几个特性:

  • 交互式外壳(终端和 Qt 控制台)

  • 基于 web 的笔记本界面,支持代码、文本和媒体

  • 支持交互式数据可视化和 GUI 工具包

  • 灵活和可嵌入的解释器加载到项目中

  • 并行计算工具包

IPython 项目已经超越了运行 Python 脚本的范畴,正在成为一个语言无关的工具。从 IPython 4.0 开始,与语言无关的部分被收集在一个新项目下,名为 Project Jupyter。Jupyter 这个名字是对 Jupyter 支持的核心编程语言的引用,这些语言是 Julia、Python 和 r。截至这个分拆决定的实施,IPython 现在只专注于交互式 Python,Jupyter 专注于笔记本格式、消息协议、QT 控制台和笔记本 web 应用程序等工具。

Jupyter 笔记型电脑

img/501289_1_En_1_Figf_HTML.jpg Project Jupyter 是 2014 年脱胎于 IPython 项目的一个分拆开源项目。Jupyter 永远免费供所有人使用,它是通过 Jupyter 社区的共识开发的。作为 Jupyter 项目的一部分,发布了几个有用的工具,如 Jupyter Notebook、JupyterLab、Jupyter Hub 和 Voilà。虽然所有这些工具可以同时用于附带目的,但安装 Jupyter Notebook 足以满足本书的环境要求。

另一方面,作为一个开源项目,Jupyter 工具可以集成到不同的工具集和捆绑包中。我们将使用 Anaconda 发行版在本地机器上安装环境,而不是通过终端(对于 macOS 和 Linux)或命令提示符(对于 Windows)安装 Jupyter Notebook。

蟒蛇分布

Anaconda 是用于科学计算的 Python 和 R 编程语言的免费开源发行版,旨在简化包的管理和部署

环境设置是编程的繁琐任务之一。开发人员经常遇到独特的问题,主要是由于他们的操作系统及其版本。使用 Anaconda 发行版,可以轻松安装 Jupyter Notebook 和其他有用的数据科学库。

在 Windows 上安装

  1. 选择 Python 3.x 的 64 位图形安装程序,在 www.anaconda.com/products/individual 下载 Anaconda 安装程序;见图 1-5 。

img/501289_1_En_1_Fig5_HTML.jpg

图 1-5

Anaconda 安装程式页面

img/501289_1_En_1_Fig6_HTML.jpg

图 1-6

Windows 操作系统的 Anaconda 安装窗口

  1. 双击安装程序启动。

  2. 单击“下一步”按钮。

  3. 阅读许可协议,然后单击“我同意”

  4. 为“仅我”选择一个安装,然后单击“下一步”按钮。

  5. 选择安装 Anaconda 的目标文件夹,然后单击“下一步”按钮(确保您的目标路径不包含空格或 Unicode 字符)。

  6. 确保(I)“将 Anaconda3 添加到您的路径环境变量”选项未选中,并且(ii)“将 Anaconda3 注册为我的默认 Python 3.x”选项已选中,如图 1-6 所示。

  7. 单击“安装”按钮,等待安装完成。

  8. 单击“下一步”按钮。

  9. 单击“下一步”按钮跳过安装 PyCharm IDE。

  10. 成功安装后,您将看到“感谢您安装 Anaconda 个人版”消息。单击“完成”按钮。

  11. 你现在可以在开始菜单中找到“巨蟒导航”应用程序来打开 Jupyter 笔记本。只需打开应用程序,点击 Jupyter 笔记本卡中的“启动”按钮。这一步将在本地主机上提示一个 web 浏览器:8888。

在 Mac 上安装

  1. 选择 Python 3.x 的 64 位图形安装程序,在 www.anaconda.com/products/individual 下载 Anaconda 安装程序;见图 1-7 。

img/501289_1_En_1_Fig7_HTML.jpg

图 1-7

Anaconda 安装程式页面

  1. 双击下载的文件,然后单击“继续”按钮开始安装。

  2. 单击简介、自述文件和许可证屏幕上的“继续”按钮。

  3. 单击提示窗口上的“同意”按钮,同意软件许可协议的条款。

  4. 确保在目的地选择屏幕中选择了“仅为我安装”选项,然后单击“继续”按钮。

  5. 单击 Install 按钮安装 Anaconda,并等待安装完成。

  6. 单击“继续”按钮跳过 PyCharm IDE 的安装。

img/501289_1_En_1_Fig9_HTML.jpg

图 1-9

创建新的 Jupyter 笔记本

  1. Click the “Close” button, as shown in Figure 1-8, to close the installer.

    img/501289_1_En_1_Fig8_HTML.jpg

    图 1-8

    macOS 的 Anaconda 安装窗口

  2. 你现在可以打开 Jupyter 笔记本,在你的 Launchpad 下找到“Anaconda-Navigator”应用程序。只需打开应用程序,点击 Jupyter 笔记本卡中的“启动”按钮。这一步将提示本地主机上的终端和 web 浏览器:8888。

  3. 点击新建➤ Python3 可以新建一个 IPython 笔记本,如图 1-9 所示。

Jupyter Notebook 附带了重要的数据科学库,如 Pandas、NumPy 和 Matplotlib。但是,如果你决定使用 Jupyter 笔记本进行深度学习,还是要安装 TensorFlow。TensorFlow 的安装可以用 Python 的“pip”包管理器来实现,因为我们已经用 Anaconda 发行版安装了 Python。您可以按照以下方法之一将 TensorFlow 安装到您的本地计算机上。

|

操作系统

|

安装 TensorFlow 的替代方法

|
| --- | --- |
| 苹果 | 对于 Mac,只需从 Launchpad 的其他文件夹下打开一个终端窗口,然后粘贴以下脚本:pip install --upgrade tensorflow |
| Windows 操作系统 | 对于 Windows,转到 Windows 机器上的“开始”菜单,搜索“cmd”,右键单击它并选择“以管理员身份运行”,然后粘贴前面提到的相同脚本:pip install --upgrade tensorflow |
| macOS/Windows | 对于 macOS 和 Windows,创建一个新的 IPython 笔记本,如前面所示。将以下代码复制并粘贴到一个空单元格中,然后单击页面顶部的“运行”按钮:!pip install --upgrade tensorflow注意小心感叹号!img/501289_1_En_1_Figh_HTML.jpg |

另一方面,如果你想使用谷歌 Colab,你不必安装 TensorFlow,因为谷歌 Colab 笔记本电脑预装了 TensorFlow。

Google Colab

Colaboratory,简称 Colab,是谷歌的产品,它允许开发者通过浏览器编写和执行 Python 代码。Google Colab 是深度学习任务的优秀工具。Google Colab 是一个托管的 Jupyter 笔记本,不需要设置,有一个优秀的免费版本,可以免费访问 GPU 等 Google 计算资源。

与 Anaconda 发行版一样,Google Colab 附带了重要的数据科学库,如 Pandas、NumPy、Matplotlib,以及更重要的 TensorFlow。Colab 还允许与其他开发者共享笔记本,并将你的文件保存到 Google Drive。您可以从任何地方访问和运行 Colab 笔记本中的代码。

综上所述,Colab 只是 Jupyter 笔记本的一个专门版本,运行在云上,提供免费的计算资源。

Caution

作为读者,您可以选择使用本地设备并安装前面显示的 Anaconda 发行版。使用 Jupyter Notebook,只要熟悉 Jupyter Notebook 就不会出现任何问题。另一方面,为了能够使这本书和代码保持最新,我将故意使用 Google Colab,以便我可以重新访问代码并进行更新。因此,您将始终可以访问最新版本的代码。所以,这本书我推荐你用 Google Colab。

Google Colab 设置

谷歌设置过程相对简单,可以通过以下步骤在所有设备上完成:

  1. 访问 colab.research.google.com,这将引导你到谷歌联合实验室的欢迎页面;参见图 1-10 。

  2. Click the “Sign in” button on the right top.

    img/501289_1_En_1_Fig10_HTML.jpg

    图 1-10

    谷歌 Colab 欢迎笔记本截图

  3. Sign in with your Gmail account. Create one if you don’t have a Gmail account; see Figure 1-11.

    img/501289_1_En_1_Fig11_HTML.jpg

    图 1-11

    Google 登录页面

  4. 一旦完成登录过程,您就可以使用 Google Colab 了。

  5. You may easily create a new Colab Notebook on this page by clicking File ➤ New notebook. You can see an example Colab notebook in Figure 1-12.

    img/501289_1_En_1_Fig12_HTML.jpg

    图 1-12

    空的 Google Colab 笔记本的截图

硬件选项和要求

深度学习是计算非常密集的,大型深度学习项目需要多台机器同时进行分布式计算。CPU、GPU 和 TPU 等处理单元、RAM、HDD 和 SSD 等硬盘驱动器以及电源单元是影响计算机整体训练性能的重要硬件单元。

对于使用大量数据集进行训练的项目来说,拥有强大的计算能力和合适的硬件是极其重要的。使用大型数据集进行模型定型的最关键组件是处理单元。当任务太大时,开发人员通常使用 GPU 和 TPU,而 CPU 对于中小型的训练任务可能就足够了。

这本书不包含计算量大的项目,因为这样的项目可能会使读者气馁和失去动力。因此,普通计算机足以满足本书的计算能力要求。此外,如果你按照建议使用 Google Colab 的教程,Google Colab 中提供的资源——也包括 GPUs 对于本书中的项目来说绰绰有余。因此,您完全不必担心您的硬件。

二、机器学习导论

本章旨在对机器学习领域进行介绍,并阐明类似领域的范围,尤其是深度学习。它还旨在比较不同的机器学习方法,介绍一些流行的机器学习模型,提及重要的机器学习概念,并引导您完成机器学习的步骤。这一章非常重要,因为深度学习是机器学习的一个子部分,因此,大多数解释也适用于深度学习。

什么是机器学习?

众所周知,计算机不具备认知能力,无法自主推理。然而,它们在处理数据方面非常完美,它们可以在少量时间内完成高难度的计算任务。只要我们向他们提供详细的、一步一步的逻辑和数学指导,他们就能处理任何事情。因此,如果我们可以用逻辑运算来代表人类的认知能力,计算机就可以发展认知技能。

意识是人工智能热议的话题之一:计算机能变得有意识吗?虽然这次讨论的范围是机器是否可以完全模仿人类意识(通用 AI ),但在本书中,我们专注于模仿特定任务的特定人类技能(狭义 AI )。这就是机器学习的用武之地。

“机器学习”一词最早是由 IBM 科学家、计算机游戏和人工智能领域的先驱亚瑟·塞缪尔(Arthur Samuel)在 1959 年提出的。在 20 世纪 50 年代、60 年代和 70 年代,神经网络的早期工作是以模仿人脑为目标的。然而,由于计算机技术的限制,神经网络的实际应用在很长一段时间内是不可行的。对其他 ML 技术(即,需要较少计算机资源的非深度学习技术)的基础机器学习研究在 20 世纪 80 年代和 90 年代普及。这一时期计算机技术的进步部分地允许在现实生活中采用机器学习应用。随着时间的推移,由于不成熟的计算机技术造成的限制大多被消除了,尤其是在最近几年。虽然我们总是在争取更好更高效的计算能力和存储,但现在,我们至少可以快速地建立模型,进行测试,甚至部署在互联网上,供全世界使用。今天,由于大量的数据、高效的数据存储技术以及更快更便宜的处理能力,机器学习领域非常活跃。图 2-1 总结了人工智能的时间轴。

img/501289_1_En_2_Fig1_HTML.png

图 2-1

人工智能时间轴

机器学习被认为是人工智能领域下的一个子学科。机器学习(ML)研究旨在根据经验自动提高为特定任务设计的计算机算法的性能。在机器学习研究中,经验来自训练数据,训练数据可以被定义为根据先前记录的观察收集的样本数据。通过这种体验,机器学习算法可以学习和建立数学模型来进行预测和决策。学习过程从将包含隐含模式的训练数据(例如,例子、直接经验、基本指令)输入模型开始。由于计算机比人类具有更强的处理能力,它们可以在短时间内从数据中找到这些有价值的模式。这些模式然后被用来对相关事件做出预测和决策。如果开发者构建了允许连续训练的合适的机器学习系统,则即使在部署之后,学习也可以继续。

以前,我们可能会在系统的几个子组件中使用机器学习。现在,我们实际上使用机器学习来取代整套系统,而不是试图为每一部分建立一个更好的机器学习模型。

—杰夫·迪恩

机器学习应用在不同领域的使用越来越多。这些现实生活中的应用在很大程度上有所不同。下面列出了一些使用案例:

  • 医疗保健:针对患者症状的医疗诊断

  • 电子商务:预测预期需求

  • 法律:审查法律文件,提醒律师注意有问题的条款

  • 社交网络:根据用户在约会应用上的偏好找到一个好的匹配

  • 金融:根据历史数据预测股票的未来价格

这显然是一个非详尽的列表,有数百个,如果不是数千个,潜在的机器学习用例。取决于你的目标是什么,有许多不同的方法来创建机器学习模型。这些方法通常分为四种主要方法:(I)监督学习,(ii)半监督学习,(iii)非监督学习,和(iv)强化学习。

每种方法在设计上都有明显的不同,但它们都遵循相同的基本原则,并符合相同的理论背景。在接下来的章节中,我们将更详细地介绍这些不同的方法。但首先,我们将简单谈谈相邻领域的范围:(一)人工智能,(二)深度学习,(三)大数据,(四)数据科学。

机器学习的范围及其与相邻领域的关系

一旦你开始消费书籍、文章、视频课程、博客文章等机器学习内容,你会经常看到人工智能、机器学习、深度学习、大数据、数据科学等术语。这些术语之间的区别有一点模糊。在本节中,我们将澄清这种模糊性并说明其区别。

人工智能

人工智能(AI)是一个宽泛的术语,它的定义在不同的教科书中有所不同。人工智能一词通常用于描述模拟人类智能和模仿人类与人类思维相关的“认知”能力的计算机。解决问题和学习是这些认知能力的例子。人工智能领域包含机器学习研究,因为人工智能系统能够从经验中学习。一般来说,具有人工智能的机器能够

  • 理解和解释数据

  • 从数据中学习

  • 基于从数据中提取的见解和模式做出“智能”决策

这些术语与机器学习高度相关。由于机器学习,人工智能系统可以在意识水平上学习和超越。机器学习用于训练 AI 系统,让它们变得更聪明。

深度学习

深度学习(DL)是机器学习的一个子领域,专门使用多层神经元从原始数据中提取模式和特征。这些多层相互连接的神经元创建了人工神经网络(ANNs 参见图 2-2 。人工神经网络是一种特殊的机器学习算法,旨在模拟人脑的工作机制。有许多不同类型的人工神经网络用于多种目的。总之,深度学习算法是机器学习算法的一个子集。

img/501289_1_En_2_Fig2_HTML.png

图 2-2

人工神经网络

正如在机器学习中一样,所有四种方法(监督、半监督、非监督和强化学习)都可以在深度学习中使用。当有大量数据和足够的计算能力时,深度学习几乎总是优于其他机器学习算法。深度学习算法在图像处理、语音识别和机器翻译中特别有用。

数据科学

数据科学是一个跨学科领域,位于人工智能、特定领域知识、信息科学和统计学的交叉点。数据科学家使用各种科学方法、流程和算法来获取知识,并从观察到的数据中获得洞察力。

与机器学习相比,数据科学研究的目标不一定是模型训练。数据科学研究通常旨在提取知识和洞察力,以支持人类决策过程,而不是创建人工智能系统。因此,尽管数据科学和其他相邻领域之间存在交叉,但数据科学领域与它们不同,因为它不需要提供智能系统或训练有素的模型。

大数据

大数据是一个旨在高效分析无法用传统数据处理方法和应用处理的大量数据的领域。更多观察的数据通常会带来更高的准确性,而高复杂性可能会增加错误发现率。大数据领域研究当数据集非常大时,如何高效地捕获、存储、分析、搜索、共享、可视化和更新数据。大数据研究可以用于人工智能(及其子领域)和数据科学。大数据位于前面提到的所有其他领域的交叉点,因为它的方法对所有这些领域都至关重要。

分类图

这些相邻术语之间的关系可以在下面的分类图中可视化,如图 2-3 所示。

img/501289_1_En_2_Fig3_HTML.jpg

图 2-3

人工智能和数据科学的分类

这种分类法几乎是模糊背后原因的清晰证据。每当我们在谈论深度学习的时候,我们也在谈论机器学习和人工智能。当我们在进行深度学习项目时,有些人可能会称之为数据科学项目或大数据项目。这些命名方法不一定是不正确的,但是它们很容易混淆。因此,了解这些场的交集和减法是至关重要的。

机器学习方法和模型

顶级机器学习方法根据其学习反馈机制的性质进行分类。这些不同的方法可以列举如下:

  • 监督学习

  • 无监督学习

  • 半监督学习

  • 强化学习

大多数机器学习问题可以通过采用这些方法中的一种来解决。然而,我们可能仍然会遇到不适合这些方法之一的复杂的机器学习解决方案。在本节中,我们将简要介绍这四种主要机器学习方法的范围,以及它们的应用示例。这种分类法至关重要,因为它将帮助您快速发现将来可能遇到的问题的本质,分析您的资源,并开发合适的解决方案。让我们从监督学习方法开始。

监督学习

当存在包含响应变量值(或标签)记录的数据集时,可以采用监督学习方法。根据上下文,这些带有标签的数据通常被称为“标签数据”和“训练数据”例如,当我们试图使用一个人的体重、年龄和性别来预测其身高时,我们需要包含其体重、年龄和性别信息以及实际身高的训练数据。这些数据允许机器学习算法发现身高和其他变量之间的关系。然后,使用这些知识,该模型可以预测给定人的身高。

例如,我们可以根据以前看到的垃圾邮件和非垃圾邮件的区别特征(如电子邮件的长度和电子邮件中特定关键字的使用)将电子邮件标记为“垃圾邮件”或“非垃圾邮件”。从训练数据的学习持续进行,直到机器学习模型在训练数据上达到高水平的准确度。

有两个主要的监督学习问题:(I)分类问题和(ii)回归问题。在分类问题中,模型学习根据它们的变量值对观察值进行分类。在学习过程中,模型会接触到大量带有标签的观察结果。例如,在看到数千名客户的购物习惯和性别信息后,模型可能会根据他们的购物习惯成功预测新客户的性别。二元分类是用于在两个标签下分组的术语,例如男性和女性。另一个二元分类的例子可能是预测图片中的动物是“猫”还是“不是猫”,如图 2-4 所示。

img/501289_1_En_2_Fig4_HTML.png

图 2-4

监督学习中的分类问题1

另一方面,当有两个以上的标签时,使用多标签分类。识别和预测图像上的手写字母和数字是多标签分类的一个例子。

在回归问题中,目标是通过利用其他变量(即自变量、解释变量或特征)和目标变量(即因变量、响应变量或标签)之间的关系来计算值。我们的目标变量和其他变量之间的关系强度是预测值的一个关键决定因素,此外还有用于观察的解释变量的值。根据客户的历史数据预测客户会花多少钱是一个回归问题。

有几十种不同的机器学习算法适合监督学习。由于这本书的重点是深度学习,我们只涵盖一些更受欢迎的,而不深入他们的细节。

  • 线性和逻辑回归:线性回归是一种建模数字响应变量(Y)和一个或多个解释变量(Xs)之间关系的线性方法。另一方面,逻辑回归是一种略有不同的方法,用于模拟特定类别或事件存在的概率,如男性/女性代表性别。因此,线性回归用于回归问题,而逻辑回归大多用于分类问题。

  • 决策树和集成方法:决策树是一种类似流程图的结构和决策支持工具,它将潜在决策和不确定事件与其概率联系起来,以创建一个预测可能结果的模型。我们还可以集成多个决策树来创建更高级的机器学习算法,如随机森林算法。

  • 支持向量机:支持向量机构建一个超平面来分离一个空间,该空间可用于分类、回归或离群点检测。例如,三维空间(例如,立方体)可以用二维超平面(例如,正方形)分成更小的块。这将有助于将观察结果分成两个不同的类别。潜在的应用可能比这个例子复杂得多。支持向量机是一种流行的机器学习算法,由于其高精度性能和相对低水平的计算资源要求。

  • K-最近邻:K-最近邻算法是一种机器学习算法,可用于分类和回归问题。k 是用户定义的常数,表示算法中包含的邻居观测值的数量。在分类问题中,新的未标记观察的邻居被用于基于邻居的标签来预测该新观察的标签。

  • 神经网络 (多层感知器,MLP) :仅前馈神经网络、卷积神经网络(CNN)、循环神经网络(RNNs)经常用于监督学习问题,这些将在后面的章节中介绍。

无监督学习

无监督学习是机器学习算法中使用的一种方法,用于从不包含标签的数据集进行推断。无监督学习主要用于聚类分析。聚类分析是一种分组工作,其中一个组(即一个聚类)的成员比其他聚类的成员彼此更相似。有许多不同的聚类方法可用。它们通常利用一种基于选定度量的相似性度量,例如欧几里德距离或概率距离。生物信息学序列分析、遗传聚类、模式挖掘和对象识别是可以用无监督学习方法解决的一些聚类问题。

无监督学习的另一个用例是降维。维度相当于数据集中使用的要素数量。在某些数据集中,您可能会发现存储在各个列中的数百个潜在要素。在大多数数据集中,这些列中有几个是高度相关的。因此,我们要么选择最好的,特征选择,要么结合已有特征提取新的特征,特征提取。这就是无监督学习发挥作用的地方。降维方法帮助我们创建更整洁的模型,没有噪音和不必要的特征。

无监督学习也可以用于异常检测问题和生成系统。我将简要提及一些流行的无监督机器学习模型,如下所示:

  • 层次聚类:层次聚类是一种无监督的机器学习算法,用于对具有相似特征的未标记观察值进行增量分组。层次聚类可以是聚集的(自下而上的方法)或分裂的(自上而下的方法)。聚类的层次结构被表示为树或树状图。

  • K-Means 聚类 : K-means 聚类是一种流行的无监督机器学习算法。k 是用户指定的常数,表示要创建的簇的数量。k-均值聚类根据到聚类中心的距离将观察值分组到 k 个不同的聚类中。

  • 主成分分析(PCA) : PCA 广泛用于降维。PCA 找到两个或多个变量的线性组合,称为主成分。该过程降低了模型的维度复杂度,使得问题可以被更快地可视化和分析,同时模型也更容易被训练。

  • 神经网络:自编码器、深度信念网络、Hebbian 学习、生成对抗网络(GANs)和自组织映射是用于无监督学习的一些神经网络。这些网络结构的细节和应用将在接下来的章节中介绍。

半监督学习

半监督学习是一种结合了监督学习和非监督学习特点的机器学习方法。当我们有少量标记数据和大量未标记数据可用于训练时,半监督学习方法特别有用。监督学习特征有助于利用少量的标签数据。相比之下,无监督学习特征对于利用大量未标记数据是有用的。

嗯,你可能会想,如果半监督学习有实用的现实应用。虽然监督学习是一种强大的方法,但标记数据——用于监督学习——是一个昂贵而耗时的过程。另一方面,大量数据也可能是有益的,即使它们没有被标记。因此,在现实生活中,如果做得正确,半监督学习可能会成为最合适和最有成效的机器学习方法。

在半监督学习中,我们通常从聚类未标记的数据开始。然后,我们使用标记数据来标记聚类的未标记数据。最后,大量现在标记的数据用于训练机器学习模型。半监督学习模型可能非常强大,因为它们可以利用大量的数据。

半监督学习模型通常是监督和非监督学习中使用的现有机器学习算法的转换和调整版本的组合。这种方法成功地应用于语音分析、内容分类和蛋白质序列分类等领域。这些领域的相似之处在于,它们提供了丰富的未标记数据,而只有少量的标记数据。

强化学习

强化学习是机器学习的主要方法之一,它涉及在特定的环境中寻找最优的代理行为来最大化回报。代理人学习完善自己的行动,以获得尽可能高的累积回报。强化学习有四个主要元素:

  • 代理:执行分配给它的任务的可训练程序

  • 环境:代理完成其任务的真实或虚拟世界

  • 动作:导致环境中状态改变的代理的移动

  • 报酬:基于行动的消极或积极的报酬

强化学习既可以用于现实世界,也可以用于虚拟世界。例如,您可以创建一个不断发展的广告投放系统,根据不同设置产生的广告收入来决定向网站投放多少广告。广告投放系统将是现实世界应用的一个很好的例子。另一方面,你可以用强化学习在视频游戏中训练一个代理人来与其他玩家竞争,这些玩家通常被称为机器人。最后,用强化学习的方法对机器人的运动进行虚拟和真实的训练。一些流行的强化学习模型可以列举如下:

  • q 学习

  • 国家-行动-奖励-国家-行动

  • 深 Q 网(DQN)

  • 深度确定性政策梯度(DDPG)

现有深度学习框架的一个缺点是缺乏对强化学习的全面模块支持,TensorFlow 也不例外。深度强化学习只能用构建在现有深度学习库之上的扩展库来完成,比如 Keras-RL,TF。代理,以及 Tensorforce 或专用强化学习库,如开放 AI 基线和稳定基线。因此,我们将无法在本书中深入探讨强化学习。

不同方法的评估

我们简要介绍了四种主要的机器学习方法:(I)监督学习,(ii)非监督学习,(iii)半监督学习,以及(v)强化学习。这些方法被应用于具有几种潜在算法的机器学习问题。监督学习解决分类和回归问题,而非监督学习处理维度减少和聚类。半监督学习结合了监督学习和非监督学习方法,以利用未标记的数据进行分类任务,而强化学习用于寻找最佳的行动集,以获得最高的回报。图 2-5 总结了这些方法的特点。

img/501289_1_En_2_Fig5_HTML.png

图 2-5

机器学习方法的特征概述

机器学习的步骤

由于多年的机器学习研究,我们现在已经完善了机器学习流程,可以准确地构建和训练模型。尽管您可能会在其他来源中看到稍微改变的流程,但是基本原理是相同的。机器学习过程的步骤可以列举如下:

  • 收集数据

  • 准备数据

  • 型号选择

  • 培养

  • 估价

  • 超参数调谐

  • 预报

让我们深入每一个步骤,看看它们内部发生了什么。

收集数据

数据是机器学习模型的燃料。没有适当的数据,我们就无法达到预期的目的:高精度。这些数据必须是高质量和大容量的。因此,收集的数据的质量和数量对于成功的机器学习项目都非常重要。事实上,收集数据是机器学习项目中最具挑战性的部分之一。但是不要害怕。感谢 Kaggle 和 UC Irvine 的 Repository 等平台,我们可以跳过“收集数据”这一步,至少是出于教育目的。这一步的结果是数据的表示,例如保存为 CSV ( 逗号分隔值)文件的表格。

准备数据

现在我们已经收集了数据,我们需要为模型构建和训练准备数据。

首先,我们对数据进行初步的清理和转换。这一部分可能包括几项任务,包括但不限于处理缺失值、删除重复项、纠正错误、将字符串转换为浮点数、规范化数据以及生成虚拟变量。

然后,我们将数据随机化,以消除由于数据收集的时间而导致的任何不必要的相关性。在清理和随机化我们的数据后,我们使用数据可视化工具来发现变量之间的关系,这可能在模型构建过程中对我们有所帮助。我们还可以通过数据可视化来检测类不平衡和离群值。

最后,我们将准备好的数据集分成训练和评估(即测试)数据集。

型号选择

根据我们的问题,我们尝试不同的机器学习算法来找到最佳性能的模型。如果没有机器学习和深度学习库,对整个模型算法进行编码将是极其耗时的。但多亏了这些库,我们可以在几个机器学习模型中快速建立我们的模型,并在轻松训练后找到表现最好的一个。

培养

既然我们已经选择了一个(或多个)算法,构建了我们的(或多个)模型,并准备了我们的数据,我们可以将这些数据输入到模型中,并观察它(它们)优化方程变量。训练的目标是做出最高数量的正确预测或最低数量的错误。例如,如果我们使用线性回归,我们使用的方程如下:

y = m*x + b

  • 符号:

  • y:响应变量

  • x:解释变量

  • 男:坡度

  • 拦截

我们的线性回归模型试图找到完美的斜率( m )和截距( b )值,这样我们可能以实际 y 值和 y 预测值之间的最低差异结束。完善我们的模型的过程是在几个训练步骤中反复进行的,直到在所选的性能度量上观察不到进一步的性能提高。

估价

在使用训练数据训练我们的模型之后,我们应该立即使用我们的模型从未见过的评估数据集来测试我们训练的模型。这种以前看不到的数据为我们提供了一个客观的表现得分。数据集的理想训练/测试拆分比率通常是 80/20、90/10 或 70/30,具体取决于领域。在某些情况下,数据科学家还会留出一个验证数据集。

尤其是当我们的数据有限时,数据科学家使用的一种有用的评估技术是交叉验证。请记住,我们将经常应用交叉验证进行评估。

Cross-Validation

是一种用于评估的替代重采样技术。在 k 倍交叉验证中,数据集被分成 k 个组。一组留作测试数据,该组切换 k 次。因此,每组用于测试一次。最终,我们有了一个更加可靠的性能评估。

评估是检查过拟合的一个特别重要的步骤。机器学习模型在优化方面过于急切。他们倾向于创建一组非常复杂的变量值来捕捉我们数据中的所有变化。然而,当我们在现实生活中部署模型时,这可能会导致问题,因为使用有限的训练数据来完善模型会产生短暂的视觉效果。

Overfitting

是一个机器学习问题,当模型过于接近观察值时就会出现。当模型存在过拟合问题时,它往往对训练数据表现良好,但对测试数据和现实世界表现不佳。

我们的模型应该高度准确,但也要灵活。在机器学习研究中,我们总是观察偏差和方差的权衡。引入系统的偏差水平和观察到的方差水平之间必须有一个平衡,这样我们的模型才能在现实生活中提供有意义和可靠的预测。

Bias and Variance Trade-Off

是机器学习模型的属性。偏差是模型为简化优化过程而做出的假设。方差是对目标函数可以输出的值的分布的度量。虽然偏差给模型带来了简单性,但你可能离可靠的预测还差得很远。另一方面,高方差会损害获得有意义结果的能力。

这些是评估机器学习模型时要注意的一些属性。假设我们对偏差和方差的权衡和过拟合非常小心,并且我们使用交叉验证来训练我们的模型。但是我们如何衡量我们的模型的成功呢?这是我们根据问题选择性能术语的地方。

Performance Terms for Classification

我们通常参考混淆矩阵(见图 2-6 )来理解我们的模型表现如何。混淆矩阵不仅允许我们计算模型的精确度,还允许我们计算模型性能的召回精确度F1 得分

img/501289_1_En_2_Fig6_HTML.png

图 2-6

分类问题的混淆矩阵

Performance Terms for Regression

我们通常使用基于错误的度量来度量模型性能。实际观察和预测之间的差异称为误差。通过综合计算,我们可能会找到诸如【RMSE】平均绝对误差(MSE) 等度量标准。这些度量值有助于衡量模型在特定回归中的成功程度。

**### 超参数调谐

既然我们在训练和测试数据集上都有了性能指标结果,我们可以调整模型超参数来进一步提高我们的性能。学习率、训练步骤数、初始化值、时期大小、批量大小和分布类型是一些可以随意使用的超参数。超参数调谐通常被称为艺术作品,而不是科学。数据科学家利用他们的直觉来尝试超参数的不同组合,以实现最高的性能。

预报

至此,我们已经完成了使用优化的超参数的初始训练。现在,我们可以用训练好的模型进行预测。预测步骤不应被视为学习过程的结束。在收到真实世界的反馈后,我们可以回过头来进一步训练、评估和调整我们的模型,以解决数据科学问题不断变化的本质。

最终评估

在这一章中,我们介绍了机器学习,其中也包括深度学习的子领域。我们对比了人工智能、机器学习、深度学习、数据科学和大数据等领域。

我们访问了主要的机器学习方法,(I)监督学习,(ii)非监督学习,(iii)半监督学习,和(iv)强化学习,并介绍了一些与这些方法一起使用的流行的机器学习模型。

然后,我们讲述了机器学习的步骤。本节解释了使用我们收集和清理的数据成功构建和训练机器学习模型的必要步骤。

在下一章,我们将对深度学习进行介绍。机器学习的介绍将帮助你掌握我们将在下一章看到的概念。

**

三、深度学习和神经网络概述

既然你正在读这本书,那么可以有把握地假设你知道深度学习是如何在最近几年流行起来的。深度学习越来越受欢迎有一个非常好的原因:它不可思议的准确性表现。尤其是当有丰富的数据和可用的处理能力时,深度学习是机器学习专家的选择。深度学习与传统机器学习算法的性能对比如图 3-1 所示。

img/501289_1_En_3_Fig1_HTML.png

图 3-1

深度学习与传统 ML 在准确性上的比较

深度学习是机器学习的一个子领域,它模仿人脑的数据处理和模式生成能力,用于自动决策。与其他机器学习算法相比,深度学习的独特准确性曲线有助于它被机器学习专家广泛使用和采用。由于人工神经网络,深度学习成为可能。人工神经网络是模拟人脑中神经元的网络结构,以便可以进行深度学习。在图 3-2 中,你可能会发现一个具有深度学习能力的人工神经网络(ANN)的例子。

img/501289_1_En_3_Fig2_HTML.jpg

图 3-2

具有两个隐层的人工神经网络的描述

你可能会认为深度学习是一个新发明的领域,最近推翻了其他机器学习算法。很多人都这样想。然而,人工神经网络和深度学习领域可以追溯到 20 世纪 40 年代。最近深度学习的兴起主要是因为大量的可用数据,更重要的是因为廉价而丰富的处理能力。

这是深度学习的概述章节。我们将看看我们在深度学习中经常使用的关键概念,包括(I)激活函数,(ii)损失函数,(iii)优化器和反向传播,(iv)正则化,以及(v)特征缩放。但是,首先,我们将深入研究人工神经网络和深度学习的历史,以便您对本书中经常看到的深度学习概念的根源有所了解。

神经网络和深度学习研究的时间线

神经网络和深度学习研究的时间轴并不是由一系列不间断的进步组成的。事实上,人工智能领域经历了几次失败,这些失败被称为 AI winters。让我们深入研究神经网络和深度学习的历史,它始于 1943 年。

人工神经元**的开发——1943 年,先驱学者沃尔特·皮茨和沃伦·麦卡洛克发表了论文【神经活动中内在思想的逻辑演算】,在那里他们提出了一个生物神经元的数学模型,称为 麦卡洛克-皮茨神经元 。麦卡洛克皮茨神经元的能力是最小的,它没有学习机制。麦卡洛克·皮茨神经元的重要性在于,它为深度学习奠定了基础。1957 年,Frank Rosenblatt 发表了另一篇论文,题为“感知器:感知和识别自动机”,其中他介绍了具有学习和二进制分类功能的感知器。革命性的感知机模型——在麦克库洛奇·皮茨神经元之后崛起——激励了许多从事人工神经网络研究的研究人员。

****反向传播**——1960 年,亨利·j·凯利发表了一篇题为《最佳飞行路径的梯度理论》的论文,他在论文中展示了一个连续反向传播的例子。反向传播是一个重要的深度学习概念,我们将在本章中讨论。1962 年,Stuart Dreyfus 在他的论文“变分问题的数值解”中用链式法则改进了反向传播。反向传播这个术语是由 Rumelhart、Hinton 和 Williams 在 1986 年创造的,这些研究人员已经将其应用于人工神经网络。

训练和计算机化–1965 年,通常被称为“深度学习之父”的阿列克谢·伊瓦赫年科(Alexey Ivakhnenko)建立了一个神经网络的分层表示,并通过使用多项式激活函数成功训练了这个模型。1970 年,Seppo Linnainmaa 发现了反向传播的自动微分,并能够编写第一个反向传播程序。这一发展可能标志着深度学习计算机化的开始。1971 年,Ivakhnenko 创建了一个八层神经网络,由于其多层结构,它被认为是一个深度学习网络。

1969 年,马文·明斯基和西蒙·派珀特写了《感知机》一书,他在书中猛烈抨击了感知机弗兰克·罗森布拉特的工作。这本书对人工智能项目资金造成了毁灭性的破坏,引发了从 1974 年持续到 1980 年的人工智能冬天

卷积神经网络——1980 年,Kunihiko Fukushima 推出了 neocognitron,这是第一个卷积神经网络(CNN),可以识别视觉模式。1982 年,Paul Werbos 提出了在神经网络中使用反向传播来最小化误差,并且 AI 社区已经广泛采用了这个提议。1989 年,Yann LeCun 使用反向传播来训练 CNN 识别 MNIST(改进的国家标准和技术研究所)数据集中的手写数字。在本书中,我们在第七章中有一个类似的案例研究。

循环神经网络–1982 年,John Hopfield 介绍了 Hopfield 网络,这是循环神经网络(RNNs)的早期实现。循环神经网络是革命性的算法,最适用于顺序数据。1985 年,Geoffrey Hinton、David H. Ackley 和 Terrence Sejnowski 提出了玻尔兹曼机,这是一种没有输出层的随机 RNN。1986 年,Paul Smolensky 开发了一种新的玻尔兹曼机变体,它在输入层和隐藏层中没有层内连接,这种机器被称为受限玻尔兹曼机。受限玻尔兹曼机在推荐系统中尤其成功。1997 年,Sepp Hochreiter 和 Jürgen Schmidhuber 发表了一篇关于改进的 RNN 模型——长短期记忆(LSTM)的论文,我们也将在第八章中涉及。2006 年,Geoffrey Hinton、Simon Osindero 和 Yee Whye Teh 组合了几个受限玻尔兹曼机(RBM)并创建了深度信念网络,这提高了 RBM 的能力。

深度学习的能力–1986 年,Terry Sejnowski 开发了 NETtalk,这是一个基于神经网络的文本到语音转换系统,可以对英语文本进行发音。1989 年,George Cybenko 在他的论文“Sigmoidal 函数的叠加近似”中指出,具有单个隐藏层 的前馈神经网络可以求解任何连续函数

消失梯度问题——1991 年,Sepp Hochreiter 发现并证明了消失梯度问题,它减缓了深度学习过程,使其变得不切实际。20 年后,在 2011 年,Yoshua Bengio、Antoine Bordes 和 Xavier Glorot 表明,使用校正线性单位(ReLU)作为激活函数可以防止消失梯度问题。

用于深度学习的 GPU——2009 年,吴恩达、拉杰特·刘冰和阿南德·马德哈万在他们的论文《使用图形处理器的大规模深度无监督学习》中建议使用 GPU 进行深度学习,因为在 GPU 中发现的核心数量比在 CPU 中发现的要多得多。这种转换减少了神经网络的训练时间,使它们的应用更加可行。随着 GPU 制造商推出的官方并行计算平台(如 Nvidia 的 CUDA 和 AMD 的 ROCm),GPU 在深度学习中的使用越来越多,导致了深度学习专用 ASICS 的发展(如谷歌的 TPU)。

ImageNet 和****AlexNet——2009 年,费-李非推出了一个拥有 1400 万张标注图片的数据库,名为 ImageNet。ImageNet 数据库的创建有助于图像处理神经网络的发展,因为深度学习的一个重要组成部分是丰富的数据。自从 ImageNet 数据库创建以来,每年都举行竞赛以改进图像处理研究。2012 年,Alex Krizhevsky 设计了一个经过 GPU 训练的 CNN,AlexNet,与早期的模型相比,模型精度提高了 75%。

生成对抗网络——2014 年,伊恩·古德菲勒(Ian Goodfellow)在当地一家酒吧和朋友聊天时,想到了一个新的神经网络模型的想法。这个革命性的模型是在一夜之间设计出来的,现在被称为生成对抗性神经网络(GANs),它能够生成艺术,文本和诗歌,它可以完成许多其他创造性任务。在第十二章中,我们有一个关于 GANs 实施的案例研究。

力量 强化学习——2016 年,DeepMind 训练了一个深度强化学习模型 AlphaGo,它可以玩围棋,围棋被认为是比象棋复杂得多的游戏。AlphaGo 在 2017 年击败了围棋世界冠军柯洁。

图灵奖授予深度学习的先驱——2019 年,人工智能领域的三位先驱 Yann LeCun、Geoffrey Hinton 和 Yoshua Bengio 分享了图灵奖。这个奖项证明了深度学习对于计算机科学界的重要性。

人工神经网络的结构

在深入研究基本的深度学习概念之前,让我们看看当今现代深度神经网络的发展历程。今天,我们可以很容易地找到具有数百层和数千个神经元的神经网络的例子,但在二十世纪中期之前,人工神经网络这个术语甚至不存在。这一切始于 1943 年的一个简单的人工神经元——麦卡洛克·皮茨神经元——它只能进行简单的数学计算,没有学习能力。

麦卡洛克-皮茨神经元

麦卡洛克·皮茨神经元于 1943 年问世,它只能进行基本的数学运算。每个事件被赋予一个布尔值(0 或 1),如果事件结果(0 和 1)的总和超过一个阈值,那么人工神经元就会触发。图 3-3 显示了麦卡洛克皮茨神经元的 or 和 and 运算的可视化示例。

img/501289_1_En_3_Fig3_HTML.png

图 3-3

用于“或”与“与”运算的麦卡洛克·皮茨神经元

由于来自麦卡洛克皮茨神经元事件的输入只能是布尔值(0 或 1),它的能力是最小的。随着线性阈值单元(LTU)的发展,这种限制得到了解决。

线性阈值单位(LTU)

在麦卡洛克·皮茨神经元中,每个事件的重要性是相等的,这是有问题的,因为大多数真实世界的事件不符合这种简单的设置。为了解决这个问题,1957 年引入了线性阈值单元(LTU)。在 LTU 中,权重被分配给每个事件,这些权重可以是负的或正的。每个事件的结果仍被赋予一个布尔值(0 或 1),但随后乘以指定的权重。只有当这些加权事件结果的总和为正时,才会激活 LTU。在图 3-4 中,你可能会发现 LTU 的可视化,它是今天人工神经网络的基础。

img/501289_1_En_3_Fig4_HTML.png

图 3-4

线性阈值单元(LTU)可视化

感知器

感知器是一种用于监督学习的二进制分类算法,由一层 ltu 组成。在感知器中,ltu 使用相同的事件输出作为输入。感知器算法可以调整权重,以校正经过训练的神经网络的行为。此外,可以添加偏置项来提高网络的精度性能。

当感知器只有一层时,称为单层感知器。有一个输出层和一个接收输入的输入层。当隐藏层被添加到单层感知器时,我们最终得到一个多层感知器(MLP)。MLP 被认为是一种深度神经网络,我们为日常问题建立的人工神经网络就是 MLP 的例子。在图 3-5 中,你可能会发现一个单层感知器的可视化例子。

img/501289_1_En_3_Fig5_HTML.png

图 3-5

单层感知器图的例子

现代深度神经网络

我们今天遇到的深度神经网络是多层感知器(MLP)的改进版本。我们经常使用比阶跃函数(0 或 1)更复杂的激活函数,如 ReLU、Sigmoid、Tanh 和 Softmax。现代深度神经网络通常利用一种梯度下降方法进行优化。现代深度神经网络示例如图 3-6 所示。

img/501289_1_En_3_Fig6_HTML.png

图 3-6

一个现代深度神经网络的例子

现在,您已经了解了更多关于开发今天的现代深度神经网络的旅程,这是从麦卡洛克·皮茨神经元开始的,我们可以深入研究我们在应用中使用的基本深度学习概念。

激活功能

激活函数是用于帮助人工神经网络从数据中学习复杂模式的函数。通常在每个神经元的末端添加一个激活函数,它会影响下一个神经元触发什么。换句话说,如图 3-7 所示,一个神经元的激活函数是在给定一个输入或一组输入后,给出该神经元的输出。

img/501289_1_En_3_Fig7_HTML.png

图 3-7

最后是一个带有激活函数的 LTU 图的例子

激活函数引入了最终的计算步骤,这给人工神经网络增加了额外的复杂性。因此,它们增加了所需的训练时间和处理能力。那么,我们为什么要在神经网络中使用激活函数呢?答案很简单:激活函数提高了神经网络使用相关信息和抑制不相关数据点的能力。如果没有激活函数,我们的神经网络将只能执行线性转换。尽管避免激活函数使得神经网络模型更简单,但是该模型的功能将更弱,并且不能收敛于复杂的模式结构。没有激活函数的神经网络本质上只是一个线性回归模型。

我们可以在神经网络中使用许多不同的激活函数。激活功能的非详尽列表可在此处找到:

  • 二进制步骤

  • 线性的

  • 乙状结肠(逻辑激活功能)

  • 双曲正切

  • 整流线性单位

  • Softmax(软件最大值)

  • 李奇注意到了

  • 参数化 ReLU

  • 指数线性单位

  • 嗖嗖

在这些激活函数中,Tanh、ReLU 和 Sigmoid 激活函数广泛用于单个神经元激活。还有,Softmax 函数在图层之后被广泛使用。您可以在图 3-8 中找到 Tanh、ReLU 和 Sigmoid 函数的 X-Y 图。

img/501289_1_En_3_Fig8_HTML.png

图 3-8

Tanh、ReLU 和 Sigmoid 函数图

根据问题的性质,一个激活功能可能比另一个执行得更好。尽管 ReLU、Tanh 和 Sigmoid 函数通常在深度学习中收敛得很好,但我们应该尝试所有可能的函数,并优化我们的训练,以尽可能实现最高的精度性能。可以通过以下要点对 ReLU、Tanh 和 Sigmoid 进行简单的比较:

  • ReLU 函数是一个广泛使用的通用激活函数。应该用在隐藏层。如果有死亡的神经元,泄漏的 ReLU 可能会修复潜在的问题。

  • Sigmoid 函数在分类任务中工作得最好。

  • Sigmoid 和 Tanh 函数可能会导致消失梯度问题。

优化训练实践的最佳策略是从 ReLU 开始,尝试其他激活功能,看看性能是否有所提高。

损失(成本或误差)函数

损失函数是用于测量深度学习模型对于给定数据的性能的函数。它通常基于误差项,误差项被计算为真实(测量)值和训练模型的预测值之间的距离。

$$ {e}_i={y}_i-{\hat{\mathrm{y}}}_i $$

误差=测量值-预测值

因此,我们每次预测都会有一个误差项。假设您正在处理数百万个数据点。为了能够从这些单独的误差项中获得洞察力,我们需要一个聚合函数,以便我们能够得出一个性能评估的单一值。这个函数被称为损失函数、 成本函数 ,或者 误差函数 ,视上下文而定

几个损失函数用于性能评估,选择正确的函数是建模的一个组成部分。这种选择必须基于问题的性质。虽然均方根误差(RMSE)函数对于我们想要惩罚大误差的回归问题是正确的损失函数,但是多类交叉熵应该被选择用于多类分类问题。

此外,为了被用于生成聚合误差项的单个值,损失函数也可以被用于强化学习中的奖励。在本书中,我们将主要使用带有误差项的损失函数,但请注意,使用损失函数作为奖励措施是可能的。

深度学习任务中使用了几个损失函数。均方根误差(RMSE)、均方误差(MSE)、平均绝对误差(MAE)和平均绝对百分比误差(MAPE)是回归问题的一些合适的损失函数。对于二元和多类分类问题,我们可以使用交叉熵的变体(即对数)函数。

深度学习中的优化

既然我们已经讨论了激活和损失函数,是时候继续讨论权重和偏差优化了。神经元和层中使用的激活函数对从权重和偏差项导出的线性结果进行最终调整。我们可以使用这些参数(权重和偏差)进行预测。实际值和预测值之间的距离被记录为误差项。这些误差项通过损失函数聚集成单个值。除了这个过程,优化函数对权重和偏差进行小的改变,并用损失函数测量这些改变的影响。该过程有助于找到最佳权重和偏差值,以最小化误差并最大化模型的准确性。这个训练周期如图 3-9 所示。

img/501289_1_En_3_Fig9_HTML.jpg

图 3-9

具有成本函数、激活函数和优化器的深度学习模型训练

在优化过程中会遇到一些优化算法和挑战。在本节中,我们将简要介绍这些功能和挑战。但首先,我们来看看一个必不可少的优化概念:反向传播

反向传播

反向传播算法是用于与优化器并行迭代的神经网络架构中的基本组件。它是神经网络学习的中心机制。这个名字本身就说明了问题,因为单词 propagate 的意思是传送某物。所以一词 backpropagation 的意思是将信息传回。“这正是反向传播算法所要做的:它将计算出的损失返回给系统,由优化器用来调整权重和偏差。这个过程可以一步一步地解释,如下所示:

  • 步骤 1 :训练好的神经网络用当前的权重和偏差进行预测。

  • 步骤 2 :用损失函数作为单一误差度量来测量神经网络的性能。

  • 步骤 3 :这个误差度量被反向传播到优化器,以便它可以重新调整权重和偏差。

  • 重复

通过使用反向传播算法提供的信息,优化算法可以完善神经网络中使用的权重和偏差。让我们来看看优化算法(即优化器),它们与反向传播机制并行使用。

优化算法

优化算法可以被定义为帮助另一个算法无延迟地最大化其性能的算法。深度学习是优化算法被广泛使用的一个领域。深度学习任务中最常用的优化算法如下:

  • 圣经》和《古兰经》传统中)亚当(人类第一人的名字

  • 随机梯度下降

  • 阿达德尔塔

  • Rmsprop

  • 阿达玛斯

  • 阿达格拉德

  • 那达慕

请注意,所有这些优化器以及前面提到的损失和激活函数在 TensorFlow 中都很容易找到。实际应用中最常用的是 Adam 优化器和随机梯度下降(SGD)优化器。让我们看看所有优化器之母,梯度下降和 SGD,以便更好地理解优化算法是如何工作的。

梯度下降和随机梯度下降(SGD)–随机梯度下降是梯度下降方法的一种变体。SGD 是深度学习中广泛使用的一种迭代优化方法。SGD 的根源可以追溯到 20 世纪 50 年代,它是最古老的——但也是最成功的——优化算法之一。

梯度下降法是一系列优化算法,用于最小化神经网络中的总损失(或成本)。有几种梯度下降实现:原始梯度下降或批量梯度下降算法使用每个时期的全部训练数据。随机(随机)梯度下降(SGD)选择随机观察来测量由于权重和偏差的变化而导致的总损失(或成本)的变化。最后,小批量梯度下降使用小批量,因此训练可能仍然是快速和可靠的。

Epoch

是超参数,表示使用定型数据集调整神经网络值的次数。

img/501289_1_En_3_Fig10_HTML.png

图 3-10

显示梯度下降的失重图

图 3-10 显示了梯度下降算法的工作原理。当机器学习专家选择更快的学习速率时,采取更大的增量步骤。

Learning Rate

是优化算法中的参数,它调节每次迭代时的步长,同时向前移动损失/成本函数的最小值。学习速度越快,模型越快地收敛到最小值附近,但它可能会超过实际的最小值点。如果学习速度慢,优化可能会花费太多时间。因此,一个机器学习专家必须选择最优的学习速率,它允许模型在合理的时间内找到期望的最小点。

亚当优化器–什么是亚当?

我不会深入其他优化算法的细节,因为它们大多是梯度下降法的修改或改进实现。所以暂时了解一下梯度下降算法就够了。

在下一节中,我们将看到在培训过程中对优化过程产生负面影响的优化挑战。如前所述,开发了一些优化算法来缓解这些挑战。

优化挑战

我们在深度学习中经常会遇到三个优化挑战。这些挑战是(I)局部最小值,(ii)鞍点,和(iii)消失梯度。我们简单讨论一下它们是什么。

局部最小值****–在神经网络训练中,具有单个最小值的简单减肥图可能有助于可视化体重和计算出的体重之间的关系,以用于教育目的。然而,在现实世界的问题中,这个图可能包含许多局部最小值,并且我们的优化算法可能收敛于一个局部最小值而不是全局最小值点。图 3-11 显示了我们的模型是如何陷入局部最小值的。

img/501289_1_En_3_Fig11_HTML.png

图 3-11

具有两个局部最小值和一个全局最小值的减重图

鞍点—鞍点是图中的稳定点,算法无法判断它是局部最小值还是局部最大值。鞍点的两边斜率为零。使用多个观测值进行损失计算的优化器可能会陷入鞍点。因此,随机梯度下降是鞍点的一个合适的解决方案。图 3-12 为带有鞍点的简化图形。

img/501289_1_En_3_Fig12_HTML.png

图 3-12

具有两个局部最小值和一个全局最小值的减重图

消失梯度****–过度使用某些激活函数(如 Sigmoid)可能会对优化算法产生负面影响。由于损失函数的梯度接近于零,所以降低损失函数的输出变得困难。消失梯度问题的一个有效解决方案是在隐藏层中使用 ReLU 作为激活函数。Sigmoid 激活函数——消失梯度问题的主要原因——及其导数如图 3-13 所示。

img/501289_1_En_3_Fig13_HTML.png

图 3-13

Sigmoid 函数及其导数

为了能够解决这种常见的优化挑战,我们应该尝试并找到激活函数和优化函数的最佳组合,以便我们的模型正确收敛并找到理想的最小点。

过拟合和正则化

深度学习和机器学习的另一个重要概念是过拟合。在本节中,我们将讨论过拟合问题以及如何用正则化方法解决过拟合问题。

过拟合

在第二章中,我们已经简单解释了机器学习的过拟合概念。过拟合也是深度学习中的一个挑战。我们不希望我们的神经网络过于紧密地拟合有限的一组数据点,这会危及它在现实世界中的性能。我们也不希望我们的模型欠拟合,因为它不会给我们一个好的精度水平。欠配合和过配合问题如图 3-14 所示。

img/501289_1_En_3_Fig14_HTML.png

图 3-14

X-Y 图中的欠拟合和过拟合

解决拟合不足问题的方法是建立一个具有有意义特征的好模型,输入足够的数据,进行足够的训练。另一方面,更多的数据、去除过多的特征和交叉验证是解决过拟合问题的适当方法。此外,我们有一组复杂的方法来克服过拟合问题,即正则化方法。

正规化

正则化是一种对抗过拟合的技术。有许多可能的正则化方法,可列举如下:

  • 提前停止

  • 拒绝传统社会的人

  • L1 和 L2 正规化

  • 日期增加

提前停止–提前停止是一种非常简单但有效的防止过拟合的策略。设置足够数量的历元(训练步骤)对于实现高水平的准确性至关重要。但是,您可能很容易走极端,将您的模型训练得与您的训练数据过于吻合。使用早期停止,如果模型在一定数量的时期内没有显示出显著的性能改善,则学习算法停止。

辍学–辍学是另一种简单但有效的正规化方法。启用 dropout 后,我们的模型会暂时从网络中删除一些神经元或层,这会给神经网络增加额外的噪声。这种噪声防止模型与训练数据过于接近,并使模型更加灵活。

L1 和 L2 正则化–这两种方法在损失函数中增加了一个额外的惩罚项,对误差的惩罚力度更大。对于 L1 正则化,这一项是套索回归,而对于 L2 正则化,这是岭回归。处理大量要素时,L1 和 L2 正则化尤其有用。

数据扩充–数据扩充是一种增加训练数据量的方法。通过对现有数据进行小的转换,我们可以生成更多的观察值,并将它们添加到原始数据集中。数据扩充增加了训练数据的总量,这有助于防止过拟合问题。

特征缩放

深度学习的另一个关键概念是特征缩放。特征缩放是一种标准化特征范围的方法,以便神经网络更准确地执行。当特征值的范围变化很大时,一些目标函数可能无法在机器学习模型中正确工作。例如,分类器通常计算两个数据点之间的距离。当一个特征的值的方差很大时,这个特征决定了这个计算的距离,这意味着这个特定特征对结果的夸大影响。缩放每个特性的值范围有助于消除这个问题。下面列出了几种特征缩放方法:

  • 标准化:调整各特征值,使均值和单位方差为零。

  • 最小-最大归一化(重新缩放):在[0,1]和[-1,1]之间缩放每个特性的值。

  • 均值归一化:从每个数据点中扣除均值,并将结果除以最大-最小差。它是最小-最大归一化的一个稍有改变且不太流行的版本。

  • 缩放到单位长度:将特征的每个分量除以该特征向量的欧几里德长度。

在深度学习中使用特征缩放有两个好处:

  • 它确保每个特征对预测算法的贡献成比例。

  • 它加速了梯度下降算法的收敛,从而减少了训练模型所需的时间。

最终评估

在这一章中,我们讨论了人工神经网络和深度学习的时间线。经过多年的研究,它帮助我们理解了我们在职业生活中使用的概念是如何变成现实的。好消息是,由于 TensorFlow,我们能够在几秒钟内将这些组件添加到我们的神经网络中。

沿着深度学习时间线,我们详细分析了神经网络和人工神经元的结构。此外,我们涵盖了基本的深度学习概念,包括(I)优化函数,(ii)激活函数,(iii)损失函数,(iv)过拟合和正则化,以及(v)特征缩放。

这一章是上一章的延续,上一章我们讲述了机器学习的基础知识。在下一章中,我们将了解深度学习研究中最常用的补充技术:(i) NumPy,(ii) SciPy,(iii) Matplotlib,(iii) Pandas,(iv) scikit-learn,以及(v) Flask。**

四、TensorFlow 2.x 的补充库

现在我们已经讨论了机器学习和深度学习的基础知识,我们可以慢慢转向深度学习的应用方面。如你所知,每个机器学习应用,包括深度学习应用,都有一个由几个步骤组成的流水线。TensorFlow 为所有这些步骤提供了几个模块。即使 TensorFlow 在建模、训练、评估和预测方面非常强大,我们仍然需要其他补充库来完成某些任务,尤其是数据准备。尽管您可能在深度学习流水线中使用的潜在库可能在很大程度上有所不同,但最受欢迎的补充库如下:

| NumPySciPy 熊猫 | • Matplotlib• 科学学习烧瓶 |

特别是在 TensorFlow 2.x 之后,我们开始看到越来越多的数据准备、可视化以及其他相关功能添加到 TensorFlow 中。然而,这些功能还不能与这些专用库所提供的相提并论。表 4-1 列出了这些库及其核心功能。

表 4-1

TensorFlow 的补充库及其主要用例

|

图书馆

|

核心能力

|
| --- | --- |
| NumPy | 阵列处理 |
| 我的天啊 | 科学计算 |
| 熊猫 | 阵列处理和数据分析,包括数据可视化 |
| Matplotlib | 数据可视化 |
| Scikit-learn | 机器学习 |
| 瓶 | 用于部署的 Web 框架 |

让我们看看如何使用 pip(我们的 Python 包安装程序)将它们安装在一起。

使用 Pip 安装

Pip 是 Python 事实上的标准包管理系统,已经包含在 Python 安装包中。用 pip 可以轻松安装和管理 Python 库。

*最初使用 pip 的环境是 macOS 的终端和 Windows OS 的命令提示符。不过你也可以用 Jupyter 笔记本里面的 pip 和 Google Colab 稍微调整一下。这两个选项的区别只有一个感叹号(!).

|

终端和命令提示符

|

Jupyter 笔记本和 Google Colab

|
| --- | --- |
| pip install package-name | !pip install package-name |

如果您决定在本地 Jupyter 笔记本上安装本书,我们必须确保您的系统上安装了 pip。

Use of Pip in Google Colab

如果你使用的是推荐的 Google Colab,你就不用担心你的系统上有没有 pip。你可以在你的 Google Colab 笔记本中使用带有感叹号的 pip。

Pip 安装,或其 确认分三步完成:

  1. 打开 macOS 的终端 Windows OS 的命令提示符:

    1. 您可以从“其他”文件夹下的 Launchpad 打开终端窗口。

    2. 您可以打开命令行窗口,方法是(I)按 Windows+X 打开超级用户菜单,然后(ii)单击“命令提示符”或“命令提示符(管理)”

  2. 检查是否安装了 pip ,并使用以下脚本查看系统上安装的当前版本:

pip --version

  1. 如果终端/命令行没有返回版本信息,请使用以下命令安装 pip:

python -m pip install -U pip

如果它返回版本信息,那么您确认您的系统上安装了 pip。

  1. 关闭终端/命令行窗口。

安装 ——现在我们确认您的系统上已经安装了 pip,您可以使用表 4-2 中的以下脚本安装本章中提到的所有库。

表 4-2

Pip 安装脚本的补充库

|

图书馆

|

安装脚本

|
| --- | --- |
| NumPy | pip install numpy |
| 我的天啊 | pip install scipy |
| 熊猫 | pip install pandas |
| Matplotlib | pip install matplotlib |
| Scikit-learn | pip install scikit-learn |
| 瓶 | pip install flask |

Beware of Already Installed Packages

Google Colab 笔记本和 Jupyter 笔记本都已经预装了这些库。只需运行前面提到的脚本一次,以确保您已经安装了它们,这样在案例研究期间,万一它们中的一些丢失了,您就不会受到干扰。

现在我们确信您的系统中已经安装了这些库(Google Colab 或 Jupyter Notebook),我们可以深入了解这些库的细节了。

NumPy–数组处理

NumPy(数值 Python)是一个非常流行的开源数值 Python 库,由 Travis Oliphant 创建。NumPy 提供了多维数组以及大量用于数学运算的有用函数。

NumPy 充当 C 中实现的相应库的包装器。因此,它提供了两个世界的最佳状态:(I)C 的效率和(ii)Python 的易用性。NumPy 数组是易于创建的高效对象,用于(I)存储数据和(ii)快速矩阵运算。使用 NumPy,您可以快速生成包含随机数的数组,这对于增强学习体验和概念验证任务来说是完美的。此外,我们将在后面介绍的 Pandas 库严重依赖于 NumPy 对象,并且几乎是作为 NumPy 扩展来工作的。

多亏了 NumPy 阵列,我们可以轻松地处理大量数据和进行高级数学运算。与内置的 Python 序列相比,NumPy 的ndarray对象用更少的代码执行起来更快更有效。越来越多的库依赖 NumPy 数组来处理数据,这显示了 NumPy 的强大。由于深度学习模型通常是用数百万个数据点训练的,NumPy 阵列的大小和速度优势对于机器学习专家来说至关重要。

关于 NumPy 的有用信息

科学计算

SciPy 是一个开源的 Python 库,它包含一组用于数学、科学和工程研究的函数。SciPy 函数建立在 NumPy 库的基础上。SciPy 允许用户使用易于使用的语法来操作和可视化他们的数据。SciPy 是一个库,它提高了开发人员的数据处理和系统原型能力,并使 Python 与竞争系统如 MATLAB、IDL、Octave、R-Lab 和 SciLab 一样有效。因此,SciPy 的数据处理和原型函数集合进一步加强了 Python 作为通用编程语言已经确立的优势。

SciPy 的大量函数集合被组织到基于域的子包中。SciPy 子包必须与母 SciPy 库分开调用,例如

from scipy import stats, special

在表 4-3 中,您可以找到 SciPy 子包的列表。

表 4-3

SciPy 子包

|

子包

|

描述

|

子包

|

描述

|
| --- | --- | --- | --- |
| stats | 统计函数和分布 | linalg | 线性代数 |
| special | 特殊功能 | io | 输入和输出 |
| spatial | 空间数据结构和算法 | interpolate | 插值和平滑样条 |
| sparse | 稀疏矩阵和相关例程 | integrate | 积分和方程求解 |
| signal | 信号处理 | fftpack | 快速傅立叶变换例程 |
| optimize | 优化和求根例程 | constants | 物理和数学常数 |
| odr | 正交距离回归 | cluster | 聚类算法 |
| ndimage | n 维图像处理 |   |   |

关于 的有用信息 SciPy

pandas–阵列处理和数据分析

Pandas 是一个 Python 库,它提供了灵活且富于表现力的数据结构,适合执行快速的数学运算。Python 是一个全面且易于使用的数据分析库,它的目标是成为领先的开源语言中立的数据分析工具。

一维序列和二维数据帧是 pandas 中的两种主要数据结构。因为它扩展了 NumPy 的功能,并且是建立在 NumPy 之上的,Pandas 几乎像 NumPy 的扩展一样运行。Pandas 还提供了几种数据可视化方法,这对于从数据集中获得洞察力非常有用。

您可以使用 Pandas 分析您的数据并执行多项计算任务。以下是你可以对熊猫做的事情的非详尽列表:

  • 通过填充和删除来处理缺失数据

  • 由于允许可变性,数据插入和删除

  • 自动和显式数据对齐

  • 分组依据和排序依据功能

  • 轻松地将无组织的对象转换为数据帧

  • 切片、索引和子集操作

  • 合并、连接和联接操作

  • 重塑和透视操作

  • 分级和多重标记

  • 时序和序列数据的特定操作

  • 强大的输入和输出操作,支持广泛的文件格式(包括 CSV、XLSX、HTML、HDF5)

由于 Pandas 是 NumPy 的事实上的扩展,提高了它的能力,我们比 NumPy 更经常地利用 Pandas。但是在某些情况下,由于其他补充库的限制,我们不得不依赖 NumPy。

关于熊猫的有用信息

Matplotlib 和 Seaborn–数据可视化

Matplotlib 是一个 Python 数据可视化库,用于创建静态的、动态的和交互式的图形和绘图。您可以为学术出版物、博客和书籍制作高质量的绘图,还可以使用 Matplotlib 从大型数据集获得见解。

除了使用 Google Colab 笔记本获得洞察力,您还可以使用 Matplotlib 的面向对象 API 将绘图嵌入到应用程序中。Matplotlib 的三个主要功能如下:

  • 创建:使用 Matplotlib,您可以用最少的代码创建高质量的绘图。Matplotlib 提供的图形类型总数超过数百种——从直方图到热度图,从条形图到曲面图。

  • 自定义 : Matplotlib 绘图非常灵活,您可以自定义线型、字体属性、颜色和轴信息。您可以从图中导出并将数据嵌入到图中。

  • Extend :你可以利用大量的第三方库来扩展 Matplotlib。其中一些库也极其有用,比如 Seaborn

您可以使用 Matplotlib 做的事情如下所示:

  • 使用 PyPlot 模块并创建交互式绘图。

  • 使用线条、条形图、标记和其他对象创建数百种不同的图形和绘图。

  • 创建独特的图,如曲面图和等高线图。

  • 向图中添加图像和字段。

  • 在一个图形下创建多个支线剧情。

  • 灵活编辑绘图中的文本、轴、颜色、标签和注释。

  • 使用 Matplotlib 创建一个或多个形状。

  • 创建展示图。

  • 利用动画支持。

关于 Matplotlib 的有用信息

除了普通的 Matplotlib,第三方软件包也被广泛用于增加 Matplotlib 的功能。在 Matplotlib 之上构建的一个有用的数据可视化库是 Seaborn。Seaborn 是一个基于 Matplotlib 的数据可视化库。它为扩展 Matplotlib 的功能提供了一个高级接口。使用 Seaborn,您可以减少生成有洞察力的图表所需的时间。

关于 Seaborn 的有用信息

sci kit-learn–机器学习

Scikit-learn 是一个强大的开源 Python 机器学习库,最初由 David Cournapeau 开发,作为谷歌代码之夏项目。你可以使用 scikit-learn 作为一个独立的机器学习库,并成功构建各种传统的机器学习模型。除了能够创建机器学习模型,scikit-learn 构建在 NumPy、SciPy 和 Matplotlib 之上,为预测数据分析提供了简单高效的工具。scikit-learn 有六个主要功能,如下所示:

  • 分类 : Scikit-learn 提供了几种算法来识别一个对象属于哪个类别,比如支持向量机、逻辑回归、k 近邻、决策树等等。

  • 回归:scikit-learn 提供的几种算法可以预测与对象相关联的连续值响应变量,例如线性回归、梯度推进、随机森林、决策树等等。

  • 聚类 : Scikit-learn 还提供聚类算法,用于将相似的对象自动分组到聚类中,比如 k-means 聚类、谱聚类、均值漂移等等。

  • 降维 : Scikit-learn 提供了几种算法来减少要考虑的解释变量的数量,比如 PCA、特征选择、非负矩阵分解等等。

  • 模型选择 : Scikit-learn 可以帮助模型验证和比较,也可以帮助选择参数和模型。你可以将你的 TensorFlow 模型与 scikit-learn 的传统机器学习模型进行比较。网格搜索、交叉验证和指标是用于模型选择和验证功能的一些工具。

  • 预处理:通过预处理、特征提取和特征缩放选项,您可以在 TensorFlow 不足的地方转换您的数据。

当我们想要将我们的深度学习模型与其他机器学习算法进行比较时,Scikit-learn 特别有用。此外,通过 scikit-learn,我们可以在将数据输入深度学习流水线之前对其进行预处理。

关于 Scikit 的有用信息-了解

烧瓶-部署

与前面提到的库不同,Flask 不是一个数据科学库,但它是 Python 的一个微型 web 框架。它被认为是一个微框架,因为它没有和其他 web 框架认为必不可少的组件打包在一起,比如数据库抽象层和表单验证。这些组件可以嵌入带有强大的第三方扩展的 Flask 应用程序中。这一特性使得 Flask 变得简单和轻量,并减少了开发时间。如果你想为你训练好的深度学习模型服务,又不想花太多时间在 web 编程上,Flask 是一个完美的选择。

与 Django 相反,Flask 易于学习和实现。Django 是一个非常完善的、流行的 Python web 框架。但是由于它的体积很大,内置了很多扩展包,Django 对于大型项目来说是一个更好的选择。目前,Flask 在其 GitHub repo 上的星级比 Python 的任何其他 web 框架都多,并在 2018 年 Python 开发者调查中被评为最受欢迎的 web 框架。

关于烧瓶的有用信息

最终评估

在这一章中,我们将介绍 TensorFlow 最常用的补充库。我们主要使用 TensorFlow,因为它有越来越多的模块可以满足开发人员在开发过程中每一步的需求。但是,仍然有一些操作我们不得不依赖这些库。

虽然 NumPy 和 Pandas 是非常强大的数据处理库,但 Matplotlib 和 Seaborn 对于数据可视化非常有用。虽然 SciPy 帮助我们进行复杂的数学运算,但 scikit-learn 对高级预处理操作和验证任务特别有用。最后,Flask 是我们选择的 web 框架,可以快速为我们训练好的模型提供服务。

在下一章中,我们将通过实际的代码示例深入研究 TensorFlow 模块。*

五、TensorFlow 2.0 和深度学习流水线指南

在前面的章节中,我们在深入研究深度学习应用之前介绍了基础知识。

  • 第一章有助于理解选择 Python 和 TensorFlow 等技术背后的原因。它还帮助我们建立了我们的环境。

  • 第二章对机器学习做了一个简单的介绍,因为深度学习是机器学习的一个子领域。

  • 在第三章,我们终于涵盖了深度学习的基础知识。这三章是概念性和介绍性的章节。

  • 第四章总结了我们在深度学习流水线中使用的所有技术,除了一项:TensorFlow。

在本章中,我们将介绍 TensorFlow 的基础知识以及本书中使用的 API 参考。

TensorFlow Basics

本章的主要重点是我们如何将 TensorFlow 用于神经网络和模型训练,但首先,我们需要涵盖 TensorFlow 基础知识下的几个主题,它们是

  • 急切执行与图形执行

  • 张量洛常数

  • tensorflow 变量

急切的执行

TensorFlow 2.0 带来的一个新特性是将急切执行作为默认选项。通过快速执行,TensorFlow 计算代码中出现的张量值。急切执行简化了 TensorFlow 中的模型构建体验,您可以立即看到 TensorFlow 操作的结果。

这种转变背后的主要动机是 PyTorch 的动态计算图形能力。借助动态计算图形功能,PyTorch 用户能够遵循通过运行定义的方法,在这种方法中,您可以立即看到操作的结果。

然而,对于图形执行,TensorFlow 1.x 遵循了一种定义并运行的方法,在这种方法中,只有在我们用tf.Session包装了代码之后才会进行评估。图形执行在分布式培训、性能优化和生产部署方面具有优势。但是由于实现的困难,图形执行也把新来者驱赶到 PyTorch。因此,新来者的这一困难导致 TensorFlow 团队采用急切执行,即 TensorFlow 的按运行定义的方法,作为默认的执行方法。

在本书中,我们只使用默认的急切执行进行模型构建和训练。

张量

张量是 TensorFlow 内置的统一类型的多维数组。它们非常类似于 NumPy 数组,并且它们是不可变的,这意味着一旦创建,它们就不能被更改,并且您只能用编辑创建一个新的副本。

张量根据其维数进行分类:

  • 秩-0(标量)张量:包含单个值且没有轴的张量

  • 秩 1 张量:一个包含单轴中一系列值的张量

  • 秩 2 张量:包含两个轴的张量

  • 秩 N 张量:包含 N 轴的张量

例如,可以创建一个秩为 3 的张量,并打印出以下几行:

rank_3_tensor = tf.constant([
  [[0, 1, 2, 3, 4],
   [5, 6, 7, 8, 9]],
  [[10, 11, 12, 13, 14],
   [15, 16, 17, 18, 19]],
  [[20, 21, 22, 23, 24],
   [25, 26, 27, 28, 29]],])

print(rank_3_tensor)

您可以使用以下功能访问关于tf.Tensor对象的详细信息:

print("Type of every element:", rank_3_tensor.dtype)
print("Number of dimensions:", rank_3_tensor.ndim)
print("Shape of tensor:", rank_3_tensor.shape)
print("Elements along axis 0 of tensor:", rank_3_tensor.shape[0])
print("Elements along the last axis of tensor:", rank_3_tensor.shape[-1])
print("Total number of elements (3*2*5): ", tf.size(rank_3_tensor).numpy())
Output:
Type of every element: <dtype: 'int32'>
Number of dimensions: 3
Shape of tensor: (3, 2, 5)
Elements along axis 0 of tensor: 3
Elements along the last axis of tensor: 5
Total number of elements (3*2*5):  30

有几个函数可以创建张量对象。除了tf.Constant(),我们经常使用tf.ones()tf.zeros()函数来创建给定大小的只有 1 或 0 的张量。以下几行提供了两者的示例:

zeros = tf.zeros(shape=[2,3])
print(zeros)
Output:
tf.Tensor(
[[0\. 0\. 0.]
 [0\. 0\. 0.]], shape=(2, 3), dtype=float32)

ones = tf.ones(shape=[2,3])
print(ones)
Output:
tf.Tensor(
[[1\. 1\. 1.]
 [1\. 1\. 1.]], shape=(2, 3), dtype=float32)

基本的tf.Tensor类要求张量是矩形的,这意味着沿着每个轴,每个元素都有相同的大小。然而,有一些特殊类型的张量可以处理不同的形状:

  • 不规则张量:沿某轴具有可变数量元素的张量

  • 稀疏张量:一种数据稀疏的张量,就像一个非常宽的嵌入空间

可变的

TensorFlow 变量是表示共享的持久状态的推荐方法,您可以使用模型来操作该状态。TensorFlow 变量被记录为一个tf.Variable对象。一个tf.Variable对象代表一个值可以改变的张量,与普通的 TensorFlow 常数相反。tf.Variable对象用于存储模型参数。

TensorFlow 变量与 TensorFlow 常量非常相似,但有一个显著的区别:变量是可变的。因此,可变对象的值可以改变(例如,用assign()函数)以及可变对象的形状也可以改变(例如,用reshape()函数)。

您可以使用以下代码创建一个基本变量:

a = tf.Variable([2.0, 3.0])

您也可以使用现有的常数来创建变量:

my_tensor = tf.constant([[1.0, 2.0], [3.0, 4.0]])
my_variable = tf.Variable(my_tensor)
print(my_variable)
Output:
<tf.Variable 'Variable:0' shape=(2, 2) dtype=float32, numpy=
array([[1., 2.],
       [3., 4.]], dtype=float32)>

您可以使用tensor.numpy()函数将 TensorFlow 变量对象或 TensorFlow 张量对象转换为 NumPy 数组,如下所示:

my_variable.numpy()
my_tensor.numpy()

这些是 TensorFlow 中的一些基本概念。现在,我们可以继续使用 TensorFlow 进行模型构建和数据处理。

TensorFlow 深度学习流水线

在第二章的最后一节,我们列出了一个完整的机器学习流水线的步骤(即获得经过训练的机器学习模型的步骤)。在深度学习模型中,我们几乎只使用相同的流水线,其中有大量 TensorFlow 的工作。图 5-1 显示了我们的流水线是如何工作的(请注意,您可能会遇到不同来源的细微变化)。

img/501289_1_En_5_Fig1_HTML.jpg

图 5-1

用 TensorFlow 构建的深度学习流水线

在接下来的部分中,我们将通过代码示例来介绍这些步骤。请注意,数据收集步骤将被省略,因为它通常被视为一项单独的任务,通常不由机器学习专家来执行。

数据加载和准备

在建立和训练神经网络之前,深度学习的第一步是加载你的数据,处理它,并将其馈送给神经网络。我们在下一章讨论的所有神经网络都需要数据,为此,我们需要以正确的格式输入数据。我们的 TensorFlow 模型接受几种对象类型,如下所示:

  • TensorFlow 数据集对象

  • TensorFlow 数据集目录

  • NumPy 数组对象

  • 熊猫数据帧对象

让我们深入了解如何使用它们。

数据集对象(tf.data.Dataset)

TensorFlow 数据集对象表示一大组元素(即数据集)。tf.data.Dataset API 是 TensorFlow 接受的模型输入对象之一,用于训练并专门为输入流水线设计。

您可以将数据集 API 用于以下目的:

  • 根据给定的数据创建数据集。

  • 使用集合函数(如地图)转换数据集。

  • 迭代数据集并处理单个元素。

Dataset API 支持各种文件格式和 Python 对象,可以用来创建tf.data.Dataset对象。让我们来看看这些受支持的文件格式和对象:

  • 来自 Python 列表、NumPy 数组、Pandas DataFrame 和from_tensor_slices函数的数据集

  • 使用TextLineDataset函数从文本文件中获取数据集

ds = tf.data.Dataset.from_tensor_slices([1, 2, 3])
ds = tf.data.Dataset.from_tensor_slices(numpy_array)
ds = tf.data.Dataset.from_tensor_slices(df.values)

  • 来自 TensorFlow 的 TFRecord 格式的数据集,带有TFRecordDataset函数
ds = tf.data.TextLineDataset("file.txt")

  • 包含以下内容的 CSV 文件中的数据集
ds = tf.data.TFRecordDataset("file.tfrecord")

  • 来自 TensorFlow 数据集目录的数据集:这将在下一节中介绍。
ds = tf.data.experimental.make_csv_dataset( "file.csv", batch_size=5)

TensorFlow 数据集目录

TensorFlow 数据集是由 TensorFlow 维护的流行数据集的集合。他们通常是干净的,随时可以使用。

装置

TensorFlow 数据集存在于两个包中:

  • 稳定版,每几个月更新一次

  • tfds-nightly:每夜发布的版本,包含数据集的最新版本

正如您从名称中可以理解的那样,您可以使用稳定版本,它更新的频率较低,但更可靠,也可以使用每夜发布的版本,它提供对数据集最新版本的访问。但是请注意,由于频繁的发布,tfds-nightly更容易崩溃,因此不建议在生产级项目中使用。

如果您的系统上没有,您可以使用以下命令行脚本安装这些软件包:

pip install tensorflow_datasets
pip install tfds-nightly

进口

这些包是通过tensorflow_datasets加载的,通常缩写为tfds。为了能够导入这些包,您只需运行一行代码,如下所示:

import tensorflow_datasets as tfds

数据集目录

导入主库后,我们可以使用加载功能导入 TensorFlow 数据集目录页面中列出的常用库之一,该页面可在上访问

www.tensorflow.org/datasets/catalog/overview

在此目录下,您可能会找到几十个数据集,它们属于以下列出的组之一:

  • 声音的

  • 图像

  • 图像分类

  • 目标检测

  • 问题回答

  • 结构化的

  • 摘要

  • 文本

  • 翻译

  • 录像

加载数据集

从 TensorFlow 数据集目录加载数据集的最简单方法是使用加载函数。该功能将

  • 下载数据集。

  • 将其保存为TFRecord文件。

  • TFRecord文件加载到您的笔记本上。

  • 创建一个tf.data.Dataset对象,可以用来训练一个模型。

以下示例显示了如何使用 load 函数加载数据集:

mnist_dataset = tfds.load(‘mnist’, split=‘train’)

您可以通过设置加载函数的特定参数来自定义加载过程:

  • split :控制加载数据集的哪一部分

  • shuffle_files :控制是否在每个历元之间对文件进行洗牌

  • data_dir :控制数据集保存的位置

  • with_info :控制是否加载DatasetInfo对象

在接下来的章节中,我们将在很大程度上利用这个目录。

硬数据集

除了 TensorFlow 数据集目录,Keras 还提供对其目录中列出的有限数量的数据集的访问,可通过 https://keras.io/api/datasets/ 访问。该目录下可访问的数据集有

  • 梦妮丝

  • CIFAR10

  • cifar 100 足球俱乐部

  • IMDB 电影评论

  • 路透社通讯社

  • 时尚 MNIST

  • 波士顿住房公司

如你所见,这个目录非常有限,但在你的研究项目中会派上用场。

您可以使用load_data()函数从 Keras API 加载数据集,如下所示:

(x_train, y_train), (x_test, y_test)= tf.keras.datasets.mnist.load_data( path="mnist.npz" )

Keras 数据集与 TensorFlow 数据集的一个重要区别是,它们是作为 NumPy 数组对象导入的。

NumPy 阵列

TensorFlow 接受作为输入数据的数据类型之一是 NumPy 数组。如前一章所述,您可以用下面一行导入 NumPy 库:

import numpy as np

可以用np.array()函数创建一个 NumPy 数组,可以馈入 TensorFlow 模型。您还可以使用一个函数,比如np.genfromtxt(),从 CSV 文件中加载数据集。

实际上,我们很少使用 NumPy 函数来加载数据。对于这个任务,我们经常利用 Pandas 库,它几乎是一个 NumPy 扩展。

熊猫数据框

TensorFlow 和 NumPy 数组也接受 Pandas 数据帧和系列对象。熊猫和熊猫之间有很强的联系。为了处理和清理我们的数据,熊猫经常提供更强大的功能。然而,NumPy 数组通常更有效,并在更大程度上被其他库所识别。例如,您可能需要使用 scikit-learn 来预处理您的数据。Scikit-learn 将接受 Pandas 数据帧和 NumPy 数组,但只返回 NumPy 数组。因此,一个机器学习专家必须学会使用这两个库。

您可以导入 Pandas 库,如下所示:

import pandas as pd

您可以轻松地从 CSV、Excel 和 text 等不同格式的文件中加载数据集,如下所示:

  • CSV 文件 : pd.read_csv("path/xyz.csv")

  • Excel 文件 : pd.read_excel("path/xyz.xlsx")

  • 文本文件 : pd.read_csv("path/xyz.txt")

  • HTML 文件 : pd.read_html("path/xyz.html")pd.read_html('URL')

从这些不同的文件格式中加载数据集后,Pandas 为您提供了大量不同的功能,您还可以使用pandas.DataFrame.head()pandas.DataFrame.tail()函数检查数据处理操作的结果。

其他对象

随着 TensorFlow 新版本的推出,支持的文件格式数量也在增加。此外,TensorFlow I/O 是一个扩展库,通过其 API 进一步扩展了受支持库的数量。虽然我们前面提到的受支持的对象和文件格式已经足够了,但是如果您对其他格式感兴趣,可以访问 TensorFlow I/O 的官方 GitHub 存储库

TensorFlow I/O: https://github.com/tensorflow/io#tensorflow-io

模型结构

在加载和处理数据集之后,下一步是建立深度学习模型进行训练。我们有两个主要选项来构建模型:

  • 响亮的接口

  • 估计器 API

在本书中,我们只使用 Keras API,因此,重点放在用 Keras API 构建模型的不同方式上。

响亮的接口

如前几章所述,Keras 是 TensorFlow 的补充库。此外,tensor flow2.0 版采用 Keras 作为内置 API 来构建模型和实现附加功能。

TensorFlow 2.x 下的 Keras API 提供了三种不同的方法来实现神经网络模型:

  • 顺序 API

  • 功能 API

  • 模型子类化

下面我们来看看每种方法。

顺序 API

Keras Sequential API 允许您逐步构建神经网络。您可以创建一个Sequential()模型对象,并且可以在每一行添加一个层。

使用 Keras Sequential API 是构建模型的最简单的方法,但需要付出代价:有限定制。尽管您可以在几秒钟内构建一个顺序模型,但是顺序模型不提供某些功能,例如(I)层共享,(ii)多个分支,(iii)多个输入,以及(iv)多个输出。当我们有一个带有一个输入张量和一个输出张量的简单层叠时,顺序模型是最佳选择。

使用 Keras Sequential API 是构建神经网络的最基本的方法,这对于接下来的许多章节来说已经足够了。但是,要构建更复杂的模型,我们需要使用 Keras Functional API 和模型子类化选项。

使用 Keras Sequential API 构建一个基本的前馈神经网络可以通过以下代码实现:

model = Sequential()
model.add(Flatten(input_shape=(28, 28)))
model.add(Dense(128,'relu'))
model.add(Dense(10, "softmax"))

或者,我们可以将一个层列表传递给顺序构造函数:

model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128,'relu'),
    Dense(10, "softmax"),
  ])

一旦构建了顺序模型,它的行为就像一个功能 API 模型,为每一层提供一个输入属性和一个输出属性。

在我们的案例研究中,我们利用其他属性和函数,如model.layersmodel.summary()来理解我们的神经网络的结构。

功能 API

Keras Functional API 是一个更健壮且稍微复杂的 API,用于使用 TensorFlow 构建强大的神经网络。我们用 Keras Functional API 创建的模型比我们用 Keras Sequential API 创建的模型更灵活。它们可以处理非线性拓扑、共享层,并且可以有多个分支、输入和输出。

Keras Functional API 方法源于这样一个事实,即大多数神经网络是层的有向无环图(DAG)。因此,Keras 团队开发了 Keras Functional API 来设计这种结构。Keras Functional API 是构建图层图表的好方法。

为了用 Keras Functional API 创建一个神经网络,我们创建一个输入层并将其连接到第一层。下一层与上一层相连,以此类推。最后,Model对象将输入和连接的层堆栈作为参数。

Keras 顺序 API 中的示例模型可以使用 Keras 功能 API 来构建,如下所示:

inputs = tf.keras.Input(shape=(28, 28))
x = Flatten()(inputs)
x = Dense(128, "relu")(x)
outputs = Dense(10, "softmax")(x)
model = tf.keras.Model(inputs=inputs,
                        outputs=outputs,
                        name="mnist_model")

就像在 Keras Sequential API 中一样,我们可以使用 layers 属性,summary()函数。此外,我们还可以用下面这条线将模型绘制成图形:

tf.keras.utils.plot_model(model)

模型和层子类化

模型子类化是最先进的 Keras 方法,它给了我们无限的灵活性,可以从零开始构建神经网络。您也可以使用层子类化来构建自定义层(即模型的构建块),您可以在神经网络模型中使用这些自定义层。

通过模型子类化,我们可以构建定制的神经网络来进行训练。Keras 模型类内部是用于定义模型架构的根类。

模型子类化的好处是它是完全可定制的,而它的缺点是实现困难。因此,如果您试图构建奇异的神经网络或进行研究级别的研究,那么模型子类化方法是一条可行之路。但是,如果您可以使用 Keras 顺序 API 或 Keras 功能 API 来完成您的项目,您就不应该为模型子类化而烦恼。

您看到的前面的例子可以用模型子类化来重写,如下所示:

class CustomModel(tf.keras.Model):
  def __init__(self, **kwargs):
    super(CustomModel, self).__init__(**kwargs)
    self.layer_1 = Flatten()
    self.layer_2 = Dense(128, "relu")
    self.layer_3 = Dense(10, "softmax")

  def call(self, inputs):
    x = self.layer_1(inputs)
    x = self.layer_2(x)
    x = self.layer_3(x)
    return x

model = CustomModel(name=' mnist_model')

模型子类化有两个关键功能:

  • __init__函数作为一个构造函数。多亏了__init__,我们可以初始化模型的属性(例如,层)。

  • super函数用于调用父构造函数(tf.keras.Model)。

  • self对象用于引用实例属性(如层)。

  • call功能是在__init__功能中定义层后定义操作的地方。

在前面的示例中,我们在 init 函数下定义了密集层,然后将它们创建为对象,并构建了我们的模型,类似于我们使用 Keras Functional API 构建神经网络的方式。但是请注意,您可以在模型子类化中构建您的模型。

我们可以通过使用自定义类(自定义模型)生成一个对象来完成我们的模型构建,如下所示:

model = CustomModel(name='mnist_model')

我们在第 10 和 11 章中使用模型子类化。

估计器 API

Estimator API 是一个高级 TensorFlow API,它封装了以下功能:

  • 培养

  • 估价

  • 预报

  • 出口供食用

我们可以利用各种预制的估算器,也可以用估算器 API 编写自己的模型。Estimator API 比 Keras APIs 有一些优势,比如基于参数服务器的训练和完全的 TFX 集成。然而,Keras APIs 将很快具备这些功能,这使得 Estimator API 成为可选的。

这本书不包括案例研究中的评估者 API。因此,我们不赘述。但是,如果您有兴趣了解有关估算器 API 的更多信息,请访问位于 www.tensorflow.org/guide/estimator 的 TensorFlow 估算器 API 指南。

编译、训练和评估模型并进行预测

编译是深度学习模型训练的重要部分,其中我们定义了我们的(I)优化器,(ii)损失函数,以及其他参数,例如(iii)回调。另一方面,训练是我们开始将输入数据输入到模型中的步骤,以便模型可以学习推断隐藏在数据集中的模式。评估是我们检查我们的模型的常见深度学习问题(如过拟合)的步骤。

有两种方法来编译、训练和评估我们的模型:

  • 使用标准方法

  • 编写自定义训练循环

标准方法

当我们遵循标准训练方法时,我们可以受益于以下功能:

  • model.compile()

  • model.fit()

  • model.evaluate()

  • model.predict()

模型.编译( )

model.compile()是我们在训练之前设置优化器、损失函数和性能指标的函数。这是一个非常简单的步骤,只用一行代码就可以完成。另外,请注意,有两种方法可以在model.compile()函数中传递损失函数和优化器参数,举例如下:

  • 选项 1 :将参数作为字符串传递

  • 选项 2 :将参数作为 TensorFlow 对象传递

model.compile(
optimizer='adam',
loss=mse,
metrics=['accuracy'])

model.compile(
optimizer=tf.keras.optimizers.Adam() ,
loss=tf.keras.losses.MSE(),
metrics=[tf.keras.metrics.Accuracy()])

将损失函数、指标和优化器作为对象传递比选项 1 更灵活,因为我们还可以在对象中设置参数。

【计算机】优化程序

TensorFlow 支持的优化器算法如下:

  • 阿达德尔塔

  • 阿达格拉德

  • 圣经》和《古兰经》传统中)亚当(人类第一人的名字

  • 阿达玛斯

  • 稀疏性

  • 那达慕

  • RMSProp

  • 签名于

最新列表可在以下网址找到:

www.tensorflow.org/api_docs/python/tf/keras/optimizers

您可以通过tf.keras.optimizers模块选择一个优化器。

损失函数

在开始训练之前,必须设置的另一个重要参数是损失函数。tf.keras.losses模块支持许多适用于分类和回归任务的损失函数。完整列表可在以下网址找到:

www.tensorflow.org/api_docs/python/tf/keras/losses

model.fit()

model.fit()为固定数量的历元(数据集上的迭代)训练模型。它需要几个参数,比如 epochs、callbacks 和 shuffle,但是它还必须接受另一个参数:我们的数据。根据问题的不同,这些数据可能是(I)仅要素或(ii)要素和标签。model.fit()功能的使用示例如下:

model.fit(train_x, train_y, epochs=50)

模型.评估( )

model.evaluate()函数使用测试数据集返回模型的损失值和度量值。它返回的内容和接受的参数类似于model.fit()函数,但是它不再进一步训练模型。

model.evaluate(test_x, test_y)

模型.预测( )

model.predict()是我们用来做单项预测的函数。model.evaluate()功能需要标签,而model.predict()功能不需要标签。它只是使用训练好的模型进行预测,如下所示:

model.evaluate(sample_x)

定制培训

您可以完全定制该流程,而不是遵循允许您使用model.compile()model.fit()model.evaluate()model.predict()等功能的标准培训选项。

为了能够定义一个定制的训练循环,你必须使用一个tf.GradientTape()tf.GradientTape()记录自动微分的操作,这对于训练时实现反向传播等机器学习算法非常有用。换句话说,tf.GradientTape()允许我们追踪 TensorFlow 计算和计算梯度。

对于定制培训,我们遵循以下步骤:

  • 设置优化器、损失函数和指标。

  • 运行一个 for 循环来计算历元数。

    • 对每个时期的每个批次运行嵌套循环:

    • tf.GradientTape()一起计算和记录损失,并进行反向传播。

    • 运行优化程序。

    • 计算、记录和打印指标结果。

以下几行显示了标准培训方法的示例。只需两行代码,您就可以配置和训练您的模型。

model.compile(optimizer=Adam(), loss=SCC(from_logits=True), metrics=[SCA()])
model.fit(x_train, y_train, epochs=epochs)

另一方面,下面几行显示了如何使用定制的训练循环获得相同的结果。

# Instantiate optimizer, loss, and metric
optimizer, loss_fn, accuracy = Adam(), SCC(from_logits=True), SCA()
# Convert NumPy to TF Dataset object
train_dataset = (Dataset.from_tensor_slices((x_train, y_train)).shuffle(buffer_size=1024).batch(batch_size=64))

for epoch in range(epochs):
    # Iterate over the batches of the dataset.
    for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
        # Open a GradientTape to record the operations, which enables auto-differentiation.
        with tf.GradientTape() as tape:
            # The operations that the layer applies to its inputs are going to be recorded
            logits = model(x_batch_train, training=True)
            loss_value = loss_fn(y_batch_train, logits)
        # Use the tape to automatically retrieve the gradients of the trainable variables
        grads = tape.gradient(loss_value, model.trainable_weights)
        # Run one step of gradient descent by updating
        # the value of the variables to minimize the loss.
        optimizer.apply_gradients(zip(grads, model.trainable_weights))
    # Metrics related part
        accuracy.update_state(y_batch_train, logits)
        if step % int(len(train_dataset)/5) == 0: #Print out
          print(step, "/", len(train_dataset)," | ",end="")
    print("\rFor Epoch %.0f, Accuracy: %.4f" % (epoch+1, float(accuracy.result()),))
    accuracy.reset_states()

如您所见,这要复杂得多,因此,您应该只在绝对必要时使用定制培训。您也可以自定义个人训练步骤、model.evaluate()功能,甚至model.predict()功能。因此,TensorFlow 几乎总是为研究人员和定制模型开发人员提供足够的灵活性。在本书中,我们利用了第十二章中的定制培训。

保存和加载模型

我们刚刚学习了如何建立一个神经网络,这些信息对于接下来章节的案例研究至关重要。但是我们也想在现实世界的应用中使用我们训练的模型。因此,我们需要保存我们的模型,以便它可以被重用。

我们可以将整个模型保存到一个工件中。当我们保存整个模型时,它包含

  • 模型的架构和配置数据

  • 模型的优化权重

  • 模型的编译信息(model.compile() info)

  • 优化程序及其最新状态

TensorFlow 提供了两种保存模型的格式:

  • tensorflow 存储模型格式

  • Keras HDF5(或 H5)格式

虽然以前旧的 HDF5 格式非常流行,但 SavedModel 已经成为 TensorFlow 中保存模型的推荐格式。HDF5 和 SavedModel 的关键区别在于,HDF5 使用对象配置来保存模型架构,而 SavedModel 保存执行图。这种差异的实际后果是显著的。SavedModels 可以保存定制对象,例如用模型子类化构建的模型或没有原始代码的定制层。为了能够以 HDF5 格式保存自定义对象,需要额外的步骤,这使得 HDF5 不那么吸引人。

保存模型

以其中一种格式保存模型非常容易。可以通过在model.save()函数中传递一个参数(save_format)来选择所需的格式。

要以 SavedModel 格式保存模型,我们可以使用下面一行:

model.save("My_SavedModel")

如果您想以 HDF5 格式保存您的模型,我们可以简单地使用带有save_format参数的相同函数:

model.save("My_H5Model", save_format="h5")

对于 HDF5 格式,您也可以使用 Keras 的save_model()函数,如下所示:

tf.keras.models.save_model("My_H5Model")

保存模型后,包含模型的文件可以在您的临时 Google Colab 目录中找到。

加载模型

保存模型文件后,您可以轻松地加载和重建之前保存的模型。为了加载我们的模型,我们使用 Keras API 提供的load_model()函数。以下代码行可用于加载以任一格式保存的模型:

import tensorflow as tf
reconstructed_model = tf.keras.models.load_model( 'My_SavedModel' )

您可以使用加载的模型,就像使用您训练的模型一样。以下几行是用于新训练模型的model.evaluate()函数的精确副本:

test_loss, test_acc = reconstructed_model.evaluate( x_test,  y_test, verbose=2)
print('\nTest accuracy:', test_acc)

结论

既然我们已经介绍了如何将 TensorFlow 用于我们的深度学习流水线以及一些 TensorFlow 基础知识,我们可以开始介绍不同类型的神经网络概念及其相应的案例研究。我们的第一个神经网络类型是前馈神经网络,或者换句话说,多层感知器。

六、前馈神经网络

在这一章中,我们将讨论神经网络最普通的版本,前馈神经网络。前馈神经网络是一组人工神经网络,其中神经元之间的连接不形成循环。神经元之间的连接是单向的,只从输入层通过隐藏层向前移动,然后输出。换句话说,这些网络之所以被称为前馈网络,是因为信息的流动是向前的。

Recurrent Neural Networks

我们将在第八章中讨论,是增加了双向功能的前馈神经网络的改进版本。因此,它们不再被视为前馈。

前馈神经网络主要用于监督学习任务。它们在也使用传统机器学习算法的分析应用和定量研究中特别有用。

前馈神经网络非常容易建立,但是它们在计算机视觉和自然语言处理(NLP)问题中不可扩展。此外,前馈神经网络没有对序列数据有用的记忆结构。为了解决可扩展性和内存问题,开发了替代的人工神经网络,如卷积神经网络和循环神经网络,这将在下一章中讨论。

你可能会遇到前馈神经网络的不同名称,如人工神经网络、常规神经网络、常规网络、多层感知器和其他一些网络。不幸的是,这里有一个歧义,但是在本书中,我们总是使用前馈神经网络这个术语。

深层和浅层前馈神经网络

每个前馈神经网络必须有两层:(I)输入层和(ii)输出层。前馈神经网络的主要目标是使用(I)输入层的输入值和(ii)输出层的最终输出值(通过将它们与标签值进行比较)来近似函数。

浅层前馈神经网络

当一个模型只有一个输入和一个输出层用于函数近似时,它被认为是一个浅层前馈神经网络。也称为单层感知器,如图 6-1 所示。

img/501289_1_En_6_Fig1_HTML.jpg

图 6-1

浅层前馈神经网络或单层感知器

浅前馈神经网络中的输出值直接从其权重与相应的输入值和一些偏差的乘积之和来计算。浅前馈神经网络对于近似非线性函数是无用的。为了解决这个问题,我们在输入层和输出层之间嵌入了隐藏层。

深度前馈神经网络

当前馈神经网络具有一个或多个隐藏层,使其能够近似更复杂的函数时,该模型被认为是深度前馈神经网络。也称为多层感知器,如图 6-2 所示。

img/501289_1_En_6_Fig2_HTML.jpg

图 6-2

深度前馈神经网络或多层感知器

一层中的每个神经元都连接到下一层中的神经元,并利用激活函数。

Universal Approximation Theory

表明前馈神经网络可以近似欧氏空间的紧致子集上的任何实值连续函数。该理论还暗示,当给定适当的权重时,神经网络可以代表所有潜在的功能。

由于深度前馈神经网络可以近似任何线性或非线性函数,因此它们被广泛用于现实世界的应用中,包括分类和回归问题。在本章的案例研究中,我们还构建了一个深度前馈神经网络,以获得可接受的结果。

前馈神经网络结构

在前向神经网络中,最左边的层称为输入层,由输入神经元组成。最右边的一层称为输出层,由一组输出神经元或单个输出神经元组成。中间的层称为隐藏层,具有几个神经元,确保非线性近似。

在前馈神经网络中,我们利用具有反向传播、激活函数、成本函数以及权重之上的附加偏差的优化器。这些术语已经在第三章中解释过,因此在此省略。更多细节请参见第三章。让我们更深入地看看前馈神经网络的层。

前馈神经网络中的层

如前所述,我们的通用前馈神经网络结构包括三种类型的层:

  • 输入层

  • 输出层

  • 许多隐藏的层

输入层

输入层是前馈神经网络的第一层,用于将数据输入网络。输入层不利用激活功能,它的唯一目的是将数据输入系统。输入层中神经元的数量必须等于输入系统的特征(即解释变量)的数量。例如,如果我们使用五个不同的解释变量来预测一个响应变量,我们模型的输入层必须有五个神经元。

输出层

输出层是前馈神经网络的最后一层,用于输出预测。输出层中神经元的数量是根据问题的性质决定的。对于回归问题,我们的目标是预测单个值,因此,我们在输出层设置单个神经元。对于分类问题,神经元的数量等于类的数量。例如,对于二元分类,我们在输出层需要两个神经元,而对于具有五个不同类的多类分类,我们在输出层需要五个神经元。输出图层还根据问题的性质利用激活函数(例如,线性激活用于回归,softmax 用于分类问题)。

隐蔽层

创建隐藏层以确保非线性函数的近似。我们可以添加任意多的隐藏层,并且每层的神经元数量可以改变。因此,与输入和输出层相反,我们对隐藏层的使用要灵活得多。隐藏层是引入偏置项的合适层,偏置项不是神经元,而是添加到影响下一层中每个神经元的计算中的常数。隐藏层还利用了激活函数,如 Sigmoid、Tanh 和 ReLU。

在下一节中,我们将构建一个深度前馈神经网络来显示所有这些层的作用。由于 Keras 顺序 API,这个过程将非常容易。

案例研究|汽车 MPG 的燃油经济性

既然我们已经介绍了前馈神经网络的基础知识,我们可以构建一个深度前馈神经网络来预测一辆汽车用一加仑汽油可以行驶多少英里。这个术语通常指的是每加仑英里数(MPG)。对于这个案例研究,我们使用一个经典数据集:自动 MPG 数据集。自动 MPG 最初用于 1983 年美国统计协会博览会。该数据涉及城市循环油耗的预测,以英里/加仑为单位,包含三个多值离散属性和五个连续属性。对于这个案例研究,我们受益于 Keras 库的创建者 Franç ois Chollet 写的一个教程。 1

让我们深入研究代码。请通过 https://colab.research.google.com 创建一个新的 Colab 笔记本。

初始安装和导入

我们将利用 TensorFlow 文档库,它最初并不包含在 Google Colab 笔记本中。因此,我们从使用以下代码的库安装开始案例研究:

# Install tensorflow_docs
!pip install -q git+https://github.com/tensorflow/docs

在这个案例研究中,我们将使用许多库。让我们导入我们将在开始时使用的那些:

# Import the initial libraries to be used
import tensorflow as tf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

请注意,还会有一些其他的导入,会在它们对应的章节里分享。

下载自动 MPG 数据

尽管 Auto MPG 是一个非常受欢迎的数据集,但我们仍然无法通过 TensorFlow 的数据集模块访问该数据集。然而,有一种非常简单的方法(多亏了tf.keras.utils模块的get_file()函数)将外部数据加载到我们的 Google Colab 笔记本中,代码如下:

autompg = tf.keras.utils.get_file(
        fname='auto-mpg', #filename for local directory
        origin='http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data',#URL address to retrieve the dataset

注意,我们从 UCI 机器学习知识库中检索数据集。加州大学欧文分校提供了一个基本的存储库,以及 Kaggle,在其中您可以访问大量流行的数据集。

数据准备

当我们查看加州大学欧文分校的自动 MPG 页面时,我们可以看到一个代表自动 MPG 数据集中所有变量的属性列表,共享如下:

属性信息:

  • mpg :连续(响应变量)

  • 气缸:多值离散

  • 位移:连续

  • 马力:连续

  • 重量:连续

  • 加速度:连续

  • 年款:多值离散

  • 原点:多值离散

  • 汽车名称:字符串(每个实例唯一)

数据帧创建

作为最佳实践,我们将使用这些属性名称命名数据集列,并从 Google Colab 目录导入,因为我们已经在上一节中保存了它:

column_names = ['mpg', 'cylinders', 'displacement', 'HP', 'weight', 'acceleration', 'modelyear', 'origin']
df = pd.read_csv(autompg, # name of the csv file
        sep=" ", # separator in the csv file
        comment='\t', #remove car name sep. with '\t'
        names=column_names,
        na_values = '?', #NA values are coded as '?'
        skipinitialspace=True)
df.head(2) #list the first two row of the dataset

这里是df.head(2)的结果,如图 6-3 所示。

img/501289_1_En_6_Fig3_HTML.jpg

图 6-3

自动 MPG 数据集的前两行

删除空值

我们可以用下面的代码检查空值的数量:

df.isna().sum()

我们得到的输出如图 6-4 所示。

img/501289_1_En_6_Fig4_HTML.jpg

图 6-4

自动 MPG 数据集中的空值计数

我们在 HP 列中有六个空值。有几种方法可以处理空值。首先,我们可以放弃他们。其次,我们可以使用一种方法来填充它们,例如(a)用其他观测值的平均值填充,或者(b)使用回归方法来插值它们的值。为了简单起见,我们将使用以下代码删除它们:

df = df.dropna() # Drop null values
df = df.reset_index(drop=True) # Reset index to tidy up the dataset
df.show()

处理分类变量

让我们用 Pandas DataFrame 对象的 info 属性来查看我们的数据集:

df.info() # Get an overview of the dataset

如图 6-5 所示,我们可以看到 Auto MPG 数据集有 392 个汽车观察值,没有空值。变量气缸年款原点是我们应该考虑使用虚拟变量的分类变量。

img/501289_1_En_6_Fig5_HTML.jpg

图 6-5

自动 MPG 数据集概述

Dummy Variable

是一种特殊的变量类型,仅取值 0 或 1 来指示分类效果的存在与否。在机器学习研究中,分类变量的每个类别都被编码为虚拟变量。但是,省略其中一个类别作为虚拟变量是一个很好的做法,这可以防止多重共线性问题。

如果分类变量的值不表示数学关系,使用虚拟变量尤其重要。这对于原点变量绝对有效,因为值 1、2 和 3 代表美国、欧洲和日本。因此,我们需要为原点变量生成虚拟变量,删除第一个以防止多重共线性,并删除初始的原点变量(原点变量现在用生成的虚拟变量表示)。我们可以用下面几行代码来完成这些任务:

def one_hot_origin_encoder(df):
        df_copy = df.copy()
        df_copy['EU']=df_copy['origin'].map({1:0,2:1,3:0})
        df_copy['Japan']=df_copy['origin'].map({1:0,2:0,3:1})
        df_copy = df_copy.drop('origin',axis=1)
        return df_copy
df_clean = one_hot_origin_encoder(df)

这里是df_clean.head(2)的结果,如图 6-6 所示。

img/501289_1_En_6_Fig6_HTML.jpg

图 6-6

带有虚拟变量的自动 MPG 数据集的前两行

为训练和测试拆分自动 MPG

既然我们已经清理了数据集,是时候将它们分成训练集和测试集了。训练集用于训练我们的神经网络(即优化神经元权重)以最小化误差。测试集被用作从未见过的观察值来测试我们训练的神经网络的性能。

因为我们的数据集是熊猫 DataFrame 对象的形式,所以我们可以使用 sample 属性。我们将 80%的观察值用于训练,20%用于测试。此外,我们还将标签从特性中分离出来,这样我们就可以将特性作为输入。然后,用标签检查结果。

这些任务可以通过下面几行代码来完成:

# Training Dataset and X&Y Split
# Test Dataset and X&Y Split
# For Training
train = df_clean.sample(frac=0.8,random_state=0)
train_x = train.drop('mpg',axis=1)
train_y = train['mpg']
# For Testing
test = df_clean.drop(train.index)
test_x = test.drop('mpg',axis=1)
test_y = test['mpg']

既然我们已经将数据集分成了训练集和测试集,那么是时候规范化我们的数据了。如第三章所述,特征缩放是数据准备的重要部分。如果没有特征缩放,特征会对我们的模型产生负面影响。

我们需要提取平均值和标准偏差,以便手动对数据进行标准化。我们可以使用以下代码轻松生成该数据集:

train_stats = train_x.describe().transpose()

运行train_stats可以得到图 6-7 中的如下输出。

img/501289_1_En_6_Fig7_HTML.jpg

图 6-7

列车组统计的 train_stats 数据帧

既然我们已经有了训练集特征的平均值和标准偏差值,那么是时候规范化训练集和测试集了。自定义 规格化器(x) 功能可用于训练、测试和新观察集。

# Feature scaling with the mean
# and std. dev. values in train_stats
def normalizer(x):
  return (x-train_stats['mean'])/train_stats['std']
train_x_scaled = normalizer(train_x)
test_x_scaled = normalizer(test_x)

请注意,我们没有标准化标签(y)值,因为它们的大范围不会对我们的模型构成威胁。

模型构建和培训

现在,我们的数据被清理,并为我们的前馈神经网络流水线做准备。让我们建立我们的模型并训练它。

TensorFlow 导入

我们已经有了一些初始进口。在这一部分中,我们将导入剩余的模块和库来构建、训练和评估我们的前馈神经网络。

剩余的导入包括以下库:

# Importing the required Keras modules containing model and layers
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
# TensorFlow Docs Imports for Evaluation
import tensorflow_docs as tfdocs
import tensorflow_docs.plots
import tensorflow_docs.modeling

Sequential()是我们用于建模的 API,而Dense()是我们将在前馈神经网络中使用的层。tf.docs模块将用于模型评估。

具有顺序 API 的模型

Sequential API 创建一个模型对象并命名为model后,我们可以通过添加Dense()层来塑造我们的空模型。除了最后一个之外,每个密集层都需要一个激活函数。在这个案例研究中,我们将使用 ReLU,但也可以随意设置其他激活函数,如 Tanh 或 Sigmoid。我们的input_shape参数必须等于特征的数量,我们的输出层必须只有一个神经元,因为这是一个回归案例。

# Creating a Sequential Model and adding the layers
model = Sequential()
model.add(Dense(8,activation=tf.nn.relu, input_shape= [train_x.shape[1]])),
model.add(Dense(32,activation=tf.nn.relu)),
model.add(Dense(16,activation=tf.nn.relu)),
model.add(Dense(1))

我们可以用一行代码看到model的流程图;见图 6-8 :

tf.keras.utils.plot_model(model, show_shapes=True)

img/501289_1_En_6_Fig8_HTML.jpg

图 6-8

汽车 MPG 前馈神经网络流程图

模型配置

既然我们已经构建了神经网络的主要网络结构,我们需要在开始训练之前配置优化器、成本函数和指标。我们将在神经网络中使用 Adam 优化器和均方误差(MSE)。此外,TensorFlow 将为我们提供平均绝对误差(MAE)值以及 MSE 值。我们可以用下面的代码配置我们的模型:

# Optimizer, Cost, and Metric Configuration
model.compile(optimizer='adam',
              loss='mse',
              metrics=['mse','mae']
)

正如在第三章中提到的,对抗过拟合的一个强有力的方法是尽早停止。使用下面的代码行,如果我们在 50 个纪元内没有看到有价值的改进,我们将设置一个早期停止器。

# Early Stop Configuration
early_stop=tf.keras.callbacks.EarlyStopping( monitor="val_loss", patience=50)

既然我们已经配置了我们的模型,我们可以用我们的model对象的fit属性来训练我们的模型:

# Fitting the Model and Saving the Callback Histories
history=model.fit(
        x=train_x_scaled,
        y=train_y,
        epochs=1000,
        validation_split = 0.2,
        verbose=0,
        callbacks=[early_stop,
                tfdocs.modeling.EpochDots()
                ])

我们留出 20%的训练集进行验证。因此,我们的神经网络甚至会在测试集之前评估模型。我们将 epoch 值设置为 1000,但是如果它不能观察到验证损失/成本的有价值的改进,它将提前停止。最后,回调参数将为我们保存有价值的信息,以便用绘图和其他有用的工具来评估我们的模型。

评估结果

既然我们已经训练了模型,我们就可以评估结果了。我们的 TensorFlow 文档 库允许我们绘制每个时期的损失值。我们可以使用HistoryPlotter创建一个新的对象,用下面的代码创建这个对象:

plot_obj=tfdocs.plots.HistoryPlotter(smoothing_std=2)

创建对象后,我们可以使用 plot 属性来创建绘图,并且我们可以像在 Matplotlib 中一样使用以下代码来设置ylimylabel值:

plot_obj.plot({'Auto MPG': history}, metric = "mae")
plt.ylim([0, 10])
plt.ylabel('MAE [mpg]')

图 6-9 显示了我们在每个时期的损失值的概况。

img/501289_1_En_6_Fig9_HTML.jpg

图 6-9

该线图显示了每个时期的平均绝对误差值

有了模型的 evaluate 属性,我们还可以使用测试集来评估我们的模型。如图 6-10 所示,下面几行将使用我们的测试集生成损耗、MAE 和 MSE 值:

loss,mae,mse=model.evaluate(test_x_scaled,
                            test_y,
                            verbose=2)
print("Testing set Mean Abs Error: {:5.2f} MPG".format(mae))

img/501289_1_En_6_Fig10_HTML.jpg

图 6-10

汽车 MPG 训练模型的评估结果

我们可以用单行代码使用测试集标签来生成预测:

test_preds = model.predict(test_x_scaled).flatten()

最后,我们可以使用以下代码行绘制测试集标签(实际值)与测试集特性生成的预测(见图 6-11 )的对比图:

evaluation_plot = plt.axes(aspect='equal')
plt.scatter(test_y, test_preds)#Scatter Plot
plt.ylabel('Predictions [mpg]')#Y for Predictions
plt.xlabel('Actual Values [mpg]')#X for Actual Values
plt.xlim([0, 50])
plt.ylim([0, 50])
plt.plot([0, 50], [0, 50]) #line plot for comparison

img/501289_1_En_6_Fig11_HTML.jpg

图 6-11

实际测试标签与其预测值的散点图

我们还可以生成一个直方图,显示误差项在零附近的分布(见图 6-12 ),这是我们模型中偏差的一个重要指标。下面几行代码生成了上述直方图:

error = test_preds - test_y
plt.hist(error, bins = 25)
plt.xlabel("Prediction Error [mpg]")
plt.ylabel("Count")

img/501289_1_En_6_Fig12_HTML.jpg

图 6-12

直方图显示了零附近模型的误差分布

用新的观察做预测

我们之前生成的散点图和直方图都表明我们的模型是健康的,我们的损失值也在可接受的范围内。因此,我们可以使用我们训练好的模型,使用我们自己的虚拟观察来进行新的预测。

我将使用以下代码行创建一辆虚拟汽车:

# Prediction for Single Observation
# What is the MPG of a car with the following info:
new_car = pd.DataFrame([[8,  #cylinders
                         307.0, #displacement
                         130.0, #HP
                         5504.0, #weight
                         12.0, #acceleration
                         70, #modelyear
                         1 #origin
          ]], columns=column_names[1:])

该代码将创建以下带有单次观察的 Pandas 数据帧,如图 6-13 所示。

img/501289_1_En_6_Fig13_HTML.jpg

图 6-13

单次观测的熊猫数据框架

在输入训练模型之前,我们需要创建虚拟变量并使观察结果正常化。完成这些操作后,我们可以简单地使用模型的预测属性。我们可以用这些行来完成这些操作:

new_car = normalizer(one_hot_origin_encoder(new_car))
new_car_mpg = model.predict(new_car).flatten()
print('The predicted miles per gallon value for this car is:', new_car_mpg)

前面的代码给出了以下输出:

The prediction miles per gallon value for this car is: [14.727904]

结论

前馈神经网络是广泛用于分析应用和定量研究的人工神经网络。它们是最古老的人工神经网络,通常被称为多层感知器。它们被认为是人工神经网络家族的骨干。你可以在卷积神经网络的末端找到它们。循环神经网络是从前馈神经网络发展而来的,增加了双向性。

在下一章中,我们将深入探讨卷积神经网络,它是一组神经网络家族,广泛应用于计算机视觉、图像和视频处理等领域。

七、卷积神经网络

可以肯定地说,最强大的监督深度学习模型之一是卷积神经网络(缩写为 CNN 或 ConvNet)。CNN 是一类深度学习网络,多应用于图像数据。然而,CNN 结构可以用于各种现实世界的问题,包括但不限于,图像识别、自然语言处理、视频分析、异常检测、药物发现、健康风险评估、推荐系统和时间序列预测。

CNN 通过使用在训练数据中找到的更基本的模式来组合复杂的模式,实现了高水平的准确性。例如,从线条到眉毛,从两条眉毛到一张人脸,然后到一个完整的人的形象,CNN 可以通过使用纯粹的线条正确地检测图像中的人。为了组合这些模式,CNN 需要少量的数据准备,因为它们的算法自动执行这些操作。与用于图像处理的其他模型相比,CNN 的这一特性提供了优势。

今天,CNN 的整体架构已经简化。CNN 的最后一部分非常类似于前馈神经网络(正则网络,多层感知器),其中有带权重和偏差的完全连接的神经元层。就像在前馈神经网络中一样,在 CNN 中有一个损失函数(例如,交叉熵,MSE),多个激活函数和一个优化器(例如,SGD,Adam 优化器)。此外,尽管在 CNN 中,也有卷积层、汇集层和平坦层。

在下一节中,我们将看看为什么使用 CNN 进行图像处理是一个好主意。

Note

我通常会参考图像数据来举例说明 CNN 的概念。但是,请注意,这些示例仍然适用于不同类型的数据,如音频波或股票价格。

为什么选择卷积神经网络?

前馈神经网络的主要结构特征是所有神经元的层内连通性。例如,当我们有 28 x 28 像素的灰度图像时,我们最终在一个似乎易于管理的层中有 784 (28 x 28 x 1)个神经元。然而,大多数图像有更多的像素,而且它们不是灰度的。因此,当我们在 4K 超高清中有一组彩色图像时,我们最终在输入层中有 26,542,080 (4096 x 2160 x 3)个不同的神经元连接到下一层中的神经元,这是不可管理的。因此,我们可以说前馈神经网络对于图像分类是不可扩展的。然而,特别是当涉及到图像时,两个单独的像素之间似乎几乎没有相关性或关系,除非它们彼此靠近。这一重要发现引出了卷积层和池层的概念,在每个 CNN 架构中都可以找到。

CNN 架构

通常,在一个 CNN 架构中,开头有几个卷积层和池层,主要用于简化图像数据复杂度,减小其大小。此外,它们对于从图像中观察到的基本模式中提取复杂模式非常有用。在使用了几个卷积层和池层(由激活函数支持)之后,我们将二维或三维数组中的数据改造成一个带有扁平化层的一维数组。展平图层后,一组全连通图层以展平后的一维数组为输入,完成分类或回归任务。让我们分别来看看这几层。

CNN 中的层

我们能够在卷积神经网络中使用许多不同的层。然而,卷积层、池层和全连接层是最重要的层。因此,在我们的案例研究中实现这些层之前,让我们快速了解一下这些层。

卷积层

卷积层是我们从数据集的影像中提取特征的第一层。由于像素仅与相邻的和其他接近的像素相关,卷积允许我们保留图像不同部分之间的关系。卷积层的任务仅仅是用较小的像素滤波器对图像进行滤波,以减小图像的尺寸,而不丢失像素之间的关系。当我们使用步长为 1 x 1 的 3 x 3 像素滤波器对 5 x 5 像素图像进行卷积时(每步移动 1 像素),我们最终会得到 3 x 3 像素的输出(复杂度降低 64%),如图 7-1 所示。

img/501289_1_En_7_Fig1_HTML.jpg

图 7-1

5 x 5 像素图像与 3 x 3 像素过滤器的卷积(跨距= 1 x 1 像素)

过滤

通过将一部分图像数据中的每个值乘以相应的滤波器值来执行滤波。在图 7-2 中,第一次操作如下。(图 7-1 所示的所有卷积运算请参见表 7-1 )。

表 7-1

图 7-2 的计算表

|

|

计算

|

结果

|
| --- | --- | --- |
| 第一排 | (1x0) + (0x0) + (0x0) + |   |
| 第二排 | (0x0) + (1x1) + (1x1) + | = 5 |
| 第三排 | (1x1) + (0x0) + (1x1) |   |

img/501289_1_En_7_Fig2_HTML.jpg

图 7-2

图 7-1 所示卷积的第一次滤波操作

使用过大的过滤器会降低复杂度,但也会导致重要模式的丢失。因此,我们应该设置一个最佳的过滤器大小来保持模式,并充分降低数据的复杂性。

大步

Stride 是一个参数,用于设置每次操作后滤镜将移动多少像素。对于前面的例子

  • 如果我们选择 1 x 1 像素步距,我们最终将滤波器移动 9 次来处理所有数据。

  • 如果我们选择 2×2 像素的步距,我们可以在 4 次滤波操作中处理整个 5×5 像素的图像。

使用大跨距值会减少滤波器计算的次数。大的步幅值将显著降低模型的复杂性,但是我们可能会在这个过程中丢失一些模式。因此,我们应该始终设置一个最佳步幅值不要太大,也不要太小。

汇集层

当构造 CNN 时,几乎标准的做法是在每个卷积层之后插入池层,以减小表示的空间大小,从而减少参数计数,这降低了计算复杂度。此外,合并层也有助于解决过拟合问题。

对于池操作,我们通过选择这些像素内的最大值、平均值或和值来选择池大小,以减少参数的数量。最大池化是最常见的池化技术之一,其演示如下。

img/501289_1_En_7_Fig3_HTML.jpg

图 7-3

最大池化 2 x 2

在合并层中,在设置 N×N 像素的合并大小后,我们将图像数据划分为 N×N 个像素部分,以选择这些划分部分的最大值、平均值或和值。

对于图 7-3 中的例子,我们将 4 x 4 像素的图像分割成 2 x 2 像素的部分,总共得到 4 个部分。由于我们使用最大池,我们选择这些部分中的最大值,并创建一个仍包含原始图像数据中的模式的简化图像。

选择 N x N 的最佳值对于保持数据中的模式同时实现足够的复杂度降低也是至关重要的。

一组完全连接的层

CNN 中的全连接网络是嵌入式前馈神经网络,其中一层中的每个神经元都链接到下一层中的神经元,以确定标签上每个参数的真实关系和效果。由于卷积和池层的存在,我们的时空复杂度大大降低,我们可以在 CNN 的末端构建一个完全连接的网络来对我们的图像进行分类。一组完全连接的层如图 7-4 所示:

img/501289_1_En_7_Fig4_HTML.jpg

图 7-4

具有两个隐藏层的完全连接的层

一个完整的 CNN 模型

现在你对 CNN 的各个层有了一些了解,是时候分享图 7-5 中完整卷积神经网络的概貌了:

img/501289_1_En_7_Fig5_HTML.png

图 7-5

卷积神经网络示例

虽然特征学习阶段是在卷积层和池层的帮助下执行的,但分类是使用完全连接的层集执行的。

案例研究| MNIST 影像分类

既然我们已经介绍了卷积神经网络的基础知识,我们可以构建一个用于图像分类的 CNN。在这个案例研究中,我们使用了用于图像分类的最老套的数据集:MNIST 数据集,代表修改后的国家标准与技术研究所数据库。这是一个广泛的手写数字数据库,通常用于训练各种图像处理系统。

下载 MNIST 数据

MNIST 数据集是用于影像分类的最常用数据集之一,可从许多不同的来源访问。Tensorflow 允许我们直接从其 API 导入和下载 MNIST 数据集。因此,我们从下面两行开始,在 Keras API 下导入 TensorFlow 和 MNIST 数据集。

import tensorflow as tf
import tensorflow_datasets as tfds
(x_train,y_train),(x_test,y_test)=tfds.as_numpy(tfds.load('mnist', #name of the dataset
        split=['train', 'test'], #both train & test sets
        batch_size=-1, #all data in single batch
        as_supervised=True, #only input and label
        shuffle_files=True #shuffle data to randomize
  ))

MNIST 数据库包含来自美国人口普查局员工和美国高中生的 60,000 幅训练图像和 10,000 幅测试图像。因此,在第二行中,我们将这两组分为训练组和测试组,还将标签和图像分开。x_trainx_test部分包含灰度 RGB 码(从 0 到 255),而y_trainy_test部分包含从 0 到 9 的标签,代表它们实际上是哪个数字。为了可视化这些数字,我们可以从 Matplotlib 获得帮助。

import matplotlib.pyplot as plt
img_index = 7777 #You may pick a number up to 60,000
print("The digit in the image:", y_train[img_index])
plt.imshow(x_train[img_index].reshape(28,28),cmap='Greys')

当我们运行前面的代码时,我们将得到图像的灰度可视化,如图 7-6 所示。

img/501289_1_En_7_Fig6_HTML.jpg

图 7-6

样本图像及其标签的可视化

我们还需要知道数据集的形状,以便将其导入卷积神经网络。因此,我们在下面的代码中使用 NumPy 数组的shape属性:

x_train.shape

我们得到的输出是(60000,28,28,1)。您可能已经猜到,60000 代表训练数据集中的图像数量;(28,28)代表图像的大小,28 x 28 像素;1 表示我们的图像没有颜色。

重塑和标准化图像

使用 TensorFlow 的 dataset API,我们已经创建了一个用于训练的四维 NumPy 数组,这是所需的数组维数。另一方面,我们必须规范化我们的数据,因为这是神经网络模型中的最佳实践。我们可以通过将灰度 RGB 代码分为 255(最大灰度 RGB 代码减去最小灰度 RGB 代码)来实现这一点。这可以通过下面的代码来完成:

# Making sure that the values are float so that we can get decimal points after division
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
# Normalizing the grayscale RGB codes by dividing it to the "max minus min grayscale RGB value".
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print('Number of images in x_train', x_train.shape[0])
print('Number of images in x_test', x_test.shape[0])

构建卷积神经网络

我们通过使用高级 Keras 顺序 API 来简化开发过程,从而构建我们的模型。我想提到的是,还有其他高级 TensorFlow APIs 如 Estimators、Keras Functional API 和另一种 Keras Sequential API 方法,它们帮助我们创建具有高级知识的神经网络。这些不同的选项可能会导致混淆,因为它们的实现结构各不相同。所以,如果你看到同一个神经网络完全不同的代码,虽然都用 TensorFlow,这就是为什么。

我们使用最简单的 tensor flow APIKeras Sequential API,因为我们不需要太多的灵活性。因此,我们从 Keras 导入Sequential模型对象,并添加 Conv2D、MaxPooling、Flatten、Dropout 和 Dense 图层。我们已经介绍了 Conv2D、MaxPooling 和 Dense 层。此外,丢弃层通过在训练时忽略一些神经元来对抗过拟合,而展平层在构建完全连接的层之前将二维数组展平为一维数组。

#Importing the required Keras modules containing model and layers
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,Conv2D, Dropout,Flatten,MaxPooling2D
#Creating a Sequential Model and adding the layers
model = Sequential()
model.add(Conv2D(28,kernel_size=(3,3), input_shape=(28,28,1)))
model.add(MaxPooling2D(pool_size=(2,2))
model.add(Flatten()) #Flattening the 2D arrays for fully connected layers
model.add(Dense(128,activation=tf.nn.relu))
model.add(Dropout(0.2))
model.add(Dense(10,activation=tf.nn.softmax))

对于第一致密层,我们可以用任何数目进行实验;然而,最终的密集层必须有 10 个神经元,因为我们有 10 个数字类(0,1,2,…,9)。你可以尝试内核大小、池大小、激活函数、退出率和第一密集层中的神经元数量,以获得更好的结果。

编译和拟合模型

使用前面的代码,我们创建了一个非优化的空 CNN。现在是时候为优化器设置一个使用度量标准的给定损失函数了。然后,我们可以通过使用我们的训练数据来拟合模型。我们将使用以下代码执行这些任务,并查看图 7-7 中所示的输出:

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
model.fit(x=x_train,y=y_train, epochs=10)
Output:

img/501289_1_En_7_Fig7_HTML.jpg

图 7-7

CNN 在 MNIST 数据集上训练的新纪元统计

您可以试验优化器、损失函数、指标和时期。然而,即使 Adam optimizer、分类交叉熵和准确性是合适的指标,也可以随意试验。

纪元编号可能看起来有点小。但是,您可以轻松达到 98–99%的测试准确度。由于 MNIST 数据集不需要强大的计算能力,您也可以尝试使用纪元编号。

评估模型

最后,您可以使用单行代码使用x_testy_test来评估训练好的模型:

model.evaluate(x_test, y_test)

图 7-8 中的结果显示了基于测试集性能计算的 10 个时期的评估结果。

img/501289_1_En_7_Fig8_HTML.jpg

图 7-8

我们的 MNIST 训练的 CNN 模型的评估结果具有 98.5%的准确性

我们用这样一个基本模型达到了 98.5%的准确率。坦率地说,在大多数图像分类情况下(例如,对于自动驾驶汽车),我们甚至不能容忍 0.1%的误差。打个比方,如果我们建立一个自动驾驶系统,0.1%的误差很容易意味着 1000 起事故中的 1 起。然而,对于我们的第一个模型,我们可以说这个结果是杰出的。

我们还可以使用以下代码进行单独的预测:

img_pred_index = 1000
plt.imshow(x_test[img_pred_index].reshape(28,28),
        cmap='Greys')
pred = model.predict(
                x_test[img_pred_index].reshape(1,28,28,1))
print("Our CNN model predicts that the digit in the image is:", pred.argmax())

我们训练过的 CNN 模型会将图像归类为数字“5”(,这里是图 7-9 中图像的视觉。

img/501289_1_En_7_Fig9_HTML.jpg

图 7-9

我们的模型正确地将该图像分类为数字 5(五)

请注意,由于我们打乱了数据集,您可能会看到索引 1000 的不同图像。但是你的模型仍然以大约 98%的准确率预测这个数字。

虽然图像没有数字 5 ( five )的好笔迹,但是我们的模型能够将其正确分类。

保存已训练的模型

在这个案例研究中,我们使用 Tensorflow 的 Keras Sequential API 构建了第一个卷积神经网络来分类手写数字。我们实现了超过 98%的准确率,现在我们甚至可以用下面几行代码来保存这个模型:

# Save the entire model as a SavedModel.
# Create a 'saved_model' folder under the 'content' folder of your Google Colab Directory.
!mkdir -p saved_model
# Save the full model with its variables, weights, and biases.
model.save('saved_model/digit_classifier')

使用 SavedModel,您可以重建训练过的 CNN,并使用它来创建不同的应用程序,如数字分类器游戏或图像到数字的转换器!

Note

有两种类型的保存选项——新颖别致的“SavedModel”和老式的“H5”格式。如果您想进一步了解这些格式之间的差异,请查看 TensorFlow 指南的保存和加载部分:

www.tensorflow.org/tutorials/keras/save_and_load

结论

卷积神经网络是非常重要和有用的神经网络模型,主要用于图像处理和分类。您可以对图像中的对象进行检测和分类,这可能会用于许多不同的领域,例如制造业中的异常检测、交通运输中的自动驾驶以及零售业中的库存管理。CNN 对于处理音频和视频以及金融数据也很有用。因此,利用 CNN 的应用类型比前面提到的更广泛。

CNN 由用于特征学习的卷积层和汇集层以及一组用于预测和分类的全连接层组成。CNN 降低了数据的复杂性,这是前馈神经网络无法单独做到的。

在下一节中,我们将介绍另一种基本的神经网络架构:循环神经网络(RNNs),它对于音频、视频、文本和时序数据等序列数据特别有用。

八、循环神经网络

在第六章中,我们介绍了前馈神经网络,这是最基本的人工神经网络类型。然后,我们在第七章中介绍了卷积神经网络作为人工神经网络架构的类型,它在图像数据上表现得非常好。现在,是时候介绍另一种类型的人工神经网络体系结构,即循环神经网络或 RNN,它是专为处理序列数据而设计的。

序列数据和时间序列数据

rnn 对于序列数据非常有用。如果您熟悉预测分析,您可能知道与横截面数据相比,使用时间序列数据进行预测需要不同的方法。

横断面数据是指在一个时间点记录的一组观察数据。今年年底许多不同股票的回报率百分比就是横截面数据的一个例子。

时间序列数据是指在一段时间内以相等的时间间隔记录的一组观察值。一只股票在过去 10 年中每年的回报率百分比就是时间序列数据的一个例子。

在时序数据集中,观察值是基于时间戳记录的,但这不能推广到序列数据。序列数据是一个更广泛的术语。序列数据是观察顺序很重要的任何数据。因此,时间序列是一种特殊类型的按时间戳排序的序列数据。例如,一个句子(由几个单词组成)的顺序对其含义至关重要。我们不能随意改变单词的顺序,并期望它有所指。然而,句子中的单词没有时间标记,所以它们不携带任何时间信息。因此,它们只是序列数据,而不是时间序列数据。序列数据(但不是时间序列数据)的另一个例子是 DNA 序列。DNA 序列的顺序是至关重要的,它们不是根据时间戳排序的。序列数据和时序数据的关系如图 8-1 所示。

img/501289_1_En_8_Fig1_HTML.png

图 8-1

序列数据和时间序列数据之间的关系

现在,您已经知道了时序数据和更广泛的术语序列数据之间的关系,您也知道当我们提到序列数据时,我们也指时序数据,除非另有说明。

与其他神经网络架构相比,RNNs 通常在序列数据问题上做得更好。因此,了解如何实现用于序列数据问题的循环神经网络是很重要的,例如股票价格预测、销售预测、DNA 序列建模和机器翻译。

RNNs 和顺序数据

前馈神经网络有三个主要限制,这使得它们不适合序列数据:

  • 前馈神经网络不能考虑阶数。

  • 前馈神经网络需要固定的输入大小。

  • 前馈神经网络不能输出不同长度的预测。

序列数据的基本特征之一是其顺序的重要性。重新排列月销售额的顺序可以将我们从上升趋势引向下降趋势,我们对下个月销售额的预测将会发生巨大的变化。这就是前馈神经网络的局限性所在。在前馈神经网络中,由于这种限制,不能考虑数据的顺序。重新安排月销售额的顺序会得到完全相同的结果,这证明他们不能利用投入的顺序。

在序列数据研究中,问题的性质各不相同,如图 8-2 所示。虽然机器翻译任务本质上是多对多的问题,但是情感分析是多对一的任务。特别是在可能有许多输入的任务中,我们经常需要可变的输入大小。然而,前馈神经网络要求模型具有固定的输入大小,这使得它们不适用于许多序列数据问题。如果模型被训练为使用过去 7 天进行预测,则不能使用第 8 天。

img/501289_1_En_8_Fig2_HTML.png

图 8-2

深度学习中的潜在序列数据任务

最后,前馈神经网络不能输出不同的长度预测。特别是在机器翻译问题中,我们无法预测输出的大小。例如,英语中的一个长句可以很容易地用另一种语言中的三个单词的句子来表达。前馈神经网络不能提供这种灵活性。但是,rnn 提供了这种能力,因此,它们被广泛用于机器翻译等任务。

RNNs 的基础

让我们快速回顾一下 rnn 的历史,然后简单介绍一下 rnn 的真实使用案例及其运行机制。

RNNs 的历史

我们已经在前面的章节中介绍了一些 RNNs 的历史。开发 RNNs 的主要动机是消除前面提到的问题。多年来,研究人员基于他们特定的研究领域开发了不同的 RNN 架构。RNN 有许多变体,不同 RNN 架构的总数可以用几十来表示。第一个 RNN 是约翰·霍普菲尔德在 1982 年开发的霍普菲尔德网络。1997 年,Hochreiter 和 Schmidhuber 发明了长短期记忆(LSTM)网络,以解决当时现有 rnn 的问题。LSTM 网络在序列数据任务上表现非常好,它们是非常流行的 RNN 架构,今天被广泛使用。2014 年,Kyunghyun Cho 引入了循环门控单元(gru)来简化 LSTM 网络。GRUs 在许多任务上的表现也非常出色,其内部结构比 LSTMs 更加直观。在本章中,我们将更详细地介绍简单的 rnn、LSTMs 和 gru。

RNNs 的应用

rnn 有大量的实际应用,其中一些应用只能用 rnn 构建。如果没有 RNNs,我们将无法在许多领域提供有竞争力的解决方案,例如机器翻译或情感分析。以下是 RNN 潜在使用案例的非详尽列表:

  • 语法学习

  • 手写识别

  • 人体动作识别

  • 机器翻译

  • 音乐创作

  • 预测蛋白质的亚细胞定位

  • 医疗保健路径中的预测

  • 蛋白质同源性检测

  • 节奏学习

  • 机器人学

  • 情感分析

  • 语音识别和合成

  • 时间序列异常检测

  • 时间序列预测

RNNs 的机制

RNN 通过将以前的信息保存在内存中来利用它们,这些信息在 RNN 神经元中被保存为“状态”。

在深入 LSTMs 和 GRUs 的内部结构之前,我们先用一个基本的天气预报例子来了解一下内存结构。我们想通过使用序列中提供的信息来猜测是否会下雨。该数据序列可以从文本、语音或视频中获得。每有新的信息,我们就慢慢更新降雨的概率,最后得出结论。在图 8-3 中显示了这项任务。

img/501289_1_En_8_Fig3_HTML.png

图 8-3

一个简单的天气预报任务:会下雨吗?

在图 8-3 中,我们首先记录了多云天气。这一单一信息可能是降雨的指示,其计算为 50%(或 0.5)的降雨概率。然后,我们收到以下输入:拥挤的街道。拥挤的街道意味着人们在外面,这意味着降雨的可能性更小,因此,我们的估计下降到 30%(或 0.3)。然后,我们被提供了更多的信息:膝盖疼痛。人们认为风湿病患者在下雨前会感到膝盖疼痛。因此,我的估计上升到 70%(或 0.7)。最后,当我们的模型将闪电作为最新信息时,集体估计增加到 90%(或 0.9)。在每个时间间隔,我们的神经元使用其包含先前信息的存储器,并在该存储器上添加新信息,以计算降雨的可能性。可以在层级别以及单元级别设置存储器结构。图 8-4 显示了细胞级 RNN 机制,(I)左侧为折叠版本,(ii)右侧为展开版本。

img/501289_1_En_8_Fig4_HTML.png

图 8-4

基于细胞的循环神经网络活动

RNN 类型

如前所述,RNNs 有许多不同的变体。在本节中,我们将介绍我们经常遇到的三种 RNN:

  • 简单的 RNN

  • 长短期记忆(LSTM)网络

  • 门控循环单元(GRU)网络

你可以在图 8-5 中找到这些可选 RNN 细胞的可视化。

img/501289_1_En_8_Fig5_HTML.png

图 8-5

简单 RNN、门控循环单位和长短期记忆细胞

如图 8-5 所示,所有这三种备选方案都具有共同的 RNN 特征:

  • 它们都将 t-1 状态(存储器)作为先前值的表示带入计算中。

  • 它们都应用某种激活函数并进行矩阵运算。

  • 它们都计算时间 t 的当前状态

  • 他们重复这个过程来完善他们的权重和偏差值。

让我们详细检查这三种选择。

简单 RNNs

简单的 rnn 是神经元节点的网络,其被设计在连接的层中。简单 RNN 单元的内部结构如图 8-6 所示。

img/501289_1_En_8_Fig6_HTML.jpg

图 8-6

一种简单的 RNN 单元结构

在一个简单的 RNN 单元中,有两个输入:(I)来自前一时间步(t-1)的状态和(ii)在时间 t 的观察值。在激活函数(通常为 Tanh)之后,输出作为时间 t 的状态传递给下一个单元。因此,前一个信息的效果会在每一步传递给下一个单元格。

简单的 RNNs 可以解决许多序列数据问题,并且它们不是计算密集型的。因此,在资源有限的情况下,这可能是最佳选择。有必要了解简单的 RNNs 然而,它容易出现几个技术问题,如消失梯度问题。因此,我们倾向于使用更复杂的 RNN 变体,如长短期记忆(LSTM)和门控循环单位(GRU)。

长短期记忆(LSTM)

长短期记忆(LSTM)网络是由 Hochreiter 和 Schmidhuber 在 1997 年发明的,在许多不同的应用中提高了最高的精度性能,旨在解决序列数据问题。

LSTM 单元由单元状态、输入门、输出门和遗忘门组成,如图 8-7 所示。这三个门控制进出 LSTM 单元的信息流。此外,LSTM 单元具有单元状态和隐藏状态。

img/501289_1_En_8_Fig7_HTML.jpg

图 8-7

一种长短期记忆单元结构

LSTM 网络非常适合于任何格式的序列数据问题,并且它们不容易出现消失梯度问题,这在简单的 RNN 网络中是常见的。另一方面,我们可能仍然会遇到爆炸梯度问题,梯度趋向于无穷。LSTM 网络的另一个缺点是计算密集型。使用 LSTM 训练模型可能需要大量的时间和处理能力,这是开发 gru 的主要原因。

门控循环单元

门控循环单元由 Kyunghyun Cho 于 2014 年推出。正如 LSTMs 一样,gru 也是 RNNs 中处理序列数据的门控机制。然而,为了简化计算过程,GRUs 使用两个门:(I)复位门和(ii)更新门。gru 也为隐藏状态和单元状态使用相同的值。图 8-8 显示了门控循环单元的内部结构。

img/501289_1_En_8_Fig8_HTML.jpg

图 8-8

一种门控循环单元结构

当计算资源有限时,gru 是有用的。即使 gru 在某些应用中优于 lstm,lstm 通常也优于 gru。在处理序列数据时,用 LSTM 和 GRU 训练两个模型并选择性能最好的一个模型是一个好策略,因为这两个备选选通机制的性能会因情况而异。

案例研究|带有 IMDB 评论的情感分析

既然我们已经讨论了循环神经网络的概念部分,现在是进行案例研究的时候了。一般来说,您不必记住简单 rnn、LSTMs 和 gru 的内部工作结构来构建循环神经网络。TensorFlow APIs 使得构建在几个任务上表现良好的 rnn 变得非常容易。在本节中,我们将使用 IMDB 评论数据库进行情感分析案例研究,这是受 TensorFlow 的官方教程“用 RNN 进行文本分类”的启发。 1

为 GPU 加速培训准备我们的 Colab

在深入研究我们的数据之前,有一个关键的环境调整:我们需要在我们的 Google Colab 笔记本中激活 GPU 培训。激活 GPU 训练是一项相当简单的任务,但如果不这样做,你将永远处于 CPU 训练模式。

请转到您的 Google Colab 笔记本,选择运行时➤的“更改运行时类型”菜单来启用 GPU 加速器,如图 8-9 所示。

如前几章所述,使用 GPU 或 TPU 代替 CPU 进行训练通常会加快训练速度。既然我们已经在模型中启用了 GPU,我们可以使用以下代码来确认是否为训练激活了 GPU:

import tensorflow as tf
print("Num GPUs Available: ", len(tf.config.experimental.list_physical_devices('GPU')))

Output: Num GPUs Available:  1

img/501289_1_En_8_Fig9_HTML.jpg

图 8-9

在 Google Colab 中启用 GPU 加速

IMDB 评论

IMDB 评论数据集是由 Andrew L. Maas 从流行的电影评级服务 IMDB 收集和准备的大型电影评论数据集。 2 IMDB reviews 用于二元情感分类,不管一个评论是正面还是负面。IMDB 评论包含 25,000 条用于训练的电影评论和 25,000 条用于测试的电影评论。所有这 50,000 条评论都是有标签的数据,可能用于有监督的深度学习。此外,还有另外 50,000 篇未标记的评论,我们不会在本案例研究中使用。

幸运的是,TensorFlow 已经处理了原始文本数据,并为我们准备了单词袋格式。此外,我们还可以访问原始文本。准备单词包是一项自然语言处理(NLP)任务,我们将在接下来的章节 9 中介绍。因此,在这个例子中,我们几乎不用任何 NLP 技术。相反,我们将使用经过处理的词袋版本,以便我们可以轻松地建立我们的 RNN 模型来预测评论是正面还是负面的。

用于数据集下载的 TensorFlow 导入

我们从两个初始导入开始,即主 TensorFlow 导入和 TensorFlow 数据集导入,以加载数据:

import tensorflow as tf
import tensorflow_datasets as tfds

从 TensorFlow 加载数据集

TensorFlow 提供了几个流行的数据集,可以直接从tensorflow_datasets API 加载。tensorflow_datasets API 的load()函数返回两个对象:(I)包含训练、测试和未标记集的字典,以及(ii)关于 IMDB 评论数据集的信息和其他相关对象。我们可以用下面的代码将它们保存为变量:

# Dataset is a dictionary containing train, test, and unlabeled datasets
# Info contains relevant information about the dataset
dataset, info = tfds.load('imdb_reviews/subwords8k',
                          with_info=True,
                          as_supervised=True)

理解单词袋概念:文本编码和解码

单词包是描述单词在文档中出现的文本表示。这种表示是基于词汇创建的。在我们的数据集中,评论使用 8185 个单词的词汇表进行编码。我们可以通过之前创建的“info”对象访问编码器。

# Using info we can load the encoder which converts text to bag of words
encoder = info.features['text'].encoder
print('Vocabulary size: {}'.format(encoder.vocab_size))
output: Vocabulary size: 8185

通过使用这个编码器,我们可以对新的评论进行编码:

# You can also encode a brand new comment with encode function
review = 'Terrible Movie!.'
encoded_review = encoder.encode(review)
print('Encoded review is {}'.format(encoded_review))

output: Encoded review is [3585, 3194, 7785, 7962, 7975]

我们也可以如下解码编码的评论:

# You can easily decode an encoded review with decode function
original_review = encoder.decode(encoded_review)
print('The original review is "{}"'.format(original_review))

output: The original review is "Terrible Movie!."

准备数据集

我们已经在“dataset”对象中保存了我们的评论,这是一个包含三个键的字典:(i) train,(ii) test,和(iii) unlabeled。通过使用这些键,我们将用下面的代码分割我们的训练集和测试集:

# We can easily split our dataset dictionary with the relevant keys
train_dataset, test_dataset = dataset['train'], dataset['test']

我们还需要调整我们的数据集以避免任何偏见,并填充我们的评论,以便所有的评论长度相同。我们需要选择一个大的缓冲区大小,以便我们可以有一个混合良好的训练数据集。此外,为了避免过多的计算负担,我们将序列长度限制为 64。

BUFFER_SIZE = 10000
BATCH_SIZE = 64

train_dataset = train_dataset.shuffle(BUFFER_SIZE)
train_dataset = train_dataset.padded_batch(BATCH_SIZE)
test_dataset = test_dataset.padded_batch(BATCH_SIZE)

Padding

填充是将序列数据编码成连续批次的一种有用方法。为了能够将所有序列调整到一个定义的长度,我们必须填充或截断数据集中的一些序列。

构建循环神经网络

既然我们的训练和测试数据集已经准备好输入到模型中,我们就可以开始用 LSTM 单元构建我们的 RNN 模型了。

用于模型构建的导入

我们使用 Keras 顺序 API 来构建我们的模型。我们还需要密集、嵌入、双向、LSTM 和漏失层来构建我们的 RNN 模型。我们还需要引入二进制交叉熵作为损失函数,因为我们使用二进制分类来预测评论是正面还是负面。最后,我们使用 Adam 优化器通过反向传播来优化我们的权重。这些组件是通过以下代码行导入的:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import (Dense,
                                     Embedding,
                                     Bidirectional,
                                     Dropout,
                                     LSTM)
from tensorflow.keras.losses import BinaryCrossentropy
from tensorflow.keras.optimizers import Adam

创建模型并用层填充它

我们使用一个编码层、两个双向包裹的 LSTM 层、两个密集层和一个分离层。我们从嵌入层开始,它将单词索引序列转换成向量序列。嵌入层为每个单词存储一个向量。然后,我们添加两个包裹在双向层中的 LSTM 层。双向层通过 LSTM 层来回传播输入,然后连接输出,这有助于了解长程相关性。然后,我们添加一个具有 64 个神经元的密集层来增加复杂性,添加一个丢弃层来防止过拟合。最后,我们添加一个最终的密集层来进行二元预测。以下代码行创建了一个顺序模型,并添加了所有提到的层:

model = Sequential([
    Embedding(encoder.vocab_size, 64),
    Bidirectional(LSTM(64,  return_sequences=True)),
    Bidirectional(LSTM(32)),
    Dense(64, activation="relu"),
    Dropout(0.5),
    Dense(1)
])

如图 8-10 所示,我们还可以看到带有model.summary()的车型概况。

img/501289_1_En_8_Fig10_HTML.jpg

图 8-10

RNN 模式综述

我们还可以创建我们的 RNN 模型的流程图,如图 8-11 所示,使用以下代码行:

tf.keras.utils.plot_model(model)

img/501289_1_En_8_Fig11_HTML.jpg

图 8-11

RNN 模式的流程图

编译和拟合模型

既然我们构建了一个空模型,现在是时候用下面的代码配置损失函数、优化器和性能指标了:

model.compile(
loss=BinaryCrossentropy(from_logits=True),
      optimizer=Adam(1e-4),
      metrics=['accuracy'])

我们的数据和模型已经可以进行训练了。我们可以使用model.fit()函数来训练我们的模型。大约 10 个时期对于训练我们的情感分析模型来说是足够的,这可能需要大约 30 分钟。我们还将我们的训练过程保存为一个变量,以评估模型随时间的表现。

history = model.fit(train_dataset, epochs=10,
                    validation_data=test_dataset,
                    validation_steps=30)

图 8-12 显示了每个时期的主要性能指标。

img/501289_1_En_8_Fig12_HTML.jpg

图 8-12

模拟每个时期的训练表现

评估模型

在看到大约 85%的准确性性能后,我们可以放心地继续评估我们的模型。我们使用test_dataset来计算我们的最终损耗和精度值:

test_loss, test_acc = model.evaluate(test_dataset)

print('Test Loss: {}'.format(test_loss))
print('Test Accuracy: {}'.format(test_acc))

运行上面的代码后,我们得到如下图 8-13 所示的输出:

img/501289_1_En_8_Fig13_HTML.jpg

图 8-13

培训后的模型评估

我们还可以使用 history 对象,通过下面的代码绘制一段时间内的性能指标:

import matplotlib.pyplot as plt

def plot_graphs(history, metric):
  plt.plot(history.history[metric])
  plt.plot(history.history['val_'+metric], '')
  plt.xlabel("Epochs")
  plt.ylabel(metric)
  plt.legend([metric, 'val_'+metric])
  plt.show()
plot_graphs(history, 'accuracy')

图 8-14 显示了输出的图。

img/501289_1_En_8_Fig14_HTML.jpg

图 8-14

情绪分析 LSTM 模型的准确度与时间图

做出新的预测

既然我们已经训练了我们的 RNN 模型,我们可以从我们的模型从未见过的评论中做出新的情绪预测。由于我们对训练和测试集进行了编码和填充,我们必须以同样的方式处理新的评论。因此,我们需要一个 padder 和一个编码器。以下代码是我们的自定义填充函数:

def review_padding(encoded_review, padding_size):
  zeros = [0] * (padding_size - len(encoded_review))
  encoded_review.extend(zeros)
  return encoded_review

我们还需要一个编码器功能,将编码和处理我们的审查,以馈入我们训练有素的模型。以下函数完成这些任务:

def review_encoder(review):
        encoded_review = review_padding(encoder.encode( review ), 64)
        encoded_review = tf.cast( encoded_review,  tf.float32)
        return tf.expand_dims( encoded_review, 0)

现在我们可以很容易地从以前看不到的评论中做出预测。为了这个任务,我访问了电影《搏击俱乐部》的 IMDB 评论页面,并选择了以下评论:

fight_club_review = 'It has some cliched moments, even for its time, but FIGHT CLUB is an awesome film. I have watched it about 100 times in the past 20 years. It never gets old. It is hard to discuss this film without giving things away but suffice it to say, it is a great thriller with some intriguing twists.'

评论人给了 8 星,给搏击俱乐部写了这个评论。所以,显然是正面评价。由于我们前面定义的自定义函数,进行新的预测非常容易,如下面一行所示:

model.predict(review_encoder(fight_club_review))

output: array([[1.5780725]], dtype=float32)

当输出大于 0.5 时,我们的模型将评论分类为正面,而如果低于 0.5,则分类为负面。由于我们的输出是 1.57,我们确认我们的模型成功地预测了评论的情绪。

虽然我们的模型有超过 85%的准确率,但我认识到的一个偏差是关于评论的长度。当我们选择一个非常短的评论时,不管它有多积极,我们总是得到一个消极的结果。这个问题可以通过微调来解决。尽管我们不会在这个案例研究中进行微调,但是您可以随意地对其进行工作,以进一步改进模型。

保存和加载模型

你已经成功训练了一个 RNN 模型,你可以完成这一章。但是我想再介绍一个话题:保存和加载训练好的模型。正如你所经历的,训练这个模型需要大约 30 分钟,谷歌 Colab 会在一段时间不活动后删除你所做的一切。所以,你必须保存你训练好的模型以备后用。此外,你不能简单地将它保存到 Google Colab 目录中,因为过一会儿它也会被删除。解决办法是把它保存到你的 Google Drive。为了能够通过云随时使用我们的模型,我们应该

  • 让 Colab 访问我们的 Google Drive 来保存文件

  • 将训练好的模型保存到指定路径

  • 随时从 Google Drive 加载训练好的模型

  • 使用 savemodel 对象进行预测

让 Colab 访问 Google Drive

为了能够访问 Colab,我们需要在我们的 Colab 笔记本中运行以下代码:

from google.colab import drive
drive.mount('/content/gdrive')

按照输出单元格中的说明完成此任务。

将训练好的模型保存到 Google Drive

现在我们可以从 Colab 笔记本中访问我们的 Google Drive 文件,我们可以创建一个名为saved_models的新文件夹,并使用以下代码行将我们的SavedModel对象保存到该文件夹中:

# This will create a 'saved_model' folder under the 'content' folder.
!mkdir -p "/content/gdrive/My Drive/saved_model"
# This will save the full model with its variables, weights, and biases.
model.save('/content/gdrive/My Drive/saved_model/sentiment_analysis')
# Also save the encoder for later use
  encoder.save_to_file('/content/gdrive/My Drive/saved_model/sa_vocab')

在这段代码之后,只要我们将保存的文件保存在 Google Drive 中,我们就可以加载训练好的模型。您还可以使用以下代码查看sentiment_analysis文件夹下的文件夹和文件:

import os
os.listdir("/content/gdrive/My Drive/saved_model/sentiment_analysis")
output: ['variables', 'assets', 'saved_model.pb']

加载训练好的模型并进行预测

为了能够加载saved_model,我们可以使用saved_model对象的 load 属性。我们只需要传递我们的模型所在的确切路径(确保 Colab 可以访问您的 Google Drive ),一旦我们运行代码,我们的模型就可以使用了:

import tensorflow as tf
loaded = tf.keras.models.load_model("/content/gdrive/My Drive/saved_model/sentiment_analysis/")

我们还加载了之前保存的词汇表,用下面的代码进行编码和解码:

import tensorflow_datasets as tfds
vocab_path = '/content/gdrive/My Drive/saved_model/sa_vocab'
encoder = tfds.features.text.SubwordTextEncoder.load_from_file(vocab_path)

此外,如果重新启动运行时,请确保再次运行定义了review_padding()review_encoder()函数(之前共享)的单元格。

请注意,加载的模型对象与我们之前的模型完全相同,它具有标准的模型函数,如fit()evaluate()predict()。为了能够进行预测,我们需要使用我们加载的模型对象的predict()函数。我们还需要通过我们作为embedding_input参数的过程化审查。以下代码行完成了这些任务:

fight_club_review = 'It has some cliched moments, even for its time, but FIGHT CLUB is an awesome film. I have watched it about 100 times in the past 20 years. It never gets old. It is hard to discuss this film without giving things away but suffice it to say, it is a great thriller with some intriguing twists.'

loaded.predict(review_encoder(rev))
output: array([[1.5780725]], dtype=float32)

正如所料,我们得到相同的输出。因此,我们成功地保存了我们的模型,加载了它,并进行了预测。现在,您可以将这个经过训练的模型嵌入到 web 应用程序、REST API 或移动应用程序中,为全世界服务!

结论

在本章中,我们介绍了循环神经网络,这是一种人工神经网络,专门用于处理序列数据。我们涵盖了 RNNs 的基础知识和不同类型的 RNNs(基本 RNN,LSTM,GRU 神经元)。然后,我们使用 IMDB 评论数据集进行了一个案例研究。我们的 RNN 学会了通过使用超过 50,000 条评论来预测评论是正面还是负面(即情绪分析)。

在下一章,我们将讨论自然语言处理,它是人工智能的一个分支,处理文本数据。此外,我们将在下一章构建另一个 RNN 模型,但这一次,它将生成文本数据。

九、自然语言处理

自然语言处理(NLP)是一个跨学科的子领域,包括语言学、计算机科学和人工智能等主要领域。NLP 主要关注人和计算机之间的交互。NLP 的范围从计算理解和生成人类语言到处理和分析大量自然语言数据。自然语言处理的范围还包括文本、语音、认知以及它们之间的相互作用。在这一章中,我们简要介绍了自然语言处理的历史,基于规则的自然语言处理和基于统计的自然语言处理之间的区别,以及主要的自然语言处理方法和技术。最后,我们对 NLP 进行了一个案例研究,让你为现实世界中的问题做好准备。

自然语言处理的历史

NLP 的历史可以分为四个主要时代:

  • 早期思想时代

  • 基于规则的自然语言处理时代

  • 监督学习时代的统计自然语言处理

  • 无监督和半监督学习时代

请注意,这些时代是互补的,而不是破坏性的。因此,我们仍然利用早期引入的规则和方法。

早期的想法

自然语言处理的历史始于 17 世纪,由莱布尼茨和笛卡尔提出哲学建议,引入特殊代码将不同语言之间的单词连接起来。尽管这些建议总是停留在理论层面,但它们影响了未来几个世纪的科学家们实现自动机器翻译的想法。

基于规则的自然语言处理

这个时代的共同特点是大量使用复杂的手写规则来覆盖 NLP 任务的潜在结果。与自然语言处理相关的早期创新最早出现在 20 世纪 30 年代的“翻译机”专利中。这些早期专利包含自动双语词典和处理语言间语法规则的方法。

在第二次世界大战期间,机器翻译设备被开发出来翻译敌人之间的通信。然而,这些机器大多不成功。

正如在人工智能的所有其他领域一样,1950 年,图灵测试为智能设定了标准,其中包括理解自然语言的对话。

1957 年,诺姆·乔姆斯基公布了句法结构,这是一个基于规则的系统,用一个普遍的语法规则彻底改变了语言学研究。

1954 年,在乔治敦大学的实验中,60 个俄语句子被自动翻译成英语。这个成功的实验鼓励作者宣称这两种语言之间的机器翻译将在 3 到 5 年内完成。这种积极的方法与其他人工智能子领域一致,在这些子领域中,大多数乐观的承诺都未能实现。因此,在 20 世纪 60 年代末和 70 年代初,在人工智能冬天的时代,NLP 研究的资金被削减。

尽管资金有限,一些成功的 NLP 系统还是在 20 世纪 60 年代开发出来,在有限的环境中工作。20 世纪 70 年代是许多程序员开始编写“概念本体”的年代,概念本体将现实世界的对象结构化、分组并分类成二进制数据。

统计自然语言处理和监督学习

大约在 20 世纪 80 年代,计算能力的稳步增长和优先使用机器学习方法进行语言处理的语料库语言学的普及使得在 NLP 中使用统计模型成为可能。尽管统计 NLP 的早期研究与基于规则的 NLP 研究没有太大的不同,但是随着更复杂方法的引入,统计 NLP 变得更具概率性。这种从基于规则的模型到统计模型的转变提高了精度性能,尤其是对于不寻常的观察。

在这个时代,IBM Research 走在了前面,开发了 IBM Watson 等几个成功的 NLP 解决方案。此外,欧盟、联合国和加拿大议会制作的多语言官方文档也为成功的机器翻译系统的开发做出了贡献。

另一方面,对这些大型文本语料库的访问有限的较小参与者专注于开发可以从有限数量的数据中有效学习的方法。

无监督和半监督自然语言处理

如今,NLP 问题在现实世界中的应用越来越成功。然而,找到足够的标记数据是现代自然语言处理研究中的主要问题之一。因此,使用无监督和半监督学习算法来完成常见的 NLP 任务变得越来越流行。一般来说,使用无监督学习算法做出的预测不如有监督算法准确。然而,通过无监督模型,研究人员可以从大量数据中推断出结果,这对发现更复杂的模式非常有用。

自然语言处理的实际应用

随着机器学习领域的进步,NLP 现实世界应用的数量正在增加。随着计算能力的提高,大量可用的机器学习模型,以及大量文本语料库的可用性,每天都有新的 NLP 用例被发现。以下是最受欢迎的 NLP 应用程序列表:

  • 机器翻译:将文本从一种语言翻译成另一种语言的任务(例如,谷歌翻译)

  • 语音识别:识别人声以采取行动或转换成文本的任务

  • 情感分析:理解文本片段中的情感的任务,例如评论

  • 问题回答:开发能够准确给出给定问题答案的系统的任务(例如 Siri)

  • 自动摘要(Automatic Summarization】:在不丢失要点的情况下,从全文中提取简短摘要的任务

  • 聊天机器人(Chatbots):开发特殊系统的任务,这些系统能够完成几项自然语言处理任务,比如问答、语音识别等等

  • 市场情报:利用多种 NLP 和其他统计方法分析客户行为的任务

  • 文本分类:通过分析文本的内容、结构和其他相关特征,将文本分类到给定类别的任务

  • 光学字符识别(OCR) :借助计算机视觉和图像处理方法,分析图像数据并将其转换为文本的任务

  • 拼写检查:识别和纠正文本中拼写错误的任务(例如,语法上)

为了能够开发这些真实世界的应用程序,研究人员必须使用几种 NLP 方法,这些方法将在下一节中介绍。

主要评估、技术、方法和任务

自然语言数据的处理由几个小任务组成,这些小任务可以分成以下几组:

  • 形态句法

  • 语义学

  • 话语

  • 演讲

  • 对话

  • 认识

在下一节中,我们将在相应的组下讨论这些任务。

形态句法

形态句法是对基于形态和句法属性创造的语法范畴和语言单位的研究。在自然语言处理领域,有许多基本的形态句法任务,列举如下:

  • 基本形式提取:有两种流行的方法来提取单词的基本形式。

    • 词条化:通过使用一个实际的词典(例如通过去掉 -ing 后缀将游泳转换为游泳)来去掉单词的无关紧要的词尾,并返回它们的基本词典形式(即词条)。

    • 词干化:一种将屈折词或派生词还原为其词根形式的方法。虽然词干化类似于词汇化,但是使用词干化生成的词根形式不必是真实的单词(例如,单词troubltroubltroubl被词干化为 troubl )。

  • 语法归纳:生成描述其语法的全语言形式语法。

  • 词法切分:将单词拆分成最小的有意义的单位(即语素,并识别这些单位的类别。

  • 词性标注:确定每个单词的词性类型。常见的词类有名词、动词、形容词、副词、代词、介词、连词、感叹词、数词、冠词或限定词。

  • 解析:确定给定字符串(如句子)的解析树。解析树是一个有序的、有根的树,它代表了字符串的语法结构。

  • 断句:寻找句子边界。一些标点符号,如句号或感叹号,对这项任务很有用。

  • 分词:将给定文本分割成单独的单词。这个过程通常用于创建一个单词包(BOW)和文本矢量化

  • 术语提取:从给定的语料库中提取相关术语。

语义学

语义学是语言学和逻辑学的交叉学科,研究意义。逻辑语义学关注的是意义、指称和蕴涵,而词汇语义学关注的是对词义及其关系的分析。与语义相关的主要问题、方法和任务如下:

  • 机器翻译:如前所述,将文本从一种人类语言自动翻译成另一种语言。

  • 命名实体识别(NER) :在给定的字符串中寻找人名和地名。尽管资本化对 NER 很有用,但肯定还有更多的工作要做。

  • 自然语言生成:使用单词表示和统计模型生成自然语言文本。

  • 光学字符识别:如前所述,从包含打印文本的图像中识别文本数据。

  • 问题回答:如前所述,用自然语言给出一个问题,提供答案。

  • 识别文本蕴涵:识别文本片段之间的方向关系,比死板的逻辑蕴涵更宽松。

    两个字符串之间的正文本蕴涵的示例如下:

    正文:努力就会成功。

    假设:努力工作会有好的结果。

  • 关系提取:从给定文本中识别现实世界的关系(例如,人 A 为 X 公司工作)。

  • 情感分析:如前所述,从一组文档中提取主观信息(即情感)。

  • 主题分割和识别:将一组文档或文本分类成单独的主题。虽然在某些情况下主题界限可能很明显,但通常需要更多的评估。

  • 词义消歧:根据上下文确定一个有一个以上含义的单词的含义。

话语

  • 自动摘要:如前所述,产生一个大文本的可读摘要。

  • 共指消解:确定哪些词指代相同的对象,既包括名词也包括代词。当一个文本中有多个表达式引用同一个对象时,就会出现共指。

  • 话语分析:研究书面语或口语与其社会背景的关系,旨在了解语言在现实生活中的使用情况。

演讲

  • 语音识别:如前所述,将一个人说话的给定声音片段转换成文本

  • 语音分割:语音识别的一个子任务,将识别的文本分割成单词

  • 文本到语音:将给定的文本转换成它的音频表示

对话

开始并继续与人或机器进行有意义的书面或口头交流。对话需要同时完成几项任务,如回答问题、文本到语音转换、语音识别、情感分析等。

认识

通过思想、经验和感觉获得知识和理解。它被认为是最复杂的自然语言处理评估,通常被称为自然语言理解(NLU)。

自然语言工具包(NLTK)

NLTK 是一个为 NLP 任务设计的重要 Python 库。NLTK 支持基本的 NLP 任务,例如文本分类、词干提取和词汇化、标记、解析、标记化,甚至推理。

在由宾夕法尼亚大学的 Steven Bird 和 Edward Loper 开发之后,NLTK 被认为是 Python 的主要 NLP 库。

尽管您可以利用数据科学库,如 Pandas、scikit-learn、TensorFlow 和 NumPy,但这些库中可用的方法甚至无法与 NLTK 提供的方法相比。

这里列出了可用的 NLTK 模块。

|

app

|

parse

|
| --- | --- |
| ccg | probability |
| chat | sem |
| chunk | sentiment |
| classify | stem |
| cluster | tag |
| collections | tbl |
| corpus | test |
| data | text |
| downloader | tokenize |
| draw | toolbox |
| featstruct | translate |
| grammar | tree |
| help | treetransform |
| inference | twitter |
| lm | util |
| metrics | wsd |
| misc |   |

关于 NLTK 的有用信息

案例研究|深度自然语言处理的文本生成

请注意,NLP 的主题本身就是一个专业领域。有人可以花一生的时间来研究 NLP。在这一章中,我们只做一个介绍,现在我们已经讨论了自然语言处理中的主要主题,我们可以继续我们的案例研究:使用深度自然语言处理的文本生成。

NLP 项目中最重要的主题之一是文本矢量化。在本案例研究中,我们将参考 Andrej Karpathy 的博文《循环神经网络的不合理有效性》 1 ,以及 TensorFlow 团队对这篇博文的承担 2

研究表明,用于 NLP 的最有效的人工神经网络类型之一是循环神经网络(RNNs)。rnn 广泛用于 NLP 任务,如机器翻译、文本生成和图像字幕。在 NLP 任务中,通常,开发者使用 NLP 工具和方法来将文本数据处理成向量,然后将它们馈送到选定的人工神经网络,例如 RNN、CNN,或者甚至前馈神经网络,以完成任务。在我们的案例研究中,我们也遵循这两个标准化的步骤:(I)将文本处理成向量,(ii)用这些向量训练神经网络。

案例研究的目标

充分理解案例研究的目标至关重要。在这个案例研究中,我们的目标是训练一个 RNN,它能够使用字符生成有意义的文本。RNN 可以从单词和字符中生成文本,我们选择使用字符来生成这个案例研究的文本。问题是,当我们未经训练就建立一个新的 RNN 时,它组合了一堆毫无意义的字符,这没有任何意义。然而,如果我们给我们的 RNN 输入大量文本数据,它就会开始模仿这些文本的风格,并使用字符生成有意义的文本。所以,如果我们给模型输入大量说教性的文本,我们的模型就会生成教育材料。如果我们给我们的模型输入大量的诗歌,我们的模型将开始生成诗歌,所以我们将最终拥有一个人工诗人。这些都是可行的选择,但是我们将为我们的模型提供一些其他的东西:一个包含莎士比亚作品的长文本数据集。因此,我们将创造一个人造的莎士比亚。

莎士比亚文集

莎士比亚语料库是一个包含 40,000 行莎士比亚作品的文本文件,由 Karpathy 清理和准备,由 TensorFlow 团队托管于此 URL:

https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt

我强烈建议您看看。txt 文件来理解我们正在处理的文本。文件包含对话内容,每个角色的名字放在相应部分的前面,如图 9-1 所示。

img/501289_1_En_9_Fig1_HTML.jpg

图 9-1

莎士比亚文集的一部分

初始进口

在本案例研究中,所需的库是 TensorFlow、NumPy 和 os,我们可以使用以下代码导入它们:

import tensorflow as tf
import numpy as np
import os

你注意到我没有提到 NLTK 库吗?原因是 TensorFlow 对 NLP 任务的支持也有限,在本案例研究中,结合 NumPy 操作,我们能够使用 TensorFlow 对数据集进行矢量化。这主要是因为我们的语料库非常标准化和干净。如果我们需要一个更复杂的 NLP 方法,我们将不得不在更大程度上依赖 NLTK、Pandas 和 NumPy 库。

加载语料库

为了能够从在线目录加载数据集,我们可以使用 TensorFlow 中 Keras API 的util模块。对于这个任务,我们将使用get_file()函数,如果文件不在缓存中,它将从 URL 下载文件,代码如下:

path_to_file = tf.keras.utils.get_file('shakespeare.txt', 'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt')

下载文件后,我们可以使用以下 Python 代码从缓存中打开文件:

text = open(path_to_file, 'rb').read()
text = text.decode(encoding='utf-8')

现在,我们成功地将整个语料库作为变量保存在了 Colab 笔记本的内存中。让我们看看语料库中有多少个字符,前 100 个字符是什么,代码如下:

print ('Total number of characters in the corpus is:', len(text))
print('The first 100 characters of the corpus are as follows:\n', text[:100])
Output:
Total number of characters in the corpus is: 1115394
The first 100 characters of the corpus are as follows:
 First Citizen:
Before we proceed any further, hear me speak.

All:
Speak, speak.

First Citizen:
You

我们的整个语料库可以通过一个名为 text 的 Python 变量来访问,现在我们可以开始对它进行矢量化了。

向量化文本

文本矢量化是一种基本的 NLP 方法,用于将文本数据转换为机器可以理解的有意义的数字向量。文本矢量化有多种方法。在本案例研究中,我们是这样一步步进行的:

  • 给每个独特的字符一个索引号。

  • 在语料库中运行一个 for 循环,并索引整个文本中的每个字符。

为了给每个独特的字符分配一个索引号,我们首先必须创建一个只包含文本中所有独特字符的单个副本的列表。使用内置的set()函数很容易做到这一点,该函数将一个列表对象转换成一个只有唯一值的集合对象。

集合和列表数据结构的区别在于,列表是有序的,允许重复,而集合是无序的,不允许重复元素。因此,当我们运行set()函数时,如下面的代码所示,它在文本文件中返回一组独特的字符:

vocab = sorted(set(text))
print ('The number of unique characters in the corpus is', len(vocab))
print('A slice of the unique characters set:\n', vocab[:10])
Output:
The number of unique characters in the corpus is 65
A slice of the unique characters set:
 ['\n', ' ', '!', '$', '&', "'", ',', '-', '.', '3']

我们还需要给每个字符一个索引号。以下代码为每个集合项目分配一个编号,然后使用以下代码创建一个集合项目字典,其中包含这些集合项目的给定编号:

char2idx = {u:i for i, u in enumerate(vocab)}

我们还复制了 NumPy 数组格式的唯一集合元素,供以后解码预测时使用:

idx2char = np.array(vocab)

现在,我们可以使用一个简单的 for 循环对文本进行矢量化处理,遍历文本中的每个字符,为它们分配相应的索引值,并将所有索引值保存为一个新列表,代码如下:

text_as_int = np.array([char2idx[c] for c in text])

创建数据集

此时,我们用char2idx字典对文本进行矢量化,用idx2char对矢量化后的文本进行去矢量化(即解码)。最后,我们将text_as_int作为矢量化的 NumPy 数组。我们现在可以创建数据集了。

首先,我们将使用来自Dataset模块的from_tensor_slices方法从我们的text_as_int对象创建一个 TensorFlow Dataset 对象,我们将把它们分成几批。数据集的每个输入的长度限制为 100 个字符。我们可以用下面的代码实现所有这些:

char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)
seq_length = 100 # The max. length for single input
sequences = char_dataset.batch(seq_length+1, drop_remainder=True)

我们的sequences对象包含字符序列,但是我们必须创建一个这些序列的元组,以便输入到 RNN 模型中。我们可以通过如下自定义映射函数来实现这一点:

def split_input_target(chunk):
  input_text = chunk[:-1]
  target_text = chunk[1:]
  return input_text, target_text

dataset = sequences.map(split_input_target)

我们生成这些元组的原因是为了让 RNN 工作,我们需要创建一个流水线,如图 9-2 所示。

img/501289_1_En_9_Fig2_HTML.jpg

图 9-2

具有四维输入和输出层的 RNN 的例子。注意输入和输出字符之间的延迟

最后,我们重组数据集,分成 64 个句子批次,每行如下:

BUFFER_SIZE = 10000 # TF shuffles the data only within buffers
BATCH_SIZE = 64 # Batch size

dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
print(dataset)

Output:

<BatchDataset shapes: ((64, 100), (64, 100)), types: (tf.int64, tf.int64)

构建模型

我们的数据已经准备好输入模型流水线。让我们创建我们的模型。我们希望训练我们的模型,然后做出新的预测。重要的是,我们的训练流水线将在每一批输入 64 个句子。因此,我们需要以一次接受 64 个输入句子的方式构建我们的模型。然而,在我们训练了我们的模型之后,我们想要输入单句来生成新的任务。所以,我们需要不同的训练前和训练后模型的批量大小。为了实现这一点,我们需要创建一个函数,它允许我们为不同的批量大小重现模型。下面的代码可以做到这一点:

def build_model(vocab_size, embedding_dim, rnn_units, batch_size):

  model = tf.keras.Sequential([

    tf.keras.layers.Embedding(
            vocab_size,
            embedding_dim,
            batch_input_shape=[batch_size, None]),

    tf.keras.layers.GRU(
            rnn_units,
            return_sequences=True,
            stateful=True,
            recurrent_initializer='glorot_uniform'),

    tf.keras.layers.Dense(vocab_size)

  ])
  return model

我们的模型有三层:

  • 嵌入层:该层作为输入层,接受输入值(数字格式)并将其转换为矢量。

  • GRU 图层:填充了 1024 个梯度下降单元的 RNN 图层

  • 密集层:输出结果,vocab_size输出。

现在,我们可以使用以下代码创建我们的培训模型:

model = build_model(
    vocab_size = len(vocab), # no. of unique characters
    embedding_dim=embedding_dim, # 256
    rnn_units=rnn_units, # 1024
    batch_size=BATCH_SIZE)  # 64 for the training

图 9-3 总结了我们的模型。

img/501289_1_En_9_Fig3_HTML.jpg

图 9-3

培训模型的摘要视图。请注意输出形状中的 64,对于训练后的单个预测,它必须为 1

编译和训练模型

为了编译我们的模型,我们需要配置我们的优化器和损失函数。对于这个任务,我们选择“Adam”作为优化器,选择稀疏分类交叉熵函数作为损失函数。

由于我们的输出总是 65 个字符中的一个,这是一个多类分类问题。因此,我们必须选择一个分类交叉熵函数。然而,在这个例子中,我们选择了分类交叉熵的一个变体:稀疏分类交叉熵。我们使用稀疏分类交叉熵的原因是,即使它们使用相同的损失函数,它们的输出格式也是不同的。请记住,我们将文本矢量化为整数(例如,[0],[2],[1]),而不是一键编码格式(例如,[0,0,0],[0,1],[1,0,0])。为了能够输出整数,我们必须使用稀疏分类交叉熵函数。

为了能够设置我们自定义的损失函数,让我们创建一个包含稀疏分类交叉熵损失的基本 Python 函数:

def loss(labels, logits):
  return tf.keras.losses.sparse_categorical_crossentropy( labels, logits, from_logits=True)

现在,我们可以使用以下代码设置损失函数和优化器:

model.compile(optimizer='adam', loss=loss)

为了能够加载我们的重量并保存我们的训练成绩,我们需要使用以下代码设置和配置一个检查点目录:

# Directory where the checkpoints will be saved
checkpoint_dir = './training_checkpoints'

# Name of the checkpoint files
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt_{epoch}")

checkpoint_callback=tf.keras.callbacks.ModelCheckpoint(
    filepath=checkpoint_prefix,
    save_weights_only=True)

我们的模型和检查点目录已经配置好了。我们将为我们的模型训练 30 个时期,并将训练历史保存到名为 history 的变量中,代码如下:

EPOCHS = 30
history = model.fit(dataset, epochs=EPOCHS, callbacks=[checkpoint_callback])

在训练模型时,我们得到了如图 9-4 所示的以下输出:

img/501289_1_En_9_Fig4_HTML.jpg

图 9-4

模型训练的最后八个时期

由于模型的简单性以及我们对模型进行编码的方式,我们的训练并不需要太长时间(大约 3 4 分钟)。现在,我们可以使用保存的权重并构建一个自定义模型,该模型接受单个输入来生成文本。

用训练好的模型生成文本

为了能够查看我们最新检查点的位置,我们需要运行以下代码:

tf.train.latest_checkpoint(checkpoint_dir)
Output:
./training_checkpoints/ckpt_30

现在,我们可以使用之前创建的定制build_model()函数,使用latest_checkpoint中保存的权重,用batch_size=1load weights构建一个新模型,并使用build()函数基于接收到的输入形状(即[1, None])构建模型。我们可以用下面的代码实现所有这些和summarize()关于我们新模型的信息:

model = build_model(vocab_size, embedding_dim, rnn_units, batch_size=1)
model.load_weights(tf.train.latest_checkpoint(checkpoint_dir))
model.build(tf.TensorShape([1, None]))
model.summary()

输出如图 9-5 所示:

Output:

img/501289_1_En_9_Fig5_HTML.jpg

图 9-5

新创建的模型的概要视图。现在它接受单一输入

我们的模型已经准备好进行预测,我们所需要的只是一个定制函数来为模型准备输入。我们必须设置以下内容:

  • 要生成的字符数

  • 向量化输入(从字符串到数字)

  • 存储结果的空变量

  • 手动调整预测可变性的温度值

  • 对输出进行去因子化,并且还将输出再次馈送到模型,用于下一次预测

  • 将所有生成的字符连接成一个最终字符串

以下自定义函数完成所有这些工作:

def generate_text(model, num_generate, temperature, start_string):
  input_eval = [char2idx[s] for s in start_string] # string to numbers (vectorizing)
  input_eval = tf.expand_dims(input_eval, 0) # dimension expansion
  text_generated = [] # Empty string to store our results
  model.reset_states() # Clears the hidden states in the RNN

  for i in range(num_generate): #Run a loop for number of characters to generate
    predictions = model(input_eval) # prediction for single character
    predictions = tf.squeeze(predictions, 0) # remove the batch dimension

    # using a categorical distribution to predict the character returned by the model
    # higher temperature increases the probability of selecting a less likely character
    # lower --> more predictable

    predictions = predictions / temperature
    predicted_id = tf.random.categorical(predictions, num_samples=1)[-1,0].numpy()

    # The predicted character as the next input to the model
    # along with the previous hidden state
    # So the model makes the next prediction based on the previous character
    input_eval = tf.expand_dims([predicted_id], 0)
    # Also devectorize the number and add to the generated text
    text_generated.append(idx2char[predicted_id])

  return (start_string + ''.join(text_generated))

它返回我们的最终预测值,我们可以使用下面一行轻松地生成一个文本:

generated_text = generate_text(
                    model,
                    num_generate=500,
                    temperature=1,
                    start_string=u"ROMEO")

我们可以用内置的打印功能打印出来:

print(generated_text)

Output:
ROMEO:

Third Servingman:
This attemptue never long to smile
under garlands grass and enterhoand of death.

GREMIO:
Have I not fought for such a joy? can come to Spilet O, thy husband!
Go, sirs, confusion's cut off? princely Noboth, my any thing thee;
Whereto we will kiss thy lips.

ANTIGONUS:
It is your office: you have ta'en her relatants so many friends as they
or no man upon the market-play with thee!

GRUMIO:
First, know, my lord.

KING RICHARD II:
Then why.

CORIOLANUS:
How like a tinker? Was e

如您所见,我们的模型能够生成任意长度的文本。请注意:我们的模型使用字符,因此模型的奇迹在于它学会了从字符中创建有意义的单词。所以,不要认为它把一堆不相关的单词加在一起。它检查了数千个单词,学习了不同字符之间的关系,以及如何使用它们来创建有意义的单词。然后它复制这个,并返回给我们有意义的单词的句子。

请玩玩温度,看看如何将输出从更合适的单词变成更失真的单词。较高的温度值会增加我们的功能选择不太可能的字符的机会。当我们把它们都加起来时,我们得到的结果就没那么有意义了。另一方面,低温会导致函数生成更简单的文本,更像是原始语料库的副本。

结论

在这一章中,我们讨论了自然语言处理,它是人工智能的一个分支,处理文本数据。我们介绍了 NLP 研究中使用的主要方法和技术。我们也简单参观了 NLP 的时间线。我们最后进行了一个案例研究,使用循环神经网络生成类似莎士比亚的文本。

在下一章,我们将讨论推荐系统,它是我们今天所知的大型科技公司提供的许多服务的支柱。

十、推荐系统

推荐系统(RSs)是强大的信息过滤系统,它根据用户的偏好和项目的特征对项目进行排序并推荐给用户。这些推荐可以从看哪部电影到购买什么产品,从听哪首歌到接受哪种服务。推荐系统的目标是向用户推荐合适的项目,以建立信任关系,实现长期的商业目标。大多数大型科技公司,如亚马逊、网飞、Spotify、YouTube 和谷歌,都在很大程度上受益于推荐系统;亚马逊示例见图 10-1 。

img/501289_1_En_10_Fig1_HTML.jpg

图 10-1

Amazon.com 礼品创意推荐系统

让我们在下一节看看推荐系统的流行方法。

流行的方法

有多种方法可以创建一个强大的推荐系统,但是最常用的两种方法是(I)协同过滤和(ii)基于内容的过滤。在本节中,我们将简要介绍这些过滤方法。

协同过滤

协同过滤是一种推荐方法,它基于具有相似特征的用户的反应来过滤出用户可能偏好的项目。它基于将用户分组为具有相似偏好的更小的组,并向他们推荐组中其他成员满意的项目。

协作过滤的主要假设是,过去同意的用户倾向于将来同意。因此,纯粹的协同过滤系统只需要用户对一组给定项目的历史偏好数据。图 10-2 显示了协同过滤方法的直观解释。

img/501289_1_En_10_Fig2_HTML.png

图 10-2

协作推荐系统的描述

协同过滤子方法

在协同过滤方法中也有子方法。协同过滤可以是(I)基于记忆的或者(ii)基于模型的。基于记忆的方法是基于使用选定的度量(例如,余弦相似性或皮尔逊相关)来寻找相似的用户,并对评级进行加权平均。虽然它很容易构建并且更具可解释性,但是当数据有限时,它的性能并不好。另一方面,基于模型的方法利用机器学习来预测未评级项目的预期用户评级。虽然这种方法妨碍了模型的可解释性,但在可用数据有限的情况下,这种方法更有效。

数据收集

由于协同过滤是基于用户的历史数据,所以采用协同方法开发推荐系统的一个重要步骤是收集用户的反馈和偏好数据。该数据可以是用户的显式反馈或隐式行为。

显式数据收集

显式数据收集包括用户直接提供给系统的所有数据。这包括

  • 用户对滑动标尺上的项目的评分

  • 用户的项目在收藏中从最喜欢到最不喜欢的排序

  • 用户在两个或多个项目之间的选择

  • 用户最喜欢的项目列表

隐式数据收集

隐式数据收集基于用户的可观察行为。这些观察可以在系统内部进行,也可以在系统外部使用 cookies 和第三方解决方案等工具进行。隐式数据包括

  • 用户的已查看项目列表

  • 用户在线购买的商品的记录

  • 用户访问的网站

  • 用户的社交网络参与度

关于协同过滤的问题

尽管协同过滤工作得非常好,但是推荐系统可能会遇到三个常见问题:

  • 冷启动

  • 可量测性

  • 稀少

冷启动

当推荐系统首次被部署时,关于用户和项目的可用数据的大小是最小的,这是非常普遍的。此外,即使当推荐系统成熟时,关于新增加的用户或产品的足够信息也可能比期望的数量少得多,这对于做出有价值的推荐是至关重要的。因此,当没有足够的信息时,推荐系统往往不能提供有用的推荐,这被称为“冷启动”问题。

可量测性

关于基于协同过滤的推荐系统的另一个问题是可扩展性。特别是当基于存储器的方法被用于具有数百万用户的系统时,计算相似性度量可能变成非常耗时和资源密集型的任务。

稀少

最后,收集关于项目的足够信息可能是建立成功的推荐系统的另一个问题。这是因为基于观看、销售或收听的项目的反馈百分比较低。因此,低水平的反馈比率可能会降低结果的重要性,并提供不正确的项目排名。

基于内容的过滤(基于个性的方法)

基于内容的过滤是推荐系统的另一种流行方法。内容指的是用户参与的项目的内容或属性。在基于内容的过滤方法中,项目被分类,并且基于用户的有限反馈,系统推荐属于用户喜欢的类别的新项目。例如,当你给动作片正面评价,给儿童片负面评价时,基于内容过滤的推荐系统会给你推荐另一部动作片。

对于基于内容的过滤,项目和用户都用关键字标记,以便对它们进行分类。项目基于它们的属性被标记,而为了标记用户,专用模型被设计为基于他们与推荐系统的交互来创建用户简档。向量空间表示算法(例如,tf-idf)用于提取项目的特征。然后,系统根据算法做出推荐。

近年来,单纯基于内容过滤的推荐系统已经不再流行。通常,基于内容的过滤与其他过滤方法一起使用来创建混合模型。

其他推荐系统方法

除了协作和基于内容的推荐系统之外,还有其他种类的推荐系统正在被越来越多地使用。

  • 多标准推荐系统:这些推荐系统使用不止一个标准来进行推荐。一般来说,推荐系统收集对一个项目的单个偏好评级。但是更复杂的用户评级系统可以帮助创建更准确和更先进的推荐系统。例如,在电影推荐系统中,收集特定电影方面(例如,表演、视觉效果、演员)的评级可以提高推荐性能,而不是收集给定电影的总体评级。

  • 风险感知推荐系统:集成了风险度量的推荐系统称为风险感知推荐系统。例如,推荐的频率(例如,30 次/天)和定时(例如,在营业时间)可能影响用户的体验,并且这些推荐系统将这些特征考虑在内以增强用户体验。

  • 移动推荐系统:移动推荐系统利用移动设备收集的数据。这些推荐系统通常实时操作,基于用户的变化状态(例如,用户的位置)进行瞬时更新。

  • 混合推荐系统:混合推荐系统结合了多种方法,如基于内容的过滤、协同过滤和风险评估。他们从每种方法的输出中进行选择、混合或加权,并提供最终的推荐输出。

请注意,前面的列表是一个不完整的列表,它随着计算机和数据科学的进步而增长。

案例研究|使用 MovieLens 数据集的深度协同过滤

我们将使用模型子类化构建一个定制的神经网络来实现协同过滤。记住,协同过滤的主要假设是,过去同意的用户将来也会同意。因此,纯粹的协同过滤系统只需要用户对一组项目的历史偏好。

MovieLens 数据集

在这个案例研究中,我们使用了一个流行的电影评级数据集 MovieLens,它是由 GroupLens 设计和维护的。GroupLens 是明尼苏达大学计算机科学与工程系的一个研究实验室,他们在位于 https://grouplens.org/datasets/movielens/ 的网页上维护了大量数据集。在此页面上,您可以访问许多具有不同数量观察值的数据集。

我们更喜欢一个小数据集:MovieLens 最新的小数据集包括 100,000 个评级和 3,600 个标签应用程序,由 600 个用户应用于 9,000 部电影。数据集的大小大约只有 1 MB,这使得我们的网络训练过程非常快。数据集在 http://files.grouplens.org/datasets/movielens/ml-latest-small.zip 可用。

在案例研究期间,我们将深入研究数据集的列(即,userIdmovieIdratingtimestamp)的含义。让我们从最初的进口开始

初始进口

本案例研究需要六个初始导入,它们是为了以下功能而导入的:

  • TensorFlow :建立和训练我们的模型,并进行预测

  • ZipFile :解压保存为 zip 文件的 MovieLens 数据集

  • 熊猫:创建数据帧并执行基本的数据处理任务

  • NumPy :生成 NumPy 数组,进行数据处理任务

  • 来自 scikit 的train_test_split-学习:进行训练和测试分割操作

  • 从 TensorFlow 嵌入:从 TensorFlow 导入嵌入图层

  • get_file 来自 TensorFlow :从外部 URL 下载数据集

下面几行导入所有相关的库和函数:

import tensorflow as tf
from zipfile import ZipFile
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from tensorflow.keras.layers import Embedding
from tensorflow.keras.utils import get_file

加载数据

既然我们已经完成了最初的导入,我们就可以把精力集中在数据处理和模型构建上了。我们将从官方发布者的网站 GroupLens.org 下载和加载我们的数据。然后,我们将使用 TensorFlow 的get_file()函数下载数据集,代码如下:

URL = "http://files.grouplens.org/datasets/movielens/ml-latest-small.zip"
movielens_path = get_file("movielens.zip", URL, extract=True)

Colab 将临时下载并保存包含多个 CSV 文件的 zip 文件。为了能够打开这些 CSV 文件中的一个,我们需要ZipFile()函数,其工作方式如下:

with ZipFile(movielens_path) as z:
   with z.open("ml-latest-small/ratings.csv") as f:
      df = pd.read_csv(f)

使用前面的代码,我们将评级表保存为 Pandas DataFrame,如图 10-3 所示。

img/501289_1_En_10_Fig3_HTML.jpg

图 10-3

评级数据集的前五行

评级数据框架有四列:

  • userId :每个用户的 Id 号

  • movieId :每部电影的 Id 号

  • 评分:特定用户对电影的评分

  • 时间戳:显示用户对电影的评分时间

现在我们知道了我们的数据集,是时候处理我们的列并为深度学习模型做准备了。

处理数据

在我们的 MovieLens 数据集中,用户 id 从 1 开始,电影 id 不是连续的。这对于训练期间的计算效率来说不是很健康。因此,我们会给他们新的 ID 号,以后可以映射回原来的 ID 号。

正在处理用户 id

我们首先需要枚举唯一的用户 id,并根据枚举的用户 id 创建一个字典。然后,我们还用这些枚举 id 创建一个反向字典(键和值是反向的)。然后,我们为新用户 id 创建一个新列,名为user。最后,我们用下面的代码将唯一用户计数保存为num_users:

user_ids = df["userId"].unique().tolist()
user2user_encoded = {x: i for i, x in enumerate(user_ids)}
user_encoded2user = {i: x for i, x in enumerate(user_ids)}
df["user"] = df["userId"].map(user2user_encoded)
num_users = len(user_encoded2user)

正在处理电影 id

对于电影 id,我们也遵循与用户 id 相似的路径。这一步对于电影 id 更为重要,因为这些 id 在我们的数据集中不是连续给出的。下面的代码处理电影 id,用新 id 创建一个新列,并将唯一的电影计数保存为num_movies:

movie_ids = df["movieId"].unique().tolist()
movie2movie_encoded = {x: i for i, x in enumerate(movie_ids)}
movie_encoded2movie = {i: x for i, x in enumerate(movie_ids)}
df["movie"] = df["movieId"].map(movie2movie_encoded)
num_movies = len(movie_encoded2movie)

现在,我们可以使用以下代码查看数据集中有多少用户和电影:

print("Number of users: ", num_users,
      "\nNumber of Movies: ", num_movies)
Output:
Number of users:  610
Number of Movies:  9724

处理评级

对于评级,我们所要做的就是为了计算效率和模型的可靠性将它们标准化。我们需要检测最小和最大额定值,然后应用 lambda 函数进行最小最大标准化。下面的代码成功地做到了这一点:

min, max  = df["rating"].min(), df["rating"].max()
df["rating"] = df["rating"].apply(lambda x:(x-min)/(max-min))

让我们用图 10-4 最后看一下我们处理过的测向数据帧:

img/501289_1_En_10_Fig4_HTML.jpg

图 10-4

已处理评级数据集的前五行

分割数据集

由于这是一项监督学习任务,我们需要将数据分为(I)特征(x)和标签(Y)以及(ii)训练和验证集。

对于要素和标注分割,我们只需选择列并将其另存为新变量,如下所示:

X = df[["user", "movie"]].values
y = df["rating"].values

新的usermovie列是我们的功能,我们将使用它们来预测一部未上映电影的用户评级。

对于训练和验证分割,我们可以使用 scikit-learn 中的train_test_split()函数,它可以分割和打乱我们的数据集。以下代码足以拆分我们的数据集:

(x_train, x_val, y_train, y_val) = train_test_split(
          X, y,
          test_size=0.1,
          random_state=42)

让我们来看看四个新数据集的形状:

print("Shape of the x_train: ", x_train.shape)
print("Shape of the y_train: ", y_train.shape)
print("Shape of the x_val: ", x_val.shape)
print("Shape of the x_val: ", y_val.shape)
Output:
Shape of the x_train:  (90752, 2)
Shape of the y_train:  (90752,)
Shape of the x_val:  (10084, 2)
Shape of the x_val:  (10084,)

构建模型

在 TensorFlow 中,除了顺序 API 和函数 API 之外,还有第三种构建模型的选择:模型子类化。在模型子类化中,我们可以自由地从头开始实现任何东西。模型子类化是完全可定制的,使我们能够实现我们自己的定制模型。这是一个非常强大的方法,因为我们可以建立任何类型的模型。但是,它需要基本的面向对象编程知识。我们的自定义类将子类化tf.keras.Model对象。它还需要声明几个变量和函数。不过,这没什么可怕的。要构建模型,我们只需完成以下任务:

  • 创建一个类扩展keras.Model对象。

  • 创建一个__init__函数来声明我们在模型中使用的七个变量:

    • embedding_size

    • num_users

    • user_embedding

    • user_bias

    • num_movies

    • movie_embedding

    • movie_bias

  • 创建一个调用函数,告诉模型如何用函数.初始化变量来处理输入

  • 在一个 Sigmoid 激活层之后返回输出。

下面的代码做到了所有这些(请注意,大部分代码都是注释):

class RecommenderNet(tf.keras.Model):
    # __init function is to initialize the values of
    # instance members for the new object
    def __init__(self, num_users, num_movies, embedding_size, **kwargs):
        super(RecommenderNet, self).__init__(**kwargs)
        # Variable for embedding size
        self.embedding_size = embedding_size

        # Variables for user count, and related weights and biases
        self.num_users = num_users
        self.user_embedding = Embedding(
            num_users,
            embedding_size,
            embeddings_initializer="he_normal",
            embeddings_regularizer=tf.keras.regularizers.l2(1e-6),
        )
        self.user_bias = Embedding(num_users, 1)

        # Variables for movie count, and related weights and biases
        self.num_movies = num_movies
        self.movie_embedding = Embedding(
            num_movies,
            embedding_size,
            embeddings_initializer="he_normal",
            embeddings_regularizer=tf.keras.regularizers.l2(1e-6),
        )
        self.movie_bias = Embedding(num_movies, 1)

    def call(self, inputs):
        # call function is for the dot products
        # of user and movie vectors
        # It also accepts the inputs, feeds them into the layers,
        # and feed into the final sigmoid layer

        # User vector and bias values with input values
        user_vector = self.user_embedding(inputs[:, 0])
        user_bias = self.user_bias(inputs[:, 0])

        # Movie vector and bias values with input values
        movie_vector = self.movie_embedding(inputs[:, 1])
        movie_bias = self.movie_bias(inputs[:, 1])
        # tf.tensordot calculcates the dot product
        dot_user_movie = tf.tensordot(user_vector, movie_vector, 2)
        # Add all the components (including bias)
        x = dot_user_movie + user_bias + movie_bias

        # The sigmoid activation forces the rating to between 0 and 1
        return tf.nn.sigmoid(x)

在声明了RecommenderNet类之后,我们可以创建这个定制类的一个实例来构建我们的定制RecommenderNet模型:

model = RecommenderNet(num_users, num_movies, embedding_size=50)

编译和训练模型

创建模型后,我们可以配置它。由于我们致力于预测一部未上映电影的收视率,这更像是一项回归任务。因此,使用均方误差(MSE)度量而不是交叉熵度量将是更好的选择。此外,我们还选择 Adam optimizer 作为我们的优化器。以下代码完成了所有这些工作:

model.compile(
    loss='mse',
    optimizer=tf.keras.optimizers.Adam(lr=0.001)
)

我们将使用以下代码为 5 个时期训练我们的定制模型:

history = model.fit(
    x=x_train,
    y=y_train,
    batch_size=64,
    epochs=5,
    verbose=1,
    validation_data=(x_val, y_val),
)

图 10-5 显示了每个时期的 MSE 损失值。

img/501289_1_En_10_Fig5_HTML.jpg

图 10-5

在我们的定制模型训练期间的纪元统计

提出建议

现在我们的模型已经训练好了,可以用协同过滤来做推荐了。我们可以用下面的代码随机选择一个用户 ID:

user_id = df.userId.sample(1).iloc[0]
print("The selected user ID is: ", user_id)

Output: The selected user ID is:  414

下一步是过滤掉用户以前看过的电影。下面的代码列出了用户以前没有看过的电影:

movies_watched = df[df.userId == user_id]
not_watched = df[~df['movieId'].isin(movies_watched.movieId.values)]['movieId'].unique()
not_watched = [[movie2movie_encoded.get(x)] for x in not_watched]
print('The number of movies the user has not seen before: ', len(not_watched))

Output: The number of movies the user has not seen before is 7026

通过下面的代码,我们获得了在初始数据处理步骤中提供给用户的新 ID 号,然后使用np.hstack()函数创建一个 NumPy 数组,并使用model.predict()函数生成预测的电影评级:

user_encoder = user2user_encoded.get(user_id)
user_movie_array = np.hstack(
        ([[user_encoder]] * len(not_watched), not_watched )
        )
ratings = model.predict(user_movie_array).flatten()

前面的代码给了我们一个 NumPy 数组,其中包含所有电影的标准化评级值。但是我们不需要全部。我们只需要收视率最高的前 10 部电影。此外,我们需要他们的身份证号码,以便我们可以映射他们,了解他们有哪些头衔。

NumPy argsort()函数对所有项目进行排序,并返回它们的索引(id)。最后,我们需要颠倒它们,因为它是按升序工作的。以下代码完成了所有这些任务:

top10_indices = ratings.argsort()[-10:][::-1]

以下代码将我们分配的电影 id 转换为数据集中给定的原始电影 id:

recommended_movie_ids = [
    movie_encoded2movie.get(not_watched[x][0]) for x in top10_indices
]

现在我们有了前 10 部电影的原始 id。但是我们不能只向用户显示电影 id。相反,我们希望向他们显示电影标题及其类型信息。因此,我们需要使用 zip 文件中的另一个 CSV 文件:movies.csv。以下代码将加载数据集并创建一个名为movie_df的熊猫数据帧(输出见图 10-6 ):

# Create a DataFrame from Movies.csv file
with ZipFile(movielens_path) as z:
   with z.open("ml-latest-small/movies.csv") as f:
      movie_df = pd.read_csv(f)
movie_df.head(2)

img/501289_1_En_10_Fig6_HTML.jpg

图 10-6

电影数据集的前两行

让我们通过过滤用户观看的前 10 部电影来检查用户已经观看并给出高评级的电影:

top_movies_user = (
    movies_watched.sort_values(by="rating", ascending=False)
    .head(10)
    .movieId.values
)
movie_df_rows = movie_df[movie_df["movieId"].isin(top_movies_user)]

我们可以通过运行下面的代码来查看它们,如图 10-7 所示:

print("Movies with high ratings from user")
movie_df_rows[['title','genres']]

img/501289_1_En_10_Fig7_HTML.jpg

图 10-7

用户评价高的电影列表

现在我们还可以查看我们的协同过滤模型推荐给用户的前 10 部电影,代码如下,如图 10-8 。

recommended_movies = movie_df[movie_df["movieId"].isin(recommended_movie_ids)]
print("Top 10 movie recommendations")
recommended_movies[['title','genres']]

img/501289_1_En_10_Fig8_HTML.jpg

图 10-8

为用户推荐的前 10 部电影

正如你所看到的,用户观看的大多数电影都是经典电影,我们的推荐系统还向用户推荐了从 20 世纪 40 年代到 20 世纪 70 年代的电影。此外,观看的电影和推荐的电影在类型上也很相似。

在这个案例研究中,我们成功地构建了一个基于纯协同过滤方法的工作推荐系统。您可以轻松地更改userId并为其他用户提供建议,以测试模型的成功。此外,您可以使用不同的、可能更大的 MovieLens 数据集来提高模型的准确性。试着改变变量,测试你的模型。

结论

在这一章中,我们介绍了推荐系统,它可以使用神经网络来构建。我们介绍了推荐系统的不同方法,并使用基于深度协同过滤的 MovieLens 数据集构建了一个推荐系统。这个推荐系统能够推荐用户最喜欢的未看过的电影。

在下一章,我们将介绍自编码器网络,它主要用于无监督学习任务。

十一、自编码器

在前几章中,我们讨论了前馈神经网络、CNN 和 rnn。这些网络主要用于监督学习任务。在这一章中,我们重点关注自编码器(见图 11-1 ),一种主要用于无监督学习任务的神经网络架构。

自编码器的主要承诺是学习给定数据集的编码结构和解码结构。自编码器主要用于降维、降噪和一些生成任务。有几种为特定任务设计的自编码器,但首先,让我们深入了解自编码器的架构。

img/501289_1_En_11_Fig1_HTML.png

图 11-1

自编码器架构的粗略可视化

自编码器的优点和缺点

自编码器是非常有前途的神经网络架构,被认为是其他无监督机器学习模型(如主成分分析)的强大替代方案。

实际上,自编码器可以做 PCA 模型所做的一切,甚至更多。纯线性自编码器将给出与 PCA 相同的结果。但是,在非线性特征提取问题中,自编码器可以比 PCA 模型做得更多。在大多数情况下,这些问题具有非线性性质,因此,它们肯定优于 PCA 模型。

但并非一切都是黑白分明的。就像其他神经网络架构一样,与 PCA 模型相比,自编码器需要大量数据和计算能力。此外,自编码器训练中使用的结构不良的训练数据集甚至会进一步模糊我们试图提取的特征,因为自编码器专注于提取所有信息,而不是提取相关信息。因此,结构不良的数据集可能对解决机器学习任务有害。

在半监督学习任务中,自编码器可以与不同的神经网络架构耦合,例如前馈 NNs、CNN 和 RNNs。这些组合可以在几个机器学习任务中提供成功的结果。但这也可能进一步损害模型的可解释性。尽管存在缺点,但自编码器提供了许多好处,并且可以(I)与其他神经网络结合使用,以及(ii)独立地用于无监督和半监督的学习任务。

自解压体系结构

自编码器是由 Hinton 和 PDP 小组在 20 世纪 80 年代首次推出的。该建议的主要目的是解决无监督的反向传播问题(也称为“无教师反向传播”问题)。

自编码器网络最重要的结构特征是能够对输入进行编码,但只能解码为原始形式。因此,自编码器的输入端和输出端几乎只接收相同的数据。这将消除监管标签数据的必要性。因此,在每个自编码器网络中有一个编码器网络和一个解码器网络。这些编码器和解码器网络通过一个狭窄的潜在空间连接,如图 11-2 所示。

img/501289_1_En_11_Fig2_HTML.png

图 11-2

自编码器网络的例子

由于自编码器的主要任务是确保输入和输出值的等效性,因此自编码器网络被迫保留网络中最相关的信息,以便最终重建输入值。这种性质使得自编码器非常适合于降维、特征学习和降噪(即去噪)。

自编码器的最基本形式由三个主要部分组成:(I)输入层,(ii)潜在空间层,和(iii)输出层。输入层与潜在空间一起构成编码器网络,而输出层与潜在空间一起构成解码器网络。简单的多层感知器将编码器和解码器结合在一起,是基本自编码器的一个例子,如图 11-3 所示。

img/501289_1_En_11_Fig3_HTML.png

图 11-3

基本自编码器网络的示例

目标是以优化的方式调整权重,以最小化输入图层值和输出图层值之间的差异。这是通过误差项的反向传播实现的,类似于前馈神经网络。

自编码器中使用的层

自编码器中可以使用的层可能因问题而异。每个自编码器必须有一个编码器和一个解码器网络,它们通过一个层,即潜在空间连接。这是 autoencoder 的承诺,您可以在这些网络中添加任何类型的层,包括但不限于密集层、卷积层、池化层、LSTM 层和 GRU 层。事实上,根据手头任务的性质,编码器和解码器网络可以设计为独立的前馈、CNN 或 RNN 网络。另一方面,在大多数自编码器应用中,解码器网络被设计为编码器网络的反向版本,以确保模型的收敛性。例如,当您使用卷积层构建基于 CNN 的编码器时,解码器网络必须由转置卷积层组成。

深度的优势

在图 11-3 中,你可以看到自编码器的基本版本。但是,在大多数实际应用中,编码器和解码器网络中插入多层有三个原因:

  • 与浅层自编码器相比,压缩效果更好:与浅层自编码器相比,多层自编码器在将重要信息压缩到潜在空间方面做得更好。

  • 更低的代价(误差)测度:一般来说,多层网络更擅长收敛于复杂函数,这就降低了 MSE 等代价测度。

  • 所需的训练数据量更少:当可用数据量有限时,多层自编码器比浅层自编码器收敛得更好。

在下一节中,让我们看看自编码器的变化。

自编码器的变体

所有的自编码器类型都有一个编码器-解码器架构,但是有几种不同的自编码器来处理特定的机器学习任务。有三大类:(I)欠完备自编码器,(ii)正则化自编码器,和(iii)变分自编码器。

欠完整自编码器

欠完整自编码器是基本的自编码器,它将潜在空间中的神经元数量限制为比输入层更小的尺寸。与输入层中的神经元计数相比,其潜在空间中的神经元计数较小的自编码器称为欠完整自编码器。

欠完成自编码器将输入复制到输出,这似乎毫无意义。但是,自编码器的有用部分是它的潜在空间,解码器的输出很少被使用。自编码器的目标是通过在位于编码器和解码器网络交叉点的潜在空间中总结特征来提取特征。

然而,在某些情况下,autoencoder 只是将任务从编码器复制到解码器(即,从输入到输出),而没有学到任何重要的东西。为了能够消除这种可能性,自编码器的容量受到正则化方法的限制。这些容量有限的自编码器组成了正则化自编码器家族。

正则化自编码器

在自编码器中遇到的主要问题之一是倾向于为解码器制作编码器结构的对称副本。这个问题损害了自编码器从模型中获得有意义的特征的能力。有几种方法可以防止自编码器为解码器复制其编码器网络,这对捕获信息至关重要。规则化自编码器的变体配置有专门的成本函数,该成本函数鼓励这些自编码器发现有意义的特征,并防止它们将输入无用地复制到输出。正则化自编码器有三种流行的变体:

  • 稀疏自编码器

  • 降噪自编码器(DAE)

  • 收缩式自编码器

稀疏自编码器

稀疏自编码器(SAE)是依赖于潜在空间内活动神经元的稀疏性的自编码器。一般来说,潜在空间中的神经元数量少于输入层和输出层中的神经元数量,这使得它们欠完备。另一方面,有一些自编码器,其潜在空间中的神经元比输入层中的多,这被称为过完备。

欠完整和过完整自编码器在特定情况下都可能无法学习到有意义的特征,而稀疏自编码器通过向潜在空间引入稀疏性来解决这个问题。在训练过程中,一些神经元被故意停用,这迫使模型从数据中学习有意义的特征。因此,自编码器必须响应数据集的独特统计特征,而不仅仅是作为满足等式的唯一目的的身份函数。稀疏自编码器通常用于提取用于另一项任务(如分类)的特征。

降噪自编码器(DAE)

去噪自编码器(DAE)是特殊的自编码器,其被设计成通过进行精确的近似来最小化原始输入和输入的损坏副本之间的误差。因此,去噪自编码器必须找到方法来测量损坏的副本和原始副本之间的差异。在他们了解损坏的副本和原始副本之间的差异以及如何消除这种差异后,他们可以用来清理有噪声的数据。例如,我们可以使用图像数据集及其添加了噪声的副本来训练去噪自编码器网络。然后,这个经过训练的模型可以在现实世界中用于清理有噪声的图像文件。

收缩式自编码器

收缩型自编码器(CAE)主要与其他类型的自编码器并行使用。由于它们对训练数据集中的小变化不太敏感,因此它们在维数减少和生成任务中非常方便,特别是当其他自编码器类型无法学习有意义的特征时。这种学习是通过向优化器算法试图最小化的成本函数添加特定的正则项来实现的。该特定正则化符对应于关于输入数据的编码器激活的雅可比矩阵(函数的所有一阶偏导数的矩阵)的 Frobenius 范数。

img/501289_1_En_11_Fig4_HTML.png

图 11-4

变分自编码器的可视化

虽然收缩自编码器的正则化策略类似于稀疏自编码器,但是其对小变化的阻力尽管采用不同的手段也显示出与去噪自编码器的阻力相似。如前所述,当其他自编码器无法学习有意义的特性时,它们通常与其他自编码器一起使用,作为最后的手段。

可变自编码器(VAE)

与其他自编码器(如稀疏和去噪自编码器)不同,变分自编码器(vae)主要用于生成任务。它们的功能更类似于生成对抗网络,并且由于它们的网络架构(由编码器网络和解码器网络组成),它们被视为自编码器的变体。

对于生成性任务,我们需要连续函数的随机变化。然而,普通的自编码器不提供连续的空间。因此,与其他自编码器相比,VAEs 的不同之处在于它位于潜在空间中的连续空间。

连续空间由两个神经元创建,一个均值神经元和一个方差神经元。这两个神经元用来得到一个采样编码,这个采样编码传递给解码器,如图 11-4 所示。由于编码是从具有与输入相同的均值和方差的分布中生成的,所以解码器从涉及相同潜在空间的所有邻近点中学习,这使得模型能够使用输入数据生成相似但不相同的输出。

自编码器的使用案例

尽管自编码器的传统用例是降维,但是随着围绕自编码器的研究的成熟,已经观察到了自编码器的新用例。自编码器使用案例的非详尽列表如下:

  • 降维:通过将输入层的高特征空间映射到潜在空间的低特征空间,自编码器可以降低维数。具有线性激活函数的非常基本的自编码器将使用主分量分析(PCA)方法呈现相同的结果。

  • 降噪:尤其是降噪自编码器可以成功去除图像、视频、声音和其他类型数据中的噪声。

  • 图像处理:自编码器可用于图像压缩和图像去噪。

  • 药物发现:变分编码器由于其生成性,可用于药物发现。

  • 机器翻译:通过将源语言的文本作为输入,将目标语言的文本作为输出,自编码器可以学习神经机器翻译所需的重要特征。

  • 此外,自编码器还用于许多其他任务,如信息检索、异常检测、群体合成和流行度预测。

案例研究|时尚 MNIST 图像去噪

既然我们已经讨论了自编码器的概念部分,我们可以继续进行案例研究了。在这个案例研究中,我们采用了 TensorFlow 的一个官方教程“自编码器简介”。 1

案例研究的目标是去噪(清除噪声)图像。对于这项任务,我们应用随机噪声的整个数据集。然后,我们将这个包含噪声图像的数据集提供给自编码器的一端,而将干净的版本提供给另一端。在训练步骤之后,我们的自编码器学习如何清除图像噪声。

时尚 MNIST 数据集

在这个案例研究中,我们使用人工智能社区的另一个流行数据集:时尚 MNIST。时尚 MNIST 由位于德国柏林的欧洲电子商务公司 Zalando 设计和维护。时尚 MNIST 由 60,000 幅图像的训练集和 10,000 幅图像的测试集组成。每个示例都是 28 x 28 灰度图像,与来自 10 个类别的标签相关联。时装 MNIST,包含服装项目的图像(如图 11-5 所示),被设计为包含手写数字的 MNIST 数据集的替代数据集。

初始进口

本案例研究需要七个初始导入,它们是为了以下功能而导入的:

  • TensorFlow :建立和训练我们的模型,并进行预测

  • Matplotlib :发现我们的数据集并可视化我们的结果

  • NumPy :生成 NumPy 数组,进行数据处理任务

  • 熊猫:创建数据帧并执行基本的数据处理任务

  • fashion_mnist 来自 TensorFlow :将时尚 MNIST 数据集直接加载到 Colab 笔记本

  • 来自 scikit 的train_test_split-学习:进行训练和测试分割操作

  • TensorFlow 中的Conv2DTranspose Conv2D Input 图层:使用这些图层构建自编码器模型

下面几行导入所有相关的库和方法:

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from tensorflow.keras.datasets import fashion_mnist
from sklearn.model_selection import train_test_split
from tensorflow.keras.layers import Conv2DTranspose, Conv2D, Input

加载和处理数据

初始导入后,我们可以使用以下代码轻松下载和加载时尚 MNIST 数据集:

# We don't need y_train and y_test
(x_train, _), (x_test, _) = fashion_mnist.load_data()
print('Max value in the x_train is', x_train[0].max())
print('Min value in the x_train is', x_train[0].min())
Output:
Max value in the x_train is 255
Min value in the x_train is 0

现在我们有两个数据集,包含代表图像像素值的数组。注意,我们不会使用标签,所以我们甚至没有保存 y 值。

让我们取一个数据集样本,并使用以下 Matplotlib 代码绘制图像:

fig, axs = plt.subplots(5, 10)
plt.figure(figsize=(5, 10))
fig.tight_layout(pad=-1)
a = 0
for i in range(5):
  for j in range(10):
    axs[i, j].imshow(tf.squeeze(x_test[a]))
    axs[i, j].xaxis.set_visible(False)
    axs[i, j].yaxis.set_visible(False)
    a = a + 1
    plt.gray()

图 11-5 显示了输出,即所选服装项目的网格:

Output:

img/501289_1_En_11_Fig5_HTML.jpg

图 11-5

来自时尚 MNIST 数据集的示例

为了计算效率和模型可靠性,我们必须对我们的图像数据应用最小最大归一化,将值范围限制在 0 和 1 之间。因为我们的数据是 RGB 格式的,所以我们的最小值是 0,最大值是 255,我们可以用下面几行进行最小最大规范化操作:

x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.

我们还必须调整 NumPy 数组的形状,因为数据集的当前形状是(60000,28,28)和(10000,28,28)。我们只需要添加一个具有单一值的第四维(),例如从(60000,28,28)到(60000,28,28,1) 。第四维空间很大程度上证明了我们的数据是灰度格式的,用一个值表示从白色到黑色的颜色信息。如果我们有彩色图像,那么我们就需要第四维中的三个值。但是我们所需要的是包含单一值的第四维,因为我们使用灰度图像。以下代码行可以做到这一点:

x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]

让我们用下面几行来看看 NumPy 数组的形状:

print(x_train.shape)
print(x_test.shape)
Output:
(60000, 28, 28, 1)
(10000, 28, 28, 1)

向图像添加噪声

记住,我们的目标是创建一个去噪的自编码器。对于这项任务,我们需要图像文件的干净和嘈杂的副本。自编码器的任务是调整其权重,以复制噪声处理过程,并能够消除噪声图像。换句话说,我们故意在图像中添加随机噪声来扭曲它们,这样我们的自编码器就可以知道它们是如何变成噪声的,以及如何去噪。因此,我们需要在现有图像中添加噪声。

我们通过使用tf.random.normal方法给每个数组项添加一个随机生成的值。然后,我们将随机值乘以一个noise_factor,您可以随意使用它。以下代码向图像添加了噪声:

noise_factor = 0.6
x_train_noisy = x_train + noise_factor * tf.random.normal(shape=x_train.shape)
x_test_noisy = x_test + noise_factor * tf.random.normal(shape=x_test.shape)

我们还需要确保数组项的值在 0 到 1 的范围内。为此,我们可以使用tf.clip_by_value方法。clip_by_value是一种 TensorFlow 方法,它截取最小值-最大值范围之外的值,并用指定的最小值或最大值替换它们。以下代码将值截取到范围之外:

x_train_noisy = tf.clip_by_value(x_train_noisy, clip_value_min=0., clip_value_max=1.)
x_test_noisy = tf.clip_by_value(x_test_noisy, clip_value_min=0., clip_value_max=1.)

现在我们有了噪声和干净的图像,让我们用下面的代码来看看随机噪声的效果:

n = 5
plt.figure(figsize=(20, 8))
for i in range(n):
    ax = plt.subplot(2, n, i + 1)
    plt.title("original", size=20)
    plt.imshow(tf.squeeze(x_test[i]))
    plt.gray()

    bx = plt.subplot(2, n, n+ i + 1)
    plt.title("original + noise", size=20)
    plt.imshow(tf.squeeze(x_test_noisy[i]))
    plt.gray()
plt.show()

图 11-6 显示了原始图像及其噪声版本:

Output:

img/501289_1_En_11_Fig6_HTML.jpg

图 11-6

时尚 MNIST 干净与嘈杂的图像示例

正如你所看到的,我们对我们的图像应用了强噪声,没有人能看出底部的图像中有服装项目。但是,通过我们的自编码器,我们将能够对这些极其嘈杂的图像进行降噪处理。

构建模型

正如我们在第十章中所做的,我们再次利用了模型子类化。在模型子类化中,我们可以自由地从头开始实现任何东西。这是一个非常强大的方法,因为我们可以建立任何类型的模型。我们的自定义类将扩展tf.keras.Model对象。它还需要声明几个变量和函数。不过,这没什么可怕的。要构建模型,我们只需完成以下任务:

  • 创建一个扩展keras.Model对象的类。

  • 创建一个__init__函数来声明用顺序 API 构建的两个独立的模型。

    • 在它们内部,我们需要声明可以相互反转的层。编码器模型的 Conv2D 层,而解码器模型的 conv 2d 转置层。
  • 创建一个调用函数,告诉模型如何使用初始化变量和__init__方法处理输入:

    • 我们需要调用初始化的编码器模型,该模型将图像作为输入。

    • 我们还需要调用初始化的解码器模型,它将编码器模型的输出(已编码)作为输入。

  • 返回解码器的输出。

以下代码完成了所有这些工作:

class Denoise(tf.keras.Model):
  def __init__(self):
    super(Denoise, self).__init__()
    self.encoder = tf.keras.Sequential([
      Input(shape=(28, 28, 1)),
      Conv2D(16, (3,3), activation="relu", padding="same", strides=2),
      Conv2D(8, (3,3), activation="relu", padding="same", strides=2)])

    self.decoder = tf.keras.Sequential([
      Conv2DTranspose(8, kernel_size=3, strides=2, activation="relu", padding="same"),
      Conv2DTranspose(16, kernel_size=3, strides=2, activation="relu", padding="same"),
      Conv2D(1, kernel_size=(3,3), activation="sigmoid", padding="same")])

  def call(self, x):
    encoded = self.encoder(x)
    decoded = self.decoder(encoded)
    return decoded

让我们用下面的代码创建一个模型对象:

autoencoder = Denoise()

我们使用 Adam optimizer 作为我们的优化算法,使用均方误差(MSE)作为我们的损失函数。以下代码设置了这些配置:

autoencoder.compile(optimizer='adam', loss="mse")

最后,我们可以通过输入有噪声和干净的图像来运行我们的模型 10 个时期,这将需要大约 1 分钟来训练。我们还使用测试数据集进行验证。以下代码用于训练模型:

autoencoder.fit(x_train_noisy, x_train,

图 11-7 显示了每个历元的训练过程输出:

                epochs=10,
                shuffle=True,
                validation_data=(x_test_noisy, x_test))

img/501289_1_En_11_Fig7_HTML.jpg

图 11-7

在我们的定制模型训练期间的纪元统计

噪声图像去噪

既然我们已经训练了我们的模型,我们可以轻松地完成去噪任务。为了简化预测过程,我们使用测试数据集。但是,您可以随意处理和尝试其他图像,例如 MNIST 数据集中的数字。

现在,我们运行以下行来对有噪声的测试图像进行降噪:

encoded_imgs=autoencoder.encoder(x_test).numpy()
decoded_imgs=autoencoder.decoder(encoded_imgs.numpy()

正如您在这里看到的,我们可以分别使用编码器和解码器网络及其相应的属性。因此,我们首先使用编码器网络对我们的图像进行编码(x_test)。然后,我们在解码器网络中使用这些编码图像(encoded_imgs)来生成我们在开始时使用的图像的干净版本(decoded_imgs)。

我们可以使用以下代码比较测试数据集的前十幅图像的噪声、重建(去噪)和原始版本:

n = 10
plt.figure(figsize=(20, 6))
for i in range(n):

    # display original + noise
    bx = plt.subplot(3, n, i + 1)
    plt.title("original + noise")
    plt.imshow(tf.squeeze(x_test_noisy[i]))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    # display reconstruction
    cx = plt.subplot(3, n, i + n + 1)
    plt.title("reconstructed")
    plt.imshow(tf.squeeze(decoded_imgs[i]))
    plt.gray()
    bx.get_xaxis().set_visible(False)
    bx.get_yaxis().set_visible(False)

    # display original
    ax = plt.subplot(3, n, i + 2*n + 1)
    plt.title("original")
    plt.imshow(tf.squeeze(x_test[i]))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

图 11-8 显示了所选图像的噪声、重建和原始版本:

输出:

img/501289_1_En_11_Fig8_HTML.jpg

图 11-8

时尚 MNIST 测试数据集样本图像与噪声,重建(去噪)和原始版本

正如你之前看到的,我们的模型可以成功地对非常嘈杂的照片进行降噪,这是以前从未见过的(我们使用了测试数据集)。显然有一些未恢复的失真,例如右边第二个图像中缺失的拖鞋底部。然而,如果你考虑噪声图像的变形程度,我们可以说我们的模型在恢复失真图像方面相当成功。

在我的脑海中,你可以例如考虑扩展这个自编码器,并将其嵌入到照片增强应用程序中,这可以增加照片的清晰度和清晰度。

结论

在这一章中,我们介绍了一个神经网络架构,自编码器,它主要用于无监督的学习任务。我们还进行了一个案例研究,其中我们训练了一个能够对失真图像进行去噪的自编码器模型。

在下一章中,我们将深入研究生成对抗网络,它彻底改变了深度学习的生成方面。

十二、生成对抗网络

生成对抗网络(GANs)是 Ian Goodfellow 和他的同事在 2014 年设计的一种深度学习模型。

GANs 的发明出乎意料。著名的研究员,当时是蒙特利尔大学的博士研究员,伊恩·古德费勒,在一个朋友的告别派对上和他的朋友讨论其他生成算法的缺陷时偶然想到了这个想法。聚会结束后,他满怀希望地回到家中,实施了他心中的构想。令人惊讶的是,在第一次试验中,一切都如他所愿,他成功地创建了生成性对抗网络(简称 GANs)。

据脸书大学人工智能研究主任、纽约大学教授 Yann LeCun 称,GANs 是“过去 10 年机器学习中最有趣的想法”

方法

在 GAN 架构中,有两个神经网络(一个生成器和一个鉴别器)在博弈中相互竞争。在暴露于训练集之后,生成器学习生成具有相似特征的新样本。另一方面,鉴别器试图判断生成的数据是真实的还是伪造的。通过训练,生成器被迫生成接近真实的样本,使得鉴别器无法将它们与训练数据区分开。训练结束后,我们可以使用生成器生成非常真实的样本,如图像、声音和文本。

GANs 最初被设计用来处理无监督的学习任务。然而,最近的研究表明,GANs 在监督、半监督和强化学习任务中表现出良好的结果。

体系结构

如前所述,有两个网络形成了生成性对抗网络:生成者网络和鉴别者网络。这两个网络通过一个潜在的空间相互连接,所有的魔法都在这里发生。换句话说,我们使用发生器网络的输出作为鉴别器网络的输入。让我们深入研究一下生成网络和判别网络,以真正理解 gan 是如何工作的;见图 12-1 :

img/501289_1_En_12_Fig1_HTML.jpg

图 12-1

生成性对抗网络的可视化

GAN 组件

生成网络

生成器网络采用固定长度的随机向量(从随机噪声开始)并生成新样本。它使用高斯分布来生成新的样本,通常从一维层开始,最终被整形为训练数据样本的形状。例如,如果我们使用 MNIST 数据集来生成图像,则生成器网络的输出图层必须对应于图像尺寸(例如,28 x 28 x 1)。这最后一层也被称为潜在空间或向量空间。

鉴别器网络

鉴别器网络以相对相反的顺序工作。生成网络的输出被用作鉴别器网络中的输入数据(例如,28×28×1)。鉴别器网络的主要任务是决定产生的样本是否真实。因此,鉴别器网络的输出由单个神经元密集层提供,该神经元密集层输出所生成样本的真实性的概率(例如,0.6475)。

潜在空间

潜在空间(即向量空间)作为发生器网络的输出和鉴别器网络的输入。生成对抗模型中的潜在空间通常具有原始训练数据集样本的形状。潜在空间试图捕捉训练数据集的特征,以便生成器可以成功地生成接近真实的样本。

一个已知问题:模式崩溃

在生成对抗网络的训练过程中,我们经常会遇到“模式崩溃”的问题。模式崩溃基本上是指未能正确归纳,或者换句话说,未能了解成功生成样本的有意义特征。模式崩溃的形式可能是完全学习失败或学习部分特征失败。例如,当我们处理 MNIST 数据集(从 0 到 9 的手写数字)时,由于模式崩溃问题,我们的 GAN 可能永远不会学习生成一些数字。模式崩溃有两种可能的解释:

  • 弱鉴别网络

  • 目标函数选择错误

因此,研究一下我们网络的规模和深度,以及目标函数,可能会解决这个问题。

关于建筑的最后笔记

保持发生器和鉴别器网络之间的良性竞争对于构建有用的 GAN 模型是至关重要的。只要这两个网络互相对抗来完善它们的性能,你就可以根据问题自由设计这些网络的内部结构。例如,在处理序列数据时,只要其中一个网络作为生成器网络,而另一个网络作为鉴别器网络,就可以用 LSTM 和 GRU 图层构建两个网络。另一个例子是我们的案例研究。当用 GANS 生成图像时,我们给我们的网络增加了许多卷积或转置卷积层,因为它们降低了图像数据的计算复杂度。

氮化镓的应用

目前有许多领域正在使用 GANs,可列举如下:

  • 时尚、艺术和广告

  • 制造业和 R&D

  • 视频游戏

  • 恶意应用程序和深度伪造

  • 其他应用

艺术和时尚

生成对抗网络能够“生成”样本。所以,他们天生就有创造力。这就是为什么艺术和时尚是生成性对抗网络最有前途的领域之一。有了训练有素的 GANs,你可以创作绘画、歌曲、服装,甚至诗歌。事实上,Nvidia 的 StyleGAN 网络生成的一幅画“Edmond de Belamy,from La Famille de Belamy”在纽约以 43.25 万美元的价格售出。因此,你可以清楚地看到甘如何有潜力被用于艺术世界。

制造、研究和 R&D

GANs 可用于预测科学研究项目和工业应用中的计算瓶颈。

GAN 网络也可用于提高基于统计分布的图像的清晰度。换句话说,GANs 可以使用统计分布来预测缺失的部分,并产生合适的像素值,这将提高望远镜或显微镜拍摄的图像质量。

视频游戏

GANs 可用于使用小清晰度图像获得更精确和更清晰的图像。这种能力可能被用来使老游戏对新的一代更有吸引力。

恶意应用程序和深度伪造

GANs 可能被用来生成接近真实的虚假社会档案或虚假的名人视频。例如,GAN 算法可用于伪造证据来陷害某人。因此,有许多恶意的 GAN 应用程序,也有许多检测恶意 GAN 生成的样本并将其标记为假的 GAN。

杂项应用

除了前面的用例,gan 还用于以下目的:

  • 用于医疗行业的早期诊断

  • 在建筑和内部设计行业中生成逼真的图像

  • 从图像中重建物体的三维模型

  • 对于诸如老化的图像处理

  • 以产生可用于癌症研究的蛋白质序列

  • 通过声音重建一个人的脸。

生成性对抗性网络应用是巨大的和无限的,它是人工智能界的一个非常热门的话题。既然我们已经讨论了生成性敌对网络的基础,我们可以开始我们的案例研究了。请注意,我们将从 TensorFlow 团队发布的深度卷积 GAN 教程中获取自己的内容。 1

案例研究|使用 MNIST 生成数字

在本案例研究中,我们一步一步地构建了一个生成性对抗网络(GAN),它能够生成手写数字(0 到 9)。为了能够完成这项任务,我们需要建立一个生成器网络和一个鉴别器网络,以便我们的生成模型可以学习欺骗鉴别器模型,后者检查生成器网络生产什么。让我们从最初的进口开始。

初始进口

正如我们在案例研究中经常做的那样,我们进行了一些初始导入,这些导入在我们的 Colab 笔记本的不同单元格中使用。以下行导入 TensorFlow、相关 TensorFlow 图层对象和 Matplotlib:

import tensorflow as tf
from tensorflow.keras.layers import(Dense,
                                 BatchNormalization,
                                 LeakyReLU,
                                 Reshape,
                                 Conv2DTranspose,
                                 Conv2D,
                                 Dropout,
                                 Flatten)
import matplotlib.pyplot as plt

在接下来的部分中,我们还使用其他库,如 ostimeIPython.displayPILglob和 imageio ,但为了保持它们与上下文相关,我们只在需要使用它们时才导入它们。

加载并处理 MNIST 数据集

我们已经讨论过几次 MNIST 数据集的细节。这是一个手写数字数据集,有 60,000 个训练样本和 10,000 个测试样本。如果你想了解更多关于 MNIST 数据集的信息,请参考第七章。

由于这是一个无监督的学习任务,我们只需要特征,因此我们不保存标签数组。让我们用下面几行导入数据集:

# underscore to omit the label arrays
(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()

然后,我们重塑我们的train_images,使其具有第四维度,并使用以下代码对其进行规范化(在-1 到 1 的范围内):

train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5 # Normalize the images to [-1, 1]

然后,我们设置一个BUFFER_SIZE用于混洗,一个BATCH_SIZE用于批量处理数据。然后,我们调用以下函数将 NumPy 数组转换为 TensorFlow Dataset 对象:

# Batch and shuffle the data
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)

现在我们的数据被处理和清理。我们可以进入模型构建部分。

构建 GAN 模型

与其他案例研究相比,本案例研究的模型构建部分稍微高级一些。我们需要定义自定义损失、训练步骤和训练循环函数。理解正在发生的事情可能更具挑战性。但是我会尽可能地添加更多的评论,让你更容易理解。此外,将这个案例研究视为成为高级机器学习专家的一条途径。另外,如果你真的关注了评论,那就比看起来容易多了。

发电机网络

作为我们 GAN 网络的一部分,我们首先构建一个带有顺序 API 的生成器。生成器将接受具有 100 个数据点的一维输入,并将其慢慢转换成 28×28 像素的图像数据。由于我们使用该模型从一维输入生成图像,因此使用转置卷积层是最佳选择。转置卷积层的工作方式与卷积层正好相反。它们增加了图像数据的清晰度。在使用转置卷积层之后,我们还利用了批量归一化和泄漏 ReLU 层。下面的代码为我们定义了这个网络:

def make_generator_model():
  model = tf.keras.Sequential()
  model.add(Dense(7*7*256, use_bias=False, input_shape=(100,)))
  model.add(BatchNormalization())
  model.add(LeakyReLU())

  model.add(Reshape((7, 7, 256)))
  assert model.output_shape == (None, 7, 7, 256) # Note: None is the batch size

  model.add(Conv2DTranspose(128, (5, 5), strides=(1, 1), padding="same", use_bias=False))
  assert model.output_shape == (None, 7, 7, 128)
  model.add(BatchNormalization())
  model.add(LeakyReLU())

  model.add(Conv2DTranspose(64, (5, 5), strides=(2, 2), padding="same", use_bias=False))
  assert model.output_shape == (None, 14, 14, 64)
  model.add(BatchNormalization())
  model.add(LeakyReLU())

  model.add(Conv2DTranspose(1, (5, 5), strides=(2, 2), padding="same", use_bias=False, activation="tanh"))
  assert model.output_shape == (None, 28, 28, 1)

  return model

我们可以用下面的代码来声明我们的网络:

generator = make_generator_model()

让我们来看看图 12-2 中的发电机网络总结:

generator.summary()
Output:

img/501289_1_En_12_Fig2_HTML.jpg

图 12-2

我们的发电机网络概述

并使用我们未经训练的生成器网络生成和绘制一个样本,代码如下:

# Create a random noise and generate a sample
noise = tf.random.normal([1, 100])
generated_image = generator(noise, training=False)
# Visualize the generated sample
plt.imshow(generated_image[0, :, :, 0], cmap="gray")

Output is shown in Figure``12-3

img/501289_1_En_12_Fig3_HTML.jpg

图 12-3

未经训练的随机生成样本的示例

鉴别器网络

在生成器网络之后,我们应该构建一个鉴别器网络来检查生成器生成的样本。我们的鉴别器网络必须决定生成图像的伪造概率。因此,它获取生成的图像数据(28 x 28)并输出一个值。对于这个任务,我们使用由泄漏 ReLU 和漏失层支持的卷积层。展平图层将二维数据转换为一维数据,密集图层用于将输出转换为单个值。下面几行定义了鉴别器网络的功能:

def make_discriminator_model():
  model = tf.keras.Sequential()

  model.add(Conv2D(64, (5, 5), strides=(2, 2), padding="same", input_shape=[28, 28, 1]))
  model.add(LeakyReLU())
  model.add(Dropout(0.3))

  model.add(Conv2D(128, (5, 5), strides=(2, 2), padding="same"))
  model.add(LeakyReLU())
  model.add(Dropout(0.3))

  model.add(Flatten())
  model.add(Dense(1))

  return model

我们可以通过调用以下函数来创建鉴别器网络:

discriminator = make_discriminator_model()

我们可以看到我们的鉴频器网络总结,代码如下(输出见图 12-4 ):

discriminator.summary()
Output:

img/501289_1_En_12_Fig4_HTML.jpg

图 12-4

我们的鉴别器网络综述

如果我们使用鉴别器网络,我们实际上可以决定我们随机生成的图像是否足够真实:

decision = discriminator(generated_image)
print (decision)
Output:
tf.Tensor([[-0.00108097]], shape=(1, 1), dtype=float32)

正如你所看到的,我们的输出小于零,我们可以得出结论,这个未经训练的生成器网络生成的特定样本是假的。

配置 GAN 网络

作为模型配置的一部分,我们需要为生成器和鉴别器设置损失函数。此外,我们还需要为它们设置单独的优化器。

损失函数

我们从从tf.keras.losses模块创建一个二进制交叉熵对象开始。我们还将参数from_logits设置为 true。创建对象后,我们用定制的鉴别器和生成器损耗函数填充它们。

我们的鉴别器损耗计算为(I)鉴别器对真实图像的预测为一组 1,以及(ii)鉴别器对生成图像的预测为一组 0 的组合。

我们的发电机损耗是通过测量它欺骗鉴别器的能力来计算的。因此,我们需要将鉴别者对生成的图像的决定与一系列的决定进行比较。

以下代码行完成了所有这些工作:

# This method returns a helper function to compute cross entropy loss
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)

def discriminator_loss(real_output, fake_output):
  real_loss = cross_entropy(tf.ones_like(real_output), real_output)
  fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
  total_loss = real_loss + fake_loss
  return total_loss

def generator_loss(fake_output):
  return cross_entropy(tf.ones_like(fake_output), fake_output)

【计算机】优化程序

我们还为生成器和鉴别器网络分别设置了两个优化器。我们可以使用来自tf.keras.optimizers模块的 Adam 对象。下面几行设置了优化器:

generator_optimizer=tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer=tf.keras.optimizers.Adam(1e-4)

设置检查点

由于网络的复杂性,训练 GAN 网络比训练其它网络需要更长的时间。我们必须运行至少 50 60 个时期的训练,以生成有意义的图像。因此,设置检查点对于以后使用我们的模型非常有用。

通过使用操作系统库,我们设置了一个保存所有培训步骤的路径,代码如下:

import os

checkpoint_dir = './training_checkpoints'

checkpoint_prefix=os.path.join(checkpoint_dir, "ckpt")

checkpoint = tf.train.Checkpoint(
  generator_optimizer=generator_optimizer,
  discriminator_optimizer=discriminator_optimizer,
  generator=generator,
  discriminator=discriminator)

训练 GAN 模型

让我们用下面几行创建一些变量:

EPOCHS = 60
# We will reuse this seed overtime (so it's easier)
# to visualize progress in the animated GIF)
noise_dim = 100
num_examples_to_generate = 16
seed = tf.random.normal([num_examples_to_generate, noise_dim])

我们的种子是我们用来生成图像的噪音。下面的代码生成一个形状为(16,100)的正态分布随机数组。

训练步骤

这是我们模型中最不寻常的部分:我们正在设置一个定制的训练步骤。在通过注释tf.function模块定义自定义train_step()函数后,我们的模型将基于我们定义的自定义train_step()函数进行训练。

以下带有过多注释的代码是针对培训步骤的。请仔细阅读评论。

# tf.function annotation causes the function
# to be "compiled" as part of the training
@tf.function
def train_step(images):
  # 1 - Create a random noise to feed it into the model
  # for the image generation
  noise = tf.random.normal([BATCH_SIZE, noise_dim])
  # 2 - Generate images and calculate loss values
  # GradientTape method records operations for automatic differentiation.
  with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
    generated_images = generator(noise, training=True)
    real_output = discriminator(images, training=True)
    fake_output = discriminator(generated_images, training=True)
    gen_loss = generator_loss(fake_output)
    disc_loss = discriminator_loss(real_output, fake_output)

  # 3 - Calculate gradients using loss values and model variables
  # "gradient" method computes the gradient using
  # operations recorded in context of this tape (gen_tape and disc_tape).
  # It accepts a target (e.g., gen_loss) variable and
  # a source variable (e.g.,generator.trainable_variables)
  # target --> a list or nested structure of Tensors or Variables to be differentiated.
  # source --> a list or nested structure of Tensors or Variables.
  # target will be differentiated against elements in sources.
  # "gradient" method returns a list or nested structure of Tensors
  # (or IndexedSlices, or None), one for each element in sources.
  # Returned structure is the same as the structure of sources.
  gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
  gradients_of_discriminator = disc_tape.gradient( disc_loss, discriminator.trainable_variables)
  # 4 - Process  Gradients and Run the Optimizer
  # "apply_gradients" method processes aggregated gradients.
  # ex: optimizer.apply_gradients(zip(grads, vars))

  """
  Example use of apply_gradients:
  grads = tape.gradient(loss, vars)
  grads = tf.distribute.get_replica_context().all_reduce('sum', grads)
  # Processing aggregated gradients.
  optimizer.apply_gradients(zip(grads, vars), experimental_aggregate_gradients=False)
  """
  generator_optimizer.apply_gradients(zip( gradients_of_generator, generator.trainable_variables))
  discriminator_optimizer.apply_gradients(zip( gradients_of_discriminator, discriminator.trainable_variables))

现在我们已经用tf.function注释定义了我们的定制训练步骤,我们可以为训练循环定义我们的训练函数了。

训练循环

我们为训练循环定义了一个名为train的函数。我们不仅运行 for 循环在 MNIST 上迭代我们的自定义训练步骤,还使用单个函数执行以下操作:

  • 在训练期间

    • 开始记录每个时期开始时花费的时间

    • 制作 GIF 图像并显示它们

    • 每隔 5 个时期将模型保存为一个检查点

    • 打印出完整的纪元时间

  • 训练完成后,最终生成最终图像

以下带有详细注释的行完成所有这些任务:

import time
from IPython import display # A command shell for interactive computing in Python.

def train(dataset, epochs):
  # A. For each epoch, do the following:
  for epoch in range(epochs):
  start = time.time()
  # 1 - For each batch of the epoch,
  for image_batch in dataset:
    # 1.a - run the custom "train_step" function
    # we just declared above
    train_step(image_batch)

  # 2 - Produce images for the GIF as we go
  display.clear_output(wait=True)
  generate_and_save_images(generator,
                           epoch + 1,
                           seed)

  # 3 - Save the model every 5 epochs as
  # a checkpoint, which we will use later
  if (epoch + 1) % 5 == 0:
    checkpoint.save(file_prefix = checkpoint_prefix)

  # 4 - Print out the completed epoch no. and the time spent
  print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))

  # B. Generate a final image after the training is completed
  display.clear_output(wait=True)
  generate_and_save_images(generator,
                           epochs,
                           seed)

图像生成功能

在训练函数中,有一个我们还没有定义的自定义图像生成函数。我们的图像生成功能执行以下任务:

  • 使用模型生成图像。

  • 使用 Matplotlib 在 4 x 4 网格布局中显示生成的图像。

  • 最后保存最后的数字。

以下部门负责这些任务:

def generate_and_save_images(model, epoch, test_input):
  # Notice `training` is set to False.
  # This is so all layers run in inference mode (batchnorm).
  # 1 - Generate images
  predictions = model(test_input, training=False)
  # 2 - Plot the generated images
  fig = plt.figure(figsize=(4,4))
  for i in range(predictions.shape[0]):
    plt.subplot(4, 4, i+1)
    plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap="gray")
      plt.axis('off')

  # 3 - Save the generated images

  plt.savefig('image_at_epoch_{:04d}.png'.format( epoch))
  plt.show()

既然我们已经定义了自定义图像生成函数,我们可以在下一部分中安全地调用 train 函数。

开始训练

开始训练循环非常容易。下面的单行代码将从 train 函数开始训练,该函数循环遍历train_step()函数并使用generate_and_save_images()函数生成图像。在此过程中,我们还会收到统计数据和信息,以及在 4 x 4 网格布局上生成的图像。

train(train_dataset, EPOCHS)
Output:

img/501289_1_En_12_Fig5_HTML.jpg

图 12-5

在 4 x 4 网格布局中 60 个时期后生成的图像

如图 12-5 所示,经过 60 个历元,生成的图像非常接近正确的手写数字。我唯一看不到的数字是数字二(2),这可能只是一个巧合。

既然我们已经训练了我们的模型并保存了我们的检查点,我们可以用下面的代码行恢复训练好的模型:

checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))

在训练过程中动画生成的数字

在训练过程中,我们的generate_and_save_images()函数成功保存了每个时期生成的 4 x 4 图像网格布局。让我们通过一个简单的练习来看看我们的模型的生成能力是如何随着时间的推移而发展的。

为了能够打开图像,我们可以使用 PIL (Python Image Library),它支持许多不同的图像格式,包括 PNG。我们可以定义一个自定义函数,用下面几行打开图像:

# PIL is a library which may open different image file formats
import PIL
# Display a single image using the epoch number
def display_image(epoch_no):
  return PIL.Image.open( 'image_at_epoch_{:04d}.png'.format( epoch_no ))

现在用下面一行测试这个函数,它将显示我们的模型生成的最新 PNG 文件:

display_image(EPOCHS)

输出 is shown in Figure 12-6 :

img/501289_1_En_12_Fig6_HTML.jpg

图 12-6

GAN 模型生成的最新 PNG 文件的显示。注意,它们与图 12-5 所示的样品相同。因为我们从上一个检查点恢复了模型

使用display_images()功能,我们可以显示任何我们想要的图像。除此之外,生成一个动画 GIF 图像来显示我们的模型是如何随着时间的推移而演变的,这不是很酷吗?我们可以使用 glob 和 imageio 库来实现这一点,它们将所有的 PNG 文件堆积起来,创建一个动画 GIF 文件。以下代码行执行此任务:

import glob # The glob module is used for Unix style pathname pattern expansion.
import imageio # The library that provides an easy interface to read and write a wide range of image data

anim_file = 'dcgan.gif'

with imageio.get_writer(anim_file, mode="I") as writer:
  filenames = glob.glob('image*.png')
  filenames = sorted(filenames)
  for filename in filenames:
    image = imageio.imread(filename)
    writer.append_data(image)
  image = imageio.imread(filename)
  writer.append_data(image)

点击 Google Colab 笔记本左侧的文件图标,查看所有文件,包括“ dcgan.gif ”。您可以简单地下载它来查看我们的模型在每个时期生成的图像的动画版本。为了能够在您的 Google Colab 笔记本中查看 GIF 图像,您可以使用以下代码行:

display.Image(open('dcgan.gif','rb').read())

图 12-7 显示了我们创建的 GIF 图像中的几帧:

img/501289_1_En_12_Fig7_HTML.jpg

图 12-7

从不同时代产生的数字例子。了解 GAN 模型如何随着时间的推移学习生成数字

结论

在这一章中,我们讨论了最后一个神经网络架构,生成对抗网络,它主要用于艺术、制造、研究和游戏等领域的生成任务。我们还进行了一个案例研究,其中我们训练了一个能够生成手写数字的 GAN 模型。

posted @   绝不原创的飞龙  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
历史上的今天:
2020-10-02 《线性代数》(同济版)——教科书中的耻辱柱
点击右上角即可分享
微信分享提示