机器学习预测模型部署指南-全-
机器学习预测模型部署指南(全)
原文:Deploy Machine Learning Models to Production
协议:CC BY-NC-SA 4.0
一、机器学习导论
在第一章中,我们将讨论机器学习和深度学习的一些基础知识。我们还将关注正在通过使用机器学习进行转型的不同商业垂直领域。最后,在进入下一组关于生产化的章节之前,我们将回顾在云平台(Databricks)上训练和建立一个相当简单的机器学习模型和深度学习模型的传统步骤。如果您知道这些概念,并且对自己在机器学习方面的专业水平感到满意,我鼓励您跳过接下来的两个部分,继续阅读最后一部分,在最后一部分,我提到了开发环境,并给出了本书附带的代码库和数据下载信息的链接,以便您能够适当地设置环境。本章分为三节。第一部分介绍机器学习的基础知识。第二部分深入研究深度学习的基础知识和广泛使用的深度神经网络的细节。前面的每一节后面都有在云平台上构建模型的代码。最后一部分是关于本书剩余章节的需求和环境设置。
历史
机器学习/深度学习并不新鲜;事实上,这可以追溯到 20 世纪 40 年代,当时人们第一次试图建造某种具有一定内置智能的东西。第二次世界大战期间,伟大的艾伦·图灵致力于建造这台能够解密德国密码的独特机器。这是机器智能时代的开端,几年内,许多国家的研究人员开始详细探索这一领域。在当时,ML/DL 被认为在改变世界方面非常强大,大量的资金被给予以使其具有生命力。几乎每个人都非常乐观。到 20 世纪 60 年代末,人们已经在研究机器视觉学习,开发具有机器智能的机器人。
虽然表面上看起来一切都很好,但仍有一些严重的挑战阻碍着这一领域的进展。研究人员发现在机器中创造智能极其困难。主要是由于几个原因。其中之一是当年计算机的处理能力不足以处理和加工大量数据,原因是相关数据本身的可获得性。尽管有政府的支持和充足的资金,但从 20 世纪 60 年代末到 90 年代初,人工智能研究遇到了障碍。这一段时间在社区成员中也被称为“艾温特斯”。
20 世纪 90 年代末,企业再次对人工智能产生了兴趣。日本政府公布了开发第五代计算机以提升器器学习的计划。人工智能爱好者认为,很快计算机将能够像人一样进行对话、翻译语言、解释图片和推理。1997 年,IBM 的“深蓝”成为第一台击败国际象棋世界冠军加里·卡斯帕罗夫的计算机。21 世纪初网络泡沫破裂时,一些人工智能资金枯竭了。然而,机器学习继续前进,这在很大程度上要归功于计算机硬件的改进。
过去十年
不可否认的是,在过去十年左右的时间里,世界在机器学习和人工智能应用方面取得了重大进展。事实上,如果要与任何其他技术相比较,ML/AI 在许多方面都是开创性的。亚马逊、谷歌和脸书等企业在人工智能的这些进步中蓬勃发展,也对此负有部分责任。像这样的组织的研发部门正在推动极限,并在将人工智能带给每个人方面取得令人难以置信的进展。不仅是像这样的大公司,还有成千上万家专门从事人工智能产品和服务的初创公司。在我写这一章的时候,这个数字只会继续增长。如前所述,在过去十年左右的时间里,各种业务对 ML 和 AI 的采用呈指数增长,这种行为的主要原因是多方面的。
-
数据的增长
-
提高计算效率
-
改进的最大似然算法
-
数据科学家的可用性
数据的增长
这一趋势的第一个最突出的原因是过去几十年中数据生成的大幅增长。数据总是存在的,但是理解这些大量数据背后的确切原因是必要的。在早期,数据是由特定组织的雇员或工人生成的,因为他们会将数据保存到系统中,但是只有有限的数据点保存几个变量。随后出现了革命性的互联网,使用互联网的几乎每个人都可以访问通用信息。有了互联网,用户可以控制输入和生成自己的数据。这是一个巨大的转变,因为世界上互联网用户的总数以爆炸式的速度增长,这些用户创建的数据量以更高的速度增长。所有这些数据——捕获用户详细信息的登录/注册表单、上传到各种社交平台的照片和视频以及其他在线活动——导致了术语“大数据”的产生。因此,ML 和 AI 研究人员在早期由于缺乏数据点而面临的挑战被完全消除,这被证明是 ML 和 AI 采用的主要促成因素。
最后,从数据的角度来看,我们已经达到了下一个水平,因为机器正在生成和积累数据。我们周围的每一个设备都在捕捉数据,比如汽车、建筑、手机、手表和飞机引擎。它们嵌入了多个监控传感器,每秒钟都在记录数据。这种数据在数量上甚至高于用户生成的数据,通常称为物联网(IoT)数据。
提高计算效率
我们必须理解这样一个事实,ML 和 AI 最终只是处理一大堆放在一起的数字,并从中找出意义。为了应用人工智能或人工智能,对强大的处理系统有着强烈的需求,我们已经见证了计算能力以极快的速度显著提高。仅仅观察一下我们在最近十年左右所看到的变化,移动设备的尺寸已经急剧减小,速度也有了很大程度的提高。这不仅仅是指微处理器芯片中使用 GPU 和 TPU 进行更快处理的物理变化,还包括 Spark 等数据处理框架的出现。在过去的十年中,处理能力的进步和使用 Spark 的内存计算的结合使得许多 ML 算法能够成功运行。
改进的最大似然算法
在过去的几年中,在新的和升级的算法的可用性方面已经取得了巨大的进步,这些算法不仅提高了预测的准确性,而且解决了传统 ML 面临的多重挑战。在第一阶段,这是一个基于规则的系统,人们必须首先定义所有的规则,然后在这些规则集内设计系统。由于环境过于动态,控制和更新规则的数量变得越来越困难。因此,传统的 ML 取代了基于规则的系统。这种方法面临的挑战是,数据科学家必须花费大量时间来手工设计构建模型的功能(称为功能工程),并且在预测准确性方面有一个上限,无论输入数据大小是否增加,这些模型都不能超过该上限。第三阶段是引入深度神经网络,其中网络将自己找出最重要的特征,并且也优于其他 ML 算法。此外,在过去的几年中,其他一些方法也引起了很多关注,如下所示:
-
元学习
-
迁移学习(纳米网络)
-
胶囊网络
-
深度强化学习
-
生成对抗网络
数据科学家的可用性
ML/AI 是一个专业领域,因为能够做到这一点所需的技能确实是多个学科的结合。为了能够建立和应用最大似然模型,你需要对数学和统计学的基础知识有一个很好的了解。与此同时,对机器学习算法和各种优化技术的深入理解对于采取正确的方法来使用 ML 和 AI 解决商业问题至关重要。下一个重要的技能是非常擅长编码,最后一个是成为特定领域(金融、零售、汽车、医疗保健等)的专家。)或拥有多个领域的深厚知识。数据科学家的角色在就业市场上非常令人兴奋,世界各地都有大量的数据科学家需求,尤其是在美国、英国和印度等国家。
机器学习
现在我们知道了一点机器学习的历史,我们可以复习机器学习的基础知识。我们可以把 ML 分解成四个部分,如图 1-1 所示。
图 1-1
机器学习类别(来源:en.proft.me)
-
监督机器学习
-
无监督机器学习
-
半监督机器学习
-
强化机器学习
监督机器学习
监督机器学习是机器学习的主要类别,它为企业带来了大量应用和价值。在这种类型的学习中,模型是根据我们已经有了正确标签或输出的数据来训练的。简而言之,我们试图映射输入数据和输出数据之间的关系,这样它也可以很好地概括看不见的数据,如图 1-2 所示。模型的训练通过将实际输出与预测输出进行比较,然后优化函数以减少实际输出和预测输出之间的总误差来进行。
图 1-2
一般化
这种类型的学习主要用于历史数据可用并且需要对未来数据进行预测的情况。监督学习的进一步分类基于用于预测的标签类型,如图 1-3 所示。如果输出变量的性质是数字,它属于回归,而如果是分类,它属于分类类别。
图 1-3
回归与分类
分类是指输出变量是离散值或本质上是分类的情况。分类有两种类型。
-
二元分类
-
多经典小说
当目标类为两类时,称为二元,当目标类多于两类时,称为多类,如图 1-4 所示。
图 1-4
二元对多元
监督学习的另一个特性是可以评估模型的性能。基于模型的类型(分类或回归),可以应用评估度量,并且可以测量性能结果。这主要通过将训练数据分成两组(训练集和验证集)并在训练集上训练模型并在验证集上测试其性能来实现,因为我们已经知道验证集的正确标签/结果。
无监督学习
无监督学习是在商业应用中大量使用的另一类机器学习。就输出标签而言,它不同于监督学习。在无监督学习中,除了数据集不包含任何标签或结果列之外,我们在与监督学习类似的数据类别上建立模型。本质上,我们在没有任何正确答案的情况下对数据应用模型。在无监督学习中,机器试图在数据中找到隐藏的模式和有用的信号,这些数据可以在以后用于其他应用。主要目标是探测数据,并在数据集中找出隐藏的模式和相似性结构,如图 1-5 所示。用例之一是在客户数据中寻找模式,并将客户分组到不同的集群中。它还可以识别区分任何两个组的属性。从验证的角度来看,没有对无监督学习的准确性进行测量。基于用于建立模型的参数,由人 A 完成的聚类可能与人 B 完成的聚类完全不同。有不同类型的无监督学习。
图 1-5
使聚集
-
k 均值聚类
-
最近邻映射
半监督学习
顾名思义,半监督学习介于监督学习和无监督学习之间。事实上,它使用了这两种技术。这种类型的学习主要与我们处理混合类型的数据集的场景相关,这种数据集包含有标签和无标签的数据。有时它只是完全未标记的数据,但我们手动标记了其中的一部分。半监督学习的整个思想是使用这一小部分已标记的数据来训练模型,然后使用它来标记其他剩余部分的数据,这些数据可以用于其他目的。这也被称为伪标记,因为它使用监督模型做出的预测来标记未标记的数据。举一个简单的例子,假设我们有很多来自社交媒体的不同品牌的图片,其中大部分都没有标签。现在使用半监督学习,我们可以手动标记这些图像中的一些,然后在标记的图像上训练我们的模型。然后,我们使用模型预测来标记剩余的图像,以将未标记的数据完全转换为标记的数据。
半监督学习的下一步是在整个标记数据集上重新训练模型。它提供的优势是模型在更大的数据集上进行训练,这在早期是不存在的,现在更健壮,更擅长预测。另一个优势是半监督学习节省了大量人工标记数据的精力和时间。这样做的另一面是,很难获得伪标记的高性能,因为它使用一小部分标记数据来进行预测。然而,这仍然是比手动标记数据更好的选择,手动标记数据既昂贵又耗时。这就是半监督学习如何使用监督和非监督学习来生成标记数据。面临标签化培训过程相关成本挑战的企业通常会选择半监督学习。
强化学习
强化学习是第四种学习,在数据使用和预测方面没有什么不同。强化学习本身就是一个很大的研究领域,可以就此写一整本书。其他类型的学习和强化学习的主要区别在于,我们需要数据,主要是历史数据来训练模型,而强化学习是在一个奖励系统上工作的,如图 1-6 所示。它主要是基于代理为改变其状态而采取的某些行动的决策,同时试图最大化回报。让我们使用可视化将它分解为单个元素。
图 1-6
强化学习
-
自主代理:这是整个学习中负责采取行动的主角。如果是一个游戏,代理采取行动来完成或达到最终目标。
-
Actions :这是一组可能的步骤,代理可以采取这些步骤来推进任务。每个动作都会对代理的状态产生一些影响,并可能导致奖励或惩罚。例如,在网球比赛中,动作可能是发球、回球、向左或向右移动等。
-
奖励:这是强化学习取得进步的关键。奖励使代理能够根据它们是积极的奖励还是惩罚来采取行动。它是一种即时反馈机制,区别于传统的监督和非监督学习技术。
-
环境(Environment):这是代理可以参与的领域。环境决定了行动者采取的行动是奖励还是惩罚。
-
状态:代理在任何给定时间点所处的位置定义了代理的状态。为了向前推进或达到最终目标,代理人必须不断地朝积极的方向改变状态,以使回报最大化。
强化学习的独特之处在于,它有一个即时反馈机制,基于一个奖励系统来驱动代理的下一个行为。大多数使用强化学习的应用是在导航、机器人和游戏中。然而,它也可以用来建立推荐系统。
现在让我们回顾一下机器学习中的一些重要概念,因为在进入生产中的机器学习之前,很好地理解这些方面是至关重要的。
梯度下降
在一天结束时,机器学习模型就像它在预测中能够最小化的损失一样好。对于特定类别的问题,有不同类型的损失函数,通常在典型的分类或回归任务中,我们试图在训练和交叉验证期间最小化均方误差和对数损失。如果我们把损耗想成一条曲线,如图 1-7 所示,梯度下降帮助我们到达损耗值最小的点。我们基于模型中的初始权重或参数开始一个随机点,并向它开始减少的方向移动。这里值得记住的一点是,当梯度下降远离实际最小值时,它会采取很大的步长,而一旦它达到一个附近的值,步长就会变得很小,不会错过最小值。
为了向最小值点移动,它从对参数/系数(在神经网络的情况下是权重)取误差的导数开始,并试图找到该误差曲线的斜率等于零的点。梯度下降中的一个重要组成部分是学习率,因为它决定了下降到最低误差值的快慢。如果学习率参数被设置为较高的值,那么很可能它会跳过最低值,相反,如果学习率太小,它将需要很长时间才能收敛。因此,学习率成为整个梯度下降过程中的重要部分。
梯度下降的总体目标是基于训练数据达到反映最小误差的输入系数的相应组合。因此,在某种程度上,我们试图改变这些系数值,使损失最小。这是通过从旧系数值中减去学习速率和斜率(误差相对于系数的导数)的乘积的过程来实现的。系数值的这种改变持续发生,直到模型的系数/权重不再有变化,因为它表示梯度下降已经达到损失曲线中的最小值点。
图 1-7
梯度下降
另一种类型的梯度下降技术是随机梯度下降(SGD ),它涉及一种类似的方法,用于使误差向零方向最小化,但是使用多组数据点,而不是一次性考虑所有数据。它从输入数据中提取样本数据,并应用梯度下降来找到误差最低的点。
偏差与方差
偏差方差权衡是数据科学家最常关注的问题。高偏差是指机器学习模型没有从输入数据中学习足够多的信号,并导致最终预测性能不佳的情况。在这种情况下,模型过于简单,无法根据给定的输入来近似输出。另一方面,高方差指的是过度拟合(在训练数据上学习太多)。在高方差的情况下,由于模型过于复杂,对训练数据的模型学习会影响对未知或测试数据的泛化性能。你需要平衡偏差和方差,因为两者是相反的。换句话说,如果我们增加 bias,方差就会下降,反之亦然,如图 1-8 所示。
图 1-8
偏差与方差
交叉验证和超参数
对于大多数机器学习算法来说,有一组超参数可以相应地调整,以使模型具有最佳性能。超参数的著名类比是收音机/晶体管中的调谐旋钮,以匹配无线电台的精确频率,从而正确听到声音。同样,超参数为给定训练数据的模型性能提供了最佳可能组合。以下是机器学习模型(如随机森林)中超参数的几个示例:
-
树的数量
-
最大功能数量
-
树的最大深度
对于先前超参数的不同值,模型将学习给定输入数据的不同参数,并且预测性能将相应地变化。大多数库为模型的普通版本提供了这些参数的默认值,数据科学家有责任找出在特定情况下工作的最佳超参数。我们还必须小心不要过度拟合数据。现在,超参数和交叉验证齐头并进。交叉验证是一种技术,在这种技术中,我们以这样的方式分割训练数据,即训练集中的大部分记录用于训练模型,剩余的记录集(较小的记录集)用于测试模型的性能。根据交叉验证的类型(有重复或无重复),训练数据被相应地拆分,如图 1-9 所示。
图 1-9
交互效度分析
性能指标
根据所用算法的性质,有不同的方法可以评估机器学习模型的性能。如前所述,大体上有两类模型:回归和分类。对于预测连续目标的模型,如 R-square,可以使用均方根误差(RMSE ),而对于后者,精度度量是标准度量。然而,在存在类别不平衡并且业务需要只关注正面或负面类别中的一个类别的情况下,可以使用诸如精度和召回之类的度量。
既然我们已经复习了机器学习中的基础知识和重要概念,那么是时候在云平台上建立一个简单的机器学习模型了,即 Databricks。
Databricks 是一种简单方便的方式,可以开始使用云基础设施来构建和运行机器学习模型(单线程和分布式)。我在我之前的几本书里已经对 Databricks 平台做了深入的介绍(使用 PySpark 的机器学习和学习 PySpark )。本章这一节的目的是让你了解如何通过注册任何一个主要的云服务提供商(Google、Amazon、Microsoft、Databricks)来在云上使用 ML。这些平台中的大多数允许用户简单地注册并使用 ML 服务(在某些情况下具有有限的能力)一段预定的时间或者直到用尽免费信用点的程度。Databricks 允许您使用其平台的社区版,该平台提供高达 6 GB 的集群大小。我们将使用 community edition 在假货币数据集上构建并理解决策树模型。该数据集包含纸币的四个属性,可用于检测纸币是真的还是假的。因为我们使用的是社区版,所以数据集的大小是有限制的,因此为了演示的目的,它被保持得相对较小。
Note
注册 Databricks 社区版来运行这段代码。
第一步是用默认设置启动一个新的集群,因为我们在这里不是构建一个复杂的模型。一旦集群启动并运行,我们只需将数据从本地系统上传到 Databricks。下一步是创建一个新的笔记本,并将其连接到我们之前创建的集群。下一步是导入所有必需的库,并确认数据已成功上传。
[In]: import pandas as pd
[In]: import numpy as np
[In]: from sklearn.model_selection import train_test_split
[In]: from sklearn.tree import DecisionTreeClassifier
[In]: from sklearn.metrics import classification_report
以下命令行将显示从本地系统上传的表(数据集):
[In]: display(dbutils.fs.ls("/FileStore/tables/"))
下一步是从表中创建 Spark 数据帧,然后将其转换为 pandas 数据帧来构建模型。
[In:sparkDF=spark.read.csv('/FileStore/tables/currency_note_data.csv', header="true", inferSchema="true")
[In]: df=sparkDF.toPandas()
我们可以使用 pandas head 函数来查看数据帧的前五行。这确认了我们总共有五列,包括目标列(Class)。
[In]: df.head(5)
[Out]:
如前所述,数据大小相对较小,我们可以看到它总共只包含 1,372 条记录,但是目标类似乎很平衡,因此我们没有处理不平衡的类。
[In]: df.shape
[Out]: (1372, 5)
[In]: df.Class.value_counts()
[Out]:
0 762
1 610
我们还可以通过使用 info 函数来检查数据帧中是否有任何丢失的值。数据帧似乎不包含这样的缺失值。
[In]: df.info()
[Out]:
下一步是使用训练测试拆分功能将数据拆分为训练集和测试集
[In]: X = df.drop('Class', axis=1)
[In]: y = df['Class']
[In]:X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25,random_state=30)
既然我们已经分离出了训练集,我们可以用默认的超参数构建一个决策树来简化事情。请记住,构建此模型的目的只是为了介绍在云平台上训练模型的过程。如果您想要训练一个更复杂的模型,请随意添加您自己的步骤,例如增强的特征工程、超参数调整、基线模型、可视化等等。我们将建立更复杂的模型,包括本书后面章节中的所有步骤。
[In]: dec_tree=DecisionTreeClassifier().fit(X_train,y_train)
[In]: dec_tree.score(X_test,y_test)
[Out]: 0.9854227405247813
我们可以看到,决策树似乎在测试数据上做得非常好。除了准确性之外,我们还可以使用分类报告功能查看其他性能指标。
[In]: y_preds = dec_tree.predict(X_test)
[In]: print(classification_report(y_test,y_preds))
[Out]:
深度学习
在本章的这一节,我们将回顾深度学习的基础及其底层操作原理。深度学习已经引人注目好几年了,在解决各种商业挑战方面正在突飞猛进。从图像字幕到语言翻译,再到无人驾驶汽车,深度学习已经成为更大范围内的重要组成部分。举个例子,谷歌的产品,如 Gmail、YouTube、Search、Maps 和 Assistance,都在后台以某种方式使用深度学习,因为它具有令人难以置信的能力,可以提供比其他一些传统机器学习算法好得多的结果。
但是深度学习到底是什么?嗯,在进入深度学习之前,我们必须了解什么是神经网络。深度学习实际上是神经网络的一种扩展。正如本章前面提到的,神经网络不是新的,但由于各种限制,它们没有起飞。这些限制不再存在,企业和研究团体现在能够利用神经网络的真正力量。
在监督学习设置中,有特定的输入和相应的输出。机器学习算法的目标是使用这些数据并近似输入和输出变量之间的关系。在某些情况下,这种关系是显而易见的,很容易捕捉,但在现实情况下,输入和输出变量之间的关系是复杂的,本质上是非线性的。举个例子,对于自动驾驶汽车,输入变量可能如下:
-
地带
-
离最近物体的距离
-
红绿灯
-
标志板
输出需要是转弯、开快或开慢、刹车等。正如你可能想到的,输入变量和输出变量之间的关系在本质上是相当复杂的。因此,传统的机器学习算法发现很难映射这种关系。在这种情况下,深度学习优于机器学习算法,因为它也能够学习那些非线性特征。
人脑神经元与人工神经元
如上所述,深度学习只是神经网络的扩展,也称为深度神经网络。在学习方面,神经网络与其他机器学习算法相比略有不同。神经网络是由人脑中的神经元松散地激发出来的。神经网络由人工神经元组成。虽然我并不自称是神经科学或大脑功能的专家,但让我试着给你一个“人脑如何运作”的高层次概述。你可能已经意识到了,人类的大脑是由数十亿个神经元以及它们之间数量惊人的连接组成的。每个神经元都与多个其他神经元相连,它们反复交换信息(信号)。我们在身体上或精神上所做的每一项活动都会激活我们大脑中的某一组神经元。现在,每一个神经元都由三个基本部分组成。
-
树突
-
细胞体
-
终端
正如我们在图 1-10 中看到的,树突负责接收来自其他神经元的信号。树突充当特定神经元的接收器,并将信息传递给细胞体,在细胞体中处理该特定信息。现在,根据信息的级别,它要么激活(启动),要么不触发。这种活动取决于神经元的特定阈值。如果输入信号值低于该阈值,则不会触发;否则,它会激活。最后,第三个组成部分是与其他神经元的树突连接的末端。终端负责将特定神经元的输出传递给其他相关的连接。
图 1-10
神经元
现在,我们来看看人工神经元,它是神经网络的基本构件。单个人工神经元主要由两部分组成;一个是求和,一个是激活,如图 1-11 所示。这也被称为感知器。求和是指将所有输入信号相加,激活是指根据阈值决定神经元是否触发。
图 1-11
人工神经元
假设我们有两个二进制输入(X1,X2)和它们各自连接的权重(W1,W2)。权重可以被认为类似于传统机器学习中输入变量的系数。这些权重表示特定输入要素在模型中的重要性。求和函数计算输入的总和。然后,激活功能使用该总累加值并给出一定的输出,如图 1-12 所示。激活是一种决策功能。基于所使用的激活功能的类型,它相应地给出一个输出。有不同类型的激活函数可用于神经网络层。
图 1-12
神经元计算
激活功能
激活函数在神经网络中起着至关重要的作用,因为输出基于所使用的激活函数的类型而变化。通常有四种广泛使用的主要激活功能。我们将在本节中简要介绍这些内容。
Sigmoid 激活函数
第一类激活函数是 sigmoid 函数。该激活功能确保无论输入如何,输出始终在 0 和 1 之间,如图 1-13 所示。这就是为什么它也被用于逻辑回归来预测事件的概率。
图 1-13
乙状结肠的
双曲正切
另一个激活函数被称为双曲正切激活函数,或 tanh。该功能确保数值保持在-1 到 1 之间,与输出无关,如图 1-14 所示。tanh 激活函数的公式如下:
图 1-14
双曲正切
整流器线性单元
整流线性单元(relu)在过去几年里非常成功,已经成为激活功能的默认选择。它功能强大,因为它产生 0 到∞之间的值。如果输入为 0 或小于 0,则输出将始终为 0,但对于任何大于 0 的值,输出与输入相似,如图 1-15 所示。relu 的公式如下:
f(x)= 最大值(0,x)
图 1-15
线性单元
神经元计算示例
既然我们对不同的激活函数有了基本的了解,那么让我们看一个例子来了解实际的输出是如何在神经元内部计算的。假设我们有两个输入,X1 和 X2,值分别为 0.2 和 0.7,权重分别为 0.05 和 0.03,如图 1-16 所示。求和功能计算输入信号的总和,如图 1-17 所示。
图 1-16
神经元输入
下面是求和:
图 1-17
总和
下一步是通过激活函数传递这个总和。让我们考虑使用一个 sigmoid 函数,不管输入是什么,它都返回 0 到 1 之间的值。sigmoid 函数将计算值,如下图所示和图 1-18:
图 1-18
激活
所以,这个单个神经元的输出等于 0.5077。
神经网络
当我们组合多个神经元时,我们最终会得到一个神经网络。最简单和最基本的神经网络可以仅使用输入和输出神经元来构建,如图 1-19 所示。
图 1-19
简单网络
像这样使用神经网络的挑战是,它只能学习线性关系,在输入和输出之间的关系是非线性的情况下,它不能很好地执行。正如我们已经看到的,在现实世界中,这种关系很难是简单的和线性的。因此,我们需要在输入层和输出层之间引入一个额外的神经元层,以增加它学习不同类型的非线性关系的能力。这个额外的神经元层被称为隐藏层,如图 1-20 所示。它负责将非线性引入网络的学习过程。神经网络也被称为通用逼近器,因为它们具有逼近输入和输出变量之间的任何关系的能力,无论这种关系有多复杂和非线性。很大程度上取决于网络中隐藏层的数量以及每个隐藏层中神经元的总数。如果有足够多的隐藏层,它可以非常好地映射这种关系。
图 1-20
带隐含层的神经网络
培训过程
神经网络就是各种连接(红线)和与这些连接相关的不同权重。神经网络的训练主要包括调整这些权重,以使模型能够以更高的准确度进行预测。为了理解神经网络是如何训练的,我们来分解一下网络训练的步骤。
第一步:取如图 1-21 所示的输入值,计算输出值传递给隐藏神经元。用于求和计算的第一次迭代的权重是随机生成的。
图 1-21
隐蔽层
传递的另一个分量是偏置神经元输入,如图 1-22 所示。这主要用于当你想有一些非零输出,甚至零输入值(你会学到更多的偏见在本章稍后)。
图 1-22
偏差分量
第二步:将网络预测输出与实际输出进行比较,如图 1-23 所示。
图 1-23
输出比较
第三步:误差反向传播到网络,如图 1-24 。
图 1-24
错误传播
第四步:根据输出重新调整权重,使误差最小,如图 1-25 。
步骤 5: 基于更新的权重计算新的输出值。
重复步骤 2,直到权重不可能再有变化。
图 1-25
重量调整
偏差在神经网络中的作用
人们的一个常见问题是,为什么我们要在神经网络中加入偏差?偏差的作用对于模型的正确学习是至关重要的,因为它直接关系到模型的性能。为了理解偏差在神经网络中的作用,我们需要回到线性回归,并揭示截距在回归线中的作用。我们知道一个事实,截距的值改变线的位置向上或向下,而斜率改变线的角度,如图 1-26 所示。如果斜率小于输入,变量对最终预测的影响较小,因为对于输入值的变化,小斜率对应的输出变化较小,而如果斜率值较大,则输出对输入值的最小变化更敏感,如图 1-26 所示。
图 1-26
回归斜率
因此,斜率值决定线条存在的角度,而截距值决定线条存在的位置(低或高)。
同样,如果我们不对网络使用偏差,简单的计算将是权重的组合输出和激活函数的输入。由于输入是固定的,只有权重可以改变,我们只能改变激活曲线的陡度/角度,这只是完成了一半的工作(尽管有些情况下它是可行的),如图 1-27 所示。
图 1-27
没有偏见
理想情况下,我们还希望水平移动曲线(从左到右),以获得激活函数的特定输出,从而进行适当的学习,如图 1-28 所示。这就是网络中偏差的确切目的,因为它允许我们将曲线从左向右移动,就像回归中的截距一样。
图 1-28
有偏见
现在我们已经很好地理解了深度学习是如何工作的,我们可以深入到广泛使用的特定神经网络中。深度学习模型有许多变体,但我们将只关注两种类型的深度学习模型。
-
卷积神经网络
-
循环神经网络
美国有线新闻网;卷积神经网络
当 CNN 首次用于基于图像的任务时,出现了重大突破。他们提供的准确性超过了以前用于图像识别的所有其他算法。从那时起,有多种 CNN 变体被用于解决特定的基于图像的任务,如人脸识别、计算机视觉、无人驾驶汽车等。细胞神经网络能够从图像中提取高级特征,这些特征通过称为卷积的过程捕获图像最重要的方面进行识别,如图 1-29 所示。
图 1-29
图像分类
卷积是一个容易理解的过程,因为我们在图像像素值上滚动滤波器(也称为内核)来提取卷积特征,如图 1-30 所示。
图 1-30
图像卷积
在图像上滚动过滤器表示我们用过滤器取图像的特定区域的值的点积。一旦我们有了复杂的特征地图,我们就使用池化来减少它。共有不同版本的池(最大池、最小池和平均池)。这样做是为了确保通过网络减少图像数据的空间大小。根据数据集和其他指标,我们可以在 CNN 的不同阶段建立池层。图 1-31 显示了最大池和图像池的示例。
图 1-31
联营
我们可以在一个网络中多次重复前面的步骤(卷积和池化)来学习图像的主要特征,最后传递到最后的全连通层来进行分类。
RNN
一般的前馈神经网络和 CNN 不适合时间序列类型的数据集,因为这些网络没有它们自己的任何记忆。循环神经网络带来了独特的能力,能够在一段时间的训练中记住重要的东西。这使得它们非常适合自然语言翻译、语音识别和图像字幕等任务。这些网络具有在时间线上定义的状态,并在当前输入中使用先前状态的输出,如图 1-32 所示。
图 1-32
RNN
虽然 RNNs 已被证明在时序类应用中确实有效,但由于其架构,它在性能方面确实遇到了一些严重的限制。它与所谓的消失梯度问题进行斗争,该问题是由于网络试图使用时间线早期阶段的数据点时网络权重没有更新或更新很少而发生的。因此,用简单的话来说,它的记忆是有限的。为了解决这个问题,有几个其他的 RNNs 变种。
-
长短期记忆(LSTM)
-
梯度重复单位(GRU)
-
注意力网络(编码器-解码器模型)
现在,我们将使用一个小数据集,并建立一个深度学习模型来预测用户评论的情绪。我们将利用 TensorFlow 和 Keras 来建立这个模型。在 Databricks 中训练这个模型之前,我们需要做几个步骤。我们首先需要转到群集并单击“Libraries”。在 Libraries 选项卡上,我们需要选择 Pypi 选项并提到 Keras 来安装它。同样,一旦安装了 Keras,我们还需要提到 TensorFlow。
一旦我们上传了评论数据集,我们就可以像在前面的例子中那样创建一个熊猫数据框架。
[In]: from tensorflow.keras.models import Sequential
[In]: from tensorflow.keras.layers import LSTM,Embedding
[In]: from tensorflow.keras.layers import Dense
[In]: from tensorflow.keras.preprocessing.text import Tokenizer
[In]: from tensorflow.keras.preprocessing.sequence import pad_sequences
[In]:sparkDF= spark.read.csv('/FileStore/tables/text_summary.csv', header="true", inferSchema="true")
[In]: df=sparkDF.toPandas()
[In]: df.columns
[Out]: Index(['Sentiment', 'Summary'], dtype="object")
如我们所见,数据帧中只有两列。
[In]: df.head(10)
[Out]:
[In]: df.Sentiment.value_counts()
[Out]:
1 1000
0 1000
我们还可以通过计算目标列的值来确认类平衡。数据似乎很平衡。在我们继续构建模型之前,因为我们正在处理文本数据,所以我们需要稍微清理一下,以确保在训练时不会抛出不必要的错误。因此,我们使用正则表达式编写了一个小的助手函数。
[In]:
import re
def clean_reviews(text):
text=re.sub("[^a-zA-Z]"," ",str(text))
return re.sub("^\d+\s|\s\d+\s|\s\d+$", " ", text)
[In]: df['Summary']=df.Summary.apply(clean_reviews)
[In]: df.head(10)
[Out]:
下一步是分离输入和输出数据。由于数据已经很小,我们不打算把它分成训练集和测试集;相反,我们将根据所有数据训练模型。
[In]: X=df.Summary
[In]: y=df.Sentiment
我们现在用 10,000 个 vocab 单词创建 tokenizer 对象,并为模型接触到的不属于训练的不可见单词提到了一个词汇外(oov)标记。
[In]: tokenizer=Tokenizer(num_words=10000,oov_token='xxxxxxx')
[In]: tokenizer.fit_on_texts(X)
[In]: X_dict=tokenizer.word_index
[In]: len(X_dict)
[Out]: 2018
[In]: X_dict.items()
[Out]:
我们可以看到,在训练数据中有 2018 个唯一的单词。现在,我们根据使用 tokenizer 完成的标记映射,将每个评论转换成一个数字向量。
[In]: X_seq=tokenizer.texts_to_sequences(X)
[In]: X_seq[:10]
[Out]:
尽管 text-to-sequence 函数将每个评论转换成了一个向量,但是有一个小问题,因为每个向量的长度根据原始评论的长度而不同。为了解决这个问题,我们利用
填充函数。它确保每个向量符合固定的长度(我们根据使用的填充类型(前置或后置)在末尾或开头添加一组 0)。
[In]: X_padded_seq=pad_sequences(X_seq,padding='post',maxlen=100)
[In]: X_padded_seq[:3]
[Out]:
[In]: X_padded_seq.shape
[Out]: (2000, 100)
正如我们所看到的,我们现在已经将每个评论转换为固定大小的向量。下一步,我们展平目标变量,并声明网络的一些全局参数。您可以选择自己的参数值。
[In]: y = np.array(y)
[In]: y=y.flatten()
[In]: max_length = 100
[In]: vocab_size = 10000
[In]: embedding_dims = 50
现在,我们构建一个本质上是顺序的模型,并利用 relu 激活函数。
[In]: model = tf.keras.Sequential([
tf.keras.layers.Embedding(input_length=100,input_dim=10000,output_dim=50),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(50, activation="relu"),
tf.keras.layers.Dense(1, activation="sigmoid")
])
[In]:model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])
[In]: model.summary()
[Out]:
[In]: num_epochs = 10
[In]: model.fit(X_padded_seq,y, epochs=num_epochs)
该模型似乎学习得很好,但也有可能过度拟合数据。我们将在本书后面的章节中讨论网络的过度拟合和其他设置。
现在,我们已经对机器学习和深度学习基础知识有了一些了解,并对如何在云中构建模型有了一些了解,我们可以看看 ML/DL 在世界各地企业中的不同应用,以及随之而来的一些挑战。
工业应用和挑战
在本章的最后一节,我们将介绍人工智能和人工智能的一些实际应用。全球各地的企业都在大力投资 ML 和 AI,并建立标准程序来利用 ML 和 AI 的能力建立自己的竞争优势。目前有多个领域正在应用 ML 和 AI,并为企业提供巨大的价值。我们将看看人工智能和人工智能正在改变的几个主要领域。
零售
令人难以置信地使用 ML 和 AI 的商业垂直领域之一是零售业。由于零售业务产生大量的客户数据,这为应用 ML 和 AI 提供了一个完美的平台。零售行业一直面临着多种挑战,例如缺货情况、次优定价、有限的交叉销售或追加销售以及个性化不足。ML 和 AI 已经能够应对这些挑战,并在零售领域产生令人难以置信的影响。在过去的十年中,在零售领域已经构建了许多由 ML 和 AL 驱动的应用程序,并且数量还在继续增长。最突出的应用是推荐系统。在线零售业务在推荐系统上蓬勃发展,因为它们可以大大增加收入。此外,零售使用 ML 和 AI 功能进行库存优化,以控制库存水平并降低成本。动态定价是人工智能和人工智能被广泛用于获得最大回报的另一个领域。使用 ML 也可以进行客户细分,因为它不仅使用客户的人口统计信息,还使用交易数据,并在揭示客户群中的不同群体之前考虑多个其他变量。产品分类也是使用 ML 完成的,因为它节省了大量的手工劳动,并提高了产品标签的准确性。需求预测和库存优化使用 ML 和 AI 来解决,以节省成本。在过去的几年里,路线规划也由 ML 和 AI 处理,因为它使企业能够以更有效的方式履行订单。由于 ML 和 AI 在零售业中的应用,成本节约有所改善,企业能够做出明智的决策,整体客户满意度也有所提高。
卫生保健
另一个深受 ML 和 AI 影响的垂直业务是医疗保健。使用 ML 和 AI 基于图像数据的诊断正在医疗保健领域迅速得到采用。主要原因是人工智能和人工智能提供的准确性水平以及从过去几十年的数据中学习的能力。医疗保健领域的 X 射线、MRI 扫描和各种其他图像的 ML 和 AI 算法被大量用于检测任何异常。虚拟助手和聊天机器人也作为应用程序的一部分被部署,以帮助解释实验室报告。最后,保险验证也在医疗保健中使用 ML 模型来避免任何不一致。
金融
金融领域总是有大量的数据。与任何其他领域相比,金融一直是数据丰富的领域。因此,在过去的十年中,有许多基于人工智能和人工智能的应用程序正在被构建。最突出的是欺诈检测系统,它在后台使用异常检测算法。其他领域是投资组合管理和算法交易。ML 和 AI 有能力扫描 100 多年的过去数据,并学习隐藏的模式,以建议投资组合的最佳校准。复杂的人工智能系统正被用来做出极快的交易决策,以实现收益最大化。ML 和 AI 也用于风险缓解和贷款保险承保。此外,推荐系统正被各种机构用于追加销售和交叉销售各种金融产品。他们还使用推荐系统来预测客户群的流失,以便制定策略来留住可能会停止使用特定产品或服务的客户。ML 和 AI 在金融领域的另一个重要用途是,根据模型做出的预测,检查是否应该向各种申请人发放贷款。此外,基于 ML 模型预测,ML 被用于验证保险索赔是真实的还是欺诈的。
旅行和招待
就像零售一样,旅游和酒店领域正在基于人工智能和人工智能的应用程序上蓬勃发展。举几个例子,推荐系统、价格预测和虚拟助理都是基于人工智能和人工智能的应用,正在旅游和酒店业得到利用。从推荐最佳交易到替代旅行日期,推荐系统对于推动这一领域的客户行为至关重要。它还根据用户的偏好推荐新的旅行目的地,这些都是在后台使用 ML 高度定制的。人工智能还被用来根据各种因素预测未来的价格变动,从而及时向客户发送警报。如今,虚拟助理是每个旅游网站的一部分,因为客户不想等待获取相关信息。最重要的是,与这些虚拟助手的交互非常像人类,因为自然语言智能已经在很大程度上嵌入到这些聊天机器人中,以便理解简单的问题并以类似的方式回答。
媒体和营销
每个企业或多或少都依赖营销来获得更多的客户,而接触正确的客户一直是一个巨大的挑战。由于 ML 和 AI,这个问题现在得到了更好的处理,因为它可以在很大程度上预测客户行为。基于 ML 和 AI 的应用程序正被用来区分更有可能购买或订阅该产品或服务的潜在客户和临时候选人。他们还被用来提供一个绝对个性化的报价,以转换或保留客户。再次大量使用流失预测来识别可能停止使用任何特定产品或服务的消费者群体。超目标的高级客户细分正在使用 ML 和 AI 完成。最后,许多营销内容正在使用 ML 和 AI 人工生成,以发送出表现最好的内容。
制造业和汽车
制造领域也没能逃脱 ML 和 AI 的浪潮。最主要的用途是预测性维护,因为基于 ML 和 AI 的应用程序可以根据早期数据预测维护需求,从而有助于防止潜在的损坏。汽车公司正在使用远程信息处理数据来学习客户的驾驶模式,并更迅速地采取行动,以多种方式帮助他们。他们还使用网络数据来更好地了解他们的客户,以尝试个性化体验,实现在线旅程中的无缝导航。
社会化媒体
大多数人(尤其是年轻一代)在社交媒体上花费了大量时间,却没有意识到许多应用程序正在使用 ML 和 AI。脸书、YouTube、LinkedIn、Twitter 和其他类似的应用大量使用 ML 来提供这种体验。从照片自动标记建议到好友推荐,一切都由 ML 和 AI 驱动。它们还用于为 YouTube 等各种平台生成字幕和语言翻译。各种搜索引擎和语音助手在其中使用了大量的 ML 实现。
其他人
还有很多其他的应用都用到 ML 和 AI。例如,垃圾邮件过滤器使用 ML 而不是基于规则的系统。与传统的基于规则的系统相比,ML 方法提供的一个优势是前者可以根据新邮件自动更新和升级自己,以进行区分。另一个领域是石油和天然气行业,ML 和 AI 帮助分析地下矿物和寻找替代能源。ML 和 AI 也被用于交通运输,因为它们可以预测可能的交通状况并提前警告你。
挑战
到目前为止,我们已经讨论了 ML 和 AI 对这个世界的能力和影响。然而,为了实现人工智能和人工智能的真正潜力,仍然存在许多差距。首先,熟练人才的短缺是人工智能和人工智能发展的主要障碍。我们已经讨论过,人们需要综合多种技能才能在这个领域出类拔萃,这使得寻找这些资源变得更加困难。
“找一个数据科学家很难。找到了解数据科学家的人同样困难。”
—克日什托夫扎瓦日基
下一个挑战是获得更高的计算能力。虽然我们有高性能的处理单元,如 GPU 和 TPU,但由于成本因素和训练大模型所需的时间,它仅限于一组人而不是每个人。因此,如果情况需要大量数据处理和模型训练,这仍然是一个挑战。当使用 ML 和 AI 时,安全性是最关键的方面,因为它们使用大量数据进行训练,以便给出更好的预测。然而,使用个人和敏感数据来构建模型会危及用户数据的安全性和保密性。归根结底,机器学习和人工智能并不是解决所有问题的灵丹妙药。与 ML 和 AI 相关的另一个挑战是可解释性部分,因为很难解释模型预测背后的基本原理。事实上,我们可以称之为黑盒,因为机器学习映射功能的解释有时会变得非常复杂,对其他利益相关者来说可能没有太多意义。在某些领域,人工智能和人工智能无法应用,因此许多计划和应用注定会失败。
要求
接下来的章节使用 Docker 来构建和部署容器,因此您的系统应该已经安装了 Docker 并正常工作。您还需要有管理员权限来安装一些依赖项。您还应该在系统上安装一个虚拟机器。要使用云服务部署应用程序,您应该拥有一个 Google Cloud 帐户。
结论
在这一章中,我们复习了机器学习和深度学习的基础知识。我们还看到了在 Databricks 上构建模型的过程。我们讨论了机器学习和深度学习的不同应用以及它们现有的挑战。
二、模型部署和挑战
在前一章中你已经复习了机器学习的概念,所以现在进入下一阶段是合乎逻辑的。什么是机器学习部署,在进行部署时有哪些常见挑战?
这一章包括两个主题。首先,这一章讨论了什么是模型部署,以及模型生产的不同方面。第二,本章涵盖了在大规模生产过程中面临的不同挑战。在模型部署的两个阶段(部署前和部署后阶段)都可以观察到这些挑战,但是为了简单起见,我们将把这些挑战作为一个整体来关注。虽然机器学习团队所面临的挑战对于每个特定的案例都是独特的,但是我们将讨论与部署相关的最常见的案例。
模型部署
在前一章中,我们看到了建立机器学习模型或深度学习模型(无论是本地还是云中)需要什么。模型的复杂程度或性质可能因具体情况而异,但基本框架是相似的。我们有一些来自数据源(可以是单个源或多个源)的典型数据,然后是一系列数据清理和预处理步骤,然后我们从输入数据中提取或创建重要特征,以训练特定用例的机器学习模型。一旦模型经过训练或准备好在生产环境中使用,就可以通过一些 API 将它暴露给看不见的数据来进行预测。然而,最后一部分带来了一些挑战。如果我们试图在建立成功的 ML 模型后观察这个循环,它看起来有点像图 2-1 。
第一阶段是在生产中部署训练好的 ML 模型并测试结果。接下来是在连续级别上对模型进行性能监控。一旦模型的性能低于预期的基准水平,就需要重新训练和评估模型,用新模型替换旧模型。这包括模型管理(版本、特性等。).在不影响现有用户请求的情况下,模型被再次部署到生产环境中(您将在接下来的 ML 部署章节中了解更多)。一旦新模型被部署,图 2-1 中所示的相同步骤将在整个框架中重复。
图 2-1
部署中的 ML 模型
虽然部署在本质上非常直观,因为有数百万个应用程序已经在机器学习的环境中无缝工作,但可能需要进一步解释。与其他典型的软件应用程序(如移动应用程序)相比,机器学习应用程序在本质上有两个主要方面的不同。
-
基础模型
-
基础数据
当我们说我们将在生产中部署模型或生产化模型时,我们指的是将机器学习模型集成到现有的业务应用程序中。简而言之,我们将 ML 模型作为 REST API 端点公开,以服务于应用程序平台内的请求或指导用户请求。要在生产中部署的这个模型可以是一个独立的预测器,根据用于批处理数据的算法提供一些输出,或者可以用于实时处理请求,使其成为一个动态模型。从数据科学家的角度来看,模型部署通常可以被视为机器学习周期的最后一个阶段;然而,这是模型管理阶段的开始。能够成功部署机器学习模型需要来自多个利益相关方的大量输入和协调,例如数据科学家、数据工程师、应用程序开发人员、mlop/devo PS 和业务团队成员。老实说,这是机器学习生命周期中最困难的阶段,因为在部署阶段可能会出现许多问题。我们将在本章的后面深入探讨这些挑战。
为什么我们需要机器学习部署?
到目前为止,我们已经很好地理解了机器学习模型是做什么的,以及它是如何被训练的,但关键是要理解 ML 模型在整个商业应用中的作用。它可以是简单的预测,也可以是多种预测或某种建议的组合。最终,它需要使整个业务应用程序更加有效。例如,生产中的 ML 模型可以用于基于网站上的活动来预测每个在线访问者购买或不购买特定产品的倾向,或者基于其他因素来预测网络流量。不管 ML 在整个应用程序中的实际角色如何,部署都成为 ML 模型与应用程序对话的一个不可或缺的部分。
有人可能会问,为什么将 ML 模型投入生产如此重要?这个问题有多个答案,但最重要的是从机器学习模型中提取真正的价值。它必须成为应用程序的一部分,并为应用程序提供所有的预测和见解。我能想到的不将机器学习模型投入生产的最佳类比就像是为体育赛事进行训练,但却没有参与其中。这限制了它嵌入到应用程序中可能产生的影响。话虽如此,但在某些情况下,不将模型投入生产更有意义。如前所述,这取决于案例和使用机器学习的应用程序的实际上下文。在某些情况下,一个独立的模型确实被证明更简单,更容易使用。独立模型在构建、训练、预测、提取业务见解和制定决策方面要快得多。但是,在下列情况下,这可能不是一种相关的方法:
-
数据是巨大的。
-
数据正在流动。
-
有很多活跃用户。
-
有更快的反应。
专门针对大数据进行训练以处理类似预测数据的 ML 模型需要在整体应用中得到很好的管理。类似地,如果手头的数据是流类型的,那么模型应该与应用程序集成在一起,以处理不断传入的数据。另一个方面是当你有大量用户的时候;在这种情况下,您希望确保模型能够处理许多请求,并且相同模型的多个实例并行运行以服务于这些请求。需要记住的一个关键点是,在生产中部署 ML 模型并不能保证预测质量的一致性。在现实中,随着模型暴露于真实数据,模型的性能通常会迅速恶化(这种影响被称为漂移,您将在“挑战”部分了解更多信息),这就是模型管理发挥重要作用的地方。
总之,部署有助于通过将模型与应用程序集成来提取机器学习的真正价值,从而产生切实的业务洞察力。这也意味着可以根据实时数据进行预测。
挑战
如果部署 ML 模型很容易,没有任何问题,我就不会写本章的这一节了。很少有人拥有从开发阶段到生产阶段的理想条件,而没有一些改变和调整。我们必须理解开发一个模型和部署一个模型之间的主要区别。这两种任务在性能、速度和资源消耗方面的预期都是不同的。通常情况下,有两个独立的小组分别在这些阶段中的每一个阶段工作(尽管随着更多的数据科学家承担 ML 部署任务以及 DevOps 人员正在学习自己构建 ML/DL 模型,这一趋势正在发生变化)。在对 ML 模型的所有期望中,最与众不同的一个是,当模型在生产中服务于来自用户的实时请求时,需要连续地监控模型的性能,因为应用程序必须在生产中具有模型的最佳可用版本。
斯卡利等人在 2015 年发表的著名谷歌论文《机器学习系统中隐藏的技术债务》,在质疑机器学习在整体应用中的实际作用和重要性时,向机器学习界提出了不同的观点( https://papers.nips.cc/paper/5656-hidden-technical-debt-in-machine-learning-systems.pdf
)。
“他们说,在现实世界的机器学习(ML)系统中,只有一小部分是由实际的 ML 代码组成的。有大量的周边基础设施和流程来支持它们的发展。”
该论文提到,其他组件,如数据依赖性、模型复杂性、可再现性、测试、监控和版本变更,在获得现实的 ML 应用中起着相对更大的作用。在那篇论文之前,对机器学习角色的普遍看法是,它是整个生命周期中最关键的部分,如图 2-2 所示。
图 2-2
应用程序中的 ML 角色
根据这篇论文,这与现实相差甚远。实际上,机器学习代码是整个事物方案的一小部分,如图 2-3 所示。有许多其他的组件驱动着基于 ML 的应用程序的真正价值。
图 2-3
应用程序中的实际 ML 角色
老实说,模型部署期间面临的挑战与图 2-3 中所示的组件密切相关,因为它们使整个应用程序变得实用。如前所述,部署团队还会面临其他挑战。接下来的几节将为您提供一个关于将 ML 模型投入生产时会发生什么的合理想法。
挑战 1:利益攸关方之间的协调
如前所述,部署模型时最自然的障碍是与没有数据科学或机器学习背景的其他团队成员保持一致,如开发人员、应用程序开发人员和业务团队成员。模型部署是一项团队任务,需要不断的沟通和对整体目标的共同理解。在 ML 模型开发的早期阶段进行适当的规划将有助于 MLOps/DevOps 团队提前为部署做好准备。
挑战 2:编程语言差异
与应用程序集成或开发人员使用的语言(Java/C++/Ruby)相比,机器学习模型很可能是用不同的语言(Python/TensorFlow/PyTorch)构建的。这使得集成更加困难,因为 ML 模型需要使用应用程序的本地语言重新编码。如今,迁移 ML 模型以轻松地与应用程序的其余部分集成已经变得越来越容易,但是使用公共语言来构建模型以避免集成问题也很有帮助。
挑战 3:模型漂移
当涉及到机器学习模型在生产中的性能时,模型漂移是一种常见现象。当模型的预测性能降低到可接受的基准以下时,就会发生模型漂移。同样,这取决于模型在什么样的环境中使用以及如何评估这些预测,但是每个应用程序都需要有最佳版本的后端 ML 模型,以获得更高的效率和输出。这成为在生产中持续跟踪已部署模型的性能的主要原因之一。
但是,如果我们深入了解生产中模型性能下降的原因,我们可以将性能下降归因于几个原因。
-
改变新数据的行为
-
改变对新数据的解释
改变数据的行为
我们通常会根据具有必要特性的历史数据训练模型,并在达到满意的性能水平后(在所有超参数调整和测试后)将模型投入生产。然而,一个简单的事实是,随着时间的推移,数据可能会以不同的方式发生变化,带来模型之前未发现的新变化和维度(在训练期间)。这会影响模型在实时或生产中的性能。一个例子是,如果使用机器学习来预测用户转换或不转换(购买或不购买产品)的可能性或倾向,基于使用捕获在线行为的前两年历史数据的训练(用户在做出购买决定之前如何在网站上导航)。在开始时,该模型表现得非常好,能够在所有在线访问者中决定谁是潜在的认真买家,谁是临时访客,但随着时间的推移,用户的购买模式和行为会发生变化,并且会出现与机器学习模型所建议的不同的结果。也许模型学习了不同的决策界限来区分购买者和非购买者,现在需要根据新的潜在模式重新调整。这被称为模型漂移,因为预测是基于输入数据的动态特性。
改变对新数据的解释
在另一种情况下,类别或标签的解释可能会随着时间的推移而改变。例如,如果先前给定的一组客户属于类别 A,但是由于理解和业务案例的变化,该类别或者被改变或者与其他类别(比如说类别 C)组合,这将影响模型的整体性能。在再次投入生产之前,我们需要使用新的标签集(针对历史数据)对模型进行重新训练。这在机器学习中也被称为概念漂移。
挑战 4:内部部署与基于云的部署
许多企业发现很难决定是需要使用内部资源进行模型部署还是选择云服务。从长远来看,根据应用程序用户的数量,这两种决策会产生不同的影响。在决定生产策略之前,DS 团队和基础设施团队应该考虑所有的利弊。
挑战 5:明确的所有权
所有权有时会变得令人困惑,因为当涉及到模型部署时,需要清楚地定义每个团队成员的角色和职责。数据科学家假设他们的工作在模型建立后就完成了,而开发人员/开发人员对模型中发生的事情一无所知,因此需要更多的输入来将模型正确地集成到应用程序中。在一天结束时,整个团队的共同责任是致力于部署,以确保将正确的模型部署到生产中,并避免不必要的延迟。
挑战 6:模型性能监控
尽管监视发生在模型部署阶段之后,但仍需要在实际部署之前进行规划。框架需要到位,以便在一致的基础上跟踪模型的性能。这有助于围绕以下问题制定策略:
-
什么时候重新训练模型?
-
使用多少数据来重新训练模型?
-
该型号的性能基准是什么?
在一段时间内,来自该框架的数据有助于得出模型在生产中的可接受性能的基准数字。
挑战 7:发布/版本管理
当涉及到机器学习部署时,有许多来回的活动,版本跟踪成为整个成功部署过程的重要部分。因此,版本控制有助于跟踪哪个模型是最好的模型、不同的文件依赖关系以及数据资源指针。有多种方式可以处理版本控制;Git 是最广泛使用的跟踪不同版本和分阶段部署的工具。
挑战 8:隐私保护和安全模型
模型部署中的另一个挑战是保护模型免受任何恶意攻击,并将模型数据暴露给第三方渗透。这对于确保该模型不会暴露给任何在安全模式下运行的未授权用户变得至关重要。
还有许多其他挑战,如可伸缩性、公开服务等。,我们将在接下来的章节中继续讨论这些挑战。在下一章,我们将会看到模型部署的不同模式。我们可以将模型部署分为四种不同的方法。
-
在本地部署模型
-
在生产服务器上保存模型
-
将模型部署为 REST 服务
-
托管服务
结论
在这一章中,我们回顾了模型部署的基础及其相关的挑战。
三、作为 Web 服务的机器学习部署
在本章中,我们将讨论如何使用不同的 web 框架来部署机器学习和深度学习模型,作为本地系统上托管的 web 服务。本章涵盖三个主要主题。
本章首先介绍了 Flask 框架以及如何使用它部署 ML 模型。然后,本章展示了如何建立一个标准的机器学习模型,并使用另一个 web 框架 Streamlit 部署它。最后,本章将介绍如何再次使用 Streamlit 平台部署经过训练的深度学习模型。如果您已经熟悉 Flask 基础知识,可以随意跳到第一部分的部署部分,了解使用 Flask 部署机器学习模型的过程。如前一章所述,我们主要使用 Jupyter 笔记本在本地开发和测试模型,但当我们想要将 ML 模型连接到应用程序或 web 服务时,我们本质上需要使用 web 服务器来部署它。这个 web 服务器可以托管在本地或云中。有许多不同的方法可以部署机器学习模型,但在本章中,我们将探索两种方法:Flask 和 Streamlit。
烧瓶简介
简而言之,Flask 是一个开源的轻量级 web 框架,内置于 Python 中,用于部署 web 应用程序。当我们说 web 框架时,我们指的是运行 web 应用程序所需的一组资源。这可能包括 web 开发人员可以用来成功构建和运行应用程序的不同模块、库和工具。不幸的是,这本书没有深入研究 Flask,但是对于那些从未使用过它的人来说,下面的代码片段给出了 Flask 的快速介绍。要使用 Flask,我们首先需要在本地机器上安装它。我们可以简单地使用pip install flask
来安装 Flask。
[In]: from flask import Flask
[In]: app = Flask(__name__)
[In]: @app.route("/")
[In]: def hello():
return "Hello World!"
[In]: if __name__ == '__main__':
app.run(debug=True)
路由功能
route
函数是一个装饰器,它告诉哪个 URL 与一个特定的函数相关联。它有两个参数。
-
rule
-
options
rule
表示 URL 路径及其与给定函数的绑定。当 URL 在浏览器中打开时,它呈现函数的当前输出。这些选项允许您传递不同的参数集。
运行方法
run
方法执行在特定 web 服务器上运行的应用程序。它有四个可以在执行过程中传递的参数。所有这些都是可选参数,app.run
也可以不传递任何参数而执行。
-
host
-
port
-
debug
-
options
默认情况下,应用程序在本地主机(127.0.0.1)上运行。默认情况下,debug
选项设置为 false,可以设置为 true 来查看调试信息。
将机器学习模型部署为 REST 服务
现在我们知道了 Flask 框架是如何工作的,我们将构建一个简单的线性回归模型,并使用 Flask 服务器部署它。我们从导入所需的库开始。
[In]: import pandas as pd
[In]: import numpy as np
[In]: from sklearn.linear_model import LinearRegression
[In]: import joblib
我们将数据集加载到 pandas 中,正如我们所看到的,我们的数据集包含五个输入列和一个目标列。
[In]: df=pd.read_csv('Linear_regression_dataset.csv',header='infer')
[In]: df.sample(5)
[Out]:
因为我们的想法不是构建一个超级强大的模型,而是部署一个 ML 模型,所以我们不需要将这些数据分成训练集和测试集。我们拟合了一个线性回归模型,得到了一个不错的 r 平方值。
[In]: X=df.loc[:,df.columns !='output']
[In]: y=df['output']
[In]: lr = LinearRegression().fit(X, y)
[In]: lr.score(X,y)
[Out]: 0.8692670151914198
下一步是保存训练好的模型,该模型可以在作为 web 服务提供时加载回来。我们利用了序列化模型的joblib
库(将输入变量的系数值保存为字典)。
[In]: joblib.dump(lr,'inear_regression_model.pkl')
现在我们已经保存了模型,我们可以创建主app.py
文件,它将启动 Flask 服务器,将 ML 模型作为 web 应用程序运行。
[In]: import pandas as pd
[In]: import numpy as np
[In]: import sklearn
[In]: import joblib
[In]: from flask import Flask,render_template,request
[In]: app=Flask(__name__)
[In]: @app.route('/')
[In]: def home():
return render_template('home.html')
[In]: @app.route('/predict',methods=['GET','POST'])
[In]: def predict():
if request.method =='POST':
print(request.form.get('var_1'))
print(request.form.get('var_2'))
print(request.form.get('var_3'))
print(request.form.get('var_4'))
print(request.form.get('var_5'))
try:
var_1=float(request.form['var_1'])
var_2=float(request.form['var_2'])
var_3=float(request.form['var_3'])
var_4=float(request.form['var_4'])
var_5=float(request.form['var_5'])
pred_args=[var_1,var_2,var_3,var_4,var_5]
pred_arr=np.array(pred_args)
preds=pred_arr.reshape(1,-1)
model=open("linear_regression_model.pkl","rb")
lr_model=joblib.load(model)
model_prediction=lr_model.predict(preds)
model_prediction=round(float(model_prediction),2)
except ValueError:
return "Please Enter valid values"
return render_template('predict.html',prediction=model_prediction)
[In]: if __name__=='__main__':
app.run(host='0.0.0.0')
让我们过一遍步骤,了解一下app.py
文件的细节。首先,我们从 Python 导入所有需要的库。接下来,我们创建第一个函数,它是呈现 HTML 模板的主页,允许用户填充输入值。下一个功能是发布模型对用户提供的输入值的预测。我们将输入值保存到来自用户的五个不同变量中,并创建一个列表(pred_args
)。然后我们把它转换成一个 numpy 数组。我们把它重塑成想要的形式,以便能够对它做出预测。下一步是加载训练好的模型(linear_regression_model.pkl
)并进行预测。我们将最终输出保存到一个变量中(model_prediction
)。然后,我们通过另一个 HTML 模板predict.html
发布这些结果。如果我们现在在终端中运行主文件(app.py
),我们会看到页面出现,要求用户填写值,如图 3-1 所示。
图 3-1
最大似然预测的输入
模板
我们必须设计两个网页,向服务器发送请求并接收响应消息,该响应消息是 ML 模型对特定请求的预测。由于本书并不关注 HTML,你可以简单地使用这些文件,不需要对它们做任何修改,如图 3-2 所示。但是对于好奇的读者来说,我们正在创建一个表单来请求五个不同变量中的五个值。我们使用一个带有一些基本字段的标准 CSS 模板。预先了解 HTML 的用户可以根据自己的需求随意重新设计主页。
图 3-2
输入请求 HTML 表单
下一个模板是向用户发布模型预测。与第一个模板相比,这没有那么复杂,因为只有一个值需要返回给用户,如图 3-3 所示。
图 3-3
模型预测 HTML 表单
让我们继续输入模型预测值,如图 3-4 所示。正如我们在图 3-5 中所观察到的,模型预测结果是一个连续变量,因为我们已经训练了一个回归模型。
图 3-5
模型预测法
图 3-4
用户输入页面
正如我们所见,Flask 使得将机器学习应用部署为 web 服务变得很容易。使用 Flask 的一个缺点是,因为它是一个轻量级的 web 框架,所以处理复杂应用程序的能力有限。另一个缺点是,大多数数据科学家不习惯使用 HTML 和 JavaScript 来创建应用程序的前端。因此,在下一节中,我们将看看一个更简单的替代方案,使用 Streamlit 部署机器学习应用程序。与 Flask 相比,这使得为应用程序开发简单的 UI 更加容易。
使用细流部署机器学习模型
Streamlit 是 Flask 的替代方案,用于将机器学习模型部署为 web 服务。使用 Streamlit 的最大优势是它允许您在应用程序 Python 文件中使用 HTML 代码。它本质上不需要为前端 UI 提供单独的模板和 CSS 格式。但是,对于更复杂的应用程序,建议您为模板和样式指南创建单独的文件夹。要安装 Streamlit,我们可以简单地使用pip
在我们的终端中安装 Streamlit。
我们将使用构建 Flask 应用程序模型时使用的相同数据集。唯一要改变的内容在app.py
文件中。第一组命令是导入所需的库,如joblib
和streamlit
。
[In]: import pandas as pd
[In]: import numpy as np
[In]: import joblib
[In]: import streamlit
在下一步中,我们导入经过训练的线性回归模型,以便能够对测试数据进行预测。
[In]: model=open("linear_regression_model.pkl","rb")
[In]: lr_model=joblib.load(model)
下一步是定义一个函数,使用训练好的模型进行预测。我们在函数中传递五个输入参数,并做一些整形和数据转换,以确保预测的一致性。然后,我们创建一个变量来保存模型预测结果,并将其返回给用户。
[In]: def lr_prediction(var_1,var_2,var_3,var_4,var_5):
pred_arr=np.array([var_1,var_2,var_3,var_4,var_5])
preds=pred_arr.reshape(1,-1)
preds=preds.astype(int)
model_prediction=lr_model.predict(preds)
return model_prediction
下一步,我们创建最重要的函数。我们接受来自浏览器的用户输入,并在网页上呈现模型的最终预测。我们可以给这个函数起任何名字。比如我用过run
(因为它和 Flask 的app.run
做的是一样的事情)。在这个功能中,我们包括前端代码,以及如定义标题,主题,颜色,背景等。为了简单起见,我保持了它的基本性,但是它可以有多层次的增强。有关更多详情,您可以访问 Streamlit 网站。接下来,我们创建五个输入变量来接受来自浏览器的用户输入值。这是使用 Streamlit 的text_input
功能完成的。最后一部分包含模型预测,它从前面定义的lr_prediction
函数获得输入,并通过streamlit.button
在浏览器中呈现。
[In]: def run():
streamlit.title("Linear Regression Model")
html_temp="""
"""
streamlit.markdown(html_temp)
var_1=streamlit.text_input("Variable 1")
var_2=streamlit.text_input("Variable 2")
var_3=streamlit.text_input("Variable 3")
var_4=streamlit.text_input("Variable 4")
var_5=streamlit.text_input("Variable 5")
prediction=""
if streamlit.button("Predict"):
prediction=lr_prediction(var_1,var_2,var_3,var_4,var_5)
streamlit.success("The prediction by Model : {}".format(prediction))
现在我们已经有了应用程序文件中提到的所有步骤,我们可以调用 main 函数(在我们的例子中是run
)并使用streamlit run
命令来运行应用程序。
[In]: if __name__=='__main__':
run()
[In]: streamlit run app.py
一旦我们运行前面的命令,我们将很快看到应用程序启动并在端口 8501 上运行,如图 3-6 所示。我们只需点击链接,进入应用程序。
图 3-6
访问跑步应用
一旦我们到达http://localhost:8501/
,我们将看到如图 3-7 所示的屏幕。
图 3-7
用户输入页面
如您所见,我们在 UI 中没有任何花哨的东西,但它服务于我们能够在幕后与模型/应用程序交互的总体目的。它类似于我们用 Flask 构建的东西,但是它需要的 HTML 和 CSS 代码要少得多。我们现在可以继续填充值,如图 3-8 所示,并获得模型预测。为了便于比较,我们填充了与基于 Flask 的应用程序中相同的值。
图 3-8
为模型提供输入值
填入数值后,我们需要点击预测按钮来获取模型预测结果和voilà——这是我们在基于烧瓶的 app 中得到的相同数字,如图 3-9 所示。
图 3-9
模型预测法
现在我们已经看到了如何使用 Flask 和 Streamlit 部署传统的机器学习模型,我们可以继续本章的最后一个主题,重点是将深度学习模型(LSTM)部署为 web 服务。
部署深度学习模型
在本章的前几节中,我们介绍了使用两种不同的框架构建和部署机器学习模型(线性回归)的过程。
-
瓶
-
细流
在本节中,我们将了解如何构建深度学习模型,并使用 Streamlit 部署它。特别是,我们建立了一个基于 LSTM 的神经网络来预测给定评论的情绪。众所周知,LSTM/RNNs 是建立序列模型和捕捉预测输入顺序的有效方法。我们将使用开源评论数据集,并训练一个 LSTM 模型来预测评论的情绪。然后,我们使用 Streamlit 将该模型作为 web 服务提供。同样,在前面的案例中,重点不是训练一个完美的模型,而是一个合适的模型,并部署它。除了一些额外的组件,如文本处理和处理输入数据(用户检查)的标记器之外,基本原理与以前相同。我们正在利用 Streamlit 来避免大量的模板、HTML 和 CSS 东西,并使部署深度学习模型变得容易。
培训 LSTM 模型
对于那些不熟悉 RNN/LSTMs 的人,我建议你们多读一些关于它们的内容,以了解它们是如何工作的,以及它们何时可以用于预测。第一步是导入所有需要的库。
[In]: import numpy as np
[In]: import pandas as pd
[In]: from tensorflow.keras.models import Sequential
[In]: from tensorflow.keras.layers import LSTM,Embedding
[In]: from tensorflow.keras.layers import Dense
[In]: from tensorflow.keras.preprocessing.text import Tokenizer
[In]: from tensorflow.keras.preprocessing.sequence import pad_sequences
[In]: from sklearn.model_selection import train_test_split
[In]: from keras.utils.np_utils import to_categorical
[In]: import re
[In]: import pickle
下一步是加载数据并检查数据的大小。
[In]: df = pd.read_csv('reviews_dataset.tsv.zip',header=0, delimiter="\t", quoting=3)
[In]: df = df[['review','sentiment']]
如我们所见,我们的数据集中有 25,000 条记录和两个相等的情感类别。
[In]: df.shape
[Out]: (25000, 2)
[In]: df.sentiment.value_counts()
[Out]:
1 12500
0 12500
接下来,我们应用一些文本预处理,使用正则表达式来清理评论。
[In]: df['review'] = df['review'].apply(lambda x: x.lower())
[In]: df['review'] = df['review'].apply((lambda x: re.sub('[^a-zA-z0-9\s]','',x)))
我们将特征的数量限制为 1000,并将评论标记化,并添加填充以使每个评论的大小相同。
[In]: max_features = 1000
[In]: tokenizer = Tokenizer(num_words=max_features, split=' ')
[In]: tokenizer.fit_on_texts(df['review'].values)
[In]: X = tokenizer.texts_to_sequences(df['review'].values)
[In]: X = pad_sequences(X)
[In]: X.shape
(25000, 1473)
然后,我们保持嵌入层大小为 50。
[In]: embed_dim = 50
[In]: model = Sequential()
[In]: model.add(Embedding(max_features, embed_dim,input_length = X.shape[1]))
[In]: model.add(LSTM(10))
[In]: model.add(Dense(2,activation='softmax'))
[In]: model.compile(loss = 'categorical_crossentropy', optimizer="adam",metrics = ['accuracy'])
[In]: print(model.summary())
[Out]:
[In]: y = pd.get_dummies(df['sentiment']).values
[In]: X_train, X_test, y_train, y_test = train_test_split(X,y, test_size = 0.25, random_state = 99)
[In]: print(X_train.shape,y_train.shape)
[In]: print(X_test.shape,y_test.shape)
[Out]:
(18750, 1473) (18750, 2)
(6250, 1473) (6250, 2)
[In]: model.fit(X_train, y_train, epochs = 5, verbose = 1)
现在我们已经训练了 LSTM 模型,让我们试着通过一个测试来查看模型的预测。
[In]: test = ['Movie was pathetic']
[In]: test = tokenizer.texts_to_sequences(test)
[In]: test = pad_sequences(test, maxlen=X.shape[1], dtype="int32", value=0)
[In]: print(test.shape)
[In]: sentiment = model.predict(test)[0]
if(np.argmax(sentiment) == 0):
print("Negative")
elif (np.argmax(sentiment) == 1):
print("Positive")
[Out]: Negative
正如我们所看到的,该模型能够很好地预测测试评论。下一步是使用 pickle 保存模型和标记器,并在以后加载它,以便对用户输入评论进行预测。
[In]: with open('tokenizer.pickle', 'wb') as tk:
pickle.dump(tokenizer, tk, protocol=pickle.HIGHEST_PROTOCOL)
[In]: model_json = model.to_json()
with open("model.json", "w") as js:
js.write(model_json)
[In]: model.save_weights("model.h5")
现在我们已经保存了训练好的模型和标记器,我们可以创建类似于前面的app.py
脚本的应用程序脚本。
[In]: import os
[In]: import numpy as np
[In]: import pandas as pd
[In]: import pickle
[In]: import tensorflow
[In]: from tensorflow.keras.preprocessing.text import Tokenizer
[In]: from tensorflow.keras.preprocessing.sequence import pad_sequences
[In]: import tensorflow.keras.models
[In]: from tensorflow.keras.models import model_from_json
[In]: import streamlit
[In]: import re
[In]: os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
导入所需库后的步骤是加载记号化器和深度学习模型。
[In]: with open('tokenizer.pickle', 'rb') as tk:
tokenizer = pickle.load(tk)
[In]: json_file = open('model.json','r')
[In]: loaded_model_json = json_file.read()
[In]: json_file.close()
[In]: lstm_model = model_from_json(loaded_model_json)
[In]: lstm_model.load_weights("model.h5")
接下来,我们创建一个助手函数来清理输入审查,将其标记化,并填充序列。一旦它被转换成数字形式,我们将使用加载的 LSTM 模型来进行情绪预测。
[In]: def sentiment_prediction(review):
sentiment=[]
input_review = [review]
input_review = [x.lower() for x in input_review]
input_review = [re.sub('[^a-zA-z0-9\s]','',x) for x in input_review]
input_feature = tokenizer.texts_to_sequences(input_review)
input_feature = pad_sequences(input_feature,1473, padding="pre")
sentiment = lstm_model.predict(input_feature)[0]
if(np.argmax(sentiment) == 0):
pred="Negative"
else:
pred= "Positive"
return pred
最后,我们创建 run 函数来加载 HTML 页面,并使用 Streamlit 功能接受用户输入(类似于前面的模型部署)。唯一的区别是,这次我们只加载一个评论,而不是加载多个输入。然后,我们将这个评论传递给前面创建的情绪预测函数。
[In]: def run():
streamlit.title("Sentiment Analysis - LSTM Model")
html_temp="""
"""
streamlit.markdown(html_temp)
review=streamlit.text_input("Enter the Review ")
prediction=""
if streamlit.button("Predict Sentiment"):
prediction=sentiment_prediction(review)
streamlit.success("The sentiment predicted by Model : {}".format(prediction))
[In]: if __name__=='__main__':
run()
一旦我们在终端中使用streamlit run
命令运行app.py
文件,我们可以看到 web 服务正在本地主机端口 8501 上运行,如图 3-10 所示。
图 3-10
访问 ML 应用程序
[In]: streamlit run app.py
我们可以在端口 8501 上访问 ML 服务,并且可以看到 ML 应用程序成功运行。该页面包含三个内容。
-
提供用户评论的占位符
-
预测情绪按钮(使用模型进行预测)
-
模型的最终结果
让我们在输入框中提供一个评论并检查模型预测,如图 3-11 所示。首先,我们提供一个正面评论,并点击预测情绪按钮,如图 3-12 所示。
图 3-12
正面评论预测
图 3-11
用户输入页面
正如我们可以观察到的,模型预测的结果似乎是正确的,因为它也预测这是一个积极的评论。我们尝试进行另一项审查——这次是否定的——并测试模型预测,如图 3-13 所示。对于第二次审查,我们也通过模型得到了正确的预测。我们可以用一个更强大、训练有素和优化的模型来取代现有的模型,以获得更好的预测。
图 3-13
负面评论预测
结论
在这一章中,我们回顾了 Flask 及其不同组件的基础知识。我们还看到了使用 Flask 构建和部署机器学习模型的过程。最后,我们探索了另一个部署 ML 模型的平台 Streamlit 及其相对于 Flask 的优势。
四、使用 Docker 的机器学习部署
在过去的几年里,Docker 改变了应用程序在生产中的部署方式。应用程序架构已经从整体服务转移到微服务,对持续(正在进行的)部署有了更多的控制,不会影响大部分正在运行的应用程序。事实证明,Docker 有助于让应用程序大规模运行并始终可用。虽然距离 Docker 发布已经过去了七年多,但它最近已经得到了开发人员社区的大量关注(尤其是 DevOps 和 MLOps 团队)。大大小小的公司都在应用程序中使用 Docker。
在这一章中,我们将回顾 Docker 生态系统,看看 Docker 的不同组成部分。本章涵盖三个主要主题。首先,本章介绍了 Docker 是什么以及它为什么有用。其次,本章涵盖了不同的组件,如 Docker 映像、Docker 服务器、Docker Hub 和容器。最后,本章介绍了如何“Dockerize”整个 ML 应用程序,并使用 Docker 容器运行它。Docker 在过去的几年里变得非常受欢迎,并且有许多专门关于这个主题的书籍。我们将涵盖从机器学习的角度来看与我们最相关的主题。
Docker 是什么,我们为什么需要它?
场景 1:假设我们想在笔记本电脑或主机系统上安装一个新的应用程序。这是一个简单的过程。我们通常会从应用程序的源代码中下载安装文件,并在我们的系统中运行它。有时第一次尝试就能完美安装;然而,其他时候,我们可能会遇到不同的问题(依赖性、兼容性错误等)。),如图 4-1 所示。
图 4-1
典型应用程序安装
场景 2:假设我们在特定的环境中使用特定的库在我们的机器上编写了一些代码。当我们在本地执行它时,一切都运行得很完美,但是当我们与他人共享它进行测试时,它就坏了。发生这种情况是因为底层配置发生了变化。这可能是因为缺少依赖项、不同的操作系统或其他原因。
Docker 可以很好地处理前面两种情况,因为 Docker 可以确保在任何地方运行应用程序的环境都是相同的。如果我们能够在一台特定的机器上运行一个应用程序或某些代码,我们也可以很容易地在其他任何地方运行它。这就是 Docker 的妙处。它使安装和运行任何应用程序或程序变得容易,而不必担心底层系统上的一组依赖项和文件。用一个例子来详细说明,如果我们必须在我们的系统上安装 Redis(一个内存中的数据库),我们必须去 Redis 网站并遵循某些指示。
从输出来看,这似乎很简单,但是在每个阶段我们都可能会遇到错误,因为缺少安装 Redis 的依赖项。Docker 让这变得超级简单快捷。通过一行代码,我们能够在我们的系统上安装 Redis(在 Docker 中)。
docker container run redis
Docker 简介
Docker是一个执行操作系统级虚拟化的计算机程序,也称为容器化。”**
*容器允许我们将应用程序所需的所有文件打包到一个包中,比如库、二进制文件和其他依赖项。这样,我们的应用程序可以在任何机器上运行,并具有相同的行为。核心思想是能够复制应用程序的行为,而不管它在哪个主机上运行。例如,当我们构建任何应用程序时,我们通常在开发环境中编写代码,然后在测试环境中测试它。就生产环境而言,这些环境通常互不相同,因此,由于操作平台不同,开发人员会遇到多种问题。Docker 确保如果应用程序在开发环境中成功运行,它也可以安全地部署在生产环境中,如图 4-2 所示。
图 4-2
应用程序部署阶段
Docker 与虚拟机
您可能在某个时候使用过虚拟机。当我们想要创建不同的环境并使用不同的操作系统运行不同的应用程序时,虚拟机非常强大。虚拟化层有一个虚拟机管理程序来创建虚拟机。一旦在主机系统上创建了虚拟机,每个虚拟机都将拥有自己的一套操作系统、库、二进制文件和应用程序。如图 4-3 所示,虚拟机会消耗一定量的内存和硬件资源,而这些资源是无法与其他虚拟机共享的。
图 4-3
基于虚拟机管理程序的虚拟化
在 Docker 中没有管理程序;取而代之的是 Docker 服务器,它将每个运行在单独容器中的 app 隔离开来,如图 4-4 所示。说到底,Docker 应用程序是使用 Linux 操作系统的轻量级虚拟机;这样做的好处是 Docker 只根据需求使用内存和硬件资源,而在虚拟机中,我们必须在创建虚拟机之前分配资源,并且资源不能在虚拟机之间共享。这是资源的浪费,增加了成本(在云上)。
图 4-4
基于 Docker 的虚拟化
Docker 组件和有用的命令
既然我们已经介绍了 Docker 及其用途,我们可以看看 Docker 生态系统的其他组件。Docker 实际上是一个由 Docker 镜像、Docker CLI、Docker 服务器和 Docker Hub 等多个组件组成的平台,如图 4-5 所示。我们将详细研究其中的每一个,以了解整个 Docker 生态系统以及组件之间的交互方式。
图 4-5
Docker 生态系统
Docker 图像
在进入 Docker 映像之前,它本质上是 Docker 容器的蓝图,先了解一下 Docker 文件是有意义的。一个 Dockerfile 是 Docker 容器生命周期的初始化点。我们首先需要一个 Docker 文件来构建 Docker 映像,然后基于该映像运行一个容器。
Dockerfile
当我们使用 Docker 图像时,会出现两种情况。
-
使用公共 Docker 图像
-
构建客户 Docker 图像
在第一种情况下,我们可能不需要创建 Dockerfile,因为我们可以简单地从 Docker Hub 中提取映像,并使用它创建/运行一个容器,而无需对映像进行任何更改。例如,如果我们想使用 Docker 运行 Nginx(一个 web 服务器),我们只需提取 Nginx Docker 映像,不做任何更改就可以运行它。然而,在第二种情况下,我们希望根据我们的特定需求定制图像,我们需要创建一个 docker 文件。
Dockerfile 是一个简单的文本文件,没有任何扩展名,固定名称为Dockerfile
。它包含一组命令(根据需求不同,每个文件会有所不同),执行这些命令来构建 Docker 映像。创建 Dockerfile 的过程非常简单,如图 4-6 所示。我们将查看 docker 文件来理解这些命令。
图 4-6
Dockerfile 过程
我们从我们想要利用的基础映像开始,而不是从零开始。之后,我们有了与文件、依赖项和环境变量相关的附加命令。最后,Dockerfile 包含在运行容器时应该首先执行的启动命令。
Dockerfile 命令
尽管 docker 文件中可以使用许多命令( https://kapeli.com/cheat_sheets/Dockerfile.docset/Contents/Resources/Documents/index
),但大多数情况下,以下命令足以满足基本需求。想法是从新映像的基础映像开始,并向新映像添加依赖项和需求。
以下命令用于创建 Dockerfile 文件。我们将在一个示例的帮助下查看这些内容,在这个示例中,我们创建了一个 over 文件来运行 Node.js 应用程序。
-
FROM
-
COPY
-
WORKDIR
-
EXPOSE
-
RUN
-
CMD OR ENTRYPOINT
以下命令为 Dockerfile 提供了基础映像,它需要成为映像其余部分的起点:
FROM node:alpine
对于 Node.js 应用程序,我们提到了官方的 Node.js 基础映像。FROM
总是 docker 文件中的第一个命令,因为它后来成为整个容器的基础层。
下一个命令是将工作目录或项目文件夹的内容从主机系统添加或复制到 Docker 容器文件夹:
COPY . /app/usr/nodejs
我们可以使用COPY
命令在 Docker 中创建一个新的文件夹,或者复制同一个工作文件夹中的所有文件和脚本。在前一个例子中,我们将所有应用程序文件复制到 Docker 容器中一个名为nodejs
的新文件夹中。核心思想是在运行时提供 Docker 中所有可用的应用程序文件。
既然我们已经复制了 Docker 文件夹中的所有内容,那么从那个特定的文件夹:nodejs
运行应用程序是有意义的。因此,我们将工作目录更改为 Docker 容器中的nodejs
文件夹。我们利用WORKDIR
来设置应用程序的目录。
WORKDIR /app/usr/nodejs
Docker 容器有自己的一组网络端口,因此为了响应传入的请求,我们需要在一个端口上公开应用程序。EXPOSE
命令允许我们让外部请求在给定的端口上访问应用程序。
EXPOSE 5000
RUN
命令帮助我们安装一组依赖项和库,以便在容器内运行应用程序。我们没有单独安装每个依赖项,而是使用了一个包含所有特定版本所需文件的requirement.txt
文件。
RUN pip install -r requirement.txt
Dockerfile 中的最后一个命令是CMD
,它是容器的启动命令。
CMD ["yarn", "start"]
坞站集线器
Docker Hub ( https://hub.docker.com/
)对于图像就像 GitHub 对于代码库一样。这是一个公共和私人图像的集合。它包含官方图像以及为不同应用定制的公共图像。例如,我们获得 Postgres、Spark、Ubuntu 和其他类似应用程序的官方图片。它允许用户使用官方映像直接运行容器并使用应用程序。我们需要一个帐户来访问 Docker Hub。它还允许我们从本地系统提取和推送 Docker 映像。一旦保存在 Docker Hub 上,我们就可以在任何给定的环境中的任何时间点使用特定的图像。现在 Docker 文件已经创建,我们也看到了 Docker Hub,我们可以研究 Docker 客户机和服务器的细节,并使用它们从我们前面创建的 Docker 文件创建 Docker 映像。
Docker 客户端和 Docker 服务器
Docker 的生态系统中有很多组件;其中最重要的两项如下:
-
客户端坞站(CLI)
-
docker server 守护进程
Docker CLI 是在任何系统上与 Docker 交互的网关。我们可以通过 CLI 向 Docker 服务器发出指令或命令,它通过 Docker 服务器与它们进行通信,只需稍作修改。Docker CLI 也是我们可以看到容器输出或登录到正在运行的容器的地方。要查看关于 Docker 客户端和服务器的更多信息,我们可以简单地在我们的终端中运行docker version
,如图 4-7 所示。
图 4-7
Docker 服务器信息
另一方面,docker server
执行所有与映像、网络、卷和容器相关的任务。基本上,它在幕后完成所有繁重的工作。如果图像出现在本地,它会查看这些图像;否则,它会从 Docker Hub 下载它们。为了更好地理解这一点,我们来看一个例子。如果我们需要运行 Nginx 服务器,我们将运行的命令是docker container run nginx
。
为了使用 Docker 运行 Nginx 服务器,需要遵循一系列步骤。当我们使用 CLI 向 Docker 发出命令时,它会被传递给 Docker 服务器,后者会在本地系统上查找特定的映像。如果映像在本地可用,它会使用该特定映像初始化并运行容器,否则它会访问 Docker Hub 并提取该特定映像(在本例中为 Nginx 映像),如图 4-8 所示。
图 4-8
Docker 图像提取
一旦图像保存在本地图像缓存中,它就不必两次提取相同的图像,下次使用该图像运行容器时,它会被重用。在运行 node.js 的示例中,我们只需从 Dockerfile 构建图像,如图 4-9 所示。
图 4-9
docker image from dockerfile 停靠影像
我们看到了从 Docker 文件构建 Docker 映像的过程。它本质上包含了容器应该操作的信息。Docker 映像就像文件系统的快照——应用程序需要运行的一组目录和文件。它们还包含要执行的初始化命令,而容器的实例使用映像运行。这也可能是可选的,因为我们也可以通过在运行时提供一个新命令来覆盖默认的启动命令。因此,映像是核心文件,它包含了与需要运行的应用程序相关的所有关键需求和配置信息。
不用说,它使用运行应用程序所需的最小配置集,这使得 Docker 快速且易于使用,但有时应用程序本质上可能相当复杂,我们最终会为应用程序的各个组件创建不同的 Docker 映像,以使它们能够相互通信,从而成功执行。如前所述,映像充当运行特定应用程序所需的所有文件和目录的来源。如果映像没有正确包含相关的配置文件或依赖项,我们就不能使用 Docker 运行应用程序,因为它依赖于正在执行的这些文件。例如,假设我们试图覆盖 Nginx Docker 容器的默认命令,转而运行ls
命令。如图 4-10 所示,我们看到这个容器中已经存在一堆文件夹。这些文件夹是运行 Nginx 服务器所需的默认配置文件和依赖项。这些是 Nginx Docker 基本映像的一部分,以确保该映像的实例在任何地方都能成功运行。
图 4-10
访问正在运行的容器
码头集装箱
既然我们了解了什么是图像以及 Docker 图像都包含什么,我们就可以继续讨论容器了。该映像用于启动容器,这些容器不过是特定映像的运行实例。可以从同一个映像创建多个容器,使同一个应用程序有多个实例运行,如图 4-11 所示。
图 4-11
使用相同 Docker 图像的多个容器
实际上,容器是在特定环境中运行的打包应用程序。Docker 容器就是从那个环境开始的。其实现方式是从主机资源中分配一些资源。我们已经知道,Docker 映像充当文件系统快照,并且还包含启动命令。一旦我们将这个映像传递给 Docker CLI,它就会与 Docker 服务器通信,用这个特定的映像启动容器。因此,Docker 服务器确保在容器内分配的资源(RAM、CPU、硬盘、网络)内创建类似的文件系统,如图 4-12 所示。一旦初始化,特定的应用程序就像主机系统上的任何其他应用程序一样在容器内运行。
图 4-12
图像到容器
如前所述,容器是一个进程或一组进程,它利用分配给它的一组特定资源来运行特定的应用程序。运行一个容器实际上有两个部分。
-
创建容器
-
运行容器
对于第一部分,我们获取基本映像,并根据映像的默认文件系统和配置文件获取分配给容器的不同资源。记住,我们不执行任何启动或覆盖命令来运行容器,如图 4-13 所示。
图 4-13
Docker 容器初始化
在第二部分中,一旦分配了资源,运行容器包括执行启动命令或覆盖命令以成功运行应用程序。-a
表示附加容器 ID,以查看执行后的输出,如图 4-14 所示。
图 4-14
运行 Docker 容器
docker run
命令依次执行前两个阶段。
一些有用的容器相关命令
我们可以使用以下命令列出所有正在运行的容器:
[In]: docker container ps
如果我们想查看到目前为止使用的所有容器,我们可以将–all
添加到前面的命令中:
[In]: docker container ps –all
如果我们想从 Docker Hub 中提取一个特定的图像,并使用它根据我们的特定需求定制一些东西,我们可以使用pull
命令。
[In]: docker pull redis
为了使用特定的映像运行容器,我们使用了docker run
命令。
[In]: docker run
正如我们在这个例子中已经看到的,我们可以使用build
命令从 Docker 文件创建一个 Docker 映像。
[In]: docker build
如果我们想将 Docker 映像推送到 Docker Hub 平台,我们可以使用带有映像唯一名称的docker push
命令。
[In]: docker push
如果我们想停止一个正在运行的容器,我们可以使用docker stop
命令。如果集装箱在十秒钟内没有停止,则docker kill
命令将被激活,集装箱将被立即移走。
[In]: docker stop
如果我们想要删除一个容器,我们可以使用rm
命令来删除使用容器 ID 的特定容器。
[In]: docker rm
如果我们想删除一个特定的 Docker 图像,我们可以使用rmi
命令。有时,我们可能不得不使用docker rmi –force
来强制删除图像。
[In]: docker rmi
exec
命令帮助我们进入一个正在运行的容器。
[In]: docker exec
命令通过移除所有停止的容器和悬挂的图像来清理你的系统,并释放大量空间。
[In]: docker system prune
使用 Docker 的机器学习
在本节中,我们将从头构建一个分类模型,并尝试使用 Flask 应用程序部署它。与前一章的唯一区别是将整个应用程序 Dockerize 并在任何平台上运行。我们还将利用库 Flasgger 来处理应用程序的 UI 部分,以便更直观地使用结果。首先,让我们在本地系统中创建一个名为docker_app
的新文件夹。
我们将按照以下步骤执行该流程:
-
训练 ML 模型。
-
保存并导出 ML 模型。
-
创建一个包含 UI 层的 Flask 应用程序。
-
为应用程序构建自定义 Docker 图像。
-
使用 Docker 容器运行应用程序。
-
停下集装箱。
步骤 1:训练机器学习模型
这是我们将在特定数据集上训练机器学习模型的第一阶段。由于总体想法是部署 ML 应用程序,所以重点是将应用程序容器化,而不是提高模型的准确性。因此,我们不会涉及太多的特征工程或复杂的 ML 模型。更确切地说,一个简单的逻辑回归或随机森林模型就足够了。话虽如此,我们总是可以用不同的、更好的模型替换给定的模型,而不会影响流程的其余部分。
在这种情况下,我们拥有的数据集来自一个关于客户历史交易的在线平台。它包含数据点,如年龄、浏览的总页数以及客户是新客户还是回头客。输出变量包含客户是否在线购买了产品。因此,我们将训练一个简单的逻辑回归模型来对测试数据进行预测,然后出于部署目的将其导出。
我们首先导入两个基本库 numpy 和 pandas,然后研究数据集。
[In]: import numpy as np
[In]: import pandas as pd
[In]: df = pd.read_csv('online_sales.csv')
[In]: df.shape
[Out]:(316200, 4)
在总共有 4 列的数据集中,我们有大约 30 万条记录。我们来看看数据是怎么看的,阶级平衡。
[In]:df.head()
[Out]:
[In]: df.converted.value_counts()
[Out]:
0 306000
1 10200
我们可以清楚地看到,在这个数据集中有一个倾斜的目标类,通常需要通过一些欠采样/过采样技术来处理。但是,我们并不希望在这个练习中找到最佳的模型精度,因此我们将使用我们拥有的所有数据来构建模型。我们还可以在模型训练之前检查数据是否包含要处理的任何缺失值。
[In]: df.info()
[Out]:
由于数据集中的任何列都没有任何缺失值,因此我们可以继续将数据分为定型集和测试集。
[In]: input_columns = [column for column in df.columns if column != 'converted']
[In]: print (input_columns)
[Out]: ['age', 'new_user', 'total_pages_visited']
[In]: output_column = 'converted'
[In]:print (output_column)
[Out]: converted
[In]: X = df.loc[:,input_columns].values
[In]: y = df.loc[:,output_column]
[In]: print (X.shape, y.shape)
[Out]: (316200, 3) (316200,)
[In]: from sklearn.model_selection import train_test_split
[In]: from sklearn.linear_model import LogisticRegression
[In]:X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3, random_state=555, stratify=y)
[In]: print(np.sum(y_train))
[In]: 7140
[In]: print(np.sum(y_test))
[Out]: 3060
现在我们已经形成了训练和测试数据集,我们可以继续进行模型训练了。
Note
在理想的 ML 场景中,在模型训练之前建议进行适当的数据探索和特征工程。
[In]:logreg=LogisticRegression(class_weight='balanced').fit(X_train,y_train)
[In]: logreg.score(X_test, y_test)
[Out]: 0.93
[In]: predictions=logreg.predict(X_test)
[In]: from sklearn.metrics import confusion_matrix
[In]: from sklearn.metrics import classification_report
[In]:print(classification_report(y_test,predictions,target_names=["Non Converted", "Converted"]))
[Out]:
我们可以观察到,模型的整体准确率没有那么差,召回率也不错。根据具体需要,可以进一步提高精度。使用 Docker 运行 ML 的整个过程的第一步到此结束。
步骤 2:导出训练好的模型
既然我们已经训练好了模型,我们需要使用 pickle/joblib 保存它,以便在以后的预测中重用它。在本练习中,我们将看到使用 ML 模型的两种预测。
-
单一客户预测
-
群体预测(多个客户)
我们有一个单独的测试数据集,可以预测一组客户,而不仅仅是一个客户。让我们看看当我们使用 Docker 运行应用程序进行预测时,结果是什么样的。
[In]: import pickle
[In]: pickle_out = open("logreg.pkl","wb")
[In]: pickle.dump(logreg, pickle_out)
[In]: pickle_out.close()
让我们加载训练好的模型,看看它是否能很好地预测测试数据以及单个客户的值。
[In]: pickle_in = open("logreg.pkl","rb")
[In]: model=pickle.load(pickle_in)
下面是对单个客户的预测:
[In]: model.predict([[45,0,5]])[0]
[Out]: 0
以下是对一组客户的预测:
[In]: df_test=pd.read_csv('test_data.csv')
[In]: predictions=model.predict(df_test)
[In]: print(list(predictions))
[Out]: [0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
正如我们所观察到的,该模型似乎在为单个客户以及一组客户进行预测。现在我们可以进入下一步,构建一个 Flask 应用程序来运行这个模型。
步骤 3:创建一个包含 UI 的 Flask 应用程序
在前一章中,我们介绍了如何使用 Flask 框架轻松部署 ML 模型。类似地,在这种情况下,我们将构建一个 Flask 应用程序和 Flasgger。我们首先导入运行 Flask 应用程序所需的库。
[In]: from flask import Flask, request
[In]: import numpy as np
[In]: import pickle
[In]: import pandas as pd
[In]: import flasgger
[In]: from flasgger import Swagger
我们创建了 Flask 应用程序,并将其包装在 Swagger 周围。它做了所有繁重的工作来以布局良好的方式表示结果。这可以节省大量编写 HTML 和 CSS 的时间和精力。
[In]: app=Flask(__name__)
[In]: Swagger(app)
接下来,我们加载用于预测的训练模型。由于我们将进行两种类型的预测(单个客户和一组客户),我们将需要使用 get 请求和 post 请求在我们的应用程序中创建两个独立的函数。对于单客户预测,我们将使用 get 请求,因为我们将从用户处获取输入值,而对于组预测,我们将使用 post 请求来发布预测的测试数据集。这里要记住的一件关键事情是向应用程序提供关于模型中使用的要素的参数信息,以正确地进行预测。最后一部分是提供运行这个应用程序的本地主机 IP 地址。
[In]: pickle_in = open("logreg.pkl","rb")
[In]: model=pickle.load(pickle_in)
[In]: @app.route('/predict',methods=["Get"])
[In]: def predict_class():
"""Predict if Customer would buy the product or not.
---
parameters:
- name: age
in: query
type: number
required: true
- name: new_user
in: query
type: number
required: true
- name: total_pages_visited
in: query
type: number
required: true
responses:
500:
description: Prediction
"""
age=int(request.args.get("age"))
new_user=int(request.args.get("new_user"))
total_pages_visited=int(request.args.get("total_pages_visited"))
prediction=model.predict([[age,new_user,total_pages_visited]])
print(prediction[0])
return "Model prediction is"+str(prediction)
[In]: @app.route('/predict_file',methods=["POST"])
[In]: def prediction_test_file():
"""Prediction on multiple input test file
.
---
parameters:
- name: file
in: formData
type: file
required: true
responses:
500:
description: Test file Prediction
"""
df_test=pd.read_csv(request.files.get("file"))
prediction=model.predict(df_test)
return str(list(prediction))
[In]: if __name__=='__main__':
app.run(debug=True,host='0.0.0.0')
创建 Flask 应用程序和 UI 层的第 3 步到此结束。我们现在准备进入 Docker 并构建一个映像来运行这个应用程序。
步骤 4:构建 Docker 映像
我们现在将创建一个 Docker 文件,并提及使用 Docker 运行此应用程序的所有步骤。我们首先向需要从 Docker Hub 中提取的 Docker 服务器提供基本映像(如果本地还没有)。
FROM continuumio/anaconda3:4.4.0
下一步,我们将所有文件和内容从本地目录(我们构建 ML 模型和 Flask 应用程序的地方)复制到 Docker 目录(我们也可以创建一个新的或当前的 Docker 目录)。在这种情况下,我们创建一个名为usr/ML/app
的新目录。
COPY . /usr/ML/app
下一个命令是公开 Docker 的端口 5000 来运行这个应用程序。基本上,当请求来自用户时,主机系统会将这个请求路由到 Docker 的端口 5000,应用程序将在那里运行。我们还必须在主机系统和 Docker 容器之间进行显式端口映射,同时运行容器来正确路由请求和访问预测。
EXPOSE 5000
下一个命令是将 Docker 的当前工作目录更改为我们已经从本地系统复制了所有文件的目录(Flask/ML model/data 等。).
WORKDIR /usr/ML/app
下一步是安装所有的依赖项和所需的库,以便成功运行应用程序。我们有一个requirement.txt
文件,其中包含所需的库和版本。
RUN pip install -r requirements.txt
Docker 文件中的最后一个命令总是启动命令,在这种情况下,我们希望 Docker 运行我们在上一步中构建的 Flask 应用程序。
CMD python flask_api.py
这个命令列表完成了我们的 Docker 文件,现在可以将其转换成 Docker 映像并作为容器运行,这是下一步。
步骤 5:运行 Docker 容器
在流程的这一步中,我们从上一步中创建的 Docker 文件构建 Docker 定制映像,并运行容器。我们必须转到终端中所有文件都存在的同一个目录中。在终端中,我们运行docker build
命令从 Dockerfile 构建 Docker 映像。
[In]: docker build -t ml_app_docker .
可以将图像标记为特定的名称,以帮助运行容器。在这种情况下,我们将其标记为ml_app_docker
。一旦 Docker 映像开始构建,Docker 内部就会发生一系列的步骤。第一步是 Docker 服务器将寻找基本映像。如果基本映像在本地系统中不可用,Docker 服务器会从 Docker Hub 中提取映像,根据映像大小可能需要一些时间。如图 4-15 所示,从 Docker Hub 中获取基础映像。
图 4-15
从 Docker 文件构建 Docker 映像
一旦基本映像被完全提取出来,Docker 就会用该基本映像创建一个临时容器。它是临时容器的原因是 Docker 应用下一步并构建一个新容器来满足下一个需求。如图 4-16 所示,我们可以观察到,在每个阶段结束时,Docker 都会向我们显示一个新的容器 ID,在运行 Dockerfile 中的最后一个命令后,它会向我们提供最终的容器 ID。
图 4-16
Docker 服务器信息
文件requirements.txt
包含了所有的依赖项和库,因此 Docker 也会安装所有的依赖项和库,如图 4-17 所示。
图 4-17
安装依赖项
一旦 docker 文件中的所有命令都被执行,我们就有了从 docker 文件构建的最终图像,如图 4-18 所示,我们可以启动容器来运行我们的 ML 应用程序。
图 4-18
最终 Docker 图像
这里要记住的关键是进行显式端口映射,以便将请求从主机路由到 Docker 端口。我们利用-p
并将主机端口 5000 映射到 Docker 容器端口 5000。
[In]: docker container run -p 5000:5000 ml_app_docker
[Out]:
如图 4-19 所示,app 成功启动,一切都在 Docker 容器内运行。要访问该应用程序,我们只需前往http://localhost://5000/apidocs
加载 Swagger UI 页面,如图 4-20 所示。
图 4-20
访问 ml-app 的 Swagger API
图 4-19
访问正在运行的 ml-app
应用程序中有两个选项卡。
-
得到
-
邮政
基于 get 请求的预测适用于单客户预测,而 Post 选项卡适用于测试数据预测(客户组)。单击 Get 选项卡后,我们可以看到提供输入参数的选项,需要根据这些参数进行预测。这些参数与我们在模型培训中使用的以及在 Flask 应用程序代码中调用的参数相同。右上角包含一个“试用”选项卡,允许我们填写输入参数的值,如图 4-21 所示。
图 4-21
获取请求
我们可以为一个测试客户填写所有三个参数的值,如图 4-21 所示,然后点击执行选项卡。在执行调用时,请求会发送到应用程序,然后由模型进行预测。模型预测的结果显示在页面的预测部分。它还提供了访问结果的替代方法,例如使用curl
命令或 URL,如图 4-22 所示。
图 4-22
模型预测法
下一个可以完成的预测是通过 post 请求对一组客户(测试数据)进行预测。我们需要上传如图 4-23 所示的包含相同参数的测试数据文件(必须是相似的格式)。模型进行预测,执行后显示结果,如图 4-24 所示。
图 4-24
多重预测
图 4-23
上传测试数据
步骤 6:停止/终止正在运行的容器
运行应用程序后剩下的最后一步是停止正在运行的容器。这可以使用运行容器上的docker stop
或kill
命令来完成。我们可以使用docker ps
命令查看正在运行的容器列表,并可以选择正在运行的容器 ID 来停止它。
[In]: docker ps
[In]:docker kill <Container_ID>
结论
在这一章中,我们回顾了 Docker 及其生态系统的基础知识。我们还讨论了不同的 Docker 命令以及构建定制 Docker 映像的过程。我们还使用 Docker 部署了一个机器学习应用程序,并将其托管在 Flask 应用程序上。*
五、使用 Kubernetes 的机器学习部署
在前一章中,我们看到了如何使用 Flask 来封装和部署一个应用程序。在本章中,我们将使用编排平台(Kubernetes)部署相同的 ML 应用程序。本章涵盖两个主要主题。它首先介绍了 Kubernetes 平台的基础知识以及它如何处理部署。然后这一章讲述了如何使用 Kubernetes 部署 ML 应用程序。更具体地说,本章涵盖以下内容:
-
什么是 Kubernetes
-
谷歌云平台
-
使用 Kubernetes 的 ML 模型部署
Kubernetes 是一个用于部署和管理大型容器的开源容器编排引擎。它具有多种杠杆来管理调度、集群、部署和负载平衡,并且在使用容器运行微服务方面具有更多功能。Kubernetes 最初是在谷歌创建的,后来捐赠给了云本地计算基金会(CNCF)。它现在由 CNCF 管理和维护,在全球拥有强大的社区支持和用户。使用 Kubernetes 的主要优势是能够运行大量的容器,而不用担心部署和集群管理细节。举个例子,Google 使用 Kubernetes 为用户运行大约 25 亿个容器。尽管有其他容器编排平台,如 Apache Mesos 的 Docker Swarm 和 Marathon,但 Kubernetes 很快成为用户的默认 COE,原因有两个。首先,它已经在极端情况下经过了反复测试(自从它在谷歌开发以来)。第二,它有丰富的特性和复杂的底层架构,允许它运行高度可伸缩的服务。
Kubernetes 可在不同的云平台上使用,如谷歌云平台的谷歌 Kubernetes 引擎(GKE)、AWS EC2 容器服务和微软 Azure 容器。在这一章中,我们将利用 GKE 来部署 ML 应用程序。可以使用类似的方法在 AWS 和 Azure 上部署它,但 Google 为其用户提供了一些免费的积分,可以很容易地用来完成这一部署。
不可思议的建筑
幕后的 Kubernetes 像任何其他分布式应用程序一样工作。它的工作原理是主从节点。主节点向负责计算和执行任务的工作节点发出命令/指令。在 Kubernetes 的情况下,也可以有一个以上的主人。工作节点通常是用于计算和存储数据的虚拟机。如果我们观察 Kubernetes 的架构,如图 5-1 所示,我们可以观察到它有两个主要部分。
图 5-1
不可思议的建筑
-
掌握
-
工人
用于与 Kubernetes 对象交互的 CLI 工具称为kubectl
。
忽必烈大师
Kubernetes master 负责运行整个集群,并确保 pods 的调度和供应(参见“Worker Nodes”一节)。像分布式应用程序的任何其他主节点一样,它负责通信和跟踪工作节点的状态,并在工作节点出现故障时提供一个健康的节点。在 Kubernetes master 中,有四个主要组件。
-
API 服务器:这个组件允许我们与不同的 Kubernetes 对象进行交互。它为不同的对象(如 pod、部署、负载平衡器等)验证和配置 API。它允许我们添加、删除、更新和显示关于 Kubernetes 对象的信息。如前所述,我们使用
kubectl
与 API 服务器进行交互。这有点类似于我们在 Docker 中看到的(Docker CLI 和 Docker server 之间的关系)。 -
调度器:master 内部的另一个重要组件是调度器。顾名思义,它负责根据声明的配置(如内存大小、CPU 内核等)调度集群内部的 pod(工作节点)。
-
控制管理器:该组件负责确保集群健康,并且所需数量的工作节点处于健康状态,特定的 pod 在其中运行。
-
etcd :该组件以键值的形式捕获集群的当前状态。它是一个分布式轻量级的键值数据库。
工作节点
工作节点是云中的典型虚拟机或数据中心的物理服务器。它们负责计算和存储正在运行的应用程序的数据。每个 worker 节点必须有一个容器运行时(Docker/rkt ),以便能够在其中运行容器。每个工作节点能够在其中运行一个或多个 pod。豆荚是库伯内特最基本的单位。Pods 本质上是 Kubernetes 中的调度单元,包含一个或多个容器。理想情况下,每个 pod 应该包含一个容器,但是在依赖容器的情况下,它们可以在同一个 pod 中运行。pod 就像包裹容器的包裹物,允许我们与容器内部的容器进行交互。worker 节点中有两个额外的组件帮助与 Kubernetes 主节点通信。
-
kubelet
:这是在每个工作节点上运行的主节点代理。它确保在 pod 内部运行的容器符合提交给 API 服务器的规范。如果观察到任何更改或任何 pod 关闭,它会根据配置信息在同一节点上启动一个新的 pod,并使用新的容器。 -
kube-proxy
:该组件负责维护集群的分布式网络配置。它管理节点、单元和单元内部运行的容器的网络配置,并确保外部世界可以访问正在运行的服务。
ML 应用程序使用 kubernetes
现在我们对 Kubernetes 平台有了基本的了解,我们可以进入本章的第二部分,使用 Kubernetes 部署一个 ML 应用程序。要使用 Kubernetes 部署我们的应用程序,第一步是将本地代码推送到 Git repo 中,以便我们稍后可以克隆它。在理想情况下,数据通常存储在 Google 存储桶中,但是为了简单起见,我们将所有东西都打包在 Docker 映像中,因为我们没有大型数据集。如前所述,我们将使用 GCP 来利用谷歌提供的免费积分,以使用云资源,如谷歌 Kubernetes 引擎。
谷歌云平台
谷歌云平台是一个巨大的平台,包含许多工具和服务,可以满足各种需求。不可能涵盖 GCP 的所有方面,因此我们将重点关注某些服务来部署 ML 应用程序。我们将使用一些组件,如 GKE 和谷歌容器注册(GCR)为我们的部署。使用 GCP 的先决条件是创建一个谷歌账户,并通过访问 https://console.cloud.google.com/
登录谷歌云平台。
Note
在 GCP 使用的每一种工具和服务都是有成本的,因此我们鼓励读者在 Google 控制台的详细页面上了解更多关于费率的信息。但是,对于这种部署,Google 提供的免费积分就足够了。
登录后,第一步是在 Google 控制台中创建新项目。这就是我们如何为这个特殊的项目分配单独的资源。我们可以根据自己的喜好给这个项目命名,如图 5-2 所示。例如,在这种情况下,我将这个新项目命名为ml-model
,如图 5-3 所示。
图 5-3
GCP 的新项目
图 5-2
GCP 项目
新项目一旦创建,就会反映在主页的项目信息页签上,如图 5-4 所示。它将包含一个项目名称和一个项目 ID。还有一个选项,如果有更多的人在这个项目上工作,可以向这个项目添加更多的人。
图 5-4
GCP 项目信息
成功创建项目后,我们需要启用部署应用程序所需的其他服务。我们需要选择的第一个服务是容器注册 API。GCP 的容器注册中心允许我们访问 Google 项目中的 Docker 图像,并将其部署到 Kubernetes 上。为此,我们转到 GCP 菜单选项卡并选择容器注册表,如图 5-5 所示。容器注册表下有两个选项。
-
形象
-
设置
我们需要选择图像,默认情况下,如果我们查看图像内部,它应该显示一个空窗格,如图 5-6 所示。这是因为我们还没有在容器注册表中推送任何图像。如果您以前为其他应用程序使用过 Container Registry,您可能会有以前的图像,但是对于新用户,它不应该包含任何以前的图像。
图 5-5
GCP 的集装箱注册菜单项
要使用 Docker 映像并将其推送到容器注册表,我们应该通过选择 enable 选项来启用容器注册表 API,如图 5-7 所示。
图 5-7
启用容器注册 API
图 5-6
容器注册表中的图像
我们必须启用的下一个 API 是针对 Kubernetes 引擎本身的。在主菜单选项卡上选择 APIs & Services 选项,选择 Dashboard,如图 5-8 所示。
图 5-8
启用 API 和服务项目
仪表板将提供对 GCP 上整个 API 库的访问,我们现在可以搜索特定于 Kubernetes 的 API,如图 5-9 所示。
图 5-9
API 库
一旦我们搜索了 Kubernetes API,我们就可以选择启用 Kubernetes 引擎 API,如图 5-10 所示。继续并单击启用选项;完全启用它可能需要几分钟时间。
图 5-10
库柏引擎 API
现在我们已经创建了一个新项目并启用了所需的服务,我们可以开始 Google Cloudshell 中的配置步骤了。我们可以通过点击窗口右上角的终端图标来打开 Google Cloudshell,如图 5-11 所示。
图 5-11
启用 Google Cloudshell
一旦终端打开,第一步就是在源代码/数据可用的地方克隆 Git repo。在这种情况下,我们克隆了docker_flask_gke.git
回购,如图 5-12 所示。
图 5-12
克隆应用程序文件
为了确认一切都已正确设置,我们进入新的克隆文件夹,运行一个快速的ls
命令,看看文件和代码是否在 Google Cloudshell 文件夹中。下一步是创建一些环境变量来保持部署的一致性。我们声明项目 ID 变量,这是我们在开始时创建的项目 ID。下一步是使用目录中的 Docker 文件构建 Docker 映像。由于格式与我们在本地系统上使用的格式相同,我们可以利用相同的 Docker 文件来构建新的 Docker 映像。我们使用docker build
命令并用包含项目 ID 的名称来标记它。构建过程需要一些时间(取决于互联网带宽)。它将运行与我们在上一章看到的相同的步骤来构建 Docker 映像,如图 5-13 所示。
[In]: export PROJECT_ID=ml-model-123456
[In]: docker build -t gcr.io/${PROJECT_ID}/ml_app:v1 .
图 5-13
建立码头工人形象
在构建过程中,它将检查安装requirement.txt
文件中提到的依赖项的所有必要步骤,例如设置工作目录,如图 5-14 所示。
图 5-14
执行 Dockerfile 命令
我们可以通过使用docker images
命令列出 Docker 映像来确认映像已经成功构建;新建的图像应该显示出来,如图 5-15 所示(因为这个项目没有以前的 Docker 图像)。
图 5-15
Docker 图像
[In]: docker images
[Out]:
下一步是通过使用gcloudauth
命令并将之前创建的 Docker 映像推送到 Google 容器注册中心来提供 Google 身份验证。我们利用docker push
命令并在 Google 容器注册表中提供它的位置来上传 Docker 图像,如图 5-16 所示。
图 5-16
将码头工人形象推向 GCR
[In]: gcloudauth configure-docker
[In]: docker push gcr.io/${PROJECT_ID}/ml_app:v1
根据图片大小,推送至 Google 容器注册表可能需要一些时间。一旦完成,我们可以打开容器注册表下的图像,我们上传的 Docker 图像应该出现在ml_app
文件夹下,如图 5-17 所示。
图 5-17
GCR 中的坞站图像
既然我们已经将 Docker 映像推送到容器注册中心,我们就可以设置其他配置了。我们将项目设置为项目 ID,将计算区域设置为 us-central1,并创建一个名为ml-cluster
的小型双节点群集(选择更高配置的群集可能会花费更多)。
[In]: gcloud config set project $PROJECT_ID
[In]: gcloud config set compute/zone us-central1
[In]: gcloud container clusters create ml-cluster --num-nodes=2
如果我们转到 Kubernetes 引擎选项并选择集群,我们将很快看到一个新的集群启动(ml-cluster
),它有一个主节点和两个工作节点,如图 5-18 所示。
图 5-18
不可思议的群集
既然集群已经启动并运行,我们可以部署 Docker 容器来使用我们之前构建的基本 Docker 映像运行 ML 应用程序。我们使用了create deployment
命令,并将特定的图像位置作为输入进行传递。我们需要做的另一件事是在端口 5000 上公开部署的应用程序。一旦部署完成,我们就可以使用get service
命令访问正在运行的服务,如图 5-19 所示。
图 5-19
GCR 中的坞站图像
[In]: kubectl create deployment ml-app --image=gcr.io/${PROJECT_ID}/ml_app:v1
[In]: kubectl expose deployment ml-app --type=LoadBalancer --port 80 --target-port 5000
Kubernetes 显示了外部 IP 地址,通过该地址可以访问正在运行的应用程序。我们可以简单地转到那个外部 IP 地址并添加apidocs
到其中(以访问 Swagger API),如图 5-20 所示。
图 5-20
从外部访问应用程序
我们可以通过将一些虚拟值作为输入传递给模型并单击执行按钮来测试应用程序是否正常运行,如图 5-21 所示。
图 5-21
ML 应用程序预测
正如我们所看到的,我们有模型预测,并且该应用程序使用 Kubernetes 在 Google 云平台上成功部署。在部署应用程序时,会出现各种组件,如模型管理、负载平衡、安全性和其他组件,但核心思想是提供一个框架,以便您可以了解该流程,并根据应用程序的复杂性向该方法添加更多杠杆。最后,我们还可以通过使用一些kubectl
命令来查看部署细节。例如,我们可以通过使用get deployment
命令来获取活动吊舱,如图 5-22 所示。同样的信息可以在 Kubernetes 引擎选项内的工作负载选项卡上查看,如图 5-23 所示。
图 5-23
已部署应用的状态
图 5-22
在库比涅斯激活 pods
如果需要,可以使用 Kubernetes 中的scale deployment
命令增加或减少正在运行的应用程序的副本数量。但是,您还需要增加群集中的节点数量,以满足运行那么多应用程序副本的最低要求。在这种情况下,由于我们使用的是只有两个节点的小型集群,因此我们最多可以有三到四个副本。
[In]: kubectl scale deployment ml-app --replicas=3
如前所述,所有这些资源都有相关成本,因此删除这些已用资源以避免任何成本对我们来说很重要。要删除活动集群并移除所有资源,我们需要使用以下命令:
[In]: gcloud container clusters delete ml-cluster
建议删除项目和相关文件(图像、数据等)。)以及一旦该过程完成,以避免继续使用 GCP 资源的任何费用。
结论
在本章中,我们介绍了 Kubernetes 的基本架构,并使用 Kubernetes 在 Google 云平台上部署了一个机器学习应用程序。