【Designing ML Systems】第 10 章 :MLOps 的基础设施和工具

 大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流

个人主页-Sonhhxg_柒的博客_CSDN博客 

欢迎各位→点赞 + 收藏⭐️ + 留言​

系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

 foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟

文章目录

存储和计算

公共云与私有数据中心

开发环境

开发环境设置

IDE

标准化开发环境

从开发到产品:容器

资源管理

Cron、调度器和编排器

数据科学工作流管理

机器学习平台

模型部署

模型Store

特色商店

构建与购买

概括


在第4章到第6章中,我们讨论了开发 ML 系统的逻辑。在第7章到第9章中,我们讨论了部署、监控和持续更新 ML 系统的注意事项。到目前为止,我们假设 ML 从业者可以访问他们实现该逻辑和执行这些考虑所需的所有工具和基础设施。然而,这种假设远非正确。许多数据科学家告诉我,他们知道为他们的 ML 系统做正确的事情,但他们做不到,因为他们的基础设施没有以使他们能够这样做的方式设置。

机器学习系统很复杂。系统越复杂,它就越能从良好的基础设施中受益。正确设置的基础设施可以帮助自动化流程,减少对专业知识和工程时间的需求。反过来,这可以加快 ML 应用程序的开发和交付,减少错误的表面积,并启用新的用例。然而,如果设置错误,基础设施使用起来很痛苦,而且更换起来也很昂贵。在本章中,我们将讨论如何为 ML 系统设置基础设施。

在我们深入研究之前,重要的是要注意每个公司的基础设施需求都不同。您所需的基础架构取决于您开发的应用程序的数量以及应用程序的专业化程度。一方面,您有公司使用 ML 进行临时业务分析,例如预测他们明年将在季度计划会议上展示的新用户数量。这些公司可能不需要投资任何基础设施——Jupyter Notebooks、Python 和 Pandas 将是他们最好的朋友。如果您只有一个简单的 ML 用例,例如用于向您的朋友展示对象检测的 Android 应用程序,您可能也不需要任何基础设施——您只需要一个与 Android 兼容的 ML 框架,如 TensorFlow Lite。

另一方面,有些公司致力于开发具有独特要求的应用程序。例如,自动驾驶汽车具有独特的准确性和延迟要求——算法必须能够在几毫秒内做出响应,并且其准确性必须接近完美,因为错误的预测会导致严重的事故。同样,谷歌搜索有一个独特的规模要求,因为大多数公司不会像谷歌那样每秒处理 63,000 个搜索查询,相当于每小时 2.34 亿个搜索查询。1这些公司可能需要开发自己的高度专业化的基础设施。谷歌为搜索开发了很大一部分内部基础设施;特斯拉和Waymo等自动驾驶汽车公司也是如此。2一部分专业基础设施后来被公开并被其他公司采用是很常见的。例如,谷歌将其内部云基础设施扩展到公众,从而产生了谷歌云平台。

处于中间位置的是大多数公司,他们将 ML 用于多种常见应用——欺诈检测模型、价格优化模型、客户流失预测模型、推荐系统等——以合理的规模。“合理规模”是指公司每天处理千兆字节和太字节的数据,而不是 PB。他们的数据科学团队可能有 10 到数百名工程师。3此类别可能包括任何公司,从 20 人的初创公司到 Zillow 规模的公司,但不包括 FAAAM 规模的公司。4例如,早在 2018 年,Uber 每天向其数据湖添加数十 TB 的数据,Zillow 最大的数据集每天引入 2 TB 的未压缩数据。5相比之下,早在 2014 年,Facebook 每天就产生4 PB的数据。6

处于中间位置的公司可能会受益于日益标准化的通用 ML 基础设施(见图 10-1)。在本书中,我们将专注于以合理的规模为绝大多数 ML 应用程序提供基础设施。

【Designing ML Systems】第 10 章 :MLOps 的基础设施和工具_第1张图片

图 10-1。不同生产规模的公司的基础设施要求

 为了建立正确的基础设施根据您的需求,准确了解基础架构的含义及其组成非常重要。根据维基百科,在物理世界中,“基础设施是支持家庭和公司可持续功能的一组基本设施和系统。” 7在 ML 世界中,基础设施是一组支持 ML 系统开发和维护的基础设施。什么应该被认为是“基础设施”正如本章前面所讨论的,各公司之间的差异很大。在本节中,我们将检查以下四个层:

存储和计算

存储层在哪里数据被收集和存储。计算层提供运行 ML 工作负载所需的计算,例如训练模型、计算特征、生成特征等。

资源管理

资源管理包括调度工具并协调您的工作负载,以充分利用您的可用计算资源。此类别中的工具示例包括 Airflow、Kubeflow 和 Metaflow。

机器学习平台

这提供了工具来帮助ML 应用程序的开发,例如模型存储、特征存储和监控工具。此类别中的工具示例包括 SageMaker 和 MLflow。

开发环境

这通常被称为开发环境;它是编写代码和运行实验的地方。代码需要进行版本控制和测试。需要跟踪实验。

这四个不同的层如图 10-2所示。数据和计算是任何 ML 项目所需的基本资源,因此形成了存储和计算层任何想要应用机器学习的公司的基础设施。这一层对于数据科学家来说也是最抽象的。我们将首先讨论这一层,因为这些资源是最容易解释的。

【Designing ML Systems】第 10 章 :MLOps 的基础设施和工具_第2张图片

图 10-2。ML 的不同基础架构层

 开发环境是数据科学家每天必须与之交互的环境,因此,它对他们来说是最不抽象的。接下来我们将讨论这一类别,然后我们将讨论资源管理,这是数据科学家之间有争议的话题——人们仍在争论数据科学家是否需要了解这一层。因为“机器学习平台”是一个相对较新的概念,其不同的组件仍在成熟中,所以在我们熟悉了所有其他类别之后,我们将最后讨论这个类别。ML 平台需要公司的前期投资,但如果做得好,它可以让数据科学家在该公司的业务用例中的生活变得更加轻松。

即使两家公司有完全相同的基础设施需求,他们最终的基础设施也可能看起来不同,这取决于他们构建与购买决策的方法——即,他们想要内部构建的内容与他们想要外包给其他公司的内容。我们将在本章的最后部分讨论构建与购买决策,我们还将讨论对 ML 基础设施标准化和统一抽象的希望。

让我们潜入吧!

存储和计算

机器学习系统处理大量数据,这些数据需要存储在某个地方。存储层是收集和存储数据的地方。在最简单的形式中,存储层可以是硬盘驱动器 (HDD) 或固态磁盘 (SSD)。存储层可以在一个地方,例如,您可能将所有数据保存在 Amazon S3 或 Snowflake 中,或者分布在多个位置。8您的存储层可以在私有数据中心或云中进行本地部署。过去,公司可能会尝试管理自己的存储层。然而,在过去的十年中,存储层大多已经商品化并转移到了云端。数据存储变得如此便宜,以至于大多数公司只是免费存储他们拥有的所有数据。9我们在第 3 章中深入介绍了数据层,因此在本章中,我们将重点关注计算层。

计算是指所有计算公司可以访问的资源以及确定如何使用这些资源的机制。可用计算资源的数量决定了工作负载的可扩展性。您可以将计算层视为执行作业的引擎。在最简单的形式中,计算层可以只是一个 CPU 核心或一个 GPU 核心来完成所有计算。它最常见的形式是由云提供商管理的云计算,例如 AWS Elastic Compute Cloud (EC2) 或 GCP。

计算层通常可以被分割成更小的计算单元以同时使用。例如,一个 CPU 内核可能支持两个并发线程;每个线程都用作计算单元来执行自己的作业。或者可以将多个 CPU 内核连接在一起形成一个更大的计算单元来执行更大的作业。可以为特定的短期作业(例如 AWS Step Function 或 GCP Cloud Run)创建计算单元——该单元将在作业完成后被消除。计算单元也可以创建得更“永久”,也就是不依赖于工作,比如虚拟机。更永久的计算单元有时称为“实例”。

但是,计算层并不总是使用线程或内核作为计算单元。有一些计算层可以抽象出核心的概念并使用其他计算单元。例如,像 Spark 和 Ray 这样的计算引擎使用“作业”作为它们的单元,而 Kubernetes 使用“pod”(容器的包装器)作为其最小的可部署单元。虽然您可以在一个 pod 中拥有多个容器,但您不能在同一个 pod 中独立启动或停止不同的容器。

