机器学习应用程序在我们的行业中变得越来越流行,但是与更传统的软件(例如: Web 服务或移动应用程序)相比,开发、部署和持续改进它们的过程更加复杂。 它们会在三个轴上发生变化:代码本身、模型和数据。 他们的行为通常很复杂且难以预测,而且他们更难测试、更难解释、更难改进。 机器学习的持续交付 (CD4ML) 是将持续交付原则和实践引入机器学习应用程序的学科。
机器学习持续交付的简介和定义
Sculley 等人发表的著名的谷歌论文中谈到:在 2015 年的“机器学习系统中的隐藏技术债务”中,他们强调在现实世界的机器学习 (ML) 系统中,只有一小部分由实际的 ML 代码组成,周围有大量的基础设施和流程来支持它们的演进。他们还讨论了此类系统中可能积累的许多技术债务的来源,其中一些与数据依赖、模型复杂性、可重复性、测试、监控和处理外部世界的变化有关。
许多相同的问题也存在于传统的软件系统中,而持续交付已经成为一种方法,可以带来自动化、质量和纪律,以创建一个可靠和可重复的过程,从而将软件发布到生产中。
Jez Humble 和 David Farley 在他们的开创性著作《持续交付》中指出:
持续交付是能够以可持续的方式安全、快速地将所有类型的更改(包括新功能、配置更改、错误修复和实验等)投入生产或交到用户手中。
除了代码之外,对 ML 模型和用于训练它们的数据的更改是另一种需要管理并融入软件交付过程的更改,如下图展示了机器学习应用程序中的 3 个变化轴—数据、模型和代码,以及它们发生变化的几个原因。
考虑到这一点,我们可以扩展持续交付的定义,以纳入现实世界机器学习系统中存在的新元素和挑战,我们称之为“机器学习的持续交付 (CD4ML)”。
机器学习的持续交付 (CD4ML) 是一种软件工程方法,在这种方法中,跨职能团队以小而安全的增量生成基于代码、数据和模型的机器学习应用程序,这些应用程序可以在较短的周期内随时复制和可靠地发布。
该定义包含以下所有的基本原则:
-
软件工程方法:它使团队能够有效地生产高质量的软件。
-
跨职能团队:在数据工程、数据科学、机器学习工程、开发、运维和其他知识领域拥有不同技能和工作流程的专家以协作的方式一起工作,强调每个团队成员的技能和优势。
-
基于代码、数据和机器学习模型生产软件:ML 软件生产过程的所有工件都需要不同的工具和工作流,必须相应地进行版本控制和管理。
-
小而安全的增量:软件工件的发布被分成小增量,这允许围绕其结果的变化水平进行可视化和控制,从而增加流程的安全性。
-
可重现且可靠的软件发布:虽然模型输出可能是不确定的且难以重现,但将 ML 软件发布到生产中的过程是可靠且可重现的,并尽可能的利用自动化。
-
随时发布软件:ML 软件可以随时交付到生产,这一点很重要。即使组织不想一直交付软件,它也应该始终处于可发布状态。这使得关于何时发布它的决定成为业务决策,而不是技术决策。
-
短改造周期:短周期意味着开发周期是几天甚至几小时,而不是几周、几个月甚至几年。实现这一目标的关键是实现内置特征流程的自动化。这将创建一个反馈循环,允许您通过从生产中的行为学习来调整模型。
在本文中,我们将描述我们在实现 CD4ML 时发现的重要技术组件,使用 ML 应用程序 示例来解释概念并演示如何一起使用不同的工具来实现完整的端到端流程。在适当的情况下,我们将强调所选工具之外的其他工具的选择。随着行业实践的成熟,我们还将讨论进一步的开发和研究领域。
用于销售预测的机器学习应用程序
自 2016 年以来,我们就开始考虑如何将持续交付应用到机器学习系统中,并且我们发布并展示了一个来自我们使用 AutoScout 构建的客户项目的案例研究,以预测在其平台上发布的汽车的价格。
但是,我们决定基于一个公共问题和数据集构建一个示例 ML 应用程序来说明 CD4ML 实现,因为我们不允许使用来自真实客户的代码示例。
此应用程序解决了许多零售商面临的一个常见的预测问题:尝试根据历史数据预测给定产品未来的销量。我们为厄瓜多尔大型杂货零售商 Corporación Favorita 发布的 Kaggle 问题构建了一个简化的解决方案。出于我们的目的,我们已经合并并简化了他们的数据集,因为我们的目标不是找到最好的预测(这是一项由您的数据科学家更好地处理的工作),而是展示如何实施 CD4ML。
我们采用监督学习算法和流行的 scikit-learn
Python 库,并使用标记的输入数据训练预测模型,将该模型集成到一个简单的 Web 应用程序中,然后将其部署到云中的生产环境中。下图显示了训练 ML 模型、将其与 Web 应用程序集成并部署到生产中的过程。
部署后,我们的 Web 应用程序允许用户选择产品和未来的日期,该模型将输出其对该产品当天销售量的预测。下面是用于展示我们的模型的 Web 界面。
端到端 ML 系统面临的挑战
虽然这是我们讨论的一个很好的起点,但端到端实施这个过程已经存在两个挑战。
第一个挑战是组织结构:不同的团队可能拥有流程的不同部分,并且存在交接(对如何跨越这些界限没有明确的期望)。
数据工程师可能正在构建流水线以使数据可访问,而数据科学家则为构建和改进 ML 模型而担忧。 然后机器学习工程师或开发人员将不得不担心如何集成该模型并将其发布到生产中。
大型组织中常见的功能孤岛可能会造成障碍,扼杀将 ML 应用程序部署到生产的端到端流程自动化的能力,具体如下图所示。
这会导致延误和分歧(friction)。一个常见的症状是模型只能在实验室环境中工作,并且永远不会离开概念验证阶段。或者,如果它们以手动临时方式投入生产,它们就会变得陈旧且难以更新。
第二个挑战是技术上的:如何使流程具有可重复性和可审计性。由于这些团队使用不同的工具并遵循不同的工作流程,因此很难实现端到端的自动化。除了代码之外,还有更多的制品需要管理,并且对它们进行版本控制并不简单。其中一些可能非常大,需要更复杂的工具来有效地存储和检索它们。
解决组织挑战超出了本文的范围,但我们可以将我们从敏捷和 DevOps 中学到的东西带入并建立跨职能和以结果为导向的团队,其中包括来自不同学科的专家,以交付端到端的 ML 系统。如果这在您的组织中是不可能的,至少要鼓励去打破这些障碍,让他们尽早并更频繁的在整个过程中进行协作。
本文的其余部分将探讨我们针对技术挑战找到的解决方案。我们将深入研究每个技术组件,并慢慢改进和扩展端到端流程,使其更加健壮。
CD4ML 的技术组件
当我们考虑如何使用机器学习解决预测问题时,第一步是了解数据集。在本例中,它是 CSV 文件的集合,包含以下信息:
- 产品,例如:它们的分类以及它们是否易腐烂
- 商店,比如,它们的位置以及它们是如何聚集在一起的
- 特殊事件,例如:公共假期、季节性事件或 2016 年厄瓜多尔发生的 7.8 级地震
- 销售交易,包括:给定产品、日期和地点的销售量
在这个阶段,数据分析师和数据科学家通常会执行某种探索性数据分析 (EDA) 以了解数据的形状,并识别广泛的模式和异常值。例如,我们发现售出数量为负数的产品,我们将其解释为退货。由于我们只打算探索销售而不是退货,因此我们将它们从我们的训练数据集中删除。
在许多组织中,训练有用的 ML 模型所需的数据可能不会完全按照数据科学家可能需要的方式构建,因此,需要的第一个技术组件:可发现和可访问的数据。
可发现和可访问的数据
数据流水线
流水线是一个重载的术语,尤其是在 ML 应用程序中。 我们希望将“数据流水线”定义为将输入数据通过一系列转换阶段,生成数据作为输出的过程。 输入和输出数据都可以获取并存储在不同的位置,例如:数据库、流、文件等。转换阶段通常在代码中定义,尽管一些 ETL 工具允许您以图形形式表示它们。它们可以作为批处理作业执行,也可以作为长时间运行的流应用程序执行。
对于 CD4ML,我们将数据流水线视为一个制品,可以对其进行版本控制、测试和部署到目标执行环境。
最常见的数据源将是您的核心事务系统。但是,从组织外部引入其他数据源也很有价值。我们发现了一些收集和提供数据的常见模式,例如:使用数据湖架构、更传统的数据仓库、实时数据流的集合,或者最近,我们正在尝试分散式的数据网格架构。
对于 CD4ML,我们将数据流水线视为工件,可以对其进行版本控制、测试和部署到目标执行环境。
无论您拥有哪种架构风格,重要的是数据易于发现和访问。数据科学家越难找到他们需要的数据,他们构建有用模型的时间就越长。我们还应该考虑到他们会希望在输入数据之上设计新功能,这可能有助于提高他们模型的性能。
在我们的示例中,在进行初始探索性数据分析之后,决定将多个文件反规范化为单个 CSV 文件,并清理不相关或可能在模型中引入不需要的噪声的数据点(如负销售)。然后,我们将输出存储在云存储系统中,例如:Amazon S3
、Google Cloud Storage
或 Azure Storage Account
。
使用这个文件来表示输入训练数据的快照,我们能够设计一种简单的方法来根据文件夹结构和文件命名约定对我们的数据集进行版本控制。
数据版本控制是一个广泛的主题,因为它可以在两个不同的轴上发生变化:架构的结构变化,以及随时间推移的数据实际采样。 我们的数据科学家 Emily Gorcenski 在这篇博文中更详细地介绍了这个主题,但在本文后面,我们将讨论随着时间的推移对数据集进行版本控制的其他方法。
值得注意的是,在现实世界中,您可能会有更复杂的数据流水线将数据从多个来源移动到数据科学家可以访问和使用的地方。
可重现的模型训练
机器学习流水线
“机器学习流水线”,也称为“模型训练流水线”,是以数据和代码为输入,生成经过训练的 ML 模型作为输出的过程。这个过程通常涉及数据清洗和预处理、特征工程、模型和算法选择、模型优化和评估。
虽然开发此过程包含数据科学家工作流程的主要部分,但出于 CD4ML 的目的,我们将 ML 流水线选择模型训练过程视为最后的自动化来完成。
一旦数据可用,我们将进入模型构建工作流(迭代数据科学)中。 这通常涉及将数据拆分为训练集和验证集,尝试不同的算法组合,并调整它们的参数和超参数。这会产生一个可以根据验证集进行评估的模型,以评估其预测的质量。 这个模型训练过程的一步一步变成了机器学习流水线。
下图展示了我们如何针对销售预测问题构建 ML 流水线以及使用 DVC 实现自动化的3个步骤,突出显示了不同的源代码、数据和模型组件。 输入数据、中间训练和验证数据集以及输出模型可能是大文件,我们不希望将其存储在源代码控制存储库(如:github/gitlab)中。 此外,流水线的阶段通常在不断变化,这使得很难在数据科学家的本地环境之外重现它们。
为了在代码中是模型训练过程定型,我们使用了一个名为 DVC(数据科学版本控制)的开源工具。 它提供了与 Git 类似的语义,但也解决了一些特定于 ML 的问题:
- 它有多个后端插件,用于在源代码控制存储库之外的外部存储中获取和存储大文件;
- 它可以跟踪这些文件的版本,允许我们在数据更改时重新训练我们的模型;
- 它跟踪用于执行 ML 流水线的依赖图和命令,允许在其他环境中重现该过程;
- 它可以与 Git 分支集成,以允许多个实验共存;
例如,我们可以使用三个 dvc run
命令配置下图中的 ML 流水线(-d 指定依赖项,-o 指定输出,-f 是记录该步骤的文件名,-M 是结果指标):
dvc run -f input.dvc \ ➊-d src/download_data.py -o data/raw/store47-2016.csv python src/download_data.pydvc run -f split.dvc \ ➋-d data/raw/store47-2016.csv -d src/splitter.py \-o data/splitter/train.csv -o data/splitter/validation.csv python src/splitter.pydvc run ➌-d data/splitter/train.csv -d data/splitter/validation.csv -d src/decision_tree.py \-o data/decision_tree/model.pkl -M results/metrics.json python src/decision_tree.py
每次运行都会创建一个相应的文件,该文件可以提交给版本控制,并允许其他人通过执行 dvc repro
命令来重现整个 ML 流水线。
一旦找到合适的模型,我们会将其视为需要进行版本控制并部署到生产环境的制品。 使用 DVC,我们可以使用 dvc push
和 dvc pull
命令从外部存储发布和获取它。
您可以使用其他开源工具来解决这些问题:
- Pachyderm 使用容器来执行流水线的不同步骤,并通过跟踪数据提交和基于此优化流水线执行来解决数据版本控制和数据来源的问题。
- MLflow Projects 定义了一种文件格式来指定环境和流水线的步骤,并提供 API 和 CLI 工具来本地或远程运行项目。
我们选择 DVC 是因为它是一个简单的 CLI 工具,可以很好地解决这部分问题。
模型服务(Model Serving)
一旦找到合适的模型,我们需要决定如何在生产中提供和使用它。我们已经看到了一些实现这一目标的模式:
- 嵌入模型:这是一种更简单的方法,您可以将模型制品视为在消费应用程序中构建和打包的依赖项。从现在开始,您可以将应用程序制品和版本视为应用程序代码和所选模型的组合。
- 模型部署为单独的服务:在这种方法中,模型被包装在一个服务中,该服务可以独立于消费应用程序进行部署。这允许独立发布对模型的更新,但它也可能在推理时引入延迟,因为每个预测都需要某种远程调用。
- 模型作为数据发布:在这种方法中,模型也被独立处理和发布,但消费应用程序将在运行时将其作为数据摄取。我们已经看到这在流式/实时场景中使用,应用程序可以订阅每当发布新模型版本时发布的事件,并将它们摄取到内存中,同时继续使用以前的版本进行预测。蓝绿部署或金丝雀发布等软件发布模式也可以应用于此场景。
在我们的示例中,我们决定使用更简单的嵌入模型的方法,因为我们的消费应用程序也是用 Python 编写的。我们的模型导出为序列化对象(pickle 文件)并由 DVC 推送到存储。在构建我们的应用程序时,我们将其拉取并嵌入到同一个 Docker 容器中。从那时起,Docker 镜像成为我们的应用程序+模型的制品,它被版本化并部署到生产环境中。
除了使用 pickle 序列化模型对象之外,还有其他工具选项可以实现嵌入模型模式。 MLeap 为导出/导入 Spark、scikit-learn 和 Tensorflow 模型提供了一种通用的序列化格式。还有与语言无关的交换格式来共享模型,例如:PMML、PFA 和 ONNX。其中,一些序列化选项也适用于实现“模型即数据”模式。
另一种方法是使用 H2O 之类的工具将模型导出为 POJO(在 JAR Java 库中);然后,您可以将其作为依赖项添加到应用程序中。这种方法的好处是您可以使用数据科学家熟悉的语言(例如:Python 或 R)训练模型,并将模型导出为在不同目标环境 (JVM) 中运行的编译二进制文件,这样在推理时会更快。
为了实现“模型即服务”模式,许多云提供商都有工具和 SDK 来包装您的模型以部署到他们的 MLaaS(机器学习即服务)平台,例如:Azure 机器学习、AWS Sagemaker 或 Google AI 平台。另一种选择是使用像 Kubeflow 这样的工具,这是一个旨在在 Kubernetes 上部署 ML 工作流的项目,尽管它试图解决的不仅仅是服务于部分问题的模型。
MLflow Models 试图提供一种标准方法来打包不同风格的模型,供不同的下游工具使用,有些是“模型即服务”,有些是“嵌入模型”模式。可以说,这是当前的开发领域,各种工具和供应商正在努力简化这项任务。但这意味着还没有明确的标准(开放或专有)可以被视为明显的赢家,因此您需要评估适合您需求的正确选项。
值得注意的是,无论您决定使用哪种模式,模型与其消费者之间总是存在隐含的契约。该模型通常会期望某种形状的输入数据,如果数据科学家更改该契约以要求新输入或添加新功能,这可能会导致集成的问题并破坏使用它的应用程序。这将我们引向测试的主题。
机器学习中的测试和质量
可以在 ML 工作流程中引入不同类型的测试。虽然某些方面本质上是不确定的并且难以自动化,但有许多类型的自动化测试可以增加价值并提高 ML 系统的整体质量:
- 验证数据:我们可以添加测试来验证输入数据是否符合预期的模式,或者验证我们对其有效值的假设,例如:它们在预期范围内,或者不为空。对于工程特征,我们可以编写单元测试来检查它们的计算是否正确,例如:数字特征被缩放或归一化,one-hot 编码向量包含全零和单个 1,或缺失值被适当替换。
- 验证组件集成:我们可以使用类似的方法来测试不同服务之间的集成,使用契约测试(Contract Tests)来验证预期的模型接口是否与消费应用程序兼容。当您的模型以不同格式生产时,另一种相关的测试类型是确保导出的模型仍然产生相同的结果。这可以通过对相同的验证数据集运行原始模型和生产模型,并比较相同的结果来实现。
- 验证模型质量:虽然 ML 模型的性能是不确定的,但数据科学家通常会收集和监控许多指标来评估模型的性能,例如:错误率、准确度、AUC、ROC、混淆矩阵、精度、召回率等。它们在参数和超参数优化期间也很有用。作为一个简单的质量阀门,我们可以使用这些指标在我们的流水线中引入阈值测试或棘轮测试(ratcheting),以确保新模型不会相对于已知的性能基线降低。
- 验证模型偏差和公平性:虽然我们可能会在整体测试和验证数据集上获得良好的性能,但检查模型如何针对特定数据切片的基线执行也很重要。例如,您可能在训练数据中存在固有偏差,与现实世界中的实际分布相比,某个特征的给定值(例如:种族、性别或地区)的数据点要多得多,因此,检查不同数据切片的性能很重要。像 Facets 这样的工具可以帮助您可视化这些切片以及数据集中各个特征的值分布。
在我们的示例应用程序中,Favorita 定义的评估指标是归一化错误率。我们编写了一个简单的 PyUnit 阈值测试,如果错误率超过 80% 就会中断,并且可以在发布新模型版本之前执行此测试,以展示我们如何防止不良模型得到提升。
虽然这些是更容易自动化的测试示例,但更全面地评估模型的质量却更难。随着时间的推移,如果我们总是针对相同的数据集计算指标,我们就会开始过度拟合。当您已经拥有其他模型时,您需要确保新模型版本不会因看不见的数据而降级。因此,管理和整理测试数据变得更加重要。
当模型被分发或导出以供不同的应用程序使用时,您还可以发现工程特征在模型训练和模型服务之间的计算方式不同的问题。帮助捕获这些类型问题的一种方法是将保留数据集与模型制品一起分发,并允许消费应用程序团队在集成后针对保留数据集重新评估模型的性能。这相当于传统软件开发中的广义集成测试。
也可以考虑其他类型的测试,但我们认为在部署流水线中添加一些手动阶段也很重要,以显示有关模型的信息并允许人们决定是否应该提升它们。这使您可以对机器学习治理流程进行建模,并引入对模型偏差、模型公平性的检查,或为人类收集可解释性信息以了解模型的行为方式。
随着更多类型的测试,它会让您重新思考测试金字塔的形状:您可以考虑为每种类型的制品(代码、模型和数据)设置单独的金字塔,还可以考虑如何组合它们,如下图 展示了如何在 CD4ML 中为数据、模型和代码组合不同的测试金字塔的示例。
总体而言,ML 系统的测试和质量更加复杂,应该成为另一篇深入、独立的文章的主题。
实验跟踪
为了支持这个治理过程,重要的是捕获和显示信息,这些信息将允许人们决定是否应该将哪个模型推广到生产环境中(由于数据科学过程非常以研究为中心,您通常会同时尝试多个实验,其中许多实验可能永远无法投入生产)。
这种在研究阶段的实验方法与更传统的软件开发过程不同,因为我们预计其中许多实验的代码将被丢弃,只有少数被认为值得投入生产。出于这个原因,我们需要定义一种方法来跟踪它们。
在我们的案例中,我们决定遵循 DVC 建议的方法,使用不同的 Git 分支来跟踪源代码控制中的不同实验。尽管这违背了我们在单个主干上实践持续集成的偏好。 DVC 可以从在不同分支或标签中运行的实验中获取和显示指标,从而可以轻松地在它们之间导航。
在传统软件开发中使用特性分支进行开发的一些缺点是,如果分支长期存在,它可能会导致合并痛苦,它会阻止团队更积极地重构,因为更改会影响代码库更广的范围,并且会阻碍持续集成 (CI) 的实践,因为它迫使您为每个分支设置多个作业,并且正确的集成被延迟,直到代码合并回主线。
对于 ML 实验,我们预计大多数分支永远不会被集成,并且实验之间的代码变化通常并不显着。从 CI 自动化的角度来看,我们实际上确实希望为每个实验训练多个模型,并收集指标来告知我们哪个模型可以移动到部署流水线的下一阶段。
除了 DVC,我们用来帮助进行实验跟踪的另一个工具是 MLflow Tracking。它可以部署为托管服务,并提供 API 和 Web 界面来可视化多个实验运行及其参数和性能指标,如下图展示了MLflow Tracking Web UI 显示实验运行、参数和指标。
为了支持这个实验过程,拥有弹性基础设施的好处也很重要,因为您可能需要多个可用环境(有时还需要专用硬件)来进行训练。 基于云的基础设施非常适合这一点,许多公共云提供商正在构建服务和解决方案来支持这个过程的各个方面。
模型部署
在我们的简单示例中,我们只是在尝试构建一个模型,该模型嵌入并部署到应用程序中。在现实世界中,部署可能会带来更复杂的场景:
- 多个模型:有时您可能有多个模型执行相同的任务。例如,我们可以训练一个模型来预测每种产品的需求。在这种情况下,将模型部署为单独的服务可能更适合使用应用程序通过单个 API 调用获得预测。并且您可以稍后发展该已发布接口背后需要多少模型。
- 影子模型:在考虑替换生产中的模型时,此模式很有用。您可以将新模型与当前模型并排部署,作为影子模型,并发送相同的生产流量以收集有关影子模型如何执行的数据,然后再进行推广。
- 竞争模型:稍微复杂一点的场景是,当您在生产环境中尝试模型的多个版本时(例如 A/B 测试)以找出哪个更好。这里增加的复杂性来自确保将流量重定向到正确模型所需的基础设施和路由规则,并且您需要收集足够的数据以做出具有统计意义的决策,这可能需要一些时间。评估多个竞争模型的另一种流行方法是 Multi-Armed Bandits(多臂老虎机问题),它还要求您定义一种方法来计算和监控与使用每个模型相关的奖励,将其应用于 ML 是一个活跃的研究领域。我们也开始看到一些工具和服务出现,例如:
Seldon core
和Azure Personalizer
。 - 在线学习模型:与我们到目前为止讨论的模型不同,这些模型是离线训练并用于在线预测的,在线学习模型使用的算法和技术可以随着新数据的到来而不断提高其性能。他们在生产中不断学习。这带来了额外的复杂性,因为如果没有提供相同的数据,将模型版本化为静态制品将不会产生相同的结果。您不仅需要对训练数据进行版本化,还需要对会影响模型性能的生产数据进行版本化。
再次,为了支持更复杂的部署场景,您将受益于使用弹性基础设施。除了可以在生产中运行这些多个模型之外,它还允许您在需要时启动更多基础架构来提高系统的可靠性和可扩展性。
持续交付编排
部署流水线
在本文中,我们讨论了数据流水线和 ML 流水线,但还有另一种类型的流水线需要了解:部署流水线。部署流水线可自动化将软件从版本控制投入生产的过程,包括所有阶段、批准、测试和部署到不同环境。
在 CD4ML 中,我们可以将自动和手动 ML 治理阶段建模到我们的部署流水线中,以帮助检测模型偏差、公平性,或者为人类引入可解释性来决定模型是否应该进一步向生产发展。
有了所有主要构建块,就需要将所有内容联系在一起,这就是我们的持续交付编排工具的用武之地。这个领域有很多工具可供选择,其中大多数都提供了配置和执行部署流水线的方法,以构建和发布软件到生产环境。在 CD4ML 中,我们有额外的编排要求:
- 基础设施的配置和机器学习流水线的执行,以训练和捕获来自多个模型实验的指标;
- 我们的数据流水线的构建、测试和部署过程;
- 不同类型的测试和验证来决定推广哪些模型;
- 提供基础设施并将我们的模型部署到生产中。
我们选择使用 GoCD 作为我们的持续交付工具,因为它是以流水线的概念作为首要关注点构建的。不仅如此,它还允许我们通过组合不同的流水线、它们的触发器以及在流水线阶段之间定义手动或自动升级步骤来配置复杂的工作流和依赖项。
在我们的简化示例中,我们尚未构建任何复杂的数据流水线或基础设施配置,但我们演示了如何组合两个 GoCD 流水线,如下图演示了在 GoCD 中结合机器学习流水线和应用程序部署流水线:
- 机器学习流水线:在 GoCD 代理中执行模型训练和评估,以及执行基本阈值测试以确定模型是否可以提升。如果模型很好,我们执行
dvc push
命令将其发布为制品。 - 应用程序部署流水线:构建和测试应用程序代码,使用
dvc pull
从上游流水线获取改进的模型,将包含模型和应用程序的新组合制品打包为 Docker 镜像,并将它们部署到 Kubernetes生产集群。
随着时间的推移,可以扩展 ML 流水线以并行执行多个实验(GoCD 的扇出/扇入(fan-out/fan-in)模型支持的特性),并定义您的模型治理流程以检查偏差、公平性、正确性和其他类型阀门,以做出有关将哪种模型推广和部署到生产中的明智决策。
最后,持续交付编排的另一个方面是定义回滚流程,以防部署的模型在生产中表现不佳或不正确。这为整个过程增加了另一个安全保障(safety net)。
模型监控和可观察性
现在模型已经上线,我们需要了解它在生产中的表现并关闭数据反馈循环。在这里,我们可以重用可能已经为您的应用程序和服务提供的所有监控和可观察性基础设施。
日志聚合和指标收集工具通常用于从实时系统中捕获数据,例如:业务 KPI、软件可靠性和性能指标、用于故障排除的调试信息以及其他在出现异常情况时可能触发警报的指标。我们还可以利用这些相同的工具来捕获数据,以了解我们的模型的行为方式,例如:
- 模型输入:将哪些数据提供给模型,从而了解任何训练服务偏差。模型输出:模型从这些输入中做出哪些预测和建议,以了解模型在实际数据中的表现。
- 模型可解释性输出:模型系数、ELI5 或 LIME 输出等指标允许进一步调查以了解模型如何进行预测,以识别在训练期间未发现的潜在过拟合或偏差。
- 模型输出和决策:在给定生产输入数据的情况下,我们的模型做出了哪些预测,以及根据这些预测做出了哪些决策。有时应用程序可能会选择忽略模型并根据预定义的规则做出决定(或避免未来的偏差)。
- 用户行为和奖励:基于进一步的用户行为,我们可以捕获奖励指标以了解模型是否具有预期的效果。例如,如果我们显示产品推荐,我们可以跟踪用户何时决定购买推荐产品作为奖励。
- 模型公平性:针对可能存在偏差的已知特征(例如:种族、性别、年龄、收入群体等)分析输入数据和输出预测。
在我们的示例中,我们使用 EFK stack 进行监控和可观察性,由三个主要工具组成:
- Elasticsearch:一个开源搜索引擎。
- FluentD:用于统一日志层的开源数据收集器。
- Kibana:一个开源 Web UI,可以轻松探索和可视化 Elasticsearch 索引的数据。
我们可以检测我们的应用程序代码以将模型输入和预测记录为 FluentD 中的事件:
predict_with_logging.py
df = pd.DataFrame(data=data, index=['row1'])
df = decision_tree.encode_categorical_columns(df)
pred = model.predict(df)logger = sender.FluentSender(TENANT, host=FLUENTD_HOST, port=int(FLUENTD_PORT))
log_payload = {'prediction': pred[0], **data}
logger.emit('prediction', log_payload)
然后这个事件在 ElasticSearch 中被转发和索引,我们可以使用 Kibana 通过 Web 界面对其进行查询和分析,如下图展示了在 Kibana 中根据真实输入数据分析我们的模型的预测。
还有其他流行的监控和可观察性工具,例如: ELK stack(一种使用 Logstash 而不是 FluentD 进行日志采集和转发的变体)、Splunk 等。
当您在生产中部署了多个模型时,收集监控和可观察性数据变得更加重要。例如,您可能有一个影子模型要评估,您可能正在执行拆分测试,或者使用多个模型运行多臂老虎机实验。
如果您在边缘训练或运行联邦(federated)模型,这也很重要,例如:在用户的移动设备上或者如果您正在部署在线学习模型,这些模型会随着时间的推移从生产中的新数据中学习。
通过捕获这些数据,您可以结束数据反馈循环。这可以通过收集更多真实数据(例如,在定价引擎或推荐系统中)或通过在循环中添加人员来分析从生产中捕获的新数据,并对其进行管理为新的和改进的模型创建新的训练数据集来实现。
结束这个反馈循环是 CD4ML 的主要优势之一,因为它允许我们根据从实际生产数据中获取的经验来调整我们的模型,从而创建一个持续改进的过程。
端到端 CD4ML 流程
通过逐步解决每个技术挑战,并使用各种工具和技术,我们设法创建了如下图所示的机器学习端到端流程的持续交付,该流程管理所有三个轴上的制品:代码、模型和数据。
在此基础上,我们需要一种简单的方法来管理、发现、访问和版本化我们的数据。 然后,我们将模型构建和训练过程自动化,使其具有可重复性。 这使我们能够实验和训练多个模型,这就需要测量和跟踪这些实验。 一旦找到合适的模型,我们就可以决定如何生产和提供服务。 由于模型在不断发展,我们必须确保它不会违反与消费者的任何契约,因此我们需要在部署到生产之前对其进行测试。 一旦投入生产,我们就可以使用监控和可观察性基础设施来收集新数据,这些数据可以进行分析并用于创建新的训练数据集,从而结束持续改进的反馈循环。
持续交付编排工具协调端到端 CD4ML 流程,按需提供所需的基础设施,并管理模型和应用程序如何部署到生产环境。
我们应该去向何方?
我们在本文中使用的示例应用程序和代码可在我们的 Github 存储库中获得,并用作我们在各种会议和客户中展示的半天研讨会的基础。我们将继续发展关于如何实施 CD4ML 的想法。在本节中,我们将重点介绍研讨会材料中未反映的一些改进领域,以及一些需要进一步探索的开放领域。
数据版本控制
在持续交付中,我们将每个代码提交视为发布候选,这会触发部署流水线的新执行。假设提交通过所有流水线阶段,它可以部署到生产中。在谈论 CD4ML 时,我们经常遇到的问题之一是“如何在数据更改时触发流水线?”
在我们的示例中,下图的机器学习流水线从 download_data.py
文件开始,该文件负责从共享位置下载训练数据集。如果我们在共享位置更改数据集的内容,它不会立即触发流水线,因为代码没有更改,DVC 将无法检测到它。要对数据进行版本化,我们必须创建一个新文件或更改文件名,这反过来又需要我们使用新路径更新 download_data.py 脚本,从而创建一个新的代码提交。
这种方法的改进是允许 DVC 为我们跟踪文件的内容,方法是将我们的手写下载脚本替换为:
dvc add data/raw/store47-2016.csv ➊
这将稍微改变我们机器学习流水线的第一步,如下图展示了更新第一步以允许 DVC 跟踪数据版本并简化 ML 流水线。
这将创建一个元数据文件,该文件跟踪我们可以提交给 Git 的文件内容的校验和。现在,当文件内容更改时,校验和将更改,DVC 将更新该元数据文件,这将是我们触发流水线执行所需的提交。
虽然这允许我们在数据更改时重新训练我们的模型,但它并不能说明数据版本控制的全部内容。一方面是数据历史记录:理想情况下,您希望保留所有数据更改的完整历史记录,但这并不总是可行的,具体取决于数据更改的频率。另一个方面是数据来源:了解哪些处理步骤导致数据发生变化,以及它如何在不同的数据集之间传播。还有一个问题是随着时间的推移跟踪和发展数据模式,以及这些变化是否向后和向前兼容。
如果您身处流媒体世界,那么数据版本控制的这些方面会变得更加难以理解,因此,我们期望在这个领域发展更多的实践、工具和技术。
数据流水线
到目前为止我们尚未涉及的另一个方面是如何对数据管道本身进行版本控制、测试、部署和监控。在现实世界中,一些工具选项比其他工具选项更好地启用 CD4ML。例如,许多要求您通过 GUI 定义转换和处理步骤的 ETL 工具通常不容易进行版本控制、测试或部署到混合环境。其中一些可以生成代码,您可以将其作为一个制品进行处理并将它们视为一个部署流水线。
我们倾向于更喜欢允许我们在代码中定义数据流水线的开源工具,这更容易进行版本控制、测试和部署。例如,如果您使用 Spark,您可能使用 Scala 编写数据流水线,您可以使用 ScalaTest 或 spark-testing-base 对其进行测试,您可以将作业打包为 JAR 制品,可以在 GoCD 中的部署流水线上对其进行版本控制和部署。
由于数据流水线通常作为批处理作业或作为长时间运行的流应用程序运行,我们没有将它们包含在端到端 CD4ML 流程图中,但它们也是集成问题的另一个潜在来源。如果他们改变了您的模型或您的应用程序期望的输出。因此,将集成和数据契约测试(Contract Tests)作为我们部署流水线的一部分来捕捉这些错误是我们努力的目标。
另一种与数据流水线相关的测试是数据质量检查,但这可能会成为另一个广泛讨论的话题,最好在单独的文章中讨论。
平台思维
您可能已经注意到,我们使用各种工具和技术来实现 CD4ML。如果您有多个团队尝试这样做,他们最终可能会重新发明事物或重复工作。这就是平台思维可以发挥作用的地方。不是通过将所有工作集中在一个成为瓶颈的团队中,而是通过将平台工程工作集中在构建与领域无关的工具上,这些工具隐藏了底层的复杂性并加快了团队尝试采用它的速度。我们的同事 Zhamak Dehghani 在她的 Data Mesh 文章中对此进行了更详细的介绍。
将平台思维应用于CD4ML是我们看到机器学习平台和其他产品越来越受关注的原因,这些产品正试图提供一个单一的解决方案来管理端到端的机器学习生命周期。许多主要的技术巨头已经开发了自己的内部工具,但我们认为这是一个活跃的研发领域,并期待新的工具和供应商出现,提供可以更广泛采用的解决方案。
逐步发展无偏见(Bias)的智能系统
一旦您的第一个机器学习系统部署到生产中,它将开始进行预测并用于处理看不见的数据。它甚至可能取代您以前拥有的基于规则的系统。重要的是要意识到您执行的训练数据和模型验证是基于历史数据,其中,可能包括基于先前系统行为方式的固有偏差。此外,您的 ML 系统可能对您的用户未来产生的任何影响,也会影响您未来的训练数据。
让我们考虑两个例子来理解影响。首先,让我们考虑一下我们在本文中探讨的需求预测解决方案。让我们假设有一个应用程序根据预测的需求来决定要订购和提供给客户的产品的确切数量。如果预测需求低于实际需求,您将没有足够的商品出售,因此该商品的交易量会减少。如果您只使用这些新交易作为训练数据来改进您的模型,那么随着时间的推移,您的需求预测将会下降。
对于第二个示例,假设您正在构建一个异常检测模型来确定客户的信用卡交易是否具有欺诈性。如果您的应用程序采用模型决定阻止它们,那么随着时间的推移,您将只有模型允许的交易的“真实标签”和较少的欺诈性交易来训练。模型的性能也会下降,因为训练数据会偏向“好”交易。
这个问题没有简单的解决方案。在我们的第一个示例中,零售商还考虑缺货情况并订购比预期更多的商品以弥补潜在的短缺。对于欺诈检测场景,我们有时可以忽略或覆盖模型的分类,使用一些概率分布。同样重要的是要认识到许多数据集是时间的,即它们的分布随时间而变化。许多执行数据随机拆分的验证方法假设它们是 i.i.d。 (独立同分布),但是一旦考虑到时间的影响,情况就不是这样了。
因此,重要的是不仅要捕获模型的输入/输出,还要捕获消费应用程序使用或覆盖模型输出的最终决定。这使您可以对数据进行注释,以避免在未来的训练轮次中出现这种偏差。当您面临这些问题时,管理训练数据并拥有允许人类管理它们的系统是另一个关键组件。
随着时间的推移发展智能系统以选择和改进 ML 模型也可以被视为元学习问题。该领域的许多最新研究都集中在这些类型的问题上。例如,强化学习技术的使用,如多臂老虎机问题,或生产中的在线学习。我们希望我们在如何最好地构建、部署和监控这些类型的 ML 系统方面的经验和知识将继续发展。
总结
随着机器学习技术不断发展并执行更复杂的任务,我们在如何管理此类应用程序并将其交付生产方面的了解也在不断发展。
通过引入和扩展持续交付的原则和实践,我们可以以安全可靠的方式更好地管理机器学习应用程序发布更改带来的风险。
通过销售预测应用程序示例,我们在本文中展示了 CD4ML 的技术组件,并讨论了我们如何实现它们的一些方法。我们相信这项技术将继续发展,新工具会出现又消失,但持续交付的核心原则仍然是相关的,您应该为自己的机器学习应用程序考虑这些原则。
原文链接:Continuous Delivery for Machine Learning