第 1 节:Web 人工智能
本节介绍人工智能(AI)的定义,并说明 AI 在很大程度上如何影响网络。 它还简要讨论了机器学习的基础。
本节包括以下章节:
- “第 1 章”,“揭秘人工智能和机器学习基础”
一、揭秘人工智能和机器学习基础
“就像电力在 100 年前改变了几乎所有事物一样,今天我实际上很难想到一个行业,我认为 AI 在未来几年内不会改变。”
——Andrew Ng
这句话可能看起来非常熟悉,不用说,它确实是对当前技术中断的强烈共鸣。 在最近的一段时间里,人工智能(AI)成为几乎每个行业都感兴趣的领域。 无论是教育公司,电信公司,还是从事医疗保健的组织,他们都已采用 AI 来增强业务。 AI 与其他多个行业的这种不可思议的整合只能保证随着时间的推移变得更好,并以智能方式解决关键的实际问题。 今天,我们的电话可以根据我们的指示为您进行临床约会,我们的电话摄像头可以告诉我们所捕获图像的一些人类感知属性,我们的汽车警报系统可以检测到我们的驾驶手势,并可以避免发生意外事故。 随着研究,技术和计算能力普及的发展,这些示例将变得越来越好,并且将变得越来越智能。
随着我们步入软件 2.0 时代,了解为什么自 1950 年代以来就存在的一项技术成为当今大多数新闻的头等重要。 是! 人工智能诞生于 1950 年代,当时诸如 Alan Turing 之类的少数计算机科学家和数学家开始思考机器是否可以思考以及是否可以通过智能获得支持,以便他们可以自己回答问题,而无需明确编程。
自成立以来不久,人工智能一词由 John McCarthy 在 1956 年的一次学术会议上首次提出。 从问题“机器可以思考吗?”(由图灵在他的论文中提出,题目为《计算机械和智能》)。从 1950 年到今天,在二十一世纪中,人工智能世界已经展现了一些我们从未想过的前所未有的成果。
今天,如果不使用网络,几乎不可能想到一天。 它已轻松成为我们的基本必需品之一。 我们最喜欢的搜索引擎可以直接回答我们的问题,而不是向我们提供相关链接的列表。 他们可以分析在线文本并检测其意图并总结其内容。 由于 AI,所有这些都是可能的。
本书旨在为读者提供动手操作指南,指导他们如何利用深度学习等 AI 技术,使基于计算机视觉,自然语言处理的智能 Web 应用变得安全等。 本章为读者提供了有关 AI 及其不同类型和 ML 基本概念的快速复习,并介绍了一些业内知名人士及其通过将 AI 和 Web 技术融合在一起所做的工作。 我们将涵盖以下方面:
- 人工智能及其不同类型的介绍
- 机器学习(ML):最受欢迎的 AI
- 深度学习(DL)简介
- AI,ML 和 DL 之间的关系
- 机器学习基础
- AI 前后的 Web
- 最大的 Web AI 参与者及其所作所为
人工智能及其类型简介
从更简单的意义上讲,人工智能就是赋予机器智能执行能力的全部。 例如,我们许多人都可以下棋。 本质上,我们首先通过学习进行游戏的基础知识,然后再与其他人一起实际进行游戏。 但是机器可以做到吗? 机器可以自己学习并与我们下棋吗?
AI 试图通过赋予我们一些规则来综合我们所谓的智能并将其灌输到机器中的能力,来实现这一目标。 这里提到的机器可以是任何可以计算的东西。 例如,它可以是软件或机器人。
实际上,有几种类型的 AI。 最受欢迎的是以下几种:
- 模糊系统
- 专家系统
- 机器学习系统
最终类型听起来最熟悉。 我们将在下一节中介绍它。 但是,在继续进行之前,是时候看看一些使我们今天目睹的 AI 进步成为现实的要点。
驱动 AI 的因素
驱动 AI 力量的主要因素如下:
- 数据
- 算法上的进步
- 计算机硬件进步
- 高性能计算的普及
数据
我们今天拥有的数据量是巨大的-正如 Google 首席经济学家 Hal Varian 在 2016 年所说:
“在文明曙光到 2003 年之间,我们只创建了 5 EB,现在每两天创建一个 EB。到 2020 年,这个数字预计将达到 53 ZB(53 万亿 GB),增长 50 倍。 ”
大量的数据。 随着数字设备数量的增长,此数据量将仅继续呈指数增长。 行驶的汽车仅在速度计上显示速度的时代已经一去不复返了。 我们处于这样一个时代,可以使汽车的每一部分都能在每一瞬间产生原木,从而使我们能够完全重建汽车生命中的任何时刻。
一个人从生活中学到的越多,他就变得越聪明,就越能预测未来事件的结果。 与机器类似,一个软件要训练的(质量)数据量越大,则预测未来看不见的数据就越好。
在过去的几年中,由于各种因素,数据的可用性得到了飞速增长:
- 便宜的存储
- 更高的数据传输速率
- 基于云的存储解决方案的可用性
- 先进的传感器
- 物联网
- 各种形式的数字电子设备的增加
- 网站和本机应用的使用增加
现在有比以往更多的数字设备。 它们都配备了可以随时生成日志并将其通过互联网传输到制造日志的公司或购买该数据的任何其他供应商的系统。 同样,很多日志是由人们使用的网站或应用创建的。 所有这些都可以轻松地存储在基于云的存储解决方案或高存储容量的物理存储中,这些存储现在比以前便宜。
如果环顾四周,您可能会看到一台笔记本电脑,在该笔记本电脑上您经常使用一些软件和网站-所有这些都可能在收集有关您对它们执行的每个操作的数据。 同样,您的电话将充当此类数据生成设备。 对于电视服务提供商提供的带有多个频道的电视,服务提供商和频道提供商都在收集有关您的数据,以更好地为您服务并改善他们的产品。 您只能想象一个人每天产生的海量数据,这个星球上有数十亿人!
算法的进步
算法是明确的步骤序列,可以解决给定的问题。 随着时间的流逝,随着科学的发展和人类借助数学对自然法则的理解,算法得到了改善。 大自然往往会启发人们解决复杂问题的方法。 神经网络可能是当今最受关注,自然启发的算法。
当计算机逻辑以多个if-else
阶梯开始时,没有人会想到有一天我们会拥有一种计算机程序,该程序可以学会产生类似于if-else
阶梯的结果,而无需手动编写条件。 此外,我们今天拥有的计算机程序可以生成其他可以模拟 AI 的程序!
当然,随着时间的流逝,由人类以及现在由机器开发的算法在执行任务时变得越来越聪明,功能越来越强大。 这直接影响了神经网络的兴起,而神经网络的基本形式似乎是解决矩阵和向量算术问题的循环的耗时超级嵌套。
硬件进步
英特尔在 1970 年推出其首个动态 RAM 模块时,它能够保存 1 KB 数据。 大约 50 年后,我们在市场上提供了 128 GB RAM 模块。 这几乎是内存空间的1.28 x 10^8
倍。
硬盘也表现出类似的趋势。 随着首款个人计算机硬盘能够存储宝贵的 5 兆字节,希捷在 2016 年宣布将 60 TB 的存储空间存储在固态驱动器上。 这是1.2 x 10^7
的增加倍数。
但是,我们只讨论了直接的个人计算比较,而没有考虑自第一台计算机问世以来技术发展的影响。 如今,随着云计算的到来,听到有人谈论无限云存储变得很普遍。
人工智能极大地受益于计算速度和数据存储的指数级增长。
高性能计算的普及
随着商品硬件成本的降低及其表现能力的提高,如今,高性能计算已不再是科技巨头独有的东西。 如今,如果每个人都不满意可以通过单个设备提供的出色表现,那么很容易就可以为自己的个人使用建立一个计算设备网络,以促进高性能计算。 但是,投资硬件并不是获得高性能计算的唯一方法。 基于云的计算解决方案的出现导致单击部署方法可以使用非常高速的计算基础结构。 用户可以随时通过网络启动基于云的实例,并在其上运行表现密集型软件,而费用却很少。
随着高性能计算变得易于个人开发者使用,人工智能解决方案的开发已进入广大开发者社区。 这导致 AI 的创新和基于研究的应用数量激增。
现在,让我们来探讨截至撰写本文时最流行的 AI 形式,并讨论有关它的一些重要概念。
ML - 最受欢迎的 AI 形式
在不采用任何数学符号或太多理论细节的情况下,让我们尝试从直观的角度使用术语机器学习(ML)。 为此,我们将不得不看看我们如何实际学习。 当在学校被教导要识别句子中的词性时,您是否还记得学校? 我们得到了一套识别句子中演讲部分的规则。 我们给了很多例子,我们的老师首先为我们识别了句子中的语音部分来有效地训练我们,以便我们可以利用这种学习经验来识别句子中的语音部分, 没有被教给我们。 而且,该学习过程从根本上适用于我们学习的任何内容。
如果我们可以类似地训练机器怎么办? 如果我们可以对他们进行编程,使他们可以从经验中学习并可以根据这些知识开始回答问题,该怎么办? 嗯,这已经完成了,并且,无论是有意还是无意,我们所有人都在从中受益。 而这正是直觉上讨论 ML 的意义。 为了更正式,更标准的理解,让我们看一下汤姆·米切尔(Tom Mitchell)在他的书《机器学习》中的以下定义:
“如果某个计算机程序在T
上的表现(由P
衡量)随着经验E
的提高而改善,那么据说它可以从经验E
中学习一些任务T
和一些表现度量P
。”
前面的定义是我们从直观的角度讨论 ML 的更为精确的版本。 在此必须注意,由于这种形式的 AI,我们今天看到的大多数 AI 向导都是可能的。
现在,我们对 ML 是个好主意了。 现在,我们将进入下一部分,该部分讨论 ML 的最强大子字段。 我们不会深入探讨数学细节。 相反,如本节所述,我们将对其进行直观地分解。
什么是 DL?
现在是本世纪最令人兴奋的部分,并且可能是本世纪最热门的技术术语。 分开现实,我们现在在某种程度上理解了学习,因此让我们进入术语深度学习 – 深度的第一部分。
DL 是一种机器学习,但它完全基于神经网络。 我们还将在下一章中介绍神经网络。 任何机器学习系统的基本目标都是学习提供给它的数据的有用表示。 但是,什么使 DL 与众不同? 事实证明,DL 系统将数据视为层的表示。 例如,可以将图像视为具有不同属性(例如边缘,轮廓,方向,纹理和梯度)的层的表示。 下图来自的《Python 深度学习》书,FrançoisChollet 很好地抓住了这个想法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JvSdDHxk-1681705004253)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/8b22fd60-5661-44c7-b485-a00f74f9fb83.jpg)]
在上图中,采用 DL 系统对手写数字图像进行分类。 系统将手写数字的图像作为输入,并尝试学习其基础表示。 在第一层中,系统学习通用特征,例如笔触和线条。 随着层数的增加,它将了解特定于给定图像的特征。 层数越多,系统越深。 让我们看一下下面的定义,它是由 FrançoisChollet 在他的书《Python 深度学习》中给出的:
“深度学习中的深度并不是指通过这种方法实现的任何形式的更深层次的理解;它代表的是连续表示层的思想。有多少层对数据的模型有所贡献,称为模型的深度。在深度学习中,这些分层表示(几乎总是)是通过称为神经网络的模型来学习的,这些模型构造为彼此堆叠的文字层。
该定义非常恰当地捕获了 DL 的所有必要成分,并精美地引入了将数据视为分层表示的概念。 因此,从广义上讲,DL 系统以分层的方式将数据分解为简单的表示形式,并且为了学习这些表示形式,它经常利用许多层(称为深) 。 现在我们来看看大图,它告诉我们 AI,ML 和 DL 是如何相互关联的。
AI,ML 和 DL 之间的关系
为了确保我们清楚了解 AI,ML 和 DL 之间的区别,让我们参考下图,该图优雅地捕获了这三个大名之间的关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ruTZGDql-1681705004255)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/f7e17554-5438-49ea-964b-365d2bada273.jpg)]
该图是不言自明的,并且在 DL 领域的许多书籍中都已提及。 让我们尝试从该图中得出一个有趣的结论。
所有 DL 系统都是 ML 系统,因此所有 DL 系统也都是 AI 系统。 但是事实并非如此-并非所有的 AI 系统都是 DL 系统。
乍一看,该声明可能看起来有些混乱,但是,如果我们掌握了正确的基础知识,那么就可以很好地捕捉到 AI,ML 和 DL 之间的区别。 我们将着手重新审视本书后半部分所必需的一些 ML 术语和概念。
回顾机器学习的基础
我们已经了解了 ML 的含义。 在本节中,我们将重点介绍几种术语,例如监督学习和非监督学习,并且我们将研究标准 ML 工作流程中涉及的步骤。 但是您可能会问:为什么选择 ML? 我们应该在本书中学习 DL 的应用。 我们刚刚了解到 DL 仅是 ML 的一种。 因此,快速概述与 ML 相关的基本概念肯定会有所帮助。 让我们从几种类型的 ML 以及它们之间的区别开始。
ML 的类型
ML 包含许多算法和主题。 虽然构成 ML 模型的所有此类算法都不过是对给定数据的数学计算,但是所提供的数据形式和要在其上执行的任务的方式可能会有很大的不同。 有时,您可能希望您的 ML 模型根据先前房价的数据(相对于房屋的详细信息,例如房间数和拥有的楼层数)来预测未来的房价,而在其他时候,您可能希望 ML 模型,学习如何与您对战计算机游戏。 您可以很容易地期望第一个任务的输入数据采用表格格式,但是对于第二个示例,您可能无法提供相同的格式。 因此,ML 算法根据接收到的输入数据和应该产生的输出类型,分为三大类,以及从中得出的另一种形式:
- 监督学习
- 无监督学习
- 强化学习
- 半监督学习
下图捕获了 ML 的三种主要类型,以及第四种类型的混合形式,以及每种类型的非常简短的摘要:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EczMGgKG-1681705004256)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/bc83753f-d373-4b82-a266-72673135f8d1.png)]
您可能已经听说过 ML 的第四种形式,即半监督学习,它融合了监督学习和非监督学习的世界。
现在,根据它们的功能以及可以用来解决的问题类型,更深入地了解这些类型的 ML。
监督学习
在这种 ML 形式中,该算法具有大量训练样本,其中包含有关将用于确定输出特征的所有参数或特征的信息。 此输出特征可以是连续的值范围或离散的标签集合。 基于此,监督式机器学习算法分为两部分:
- 分类:在输出特征中产生离散标签的算法,例如正常和异常或一组新闻类别
- 回归:例如,当输出特征具有真实值时,政党在选举中可能获得的选票数,或预测达到其熔点的材料的温度
大多数 ML 爱好者在开始学习机器学习时,由于其直观的简单性,往往倾向于首先熟悉监督学习。 它具有一些最简单的算法,无需深入的数学知识即可轻松理解,甚至可以从学生在学校最后几年学到的数学知识中得出。 一些最著名的监督学习算法是线性回归,逻辑回归,支持向量机和 K 最近邻。
无监督学习
在训练样本不带有输出特征的情况下,无监督学习就会出现。 那么,您可能想知道,在这种情况下我们应该学习或预测什么? 答案是相似。 用更详尽的术语讲,当我们有一个用于无监督学习的数据集时,我们通常试图学习训练样本之间的相似性,然后为它们分配类别或标签。
考虑一群人站在一个广阔的领域。 它们都具有年龄,性别,婚姻状况,薪资范围和受教育程度等特征。 现在,我们希望根据它们的相似性将它们分组。 我们决定组成三个小组,并看到他们以性别的方式安排自己:一组女性,一组男性以及一组认同其他性别的人。 我们再次要求他们在这些组中分组,看看人们根据年龄范围(儿童,青少年,成人和老年人)来分组。 这使我们总共有 12 个这样的子组。 我们可以根据任何两个个体表现出的相似性来进一步划分较小的子组。 同样,在前面的示例中讨论的分组方式只是形成组的几种方式中的一种。 现在,说我们有 10 个新成员加入人群。 由于我们已经定义了组,因此我们可以轻松地将这些新成员分类到这些组中。 因此,我们可以成功地将组标签应用到它们。
前面的示例仅演示了一种无监督学习形式,可以分为两种类型:
- 聚类:这是根据训练样本的特征相似性来形成训练样本组。
- 关联:这是查找特征或训练样本之间展示的抽象关联或规则。 例如,在分析商店的销售记录时,发现顾客大多在晚上 7 点以后购买啤酒。
K-均值聚类,DBSCAN 和 Apriori 算法是用于无监督学习的一些最著名算法。
强化学习
强化学习(RL)是 ML 的一种形式,其中虚拟智能体试图学习如何与周围的环境互动,从而可以从中获得最大的回报。 一组特定的动作。
让我们尝试通过一个小示例来理解这一点-例如,您构建了一个玩飞镖的机器人。 现在,仅当机器人击中飞镖板的中心时,它才会获得最大奖励。 它从掷飞镖开始,然后降落在最外圈。 它得到一定数量的点,例如x1
。 现在它知道在该区域附近投掷将产生预期值x1
。 因此,在下一次掷球时,它会稍微改变角度,并幸运地降落在第二最右端,并获得x2
点。 由于x2
大于x1
,因此机器人取得了更好的效果,并且将来会学会将其扔到该区域附近。 如果飞镖降落的距离比最外圈的还要远,则机器人会继续将飞镖扔到它所进行的第一次投掷附近,直到获得更好的结果。
在几次这样的试验中,机器人一直在学习更好的投掷位置,并从这些位置绕行一些弯路,直到获得下一个更好的投掷位置为止。 最终,它找到了靶心,并且每次都达到最高点。
在前面的示例中,您的机器人是试图在环境中的飞镖板上投掷飞镖的智能体。 投掷飞镖是智能体对环境执行的动作。 智能体获得的积分将作为奖励。 智能体在多次尝试中尝试通过执行操作来最大化其所获得的回报。
一些著名的 RL 算法是 Monte Carlo,Q 学习和 SARSA。
半监督学习
虽然我们讨论了 ML 的三种主要类型,但还有另一种类型是半监督学习。 用这个术语的名字,您可能会猜测它必须对标记和未标记的训练样本进行混合处理。 在大多数情况下,未标记训练样本的数量超过了标记样本的数量。
当一些标记样本添加到完全属于无监督学习的问题中时,半监督学习已成功用于产生更有效的结果。 而且,由于仅标记了几个样本,因此避免了监督学习的复杂性。 通过这种方法,我们可以获得比纯无监督学习系统更好的结果,并且比纯监督学习系统产生的计算成本更低。
必要的术语
我们已经使自己熟悉不同类型的 ML 系统。 现在,我们将学习一些与 ML 相关的极其重要的术语,这些术语将在本书的后续章节中为我们提供帮助。
训练,测试和验证集
任何 ML 系统都将获得数据。 没有数据,实际上不可能设计 ML 系统。 到目前为止,我们并不担心数据的数量,但请务必记住,我们需要数据来设计 ML 系统。 有了这些数据后,我们将其用于训练我们的机器学习系统,以便它们可以用于在新数据上预测某些东西(某些东西是更广的项目,因问题而异)。 因此,用于训练目的的数据称为训练集,对其进行测试的数据称为测试集。 同样,在将模型实际应用于测试数据之前,我们倾向于在另一组数据上验证其表现,这称为验证集。 有时,我们不会在这些漂亮的分区中获得数据; 我们只是以原始的无法理解的格式获取数据,我们将对其进行进一步处理并进行相应的分区。*
从技术上讲,这三个不同集合中的所有实例都应该彼此不同,而数据中的分布应该是相同的。 如今,许多研究人员发现了关于这些假设的关键问题,并提出了对抗训练之类的东西,这超出了本书的范围。
偏差和方差
偏差和方差对于任何 ML 模型都是非常固有的。 很好地了解它们确实有助于进一步评估模型。 从业人员实际上使用了两者之间的权衡来评估机器学习系统的表现。
鼓励您观看吴安德(Andrew Ng)的讲座,以了解有关此权衡的更多信息。
偏差是 ML 算法为学习给定数据基础的表示而做出的一组假设。 当偏差高时,这意味着相应的算法将对数据进行更多的假设,而在偏差低的情况下,算法将进行尽可能少的假设。 据说 ML 模型在训练上表现良好时具有较低的偏差。 低偏差 ML 算法的一些示例是 K 近邻算法和支持向量机,而逻辑回归和朴素贝叶斯等算法通常是高偏差算法。
ML 上下文中的差异涉及数据中存在的信息。 因此,高方差是指 ML 模型能够很好地捕获提供给它的数据中存在的全部信息的质量。 低方差正好相反。 诸如支持向量机之类的算法通常方差高,而诸如朴素贝叶斯之类的算法方差低。
过拟合和欠拟合
当 ML 模型在训练数据上表现很好但在测试集或验证集的数据上表现不佳时,该现象称为过拟合。 可能有几个原因。 以下是最常见的:
- 该模型在数据方面非常复杂。 在这种情况下,具有很高级别的决策树和具有许多层的神经网络是很好的模型复杂性示例。
- 数据具有很多特征,但总体实例很少。
在 ML 文献中,过拟合问题也被视为高方差的问题。 正则化是防止过拟合的最广泛使用的方法。
我们已经讨论了偏差的概念。 如果模型在训练数据上表现良好,则偏差较小,也就是说,模型对数据的假设不太多,无法推断其表示形式。 如果该模型在训练数据上惨败,则认为该模型具有较高的偏差,并且该模型欠拟合。 欠拟合也可能有很多原因。 在这种情况下,以下是最常见的情况:
- 该模型太简单了,无法学习提供给它的数据的基本表示形式。
- 在将数据馈送到 ML 模型之前,尚未对其数据进行精心设计。 工程部分非常流行,称为特征工程。
基于此讨论,我们可以得出一个非常有用的结论:过拟合的 ML 模型可能会遭受高方差问题,而欠拟合的模型可能会遭受高偏差问题。
如果没有以下图表,关于过拟合和不足拟合的讨论仍然是不完整的(Andrew Ng 在其旗舰课程《机器学习》中显示):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-anH909NM-1681705004256)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/786819b2-885e-4fa8-80bf-de2842258087.png)]
上图很好地说明了通过数据点的曲线拟合方面的欠拟合和过拟合。 它还给我们提供了一个模型的想法,它泛化得很好,即在训练集和测试集上均表现良好。 蓝色的模型预测线偏离样本,导致欠拟合,而在过拟合的情况下,模型会捕获训练数据中的所有点,但不会产生对训练数据以外的数据表现良好的模型。
通常,学习数据表示形式的想法被视为逼近最能描述数据的函数的问题。 而且,可以像上一个一样轻松地以图形方式绘制函数,因此有了曲线拟合的想法。 模型能够很好地概括模型在欠拟合和过拟合之间的最佳点,称为良好拟合。
训练误差和泛化误差
模型在训练阶段进行预测时所犯的误差统称为训练误差。 模型在验证集或测试集上进行测试时所犯的误差称为泛化误差。
如果我们要在这两种类型的误差与偏差和方差之间建立关系(最终导致过拟合和欠拟合),则外观将类似于以下内容(尽管关系每次都可能不是线性的,如图所示):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d0834BXl-1681705004256)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/46c1d591-c385-4491-baca-a52af92648cb.png)]
如果 ML 模型不适合(高偏差),则其训练误差必须很高。 另一方面,如果模型过拟合(高方差),则其泛化误差很高。
我们将在下一节中介绍标准的 ML 工作流程。
标准的 ML 工作流程
任何项目都首先考虑到问题,而 ML 项目也不例外。 在开始 ML 项目之前,对要使用 ML 解决的问题有清楚的了解是非常重要的。 因此,关于标准 ML 工作流程的问题表述和映射是 ML 项目中的良好起点。 但是 ML 工作流程是什么意思? 本节就是关于这一点的。
设计 ML 系统并使用它们来解决复杂的问题不仅需要 ML,还需要一系列技能。 很高兴知道 ML 需要不同比例的知识,例如统计,领域知识,软件工程,特征工程和基础高中数学。 为了能够设计这样的系统,某些步骤对于几乎所有 ML 工作流程都是必不可少的,并且每个步骤都需要一定的技能。 在本节中,我们将看一下这些步骤并简要讨论它们。
该工作流程的灵感来自 CRISP-DM,它代表跨行业数据挖掘标准流程,在与数据挖掘和分析有关的许多行业中得到了广泛的应用。
数据获取
如本章前面所述,机器学习系统需要数据才能运行。 它并非始终可用,实际上,在大多数情况下,数据本身并不是以我们可以实际开始训练 ML 模型的格式提供的。 但是,如果没有针对我们要使用 ML 解决的特定问题的标准数据集,该怎么办? 欢迎来到现实! 大多数现实生活中的 ML 项目都会发生这种情况。 例如,假设我们正在尝试分析有关 2018 年新年决议的推文情感,并尝试估计最有意义的推文。 这实际上是没有标准数据集可用的问题。 我们将不得不使用其 API 从 Twitter 抓取它。 另一个很好的例子是业务日志。 商业日志是知识的宝库。 如果有效地进行挖掘和建模,它们可以在许多决策过程中提供帮助。 但是通常,日志不能直接提供给 ML 工程师。 因此,ML 工程师需要花费大量时间来确定日志的结构,他们可能会编写脚本,以便根据需要捕获日志。 所有这些过程统称为数据检索或数据收集。
数据准备
在数据收集阶段之后,我们倾向于准备数据以将其馈送到 ML 系统,这称为数据准备。 值得一提的是,这是 ML 工作流程/管道中最耗时的部分。 数据准备包括以下步骤:
- 探索性数据分析
- 数据处理与整理
- 特征工程和提取
- 特征缩放和选择
这是 ML 项目中最耗时的部分之一。 当我们更广泛地看待该过程时,我们发现数据标识和收集有时也确实是重要的方面,因为如前所述,正确的格式可能并不总是可用。
探索性数据分析(EDA)
收集数据之后,数据准备阶段的第一步是探索性数据分析,这是众所周知的 EDA。 EDA 技术使我们能够详细了解数据以更好地理解。 这是整个 ML 流程中极为重要的一步,因为如果没有对数据本身的充分了解,如果我们盲目地将 ML 模型拟合到数据,则很可能不会产生良好的结果。 EDA 为我们提供了前进的方向,并帮助我们确定了进一步的步骤。 EDA 涉及很多事情,例如计算有关数据的有用统计信息以及确定数据是否遭受任何异常值的影响。 它还包括有效的数据可视化,这有助于我们以图形方式解释数据,从而帮助我们以有意义的方式传达有关数据的重要事实。
简而言之,EDA 就是要更好地了解数据。
数据处理与整理
我们已经对数据进行了一些统计分析。 怎么办? 大多数情况下,从多个数据源收集的数据以其原始形式存在,无法馈入 ML 模型,因此需要进一步的数据处理。
但是您可能会问,为什么不以某种方式收集数据,以便在完成所有必要的处理后就可以对其进行检索? 这通常不是一个好习惯,因为它破坏了工作流程的模块化。
这就是为什么要在工作流的后续步骤中使数据可消耗,我们需要清理,转换和持久化数据。 其中包括几项内容,例如数据标准化,数据标准化,缺失值插补,从一个值到另一个值的编码以及离群值处理。 所有这些统称为数据处理。
特征工程和提取/选择
考虑一种情况,在这种情况下,会向分析公司的员工提供公司的账单数据,并要求其经理使用该数据构建机器学习系统,从而可以优化公司的整体财务预算。 现在,此数据的格式不能直接提供给 ML 模型,因为 ML 模型期望数据以数字向量的形式出现。
尽管数据可能状况良好,但是员工仍必须执行某些操作才能将数据转换为有利的形式。 考虑到数据已经被弄乱了,他们仍然需要确定他将要包含在最终数据集中的哪些特征。 实际上,任何可测量的都可以成为此处的特征。 这就是优秀的领域知识来的地方。 这些知识可以帮助员工选择具有高预测能力的特征。 听起来似乎很轻巧,但它需要很多技巧,而且绝对是一项艰巨的任务。 这是特征工程的经典示例。
有时,我们采用多种技术来帮助我们从给定的数据集中自动提取最有意义的特征。 当数据的维数很高且特征难以解释时,此功能特别有用。 这被称为特征选择。 特征选择不仅有助于开发具有最相关特征的数据的 ML 模型,而且还有助于增强模型的预测表现并减少其计算时间。
除了特征选择外,我们可能还想减少数据的维数以更好地可视化它。 此外,降维也用于从完整的数据特征集中捕获一组代表性特征。 主成分分析(PCA)是一种非常流行的降维技术。
重要的是要记住,特征选择和降维是不同的。
建模
我们终于走到了最激动人心的一步,即 ML 建模部分。 但是在这里值得注意的是,一个好的机器学习项目不仅仅涉及这部分。 前面提到的所有部分均对项目标准做出了同样的贡献。 实际上,如何为项目收集数据非常重要,为此,我们得到了功能强大的数据工程师的帮助。 现在,让我们将这一部分放在一边。
到目前为止,我们已经拥有了相当不错的数据。 在数据建模过程中,我们将训练数据输入到 ML 模型中进行训练,我们监视其训练进度并调整不同的超参数,从而优化其表现,并在测试集上对模型进行评估。 模型比较也是此阶段的一部分。 这确实是一个迭代过程,并且在某种程度上涉及反复试验。
这里的主要目的是提出一个最能代表数据的 ML 模型,即很好地泛化。 计算时间是我们在这里必须考虑的另一个因素,因为我们需要一个表现良好但在可行的时间范围内能够优化特定业务成果的模型。
以下是构成建模核心的部分:
- 模型训练
- 模型评估
- 模型调整
模型训练
这是建模的基础部分,因为我们将数据介绍给不同的 ML 模型,然后训练模型,以便它可以全面了解数据的表示形式。 我们可以看到模型在使用训练误差进行训练期间的进展。 我们也经常将验证误差(这意味着我们同时验证模型训练)引入此图片中,这是一种标准做法。 当今大多数现代库都允许我们这样做,我们将在本书的后续章节中看到它。 现在,我们将讨论一些最常用的误差指标。
模型评估
我们已经训练了一个 ML 模型,但是该模型对从未见过的数据的表现如何? 我们使用模型评估回答这个问题。
不同的机器学习算法需要不同的评估指标。
对于监督学习方法,我们通常使用以下方法:
- 混淆矩阵,它是由四个值组成的矩阵:真正,假正,真负和假负
- 准确率,精确度,召回率和 F1 分数(这些都是混淆矩阵的副产品)
- 受试者工作特征(ROC)的曲线下面积(AUC)度量
- R 平方(确定系数),均方根误差(RMSE),F 统计量,赤池信息准则(AIC)和 p 值(专门用于回归模型)
在本书中,我们将结合这些指标来评估我们的模型。 尽管这些是最常见的评估指标(无论是针对 ML 还是 DL),但还有更具体的评估指标对应于不同的域。 我们将做到这一点以及我们前进的方向。
这里值得一提的是,在分类问题中,数据不平衡的情况下,我们经常倾向于陷入准确率悖论的陷阱。 在这些情况下,分类准确率只能说出故事的一部分,也就是说,它给出的是正确预测所占预测总数的百分比。 在数据集不平衡的情况下,该系统严重失败,因为准确率无法捕获模型在预测数据集的否定实例时的表现(这最初是问题,即预测不常见的类)。
以下是评估无监督方法(例如聚类)的最常用指标:
- 轮廓系数
- 误差平方和
- 同质性,完整性和 V 度量
- Calinski-Harabasz 指数
对于训练集,测试集或验证集,评估指标/误差指标保持不变。 我们不能仅仅通过查看训练模型的表现来得出结论。
模型调优
在这个阶段,我们应该有一个基准模型,通过它我们可以进一步调整模型,使其表现更好。 模型调整对应于超参数调整/优化。
ML 模型带有不同的超参数,这些超参数无法从模型训练中学习。 他们的值是由从业者设定的。 您可以将超参数值与音频均衡器的旋钮进行比较,在此处我们可以手动调节旋钮以获得完美的听觉体验。 在后面的章节中,我们将看到超参数调优如何极大地提高模型的表现。
有几种用于调整超参数的技术,以下是最常用的技术:
- 网格搜索
- 随机搜索
- 贝叶斯优化
- 基于梯度的优化
- 进化优化
模型比较与选择
在完成模型调整部分之后,我们肯定希望对除当前模型之外的其他模型重复整个建模部分,以期获得更好的结果。 作为 ML 的从业者,我们的工作是确保最终提出的模型比其他模型更好(显然在各个方面)。 自然地,比较不同的机器学习模型是一项耗时的任务,当我们需要满足较短的期限时,我们可能无法总是负担得起。 在这种情况下,我们合并了 ML 模型的以下方面:
- 可解释性,可回答给定的问题(模型的可解释性如何以及模型的解释和传达的容易程度?)
- 内存内与内存外建模
- 数据集中的特征和实例数
- 类别与数值特征
- 数据的非线性
- 训练速度
- 预测速度
这些指标是最受欢迎的指标,但在很大程度上取决于当前的问题。 当这些指标不适用时,一个很好的经验法则是查看模型如何在验证集中执行。
部署和监控
构建机器学习模型后,它将与应用的其他组件合并并投入生产。 该阶段称为模型部署。 在将开发的 ML 模型部署到实际系统中之后,将评估其真实表现。 此阶段还涉及对模型的全面监视,以找出模型表现不佳的领域以及可以进一步改进模型的哪些方面。 监视非常关键,因为它提供了增强模型表现的手段,从而增强了整个应用的表现。
因此,这是 ML 项目所需的最重要的术语/概念的入门。
要更深入地学习 ML 的基础知识,建议您阅读以下资源:《Google 的机器学习速成课程》和《Python 机器学习》。
为了便于参考,您可以参考本书和《Python 深度学习实践指南》(Dipanjan 等人)中给出的下图,该图以图形方式描述了所有上述步骤:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M2jD5t8P-1681705004257)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/1adb3e40-cbd7-469f-8234-3002ac0d91c2.png)]
实际上,ML 在许多领域都带来了许多增强,并且几乎没有任何影响。 本书着重于构建智能 Web 应用。 因此,我们将从下一个章节开始,从总体上讨论 Web 以及从 AI 的前后角度来看自 AI 问世以来 Web 的变化。 最终,我们将研究一些知名企业,以及它们如何促进 AI 构建世界一流的 Web 应用,这些应用不仅智能而且可以解决一些实际问题。
AI 前后的互联网
如果您自 2014 年以来一直是万维网的普通用户,那么您会同意网站上迅速出现的快速变化。 从解决 ReCaptcha 挑战变得越来越难以辨认到在背景中自动将其标记为人类以来,Web 开发一直是显示大量人工智能的先驱之一。 在过去的二十年中创建。
互联网的发明者蒂姆·伯纳斯·李爵士在语义网上提出了自己的观点:
“我梦想着使 Web(计算机)能够分析 Web 上的所有数据,包括人与计算机之间的内容,链接和事务。Web 的梦想还没有实现。 出现了,但是当它出现时,日常的贸易,官僚主义和我们的日常生活机制将通过与机器交谈的机器来处理。被人们吹捧多年的“智能体”将最终实现。”
通过提供静态页面,其中包含大量可见信息以及可将您永久带到相关资源的链接,网络现在是不断变化的动态生成信息门户。 如果刷新网页,则可能永远不会再看到相同的网页视图。
让我们了解由于 AI 的兴起而引起的 Web 开发中一些最重要的转变。
聊天机器人
如果您想知道某些网页如何通过其网站上的聊天提供 24/7 全天候帮助,答案几乎总是聊天机器人正在从另一端回答您的查询。 1966 年,约瑟夫·魏岑鲍姆(Joseph Weizenbaum)的 ELIZA 聊天机器人击败图灵测试在全世界掀起了一波热潮时,我们从来没有想到过聊天机器人会在万维网上产生的影响(不过,其原因可能是 ARPANET 本身仅是由 ARPANET 创造的) 在 1969 年)。
如今,聊天机器人无处不在。 许多《财富》 500 强公司都在该领域进行研究,并提出了针对其产品和服务的聊天机器人的实现方案。 在甲骨文最近进行的一项调查中,来自几家公司和初创公司的 800 位高管的回应表明,其中近 80% 的人表示,他们已经使用或计划在 2020 年之前在面向客户的产品中使用聊天机器人。
在 AI 开始为聊天机器人提供动力之前,就像 ELIZA(及其后继产品 ALICE)一样,聊天机器人主要是关于一组固定响应的映射到几种输入模式的。 在用户输入的句子中碰到单词母亲或父亲时,几乎可以肯定会产生一个关于用户家庭或他们的幸福的回答。 如果用户写了“我不想谈论 XYZ 的家庭”之类的东西,显然这不是所需的响应。
然后,这种基于规则的聊天机器人产生了著名的“对不起,我没有得到”答复,这使它们有时显得很愚蠢。 基于神经网络的算法的出现使聊天机器人能够根据用户的情感和用户输入的上下文来理解和自定义响应。 此外,一些聊天机器人会在遇到任何新查询的情况下抓取在线数据,并实时建立有关新的未知查询中提到的主题的答案。 除此之外,聊天机器人已用于为企业门户提供替代接口。 现在可以通过 WhatsApp 提供的聊天机器人平台预订酒店或航班。
Facebook Messenger 的漫游器平台在向公众开放的前 17 个月内创建了超过 100,000 个漫游器。 如今,这家社交网络巨头的数百个页面对向其页面发送消息的用户具有自动响应。 Twitter 上运行着多个机器人,这些机器人可以创建内容,紧密模仿人类用户,并且可以回复其帖子中的消息或评论。
您可以在 eliza.botlibre.com 与在线版本的 ELIZA 聊天。
网络分析
在互联网的早期,许多网站都在其中嵌入了里程表式计数器。 这些是对网站或特定页面获得的点击次数的简单计数。 然后,它们以可用的格式增长-普通计数器,每天/每周/每月的计数器,甚至是基于地理位置的计数器。
数据收集,本质上是用户交互以及他们如何与基于 Web 的应用交互的日志,处理这些数据以生成表现指标,然后最终确定公司可以采取的措施来改善他们的 Web 应用统称为 Web 分析。
自从互联网发明以来,当今的 Web 应用每时每刻都会生成大量日志。 即使将鼠标指针闲置在网页上,也可能会报告给 Google Analytics(分析)仪表板,从该站点上,网站管理员可以查看用户正在查看哪些页面以及他们在页面上花费了多少时间。 同样,用户在页面之间采取的流量将是一个非常有趣的指标。
最早的网络分析工具仅能衡量网页点击量,能够创建一个映射来访问给定页面的次数以及该页面是一个唯一用户的次数,但除非提供有关用户访问模式的信息,否则它们几乎无法提供任何信息。它们经过了专门的硬编码,将以非常笼统的方式呈现,并且从来都不是特定于网站的。 正在向进行电子商务的公司提供与向个人网站提供的分析相同的形式。
随着 AI 在网络分析领域带来的革命,如今部署人工智能功能的工具可以对网站的表现做出未来的预测,甚至建议删除或添加网页上的特定内容以提高用户对该页面的参与度 。
垃圾邮件过滤
当全世界发送的一半电子邮件被标记为垃圾邮件时,这是一个问题。 乍一想,我们将欺诈性电子邮件和不必要的电子邮件联系在一起,以宣传企业和产品为垃圾邮件,这只是定义的一部分。 重要的是要认识到,即使多次在同一文档上发布高质量的内容也是垃圾邮件。 此外,自术语垃圾邮件首先在 Usenet 组中使用以来,网络已经发展起来。 最初是为了使人烦恼而进行的活动,或者是强迫向某些目标用户发送消息的活动,但如今,垃圾邮件的发展更为广泛,并且可能更加危险—从能够跟踪浏览器活动到身份盗用, 如今,互联网上存在大量恶意垃圾邮件,这些垃圾邮件危及用户的安全性和隐私性。
如今,我们有各种垃圾邮件-即时通讯垃圾邮件,网站垃圾邮件,广告垃圾邮件,SMS 垃圾邮件,社交媒体垃圾邮件以及许多其他形式。
除了少数几种,大多数垃圾邮件都在互联网上展出。 因此,至关重要的是能够过滤垃圾邮件并采取针对性措施。 虽然最早的反垃圾邮件斗争始于 1990 年代,当时它确定发送垃圾邮件的 IP 地址,但是随着黑名单的规模越来越大,黑名单的分发和维护成为一种黑名单,很快人们意识到这是一种效率很低的方法。 疼痛。
在 2000 年代初期,保罗·格雷厄姆(Paul Graham)首次发表了题为《垃圾邮件计划》的论文时,部署了一种 ML 模型(贝叶斯过滤)来对抗垃圾邮件。 不久,从纸上纺出了几种反垃圾邮件工具,并证明是有效的。
贝叶斯过滤方法对垃圾邮件的影响就是这种影响。在 2004 年的世界经济论坛上,微软的创始人比尔·盖茨向前说:
“从现在开始的两年内,垃圾邮件将得到解决。”
然而,正如我们今天所知,比尔·盖茨在这一预测中再没有错。 垃圾邮件不断发展,垃圾邮件发送者研究贝叶斯过滤并找出避免在检测阶段被标记为垃圾邮件的方法。 如今,神经网络已大规模部署,不断扫描新电子邮件并做出确定垃圾邮件或非垃圾邮件内容的决定,而人类仅仅通过研究电子邮件垃圾邮件日志就无法达到逻辑上的目的。
搜索
网络搜索是 AI 崛起影响最大的领域之一。 从必须知道您希望访问的特定网页标题的确切措辞的卑微开始,到能够识别您环境中可听的歌曲的搜索引擎,由于 AI,该域已完全转型。
1991 年,蒂姆·伯纳斯·李(Tim Berners-Lee)建立了万维网虚拟图书馆,它看起来像这样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BvBlunBf-1681705004257)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/120bb432-ece2-40bf-9b64-19142d707316.png)]
它是手动列出的网页的集合,可通过搜索框过滤,该搜索框显示在右上角。 显然,用户本人必须决定自己的搜索项所属的类别,而不是尝试预测用户打算查找的内容。
网络搜索引擎的当前面貌是由 Johnathan Fletcher 于 1993 年 12 月提出的,当时他创建了 JumpStation,这是第一个使用现代的爬网,索引和搜索概念的搜索引擎。 JumpStation 使用的外观是我们今天如何看到领先的搜索提供商(例如 Google 和 Bing),并使 Johnathan 成为“搜索引擎之父”。
两年后的 1995 年 12 月,当 AltaVista 推出时,它带来了搜索技术的巨大转变-无限的带宽,搜索提示,甚至允许自然语言查询-1997 年 Ask Jeeves 更加强烈地引入了这一功能。
Google 于 1998 年问世。它带来了 PageRank 技术。 但是,市场上有几个竞争者,而 Google 当时并没有主导搜索引擎游戏。 五年后,当 Google 申请使用神经网络根据用户以前的搜索历史和访问过的网站的记录来定制搜索结果的专利时,该游戏迅速转向 Google,成为搜索领域最强大的提供商。
如今,庞大的代码库部署了多个深度一致的神经网络,为 Google 搜索提供了强大的动力。 由于主要使用神经网络进行的自然语言处理,使得 Google 能够确定网页的内容相关性,并且借助卷积神经网络(CNN)可以实现机器视觉 Google 图片搜索中对我们可见的准确结果。 John Ginnandrea 领导 Google 搜索并推出了知识图谱(Google 有时会针对某些问题(例如查询)提出的答案)并不令人感到惊讶; 他是人工智能领域最受追捧的专家之一,现在已经被 Apple 聘用,以改善 Siri,这又是一种神经网络产品。
最大的网络 AI 玩家以及他们如何使用 AI
AI 的突飞猛进使许多竞争者得以充分利用。 在过去的二十年中,数位个人,初创企业甚至大型工业家都在寻求从 AI 应用中获得的收益。 市场上有一些产品将人工智能作为其业务的核心。
“战争是 90% 的信息。”
————拿破仑·波拿巴,公元 18 世纪。
在第二次世界大战中,盟军部署了轰炸机。 这些是盟军采用的策略的关键。 但是不知何故,这些轰炸机未能交付,原因是它们在敌国领土上被大量击落。 很明显,轰炸机需要更多的装甲。 但是由于装甲的重量,不可能完全覆盖飞机。 因此,决定飞机的最关键区域应加装额外的装甲。 犹太数学家亚伯拉罕·瓦尔德(Abraham Wald)被要求提出一种方法,以确定飞机的哪些区域必须进行装甲。 他研究了从战斗中回来的飞机,并记下了哪些区域带有最多的子弹痕迹。
结果发现,机翼,机鼻和机尾是带有最多子弹痕的零件,并且得出的结论是这些是需要更多装甲的零件,而驾驶舱和发动机的子弹孔最少:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tPYNsBpv-1681705004257)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/a6c5d938-0eea-4819-8a4c-6e5561ccbd12.png)]
但是令人惊讶的是,与常规的思维方式背道而驰,沃尔德提出,需要驾驶室的是机舱和发动机,因为这些轰炸机没有返回。 机尾,机翼和机鼻中的子弹无法对飞机造成致命伤害,因此他们成功返回。
这样,通过处理数据并确定正确的模式,第二次世界大战的整个过程就被数学家改变了。 数据被称为新油。 更有意思的是,当您拥有石油时,会燃烧石油以产生电力和能源,以驱动车辆。 但是,有了数据,您就可以使用它来改善业务并制定决策,从而在将来产生更多数据。 意识到这一点并从现有数据中获得最大收益的公司在最近几年取得了巨大的增长。 让我们探索一下使用 AI 使用所有可用数据进行此类公司处理的公司。
谷歌
提到“人工智能”一词后,几乎每个人都想到了这个名字。
“我们现在目睹了计算领域的新变化:从移动优先到人工智能优先的转变。”——Google 首席执行官 Sundar Pichai
Google 一直在其多种产品中使用 AI; 让我们在这里进行一些探讨。
谷歌搜索
在 2018 年 12 月 14 日搜索who is the google ceo
时,显示了类似于以下屏幕截图的结果页面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UkCmkvFQ-1681705004258)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/11e3506d-ee55-42ad-a013-ee0a1016a75b.png)]
前面的功能会生成常见问题的答案,称为 Google 知识图,我们在前面的部分中提到过。 除了这一功能,由于自然语言处理和信息提取等人工智能技术,Google 搜索的功能也成倍增长。
借助 AI,可以在视频中提出与用户查询有关的准确时间安排,这一切都要归功于 AI:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QK88xRvU-1681705004258)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/b6b48628-65c7-4267-a4d4-523f18176d6c.png)]
接下来,我们将看一下 Google Translate。
谷歌翻译
Google 翻译支持 100 多种语言,可能是互联网上公开提供的最好的翻译工具。 从能够检测输入的语言到将其转换为用户设置的所需语言,在后台运行着深层的神经网络以产生最佳结果。 Google 于 2016 年 11 月将其转换为 Google 神经机器翻译算法。 对于希望实时翻译其网站内容以迎合不同地区用户的 Web 开发人员,它可以作为 API 在 Web 上使用。 此外,该服务还与 Google 的浏览器 Google Chrome 集成在一起,并在用户使用浏览器访问网页后立即提供网页的实时翻译。
谷歌助手
谷歌助手是谷歌的最新业务之一,是苹果 Siri 和微软 Cortana 的竞争对手,也是谷歌即时的继任者。 它是一款基于 AI 的虚拟助手,可在移动和智能家居设备(商标为 Google Home)上使用。 目前,它可以在用户的Google 云端硬盘数据上进行搜索,根据用户的偏好产生结果,提醒用户提供的标注,拨号,发送短信,以及按照用户的指示(通过常规点击- 在触摸屏上输入或通过语音输入:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sfo54pCG-1681705004258)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/02c4611b-f44d-47af-8ba3-932197617f70.jpeg)]
接下来,我们将介绍其他产品。
其他产品
人工智能是支持 Google Ads 的主要技术之一。 使用神经网络解决了点击诱饵或假点击的问题。 此外,使用 AI 可以有效地帮助您确定哪种类型的广告效果最好,直到每个网页的水平。 Google 广告服务的这些技术进步使其迅速从先前存在的广告平台中抢占了互联网广告空间。
Google 项目(例如 Google Lens,自动驾驶汽车和许多其他项目)主要是基于 AI 的项目。
脸书
作为具有多个配置文件的互联网上最大的社交网络平台,Facebook 每天都会生成大量数据。 其发布内容的用户数据,用户做出的报告,Facebook 提供的各种 API 的日志等等,所有这些每天总计产生近 4 PB 的数据。 不用说,这家科技巨头已经利用了这一数据黄金,并提出了使平台对用户更安全并提高用户参与度的方法。
伪造的个人资料
Facebook 面临的主要问题是大量存在虚假个人资料。 为了应对这些问题,Facebook 部署了基于 AI 的解决方案来自动标记和挑战此类配置文件以确认其身份。 仅在 2018 年第一季度,Facebook 就禁用了近 5.83 亿个伪造或克隆账户。
假新闻和令人不安的内容
Facebook 及其收购的消息服务 WhatsApp 面临的另一个问题是假新闻或误导性新闻问题。 而且,在平台上存在视觉和/或情感上令人不安的内容,这加剧了用户体验的下降。 最后,几乎所有的在线平台都必须对抗:垃圾邮件。 多年来,Facebook 的 AI 算法已经非常擅长识别和清除垃圾邮件。 通过使用 CNN 促进的计算机视觉解决方案的应用,Facebook 能够提供一种功能,该功能可以覆盖/模糊视觉干扰的图像和视频,并在允许用户查看之前征求用户的同意。
识别和删除虚假新闻的工作目前正在进行中,并且几乎完全由 AI 的应用完成。
其他用途
Facebook 提供了自己的 Messenger bot 平台,Facebook 页面和开发人员广泛使用该平台,以将丰富的交互功能添加到公司提供的即时消息服务中。
亚马逊
亚马逊是互联网上领先的电子商务平台,几乎已将 AI 集成到其所有产品和服务中。 尽管 Google,Facebook,Microsoft 和 IBM 都参加了 AI 派对的晚会,但亚马逊迅速发展并吸引了人们对其 AI 的各种用途的关注。 让我们看一下 Amazon 附带的一些主要应用。
Alexa
为公司所有 Alexa 和 Echo 设备提供支持的 AI,Alexa 是与 Google Home(由 Google Assistant(以前称为 Google Now)提供支持)直接竞争开发的虚拟助手 AI 的名称。 不用争论哪个更好,Alexa 是一种相当先进的 AI,能够为许多用户发现的有趣和机智的问题提供答案。 最近,随着亚马逊采取行动将 Alexa Skills Studio 公开提供给开发人员,Alexa 产品的采用率有所上升,这大大增加了 Alexa 可以执行的操作。
亚马逊机器人
用户从网站上购买产品后,就会坐在位于华盛顿肯特郡庞大的 855,000 平方英尺庞大的配送中心(显然,仅适用于那里可用的产品)上的机器人激怒,抬起一大箱产品, 运送到现场,运送在平台上出售的商品,然后由工作器从板条箱中取出以进行进一步处理。 在此前非常成功的运行之后,亚马逊最近为其密尔沃基配送中心配备了相同的技术,并计划将其扩展到其他 10 个大型中心。
DeepLens
启用了人工智能的摄像机在 2000 年代初将是极客的幻想。 正是由于 Amazon DeepLens 的到来,打开了无限的可能性。 想象一下这样的情况:您是聚会的主持人,而您会直接在手机上收到所有来宾的通知。 令人惊讶的是,已经实现了这一点,甚至在为公共场所配备闭路电视摄像机上进行了实验,这些摄像机可以识别罪犯并自动触发警报。
总结
在本章中,我们简要介绍了许多重要的概念和术语,这些概念和术语通常对执行 ML 项目至关重要。 这些将在整本书中有所帮助。
我们从什么是人工智能及其三种主要类型开始。 我们了解了导致我们周围发生的 AI 爆炸的因素。 然后,我们快速浏览了 ML 的几个组成部分以及它们如何为 ML 项目做出贡献。 我们看到了什么是 DL,以及 AI,ML 和 DL 是如何连接的。
在本章的最后,我们看到了一些示例,其中 AI 与 Web 技术融合在一起,以创建有望解决复杂问题的智能应用。 几乎所有启用了 AI 的应用的背后都是 DL。
在下一章中,我们将利用 DL 来制作智能 Web 应用。
第 2 节:使用深度学习的 Web 开发
本节介绍了与深度学习相关的基本概念和术语,并介绍了如何使用深度学习通过 Python 中的不同深度学习库来构建简单的 Web 应用。
本节包括以下章节:
- “第 2 章”,“使用 Python 进行深度学习入门”
- “第 3 章”,“创建您的第一个深度学习 Web 应用”
- “第 4 章”,“TensorFlow.js 入门”
二、使用 Python 入门深度学习
在第一章中,我们非常仔细地研究了深度学习及其与机器学习和人工智能的关系。 在本章中,我们将深入探讨该主题。 我们将从了解深度学习核心的内容开始,即神经网络及其基本组件,例如神经元,激活单元,反向传播等。
请注意,本章在数学上不会太繁琐,但是与此同时,我们也不会缩短对神经网络世界至关重要的最重要的公式。 如果您想对数学进行更深入的研究,建议读者阅读 Goodfellow 等人的书籍《深度学习》。
以下是对本章内容的概述:
- 神经网络及其相关概念揭秘
- 深度学习与浅层学习
- 不同类型的神经网络
- 设置基于深度学习的云环境
- 探索 Jupyter 笔记本
揭秘神经网络
让我们从找到以下问题的答案开始本节:“为什么神经网络被称为“神经”?”。 这个词背后的意义是什么?
我们的直觉说,这与我们的大脑有关,这是正确的,但只是部分原因。 在我们弄清它为什么只能部分正确的原因之前,我们需要对大脑的结构有所了解。 为此,让我们看一下我们自己大脑的解剖结构。
人脑由大约 100 亿个神经元组成,每个神经元都与大约 10,000 个其他神经元相连,从而使其具有类似网络的结构。 神经元的输入称为树突,输出称为轴突。 神经元的身体称为躯体。 因此,在较高的级别上,特定的躯体与另一个躯体相连。 “神经”一词来自“神经元”,实际上,神经是“神经元”一词的形容词形式。 在我们的大脑中,神经元是形成我们刚才讨论的密集网络的最细粒度的单元。 我们正在慢慢地了解人工神经网络与大脑的相似性,为了继续我们对这种相似性的理解,我们将简要了解神经元的功能。
网络不过是一个类似图的结构,它包含一组相互连接的节点和边。 就我们的大脑或一般任何大脑而言,神经元被称为节点,树突被称为顶点。
神经元通过其树突接收其他神经元的输入。 这些输入本质上是电化学的。 并非所有输入都同样强大。 如果输入足够强大,则连接的神经元将被激活,并继续将输入传递给其他神经元的过程。 它们的能力由预定义的阈值确定,该阈值允许激活过程具有选择性,因此它不会同时激活网络中存在的所有神经元。
总而言之,神经元从其他神经元接收输入的总和,将该总和与阈值进行比较,并相应地激活神经元。 人工神经网络(ANN),或简称为神经网络(NN),因此, 相似。
那么,是什么使网络成为神经网络呢? 形成 NN 需要什么?
以下来自 Adrian Rosebrock 的书《Python 计算机视觉和深度学习》中的引言以一种非常值得称赞的方式回答了这个问题:
每个节点执行一个简单的计算。 然后,每个连接都承载从一个节点到另一节点的信号(即计算的输出),并用权重标记,该权重指示信号放大或减小的程度。 一些连接具有较大的正权重,可以放大信号,表明进行分类时信号非常重要。 其他的则具有负权重,从而降低了信号的强度,因此指定了节点的输出在最终分类中的重要性较低。 如果这种系统由具有可通过学习算法修改的连接权重的图结构组成,则我们将其称为人工神经网络。
我们已经了解了神经网络与大脑的相似之处。 现在,我们将获取这些信息,并详细了解 ANN 的粒度单位。 让我们从学习简单的神经元在人工神经网络中必须要做的事情开始。
人工神经元
我们将其称为 ANN 人工神经元中使用的神经元。 广义上讲,人工神经元可以分为两种类型:
- 线性神经元
- 非线性神经元
线性神经元的剖析
神经元是神经网络中最细粒度的单元。 让我们看一下“神经网络”的第二个词。 网络不过是一组边缘相互连接的顶点(也称为节点)。 在神经网络的情况下,神经元充当节点。 让我们考虑以下神经网络架构,并尝试对其进行逐段分析:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DMkFXqM5-1681705004259)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/c5502868-fd41-47b9-859a-f36bcfd8020e.png)]
在上图中,我们可以看到一个具有两个隐藏层(在一个神经网络中,一层是一组神经元)且具有单个输出的神经网络。 实际上,这称为两层神经网络。 神经网络包含以下内容:
- 一个输入
- 两个隐藏层,其中第一个隐藏层包含三个神经元,第二个隐藏层包含两个神经元
- 一个单输出
将这些层称为隐藏层并没有更深层次的心理意义,仅因为涉及这些层的神经元既不是输入也不是输出的一部分,所以将它们称为隐藏层。 这里很明显的一件事是,第一个隐藏层之前有一层。 为什么我们不计算该层? 在神经网络的世界中,初始层和输出不在层堆叠中。 简而言之,如果存在n
个隐藏层,则它是n
层神经网络。
初始层(也称为输入层)用于接收神经网络的主要输入。 接收到主要输入后,输入层中存在的神经元会将它们传递给后续隐藏层中存在的下一组神经元。 在这种传播发生之前,神经元将权重添加到输入,并将偏项添加到输入。 这些输入可以来自各个域,例如,输入可以是图像的原始像素,音频信号的频率,单词集合等。 通常,这些输入作为特征向量提供给神经网络。 在这种情况下,输入数据仅具有一个特征。
现在,接下来两层的神经元在做什么? 这是一个重要的问题。 我们可以考虑将权重和偏差添加到输入中,作为学习的第一层/第一层(也称为决策层)。 初始隐藏层中的神经元重复此过程,但是在将计算的输出发送到下一个隐藏层中存在的神经元之前,它们会将此值与阈值进行比较。 如果满足阈值标准,则仅将输出传播到下一个级别。 整个神经网络学习过程的这一部分与我们之前讨论的生物学过程有很大的相似之处。 这也支持以分层方式学习复杂事物的哲学。
这里提出的一个问题是:“如果不使用任何隐藏层,会发生什么?”。 事实证明,与仅包含输入层和输出的网络相比,在神经网络中添加更多级别的复杂性(通过添加更多层)可以使神经网络以更简洁的方式学习输入数据的基本表示形式。 但是我们需要几层呢? 我们稍后再讲。
让我们在这里介绍一些数学公式以形式化我们刚刚研究的内容。
我们将输入特征表示为x
,权重表示为w
,偏差项表示为b
。 我们当前尝试剖析的神经网络模型基于以下规则:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y1SNMr4T-1681705004259)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/73d8417a-6a5d-48b3-9d3c-2df95ad05b55.png)]
该规则表示,在计算加权输入和偏差的总和之后,如果结果大于 0,则神经元将产生 1;如果结果小于或等于 0,则神经元将简单地产生 0,换句话说,神经元就不会触发。 在具有多个输入功能的情况下,规则保持完全相同,并且规则的多元版本如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nWUq2lm4-1681705004259)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/fd99fc73-d225-45bc-9dcb-8ae25e9d5647.png)]
在这里,i
意味着我们总共具有i
个输入特征。 可以将以下规则分解为:
- 我们分别处理特征,然后将其乘以权重
- 针对所有单个输入特征完成此过程后,我们将所有加权输入进行求和并求和,最后加上偏差项。
对于网络中的层数,将继续执行前面的过程。 在这种情况下,我们有两个隐藏层,因此一层的输出将被馈送到下一层。
我们刚刚研究的元素是 Frank Rosenblatt 在 1960 年代提出的。 基于某个阈值为输入的加权总和分配 0 或 1 的想法也称为阶跃函数。 文献中有许多这样的规则,这些规则称为更新规则。
我们研究的神经元是能够学习线性函数的线性神经元。 它们不适合学习本质上是非线性的表示。 实际上,馈送神经网络的几乎所有输入实际上都是非线性的。 在下一部分中,我们将介绍另一种能够捕获数据中可能存在的非线性的神经元。
某些人可能想知道这种 NN 模型是否称为 MLP(多层感知器)。 好吧,是的。 实际上,罗森布拉特(Rosenblatt)早在 1960 年代就曾提出过这种建议。 那么什么是神经网络? 我们将在短期内了解答案。
非线性神经元的剖析
非线性神经元意味着它能够响应数据中可能存在的非线性。 在这种情况下,非线性本质上意味着对于给定的输入,输出不会以线性方式变化。 看下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pMOYnj1v-1681705004260)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/43498898-28e2-44bf-a26c-a071493c6de0.png)]
前面的两个图都描述了提供给神经网络的输入与该网络产生的输出之间的关系。 从第一个图很明显,输入数据是线性可分离的,而第二个图告诉我们输入不能线性分离。 在这种情况下,线性神经元将严重失败,因此需要非线性神经元。
在神经网络的训练过程中,可能会出现这样的情况,即偏差和权重值的微小变化可能会以剧烈的方式影响神经网络的输出。 理想情况下,这不应发生。 偏差或权重值的微小变化都应该仅导致输出的微小变化。 使用阶跃函数时,权重和偏差项的变化会在很大程度上影响输出,因此需要阶跃函数以外的其他东西。
神经元的运作背后是一种函数。 在线性神经元的情况下,我们看到其操作是基于阶跃函数的。 我们有一堆能够捕获非线性的函数。 Sigmoid 函数就是这样的函数,使用该函数的神经元通常被称为 Sigmoid 神经元。 与阶跃函数不同,对于 Sigmoid 神经元,将使用以下规则生成输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OlsWcIHa-1681705004260)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/c07baaa1-6d33-4e1b-b759-e81faa535390.png)]
因此,我们最终的更新规则如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DliaX6Lk-1681705004260)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/f641c5d4-03a7-4cf6-8f46-eacadf6206f4.png)]
但是,为什么在捕获非线性方面,Sigmoid 函数比阶跃函数好呢? 让我们在图形中比较它们的表现来了解这一点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HrycwSTl-1681705004260)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/c67899b6-21c7-48ed-adf3-4fd1efd01d29.png)]
前两个图使我们清楚地了解了两个函数的内在本质。 绝对清楚的是,Sigmoid 函数比阶跃函数对非线性更为敏感。
除 Sigmoid 函数外,以下是一些众所周知的常用函数,这些函数使神经元具有非线性特征:
- Tanh
- ReLU
- 泄漏的 ReLU
在文献中,这些函数以及我们刚刚研究的两个函数都称为激活函数。 目前,ReLU 及其变体是迄今为止最成功的激活函数。
我们仍然剩下一些与人工神经网络有关的基本知识。 让我们总结一下到目前为止所学到的内容:
- 神经元及其两种主要类型
- 层数
- 激活函数
我们现在可以在 MLP 和神经网络之间划清界限。 迈克尔·尼尔森(Michael Nielson)在其在线书《神经网络和深度学习》中对此进行了很好的描述:
由于历史原因,这种多层网络有时令人困惑,尽管它们由 Sigmoid 神经元而非感知器组成,但有时也称为多层感知器或 MLP 。
在本书中,我们将使用神经网络和深度神经网络术语。 现在,我们将继续前进,并进一步了解神经网络的输入和输出层。
关于神经网络的输入和输出层的说明
重要的是要了解可以作为神经网络输入的内容。 我们是否将原始图像或原始文本数据馈送到神经网络? 还是有其他方法可以向神经网络提供输入? 在本节中,我们将学习计算机是如何真正解释图像的,以显示在处理图像时可以准确地输入神经网络(是的,神经网络在图像处理方面非常出色)。 我们还将学习显示如何向原始数据提供神经网络的方法。 但在此之前,我们需要清楚地了解如何将常规表格数据集作为神经网络的输入。 因为表格数据集无处不在,所以采用 SQL 表,服务器日志等形式。
为此,我们将采用以下玩具数据集:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z0FDmSfL-1681705004261)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/92ffcb3f-b5eb-4b0c-8ec6-a364fa7a3775.png)]
请注意有关此玩具数据集的以下几点:
- 它具有两个预测变量,
x1
和x2
,这些预测变量通常称为输入特征向量。 - 通常将
x1
和x2
分配给向量X
(稍后会详细介绍)。 - 响应变量为
y
。 - 我们有 10 个实例(包含
x1
,x2
和y
属性)被分为两个类别,0 和 1。 - 给定
x1
和x2
,我们的(神经网络)任务是预测y
,这实际上使这成为分类任务。
当我们说神经网络预测某事时,是指应该学习最能近似某个函数的输入数据的底层表示(我们看到了一段时间前的特征映射看起来像)。
现在,让我们看看如何将这些数据作为神经网络的输入。 由于我们的数据具有两个预测变量(或两个输入向量),因此神经网络的输入层必须包含两个神经元。 我们将使用以下神经网络架构来完成此分类任务:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-675EDf9W-1681705004261)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/bdab7ceb-ab2d-45c2-8c77-c896a0bfb2c5.png)]
该架构与我们之前看到的架构完全相同,但是在这种情况下,我们添加了一个输入特征向量。 其余的完全一样。
为简单起见,我们没有考虑将数据馈送到网络之前可能需要的数据预处理。 现在,让我们看看如何将数据与权重和偏差项组合,以及如何将激活函数应用于权重和偏差项。
在这种情况下,特征向量和响应变量(即y
)由神经网络分别解释,响应变量将在网络的训练过程的后期使用。 最重要的是,它用于评估神经网络的表现。 输入数据以矩阵形式组织,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oISeVV5e-1681705004265)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/75262c2f-fdbc-41c3-b461-9a04b2d24de7.png)]
我们现在使用的 NN 架构是全连接架构,这意味着特定层中的所有神经元都与下一层中的所有其他神经元相连。
权重矩阵定义如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yWoBN6c3-1681705004265)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/6c7cc6d2-b697-492b-b277-1a3217a04fe2.png)]
现在,让我们不用担心权重值。 权重矩阵的尺寸解释如下:
- 行数等于特征向量的数量(在我们的示例中为
x1
和x2
)。 - 列数等于第一个隐藏层中的神经元数。
矩阵中的每个权重值都有一些后缀和上标。 如果我们将权重的一般形式表示为W[jk]^l
,则应将其解释如下:
l
表示要从其到达权重的层。 在这种情况下,我们刚刚看到的权重矩阵将与输入层相关联。j
表示神经元在l
中的位置,而k
表示神经元在该值传播到的下一层中的位置。
权重通常是随机初始化的,这会向神经网络添加随机字符。 让我们为输入层随机初始化一个权重矩阵:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XSImBRQb-1681705004265)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/28fe1ff8-96fa-4c85-ac98-fa1c4d0ec5db.png)]
现在,我们计算要提供给 NN 的第一个隐藏层的值。 计算方法如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ei2kKZCl-1681705004266)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/4716dd77-2dfc-490d-b796-aa1215cab251.png)]
第一个矩阵包含训练集中的所有实例(没有响应变量y
),第二个矩阵是我们刚刚定义的权重矩阵。 乘法的结果存储在变量Z^(1)
中(此变量可以命名为任何名称,上标表示它与网络的第一个隐藏层有关)。
在将这些结果发送到下一层将应用激活函数的神经元之前,我们还有一步。 Sigmoid 激活函数和输入层的最终输出如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rKxJAZXr-1681705004266)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/20a23d3e-6c8c-44ac-874b-3e088653fec9.png)]
在这里,Z^(1)
是我们对下一层神经元的最终输出。 注意,将 Sigmoid 函数应用于Z^(1)
矩阵的每个元素。 最终矩阵的尺寸为10 X 3
,其中每一行用于训练集中的每个实例,每一列用于第一隐藏层的每个神经元。
我们看到的整个计算没有我们最初谈到的偏差项b
。 好吧,这只是在图片中添加另一个维度的问题。 在那种情况下,在我们将 sigmoid 函数应用于Z^(1)
矩阵的每个元素之前,矩阵本身将被更改为以下形式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9S4dtSgU-1681705004266)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/541cba3d-b30e-416a-8eac-8a9b0ec6f6f1.png)]
经过这个矩阵乘法处理后,应用了 Sigmoid 函数并将输出发送到下一层的神经元,整个过程对 NN 中的每个隐藏层和输出层重复。 在继续过程中,我们应该从输出层获取a^(3)
。
Sigmoid 激活函数输出的值范围为 0-1,但是我们正在处理二分类问题,我们只希望 0 或 1 作为 NN 的最终输出。 我们可以稍作调整。 我们可以在 NN 的输出层定义一个阈值-对于小于 0.5 的值,应将其标识为 0 类,而对于大于或等于 0.5 的值应将其标识为 1 类。请注意,这称为前向通过或前向传播。
我们刚刚看到的 NN 被称为前馈网络,在学习过程中没有进一步优化。 可是等等! 网络甚至学到什么? 嗯,NN 通常会学习权重和偏差项,以便最终输出尽可能准确。 梯度下降和反向传播会发生这种情况。
梯度下降和反向传播
在开始学习在神经网络的背景下梯度下降和反向传播必须做什么之前,让我们学习一个优化问题的含义。
简短地说,优化问题对应于以下内容:
- 最小化成本
- 最大化利润
现在让我们尝试将其映射到神经网络。 如果从前馈神经网络获得输出后,如果发现其表现达不到标准(几乎所有时间都是这种情况),会发生什么情况? 我们将如何增强 NN 的表现? 答案是梯度下降和反向传播。
我们将使用这两种技术来优化神经网络的学习过程。 但是,我们要优化什么呢? 我们将如何最小化或最大化? 我们需要一种特定类型的成本,我们将尝试将其最小化。
我们将根据函数定义成本。 在为 NN 模型定义成本函数之前,我们必须确定成本函数的参数。 在我们的案例中,权重和偏差是 NN 试图学习以提供给我们准确结果的函数的参数(请参阅本节之前的信息框)。 此外,我们将必须计算网络在训练过程的每个步骤中所造成的损失量。
对于二元分类问题,称为交叉熵损失函数的损失函数(对于二元分类问题,称为二元交叉交叉熵损失函数)被广泛使用,我们将使用它。 那么,此函数是什么样的呢?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YDeLhMHp-1681705004266)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/6d3375fb-d8cf-49aa-ba7e-26efe4d6b342.png)]
在此,y
表示给定实例的地面真值或真实标签(记住训练集中的响应变量y
),y_hat
表示由 NN 模型。 该函数本质上是凸的,对于凸优化器(例如梯度下降)而言非常理想。
这是我们没有选择一个更简单且不凸的损失函数的原因之一。 (如果您不熟悉凸和非凸等术语,请不要担心。)
我们现在有损失函数。 请记住,这仅是整个数据集的一个实例,而不是我们将要应用梯度下降的函数。 前面的函数将帮助我们定义最终将使用梯度下降进行优化的成本函数。 让我们看看成本函数的样子。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t62NFXzU-1681705004266)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/2fe2f681-2cd6-4422-b942-fba491e140e1.png)]
这里,w
和b
是网络试图学习的权重和偏差。 字母m
表示训练实例的数量,在这种情况下为 10。 其余的似乎很熟悉。 让我们把函数的原始形式L()
放进去,看看J()
是什么样子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e7fgTIvX-1681705004267)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/bc0b7b74-497f-4bae-8f14-a5661a0c48af.png)]
该函数可能看起来有些混乱,因此请放慢速度并确保您了解它。
我们最终可以朝优化过程迈进。 大致而言,梯度下降正在尝试执行以下操作:
- 给我们一个成本函数尽可能最小的点(此点称为最小值)。
- 给我们正确的权重和偏差值,以便成本函数达到该点。
为了可视化,让我们采用一个简单的凸函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GrNN6XyG-1681705004267)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/b648cee7-9c85-4912-a310-764c72904530.png)]
现在,假设我们从一个随机点开始旅程,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p2SRbIJk-1681705004267)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/b7d2ca94-6643-41d4-ac7d-aa8bdb593cff.png)]
因此,右上角的点就是我们开始的点。 该点(由虚线箭头指示)是我们希望到达的点。 那么,如何通过简单的计算做到这一点呢?
为了达到这一点,使用了以下更新规则:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r3AX6knn-1681705004267)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/932dc7a9-29ef-438c-ae22-d0397b9fab67.png)]
Here, we are taking the partial derivative of J(w,b) with respect to the weights. We are taking a partial derivative because J(w,b) containsb
as one of the parameters. 𝝰 is the learning rate that speeds up this process. This update rule is applied multiple times to find the right values of the weights. But what about the bias values? The rule remains exactly the same only the equation is changed:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WW17bHLN-1681705004267)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/0505bb51-e6be-440d-86e5-3774b8532cbe.png)]
这些权重和偏差的新分配基本上称为反向传播,并且它是与梯度下降结合完成的。 在计算了权重和偏差的新值之后,重复整个前向传播过程,直到 NN 模型得到很好的概括。 请注意,这些规则仅适用于一个实例,前提是该实例仅具有一项特征。 在包含多个特征的多个实例上执行此操作可能很困难,因此,我们将跳过该部分,但是,有兴趣看到此功能的完整版本的人员可以参考 Andrew Ng 的在线讲座。
我们已经介绍了标准神经网络的必要基本单元,这并不容易。 我们先定义神经元,然后以反向传播(反向传播的讨厌术语)结束。 我们已经奠定了深度神经网络的基础。 读者可能想知道这是否是我们刚刚研究的深度神经网络。 正如 Andriy Burkov 所说(摘自他的书《百页机器学习书》):
深度学习是指训练具有两个以上非输出层的神经网络。 ……术语“深度学习”是指使用现代算法和数学工具包独立于神经网络的深度来训练神经网络。 实际上,使用在输入和输出层之间具有 2-3 层的神经网络可以解决许多业务问题。
在下一部分中,我们将学习深度学习和浅层学习之间的区别。 我们还将研究两种不同类型的神经网络,即卷积神经网络和循环神经网络。
不同类型的神经网络
到目前为止,我们已经了解了前馈神经网络的外观,以及如何将反向传播和梯度下降等技术应用于其以优化其训练过程。 我们之前研究的二分类问题似乎过于幼稚且不切实际,不是吗?
嗯,简单的 NN 模型可以解决很多问题。 但是随着问题复杂性的增加,有必要对基本的 NN 模型进行改进。 这些复杂的问题包括对象检测,对象分类,图像标题生成,情感分析,假新闻分类,序列生成,语音翻译等。 对于此类问题,基本的 NN 模型是不够的。 它需要对架构进行一些改进,才能解决这些问题。 在本节中,我们将研究两个最强大且使用最广泛的 NN 模型:卷积神经网络和循环神经网络。 这些当今的神经网络模型是深度学习惊人应用的核心。
卷积神经网络
您是否曾经将朋友群组的照片上传到 Facebook? 如果是,您是否想知道 Facebook 如何在上传完成后自动检测照片中的所有面孔? 简而言之,答案是卷积神经网络(CNN)。
前馈网络通常由几个全连接层组成,而 CNN 由几个卷积层以及其他类型的复杂层(包括全连接层)组成。 这些全连接层通常位于最末端,通常用于进行预测。 但是什么样的预测呢? 在图像处理和计算机视觉环境中,预测任务可以包含许多用例,例如标识提供给网络的图像中存在的对象类型。 但是 CNN 仅适合与图像相关的任务吗? CNN 是为图像处理任务(例如对象检测,对象分类等)而设计和提出的,但它也已在许多文本处理任务中使用。 我们将在图像处理环境中学习 CNN,因为 CNN 可以在图像处理和计算机视觉领域中发挥作用,因此它是最流行的奇迹。 但是,在继续讨论该主题之前,了解如何用数字表示图像将很有用。
图像包含许多像素和尺寸-高 x 宽 x 深
。 对于彩色图像,深度尺寸通常为 3,对于灰度图像,深度尺寸通常为 1。让我们对其进行更深入的研究。 考虑下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vxb7IJkI-1681705004268)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/79d278c8-9d3b-409e-ae79-de957bb6251c.png)]
前一幅图像的尺寸为626 x 675 x 3
,从数值上讲,它不过是一个矩阵。 每个像素代表红色,绿色和蓝色的特定强度(根据 RGB 颜色系统)。 该图像总共包含 422,550 像素(675 x 626
)。
像素由红色,绿色和蓝色三个值的列表表示。 现在,让我们来看一下像素(对应于 422,550 像素矩阵中的第二十行和第 100 列)的编码形式:
12, 24, 10
每个值对应于红色,绿色和蓝色的特定强度。 为了理解 CNN,我们将以较小的灰度图像进行查看。 请记住,灰度图像中的每个像素在 0 到 255 之间,其中 0 对应于黑色,255 对应于白色。
以下是代表灰度图像的像素虚拟矩阵(我们将其称为图像矩阵):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n5zZgZxf-1681705004268)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/a24eda9c-4190-41e1-bc67-d4d3a775f1d9.png)]
在继续之前,让我们直观地考虑如何训练 CNN 来学习图像的底层表示并使其执行某些任务。 图像具有其固有的特殊属性:图像中包含相似类型信息的像素通常保持彼此靠近。 考虑一张标准人脸的图像:表示头发的像素更暗,并且紧靠图像放置,而表示面部其他部分的像素通常更亮,并且彼此保持非常接近。 强度可能因人而异,但是您明白了。 我们可以使用图像中像素的这种空间关系,并训练 CNN 来检测相似像素以及它们在像素之间创建的边缘,以区分图像中存在的几个区域(在人脸图像中, 头发,眉毛等之间的任意边缘)。 让我们看看如何做到这一点。
CNN 通常包含以下组件:
- 卷积层
- 激活层
- 池化层
- 全连接层
CNN 的核心是一个称为卷积的操作(在计算机视觉和图像处理的文献中也称为交叉关系)。 PyImageSearch 的 Adrian Rosebrock 描述了以下操作:
在深度学习方面,(图像)卷积是两个矩阵的元素乘积,后跟一个和。
这句话告诉我们(图像)卷积运算符是如何工作的。 引用中提到的矩阵是图像矩阵本身和另一个称为核的矩阵。 原始图像矩阵可以高于核矩阵,并且在图像矩阵的左右方向上执行卷积运算。 这是卷积运算的示例,其中涉及前面的伪矩阵和大小为2 x 2
的核:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mVXYM5gN-1681705004268)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/81b66b3b-aaa5-45a5-82d4-e14bfc38ecc0.png)]
核矩阵实际上是网络的权重矩阵,为简单起见,我们暂时忽略偏差项。 还值得注意的是,我们最喜欢的图像过滤器(锐化,模糊等)仅是应用于原始图像的某些卷积输出。 CNN 实际上会学习这些过滤器(核)值,以便它可以最好地捕获图像的空间表示。 这些值可以使用梯度下降和反向传播进一步优化。 下图描述了应用于图像的四个卷积操作:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gE40BNVF-1681705004268)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/3f1deaa5-9ebe-413d-8869-8a3b87ddc294.png)]
注意核如何滑动以及如何计算卷积像素。 但是,如果我们这样进行,则会丢失图像的原始尺寸。 这可能会导致信息丢失。 为防止这种情况,我们应用了一种称为填充的技术,并保留了原始图像的尺寸。 有许多填充技术,例如复制填充,零填充,环绕等。 零填充在深度学习中非常流行。 现在,我们将看到如何将零填充应用于原始图像矩阵,从而保留图像的原始尺寸:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LdFe58xu-1681705004268)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/c102f556-b78a-45cb-b402-302b91561d97.png)]
零填充意味着像素值矩阵的所有面将被零填充,如上图所示。
指导网络如何滑动图像矩阵很重要。 这是通过称为跨步的参数来控制的。 跨步的选择取决于数据集,而跨步 2 的正确使用是深度学习的标准做法。 让我们看一下第 1 步与第 2 步的区别:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RjFczM4Y-1681705004269)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/fee0eddc-8450-4997-ab80-615b4b4155c1.png)]
卷积的图像通常如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3XET6EeH-1681705004269)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/6e10b613-f9a8-49eb-b1d6-d6e2072fcff4.png)]
卷积的映像在很大程度上取决于所使用的核。 最终的输出矩阵将传递给激活函数,并且该函数将应用于矩阵的元素。 CNN 中的另一个重要操作是池化,但我们暂时将其跳过。 到目前为止,您应该对 CNN 的工作原理有一个很好的了解,这足以使您继续阅读本书。 如果您想更深入地了解 CNN 的工作原理,请参阅这个页面上的博客文章。
循环神经网络
**循环神经网络(RNN)**是另一种神经网络,非常擅长 NLP 任务,例如情感分析,序列预测,语音到文本翻译,语言到语言翻译, 等等。 考虑一个例子:打开 Google,然后开始搜索循环神经网络。 从您开始输入单词的那一刻起,Google 就会开始为您提供建议列表,其中最有可能是完整单词或以您当时键入的字母开头的最常用的短语。 这是序列预测的示例,其中任务是预测给定短语的下一个序列。
让我们再举一个例子:给您一堆英语句子,每个句子包含一个空白。 您的任务是用正确的单词适当地填补空白。 现在,为了做到这一点,您将需要大体上使用先前对英语的了解,并尽可能地利用上下文。 要使用这样的先前遇到的信息,请使用您的内存。 但是神经网络呢? 传统的神经网络无法执行此操作,因为它们没有任何内存。 这正是 RNN 出现的地方。
我们需要回答的问题是如何赋予神经网络记忆能力? 一个绝对幼稚的想法是执行以下操作:
- 将特定的序列输入神经元。
- 取得神经元的输出,并将其再次馈送到神经元。
事实证明,这个想法不是那么幼稚,实际上构成了 RNN 的基础。 RNN 的单个层实际上如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wqRS4i1b-1681705004269)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/598b05d6-11ee-4c08-9c52-9bebfd97f229.png)]
这个循环似乎有点神秘。 您可能已经在考虑循环的每次迭代中发生的情况:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EfNfPgyQ-1681705004269)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/4865606d-533c-4d58-a488-c425003e96e4.png)]
在上图中,展开了 RNN(左图)以显示三个简单的前馈网络。 但是这些展开的网络有什么作用? 现在让我们找出答案。
让我们考虑序列预测的任务。 为简单起见,我们将研究 RNN 如何学习预测下一个字母来完成一个单词。 例如,如果我们用一组字母{w, h, a, t}
训练网络,并将顺序提供字母w, h
和a
,网络应该能够预测字母应该是t
,以便产生有意义的单词what
。 就像我们之前看到的前馈网络一样,X
在 RNN 项中充当网络的输入向量,该向量也称为网络的词汇表。 在这种情况下,网络词汇为{w, h, a, t}
。
向网络依次提供字母w, h
和a
。 让我们尝试给这些字母指定索引:
w -> (t - 1)
h -> (t)
a -> (t + 1)
这些索引称为时间步长(图中的上标表示 RNN 的展开)。 循环层利用在先前时间步长给出的输入以及在当前时间步长上操作时的特征。 让我们一步一步地了解此循环层如何产生输出。
将字母输入网络
在我们看到循环层如何产生输出之前,重要的是要学习如何将字母集提供给网络。 单热编码使我们能够以非常有效的方式执行此操作:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SEP1fl2d-1681705004269)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/82595ac0-af4d-4777-8394-1939beb4c4b7.png)]
因此,在单热编码中,我们的字母输入向量/词汇量不过是四个4 x 1
矩阵,每个矩阵表示一个特定的字母。 单热编码是这些任务的标准做法。 该步骤实际上是数据预处理步骤。
初始化权重矩阵
当有神经网络时,就有权重。 这是真的吧? 但是在开始处理 RNN 的权重之前,让我们确切地了解它们的需要位置。
对于 RNN,存在两种不同的权重矩阵-一种用于输入神经元(请记住,我们仅通过神经元提供特征向量),另一种用于循环神经元。 RNN 中的特定状态使用以下两个公式生成:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NBcxetB4-1681705004270)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/0a36036b-7a4b-4307-b60b-5031283c9060.png)]
要了解每个项在第一个方程式中的含义,请参考下图(不用担心,我们将转到第二个方程式):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NwWOHBgw-1681705004270)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/8bbaa47e-7702-433d-aac9-870000b96aa9.png)]
RNN x[1]
的第一遍是字母w
。 我们将根据等式(1)
随机初始化两个权重矩阵。 假设初始化后的矩阵W[xh]
如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mLLyMqTj-1681705004270)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/94ec44a4-d673-4c34-854b-e1d35c371270.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c4BkVxdL-1681705004270)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/6ccce969-b83d-44cd-87d9-647322b6f44a.png)]矩阵为3 x 4
:
x = 3
,因为我们在循环层中有三个循环神经元h = 4
,因为我们的词汇量是 4
矩阵W[hh]
是1×1
矩阵。 让我们将其值为 0.35028053。 我们还在这里介绍偏置项b
,它也是1 x 1
矩阵 0.6161462。 在下一步中,我们将把这些值放在一起并确定h[t]
的值。 (稍后我们将处理第二个方程。)
将权重矩阵放在一起
首先确定W[xh]x[1]
。 x[1]
是一个4 x 1
的矩阵,表示我们先前定义的字母w
。 矩阵乘法的标准规则在这里适用:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p5tdakQf-1681705004270)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/234a5eeb-d4fd-4fb8-ad16-468a49bb1010.png)]
现在我们将计算W[hh]h[0] + b
项。 我们很快就会看到偏差项的重要性。 由于w
是我们要馈送到网络的第一个字母,因此它没有任何先前的状态,因此,我们将h[0]
看作是一个由零组成的3 x 1
矩阵:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S9uruXNO-1681705004270)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/7dcad348-20fc-4827-96c5-f4e62ffd10bb.png)]
请注意,如果不采用偏差项,我们将得到仅由零组成的矩阵。 现在,我们将根据公式(1)
将这两个矩阵相加。 加法的结果是一个3 x 1
的矩阵,并存储在h[t]
(在这种情况下为h[1]
)中:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AOF9pRhn-1681705004271)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/8d9339a0-4050-4c6f-8756-c05e881ccb3c.png)]
按照公式(1)
,我们要做的就是将激活函数应用于该矩阵。
应用激活函数和最终输出
对于 RNN,tanh
是激活函数的不错选择。 因此,在应用tanh
之后,矩阵如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IhBunQGo-1681705004271)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/fefb7cb2-a21e-4ac2-8aaa-84401ab2bf71.png)]
我们得到了h[t]
的结果。 ht
充当下一个时间步的h[t-1]
。 现在,我们将使用公式(2)
计算y[t]
的值。 我们将需要另一个随机初始化的权重矩阵W[hy]
(形状为4 x 3
):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mvcuaWOv-1681705004271)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/281bbcd0-d1ba-46a1-9998-7609b6319c5f.png)]
应用第二个方程式后,y[t]
的值变为4 x 1
矩阵:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jezPQqb5-1681705004271)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/0a969531-c833-44fd-8c6f-f034c65b267d.png)]
现在,为了预测w
之后的下一个字母是什么(请记住,我们所有的计算都以字母w
开始,但我们仍然保留了 RNN)以从给定的词汇表中得出合适的词,我们将 softmax 函数应用于y[t]
。 这将为词汇表中的每个字母输出一组概率:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-852yCHmM-1681705004271)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/20663bb4-96fe-4e90-bf5e-5ced2e6e9e10.png)]
如果有人对学习 softmax 函数的外观感到好奇,可以在这个页面上找到一篇非常有用的文章。
因此,RNN 告诉我们w
之后的下一个字母更有可能是a
。 至此,我们完成了 RNN 的初始遍历。 作为练习,您可以使用从此传递中获得的ht
值,然后将其(以及下一个字母h
)应用于 RNN 的下一传递,以了解发生了什么。
现在,让我们解决最重要的问题-什么是网络学习? 同样,权重和偏置! 您可能已经猜到了下一个句子。 使用反向传播进一步优化了这些权重。 现在,这种反向传播与我们之前看到的有些不同。 此版本的反向传播称为时间上的反向传播。 我们不会对此进行学习。 在结束本节之前,让我们总结一下在 RNN 前向传递过程中执行的步骤(在词汇表进行一次热编码之后):
- 随机初始化权重矩阵。
- 使用公式
(1)
计算h[t]
。 - 使用公式
(2)
计算y[t]
。 - 将 softmax 函数应用于
y[t]
,以获取词汇表中每个字母的概率。
很高兴知道,除了 CNN 和 RNN 之外,还有其他类型的神经网络,例如自编码器,生成对抗网络,胶囊网络等。 在前两节中,我们详细了解了两种最强大的神经网络类型。 但是,当我们谈论前沿的深度学习应用时,这些网络是否足以被使用? 还是我们需要在这些基础上进行更多增强? 事实证明,尽管这些架构表现良好,但是它们无法扩展,因此需要更复杂的架构。 在下一章中,我们将介绍其中一些专门的架构。
自“第 1 章”,“人工智能和机器学习基础知识揭秘”以来,我们已经涵盖了很多理论。 在接下来的几节中,我们将深入研究一些实际的例子。
探索 Jupyter 笔记本
在从事与深度学习相关的项目时,您必须处理大量的各种类型的变量和各种维度的数组。 另外,由于其中包含的数据非常庞大,并且几乎在每个步骤之后都会不断变化,因此我们需要一个工具来帮助我们观察每个步骤所产生的输出,以便我们可以继续进行操作。 Jupyter 笔记本就是这样一种工具。 Jupyter 笔记本以其简单性而闻名,它们对功能和平台的广泛支持目前是开发深度学习解决方案的标准工具。 考虑到一些顶级技术巨头提供了自己版本的工具,例如 Google Colaboratory 和 Microsoft Azure Notebooks,就可以理解其流行的原因。 此外,自 2016 年以来,流行的代码托管网站 GitHub 一直在提供 Jupyter 笔记本的本地渲染。
安装 Jupyter 笔记本
让我们从安装 Jupyter 笔记本开始。
使用pip
安装
如果您的系统上已经安装了 Python,则可以从pip
存储库安装 Jupyter 包,以快速开始使用 Jupyter 笔记本。
对于 Python 3,请使用以下命令:
python3 -m pip install --upgrade pip python3 -m pip install jupyter
对于 Python 2,请使用以下命令:
python -m pip install --upgrade pip python -m pip install jupyter
对于 Mac 用户,如果找不到pip
安装,则可以下载最新的 Python 版本,该版本随附捆绑了pip
。
使用 Anaconda 安装
虽然可以从pip
作为单个包安装 Jupyter,但强烈建议您安装 Python 的 Anaconda 发行版,该发行版会自动安装 Python,Jupyter 以及机器学习和数据科学所需的其他几个包。 Anaconda 使处理各种包版本和更新依赖包或依赖包变得非常容易。
首先,从这里下载适合您系统和要求的正确 Anaconda 发行版,然后按照网站上给出的相应安装步骤进行操作。
验证安装
要检查 Jupyter 是否已正确安装,请在命令提示符(Windows)或终端(Linux/Mac)中运行以下命令:
jupyter notebook
您将能够在终端上看到一些日志输出(此后,这是 Windows 上命令提示符和 Linux 或 Mac 上终端的默认项)。 之后,您的默认浏览器将打开,您将被带到浏览器上的链接,该链接类似于下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wqiyQTCz-1681705004272)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/580396c4-cea4-4e26-9fa3-b206c340510a.png)]
在“文件”选项卡下,提供了一个基本文件管理器,用户可以使用该文件管理器来创建,上载,重命名,删除和移动文件。
“运行”选项卡列出了所有当前正在运行的 Jupyter 笔记本,可以从显示的列表中将其关闭。
“群集”选项卡提供了所有可用的 IPython 群集的概述。 为了使用此功能,您需要为您的 Python 环境安装 IPython Parallel 扩展。
Jupyter 笔记本
默认情况下,Jupyter 笔记本由.ipynb
扩展名标识。 在 Jupyter 提供的文件管理器中单击一次此类笔记本的名称后,将显示类似以下的屏幕:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CBLJVJ4G-1681705004272)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/9585b379-18ef-43e1-9a0a-60b00b5e6efb.png)]
最顶部的部分称为标头,您可以在其中看到菜单栏,工具栏和笔记本的标题。 在标题的右侧,您可以看到笔记本在其中执行的环境,并且在运行任何任务时,环境语言名称旁边的白色圆圈变为灰色。
标题下方是笔记本的主体,它由垂直堆叠的单元组成。 笔记本电脑主体中的每个单元格都是代码块,降价单元格或原始单元格。 代码单元可以在其下方附加一个输出单元,用户无法手动对其进行编辑。 这将保留与之关联的代码单元产生的输出。
在 Jupyter 笔记本电脑中,键盘对于单元格的不同模式的行为有所不同。因此,这些笔记本电脑称为模式。 笔记本电脑电池可以在两种模式下运行:命令模式和 editx 模式。
当单元处于命令模式时,它具有灰色边框。 在这种模式下,单元格内容无法更改。 在此模式下,键盘的键被映射到多个快捷方式,这些快捷方式可用于修改单元格或整个笔记本。
在命令模式下,如果按键盘上的Enter
键,则单元格模式将变为编辑模式。 在此模式下,可以更改单元格的内容,并可以调用浏览器中常规文本框中可用的基本键盘快捷方式。
要退出编辑模式,用户可以使用Esc
键。 要运行特定的单元格,用户必须输入Shift + Enter
,每种情况下将执行以下操作之一:
- 对于输出单元,应显示渲染的输出。
- 对于原始单元格,输入的原始文本应可见。
- 对于代码单元,将执行该代码,并且如果它产生一些输出,则将创建连接到该代码单元的输出单元,并在此处显示输出。 如果单元格中的代码要求输入,则将出现一个输入字段,并且该单元格的代码执行将暂停,直到提供输入为止。
Jupyter 还允许使用其内置的文本编辑器来操作文本文件和 Python 脚本文件。 也可以从 Jupyter 环境中调用系统终端。
设置基于深度学习的云环境
在开始建立基于云的深度学习环境之前,我们可能想知道为什么会需要它,或者基于云的深度学习环境将如何使我们受益。 深度学习需要大量的数学计算。 在神经网络的每一层,都有一个数学矩阵与另一个或几个其他这样的矩阵相乘。 此外,每个数据点本身可以是向量,而不是单个实体。 现在,要训练几个重复,仅由于涉及的数学运算数量众多,这种深度学习模型将需要大量时间。
支持 GPU 的机器执行这些操作的效率会更高,因为 GPU 是专门为高速数学计算而制作的,但是支持 GPU 的机器价格昂贵,而且可能并非所有人都能负担得起。 此外,考虑到多个开发人员在工作环境中使用同一个软件,为团队中的所有开发人员购买支持 GPU 的计算机可能是一个非常昂贵的选择。 由于这些原因,具有 GPU 功能的云计算环境的想法具有很强的吸引力。
如今,公司越来越倾向于在其开发团队中使用支持 GPU 的云环境,这可以导致为所有开发人员创建一个通用环境,并促进高速计算。
设置 AWS EC2 GPU 深度学习环境
在本部分中,我们将学习如何在 AWS 上设置深度学习特定实例。 在开始使用 AWS 之前,您需要在 AWS 控制台上创建一个帐户。 为此,请执行以下步骤:
- 访问这里,您将看到一个登录/注册屏幕。
- 如果您还没有 AWS 账户,请单击
Create a new AWS account
,然后按照以下步骤创建一个 AWS 账户,这可能需要您输入借记卡/信用卡详细信息才能为您的账户计费。 - 登录到您的帐户后,在仪表板上,单击“所有服务”部分中的 EC2,如以下屏幕截图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w33bLfJm-1681705004272)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/a1e52502-90c0-477b-82fa-70b9ee8e3450.png)]
进入 AWS 控制台内的 EC2 管理页面后,您将需要完成以下各节中的步骤,以创建满足您的深度学习需求的实例。
步骤 1:创建启用了 EC2 GPU 的实例
首先,选择 Ubuntu 16.04 或 18.04 LTS AMI:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GmFgJ6CN-1681705004272)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/41e6aa63-bb63-4a50-bbc7-3ae7249e74f9.png)]
然后,选择启用 GPU 的实例配置。 g2.2xlarge
是入门深度学习环境的不错选择:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OSF3x6Kh-1681705004272)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/9b74f7f3-e953-46a0-a975-3d9b78497042.png)]
接下来,配置所需的实例设置或将其保留为默认设置,然后继续存储步骤。 在此,建议的卷大小为 30 GB。 然后,您可以继续使用默认选项启动实例。
为您的实例分配一个 EC2 密钥对,以便您可以从系统通过 SSH 访问实例的终端。 如果将密钥对命名为abc
,则名为abc.pem
的文件将自动下载到浏览器的默认下载位置。
步骤 2:使用 SSH 进入您的 EC2 实例
打开系统上的终端,然后使用cd
导航到abc.pem
文件存储的目录。
如果您不熟悉cd
命令,请考虑一种情况,其中您位于名为Folder1
的文件夹中,该文件夹包含以下内容:
Folder1 / - Folder2 - Folder3 - File1.jpg - File2.jpg
要访问名为Folder2
的文件夹内的任何文件,您必须将工作目录更改为该文件夹。 为此,可以使用cd
命令的以下示例:
cd Folder2
请注意,仅当您已经在Folder1
中时,此命令才起作用。可以通过在系统上的任何位置使用cd
命令类似的方式来访问此命令。
您可以使用以下命令来了解有关 Linux 系统上任何命令用法的更多信息:
man <command>
例如,您可以使用以下命令:
man cd
现在,通过输入以下命令,使用密钥文件设置 SSH 所需的权限:
$ chmod 400 abc.pem
现在,要通过 SSH 连接到您的实例,您将需要其公共 IP 或实例公共 DNS。 例如,如果公用 IP 为1.2.3.4
,则使用以下命令:
$ ssh -i abc.pem ubuntu@1.2.3.4
可以在 EC2 管理页面中 AWS 控制台上正在运行的实例列表下方的详细信息面板上找到 AWS 实例的公共 IP。
步骤 3:在 GPU 实例上安装 CUDA 驱动
首先,更新/安装 NVIDIA 图形驱动:
$ sudo add-apt-repository ppa:graphics-drivers/ppa -y $ sudo apt-get update $ sudo apt-get install -y nvidia-xxx nvidia-settings
在这里,xxx
可以替换为实例上安装的图形硬件版本,该版本可以在实例详细信息中找到。
接下来,下载 CUDA deb 文件(此代码为撰写本文时的最新版本,自 2019 年 1 月起):
$ wget https://developer.download.nvidia.com/compute/cuda/10.0/secure/Prod/local_installers/cuda-repo-ubuntu1804-10-0-local-10.0.130-410.48_1.0-1_amd64.deb
然后,继续执行以下命令:
$ sudo dpkg -i cuda-repo-ubuntu1804-10-0-local-10.0.130-410.48_1.0-1_amd64.deb $ sudo apt-key add /var/cuda-repo-<version>/7fa2af80.pub $ sudo apt-get update $ sudo apt-get install -y cuda nvidia-cuda-toolkit
要验证是否已成功安装所有内容,请运行以下命令:
$ nvidia-smi $ nvcc -version
如果两个命令的输出都没有任何警告或错误,则说明安装成功。
步骤 4:安装 Python 的 Anaconda 发行版
首先,下载 Anaconda 安装程序脚本:
$ wget https://repo.continuum.io/archive/Anaconda3-2018.12-Linux-x86_64.sh
接下来,将脚本设置为可执行文件:
$ chmod +x Anaconda*.sh
然后,运行安装脚本:
$ ./Anaconda3-2018.12-Linux-x86_64.sh
安装程序将询问几个选项。 要验证安装是否成功,请使用以下命令:
$ python3
Python3 REPL 带有一个标语,反映了您实例上安装的 Anaconda 发行版,并加载到终端中。
步骤 5:运行 Jupyter
使用以下命令在实例上启动 Jupyter 笔记本服务器:
$ jupyter notebook
终端上的输出将在打开时包含一个 URL,您可以使用该 URL 访问在 EC2 GPU 实例上运行的 Jupyter 笔记本。
在 Crestle 上进行深度学习
当您需要对系统进行更好的控制时(例如,当您希望第三方应用与您的深度学习模型一起使用时),可以使用自定义的深度学习环境,而在其他时候,您可能没有这种需求,并且您只会对以快速且协作的方式在云上执行深度学习感兴趣。 在这种情况下,支付 AWS g2.2xlarge
实例的成本将比仅为计算时间或所用 GPU 时间支付的费用高得多。
Crestle 是一项服务,以非常实惠的价格在线提供支持 GPU 的 Jupyter 笔记本电脑。 要开始使用 Crestle,请执行以下步骤:
- 登录到 www.crestle.com 。
- 单击“注册”,并填写显示的注册表单。
- 检查您的电子邮件以获取帐户确认链接。 激活您的帐户并登录。
- 您将被带到仪表板,在该仪表板中将找到一个读取“启动 Jupyter”的按钮。 您可以选择使用 GPU 或使其保持禁用状态。 单击“启动 Jupyter”按钮,启用 GPU 选项。
您将看到在云上运行并具有 GPU 支持的 Jupyter 环境。 虽然价格会随着时间的流逝而变化,但它是截至 2020 年 1 月互联网上最实惠的解决方案之一。
其他深度学习环境
除了上述在云上执行启用 GPU 的深度学习的方式之外,您还可以在某些情况下选择使用其他平台。
Google 合作实验室是一项免费的 Jupyter 笔记本服务,可通过这里访问。 协作型笔记本存储在用户的 Google 云端硬盘上,因此存储限制为 15 GB。 可以在 Google 云端硬盘上存储大型数据集,并借助 Google 云端硬盘 Python API 将其包含在项目中。 默认情况下,GPU 在 Colaboratory 上处于禁用状态,必须手动打开。
Kaggle 是又一个专门用于进行数据科学竞赛的平台。 它提供了一个类似于 Jupyter 笔记本的环境,称为内核。 每个内核都提供了大量的 RAM 和免费的 GPU 功能,但是,Kaggle 的存储限制比 Google Colaboratory 上的存储限制更为严格,因此,当计算密集但要使用的数据量较大时,这是一个有效的选择。 输出不是很大。
探索 NumPy 和 Pandas
NumPy 和 pandas 是几乎所有可用 Python 语言提供的与数据科学相关的库的骨干。 虽然 Pandas 构建在 NumPy 之上,但 NumPy 本身就是 Python 围绕高性能 C 代码的包装,以促进 Python 中纯数学形式所能提供的卓越数学计算。
几乎所有以某种方式用 Python 开发的深度学习软件都依赖 NumPy 和 pandas。 因此,重要的是要充分了解两个库及其可以提供的功能。
NumPy
NumPy 是数值 Python 的首字母缩写。 原始 Python 缺少数组的实现,数组是用于开发机器学习模型的数学矩阵的紧密相似形式。 NumPy 为 Python 提供了对多维数组和高性能计算功能的支持。 可以使用以下import
语句将其包含在任何 Python 代码中:
import numpy as np
np
是导入 NumPy 的常用约定。
NumPy 数组
在 NumPy 中有几种创建数组的方法。 以下是一些值得注意的:
np.array
:要将 Python 列表转换为 NumPy 数组:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m5ZUMVsU-1681705004273)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/bea02ad3-cb06-4762-8ddd-1ecd8acd8a7a.png)]
np.ones
或np.zeros
:要创建全 1 或全 0 的 NumPy 数组,请执行以下操作:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28GHl65S-1681705004273)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/be91b2e2-dce0-40eb-ae79-746dd9ddb97c.png)]
np.random.rand
:要生成一个随机数数组:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nnGd4uvt-1681705004273)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/e5956406-688a-4c1b-986c-d73d3ef30bff.png)]
np.eye
:要生成给定方阵尺寸的单位矩阵:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8LykfyI-1681705004273)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/3468a284-89f9-41d5-ba9e-8cadbfd6ca35.png)]
现在让我们看一下基本的 NumPy 数组操作。
NumPy 数组的基本操作
NumPy 数组是数学矩阵的 Python 类似物,因此它们支持所有基本类型的算术运算,例如加法,减法,除法和乘法。
让我们声明两个 NumPy 数组,并将它们存储为array1
和array2
:
array1 = np.array([[10,20,30], [40, 50, 60], [70, 80, 90]]) array2 = np.array([[90, 80, 70], [60, 50, 40], [30, 20, 10]])
现在让我们看一下这些数组上每个算术运算的一些示例:
- 加法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y7XriruM-1681705004273)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/170428ce-55de-4a81-bf59-4ea0b5e9af4a.png)]
- 减法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8XGBwRPV-1681705004274)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/ad215dbc-939b-4eff-a0be-e217c9cc2bf5.png)]
- 乘法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UclLzV0f-1681705004274)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/780c5000-1e48-466d-8e60-de7561b8d815.png)]
- 除法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q5F9vMXT-1681705004274)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/d1097939-d2ab-4455-a459-056d54e7e468.png)]
现在让我们将 NumPy 数组与 Python 列表进行比较。
NumPy 数组与 Python 列表
现在让我们看看 NumPy 数组如何提供优于 Python 列表的优势。
多个行和列上的数组切片
虽然无法在 Python 中对列表列表进行切片,从而无法在列表列表中选择特定数量的行和列,但是 NumPy 数组切片根据以下语法工作:
Array [ rowStartIndex : rowEndIndex, columnStartIndex : columnEndIndex ]
这是一个例子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxtQdvpe-1681705004274)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/3f6ed47b-cf3a-42f9-9413-9e6a146e5577.png)]
在前面的示例中,我们能够在 NumPy 数组a
中选择两行以及这些行的所有元素。
切片赋值
虽然无法将值分配给 Python 列表切片,但 NumPy 允许将值分配给 NumPy 数组。 例如,要将 4 分配给 NumPy 一维数组的第三到第五个元素,我们可以使用以下代码:
arr[2:5] = 4
接下来,我们将看 Pandas。
Pandas
pandas 建立在 NumPy 之上,是使用 Python 进行数据科学使用最广泛的库之一。 它有助于实现高性能的数据结构和数据分析方法。 Pandas 提供了一个称为DataFrame
的内存中二维表对象,该对象又由称为数组的一维,类似数组的结构组成。
Pandas 中的每个DataFrame
都采用类似电子表格的表格的形式,带有行标签和列标题。 可以执行基于行或基于列的操作,或同时执行这两个操作。 Pandas 与 matplotlib 紧密集成,可提供几种直观的数据可视化效果,在进行演示或探索性数据分析过程中通常非常有用。
要将 Pandas 导入到 Python 项目中,请使用以下代码行:
import pandas as pd
在这里,pd
是导入 Pandas 的通用名称。
Pandas 提供以下数据结构:
Series
:一维数组或向量,类似于表中的列DataFrames
:二维表,带有表标题和行标签Panel
:DataFrames
的字典,很像一个 MySQL 数据库,其中包含多个表
可以使用pd.Series( )
方法创建一个 Pandas 序列,而可以使用pd.DataFrame( )
方法创建一个DataFrame
-例如,在下面的代码中,我们使用多个序列对象创建一个 PandasDataFrame
对象:
import pandas as pd employees = pd.DataFrame({ "weight": pd.Series([60, 80, 100],index=["Ram", "Sam", "Max"]),"dob": pd.Series([1990, 1970, 1991], index=["Ram", "Max", "Sam"], name="year"),"hobby": pd.Series(["Reading", "Singing"], index=["Ram", "Max"])}) employees
前面代码的输出如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jFQfQB09-1681705004274)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/0eac47bd-7093-4c7e-a082-545d3e511900.png)]
PandasDataFrame
可用的一些最重要的方法如下:
head(n)
或tail(n)
:要在数据帧的n
行中显示顶部或底部。info( )
:显示有关DataFrame
的所有列,维度和数据类型的信息。describe( )
:显示有关DataFrame
中每个列的便捷汇总和统计信息。 非数字的列将被省略。
总结
在本章中,我们介绍了许多不同的内容。 我们从学习神经网络的基础开始,然后逐步进行。 我们了解了当今使用的两种最强大的神经网络类型-CNN 和 RNN-并且还从较高的层次上了解了它们,但没有跳过它们的基本单元。 我们了解到,随着神经网络复杂性的增加,它需要大量的计算能力,而哪些标准计算机可能无法满足我们的需求,我们看到了如何通过使用两个不同的供应商(AWS 和 。 我们探索了 Jupyter 笔记本,这是用于执行深度学习任务的强大工具。 我们了解了两个非常流行的 Python 库(NumPy 和 pandas)的用法。 当执行深度学习任务时,这两个库都被广泛使用。
在下一章中,我们将构建应用并集成深度学习以使其智能执行。 但是在执行此操作之前,对我们来说重要的是要了解本章介绍的基础知识。 我们现在可以进入下一章了。
三、创建您的第一个深度学习 Web 应用
在对神经网络及其在实际项目中使用的设置有了了解之后,自然而然的下一步就是开发基于 Web 的深度学习应用。 本章致力于创建一个完整的 Web 应用,尽管它是一个非常简单的应用,但它以一种非常简单的方式演示了如何完成应用中深度学习的集成。
本章将介绍将在本书中使用的几个术语,因此,即使对于已经对深度学习 Web 应用有基本了解的您也可以阅读,这是一个推荐读物,以便您能够理解以后各章中使用的术语。 。 我们将从构建深度学习 Web 应用开始,并学习如何理解数据集。 然后,我们将使用 Python 实现一个简单的神经网络,并创建一个 Flask API 以与服务器端 Python 一起使用。
本章将讨论以下主题:
- 构建深度学习 Web 应用
- 了解数据集
- 使用 Python 实现简单的神经网络
- 创建与服务器端 Python 一起使用的 Flask API
- 在 Flask 中使用 cURL 和 Web 客户端
- 改善深度学习后端
技术要求
您可以通过这里访问本章中使用的代码。
对于本章,您将需要以下内容:
- Python 3.6+
- Flask 1.1.0+
- TensorFlow 2.0+
构建深度学习 Web 应用
解决拼图游戏时,重要的是要使零件合适,而不是将它们强迫在一起。 同样,在开发软件解决方案时,解决方案的各个部分必须无缝地协同工作,并且它们之间的交互必须易于理解。 好的软件需要适当的软件计划。 因此,为软件的长期使用和将来的维护提供坚实的软件结构至关重要。
在开始创建第一个可在 Web 上使用的深度学习应用之前,我们必须画出解决方案的蓝图,同时牢记我们希望解决的问题以及针对这些问题的解决方案。 这很像我们在网站开发过程中计划认证系统或将表单值从一页传递到另一页的方式。
通用的深度学习 Web 解决方案将需要以下组件:
- 可以存储数据并响应查询的服务器
- 一个可以使用存储的数据并对其进行处理以生成基于深度学习的查询响应的系统
- 客户端可以将数据发送到服务器进行存储,使用新数据发送查询,最后在查询深度学习系统后接受并使用服务器发送的响应
让我们尝试使用图表来可视化此结构。
通用深度学习 Web 应用的结构图
下图描述了 Web 客户端,Web 服务器和深度学习模型之间的交互:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ghmwfHFM-1681705004275)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/f8763b49-1e75-456b-85d4-6b65ae1d8286.png)]
我们将创建三个软件部分-客户端,服务器和深度学习模型-它们将一起工作。 为此,客户端将向服务器发出 HTTP 请求,作为回报,服务器将产生从经过单独训练的深度学习模型中获取的输出。 该模型可能会或可能不会在服务器上响应客户端发出的 HTTP 请求的文件中执行。 在大多数情况下,深度学习模型与处理 HTTP 请求的文件分开。
在本章介绍的示例中,我们将在单独的文件中提供服务器,客户端和深度学习模型。 我们的客户端将向服务器发送简单的 HTTP 请求,例如页面加载请求或 URL 的GET
请求,这些请求将基于传递的查询从深度学习模型中产生输出。 但是,客户端通过 REST API 与服务器通信是非常常见的做法。
现在,让我们继续了解应用将要处理的数据集。
了解数据集
至关重要的是,我们必须正确理解我们正在处理的数据集,以便以最有效的代码在执行时间和空间方面获得最佳结果。 当使用带有图像的神经网络时,我们将在此处使用的数据集可能是最受欢迎的数据集-手写数字的 MNIST 数据库。
MNIST 手写数字数据集
该数据集由 Yann LeCun,Corinna Cortes 和 Christopher J.C. Burges 组成的团队创建。 它是手写数字图像的大集合,其中包含 60,000 个训练样本和 10,000 个测试样本。 该数据集可从这里公开下载,其中以四个.gz
压缩文件的形式存在。
四个文件如下:
train-images-idx3-ubyte.gz
:训练集图像。 这些图像将用于训练神经网络分类器。train-labels-idx1-ubyte.gz
:训练集标签。 训练集中的每个图像都将具有与其关联的标签,该标签是该图像中可见的相应数字。t10k-images-idx3-ubyte.gz
:测试仪图像。 我们将使用这些图像来测试我们的神经网络预测准确率。t10k-labels-idx1-ubyte.gz
:测试集中图像的标签。 当我们的神经网络对测试集进行预测时,我们会将其与这些值进行比较以检查结果。
此数据集中存储的图像由于其自定义格式而无法直接用于查看。 期望数据集上的开发人员为图像创建自己的简单查看器。 完成此操作后,您将能够看到图像,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-56MaHHLA-1681705004275)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/b1fb5c7a-789d-4a36-88df-0aab844dd80a.png)]
让我们更深入地讨论图像。 如您所见,它们在两个轴上都超过 25 个像素标记。 确切地说,图像都是28 x 28
像素的形式。 现在,由于图像是灰度图像,因此可以将它们存储在28 x 28
的单层矩阵中。 因此,我们共有 784 个值,范围从 0 到 1,其中 0 表示一个完全暗的像素,而 1 表示一个白色像素。 该范围内的任何东西都是黑色阴影。 在 MNIST 数据集中,这些图像以 784 个浮点数的展平数组形式出现。 为了查看这些图像,您需要将一维数组转换为28 x 28
形状的二维数组,然后使用任何自行开发或公共可用的工具(例如 Matplotlib 或 Pillow 库)绘制图像。
让我们在接下来的部分中讨论这种方法。
探索数据集
让我们从 MNIST 数据集网页上下载所有四个文件开始,这些文件可从这里获得。 下载后,解压缩所有文件,您应该拥有与以下列表中的名称相似的文件夹:
train-images.idx3-ubyte
train-labels.idx1-ubyte
t10k-images.idx3-ubyte
t10k-labels.idx1-ubyte
将这些文件保存在您的工作目录中。 现在,我们将创建一个 Jupyter 笔记本,对提取的数据集文件执行探索性数据分析(EDA)。
在浏览器中打开 Jupyter 笔记本环境,然后创建一个新的 Python 笔记本。 让我们从导入必要的模块开始:
import numpy as np import matplotlib.pyplot as plt
前几行将numpy
模块和matplotlib.pyplot
导入到项目中。 numpy
模块提供了 Python 中的高性能数学函数,而matplotlib.pyplot
模块提供了用于绘制和可视化图形和图像的简单界面。 为了在 Jupyter 笔记本中查看此库的所有输出,请添加以下代码行:
%matplotlib inline
如果您使用的是 Windows,则要提取.gz
文件,可以使用 7-zip 软件,它是一款出色的压缩/解压缩工具,可以从这里免费下载。
创建读取图像文件的函数
如前所述,无法直接查看下载的图像文件中的图像。 因此,我们现在将在 Python 中创建一个函数,matplotlib
模块将可以使用该函数来显示文件中的图像:
def loadImageFile(fileimage): f = open(fileimage, "rb") f.read(16) pixels = 28*28 images_arr = [] while True: try: img = [] for j in range(pixels): pix = ord(f.read(1)) img.append(pix / 255) images_arr.append(img) except: break f.close() image_sets = np.array(images_arr) return image_sets
前面的loadImageFile
函数采用单个参数,该参数是包含图像的文件的名称。 在下载的文件文件夹中,我们有两个可用的此类文件:train-images-idx3-ubyte
和t10k-images-idx3-ubyte
。 先前函数的输出是numpy
图像数组。 我们可以将结果存储在 Python 变量中,如下所示:
test_images = loadImageFile("t10k-images-idx3-ubyte")
现在,要查看包含numpy
图像数组的变量中的图像,我们可以定义另一个函数,该函数采用单个图像的 784 个浮点数的像素数组并将其绘制为单个图像。 该函数可以如下所示定义:
def gen_image(arr): two_d = (np.reshape(arr, (28, 28)) * 255).astype(np.uint8) plt.imshow(two_d, interpolation='nearest', cmap='gray') plt.show() return
现在,假设我们要显示第一个测试图像; 因为我们已经将numpy
图像数组存储在test_images
变量中,所以我们可以运行以下代码:
gen_image(test_images[0])
我们可以看到以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OnDdfx0M-1681705004275)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/67d8fcf8-1084-4a0e-aa5e-39b2ee798280.png)]
现在我们已经可以查看图像了,我们可以继续构建一个函数,该函数将允许我们从标签中提取相应的数字。
创建读取标签文件的函数
MNIST 数据集中有两个可用的标签文件:train-labels-idx1-ubyte
和t10k-labels-idx1-ubyte
。 要查看这些文件,我们可以使用以下函数,该函数将文件名的输入作为参数并生成一个由单热编码的标签组成的数组:
def loadLabelFile(filelabel): f = open(filelabel, "rb") f.read(8) labels_arr = [] while True: row = [0 for x in range(10)] try: label = ord(f.read(1)) row[label] = 1 labels_arr.append(row) except: break f.close() label_sets = np.array(labels_arr) return label_sets
此函数以单热点编码返回numpy
标签数组,数据集中样本数量的维数乘以 10。为了观察一热点编码的本质,我们观察一个条目。 运行以下代码,该代码实际上从测试集中的第一个样本打印出单热编码的标签集:
test_labels = loadLabelFile("t10k-labels-idx1-ubyte") print(test_labels[0])
我们得到以下输出:
[0 0 0 0 0 0 0 1 0 0]
我们可以注意到这一点,因为在第七个索引处的数字是1
,所以测试数据集中第一张图像的标签是7
。
数据集摘要
在对可用数据集进行了非常简洁的探索之后,我们可以得出以下结果。
训练数据集包含 60,000 张图像,尺寸为60,000 x 784
,其中每张图像为28 x 28
像素。 样本在数字之间的分布如下:
序号 | 样本数 | 序号 | 样本数 |
0 | 5,923 | 5 | 5,421 |
1 | 6,742 | 6 | 5,918 |
2 | 5,958 | 7 | 6,265 |
3 | 6,131 | 8 | 5,851 |
4 | 5,842 | 9 | 5,949 |
观察到5
位数比1
位数少。 因此,未经良好训练的模型很可能在识别数字5
时出错。
标签数量的摘要告诉我们,所有 60,000 个样本都具有其相应的标签,并且没有一个标签缺失。
同样,在测试数据集上,我们有 10,000 个图像和标签,样本数量的分布如下:
序号 | 样本数 | 序号 | 样本数 |
0 | 980 | 5 | 892 |
1 | 1,135 | 6 | 958 |
2 | 1,032 | 7 | 1,028 |
3 | 1,010 | 8 | 974 |
4 | 982 | 9 | 1,009 |
测试数据集中的样本数量分布相当均匀。
使用 Python 实现简单的神经网络
在进行了非常基本的数据分析之后,我们可以继续使用 Python 编写我们的第一个神经网络。 在继续之前,您可以在“第 2 章”中复习神经网络的概念。 现在,我们将创建一个卷积神经网络(CNN),该网络将预测手写数字标签。
我们首先创建一个新的 Jupyter 笔记本。 您可以将其命名为Model.ipynb
以作为约定。 该笔记本将用于开发深度学习模型的转储版本,随后将其放入将生成预测的脚本中。
导入必要的模块
Model.ipynb
所需的模块如下导入:
import numpy as np import keras from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten, Activation from keras.layers import Conv2D, MaxPooling2D from keras import backend as K from keras.layers.normalization import BatchNormalization
需要keras
模块才能通过 TensorFlow 后端快速实现高性能神经网络。 我们已经在前面的章节中讨论过 Keras。 要安装 Keras,可以使用以下命令:
pip3 install keras
上面的命令将安装 Keras。
复用我们的函数来加载图像和标签文件
还记得我们在探索数据集期间创建的loadImageFile
和loadLabelFile
函数吗? 我们将再次需要它们,因此将这些相同的函数复制到此笔记本中。
它们一起为每个函数生成两个代码单元:
loadImageFile()
方法loadLabelFile()
方法
在新的代码单元中,我们创建loadImageFile()
函数:
def loadImageFile(fileimage): f = open(fileimage, "rb") f.read(16) pixels = 28*28 images_arr = [] while True: try: img = [] for j in range(pixels): pix = ord(f.read(1)) img.append(pix / 255) images_arr.append(img) except: break f.close() image_sets = np.array(images_arr) return image_sets
在另一个新的代码单元中,创建了loadLabelFile()
函数:
def loadLabelFile(filelabel): f = open(filelabel, "rb") f.read(8) labels_arr = [] while True: row = [0 for x in range(10)] try: label = ord(f.read(1)) row[label] = 1 labels_arr.append(row) except: break f.close() label_sets = np.array(labels_arr) return label_sets
然后,我们可以使用以下代码行以numpy
数组的形式导入图像和标签文件:
train_images = loadImageFile("train-images-idx3-ubyte") train_labels = loadLabelFile("train-labels-idx1-ubyte") test_images = loadImageFile("t10k-images-dx3-ubyte") test_labels = loadLabelFile("t10k-labels-idx1-ubyte")
这将创建train_images
,train_labels
,test_images
和test_labels
NumPy 数组。 我们可以观察它们的形状,并获得train_images
的以下输出:
(60000, 784)
接下来,我们将学习如何重塑数组以进行 Keras 处理。
为使用 Keras 而重塑数组
图像数组的当前形状不适合 Keras。 我们必须将图像数组分别转换为(60000, 28, 28, 1)
和(10000, 28, 28, 1)
的形状。
为此,我们使用以下代码行:
x_train = train_images.reshape(train_images.shape[0], 28, 28, 1) x_test = test_images.reshape(test_images.shape[0], 28, 28, 1)
现在,如果我们观察x_train
的形状,则会得到如下输出:
(60000, 28, 28, 1)
我们对标签数组没有任何更改,因此我们将它们直接分配给y_train
和y_test
:
y_train = train_labels y_test = test_labels
接下来,我们将使用 Keras 创建一个神经网络。
使用 Keras 创建神经网络
现在,我们准备继续创建神经网络:
- 我们将首先在 Keras 中创建
Sequential
神经网络模型:
model = Sequential()
- 要将神经元层添加到网络,我们使用以下代码:
model.add(Conv2D(32, (3, 3), input_shape=(28,28,1)))
这会将二维卷积神经元层添加到网络,其输入形状与图像的形状相同。
- 现在,让我们添加带有
relu
作为激活函数的激活层:
model.add(Activation('relu'))
- 添加激活层后,我们可以执行批量归一化。 在训练期间,数据会经过几个计算层,并且可能变得太大或太小。 这被称为协变量移位,批量归一化有助于将数据带回到中心区域。 这有助于神经网络更快地训练:
BatchNormalization(axis=-1)
- 现在让我们向模型添加更多隐藏层:
model.add(Conv2D(32, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2,2))) BatchNormalization(axis=-1) model.add(Conv2D(64,(3, 3))) model.add(Activation('relu')) BatchNormalization(axis=-1) model.add(Conv2D(64, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2,2))) model.add(Flatten()) BatchNormalization() model.add(Dense(512)) model.add(Activation('relu')) BatchNormalization() model.add(Dropout(0.2))
- 在神经网络的最后一层,我们需要以热编码的形式输出 10 个值,以表示已预测的数字。 为此,我们添加了
10
神经元的最后一层。 这将在0
到1
的连续范围内保存 10 个值:
model.add(Dense(10))
- 最后,要将这 10 个浮点值转换为单点编码,我们使用
softmax
激活:
model.add(Activation('softmax'))
现在让我们编译和训练 Keras 神经网络。
编译和训练 Keras 神经网络
现在,我们准备编译和训练神经网络。 要编译神经网络,我们使用以下代码:
model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adam(), metrics=['accuracy'])
在我们的模型中(我们在前面的代码块中进行了编译),我们将分类交叉熵设置为loss
函数; 所使用的优化程序函数是Adam
优化程序,评估指标是accuracy
。
然后,我们使用 Keras 模型对象的fit()
方法训练神经网络:
model.fit(x_train, y_train, batch_size=100, epochs=10, verbose=2, validation_split=0.2)
建议您将训练数据分解为进一步的验证和训练数据,同时保持测试集不变,但是对于此数据集而言,这是很好的。
训练完成了 10 个批量,批量大小为 100 个样本。
评估和存储模型
训练模型后,我们现在准备评估其准确率。 为此,我们将使用以下代码:
score = model.evaluate(x_test, y_test, verbose=1) print('Test loss:', score[0]) print('Test accuracy:', score[1])
对于前面的代码,我们将获得以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ISzvHDgZ-1681705004275)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/523c24c1-f2c0-4d35-88e5-10b86c746140.png)]
我们获得了 99% 的准确率,这是一个非常不错的准确率得分。 现在,我们可以保存模型,该模型将在将来用于通过 Web 门户对用户输入进行预测。 我们将模型分为两部分-模型结构和模型权重。 为了保存结构,我们将使用 JSON 格式,如下所示:
model_json = model.to_json() with open("model.json", "w") as json_file: json_file.write(model_json)
现在,为了保存 Keras 模型的权重,我们对对象使用save_weights()
方法:
model.save_weights('weights.h5')
接下来,我们将创建一个 Flask API 来与服务器端 Python 一起使用。
创建 Flask API 来与服务器端 Python 协作
我们已经完成了深度学习模型,并将其结构存储在model.json
文件中,并将模型的权重存储在weights.h5
文件中。 现在,我们准备将模型数据包装在 API 中,以便可以通过GET
或POST
方法将模型公开给基于 Web 的调用。 在这里,我们将讨论POST
方法。 让我们从服务器上所需的设置开始。
搭建环境
在服务器中,我们将需要 Flask 模块(将成为服务请求),而后者又将运行需要 Keras(因此需要 TensorFlow),NumPy 和许多其他模块的代码。 为了快速为我们的项目设置环境,我们遵循以下步骤:
- 安装 Anaconda。
- 安装 TensorFlow 和 Keras。
- 安装 Pillow。
- 安装 Flask。
您可以参考以下命令块来安装 TensorFlow,Keras,Pillow 和 Flask:
pip3 install tensorflow keras pillow flask
现在,我们准备开始开发我们的 API。
上传模型结构和权重
模型目录文件model.json
和权重文件weights.h5
必须存在于工作目录中。 您可以将文件复制到新文件夹(例如flask_api
),如果使用远程服务器,则可以将它们上传到正确的路径。
创建我们的第一个 Flask 服务器
在工作目录中创建一个新文件,并将其命名为flask_app.py
。 该文件将处理所有对服务器的请求。 将以下代码放入文件中:
from flask import Flask app = Flask(__name__) @app.route("/") def index(): return "Hello World!" if __name__ == "__main__": app.run(host='0.0.0.0', port=80)
前面的代码首先将必要的模块导入脚本。 然后,它将应用设置为 Flask 服务器对象,并定义了index
函数,该指令具有处理对"/"
地址发出的所有请求的指令,而与请求的类型无关。 在脚本末尾,使用 Flask 对象应用的run()
方法将脚本绑定到系统上的指定端口。
现在,我们可以部署此简单的 HelloWorld Flask 服务器。 我们在终端中运行以下命令:
python flask_app.py
现在,当我们在浏览器中打开http://localhost/
URL 时,将看到一个页面,其中显示Hello World
。 index
函数处理在服务器根目录处发出的请求,因为其路由设置为"/"
。 现在让我们将此示例扩展为创建一个可以处理专门用于预测的请求的 API。
导入必要的模块
在前面的示例中,我们将扩展flask import
语句以导入其他方法request
,这将使我们能够处理对服务器的POST
请求。 该行如下所示:
from flask import Flask, request
然后,我们导入读取和存储图像所需的模块。 同样,numpy
模块也按以下代码片段所示导入:
from scipy.misc import imread, imresize import numpy as np
最后,我们导入 Keras 模块的model_from_json()
方法以加载保存的模型文件。 然后,我们导入tensorflow
,因为 Keras 依赖于它执行:
from keras.models import model_from_json import tensorflow as tf
接下来,我们将数据加载到脚本运行时中。
将数据加载到脚本运行时并设置模型
导入必要的模块后,我们将加载保存的模型 JSON 和权重,如以下代码片段所示:
json_file = open('model.json','r') model_json = json_file.read() json_file.close() model = model_from_json(model_json) model.load_weights("weights.h5") model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy']) graph = tf.get_default_graph()
请注意,我们还为前面的会话创建了默认的graph
项目。 它是在模型训练期间隐式创建的,但未保存在保存的model
和weights
文件中,因此我们必须在此处显式创建它。
设置应用和主页函数
现在,我们将app
变量设置为 Flask 对象,并将index
路由设置为index
函数处理,该函数实际上不会产生任何有意义的输出。 这是因为我们将使用/predict
路由来提供我们的预测 API,如下所示:
app = Flask(__name__) @app.route('/') def index(): return "Oops, nothing here!"
我们将在下一节介绍转换图像函数。
转换图像的函数
如果用户使用适当的设置发出图像POST
请求,有时我们可能会以base64
编码字符串的形式获取图像。 我们可以创建一个函数来处理:
import re import base64 def stringToImage(img): imgstr = re.search(r'base64,(.*)', str(img)).group(1) with open('image.png', 'wb') as output: output.write(base64.b64decode(imgstr))
我们将re
模块用于正则表达式,以确定传递的数据是否为base64
字符串形式。 需要base64
模块来解码字符串,然后将文件另存为image.png
。
预测 API
现在,让我们定义/predict
路由,这将是我们的 API 通过以下方式响应预测的数字:
@app.route('/predict/', methods=['POST']) def predict(): global model, graph imgData = request.get_data() try: stringToImage(imgData) except: f = request.files['img'] f.save('image.png') x = imread('image.png', mode='L') x = imresize(x, (28, 28)) x = x.reshape(1, 28, 28, 1) with graph.as_default(): prediction = model.predict(x) response = np.argmax(prediction, axis=1) return str(response[0])
在这里,predict()
函数接受POST
方法输入,检查文件的传入格式,然后将其保存为名称为image.png
的磁盘。 然后,将图像读入程序并调整为28 x 28
尺寸。 接下来,对图像数组进行整形,以便可以将其放入 Keras 模型中进行预测。 然后,我们使用 Keras 模型的predict()
方法,并获得单热编码的输出,其预测数字的索引设置为1
,其余的保持为0
。 我们确定数字并将其发送到 API 的输出。
现在,我们必须在文件末尾添加代码,以将服务器绑定到端口并设置所需的配置:
if __name__ == "__main__": app.run(host='0.0.0.0', port=80) app.run(debug=True)
我们已经设置了debug=True
参数,以便能够在服务器的控制台中查看服务器上是否发生任何错误。 在开发过程中,这始终是一个好主意,但在生产中,可以跳过此行代码。
运行应用之前的最后一步是更新'/'
路由的代码。 每当有人调用此路由时,我们都会加载我们创建的index.html
项目,如下所示:
@app.route('/') def index(): return render_template("index.html")
现在,我们都准备启动服务器并检查它是否正常运行。 我们使用与之前用于启动服务器相同的命令:
python flask_app.py
前面的命令将启动服务器。
通过 cURL 使用 API 并使用 Flask 创建 Web 客户端
在服务器运行的情况下,我们可以向其发送POST
请求以及图像内容,并期望输出中有预测的数字。 无需任何第三方工具即可测试两种 API 的两种方法如下:
- 使用 cURL。
- 开发客户端来调用 API。
我们将介绍这两种方法。
通过 cURL 使用 API
在开发客户端以将POST
请求发送到 API 服务器之前,让我们通过 cURL 测试 API,cURL 是用于模拟对 URL 的GET
和POST
请求的命令行工具。
在终端或命令提示符中使用以下命令向您的预测 API 发出curl
请求:
curl -X POST -F img=@"path_to_file" http://localhost/predict/
此处,-F
标志用于指示POST
请求将包含文件。 用于保存文件的POST
变量的名称为img
,应将path_to_file
替换为您要发送到服务器的文件的完整路径,以获取要进行预测的图像 。
让我们来看一个示例的 API。
假设我们有以下图片,文件名为self2.png
,尺寸为275 x 275
:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VB2x1cpG-1681705004276)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/c1178ecc-74c4-4240-9fa9-5fa0ff8e6c67.png)]
显然,必须在服务器端调整图像尺寸。 要发出请求,我们使用以下命令:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hjfMNq91-1681705004276)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/a9e4c932-14ec-410e-81ec-255822daa48e.png)]
API 的输出是单个整数-2
。 因此,API 成功运行。
为 API 创建一个简单的 Web 客户端
现在,我们将创建一个准系统的 Web 客户端来调用 API。 为此,我们必须修改当前代码。 在flask_app.py
中,首先将 Flask 的import
语句更改为将其扩展到另一个模块render_template
,如下所示:
from flask import Flask, request, render_template
现在,我们在工作目录中创建一个文件夹templates
,并使用以下代码向其中添加一个文件index.html
:
<!DOCTYPE html> <html lang="en"> <head> <title>MNIST CNN</title> </head> <body> <h1>MNIST Handwritten Digits Prediction</h1> <form> <input type="file" name="img"></input> <input type="submit"></input> </form> <hr> <h3>Prediction: <span id="result"></span></h3> <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script> <script src="{{ url_for('static',filename='index.js') }}"></script> </body> </html>
本质上,我们在这里所做的就是创建一个表单,该表单具有单个文件类型的输入元素,称为img
。 然后,我们将 jQuery 添加到页面,并创建指向静态文件index.js
的链接,该文件在服务器的static
文件夹中提供。
让我们创建index.js
文件。 首先,在根目录中创建一个文件夹static
,然后使用以下代码创建一个新文件index.js
:
$("form").submit(function(evt){ evt.preventDefault(); var formData = new FormData($(this)[0]); $.ajax({ url: '/predict/', type: 'POST', data: formData, async: false, cache: false, contentType: false, enctype: 'multipart/form-data', processData: false, success: function (response) { $('#result').empty().append(response); } }); return false; });
前面的 jQuery 代码向/predict/
路由发出POST
请求,然后使用服务器返回的值更新页面上的result
除法。
让我们在此 Web 客户端上运行一个示例。 首先,我们需要重启 Flask 服务器。 然后,我们在浏览器中打开http://localhost/
以获取一个如下所示的网页:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E6Ts0cso-1681705004276)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/216dfe04-0135-4771-979f-4cd732b1d1e5.png)]
假设我们选择一个名为mnist7.png
的文件,该文件实质上是测试数据集的第一张图像,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BuLIg1fu-1681705004276)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/3ea2f8b5-6d27-47cc-9670-0bb3f2e5e4f9.png)]
预期输出为7
。 单击“提交”后,我们在页面上获得以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BpKomU60-1681705004277)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/0ac49e88-f2b8-4b42-b530-eaffc91dcb4a.png)]
我们可以观察到这是正确的输出,并得出 Web 客户端正常工作的结论。
改善深度学习后端
我们在这里训练的简单模型很难说是接近完美模型的模型。 我们可以使用几种方法来扩展此模型以使其更完善。 例如,我们可以采取的一些最基本的步骤来改善深度学习模型,如下所示:
- 增加训练周期:我们只训练了 10 个周期的模型,对于任何深度学习模型来说,这通常都是很小的值。 训练次数的增加可以提高模型的准确率。 但是,它也可能导致过拟合,因此必须尝试新的周期。
- 更多训练样本:我们的网络客户端目前所做的仅是显示预测值。 但是,我们可以扩展它以从用户那里获得关于我们所做的预测是否正确的反馈。 然后,我们可以将用户的输入图像添加到训练样本中,并使用用户提供的图像标签进行训练。 但是,我们必须谨慎对待垃圾邮件用户输入的图像和标签,并且仅向 Web 应用的受信任用户或 Beta 测试人员提供此功能。
- 创建更深的网络:我们可以增加网络中的隐藏层数,以使预测更加准确。 同样,此方法容易过拟合,必须仔细试验。
总结
本章详细介绍了如何创建深度学习模型,然后通过 Web 客户端或使用 cURL 通过 API 促进其使用。 本章首先讨论深度学习 Web 应用的结构,此类应用的各个组件以及它们之间的交互方式。 然后,对 MNIST 手写数字数据集进行了简短的讨论和探索。 这导致我们进入下一部分,在该部分我们建立了深度学习模型并将其存储在文件中以备将来使用。 然后将这些文件导入服务器 API 脚本,并在调用 API 时在其中执行。 最后,本章介绍了 API 的非常基本的客户端,还指导您如何通过命令行界面在 cURL 上使用 API。
在下一章中,我们将讨论如何使用 TensorFlow.js 在浏览器窗口中执行深度学习。
四、TensorFlow.js 入门
到目前为止,我们已经向深度学习的美好世界轻轻地介绍了自己,并且对于使当今的 Web 应用变得更加智能化,我们对深度学习所能提供的东西有相当的了解。 在“第 1 章”,“揭秘人工智能和机器学习基础”中,我们详细介绍了 AI 突破前后的 Web 应用。 在“第 3 章”,“创建您的第一个深度学习 Web 应用”中,我们使用简单的神经网络构建了自己的基于图像分类器的简单 Web 应用。
Web 应用无处不在,它们已成为我们日常生活不可分割的一部分。 在构建 Web 应用时,很难忽略 JavaScript 的使用。 那么,如果我们不使用 JavaScript 而不使用其他脚本语言来构建智能 Web 应用呢? 在本章中,我们将了解如何使用名为 TensorFlow.js(TF.js)的 JavaScript 库来构建支持深度学习的 Web 应用- 将在网络浏览器中完成所有这些操作。
在本章中,我们将介绍以下主题:
- TF.js 及其产品的基础
- 使用 TF.js 开发深度学习模型并进行推理
- 直接在浏览器中使用预训练的模型
- 构建一个 Web 应用来识别花的种类
- TF.js 的优缺点
技术要求
您可以通过这里访问本章中使用的代码。
要学习本章,您需要以下软件:
- TF.js 0.15.1+
- NPM 存储库中的
@tensorflow/tfjs-node
0.3.0+ 包
TF.js 的基础
在本节中,我们将简要回顾 TF.js 的一些基本概念。 我们将从介绍 TensorFlow 开始,然后我们将继续研究 TF.js 的不同组件。
什么是 TensorFlow?
在开始讨论 TF.js 之前,我们必须了解 TensorFlow 是什么。 TensorFlow 是由 Google 开发和维护的开源库。 它建立在称为张量的数据结构上。 张量是标量和向量的广义形式。 TensorFlow 为广泛的科学领域中的高性能数值计算提供了许多有效的工具。 TensorFlow 还提供了一套非常灵活的工具套件,用于执行机器学习以及深度学习开发和研究。 鼓励您访问 TensorFlow 的官方网站,以获取更多信息。
什么是 TF.js?
TF.js 是一个 JavaScript 库,它提供了构建和部署机器学习模型的生态系统。 它提供以下功能:
- 使用 JavaScript 开发机器学习模型
- 使用预训练的机器学习模型
- 部署机器学习模型
TF.js 为您提供了机器学习项目所需的所有元素。 它具有用于数据预处理,张量处理,模型构建,模型评估等的专用模块,而所有模块均使用 JavaScript。 在继续深入研究之前,让我们快速了解对 TF.js 的需求。
为什么是 TF.js?
正如我们在上一章中所看到的,简单地在线训练和托管模型,将其包装在 REST API 中,然后在任何前端上使用该 API 来显示我们的结果是非常容易和直观的。 那么,为什么会出现使用 TF.js 的需求?
一个简单的答案就是浏览器中是否有 AI! 考虑一个需要使用 AI 智能体的游戏,该 AI 智能体会从人类玩家的玩法中学到东西,从而随着游戏的发展变得越来越困难。 现在,如果游戏每隔一秒钟就不断向服务器发送请求,以将数据往返于游戏和服务器之间传输,那将是多余的。 此外,它很容易导致拒绝服务(DoS)攻击。
因此,当智能体必须保持实时学习时,拥有可以在浏览器中生存和学习的 AI 才有意义。 它也可以通过两种方式进行混合:
- 如果在呈现智能体期间加载了预训练的模型,并且从那里开始,它会间隔一段时间在服务器上开始学习和更新模型。
- 如果 AI 智能体的多个版本同时在多个系统上运行,并且它们从运行它们的系统上的交互中学习。 同样,如果他们的集体学习在服务器上被吸收,并且智能体每隔一段时间从服务器获取更新。
因此,使用 TF.js 可以大大减少对人类用户在每一步与服务器进行交互以与服务器进行交互的页面的依赖性。
我们现在可以构建一个显示 TF.js 功能的迷你项目。 暂时不必担心 TF.js 生态系统-我们将继续介绍项目的所有元素。
TF.js 的基本概念
以下是我们将在项目中使用的 TF.js 组件:
- 张量
- 变量
- 操作符
- 模型
- 层
让我们详细研究它们中的每一个。
张量
像 TensorFlow 一样,TF.js 中的中央数据处理单元是张量。 Goodfellow 等。 (在他们关于深度学习的书中)进行以下观察:
在一般情况下,排列在具有可变数量轴的规则网格上的数字数组称为张量。
简单描述,张量是一维或多维数组的容器。 以下是您可能已经知道的一些张量示例:
- 标量(零维张量)
- 向量(一维或一级张量)
- 矩阵(二维或二级张量)
我们可以在 TF.js 中针对给定形状创建张量,如下所示:
const shape = [2, 3]; // 2 rows, 3 columns const a = tf.tensor([4.0, 2.0, 5.0, 15.0, 19.0, 27.0], shape);
a
是已创建的张量,可以使用以下命令来打印其内容:
a.print()
打印以下输出:
Output: [[4 , 2 , 5 ], [15, 19, 27]]
a
是矩阵(第二张量)。 TF.js 还提供了专用功能,例如tf.scalar
,tf.tensor1d
,tf.tensor2d
,tf.tensor3d
和tf.tensor4d
,可以创建特定形状的张量,而不必显式指定shape
参数。 它还提供了更好的可读性。 张量在 TF.js 中是不可变的。
变量
与张量不同,变量在 TF.js 中是可变的。 变量在训练神经网络时特别有用,因为它们包含大量中间数据存储和更新。 以下是如何在 TF.js 中使用变量的示例:
const initialValues = tf.ones([5]); const weights = tf.variable(initialValues); // initialize weights weights.print(); // output: [1, 1, 1, 1, 1] const updatedValues = tf.tensor1d([0, 1, 0, 1, 0]); weights.assign(updatedValues); // update values of weights weights.print(); // output: [0, 1, 0, 1, 0]
现在让我们来看一下运算符。
运算符
运算符使您可以对数据执行数学运算。 TF.js 提供了各种操作来操纵张量。 由于张量本质上是不可变的,因此运算符不会更改张量中包含的数据,而是返回新的张量作为结果。 您可以对张量执行二进制运算,例如加法,乘法和减法。 您甚至可以链接多个操作。 以下示例显示了使用链接在 TF.js 中使用两个不同的运算符的方法:
const e = tf.tensor2d([[1.0, 2.0], [3.0, 4.0]]); const f = tf.tensor2d([[3.0, 4.0], [5.0, 6.0]]); const sq_sum = tf.square(tf.add(e, f)); sq_sum.print();
我们首先创建了两个二维张量,并将它们分配给e
和f
。 然后,我们添加了他们并取得了他们的方块。
这将产生以下输出:
// Output: [[16 , 36], // [64, 100]]
接下来,我们将介绍模型和层。
模型和层
在深度学习文献中,模型是指神经网络本身,特别是神经网络架构。 正如“第 2 章”,“使用 Python 进行深度学习的入门”中所讨论的那样,神经网络由基本组件组成,例如层之间的层,神经元和连接。 TF.js 提供了两个用于创建这些模型的函数-tf.model
和tf.sequential
。 tf.model
帮助您获得更复杂的架构,例如跳过某些层,而tf.sequential
提供了一种创建线性层堆叠而无需跳过,分支等的方法。
TF.js 为不同类型的任务提供了不同类型的专用层-tf.layers.dense
,tf.layers.dropout
,tf.layers.conv1d
,tf.layers.simpleRNN
,tf.layers.gru
和tf.layers.lstm
。 以下示例在tf.sequential
和tf.layers.dense
的帮助下演示了一个简单的神经网络模型:
const model = tf.sequential(); model.add(tf.layers.dense({units: 4, inputShape: [4], activation: 'relu'})); model.add(tf.layers.dense({units: 1, activation: sigmoid}));
前面的示例创建了一个具有以下内容的简单神经网络:
- 两层(请记住,在计算总层数时我们不考虑输入层)。 网络采用具有四个特征的输入(
inputShape
参数有助于指定该特征)。 - 第一层包含四个神经元(因此
units: 4
)。 第二层(输出层)只有一个神经元。 relu
激活函数用于第一层,sigmoid
激活函数用于输出层。
建议您转到这里了解有关 TF.js 前述组件的更多信息。
使用 TF.js 的案例研究
我们将遵循机器学习项目中通常涉及的所有步骤(我们在“第 1 章”,“人工智能和机器学习基础知识揭秘”中的讨论)。 一个好的项目始于定义明确的问题陈述。 因此,让我们快速查看一下并相应地决定后续步骤。
我们的 TF.js 小型项目的问题陈述
我们将在这里讨论的问题可能是您开始机器学习之旅时遇到的最著名的挑战之一-通过从鸢尾花数据集中学习其特征来分类和预测鸢尾花的类型。 训练以及预测将在浏览器本身中执行。
我们已经为项目定义了问题陈述。 接下来是数据准备步骤。 数据已经可供我们使用,因此我们不需要自己收集数据。 但是,在准备数据之前,最好对数据本身有更多了解。
鸢尾花数据集
由统计学家和生物学家罗纳德·费舍尔(Ronald Fisher)于 1936 年引入,鸢尾花数据集包含 150 行数据和大约 3 种不同的鸢尾花品种。 列如下:
- 萼片长度(厘米)
- 萼片宽度(厘米)
- 花瓣长度(厘米)
- 花瓣宽度(厘米)
- 品种:
- 山
- 杂色
- 弗吉尼亚
您可以在这个页面中获取原始数据集并了解更多信息。
您的第一个使用 TF.js 的深度学习 Web 应用
在本节中,我们将借助 TF.js 开发一个 Web 应用。 该应用将包括一个标准的,全栈,支持深度学习的 Web 项目的步骤。 我们将从准备数据开始,然后将简短地研究项目架构,然后,我们将逐步构建所需的组件。
准备数据集
鸢尾花数据集的原始形式是一个 CSV 文件,其中包含 150 行数据,以逗号分隔的格式分成 5 列,每个条目用新行分隔。
但是,我们将使用数据的 JSON 格式,以简化 JavaScript 的可操作性。 可以从这里下载 JSON 格式的数据集。
您可以使用任何语言的简单函数将 CSV 文件转换为 JSON 文件,并按照以下约定更改列名:
- 萼片长度:
sepal_length
- 萼片宽度:
sepal_width
- 花瓣长度:
petal_length
- 花瓣宽度:
petal_width
- 品种:
species
在开发用于模型构建的张量时,我们将在 JSON 中使用这些属性名称。
项目架构
我们将在该项目中使用 Node.js 创建服务器。 这样做是为了在通过 Node.js 后端使用时获得 TF.js 更快的计算性能的好处。 我们将创建一个非常基本的前端,该前端将能够发出命令以执行使用 TF.js 构建的神经网络的训练,而另一个按钮可以发出命令以基于以下内容预测鸢尾花的假设特征向量的类别,基于用户提供的输入。
下图显示了项目的组件及其交互:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wh92rrqQ-1681705004277)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/8e47dac3-cf66-406f-8222-dbf2905cd686.png)]
现在我们已经了解了架构,让我们从项目开始。
启动项目
要开始从事该项目,首先需要安装最新版本的 Node.js 和 Node 包管理器(NPM)。 虽然执行此操作的标准方法是阅读 Node.js 网站上提供的文档,但我们建议您使用 Node 版本管理器(NVM)安装 Node.js 和 NPM。
可以在这里b.com/creationix/nvm找到设置说明和文件。
一旦安装了 Node.js 和 NPM,我们就可以开始进行项目本身的工作了:
- 创建一个名为
tfjs-iris
的文件夹。 - 打开一个终端,并使用以下命令启动该项目的包管理器:
npm init -y
这将在项目目录中创建一个文件package.json
。 上一条命令的输出如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8FiyeC2U-1681705004277)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/d566a0e8-10d6-4418-9e82-a59447d8f27a.png)]
请注意,输出为 JSON 格式。 main
键定义了作为模块导入的文件,该文件将成为程序的入口点。 默认情况下,此项目中main
的值设置为index.js
。 但是,该文件尚未创建。 让我们来研究index.js
文件。
我们将使用 Node.js 的express
模块创建服务器。 您可以在这个页面上阅读有关express
的更多信息。
- 要使用
express
,我们需要将模块添加到我们的项目中。 为此,请使用以下代码:
npm install express --save
这会将express
模块依赖项添加到package.json
文件,并将其安装在项目工作目录内的node_modules
目录中。
- 在项目存储库的根目录中创建一个名为
index.js
的文件,并添加以下代码:
var express = require('express'); var app = express();
这将创建一个express
应用对象。 现在,我们将 TF.js 添加到项目中。 最简单的方法是通过 NPM 安装它。 完整的设置说明可在这个页面中找到。
- 使用以下命令在终端中安装
TF.js
模块:
npm install @tensorflow/tfjs --save
- 现在,我们可以将模块添加到
index.js
文件中:
const tf = require('@tensorflow/tfjs');
- 我们还将需要 Express.js 中的
body-parser
模块来处理来自客户端的传入查询数据,这些数据将通过 AJAXPOST
请求发送。 为此,我们使用以下命令:
npm install body-parser --save
- 现在,我们创建一个
body-parser
对象,并使用以下代码将其绑定到应用:
var bodyParser = require('body-parser'); app.use(bodyParser.urlencoded({ extended: false }));
在此阶段,package.json
应该包含以下片段,列出您项目的依赖项:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wc6v2mPX-1681705004277)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/64f57e1d-2a8d-481d-b2bc-27ed8c7d89fd.png)]
请注意,先前的版本可能会更改。 现在,我们可以导入iris.json
文件,我们将在以下文件上训练我们的模型:
const iris = require('./iris.json');
完成初始设置后,我们现在可以继续编写 TF.js 代码以对可用数据集进行训练。
创建一个 TF.js 模型
让我们开始将存储在iris
变量中的数据读取到tensor2d
对象中:
- 在您的
index.js
文件中,添加以下代码:
const trainingData = tf.tensor2d(iris.map(item=> [ item.sepal_length, item.sepal_width, item.petal_length, item.petal_width ]),[144,4])
我们还没有任何测试数据; 这将由用户提供。
- 接下来,我们为可能的三种花创建一个一次性编码:
const outputData = tf.tensor2d(iris.map(item => [ item.species === 'setosa' ? 1 : 0, item.species === 'virginica' ? 1 : 0, item.species === 'versicolor' ? 1 : 0 ]), [144,3])
现在,我们准备创建训练模型。 以下代码可能使您想起上一章为 MNIST 手写数字数据集创建模型时使用的代码。 这仅仅是因为我们仍然仅使用另一种语言使用 TensorFlow 的概念!
- 我们首先声明一个顺序的 TensorFlow 模型:
const model = tf.sequential();
- 接下来,让我们在模型中添加一层神经元:
model.add(tf.layers.dense({ inputShape: 4, activation: 'sigmoid', units: 10 }));
inputShape
参数指示将添加到此层的输入的形状。 units
参数设置该层要使用的神经元数量。 我们正在使用的activation
函数是sigmoid
函数。
- 现在添加输出层:
model.add(tf.layers.dense({ inputShape: 10, units: 3, activation: 'softmax' }));
在这里,我们在输出层中将有 3 个神经元,并且在这一层上期望的输入是 10,这与上一层中的神经元数量匹配。
除了输入层,我们只有一个隐藏层和输出层。 在该应用中这是可以接受的,因为数据集很小并且预测很简单。 请注意,我们在此处使用了softmax
激活函数,该函数产生类概率作为输出。
这在我们的案例中特别有用,因为该问题是多类分类问题。
- 完成此操作后,我们现在可以编译我们的模型了。 为此,我们使用以下代码:
model.compile({ loss: "categoricalCrossentropy", optimizer: tf.train.adam() });
由于我们手头有一个可能存在多个标签的分类问题,因此我们将categoricalCrossentropy
用作loss
函数。 为了进行优化,使用了adam
优化器。 建议您尝试其他超参数值。
- 我们可以使用以下代码生成模型的摘要:
model.summary();
接下来,我们将训练我们的 TF.js 模型。
训练 TF.js 模型
现在,我们将编写一个async
函数。 这样做的原因是,不会使调用我们函数的客户端 JavaScript 陷入等待结果的状态。 在我们的程序中,需要花费一些时间才能完成的函数是train_data()
函数。 此函数执行模型的训练:
async function train_data(){ console.log("Training Started"); for(let i=0;i<50;i++){ let res = await model.fit(trainingData, outputData, {epochs: 50}); console.log(`Iteration ${i}: ${res.history.loss[0]}`); } console.log("Training Complete"); }
train_data()
函数可以异步运行。 它还将训练的每个阶段的损失打印到我们将要从中运行服务器的控制台。 现在,让我们创建一个将调用train_data()
函数的 API。
首先,我们创建一个名为doTrain
的中间件,该中间件将在用于训练的 API 之前运行,并将返回任何数据。
您可以在这个页面上了解有关中间件的更多信息。
doTrain()
中间件在其参数中接受对 Node.js 服务器的请求,用于做出响应的变量以及将在执行完以下代码后转发程序执行的函数的名称。 中间件中定义的代码:
var doTrain = async function (req, res, next) { await train_data(); next(); }
doTrain
中间件调用train_data()
函数并等待其结果。 train_data()
函数返回Promise
,以便继续执行而不会冻结。 next()
函数在train_data()
函数完成后立即运行,并且仅将程序的执行传递给中间件旁边链接的函数,如下所示:
app.use(doTrain).post('/train', function(req, res) { res.send("1"); });
现在,我们将'/train'
路由绑定到express
应用,然后将doTrain
中间件链接到该应用。 现在,对于'/train'
API 的每次调用,中间件都首先运行,然后执行传递到该 API 的主要代码块。 此代码块仅返回任意值以表示训练已完成。
将 TF.js 模型用于预测
训练完成后,我们还需要创建一个 API 来调用预测函数并返回预测结果。 我们使用POST
方法将 API 绑定到'/predict'
路由,以对此 API 进行请求,如下所示:
app.post('/predict', function(req, res) { var test = tf.tensor2d([parseFloat(req.body.sepLen), parseFloat(req.body.sepWid), parseFloat(req.body.petLen), parseFloat(req.body.petWid)], [1,4]); var out = model.predict(test); var maxIndex = 0; for (let i=1;i<out.size; i++){ if (out.buffer().get(0, i) > out.buffer().get(0, maxIndex)){ maxIndex = i; } } ans = "Undetermined"; switch(maxIndex) { case 0: ans = "Setosa"; break; case 1: ans = "Virginica"; break; case 2: ans = "Versicolor"; break; } console.log(ans); res.send(ans); });
了解预测 API 的代码非常简单。 让我们分部分讨论它:
app.post('/predict', function(req, res) {
这行代码将'/predict'
路由绑定到POST
请求方法,并打开将处理该路由请求的语句的代码块:
var test = tf.tensor2d([parseFloat(req.body.sepLen), parseFloat(req.body.sepWid), parseFloat(req.body.petLen), parseFloat(req.body.petWid)], [1,4]); var output = model.predict(test);
这些行从数据中创建一个 TF.js tensor2d
对象,该对象是从客户端接收的。 然后,在模型上运行predict
方法,并将结果存储在输出变量中:
var maxIndex = 0; for (let i=1;i<out.size; i++){ if (out.buffer().get(0, i) > out.buffer().get(0, maxIndex)){ maxIndex = i; } }
该代码块仅找到与tensor2d
变量输出中最高元素对应的索引。 请记住,在softmax
激活输出中,最大值对应于预测的索引。
在确定了输出的最大索引之后,我们使用一个简单的switch-case
语句来确定要从 API 发送到客户端的输出。 请求数据还将记录到服务器上可见的控制台中。 最后,我们使用以下代码将 Node.js 应用绑定为监听端口3000
:
app.listen(3000);
现在,我们将在以下部分中创建一个简单的客户端。
创建一个简单的客户端
为了在我们的应用中处理'/'
路由,我们将以下代码行添加到index.js
中,该代码仅呈现静态文件index.html
,该文件位于公共文件夹中:
app.use(express.static('./public')).get('/', function (req, res) { res.sendFile('./index.html'); });
现在,让我们按照以下步骤创建静态index.html
文件:
- 首先,创建一个文件夹
public
,并在其中创建index.html.
将以下代码添加到index.html
文件中:
<html> <head> <title>TF.js Example - Iris Flower Classficiation</title> </head> <body> <h1> TF.js Example - Iris Flower Classification </h1> <hr> <p> First, train the model. Then, use the text boxes to try any dummy data. </p> <button id="train-btn">Train</button> <hr><br> <label for="sepLen">Sepal Length: </label> <input type="number" id="sepLen" value="1" /><br> <label for="sepWid">Sepal Width: </label> <input type="number" id="sepWid" value="1" /><br> <label for="petLen">Petal Length: </label> <input type="number" id="petLen" value="1" /><br> <label for="petWid">Petal Width: </label> <input type="number" id="petWid" value="1" /><br> <br> <button id="send-btn" disabled="="true">Predict!</button> <hr> <h3> Result </h3> <h4 id="res"></h4> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
- 在为开发用于调用我们使用 TF.js 创建的 API 的客户端设置一个简单的 UI 之后,我们准备定义从客户端部署它们的函数。 请注意,
"/train"
和"/predict"
API 都将由POST
请求调用:
<script> $('#train-btn').click(function(){ $('#train-btn').prop('disabled', true); $('#train-btn').empty().append("Training..."); $.ajax({ type: 'POST', url: "/train", success: function(result) { console.log(result); $('#send-btn').prop('disabled', false); $('#train-btn').empty().append("Trained!"); } }); }); $('#send-btn').click(function(){ var sepLen = $('#sepLen').val(); var sepWid = $('#sepWid').val(); var petLen = $('#petLen').val(); var petWid = $('#petWid').val(); $.ajax({ type: 'POST', url: "/predict", data: {sepLen: sepLen, sepWid: sepWid, petLen: petLen, petWid: petWid}, success: function(result) { console.log(result); $('#res').empty().append(result); } }); }); </script> </body> </html>
现在让我们运行 TF.js Web 应用。
运行 TF.js Web 应用
使用所有应用编码后,我们现在可以运行我们的应用。 首先,打开一个终端,并在其中包含package.json
文件的tfjs-iris
文件夹作为您的工作目录。
运行以下代码行以启动 Node.js 服务器:
node index.js
该命令产生的输出类似于以下屏幕截图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Myde2Mgc-1681705004277)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/36c2cc46-bbf1-471e-beb4-5bde0c30efe1.png)]
现在,连同此输出,服务器从端口3000
启动,我们可以在浏览器中查看该端口。 打开浏览器,在地址栏中键入http://localhost:3000/
,以显示以下输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UhEeAyGS-1681705004278)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/d9ca1b9a-3a54-4848-be67-d133f21e5e88.png)]
首先,您必须单击“训练”按钮以调用'/train'
API,该 API 开始训练,并且该按钮将变为禁用状态。 一旦预测! 按钮被启用,训练完成,用户可以将虚拟数据发送到服务器进行预测。 假设我们从数据集中选择了第 50 行数据,并将其发送到服务器,其预期输出为Setosa
。
以下屏幕截图显示了项目最终版本的一小部分:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hSzYjROn-1681705004278)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-py-dl-web/img/89c50dc0-72c8-4ccf-83db-1e588b47c268.png)]
我们看到为提供的输入生成了正确的输出。
TF.js 的优缺点
现在,让我们总结一下 TF.js 在 TensorFlow 上带来的一些优势,以及我们在本章中已经谈到的那些优势:
- 自动 GPU 支持:您无需与 TF.js 分别安装 CUDA 或 GPU 驱动,即可从系统上存在的 GPU 中受益。 这是因为浏览器本身实现了 GPU 支持。
- 集成:使用 Node.js 将 TF.js 集成到 Web 开发项目中,然后将预训练的模型导入该项目并在浏览器中运行,这相当简单。
但是,它也有几个缺点,在开发生产时必须牢记。 其中一些如下:
- 速度:TF.js 适用于小型数据集。 在大规模数据集上,计算速度受到严重影响,并且速度慢了近 10 倍。
- 缺少 TensorBoard:框架的 JavaScript 端口中缺少此强大的工具,它可以使 TensorFlow 模型可视化,因为 TF.js 只是一个 API。
- 对 API 的不完全支持:并非所有 TensorFlow API 在 TF.js 上都可用,因此在使用 TF.js 开发时,您可能必须重新考虑代码逻辑或创建自己的函数以使用某些功能。
总结
在本章中,我们了解了使用 TF.js 创建模型有多么容易。 您不仅可以使用整个 JavaScript 生态系统,还可以在 TF.js 中获得所有经过预训练的 TensorFlow 模型。 我们使用鸢尾花数据集开发了一个简单的 Web 应用,并且在此过程中,我们了解了 TF.js 必须提供的几个组件。 到目前为止,我们已经构建了两个简单的基于端到端深度学习的 Web 应用。
我们的进步确实是显而易见的。 在接下来的章节中,我们将构建自己的深度学习 API,并使用它们来创建智能 Web 应用。 但是在此之前,让我们在下一章中熟悉 API 的整个概念。