要执行一项作业,您首先需要将所需数据加载到计算单元的内存中,然后对该数据执行所需的操作——加法、乘法、除法、卷积等。例如,要添加两个数组,首先需要将这两个数组加载到内存中,然后对这两个数组执行加法。如果计算单元没有足够的内存来加载这两个数组,那么如果没有处理内存不足计算的算法,该操作将是不可能的。因此,计算单元的主要特征在于两个指标:它有多少内存以及它运行操作的速度。

可以使用 GB 等单位指定内存指标,并且通常可以直接评估:具有 8 GB 内存的计算单元可以处理内存中的数据,而不是只有 2 GB 的计算单元,而且通常更昂贵。10一些公司不仅关心计算单元有多少内存,还关心将数据载入和载入内存的速度有多快,因此一些云提供商宣传他们的实例具有“高带宽内存”或指定实例的 I/O带宽。

运算速度比较有争议。最常见的指标是 FLOPS——每秒浮点运算。作为顾名思义,这个指标表示计算单元每秒可以运行的浮点操作数。您可能会看到硬件供应商宣传他们的 GPU 或 TPU 或 IPU(智能处理单元)具有 teraFLOPS(一万亿 FLOPS)或其他大量 FLOPS。

然而,这个指标是有争议的,因为首先,衡量这个指标的公司可能对什么算作运营有不同的想法。例如,如果一台机器将两个操作融合为一个并执行这个融合的操作,11这算做一个操作还是两个操作?其次,仅仅因为一个计算单元能够执行万亿次 FLOPS 并不意味着您将能够以万亿次 FLOPS 的速度执行您的工作。作业可以运行的 FLOPS 数与计算单元能够处理的 FLOP 数之比称为利用率。12如果一个实例能够执行 100 万次 FLOP,并且您的工作以 30 万次 FLOPS 运行,则利用率为 30%。当然,您希望您的利用率尽可能高。但是,要达到 100% 的利用率几乎是不可能的。根据硬件后端和应用程序,50% 的利用率可能被认为是好是坏。利用率还取决于您将数据加载到内存中以执行下一个操作的速度——因此 I/O 带宽的重要性。13

在评估一个新的计算单元时,重要的是要评估这个计算单元完成常见工作负载需要多长时间。例如,MLPerf是硬件供应商的一个流行基准,通过显示他们的硬件在 ImageNet 数据集上训练 ResNet-50 模型或使用 BERT-large 模型为 SQuAD 数据集生成预测需要多长时间来衡量他们的硬件性能.

因为考虑 FLOPS 并不是很有用,为了让事情变得更容易,在评估计算性能时,很多人只看计算单元的核心数量。因此,您可以使用具有 4 个 CPU 内核和 8 GB 内存的实例。请记住,AWS 使用 vCPU 的概念,它代表虚拟 CPU,出于实际目的,可以将其视为半个物理内核。14您可以在图 10-3中看到一些 AWS EC2 和 GCP 实例提供的内核和内存数量。

【Designing ML Systems】第 10 章 :MLOps 的基础设施和工具_第3张图片

图 10-3。截至 2022 年 2 月,AWS 和 GCP 上可用的 GPU 和 TPU 实例示例。来源:AWS 和 GCP 网站截图

公共云与私有数据中心

与数据存储一样,计算层在很大程度上是商品化的。这意味着,而不是设置自己的用于存储和计算的数据中心,公司可以向 AWS 和 Azure 等云提供商支付他们使用的确切计算量。云计算使公司可以非常轻松地开始构建,而不必担心计算层。它对具有可变大小工作负载的公司特别有吸引力。想象一下,如果您的工作负载一年中的某一天需要 1,000 个 CPU 内核,而一年中的其余时间只需要 10 个 CPU 内核。如果您建立自己的数据中心,则需要预先支付 1,000 个 CPU 内核的费用。使用云计算,您只需一年中的一天支付 1,000 个 CPU 内核,其余时间为 10 个 CPU 内核。能够根据需要添加更多计算或关闭实例是很方便的——大多数云提供商甚至会自动为你做这件事——减少工程运营开销。这在 ML 中特别有用,因为数据科学工作负载是突发性的。数据科学家倾向于在开发过程中进行数周的大量实验,这需要大量的计算能力。后来,在生产过程中,工作量更加一致。

请记住,云计算是有弹性但不神奇。它实际上没有无限计算。大多数云提供商对您一次可以使用的计算资源提供限制。这些限制中的一些(但不是全部)可以通过请愿书提出。例如,在撰写本书时,AWS EC2 的最大实例是X1e ,它有 128 个 vCPU 和近 4 TB 的内存。15拥有大量计算资源并不意味着使用起来总是很容易,尤其是当您必须使用 Spot 实例来节省成本时。16

由于云的弹性和易用性,越来越多的公司选择为云付费,而不是构建和维护自己的存储和计算层。Synergy Research Group 的研究表明,2020 年,“企业在云基础设施服务上的支出 [增长] 35%,达到近 1300 亿美元”,而“企业在数据 [中心] 上的支出下降了 6%,低于 900 亿美元,” 17如图所示在图 10-4中。

【Designing ML Systems】第 10 章 :MLOps 的基础设施和工具_第4张图片

图 10-4。2020 年,企业在云基础设施服务上的支出增长了 35%,而在数据中心的支出下降了 6%。资料来源:改编自 Synergy Research Group 的图片

 虽然与早期构建自己的存储和计算层相比,利用云往往会给公司带来更高的回报,但随着公司的发展,这变得不那么可靠了。根据公开软件公司披露的云基础设施支出,风险投资公司 a16z 显示,云支出约占这些公司收入成本的 50%。18

云的高成本促使公司开始将工作负载转移回自己的数据中心,这一过程称为“云遣返”。Dropbox 在 2018 年提交的 S-1 文件显示,由于基础设施优化大修,该公司能够在 IPO 前两年节省 7500 万美元——其中很大一部分包括将工作负载从公共云转移到自己的数据中心。由于 Dropbox 从事数据存储业务,云计算成本高昂是 Dropbox 独有的吗?不完全的。在上述分析中,a16z 估计“在目前使用云基础设施的 50 家顶级公共软件公司中,由于云对利润率的影响——相对于运行基础设施本身而言,它们的市场价值估计损失了 100B 美元。” 19

虽然开始使用云很容易,但离开云却很难。云遣返需要对商品和工程工作进行大量的前期投资。越来越多的公司采用混合方法:将大部分工作负载保留在云上,但缓慢增加对数据中心的投资。

论多云战略

企业降低成本的另一种方式对任何单一云提供商的依赖都遵循多云策略:将工作负载分散到多个云提供商上。20这使公司能够构建他们的系统,使其能够与多个云兼容,从而使他们能够利用现有的最佳和最具成本效益的技术,而不是被单一云提供商提供的服务所束缚,这种情况被称为供应商锁定。Gartner 2019 年的一项研究表明,81% 的组织正在与两个或更多公共云提供商合作。21我看到的机器学习工作负载的一个常见模式是在 GCP 或 Azure 上进行培训,并在 AWS 上进行部署。

多云策略通常不是自愿发生的。正如我们的早期评论者之一 Josh Wills 所说:“头脑正常的人都不会打算使用多云。” 跨云移动数据和编排工作负载非常困难。

通常,多云之所以会发生,是因为组织的不同部分独立运营,每个部分都做出自己的云决策。它也可能发生在收购之后——被收购的团队已经在与托管组织不同的云上,并且尚未发生迁移。

在我的工作中,我看到由于战略投资而出现了多云。微软和谷歌是创业生态系统的大投资者,我之前合作过的几家公司都是在微软/谷歌投资 AWS 后,AWS 已迁移到 Azure/GCP。

开发环境

开发环境是 ML 工程师所在的地方编写代码、运行实验并与部署冠军模型和评估挑战者模型的生产环境进行交互。开发环境由以下组件组成:IDE(集成开发环境)、版本控制和 CI/CD。

如果您是每天编写代码的数据科学家或 ML 工程师,您可能对所有这些工具非常熟悉,并且可能想知道对它们有什么要说的。根据我的经验,除了少数科技公司之外,大多数公司的开发环境都被严重低估和投资不足。根据 Ville Tuulos 在他的《有效数据科学基础设施》一书中的说法,“你会惊讶地发现有多少公司拥有经过良好调整、可扩展的生产基础设施,但如何首先开发、调试和测试代码的问题已经解决了以特别的方式。” 22

他建议“如果你有时间只设置好一个基础设施,那就让它成为数据科学家的开发环境。” 因为开发环境是工程师工作的地方,所以开发环境的改进直接转化为工程生产力的提高。

在本节中,我们将首先介绍开发环境的不同组件,然后讨论开发环境的标准化,然后再讨论如何使用容器将您的更改从开发环境带到生产环境。

开发环境设置

应该设置开发环境以包含所有可以使工程师更轻松地完成工作的工具。它应该还包括用于版本控制的工具。在撰写本文时,公司使用一组特别的工具对其 ML 工作流程进行版本控制,例如 Git 到版本控制代码、DVC 到版本数据、Weights & Biases 或 Comet.ml 来跟踪开发过程中的实验,以及 MLflow 来跟踪工件部署它们时的模型。Claypot AI 正在开发一个平台,可以帮助您在一个地方对所有 ML 工作流程进行版本控制和跟踪。版本控制对于任何软件工程项目都很重要,但对于 ML 项目更是如此,因为您可以更改的事物数量众多(代码、参数、数据本身等),并且需要跟踪之前的运行以进行重现稍后的。我们在“实验跟踪和版本控制”一节中对此进行了介绍。

还应该使用CI/CD测试套件设置开发环境,以在将代码推送到登台或生产环境之前对其进行测试。编排 CI/CD 测试套件的工具示例有 GitHub Actions 和 CircleCI。因为 CI/CD 是一个软件工程问题,所以它超出了本书的范围。

在本节中,我们将重点关注工程师编写代码的地方:IDE。

IDE

IDE是编辑器你写你的代码。IDE 倾向于支持多种编程语言。IDE 可以是 VS Code 或 Vim 等原生应用程序。IDE 可以基于浏览器,这意味着它们可以在浏览器中运行,例如 AWS Cloud9。

许多数据科学家不仅在 IDE 中编写代码,还在 Jupyter Notebooks 和 Google Colab 等笔记本中编写代码。23笔记本不仅仅是编写代码的地方。您可以包含任意工件,例如图像、绘图、漂亮的表格格式的数据等,这使 notebook 对于探索性数据分析和分析模型训练结果非常有用。

笔记本有一个很好的属性:它们是有状态的——它们可以在运行后保留状态。如果您的程序中途失败,您可以从失败的步骤重新运行,而不必运行程序从一开始就。当您必须处理可能需要很长时间才能加载的大型数据集时,这尤其有用。使用笔记本,您只需加载一次数据(笔记本可以将这些数据保留在内存中),而不必在每次要运行代码时加载它。如图 10-5所示,如果您的代码在笔记本中的第 4 步失败,您只需重新运行第 4 步,而不是从程序的开头重新运行。

【Designing ML Systems】第 10 章 :MLOps 的基础设施和工具_第5张图片

图 10-5。在 Jupyter Notebooks 中,如果第 4 步失败,您只需再次运行第 4 步,而不必再次运行第 1 至第 4 步

 请注意,这种状态可能是一把双刃剑,因为它允许您无序地执行您的单元格。例如,在普通脚本中,单元格 4 必须在单元格 3 之后运行,单元格 3 必须在单元格 2 之后运行。但是,在笔记本中,您可以运行单元格 2、3、然后 4 或单元格 4、3,然后 2。这使得除非您的笔记本附带有关运行单元顺序的说明,否则笔记本的再现性会更加困难。Chris Albon 在一个笑话中抓住了这个困难(见图 10-6)。

【Designing ML Systems】第 10 章 :MLOps 的基础设施和工具_第6张图片

图 10-6。Notebooks 的状态性允许你乱序执行单元格,从而难以重现 notebook

 由于 notebook 对于数据探索和实验非常有用,因此 notebook 已成为数据科学家和 ML 不可或缺的工具。一些公司已将笔记本作为其数据科学基础设施的中心。在他们的开创性文章“超越互动:Netflix 的笔记本创新”中,Netflix 列出了一系列基础设施工具,这些工具可用于让笔记本更加强大。24清单包括:

造纸厂Papermill造纸厂

用于生成多个不同的笔记本参数集——例如,当您想要使用不同的参数集运行不同的实验并同时执行它们时。它还可以帮助汇总笔记本集合中的指标。

通勤者Commuter通勤者

用于查看、查找和共享的笔记本中心组织内的笔记本。

另一个有趣的项目旨在改善notebook 体验是nbdev,一个基于 Jupyter Notebooks 的库鼓励您在同一个地方编写文档和测试。

标准化开发环境

关于开发环境的第一件事是它应该是标准化的,如果不是在公司范围内,那么至少在团队范围内。我们将通过一个故事来了解使开发环境标准化意味着什么以及为什么需要这样做。

在我们创业的早期,我们每个人都在自己的电脑上工作。我们有一个 bash 文件,新团队成员可以运行它来创建一个新的虚拟环境——在我们的例子中,我们使用 conda 来创建虚拟环境——并安装运行我们的代码所需的包。所需包的列表是我们在开始使用新包时不断添加的旧requirements.txt 。有时,我们中的一个人变得懒惰,我们只是添加了一个包名(例如,torch),而没有指定它是哪个版本的包(例如,torch==1.10.0+cpu)。有时,一个新的拉取请求会在我的计算机上运行良好,但在其他同事的计算机上却不能运行,25我们通常很快就发现这是因为我们使用了同一个包的不同版本。我们决定在将新包添加到requirements.txt时始终指定包名称和包版本,这消除了很多不必要的麻烦。

有一天,我们遇到了这个奇怪的错误,它只在某些运行期间发生,而不是在其他运行期间发生。我让我的同事调查它,但他无法重现该错误。我告诉他这个错误只在某些时候发生,所以他可能需要运行代码大约 20 次才能确定。他将代码运行了 20 次,但仍然一无所获。我们比较了我们的包裹,一切都匹配。经过几个小时令人毛骨悚然的挫折后,我们发现这是一个并发问题,仅在 Python 3.8 或更早版本中存在。我有 Python 3.8,我的同事有 Python 3.9,所以他没有看到这个错误。我们决定让每个人都使用相同的 Python 版本,这样就消除了更多的麻烦。

然后有一天,我的同事买了一台新笔记本电脑。那是一台配备当时新的 M1 芯片的 MacBook。他试图在这台新笔记本电脑上按照我们的设置步骤进行操作,但遇到了困难。这是因为 M1 芯片是新的,我们使用的一些工具,包括 Docker,还不能很好地与 M1 芯片配合使用。在看到他为设置环境而苦苦挣扎了一天后,我们决定迁移到云开发环境。这意味着我们仍然对虚拟环境、工具和包进行标准化,但现在每个人都在同一类型的机器上使用由云提供商提供的虚拟环境、工具和包。

使用云开发时环境,您可以使用云开发环境,该环境还附带云 IDE,例如AWS Cloud9(没有内置笔记本)和Amazon SageMaker Studio(托管 JupyterLab)。在撰写本书时,Amazon SageMaker Studio 的使用似乎比 Cloud9 更广泛。然而,我认识的大多数使用云 IDE 的工程师都是通过在他们的云实例上安装他们选择的 IDE(如 Vim)来实现的。

一个更流行的选择是使用带有本地 IDE 的云开发环境。例如,您可以使用安装在计算机上的 VS Code,并使用安全外壳 (SSH) 等安全协议将本地 IDE 连接到云环境。

虽然人们普遍认为工具和包应该标准化,但一些公司对标准化 IDE 犹豫不决。工程师可能会对 IDE 产生感情,有些人已经竭尽全力捍卫他们选择的 IDE,26所以很难强迫每个人都使用同一个 IDE。然而,多年来,一些 IDE 已经成为最受欢迎的。其中,VS Code 是一个不错的选择,因为它可以轻松与云开发实例集成。

在我们的创业公司中,我们选择了GitHub Codespaces作为我们的云开发环境,但是您可以通过 SSH 访问的 AWS EC2 或 GCP 实例也是一个不错的选择。在迁移到云环境之前,像许多其他公司一样,我们担心成本问题——如果我们在不使用时忘记关闭实例而他们不断向我们收费怎么办?然而,由于两个原因,这种担忧已经消失。首先,GitHub Codespaces 等工具会在您的实例处于非活动状态 30 分钟后自动关闭。其次,有些实例非常便宜。例如,具有 4 个 vCPU 和 8 GB 内存的 AWS 实例的成本约为 0.1 美元/小时,如果您从不关闭它,则大约为 73 美元/月。因为工程时间很昂贵,如果一个云开发环境可以帮助你每个月节省几个小时的工程时间,对很多公司来说都是值得的。

从本地开发环境迁移到云开发环境还有许多其他好处。首先,它使 IT 支持变得更加容易——想象一下必须支持 1,000 台不同的本地机器,而不是只支持一种类型的云实例。其次,远程工作很方便——你可以从任何计算机上通过 SSH 连接到你的开发环境。第三,云开发环境有助于提高安全性。例如,如果员工的笔记本电脑被盗,您可以撤消该笔记本电脑对云实例的访问权限,以防止小偷访问您的代码库和专有信息。当然,出于安全考虑,一些公司可能无法迁移到云开发环境。例如,不允许他们将代码或数据放在云端。

第四个好处,我认为对于在云上进行生产的公司来说最大的好处是,在云上拥有你的开发环境可以减少开发环境和生产环境之间的差距。如果您的生产环境在云端,那么将您的开发环境带到云端是很自然的。

有时,公司不得不将他们的开发环境迁移到云端,这不仅是因为好处,也是出于必要。对于无法在本地计算机上下载或存储数据的用例,访问它的唯一方法是通过云中的笔记本 (SageMaker Studio),它可以从 S3 读取数据,前提是它具有正确的权限。

当然,由于成本、安全或其他问题,云开发环境可能不适用于每家公司。设置云开发环境也需要一些初始投资,您可能需要对数据科学家进行云卫生方面的培训,包括建立与云的安全连接、安全合规性或避免浪费的云使用。然而,开发环境的标准化从长远来看,可能会使您的数据科学家的生活更轻松并为您节省资金。

从开发到产品:容器

在开发过程中,您可能通常使用固定数量的机器或实例(通常是一个),因为您的工作负载波动不大——您的模型不会突然从每小时仅处理 1,000 个请求到 100 万个请求。

另一方面,生产服务可能分布在多个实例上。实例的数量会根据传入的工作负载不时发生变化,这有时是不可预测的。例如,一位名人在推特上发布了有关您刚刚起步的应用程序的信息,然后您的流量突然飙升了 10 倍。您必须根据需要打开新实例,并且需要使用所需的工具和包设置这些实例来执行您的工作负载。

以前,您必须自己启动和关闭实例,但大多数公共云提供商已经处理了自动缩放部分。但是,您仍然需要担心设置新实例。

当您始终使用同一个实例时,您可以安装一次依赖项并在使用此实例时使用它们。在生产中,如果您根据需要动态分配实例,那么您的环境本质上是无状态的。当为您的工作负载分配新实例时,您需要使用预定义说明列表安装依赖项。

出现了一个问题:如何在任何新实例上重新创建环境?容器技术——其中 Docker 是最流行的——旨在回答这个问题。使用 Docker,您创建一个 Dockerfile,其中包含有关如何重新创建模型可以在其中运行的环境的分步说明:安装此包、下载此预训练模型、设置环境变量、导航到文件夹等。这些说明允许硬件在任何地方运行您的代码。

Docker 中的两个关键概念是镜像和容器。运行 Dockerfile 中的所有指令会为您提供 Docker 映像。如果你运行这个 Docker 镜像,你会得到一个 Docker 容器。您可以将 Dockerfile 视为构建模具的配方,即 Docker 映像。从这个模具中,您可以创建多个运行实例;每个都是一个 Docker 容器。

您可以从头开始构建 Docker 映像,也可以从另一个 Docker 映像构建。例如,NVIDIA 可能会提供一个 Docker 映像,其中包含 TensorFlow 和所有必要的库,以针对 GPU 优化 TensorFlow。如果你想构建一个在 GPU 上运行 TensorFlow 的应用程序,使用这个 Docker 镜像作为你的基础并在这个基础镜像之上安装特定于你的应用程序的依赖项并不是一个坏主意。

容器注册表是您可以共享 Docker 映像或查找由其他人创建的要公开共享或仅与组织内部人员共享的映像的地方。常见的容器注册表包括 Docker Hub 和 AWS ECR(弹性容器注册表)。

这是一个运行以下指令的简单 Dockerfile 示例。这个例子是为了展示 Dockerfiles 是如何工作的,并且可能是不可执行的。

  1. 下载最新的 PyTorch 基础镜像。

  2. 在 GitHub 上克隆 NVIDIA 的 apex 存储库,导航到新创建的apex文件夹,然后安装 apex。

  3. fancy-nlp-project设置为工作目录。

  4. 在 GitHub 上克隆 Hugging Face 的transformers存储库,导航到新创建的transformers文件夹,然后安装transformers

    FROM pytorch/pytorch:latest
    RUN git clone https://github.com/NVIDIA/apex
    RUN cd apex && \
        python3 setup.py install && \
        pip install -v --no-cache-dir --global-option="--cpp_ext" \
        --global-option="--cuda_ext" ./
    
    WORKDIR /fancy-nlp-project
    RUN git clone https://github.com/huggingface/transformers.git && \
        cd transformers && \
        python3 -m pip install --no-cache-dir.

如果你的应用程序做了什么有趣的事情,你可能需要不止一个容器。考虑这样一种情况,您的项目包含运行速度快但需要大量内存的特征化代码,以及运行缓慢但需要较少内存的模型训练代码。如果您在同一个 GPU 实例上运行这两个部分的代码,您将需要具有高内存的 GPU 实例,这可能非常昂贵。相反,您可以在 CPU 实例上运行特征化代码,在 GPU 实例上运行模型训练代码。这意味着您需要一个容器进行特征化,另一个容器进行训练。

当您的管道中的不同步骤具有冲突的依赖关系时,可能还需要不同的容器,例如您的特征化器代码需要 NumPy 0.8,但您的模型需要 NumPy 1.0。

如果您有 100 个微服务并且每个微服务都需要自己的容器,那么您可能同时运行 100 个容器。手动构建、运行、分配资源和停止 100 个容器可能是一件痛苦的事情。帮助您管理多个容器的工具称为容器编排。Docker Compose 是一个轻量级的容器编排器,可以在单个主机上管理容器。

但是,您的每个容器都可能在自己的主机上运行,​​这就是 Docker Compose 的所在在它的极限。Kubernetes (K8s) 正是为此而生的工具。K8s 为容器创建了一个网络来通信和共享资源。当您需要更多计算/内存时,它可以帮助您在更多实例上启动容器,并在您不再需要容器时关闭容器,并且有助于保持系统的高可用性。

K8s 是 2010 年代发展最快的技术之一。自 2014 年成立以来,它已在当今的生产系统中无处不在。Jeremy Jordan 为有兴趣了解更多信息的读者提供了关于 K8s 的精彩介绍。但是,K8s 不是对数据科学家最友好的工具,并且已经有很多关于如何将数据科学工作负载从它移开的讨论。27我们将在下一节详细介绍 K8s。

资源管理

在云之前的世界(甚至今天在维护自己的数据中心的公司中),存储和计算是有限的。那时的资源管理主要围绕如何充分利用出于有限的资源。增加一个应用程序的资源可能意味着减少其他应用程序的资源,并且需要复杂的逻辑来最大限度地利用资源,即使这意味着需要更多的工程时间。

然而,在存储和计算资源更具弹性的云世界中,关注点已从如何最大化资源利用率转移到如何经济高效地使用资源。向应用程序添加更多资源并不意味着减少其他应用程序的资源,这大大简化了分配挑战。许多公司都可以向应用程序添加更多资源,只要增加的成本可以通过回报来证明,例如额外的收入或节省的工程时间。

在世界上绝大多数地方,工程师的时间比计算时间更有价值,如果这意味着可以帮助他们的工程师提高工作效率,公司可以使用更多资源。这意味着公司投资自动化其工作负载可能是有意义的,这可能会使资源的使用效率低于手动规划工作负载,但让他们的工程师可以专注于获得更高回报的工作。通常,如果一个问题可以通过使用更多的非人力资源(例如,投入更多的计算)或使用更多的人力资源(例如,需要更多的工程时间来重新设计)来解决,那么第一个解决方案可能是首选。

在本节中,我们将讨论如何管理 ML 工作流的资源。我们将专注于基于云的资源;但是,所讨论的想法也适用于私有数据中心。

Cron、调度器和编排器

ML 工作流有两个影响其资源管理的关键特征:重复性和依赖性。

在本书中,我们详细讨论了开发 ML 系统是一个迭代过程。同样,机器学习工作负载很少是一次性操作,而是重复性的。例如,您可能每周训练一个模型或每四个小时生成一批新的预测。可以安排和编排这些重复性流程,以使用可用资源平稳且经济高效地运行。

安排重复的作业运行固定时间正是cron所做的。这也是 cron 所做的一切:在预定时间运行脚本并告诉您作业是成功还是失败。它不关心它运行的作业之间的依赖关系——你可以使用 cron 在作业 B 之后运行作业 A,但你不能安排任何复杂的事情,比如如果 A 成功则运行 B,如果 A 失败则运行 C。

这将我们引向第二个特征:依赖。ML 工作流程中的步骤之间可能具有复杂的依赖关系。例如,ML 工作流可能包含以下步骤:

  1. 从数据仓库中提取上周的数据。

  2. 从这个提取的数据中提取特征。

  3. 在提取的特征上训练两个模型 A 和 B。

  4. 比较测试集上的 A 和 B。

  5. 如果 A 更好,则部署 A;否则部署 B.

每一步都取决于上一步的成功。第 5 步就是我们所说的条件依赖:这一步的动作取决于上一步的结果。这些步骤之间的执行顺序和依赖关系可以用图表来表示,如图 10-7所示。

【Designing ML Systems】第 10 章 :MLOps 的基础设施和工具_第7张图片

图 10-7。显示简单 ML 工作流执行顺序的图,本质上是 DAG(有向无环图)

许多读者可能会认识到图 10-7是一个 DAG:有向无环图。它必须被引导来表达步骤之间的依赖关系。它不能包含循环,因为如果包含,则作业将永远继续运行。DAG 是一个表示一般计算工作流的常用方法,而不仅仅是 ML 工作流。大多数工作流管理工具都要求您以 DAG 的形式指定工作流。

调度程序是 cron 程序可以处理依赖关系。它接收工作流的 DAG 并相应地安排每个步骤。您甚至可以安排基于基于事件的触发器启动作业,例如,每当事件 X 发生时启动作业。调度程序还允许您指定作业失败或成功时要做什么,例如,如果它失败,在放弃之前重试多少次。

调度程序倾向于利用队列来跟踪作业。可以对作业进行排队、优先排序和分配执行所需的资源。这意味着调度程序需要了解可用资源以及运行每个作业所需的资源——所需资源要么在您调度作业时指定为选项,要么由调度程序估计。例如,如果一个作业需要 8 GB 内存和两个 CPU,调度程序需要在它管理的具有 8 GB 内存和两个 CPU 的实例的资源中找到并等待直到该实例不执行其他作业才能运行该作业实例。

这是一个如何使用流行的调度程序 Slurm 调度作业的示例,您可以在其中指定作业名称、需要执行作业的时间以及要为作业分配的内存量和 CPU:

#!/bin/bash#SBATCH -J JobName#SBATCH --time=11:00:00       # When to start the job#SBATCH --mem-per-cpu=4096   # Memory, in MB, to be allocated per CPU#SBATCH --cpus-per-task=4          # Number of cores per task

调度程序还应该优化资源利用率,因为它们具有可用资源、要运行的作业以及每个作业运行所需的资源的信息。但是,用户指定的资源数量并不总是正确的。例如,我可能会估计并因此指定一个作业需要 4 GB 内存,但该作业只需要 3 GB 内存或峰值需要 4 GB 内存,否则只需要 1-2 GB 内存。像 Google 的 Borg 这样的复杂调度程序会估计一个作业实际需要多少资源,并为其他作业回收未使用的资源,28进一步优化资源利用率。

设计一个通用的调度器很困难,因为这个调度器需要能够管理几乎任意数量的并发机器和工作流。如果您的调度程序出现故障,该调度程序涉及的每个工作流都将被中断。

如果调度器关心何时运行作业以及运行这些作业需要什么资源,那么协调器关心的是在哪里运行获取这些资源。调度程序处理作业类型的抽象,例如 DAG、优先级队列、用户级配额(即用户在给定时间可以使用的最大实例数)等。编排器处理较低级别的抽象,例如机器、实例、集群、服务级别分组、复制等。如果编排器注意到作业多于可用实例池,它可以增加可用实例池中的实例数。我们说它“提供”了更多的计算机来处理工作量。调度程序通常用于定期作业,而编排程序通常用于您需要长时间运行的服务响应请求的服务器。

当今最知名的管弦乐队毫无疑问是 Kubernetes,我们在“从开发到产品:容器”一节中讨论的容器编排器。K8s 可以在本地使用(甚至通过 minikube 在您的笔记本电脑上)。但是,我从来没有遇到过喜欢建立自己的 K8s 集群的人,所以大多数公司都将 K8s 用作由其云提供商管理的托管服务,例如 AWS 的 Elastic Kubernetes Service (EKS) 或谷歌 Kubernetes 引擎 (GKE)。

许多人交替使用调度程序和编排器,因为调度程序通常运行在编排器之上。调度器像 Slurm 和谷歌的 Borg 有一些编排能力,像 HashiCorp Nomad 和 K8s 这样的编排器有一些调度能力。但是您可以拥有单独的调度程序和编排程序,例如在 Kubernetes 之上运行 Spark 的作业调度程序或在 EKS 之上运行 AWS Batch 调度程序。HashiCorp Nomad 等编排器和 Airflow、Argo、Prefect 和 Dagster 等数据科学特定编排器都有自己的调度器。

数据科学工作流管理

我们已经讨论了调度器和编排器之间的区别,以及它们通常如何用于执行工作流。熟悉工作流管理工具的读者,尤其是针对Airflow、Argo、Prefect、Kubeflow、Metaflow 等数据科学可能想知道它们在这个调度器与编排器讨论中的位置。我们将在这里讨论这个话题。

以最简单的形式,工作流管理工具管理工作流。它们通常允许您将工作流指定为 DAG,类似于图 10-7中的那个。一个工作流可能包括一个特征化步骤、一个模型训练步骤和一个评估步骤。可以使用代码 (Python) 或配置文件 (YAML) 定义工作流。工作流中的每个步骤都称为一个任务。

几乎所有的工作流管理工具都带有一些调度器,因此,您可以将它们视为调度器,而不是关注单个作业,而是关注整个工作流。一旦定义了工作流,底层调度器通常与协调器一起分配资源来运行工作流,如图 10-8所示。

【Designing ML Systems】第 10 章 :MLOps 的基础设施和工具_第8张图片

图 10-8。定义工作流后,该工作流中的任务将被调度和编排

网上有很多文章比较不同的数据科学工作流管理工具。在本节中,我们将介绍五种最常用的工具:Airflow、Argo、Prefect、Kubeflow 和 Metaflow。本节并不打算对这些工具进行全面比较,而是让您了解工作流管理工具可能需要的不同功能。

Airflow 最初在 Airbnb 开发并于 2014 年发布,是最早的工作流编排器之一。这是一个了不起的任务调度程序,带有庞大的运算符库,可以轻松地将 Airflow 与不同的云提供商、数据库、存储选项等一起使用。Airflow 是“配置即代码”原则的拥护者。它的创建者认为数据工作流很复杂,应该使用代码(Python)而不是 YAML 或其他声明性语言来定义。下面是一个 Airflow 工作流程示例,取自平台的GitHub 存储库:from datetime import datetime, timedelta from airflow import DAG from airflow.operators.bash import BashOperator from airflow.providers.docker.operators.docker import DockerOperator dag = DAG( 'docker_sample', default_args={'retries': 1}, schedule_interval=timedelta(minutes=10), start_date=datetime(2021, 1, 1), catchup=False, ) t1 = BashOperator(task_id='print_date', bash_command='date', dag=dag) t2 = BashOperator(task_id='sleep', bash_command='sleep 5', retries=3, dag=dag) t3 = DockerOperator( docker_url='tcp://localhost:2375', # Set your docker URL command='/bin/sleep 30', image='centos:latest', network_mode='bridge', task_id='docker_op_tester', dag=dag, ) t4 = BashOperator( task_id='print_hello', bash_command='echo "hello world!!!"', dag=dag ) t1 >> t2 t1 >> t3 t3 >> t4

from datetime import datetime, timedelta

from airflow import DAG
from airflow.operators.bash import BashOperator
from airflow.providers.docker.operators.docker import DockerOperator
  
dag = DAG(
    'docker_sample',
    default_args={'retries': 1},
    schedule_interval=timedelta(minutes=10),
    start_date=datetime(2021, 1, 1),
    catchup=False,
)
  
t1 = BashOperator(task_id='print_date', bash_command='date', dag=dag)
t2 = BashOperator(task_id='sleep', bash_command='sleep 5', retries=3, dag=dag)
t3 = DockerOperator(
    docker_url='tcp://localhost:2375',  # Set your docker URL
    command='/bin/sleep 30',
    image='centos:latest',
    network_mode='bridge',
    task_id='docker_op_tester',
    dag=dag,
)
  
t4 = BashOperator(
    task_id='print_hello', 
    bash_command='echo "hello world!!!"', 
    dag=dag
)
  
t1 >> t2
t1 >> t3
t3 >> t4   

 但是,由于 Airflow 比大多数其他工具创建得更早,因此它没有可以从中吸取教训的工具,并且存在许多缺点,正如Uber Engineering 的博客文章中详细讨论的那样。在这里,我们将只讨论三个给你一个想法。

首先,Airflow 是单片的,这意味着它将整个工作流程打包到一个容器中。如果您的工作流程中的两个不同步骤有不同的要求,理论上您可以使用 Airflow 为它们创建不同的容器DockerOperator,但这样做并不容易。

其次,Airflow 的 DAG 没有参数化,这意味着您无法将参数传递到您的工作流程中。因此,如果您想以不同的学习率运行相同的模型,则必须创建不同的工作流程。

第三,Airflow 的 DAG 是静态的,这意味着它无法在运行时根据需要自动创建新步骤。假设您正在从数据库中读取数据,并且您想创建一个步骤来处理数据库中的每条记录(例如,进行预测),但您事先不知道数据库中有多少条记录。气流将无法处理。

创建了下一代工作流编排器(Argo、Prefect)以解决 Airflow 的不同缺点。

Prefect 的首席执行官 Jeremiah Lowin 是 Airflow 的核心贡献者。他们早期的营销活动在 Prefect 和 Airflow 之间进行了激烈的比较。Prefect 的工作流程是参数化和动态的,与 Airflow 相比有了很大的改进。它还遵循“配置即代码”原则,因此工作流是在 Python 中定义的。

但是,像 Airflow 一样,容器化步骤不是 Prefect 的首要任务。您可以在容器中运行每个步骤,但您仍然必须处理 Dockerfile 并将您的 docker 注册到 Prefect 中的工作流中。

Argo 解决了容器问题。每一步在 Argo 工作流中运行在它自己的容器中。但是,Argo 的工作流程是在 YAML 中定义的,它允许您在同一个文件中定义每个步骤及其要求。以下代码示例取自Argo GitHub 存储库,演示了如何创建工作流以显示硬币翻转:

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: coinflip-
  annotations:
    workflows.argoproj.io/description: |
      This is an example of coin flip defined as a sequence of conditional steps.
      You can also run it in Python: 
      https://couler-proj.github.io/couler/examples/#coin-flip
spec:
  entrypoint: coinflip
  templates:
  - name: coinflip
    steps:
    - - name: flip-coin
        template: flip-coin
    - - name: heads
        template: heads
        when: "{{steps.flip-coin.outputs.result}} == heads"
      - name: tails
        template: tails
        when: "{{steps.flip-coin.outputs.result}} == tails"
    
    - name: flip-coin
      script:
        image: python:alpine3.6
        command: [python]
        source: |
          import random
          result = "heads" if random.randint(0,1) == 0 else "tails"
          print(result)
    - name: heads
      container:
        image: alpine:3.6
        command: [sh, -c]
        args: ["echo \"it was heads\""]
    
    - name: tails
      container:
        image: alpine:3.6
        command: [sh, -c]
        args: ["echo \"it was tails\""]

除了凌乱的 YAML 文件之外,Argo 的主要缺点是它只能在 K8s 集群上运行,而这些集群只能在生产环境中使用。如果您想在本地测试相同的工作流程,您将拥有使用 minikube 在您的笔记本电脑上模拟 K8s,这可能会变得混乱。

进入 Kubeflow 和 Metaflow 这两个工具旨在通过抽象出通常运行 Airflow 或 Argo 所需的基础设施样板代码来帮助您在开发和生产环境中运行工作流。他们承诺让数据科学家能够从本地笔记本访问 prod 环境的全部计算能力,这有效地允许数据科学家在 dev 和 prod 环境中使用相同的代码。

尽管这两种工具都具有一定的调度能力,但它们旨在与真正的调度程序和协调器一起使用。Kubeflow 的一个组件是 Kubeflow Pipelines,它构建在 Argo 之上,旨在用于 K8s 之上。Metaflow 可以与 AWS Batch 或 K8s 一起使用。

这两种工具都是完全参数化和动态的。目前,Kubeflow 是比较流行的一种。但是,从用户体验的角度来看,我认为 Metaflow 更胜一筹。在 Kubeflow 中,虽然您可以在 Python 中定义工作流,但您仍然必须编写 Dockerfile 和 YAML 文件来指定每个组件的规范(例如,流程数据、训练、部署),然后才能将它们拼接到 Python 工作流中. 基本上,Kubeflow 通过让您编写 Kubeflow 样板来帮助您抽象出其他工具的样板。

在 Metaflow 中,您可以使用 Python 装饰器@conda来指定每个步骤的要求——所需的库、内存和计算要求——并且 Metaflow 将自动创建一个包含所有这些要求的容器来执行该步骤。您保存在 Dockerfile 或 YAML 文件上。

Metaflow 允许您在同一个笔记本/脚本中无缝地使用开发和生产环境。您可以在本地机器上运行小型数据集的实验,当您准备好在云上运行大型数据集时,只需添加@batch装饰器即可在AWS Batch@batch上执行它。您甚至可以在不同环境中的同一工作流程中运行不同的步骤。例如,如果一个步骤需要较小的内存占用,它可以在您的本地计算机上运行。但如果下一步需要大量内存占用,您只需添加即可在云端执行。

# Example: sketch of a recommender system that uses an ensemble of two models. 
# Model A will be run on your local machine and model B will be run on AWS.

class RecSysFlow(FlowSpec):
    @step
    def start(self):
        self.data = load_data()
        self.next(self.fitA, self.fitB)

    # fitA requires a different version of NumPy compared to fitB
    @conda(libraries={"scikit-learn":"0.21.1", "numpy":"1.13.0"})
    @step
    def fitA(self):
        self.model = fit(self.data, model="A")
        self.next(self.ensemble)
    
    @conda(libraries={"numpy":"0.9.8"})
    # Requires 2 GPU of 16GB memory
    @batch(gpu=2, memory=16000)
    @step
    def fitB(self):
        self.model = fit(self.data, model="B")
        self.next(self.ensemble)
    
    @step
    def ensemble(self, inputs):
        self.outputs = (
                   (inputs.fitA.model.predict(self.data) +   
                    inputs.fitB.model.predict(self.data)) / 2
                   for input in inputs
        )
        self.next(self.end)

    def end(self):
        print(self.outputs)

机器学习平台

ML 平台团队的经理一家大型流媒体公司向我讲述了他的团队是如何起步的。他最初加入该公司是为了研究他们的推荐系统。为了部署他们的推荐系统,他们需要构建诸如特征管理、模型管理、监控等工具。去年,他的公司意识到这些相同的工具可以被其他 ML 应用程序使用,而不仅仅是推荐系统。他们创建了一个新团队,即 ML 平台团队,目标是提供跨 ML 应用程序的共享基础架构。因为推荐系统团队有最成熟的工具,他们的工具被其他团队采用,推荐系统团队的一些成员被要求加入新的 ML 平台团队。

这个故事代表了自 2020 年初以来的增长趋势。随着每家公司在越来越多的应用程序中发现 ML 的用途,通过为多个应用程序利用相同的工具集而不是为每个应用程序支持单独的工具集可以获得更多收益。这套用于 ML 部署的共享工具构成了 ML 平台。

由于 ML 平台相对较新,ML 平台的确切构成因公司而异。即使在同一家公司内,这也是一个持续的讨论。在这里,我将重点介绍我最常在 ML 平台中看到的组件,包括模型开发、模型存储和特征存储。

评估每个类别的工具取决于您的用例。但是,您可能需要记住以下两个一般方面:

该工具是与您的云提供商一起使用还是允许您在自己的数据中心上使用它

您需要从计算层运行和服务您的模型,并且通常工具仅支持与少数云提供商的集成。没有人愿意为其他工具采用新的云提供商。

无论是开源还是托管服务

如果它是开源的,您可以自己托管它,而不必担心数据安全和隐私。然而,自托管意味着需要额外的工程时间来维护它。如果它是托管服务,您的模型和可能的一些数据将在其服务上,这可能不适用于某些法规。一些托管服务与虚拟私有云一起使用,它允许您将机器部署在自己的云集群中,从而有助于合规性。我们将在“构建与购买”部分中对此进行更多讨论。

让我们从第一个组件开始:模型部署。

模型部署

一旦模型经过训练(并希望经过测试),您希望让用户可以访问其预测能力。在第 7 章中,我们详细讨论了模型如何为其预测服务:在线预测或批量预测。我们还讨论了部署模型的最简单方法是将模型及其依赖项推送到生产中可访问的位置,然后将模型作为端点公开给用户。如果您进行在线预测,此端点将激发您的模型生成预测。如果您进行批量预测,此端点将获取预先计算的预测。

部署服务可以帮助将您的模型及其依赖项推送到生产环境,并将您的模型公开为端点。由于部署是游戏的名称,部署是所有 ML 平台组件中最成熟的,为此存在许多工具。所有主要的云提供商都提供部署工具:AWS 与SageMaker,GCP 与Vertex AI,Azure 与Azure ML,阿里巴巴与机器学习工作室,等等。还有无数的初创公司提供模型部署工具,例如MLflow Models、Seldon、Cortex、Ray Serve等。

在研究部署工具时,重要的是要考虑使用该工具进行在线预测和批量预测有多容易。虽然使用大多数部署服务进行较小规模的在线预测通常很简单,但进行批量预测通常比较棘手。29一些工具允许您将请求一起批处理以进行在线预测,这与批处理预测不同。许多公司都有用于在线预测和批量预测的单独部署管道。例如,他们可能使用 Seldon 进行在线预测,但利用 Databricks 进行批量预测。

模型部署的一个悬而未决的问题是如何在模型部署之前确保模型的质量。在第 9 章中,我们讨论了在生产环境中进行测试的不同技术,例如影子部署、金丝雀发布、A/B 测试等。选择部署服务时,您可能需要检查该服务是否可以让您轻松执行所需的测试。

模型Store

许多公司解雇模型店因为它们听起来很简单。在“模型部署”一节中,我们讨论了如何部署模型,您必须打包模型并将其上传到生产环境中可访问的位置。模型存储建议它存储模型——您可以通过将模型上传到 S3 等存储来做到这一点。然而,事情并不是那么简单。现在想象一下,对于一组输入,您的模型的性能下降了。收到该问题警报的人是 DevOps 工程师,她在调查问题后决定需要通知创建此模型的数据科学家。但是公司里可能有 20 名数据科学家;她应该ping谁?

现在想象一下,合适的数据科学家已经加入进来。数据科学家首先想要在本地重现问题。她仍然拥有用于生成此模型和最终模型的笔记本,因此她启动笔记本并将模型与有问题的输入集一起使用。令她惊讶的是,模型在本地产生的输出与生产中产生的输出不同。许多事情都可能导致这种差异。这里只是几个例子:

  • 现在生产中使用的模型与她在本地使用的模型不同。也许她将错误的模型二进制文件上传到生产环境?

  • 生产中使用的模型是正确的,但使用的功能列表是错误的。也许她忘记在将代码投入生产之前在本地重建代码?

  • 模型正确,特征列表正确,但特征化代码已过时。

  • 模型正确,特征列表正确,特征化代码正确,但数据处理管道有问题。

如果不知道问题的原因,将很难解决它。在这个简单的示例中,我们假设负责的数据科学家仍然可以访问用于生成模型的代码。如果该数据科学家不再有权访问该笔记本,或者她已经辞职或正在休假怎么办?

许多公司已经意识到仅将模型存储在 blob 存储中是不够的。为了帮助调试和维护,尽可能多地跟踪与模型相关的信息很重要。以下是您可能想要存储的八种类型的工件。请注意,此处提到的许多工件是模型卡中应包含的信息,如“创建模型卡”部分所述。

模型定义

这是创建模型形状所需的信息,例如,它使用什么损失函数。如果它是一个神经网络,这包括它有多少隐藏层以及每层有多少参数。

型号参数

这些是实际值你的模型的参数。然后将这些值与模型的形状相结合,重新创建可用于进行预测的模型。一些框架允许您同时导出参数和模型定义。

特征化和预测功能

给定一个预测请求,你如何提取特征并将这些特征输入到模型中以获取预测?特征化和预测功能提供了这样做的指令。这些函数通常包装在端点中。

依赖项

运行模型所需的依赖项(例如 Python 版本、Python 包)通常是一起打包成一个容器。

数据

用于训练此模型的数据可能是指向数据存储位置或数据名称/版本的指针。如果您使用 DVC 等工具对数据进行版本控制,这可能是生成数据的 DVC 提交。

模型生成代码

这是指定如何创建模型的代码,例如:

  • 它使用了哪些框架

  • 它是如何训练的

  • 有关如何创建训练/有效/测试拆分的详细信息

  • 运行实验次数

  • 考虑的超参数范围

  • 最终模型使用的实际超参数集

很多时候,数据科学家通过在笔记本中编写代码来生成模型。拥有更成熟管道的公司会让他们的数据科学家将模型生成代码提交到他们在 GitHub 或 GitLab 上的 Git 存储库中。然而,在许多公司中,这个过程是临时的,数据科学家甚至不检查他们的笔记本。如果负责模型的数据科学家丢失了笔记本或退出或去度假,则无法将生产中的模型映射到生成它以进行调试或维护的代码。

实验工件

这些是期间生成的工件模型开发过程,如“实验跟踪和版本控制”部分所述。这些伪影可以是损失曲线之类的图形。这些工件可以是原始数字,例如模型在测试集上的性能。

标签

这包括帮助模型发现和过滤,例如所有者(此模型的所有者的个人或团队)或任务(此模型解决的业务问题,如欺诈检测)。

大多数公司存储这些工件的一个子集,但不是全部。一家公司存放的文物可能不在同一个地方,而是分散的。例如,模型定义和模型参数可能在 S3 中。包含依赖项的容器可能在 ECS(弹性容器服务)中。数据可能在雪花中。实验工件可能在权重和偏差中。特征化和预测函数可能在 AWS Lambda 中。一些数据科学家可能会在自述文件中手动跟踪这些位置,但该文件很容易丢失。

能够存储足够多的通用用例的模型存储远不是一个已解决的问题。在撰写本书时,MLflow 无疑是最流行的模型商店,它与主要的云提供商无关。然而,Stack Overflow 上的六个顶级 MLflow 问题中有三个是关于在 MLflow 中存储和访问工件的,如图 10-9所示。模型店要改头换面了,我希望在不久的将来有一家初创公司能挺身而出解决这个问题。

由于缺乏好的模型店解决方案,像 Stitch Fix 这样的公司决定建立自己的模型店。图 10-10显示了 Stitch Fix 的模型存储的工件轨道。当一个模型上传到他们的模型存储时,这个模型带有序列化模型的链接、运行模型所需的依赖项(Python 环境)、创建模型代码生成的 Git 提交(Git 信息)、标签(至少指定拥有模型的团队)等。

【Designing ML Systems】第 10 章 :MLOps 的基础设施和工具_第10张图片

图 10-10。Stitch Fix 的模型商店跟踪的工件。资料来源:改编自 Stefan Krawczyk 为CS 329S(斯坦福)提供的幻灯片。

特色商店

“特色商店”越来越多可以被不同的人用来指代非常不同的事物的术语。ML 从业者已多次尝试定义特征存储应具有的功能。30本质上,特征存储可以帮助解决三个主要问题:特征管理、特征转换和特征一致性。特征存储解决方案可能会解决以下一个或多个问题:

特征管理

一家公司可能有多个 ML 模型,每个模型使用很多特征。早在 2017 年,Uber 就有大约 10,000 个跨团队的功能!31通常情况下,用于一个模型的特征可能对另一个模型有用。例如,A 团队可能有一个模型来预测用户流失的可能性,而 B 团队有一个模型来预测免费用户转化为付费用户的可能性。这两个模型可以共享许多功能。如果团队 A 发现功能 X 非常有用,那么团队 B 也可以利用它。

功能商店可以帮助团队共享和发现功能,以及管理每个功能的角色和共享设置。例如,您可能不希望公司中的每个人都可以访问公司或其用户的敏感财务信息。在这种能力下,可以将特征存储视为特征目录。功能管理工具的示例是Amundsen(在 Lyft 开发)和DataHub(在 LinkedIn 开发)。

特征计算32

特征工程逻辑,之后被定义,需要计算。例如,特征逻辑可能是:使用昨天的平均备餐时间。计算部分涉及实际查看您的数据并计算该平均值。

在上一点中,我们讨论了多个模型如何共享一个特征。如果这个特征的计算不是太昂贵,那么每次模型需要它时计算这个特征可能是可以接受的。但是,如果计算成本很高,您可能只想在第一次需要时执行一次,然后将其存储起来以供特征使用。

特征存储可以帮助执行特征计算和存储该计算的结果。在这种能力下,特征存储就像一个数据仓库。

特征一致性

在第 7 章中,我们谈到了关于同一模型有两个独立管道的问题:训练管道从历史数据中提取批量特征,推理管道提取流特征。在开发过程中,数据科学家可能会使用 Python 定义特征并创建模型。但是,为了提高性能,生产代码可能会用另一种语言(例如 Java 或 C)编写。

这意味着在开发期间用 Python 编写的功能定义可能需要转换为生产中使用的语言。所以你必须写两次相同的特征,一次用于训练,一次用于推理。首先,它既烦人又耗时。其次,它为错误创造了额外的表面,因为生产中的一个或多个特征可能与训练中的对应特征不同,从而导致奇怪的模型行为。

现代特征存储的一个关键卖点是它们统一了批量特征和流特征的逻辑,确保了训练期间的特征和推理期间的特征之间的一致性。

特征商店是一个较新的类别,在 2020 年左右才开始起飞。虽然人们普遍认为特征商店应该管理特征定义并确保特征一致性,但它们的确切容量因供应商而异。一些特征存储只管理特征定义,而不从数据中计算特征;一些特色商店两者兼而有之。一些特征存储还进行特征验证,即检测某个特征何时不符合预定义的模式,而一些特征存储将该方面留给监控工具。

在撰写本书时,最受欢迎的开源功能商店是 Feast。但是,Feast 的优势在于批处理功能,而不是流功能。Tecton 是一个完全托管的功能存储,承诺能够处理批处理功能和在线功能,但它们的实际牵引力很慢,因为它们需要深度集成。SageMaker 和 Databricks 等平台也提供了自己对特征存储的解释。我在 2022 年 1 月调查的 95 家公司中,只有大约 40% 使用特色商店。在使用特色商店的人中,有一半人建立了自己的特色商店。

构建与购买

在本章的开头,我们讨论了为您的 ML 需求设置正确的基础架构是多么困难。您需要什么基础架构取决于您拥有的应用程序以及运行这些应用程序的规模。

你需要投资多少基础设施还取决于您想在内部建造什么以及您想购买什么。例如,如果您想使用完全托管的 Databricks 集群,您可能只需要一名工程师。但是,如果您想托管自己的 Spark Elastic MapReduce 集群,您可能还需要五个人。

在一个极端情况下,您可以将所有 ML 用例外包给提供端到端 ML 应用程序的公司,然后您可能需要的唯一基础设施就是数据移动:将数据从应用程序转移到供应商,并将预测从该供应商转移回您的用户。您的基础设施的其余部分由您的供应商管理。

在另一个极端,如果您是一家处理敏感数据的公司,这些数据会阻止您使用其他公司管理的服务,您可能需要在内部构建和维护所有基础架构,甚至拥有自己的数据中心。

然而,大多数公司都不是这两个极端。如果您为其中一家公司工作,您可能会拥有由其他公司管理的一些组件和内部开发的一些组件。例如,您的计算可能由 AWS EC2 管理,您的数据仓库由 Snowflake 管理,但您拥有自己的功能存储和监控仪表板。

您的构建与购买决策取决于许多因素。在这里,我们将讨论我在与基础设施负责人讨论他们如何评估这些决策时经常遇到的三个常见问题:

贵公司所处的阶段

一开始,您可能希望利用供应商解决方案尽快开始,以便您可以将有限的资源集中在产品的核心产品上。但是,随着您的用例增长,供应商成本可能会变得过高,您投资自己的解决方案可能会更便宜。

您认为什么是您公司的重点或竞争优势

Stitch Fix 的 ML 平台团队经理 Stefan Krawczyk 向我解释了他的构建与购买决策:“如果这是我们想要真正擅长的事情,我们将在内部进行管理。如果没有,我们将使用供应商。” 对于技术领域以外的绝大多数公司(例如零售、银行、制造公司)来说,ML 基础设施不是他们的重点,因此他们倾向于购买。当我与这些公司交谈时,他们更喜欢托管服务,甚至是单点解决方案(例如,为他们解决业务问题的解决方案,如需求预测服务)。对于许多以技术为竞争优势的科技公司,其强大的工程团队更愿意控制他们的堆栈,他们往往倾向于构建。如果他们使用托管服务,他们可能更喜欢该服务是模块化和可定制的,

可用工具的成熟度

例如,您的团队可能决定您需要一个模型商店,并且您更愿意使用供应商,但没有成熟的供应商可以满足您的需求,因此您必须构建自己的功能商店,也许在开源解决方案。

这就是行业早期采用 ML 时发生的情况。早期采用者的公司,即大型科技公司,会构建自己的基础设施,因为没有足够成熟的解决方案来满足他们的需求。这导致了每家公司的基础设施都不同的情况。几年后,解决方案产品成熟。然而,这些产品很难卖给大型科技公司,因为不可能创建一个适用于大多数定制基础设施的解决方案。

在我们构建 Claypot AI 时,其他创始人实际上建议我们避免向大型科技公司出售产品,因为如果我们这样做,我们将陷入他们所谓的“集成地狱”——花更多时间将我们的解决方案与定制集成基础设施,而不是构建我们的核心功能。他们建议我们专注于拥有更清洁的基础的初创公司。

有人认为建房比买房便宜,其实不一定。构建意味着您必须聘请更多工程师来构建和维护您自己的基础架构。它还可能伴随着未来的成本:创新成本。由于集成问题,内部定制基础架构很难采用可用的新技术。

构建与购买决策是复杂的、高度依赖环境的,并且可能基础设施负责人会花费很多时间考虑。Better.com 的前 CTO Erik Bernhardsson 在一条推文中说:“CTO 最重要的工作之一是选择供应商/产品,而且随着基础设施空间的快速增长,其重要性每年都在迅速上升。 ” 33一小部分不可能解决所有的细微差别。但我希望本节为您提供一些开始讨论的指导。

概括

如果你一直和我在一起直到现在,我希望你同意将 ML 模型投入生产是一个基础设施问题。为了使数据科学家能够开发和部署 ML 模型,设置正确的工具和基础设施至关重要。

在本章中,我们介绍了 ML 系统所需的不同基础架构层。我们从存储和计算层开始,它为任何需要密集数据和计算资源的工程项目(如 ML 项目)提供重要资源。存储和计算层高度商品化,这意味着大多数公司为他们使用的确切存储和计算量支付云服务费用,而不是建立自己的数据中心。然而,虽然云供应商让一家公司很容易上手,但随着公司的发展,他们的成本变得高昂,越来越多的大公司正在考虑从云中迁移到私有数据中心。

然后,我们继续讨论数据科学家编写代码并与生产环境交互的开发环境。因为开发环境是工程师花费大部分时间的地方,所以开发环境的改进直接转化为生产力的提高。公司改善开发环境可以做的第一件事就是为在同一团队工作的数据科学家和 ML 工程师标准化开发环境。我们在本章中讨论了为什么推荐标准化以及如何做到这一点。

然后,我们讨论了一个基础设施主题,该主题与数据科学家的相关性在过去几年中一直备受争议:资源管理。资源管理对数据科学工作流程很重要,但问题是是否应该期望数据科学家来处理它。在本节中,我们追溯了资源管理工具从 cron 到调度器再到编排器的演变。我们还讨论了为什么 ML 工作流不同于其他软件工程工作流,以及为什么它们需要自己的工作流管理工具。我们比较了各种工作流管理工具,例如 Airflow、Argo 和 Metaflow。

ML 平台是最近随着 ML 采用的成熟而出现的团队。由于它是一个新兴概念,因此对于 ML 平台应该由什么组成仍然存在分歧。我们选择专注于对大多数 ML 平台必不可少的三组工具:部署、模型存储和特征存储。我们跳过了对 ML 平台的监控,因为它已经在第 8 章中介绍过。

在基础架构上工作时,一个问题一直困扰着工程经理和 CTO:构建还是购买?我们以一些讨论点结束了本章,我希望这些讨论点可以为您或您的团队提供足够的背景来做出这些艰难的决定。

你可能感兴趣的:(机器学习(ML),人工智能)