MLFlow 大规模深度学习实践指南(一)

原文:annas-archive.org/md5/0801c480d77054d281bf58617eeac06d

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

从 2012 年 AlexNet 赢得大规模 ImageNet 竞赛,到 2018 年 BERT 预训练语言模型在多个自然语言处理NLP)排行榜上名列前茅,现代深度学习DL)在广泛的人工智能AI)和机器学习ML)社区中的革命持续进行。然而,将这些 DL 模型从离线实验转移到生产环境的挑战依然存在。这主要是因为缺乏一个统一的开源框架来支持 DL 完整生命周期的开发。本书将帮助你理解 DL 完整生命周期开发的全貌,并实现可从本地离线实验扩展到分布式环境和在线生产云的 DL 管道,重点是通过实践项目式学习,使用流行的开源 MLflow 框架支持 DL 过程的端到端实现。

本书从 DL 完整生命周期的概述和新兴的机器学习运维MLOps)领域开始,提供了 DL 四大支柱(数据、模型、代码和可解释性)以及 MLflow 在这些领域中的作用的清晰图景。在第一章中,基于转移学习的基本 NLP 情感分析模型使用 PyTorch Lightning Flash 构建,并在接下来的章节中进一步开发、调优并部署到生产环境。从此开始,本书将一步步引导你理解 MLflow 实验的概念和使用模式,使用 MLflow 作为统一的框架来跟踪 DL 数据、代码与管道、模型、参数和指标的规模化管理。我们将在分布式执行环境中运行 DL 管道,确保可重现性和溯源追踪,并通过 Ray Tune、Optuna 和 HyperBand 对 DL 模型进行超参数优化HPO)。我们还将构建一个多步骤 DL 推理管道,包括预处理和后处理步骤,使用 Ray Serve 和 AWS SageMaker 部署 DL 推理管道到生产环境,最后,提供一个使用SHapley 加法解释SHAP)和 MLflow 集成的 DL 解释服务。

在本书的结尾,你将拥有从初始离线实验到最终部署和生产的深度学习(DL)管道构建的基础和实践经验,所有内容都在一个可重现和开源的框架中完成。在此过程中,你还将学习到与 DL 管道相关的独特挑战,以及我们如何通过实际且可扩展的解决方案克服这些挑战,例如使用多核 CPU、图形处理单元GPU)、分布式和并行计算框架,以及云计算。

本书适合谁阅读

本书是为数据科学家、机器学习工程师和人工智能从业者编写的,旨在帮助他们掌握深度学习从构想到生产的完整生命周期,使用开源 MLflow 框架及相关工具,如 Ray Tune、SHAP 和 Ray Serve。本书中展示的可扩展、可复现且关注溯源的实现,确保您能够成功构建企业级深度学习管道。本书将为构建强大深度学习云应用程序的人员提供支持。

本书内容概述

第一章深度学习生命周期与 MLOps 挑战,涵盖了深度学习完整生命周期的五个阶段,并使用迁移学习方法进行文本情感分类的第一个深度学习模型。它还定义了 MLOps 的概念,并介绍了其三个基础层次和四大支柱,以及 MLflow 在这些领域的作用。此外,还概述了深度学习在数据、模型、代码和可解释性方面面临的挑战。本章旨在将每个人带入相同的基础水平,并为本书其余部分的范围提供清晰的指导。

第二章使用 MLflow 开始深度学习之旅,作为 MLflow 的入门教程和首个实践模块,快速设置基于本地文件系统的 MLflow 追踪服务器,或在 Databricks 上与远程管理的 MLflow 追踪服务器互动,并使用 MLflow 自动日志记录进行首个深度学习实验。本章还通过具体示例讲解了一些基础的 MLflow 概念,如实验、运行、实验和运行之间的元数据及其关系、代码追踪、模型日志记录和模型类型。特别地,我们强调实验应作为一等公民实体,能够弥合深度学习模型的离线与在线生产生命周期之间的差距。本章为 MLflow 的基础知识奠定了基础。

第三章跟踪模型、参数和指标,涵盖了使用完整本地 MLflow 跟踪服务器的第一次深入学习模块。它从设置在 Docker Desktop 中运行的本地完整 MLflow 跟踪服务器开始,后端存储使用 MySQL,工件存储使用 MinIO。在实施跟踪之前,本章提供了基于开放源追踪模型词汇规范的开放源追踪框架,并提出了六种可以通过使用 MLflow 实现的溯源问题。接着,它提供了如何使用 MLflow 模型日志 API 和注册 API 来跟踪模型溯源、模型指标和参数的实践示例,无论是否启用自动日志。与其他典型的 MLflow API 教程不同,本章不仅仅提供使用 API 的指导,而是专注于我们如何成功地使用 MLflow 来回答溯源问题。在本章结束时,我们可以回答六个溯源问题中的四个,剩下的两个问题只能在拥有多步骤管道或部署到生产环境时回答,这些内容将在后续章节中涵盖。

第四章跟踪代码和数据版本管理,涵盖了关于 MLflow 跟踪的第二个深入学习模块。它分析了在 ML/DL 项目中使用笔记本和管道的现有实践。它推荐使用 VS Code 笔记本,并展示了一个具体的深度学习笔记本示例,该示例可以在启用 MLflow 跟踪的情况下交互式或非交互式运行。它还建议使用 MLflow 的MLproject,通过 MLflow 的入口点和管道链实现多步骤的深度学习管道。为深度学习模型的训练和注册创建了一个三步深度学习管道。此外,它还展示了通过 MLflow 中的父子嵌套运行进行的管道级跟踪和单个步骤的跟踪。最后,它展示了如何使用 MLflow 跟踪公共和私有构建的 Python 库以及在Delta Lake中进行数据版本管理。

第五章在不同环境中运行深度学习管道,涵盖了如何在不同环境中运行深度学习管道。首先介绍了在不同环境中执行深度学习管道的场景和要求。接着展示了如何使用 MLflow 的命令行界面CLI)在四种场景中提交运行:本地运行本地代码、在 GitHub 上运行本地代码、在云端远程运行本地代码、以及在云端远程运行 GitHub 上的代码。MLflow 所支持的灵活性和可重现性在执行深度学习管道时,也为需要时的持续集成/持续部署CI/CD)自动化提供了构建块。

第六章大规模超参数调优运行,介绍了如何使用 MLflow 支持大规模的超参数优化(HPO),并利用最先进的 HPO 框架如 Ray Tune。首先回顾了深度学习流水线超参数的类型和挑战。然后,比对了三个 HPO 框架:Ray Tune、Optuna 和 HyperOpt,并对它们与 MLflow 的集成成熟度及优缺点进行了详细分析。接着,推荐并展示了如何使用 Ray Tune 与 MLflow 结合,对本书中迄今为止所讨论的深度学习模型进行超参数调优。此外,还介绍了如何切换到其他 HPO 搜索和调度算法,如 Optuna 和 HyperBand。这使得我们能够以一种具有成本效益且可扩展的方式,生产符合业务需求的高性能深度学习模型。

第七章多步骤深度学习推理流水线,介绍了使用 MLflow 的自定义 Python 模型方法创建多步骤推理流水线的过程。首先概述了生产环境中四种推理工作流模式,在这些模式下,单一的训练模型通常不足以满足业务应用的需求,需要额外的预处理和后处理步骤。接着,提供了一个逐步指南,讲解如何实现一个多步骤推理流水线,该流水线将先前微调过的深度学习情感模型与语言检测、缓存以及额外的模型元数据结合起来。该推理流水线随后会作为一个通用的 MLflow PyFunc 模型进行日志记录,可以通过通用的 MLflow PyFunc 加载 API 加载。将推理流水线包装成 MLflow 模型为自动化和在同一 MLflow 框架内一致地管理模型流水线开辟了新天地。

第八章大规模部署深度学习推理流水线,介绍了如何将深度学习推理流水线部署到不同的主机环境中以进行生产使用。首先概述了部署和托管环境的全景,包括大规模的批量推理和流式推理。接着,描述了不同的部署机制,如 MLflow 内置的模型服务工具、自定义部署插件以及像 Ray Serve 这样的通用模型服务框架。示例展示了如何使用 MLflow 的 Spark mlflow-ray-serve 部署批量推理流水线。接下来,提供了一个完整的逐步指南,讲解如何将深度学习推理流水线部署到托管的 AWS SageMaker 实例中用于生产环境。

第九章深度学习可解释性基础,介绍了可解释性的基础概念,并探索了使用两个流行的可解释性工具。本章从概述可解释性的八个维度和可解释的人工智能XAI)开始,然后提供了具体的学习示例,探索如何使用 SHAP 和 Transformers-interpret 工具箱进行 NLP 情感分析管道的应用。它强调,在开发深度学习应用时,可解释性应该被提升为一类重要的工件,因为在各类商业应用和领域中,对模型和数据解释的需求和期望正在不断增加。

第十章使用 MLflow 实现深度学习可解释性,介绍了如何使用 MLflow 实现深度学习可解释性,并提供解释即服务EaaS)。本章从概述 MLflow 当前支持的解释器和解释功能开始。具体来说,MLflow API 与 SHAP 的现有集成不支持大规模的深度学习可解释性。因此,本章提供了使用 MLflow 的工件日志记录 API 和PyFunc API 来实现的两种通用方法。文中提供了实现 SHAP 解释的示例,该解释将 SHAP 值以条形图的形式记录到 MLflow 跟踪服务器的工件存储中。SHAP 解释器可以作为 MLflow Python 模型进行日志记录,然后作为 Spark UDF 批处理解释或作为 Web 服务进行在线 EaaS 加载。这为在统一的 MLflow 框架内实现可解释性提供了最大的灵活性。

如何充分利用本书

本书中的大多数代码可以使用开源的 MLflow 工具进行实现和执行,少数情况需要 14 天的完整 Databricks 试用(可以在databricks.com/try-databricks注册)和一个 AWS 免费账户(可以在aws.amazon.com/free/注册)。以下列出了本书中涵盖的一些主要软件包:

  • MLflow 1.20.2 及以上版本

  • Python 3.8.10

  • Lightning-flash 0.5.0

  • Transformers 4.9.2

  • SHAP 0.40.0

  • PySpark 3.2.1

  • Ray[tune] 1.9.2

  • Optuna 2.10.0

本书中每一章的完整包依赖列在requirements.txt文件或本书 GitHub 仓库中的conda.yaml文件中。所有代码已在 macOS 或 Linux 环境下成功测试。如果你是 Microsoft Windows 用户,建议安装WSL2以运行本书中提供的 bash 脚本:www.windowscentral.com/how-install-wsl2-windows-10。已知问题是 MLflow CLI 在 Microsoft Windows 命令行中无法正常工作。

从本书的 第三章*,* 模型、参数和指标的追踪 开始,你还需要安装 Docker Desktop(www.docker.com/products/docker-desktop/)来设置一个完整的本地 MLflow 跟踪服务器,以便执行本书中的代码。第八章*,* 在大规模上部署深度学习推理管道 需要 AWS SageMaker 进行云部署示例。本书中使用的是 VS Code 版本 1.60 或更高版本 (code.visualstudio.com/updates/v1_60),作为 集成开发环境 (IDE)。本书中的虚拟环境创建和激活使用的是 Miniconda 版本 4.10.3 或更高版本 (docs.conda.io/en/latest/miniconda.html)。

如果你使用的是本书的电子版本,建议你自己输入代码或从本书的 GitHub 仓库中获取代码(下一个部分会提供链接)。这样可以帮助你避免因复制粘贴代码而可能出现的错误。

最后,为了充分利用本书的内容,你应该具有 Python 编程经验,并且对常用的机器学习和数据处理库(如 pandas 和 PySpark)有基本了解。

下载示例代码文件

你可以从 GitHub 下载本书的示例代码文件,链接为 github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow。如果代码有更新,它将在 GitHub 仓库中同步更新。

我们还提供了来自我们丰富图书和视频目录的其他代码包,详情请访问 github.com/PacktPublishing/。赶紧去看看吧!

下载彩色图像

我们还提供了 PDF 文件,包含本书中使用的屏幕截图和图表的彩色图像。你可以在这里下载:static.packt-cdn.com/downloads/9781803241333_ColorImages.pdf

使用的约定

本书中使用了许多文本约定。

Code in text:表示文本中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 账号。示例如下:“为了学习目的,我们在 GitHub 仓库的 chapter08 文件夹下提供了两个示例 mlruns 工件和 huggingface 缓存文件夹。”

代码块按如下方式书写:

client = boto3.client('sagemaker-runtime') 
response = client.invoke_endpoint(
        EndpointName=app_name, 
        ContentType=content_type,
        Accept=accept,
        Body=payload
        )

当我们希望引起你对代码块中特定部分的注意时,相关行或项目将加粗显示:

loaded_model = mlflow.pyfunc.spark_udf(
    spark,
    model_uri=logged_model, 
    result_type=StringType())

任何命令行输入或输出都按如下方式书写:

mlflow models serve -m models:/inference_pipeline_model/6

粗体:表示一个新术语、重要词汇或屏幕上看到的词语。例如,菜单或对话框中的词语以 粗体 显示。举个例子:“要执行这个单元格中的代码,你只需点击右上角下拉菜单中的 Run Cell。”

提示或重要注意事项

以这种形式出现。

联系我们

我们始终欢迎读者的反馈。

常见反馈:如果你对本书的任何内容有疑问,请通过电子邮件联系我们:customercare@packtpub.com,并在邮件主题中注明书名。

勘误:虽然我们已尽力确保内容的准确性,但错误难免发生。如果你在本书中发现错误,我们将不胜感激,如果你能将其报告给我们。请访问 www.packtpub.com/support/errata 并填写表单。

盗版:如果你在互联网上发现我们的作品以任何形式的非法复制,我们将非常感激你能提供该材料的地址或网站名称。请通过 copyright@packt.com 与我们联系,并提供链接。

如果你有兴趣成为作者:如果你在某个领域有专业知识,并且有兴趣写书或为书籍做贡献,请访问 authors.packtpub.com

分享你的想法

一旦你阅读了 Practical Deep Learning at Scale with MLflow,我们希望听到你的反馈!请点击这里,直接访问本书的亚马逊评论页面,并分享你的看法。

你的评论对我们和技术社区都非常重要,它将帮助我们确保提供优质的内容。

第一部分 - 深度学习挑战与 MLflow 概述

在这一部分,我们将学习深度学习DL)完整生命周期的五个阶段,并了解新兴的机器学习运维MLOps)领域以及 MLflow 的作用。我们将概述深度学习过程中的四大支柱:数据、模型、代码和可解释性所面临的挑战。接下来,我们将学习如何设置一个基本的本地 MLflow 开发环境,并运行我们的第一个 MLflow 实验,实验内容是基于PyTorch Lightning Flash构建的自然语言处理NLP)模型。最后,我们将通过这个第一个 MLflow 实验示例,解释 MLflow 的基本概念,如实验、运行等。

本节包括以下章节:

  • 第一章深度学习生命周期与 MLOps 挑战

  • 第二章开始使用 MLflow 进行深度学习

第一章:第一章:深度学习生命周期与 MLOps 挑战

近年来,深度学习DL)在解决实际业务、工业和科学问题方面取得了巨大成功,特别是在自然语言处理NLP)、图像、视频、语音识别和对话理解等任务中。尽管这些领域的研究取得了巨大的进展,但将这些深度学习模型从离线实验推广到生产环境,并持续改进模型以提供可持续的价值,仍然是一个挑战。例如,VentureBeat 最近的一篇文章(venturebeat.com/2019/07/19/why-do-87-of-data-science-projects-never-make-it-into-production/)发现,87%的数据科学项目从未进入生产。虽然这种低生产率可能有商业原因,但一个主要的因素是缺乏实验管理和成熟的模型生产与反馈平台所带来的困难。

本章将帮助我们理解这些挑战,并通过学习在深度学习模型开发的整个生命周期中常用的概念、步骤和组件来弥合这些差距。此外,我们还将学习一个新兴领域——机器学习运维MLOps)的挑战,MLOps 旨在标准化和自动化机器学习生命周期的开发、部署和运营。深入理解这些挑战将激励我们学习本书其余部分中介绍的使用 MLflow 的技能,MLflow 是一个开源的机器学习全生命周期平台。采用 MLOps 最佳实践的商业价值有很多,其中包括更快的模型派生产品功能的市场推出、更低的运营成本、更灵活的 A/B 测试以及更具战略性的决策制定,从而最终改善客户体验。在本章结束时,我们将了解 MLflow 在 MLOps 四大支柱(即数据、模型、代码和可解释性)中所发挥的关键作用,实施我们的第一个工作深度学习模型,并清晰地理解数据、模型、代码和可解释性在深度学习中的挑战。

本章将覆盖以下主要内容:

  • 理解深度学习(DL)生命周期和 MLOps 挑战

  • 理解深度学习(DL)数据挑战

  • 理解深度学习(DL)模型的挑战

  • 理解深度学习(DL)代码挑战

  • 理解深度学习(DL)可解释性挑战

技术要求

本书所有的代码示例都可以在以下 GitHub 网址找到:github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow

你需要在开发环境中安装 Miniconda(docs.conda.io/en/latest/miniconda.html)。在本章中,我们将逐步介绍安装 PyTorch lightning-flash库的过程(github.com/PyTorchLightning/lightning-flash),该库可以用于在实现一个基本的深度学习情感分类器部分中构建我们的第一个深度学习模型。或者,你也可以注册一个免费的 Databricks 社区版账户(community.cloud.databricks.com/login.html),使用 GPU 集群和笔记本来进行本书中描述的模型开发。

此外,如果你是微软 Windows 用户,我们建议你安装 WSL2(www.windowscentral.com/how-install-wsl2-windows-10),这样你就能拥有一个 Linux 环境来运行本书中的命令行。

理解深度学习生命周期和 MLOps 的挑战

目前,成功部署到生产中的大多数深度学习模型主要遵循以下两个步骤:

  1. 自监督学习:指的是在数据丰富的领域中进行模型的预训练,该领域不需要标签数据。这一步生成了一个预训练模型,也称为基础模型,例如,BERT、GPT-3 用于自然语言处理(NLP),以及 VGG-NETS 用于计算机视觉。

  2. 迁移学习:指的是在特定预测任务中对预训练模型进行微调,例如文本情感分类,这需要有标签的训练数据。

一个开创性且成功的深度学习(DL)模型生产实例是买家情感分析模型,该模型基于 BERT 构建,用于分类销售互动邮件信息,提供对买家情感和信号的细致洞察,超越简单的活动指标,如回复、点击和打开率(www.prnewswire.com/news-releases/outreach-unveils-groundbreaking-ai-powered-buyer-sentiment-analysis-transforming-sales-engagement-301188622.html)。关于其工作原理有不同的变体,但在本书中,我们将主要聚焦于迁移学习范式,开发和部署深度学习模型,因为它展示了一个实用的深度学习生命周期。

让我们通过一个例子来了解典型的核心深度学习开发范式。例如,2018 年底发布的流行 BERT 模型(BERT 模型的基础版本可以在huggingface.co/bert-base-uncased找到)最初是在来自 BookCorpus 的 11,000 多本书籍以及整个英文维基百科的原始文本上进行预训练的(没有人工标注)。然后,使用该预训练语言模型对许多下游自然语言处理任务进行了微调,如文本分类和情感分析,应用于不同的领域,比如通过使用标注的电影评论数据进行电影评论分类(huggingface.co/datasets/imdb)。需要注意的是,有时为了提高最终模型在准确度方面的性能,可能需要使用无标签数据在应用领域内进一步预训练基础模型(例如 BERT),然后再进行微调。这个核心的深度学习开发范式如图 1.1所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_01_001.jpg

图 1.1 – 一个典型的核心深度学习开发范式

注意,虽然图 1.1代表了一个常见的开发范式,但并不是所有的步骤在特定的应用场景中都是必要的。例如,你可能只需要使用公开的预训练深度学习模型和你标注好的应用特定数据进行微调。因此,你不需要进行自己的预训练或使用无标签数据进一步预训练,因为其他人或组织已经为你完成了预训练步骤。

深度学习与传统机器学习

与传统的机器学习模型开发不同,通常需要进行特征工程步骤,将原始数据提取并转化为特征,以训练如决策树或逻辑回归这样的机器学习模型,深度学习能够自动学习特征,这一点对于建模非结构化数据(如文本、图像、视频、音频和语音)尤其有吸引力。由于这一特点,深度学习也被称为表征学习。除此之外,深度学习通常是数据密集型和计算密集型的,需要图形处理单元GPUs)、张量处理单元TPU)或其他类型的计算硬件加速器来进行大规模训练和推理。相比传统机器学习模型,深度学习模型的可解释性也更难实现,尽管近期的进展已经使得这一点成为可能。

实现一个基本的深度学习情感分类器

为了设置基本的深度学习情感分类器开发环境,你需要在本地创建一个虚拟环境。假设你已经有了dl_model,并且安装了 PyTorch 的lightning-flash包,这样模型就能被构建:

conda create -n dl_model python==3.8.10
conda activate dl_model
pip install lightning-flash[all]

根据你本地机器的内存情况,前面的命令可能需要大约 10 分钟才能完成。你可以通过运行以下命令来验证安装是否成功:

conda list | grep lightning

如果你看到类似以下的输出,说明安装成功:

lightning-bolts     0.3.4                    pypi_0    pypi
lightning-flash     0.5.0                    pypi_0    pypi
pytorch-lightning   1.4.4                    pypi_0    pypi

现在,你已经准备好构建你的第一个深度学习模型了!

要开始构建一个深度学习模型,请完成以下步骤:

  1. 导入必要的torchflash库,并从flash子包中导入download_dataTextClassificationDataTextClassifier

    import torch
    import flash
    from flash.core.data.utils import download_data
    from flash.text import TextClassificationData, TextClassifier
    
  2. 为了进行微调,使用download_data下载imdb.zip文件,它是来自train.csv的公共领域二元情感分类(正面/负面)数据集。

  3. valid.csv

  4. test.csv

每个文件包含两列:reviewsentiment。然后,我们使用TextClassificationData.from_csv声明一个datamodule变量,将review分配给input_fields,将sentiment分配给target_fields。此外,它还将train.csv文件分配给train_file,将valid.csv文件分配给val_file,将test.csv文件分配给test_file属性:

download_data("https://pl-flash-data.s3.amazonaws.com/imdb.zip", "./data/")
datamodule = TextClassificationData.from_csv(
    input_fields="review",
    target_fields="sentiment",
    train_file="data/imdb/train.csv",
    val_file="data/imdb/valid.csv",
    test_file="data/imdb/test.csv"
)
  1. 一旦我们有了数据,我们现在可以使用基础模型进行微调。首先,通过调用TextClassifier并将骨干网络(backbone)设置为prajjwal1/bert-tiny来声明classifier_modelprajjwal1/bert-tiny是一个更小的类似 BERT 的预训练模型,位于 Hugging Face 模型库中:huggingface.co/prajjwal1/bert-tiny)。这意味着我们的模型将基于bert-tiny模型。

  2. 下一步是通过定义我们希望运行的轮数(epochs)和希望使用的 GPU 数量来设置训练器。这里,torch.cuda.device_count()将返回0(没有 GPU)或1N,其中N是你运行环境中最大可用 GPU 的数量。现在,我们已经准备好调用trainer.finetune来训练一个二元情感分类器,使用的是 IMDb 数据集:

    classifier_model = TextClassifier(backbone="prajjwal1/bert-tiny", num_classes=datamodule.num_classes)
    trainer = flash.Trainer(max_epochs=3, gpus=torch.cuda.device_count())
    trainer.max_epochs=1 if you simply want to get a basic version of the sentiment classifier quickly.
    
  3. 微调步骤完成后,我们将通过运行trainer.test()来测试模型的准确性:

    trainer.test()
    

测试结果的输出应该类似于以下屏幕截图,表明最终模型的准确率大约为 52%:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_01_002.jpg

图 1.2 – 我们的第一个深度学习模型的测试结果

前面图表中显示的测试结果表明,我们有了模型的基本版本,因为我们只对基础模型进行了三轮微调,并且没有使用诸如超参数调优或更好的微调策略等高级技术。然而,这是一个很大的成就,因为你现在已经掌握了核心深度学习模型的工作原理!我们将在本书的后续章节中探索更高级的模型训练技术。

理解深度学习的完整生命周期开发

到目前为止,你应该已经准备好第一个深度学习模型,并应该为此感到自豪。现在,让我们一起探索完整的深度学习生命周期,充分理解其概念、组成部分和挑战。

你可能已经了解到,核心的深度学习开发范式围绕着三个关键要素:数据模型代码。除此之外,可解释性是另一个在许多关键任务应用场景中所需的重要要素,如医学诊断、金融行业以及刑事司法决策。由于深度学习通常被视为“黑盒”,因此在部署到生产环境之前和之后,提供可解释性越来越成为一个关键要求。

请注意,如果我们仍在试图通过实验室环境中的数据集确定哪个模型有效,那么图 1.1 仍然被视为离线实验。即使在这种离线实验环境中,情况也会迅速变得复杂。此外,我们希望了解并追踪我们已经或尚未执行过哪些实验,以便避免浪费时间重复相同的实验,无论我们使用了哪些参数和数据集,或者对于特定模型,我们采用了什么样的指标。一旦我们拥有一个足够适用于用例和客户场景的模型,复杂性将增加,因为我们需要一种持续部署和更新模型的方法,监控模型和数据的漂移,并在必要时重新训练模型。当需要大规模训练、部署、监控和可解释性时,这种复杂性会进一步增加。

让我们来看看深度学习生命周期的样子(见图 1.3)。有五个阶段:

  1. 数据收集、清洗和注释/标注。

  2. 模型开发(也称为离线实验)。图 1.1 中的核心深度学习开发范式被视为模型开发阶段的一部分,而该阶段本身可以是一个迭代过程。

  3. 模型部署与生产环境中的服务。

  4. 模型验证与 A/B 测试(也称为在线实验;通常在生产环境中进行)。

  5. 在生产过程中进行监控和反馈数据收集。

图 1.3 提供了一个图示,展示了这是一个持续发展的深度学习模型开发周期:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_01_003.jpg

图 1.3 – 完整的深度学习开发生命周期

除此之外,我们还想指出,正如图 1.3 所示,这五个阶段的支柱实际上围绕着四个要素:数据、模型、代码和可解释性。我们将在接下来的章节中探讨这些要素在生命周期中的挑战。然而,首先,让我们了解并理解 MLOps,这是一个不断发展的平台概念和框架,支持机器学习的完整生命周期。这将帮助我们在更宏观的背景下理解这些挑战。

理解 MLOps 挑战

MLOps 与 DevOps 有一些联系,DevOps 使用一系列技术栈和标准操作流程来进行软件开发和部署,并结合 IT 运维。与传统软件开发不同,机器学习(ML)尤其是深度学习(DL)代表着一种新的软件开发范式——软件 2.0karpathy.medium.com/software-2-0-a64152b37c35)。软件 2.0 的关键区别在于,软件的行为不仅依赖于人们已经理解的编程语言代码(这是软件 1.0 的特点),还依赖于神经网络中学习到的权重,而这些权重很难写成代码。换句话说,代码、数据和模型的整合是不可分割的,必须共同管理。因此,MLOps 正在发展,并且仍在不断演变,以适应这一新的软件 2.0 范式。本书中,MLOps 被定义为一个由三大基础层和四大支柱组成的运营自动化平台。它们如下所示:

  • 下面是三大基础层:

    • 基础设施管理与自动化

    • 应用生命周期管理与持续集成与持续部署CI/CD

    • 服务系统可观测性

  • 下面是四大支柱:

    • 数据可观测性与管理

    • 模型可观测性与生命周期管理

    • 可解释性与人工智能AI)可观测性

    • 代码可复现性与可观测性

此外,我们还将解释 MLflow 在这些 MLOps 层和支柱中的角色,以便我们清楚地了解 MLflow 如何在整体上构建 MLOps 层:

  • 基础设施管理与自动化:这包括但不限于用于自动化容器编排的Kubernetes(也称为 k8s)和用于管理数百个云服务及访问控制的Terraform(常用于此)。这些工具已适应于管理已部署为服务端点的机器学习(ML)和深度学习(DL)应用程序。书中并不专注于这些基础设施层;相反,我们将专注于如何使用 MLflow 提供的功能来部署训练好的深度学习模型。

  • 应用生命周期管理和 CI/CD:这包括但不限于用于虚拟化的Docker容器、如 Kubernetes 这样的容器生命周期管理工具,以及用于CICDCircleCIConcourse。通常,CI 指的是每当 GitHub 仓库中的代码或模型发生变化时,一系列自动化测试将被触发,以确保不会引入破坏性更改。一旦这些测试通过,新的更改将自动发布为新包的一部分。这将触发一个新的部署过程(CD),将新包部署到生产环境中(通常,这将包括人为批准作为安全门)。请注意,这些工具并非 ML 应用程序独有,而是已被调整为适应 ML 和 DL 应用,尤其是当我们需要 GPU 和分布式集群来训练和测试 DL 模型时。本书中,我们不会专注于这些工具,但会在需要时提及集成点或示例。

  • 服务系统可观察性:这主要是用于监控硬件/集群/CPU/内存/存储、操作系统、服务可用性、延迟和吞吐量。这包括如GrafanaDatadog等工具。同样,这些工具并非 ML 和 DL 应用独有,也不是本书的重点。

  • 数据可观察性和管理:这在传统的 DevOps 世界中表现得相对不足,但在 MLOps 中变得非常重要,因为数据在 ML/DL 模型的整个生命周期中至关重要。这包括数据质量监控异常检测数据漂移概念漂移检测偏差检测安全合规的数据共享数据来源追踪版本管理等。这一领域适用于 ML 和 DL 应用的工具栈仍在不断涌现。一些例子包括DataFoldwww.datafold.com/)和Databanddataband.ai/open-source/)。数据管理中的一个最新发展是一个统一的湖仓架构和实现,称为Delta Lakedelta.io),可用于 ML 数据管理。MLflow 与 Delta Lake 有原生集成点,我们将在本书中介绍该集成。

  • 模型可观察性和生命周期管理:这是 ML/DL 模型特有的,且由于 MLflow 的兴起,这一功能直到最近才得以广泛应用。这包括模型训练、测试、版本管理、注册、部署、序列化、模型漂移监控等工具。我们将学习 MLflow 在这一领域所提供的令人兴奋的能力。值得注意的是,一旦将 CI/CD 工具与 MLflow 的训练/监控、用户反馈回路和人工标注结合起来,我们就可以实现持续训练持续测试持续标注。MLflow 提供了基础能力,使得 MLOps 中的进一步自动化成为可能,尽管这种完全自动化并不是本书的重点。有兴趣的读者可以在本章末尾找到相关参考资料,以便深入探索这一领域。

  • 可解释性和 AI 可观察性:这是 ML/DL 模型特有的,尤其对 DL 模型而言尤为重要,因为传统上,DL 模型被视为“黑箱”。理解模型为何给出某些预测,对于社会影响深远的应用至关重要。例如,在医疗、金融、司法和许多人机协作的决策支持应用中,如民用和军事应急响应,对可解释性的需求日益增加。MLflow 与一种叫做 SHAP 的流行可解释性框架原生集成,本书将详细介绍该框架。

  • 代码可复现性和可观察性:这并不完全是 ML/DL 应用程序所特有的。然而,DL 模型面临一些特殊挑战,因为 DL 代码框架种类繁多,且复现模型的需求不仅仅依赖于代码本身(我们还需要数据和执行环境,如 GPU 集群)。此外,笔记本通常用于模型的开发和生产。如何管理笔记本与模型运行之间的关系非常重要。通常,GitHub 被用于管理代码库;然而,我们需要以可复现的方式构建 ML 项目代码,无论是在本地(例如本地笔记本电脑)还是远程(例如,在 Databricks 的 GPU 集群中)。MLflow 提供了这一能力,允许已经编写的 DL 项目在任何地方运行,无论是在离线实验环境中,还是在在线生产环境中。本书将介绍 MLflow 的 MLproject 功能。

总结来说,MLflow 在 MLOps 中扮演着至关重要且基础性的角色。它填补了 DevOps 传统上未覆盖的空白,因此是本书的重点。下图(图 1.4)展示了 MLflow 在不断发展的 MLOps 世界中所扮演的核心角色:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_01_004.jpg

图 1.4 – MLOps 的三层结构和四大支柱,以及 MLflow 的作用

虽然底部的两个层和最上层在许多软件开发和部署过程中是常见的,但中间的四个支柱要么完全独特于机器学习/深度学习应用,要么部分独特于机器学习/深度学习应用。MLflow 在 MLOps 的这四个支柱中起着至关重要的作用。本书将帮助你自信地应用 MLflow 解决这四个支柱的问题,同时为你提供进一步集成其他工具的能力,结合图 1.4所示的 MLOps 层,依据你的场景需求实现全自动化。

理解深度学习数据挑战

在本节中,我们将讨论深度学习生命周期各个阶段的数据挑战,如图 1.3所示。本质上,深度学习是数据中心的人工智能,与符号人工智能不同,符号人工智能可以在没有大量数据的情况下利用人类知识。深度学习中的数据挑战贯穿整个生命周期的所有阶段:

  • 数据收集/清洗/标注:深度学习的首个成功之一始于ImageNet (www.image-net.org/),该平台收集并根据 WordNet 数据库中的英文名词对数百万张图像进行了标注 (wordnet.princeton.edu/)。这促成了预训练深度学习模型在计算机视觉领域的成功发展,如 VGG-NETS (pytorch.org/hub/pytorch_vision_vgg/),该模型能够执行最先进的图像分类,并广泛应用于工业和商业领域。这种大规模数据收集和标注的主要挑战是未知的偏差,这在这一过程中很难测量 (venturebeat.com/2020/11/03/researchers-show-that-computer-vision-algorithms-pretrained-on-imagenet-exhibit-multiple-distressing-biases/)。另一个例子是销售参与平台Outreach (www.outreach.io/),我们可以在其中分类潜在买家的情绪。例如,我们可以从收集 100 个付费组织的电子邮件信息开始,以训练一个深度学习模型。接下来,我们需要收集更多组织的电子邮件信息,可能是出于准确性要求或扩展语言覆盖范围(例如从仅限英语到其他语言,如德语和法语)。这些反复的数据收集和标注会生成大量的数据集。通常,我们会仅仅用硬编码的版本号为数据集命名,并将其作为数据集文件名的一部分,例如以下所示:

    MyCoolAnnotatedData-v1.0.csv
    MyCoolAnnotatedData-v2.0.csv
    MyCoolAnnotatedData-v3.0.csv
    MyCoolAnnotatedData-v4.0.csv
    

这似乎有效,直到由于需要修正注释错误或因客户流失而移除电子邮件消息时,某些 vX.0 数据集需要更改。如果我们需要将多个数据集结合起来,或者进行一些数据清洗和转换以训练一个新的深度学习模型,怎么办?如果我们需要实施数据增强来人工生成一些数据集呢?显然,简单地更改文件名既不可扩展也不可持续。

  • 模型开发:我们需要理解,用于训练/预训练深度学习模型的数据中的偏差会在应用模型时反映到预测中。虽然本书不会专注于消除数据偏差,但我们必须在训练和服务深度学习模型时,实施数据版本控制和数据来源管理,将其作为首要的构建模块,这样我们才能跟踪所有模型实验。在针对我们的使用场景对预训练模型进行微调时,正如我们之前所做的那样,我们还需要跟踪微调数据集的版本。在我们之前的例子中,我们使用了 BERT 模型的一个变种来微调 IMDb 评论数据。虽然在第一个例子中我们并没有关心数据的版本或来源,但对于实际应用来说,这是非常重要的。总的来说,深度学习模型需要通过一种可扩展的方法与特定版本的数据集关联。本书将提供有关此主题的解决方案。

  • 模型部署与线上服务:这是将模型部署到生产环境中以服务在线流量的过程。深度学习模型的服务延迟在这一阶段尤为重要,值得收集。这可能使你能够调整用于推理的硬件环境。

  • 模型验证与 A/B 测试:我们在这一阶段收集的数据主要是在线实验环境中的用户行为指标(www.slideshare.net/pavel/ab-testing-ai-global-artificial-intelligence-conference-2019)。还需要对在线数据流量进行特征分析,以便了解模型输入在离线实验与在线实验之间是否存在统计差异。只有通过 A/B 测试并验证模型确实在用户行为指标方面优于之前的版本,我们才会将其推向生产环境,供所有用户使用。

  • 监控与反馈循环:在这一阶段,需要注意的是,数据需要持续收集,以便检测数据漂移和概念漂移。例如,在前面讨论的买家情绪分类案例中,如果买家开始使用训练数据中未出现的术语,模型的性能可能会下降。

总结来说,数据跟踪和可观测性是深度学习(DL)生命周期各个阶段中的主要挑战。

理解深度学习模型的挑战

在本节中,我们将讨论深度学习(DL)模型的挑战。让我们看看深度学习生命周期各阶段的挑战,如图 1.3 所示:

  • 数据收集/清理/标注:虽然数据挑战已经被提到,但将数据与目标模型关联的挑战仍然存在。MLflow 与 Delta Lake 原生集成,以便任何训练过的模型都可以追溯到 Delta Lake 中的特定版本。

  • pickle(github.com/cloudpipe/cloudpickle) 通常用于序列化用 Python 编写的模型。然而,TorchScript (pytorch.org/docs/stable/jit.html) 目前在 PyTorch 模型中表现出色。此外,Open Neural Network Exchange 或 ONNX (onnx.ai/) 尝试提供更具框架无关性的深度学习序列化。最后,我们需要记录序列化的模型并注册该模型,以便跟踪模型版本。MLflow 是第一个克服这些挑战的开源工具之一。

  • 生产环境中的模型部署和服务:一个易于使用的模型部署工具,能够与模型注册表进行连接,是一项挑战。MLflow 可以帮助解决这个问题,允许你加载模型进行生产部署,并完整追踪模型的来源。

  • 模型验证和 A/B 测试:在在线验证和实验过程中,需要验证模型性能并收集用户行为指标。这样我们可以轻松回滚或重新部署特定版本的模型。模型注册表对大规模在线模型生产验证和实验至关重要。

  • 监控和反馈回路:模型漂移和性能退化是一个现实的挑战。需要持续监控生产环境中模型的性能。反馈数据可用于决定是否需要重新训练模型。

总之,深度学习模型在整个生命周期中的挑战是独特的。同样值得指出的是,一个能够帮助模型开发与在线生产之间反复切换的通用框架至关重要,因为我们不希望仅仅因为执行环境不同就使用不同的工具。MLflow 提供了这个统一的框架来弥合这些差距。

理解深度学习代码的挑战

在本节中,我们将讨论深度学习代码的挑战。让我们看看这些代码挑战是如何在图 1.3所描述的各个阶段中表现出来的。在本节中,在深度学习开发的背景下,代码指的是用某些编程语言(如 Python)编写的数据处理和实现的源代码,而模型指的是以序列化格式(例如 pickle、TorchScript 或 ONNX)表示的模型逻辑和架构:

  • 数据收集/清洗/标注:尽管数据是这一阶段的核心,但执行查询、提取/转换/加载ETL)以及数据清洗和增强的代码也至关重要。我们无法将模型的开发与为模型提供数据流的数据管道分离开来。因此,实施 ETL 的数据管道需要作为离线实验和在线生产中的集成步骤之一来对待。一个常见的错误是,我们在离线实验中使用不同的数据 ETL 和清洗管道,然后在在线生产中实现不同的数据 ETL/清洗管道,这可能会导致模型行为的不同。我们需要为数据管道进行版本控制并将其序列化,作为整个模型管道的一部分。MLflow 提供了几种方法来帮助我们实现这种多步骤管道。

  • 模型开发:在离线实验中,除了不同版本的数据管道代码外,我们还可能有不同版本的笔记本,或者使用不同版本的深度学习库代码。在机器学习/深度学习的生命周期中,笔记本的使用尤其独特。需要对每次运行跟踪哪个模型结果是由哪个笔记本/模型管道/数据管道产生的。MLflow 通过自动代码版本追踪和依赖关系管理来实现这一点。此外,不同运行环境中的代码可复现性对深度学习模型来说也具有独特性,因为深度学习模型通常需要硬件加速器,如 GPU 或 TPU。无论是在本地运行,还是在 CPU 或 GPU 环境中远程运行的灵活性都非常重要。MLflow 提供了一种轻量级的方法来组织机器学习项目,从而使得代码可以编写一次并在任何地方运行。

  • 模型部署与生产服务:当模型服务生产流量时,任何错误都需要追溯到模型和代码。因此,追踪代码来源至关重要。同时,跟踪特定版本模型的所有依赖库版本也同样至关重要。

  • 模型验证与 A/B 测试:在线实验可能会使用不同版本的模型和不同的数据源。调试任何实验时,不仅需要知道使用了哪个模型,还需要知道哪些代码被用于生成该模型。

  • 监控与反馈循环:这一阶段与之前的阶段在代码挑战上相似,我们需要知道模型降级是由于代码错误还是模型和数据漂移。监控管道需要收集所有数据和模型性能的指标。

总结来说,深度学习的代码挑战特别独特,因为深度学习框架仍在不断发展(例如,TensorFlowPyTorchKerasHugging FaceSparkNLP)。MLflow 提供了一个轻量级的框架,能够克服许多常见挑战,并能无缝地与多个深度学习框架进行对接。

理解深度学习可解释性挑战

在本节中,我们将讨论在图 1.3所描述的各个阶段,深度学习可解释性面临的挑战。越来越重要的是将可解释性视为一个 integral 且必要的机制,用于在整个模型生命周期中定义、测试、调试、验证和监控模型。尽早嵌入可解释性将使后续的模型验证和运维变得更加容易。此外,为了维持对机器学习/深度学习模型的持续信任,能够在模型上线后解释和调试模型是至关重要的:

  • 数据收集/清洗/注释:正如我们所了解的,模型预测的可解释性至关重要。任何模型的可信度或偏见的根源都可以追溯到用于训练模型的数据。数据的可解释性仍然是一个新兴领域,但它至关重要。那么,在数据收集/清洗/注释阶段可能会发生什么问题并成为挑战呢?举个例子,假设我们有一个机器学习/深度学习模型,它的预测结果是关于一个贷款申请人是否会还款。如果收集到的数据中存在年龄和贷款还款结果之间的某些相关性,这将导致模型使用年龄作为预测变量。然而,基于个人年龄做出贷款决定是违法的,即使模型效果很好,这也是不允许的。所以,在数据收集过程中,可能会出现采样策略不足以代表某些子人群的问题,例如不同年龄组的贷款申请人。

一个子人群可能会有大量缺失字段,之后在数据清洗过程中被剔除。这可能会导致在数据清洗后的代表性不足。人工注释可能会偏向特权群体,并可能存在其他潜在的无意识偏见。一种叫做差异化影响的度量指标可以揭示数据中的隐藏偏见,它比较两个群体中获得积极结果的个体比例:一个是弱势群体,另一个是特权群体。如果弱势群体(例如,年龄>60 岁的人)获得积极结果(例如,贷款批准)的比例少于特权群体(例如,年龄<60 岁的人)比例的 80%,这就是根据当前常见行业标准(五分之四规则)违反了差异化影响。像Dataiku这样的工具可以帮助自动化差异化影响和子人群分析,找出可能因模型训练使用的数据而受到不公平或不同对待的群体。

  • 模型开发:在离线实验阶段,模型的可解释性非常重要,不仅有助于理解模型为何以某种方式表现,还有助于模型选择,决定如果需要将其投入生产,应使用哪个模型。准确性可能不是选择优胜模型的唯一标准。这里有一些深度学习可解释性工具,例如 SHAP(请参阅图 1.5)。MLflow 与 SHAP 的集成提供了一种实现深度学习可解释性的方法:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_01_005.jpg

图 1.5 – 使用深度学习模型时的 NLP 文本 SHAP 变量重要性图

图 1.5 显示了这个自然语言处理(NLP)模型预测结果的主要特征,第一个特征是词汇impressive,其次是rent。从本质上讲,这揭示了深度学习(DL)模型的“黑箱”,这大大增强了我们在生产环境中使用深度学习模型的信心。

  • 模型部署和生产环境中的服务:在生产阶段,如果能够轻松向用户提供模型预测的可解释性,不仅可以提高模型的可用性(用户友好性),还可以收集到更好的反馈数据,因为用户更愿意提供更有意义的反馈。一个好的可解释性解决方案应该为任何预测结果提供点级决策。这意味着我们应该能够回答为什么某个人的贷款被拒绝,以及这个拒绝与其他人(无论是相似还是不同的年龄组)相比如何。因此,挑战在于将可解释性作为发布新版本模型的一个必要部署标准。然而,与准确性指标不同,衡量可解释性作为分数或阈值是非常困难的,尽管某些基于案例的推理可以被应用并自动化。例如,如果我们有某些保留的测试用例,无论模型版本如何,我们都期望得到相同或类似的解释,那么我们可以将其作为一个部署标准。

  • 模型验证与 A/B 测试:在在线实验和持续的生产模型验证过程中,我们需要可解释性来理解模型是否已应用于正确的数据,或预测是否可信。通常,机器学习(ML)/深度学习(DL)模型编码了复杂的非线性关系。在这一阶段,通常希望了解模型如何影响用户行为的度量指标(例如,购物网站上的更高转化率)。影响敏感性分析可以提供有关某些用户特征(例如用户的收入)对结果是否有正面或负面影响的见解。如果在这一阶段,我们发现由于某些原因,高收入导致贷款批准率下降或转化率降低,那么这一点应该被自动标记。然而,在模型验证和 A/B 测试过程中,自动化的敏感性分析仍然不广泛可用,且仍然是一个具有挑战性的问题。一些供应商,如 TruEra,提供了该领域的潜在解决方案。

  • 监控与反馈循环:虽然模型性能指标和数据特征在这里非常重要,但可解释性可以为用户提供激励,促使他们提供有价值的反馈和用户行为指标,从而识别模型降级的驱动因素和原因(如果有的话)。正如我们所知,ML/DL 模型容易过拟合,且难以在训练数据之外做出良好的泛化。在模型生产监控过程中,一个重要的可解释性解决方案是衡量不同数据切分下特征重要性的变化(例如,COVID 之前与 COVID 之后)。这有助于数据科学家识别模型性能降级是由于数据变化(如统计分布变化)还是变量之间关系的变化(如概念漂移)。TruEra 提供的一个近期例子(truera.com/machine-learning-explainability-is-just-the-beginning/)说明了由于 COVID 前后人们的年收入和贷款目的的变化,贷款模型改变了其预测行为。这种特征重要性变化的可解释性在模型生产监控阶段对于识别模型行为变化的根本原因非常有帮助。

总结来说,DL 可解释性是一个重大挑战,仍需要持续的研究。然而,MLflow 与 SHAP 的集成现在为实际的 DL 应用提供了一个现成的工具,稍后我们将在本书的进阶章节中进行介绍。

总结

在本章开篇中,我们通过使用 PyTorch lightning-flash,遵循预训练加微调的核心深度学习(DL)开发范式,实施了我们的第一个 DL 模型,目标是进行文本情感分类。我们了解了 DL 的完整生命周期的五个阶段。我们定义了 MLOps 的概念,并介绍了其三个基础层和四个 ML/DL 支柱,其中 MLflow 在所有四个支柱(数据、模型、代码和可解释性)中都扮演着关键角色。最后,我们描述了 DL 中数据、模型、代码和可解释性方面的挑战。

在本章中获得的知识和第一个 DL 模型的经验后,我们现在已经准备好在接下来的章节中学习并实施 MLflow。在下一章中,我们将从启用 MLflow 自动记录功能的 DL 模型实现开始。

进一步阅读

为了进一步提升您的知识,敬请查阅以下资源和文档:

第二章:第二章:使用 MLflow 开始进行深度学习

MLflow 的一个关键功能是支持机器学习ML)实验管理。这一点至关重要,因为数据科学需要可重复性和可追溯性,这样一个深度学习DL)模型就能使用相同的数据、代码和执行环境轻松重现。本章将帮助我们快速了解如何实施 DL 实验管理。我们将学习 MLflow 实验管理的概念和功能,设置 MLflow 开发环境,并使用 MLflow 完成我们的第一个 DL 实验。在本章结束时,我们将拥有一个运行中的 MLflow 跟踪服务器,展示我们的第一个 DL 实验结果。

本章将涵盖以下主要内容:

  • 设置 MLflow

  • 实现我们的第一个启用 MLflow 日志记录的深度学习实验

  • 探索 MLflow 的组件和使用模式

技术要求

要完成本章的实验,我们需要在计算机上安装或检出以下工具、库和 GitHub 仓库:

设置 MLflow

MLflow 是一个开源工具,主要用 Python 编写。它在 GitHub 源代码库中获得了超过 10,000 个 stars(星标)(github.com/mlflow/mlflow)。使用 MLflow 的好处有很多,但我们可以通过以下场景来说明其中一个好处:假设您正在开始一个新的 ML 项目,试图评估不同的算法和模型参数。在几天之内,您运行了数百次实验,使用了不同的 ML/DL 库,并得到了不同的模型和不同的参数与准确度。您需要比较哪个模型效果更好,并且还要让您的团队成员能够重现结果以进行模型评审。您是否准备了一份电子表格,写下模型名称、参数、准确度以及模型位置?那么,其他人如何重新运行您的代码或使用您训练的模型与不同的评估数据集进行测试呢?当您为不同的项目进行大量迭代时,这可能会变得难以管理。MLflow 可以帮助您跟踪实验、比较模型运行,并让其他人轻松重现您的结果,重用您训练的模型进行评审,甚至轻松地将模型部署到生产环境中。

听起来很令人兴奋吧?那么,来设置一下 MLflow,让我们探索它的组件和模式。MLflow 支持本地设置和基于云的设置。我们将在接下来的章节中详细介绍这两种设置场景。

使用 miniconda 本地设置 MLflow

首先,让我们在本地开发环境中设置 MLflow。这可以快速进行原型设计,并帮助您熟悉 MLflow 工具的基本功能。此外,它还允许您在需要时与远程的 MLflow 云服务器进行交互。请按照以下说明设置 MLflow。

假设您已经根据 第一章,“深度学习生命周期与 MLOps 挑战”创建了一个虚拟 conda 环境,那么您可以准备在同一个虚拟环境中安装 MLflow:

pip install mlflow

上述命令将安装最新版本的 MLflow。如果您想安装特定版本的 MLflow,可以使用以下命令:

pip install mlflow==1.20.2

如您所见,我已安装了 MLflow 版本 1.20.2。默认情况下,MLflow 将使用本地文件系统来存储所有实验文档(例如,序列化模型)和元数据(参数、指标等)。如果需要关系型数据库作为 MLflow 的后端存储,则需要额外的安装和配置。现在,我们先使用文件系统进行存储。您可以通过在命令行中输入以下内容来验证本地的 MLflow 安装:

mlflow --version

然后,它将显示已安装的 MLflow 版本,如下所示:

mlflow, version 1.20.2

这表明我们已经在本地开发环境中安装了版本 1.20.2 的 MLflow。此外,您还可以在本地启动 MLflow UI,查看 MLflow 跟踪服务器 UI,如下所示:

mlflow ui

接下来,你将看到 UI 网页服务器正在运行:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_001.jpg

图 2.1 – 在本地环境中启动 MLflow UI

图 2.1 显示了本地 MLflow UI 网站:http://127.0.0.1:5000/。如果你点击这个 URL,你将在浏览器窗口中看到如下 MLflow UI。由于这是一个全新的 MLflow 安装,当前只有一个 默认 实验,且尚未有任何运行记录(请参见 图 2.2):

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_002.jpg

图 2.2 – MLflow 默认实验 UI 网页

看到默认的 MLflow UI 页面已经成功启动,标志着本地 MLflow 跟踪服务器的成功设置。

设置 MLflow 与远程 MLflow 服务器的交互

在企业生产环境中,MLflow 通常托管在云服务器上,可以是自托管的,也可以是 Databricks 在云提供商(如 AWS、Azure 或 Google Cloud)中提供的托管服务之一。在这种情况下,需要设置本地开发环境,以便能够在本地运行 ML/DL 实验,同时与远程 MLflow 服务器进行交互。接下来,我们将通过以下三个步骤介绍如何使用环境变量来实现这一目标:

  1. 在 bash shell 命令行环境中,如果你正在使用 Databricks 管理的 MLflow 跟踪服务器,请定义三个新的环境变量。第一个环境变量是 MLFLOW_TRACKING_URI,其值为 databricks

    export MLFLOW_TRACKING_URI=databricks
    export DATABRICKS_HOST=https://*******
    export DATABRICKS_TOKEN=dapi******
    
  2. 第二个环境变量是 DATABRICKS_HOST。如果你的 Databricks 管理网站地址是 https://dbc-*.cloud.databricks.com/,那么这就是 DATABRICKS_HOST 变量的值(将 * 替换为你的实际网站字符串)。

  3. 第三个环境变量是 DATABRICKS_TOKEN。前往你的 Databricks 管理网站 https://dbc-*.cloud.databricks.com/#setting/account,点击 访问令牌,然后点击 生成新令牌。你将看到一个弹出窗口,其中包含一个 备注 字段(用于记录此令牌的用途)和过期日期,如 图 2.3 所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_003.jpg

图 2.3 – 生成 Databricks 访问令牌

点击 DATABRICKS_TOKEN 环境变量作为其值:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_004.jpg

图 2.4 – 复制生成的令牌,将用于环境变量

一旦设置好这三个环境变量,您就可以在未来与 Databricks 管理的 MLflow 服务器进行交互。请注意,出于安全原因,访问令牌有有效期,管理员可以随时撤销该令牌,因此请确保在令牌更新时及时更新环境变量。

总结一下,我们已经学会了如何在本地设置 MLflow,以便与本地 MLflow 追踪服务器或远程 MLflow 追踪服务器进行交互。这将允许我们在下一节中实现第一个启用 MLflow 追踪的深度学习模型,从而以动手的方式探索 MLflow 的概念和组件。

使用 MLflow 自动日志记录实现我们的第一个深度学习实验

让我们使用在 第一章 中构建的深度学习情感分类器,深度学习生命周期与 MLOps 挑战,并向其添加 MLflow 自动日志记录,以探索 MLflow 的追踪功能:

  1. 首先,我们需要导入 MLflow 模块:

    import mlflow
    

这将为记录和加载模型提供 MLflow 应用程序编程接口 (APIs)。

  1. 在运行训练代码之前,我们需要使用 mlflow.set_experiment 为当前运行的代码设置一个活动实验:

    EXPERIMENT_NAME = "dl_model_chapter02"
    mlflow.set_experiment(EXPERIMENT_NAME)
    experiment = mlflow.get_experiment_by_name(EXPERIMENT_NAME)
    print("experiment_id:", experiment.experiment_id)
    

这将设置一个名为 dl_model_chapter02 的实验为当前活动实验。如果此实验在当前的追踪服务器中不存在,它将自动创建。

环境变量

请注意,在运行第一个实验之前,您可能需要使用 MLFLOW_TRACKING_URI 环境变量设置追踪服务器的 URI。如果您使用的是托管的 Databricks 服务器,请执行以下操作:

export MLFLOW_TRACKING_URI=databricks

如果您使用的是本地服务器,则将此环境变量设置为空或默认的本地主机端口号 5000,如下所示(请注意,这是我们当前章节的场景,并假设您正在使用本地服务器):

export MLFLOW_TRACKING_URI=http://127.0.0.1:5000

  1. 接下来,添加一行代码以启用 MLflow 的自动日志记录功能:

    mlflow.pytorch.autolog()
    

这将允许默认的参数、指标和模型自动记录到 MLflow 追踪服务器。

MLflow 自动日志记录

MLflow 中的自动日志记录仍处于实验模式(截至版本 1.20.2),未来可能会发生变化。在这里,我们使用它来探索 MLflow 组件,因为只需要一行代码就能自动记录所有相关信息。在接下来的章节中,我们将学习并实现更多关于在 MLflow 中进行追踪和日志记录的方法。此外,请注意,目前 MLflow 中的 PyTorch 自动日志记录(截至版本 1.20.2)仅适用于 PyTorch Lightning 框架,而不适用于任意的 PyTorch 代码。

  1. 使用 Python 上下文管理器 with 语句,通过调用 mlflow.start_run 来开始实验运行:

    with mlflow.start_run(experiment_id=experiment.experiment_id, run_name="chapter02"):
        trainer.finetune(classifier_model, 
                         datamodule=datamodule, 
                         strategy="freeze")
        trainer.test()
    

请注意,with 块下方的所有代码行都是常规的 DL 模型微调和测试步骤。我们只是启用了自动 MLflow 日志记录,这样我们就可以观察到 MLflow 跟踪服务器记录的元数据。

  1. 接下来,你可以使用以下命令行运行整个 first_dl_with_mlflow.py 的代码(完整代码可以在本章的 GitHub 上查看:github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow/blob/main/chapter02/first_dl_with_mlflow.py):

    python first_dl_with_mlflow.py
    

在没有 GPU 的 macOS 笔记本上,整个运行过程不到 10 分钟。你应该能在屏幕上看到以下输出:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_005.jpg

图 2.5 – 启用 MLflow 自动日志记录的 DL 模型训练/测试

如果这是你第一次运行,你会看到名为 dl_model_chapter02 的实验不存在。相反,MLflow 会为你自动创建这个实验:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_006.jpg

图 2.6 – 如果环境不存在,MLflow 会自动创建一个新的环境

  1. 现在,我们可以在本地打开 MLflow UI,通过访问 http://127.0.0.1:5000/ 来查看本地跟踪服务器记录的内容。在这里,你会看到一个新的实验(dl_model_chapter02)和一个新的运行(chapter02)已被记录:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_007.jpg

图 2.7 – MLflow 跟踪服务器 UI 显示一个新的实验和新的运行

现在,点击 图 2.7开始时间 列的超链接。你将看到该运行记录的元数据详情:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_008.jpg

图 2.8 – MLflow 运行 UI 显示实验运行的元数据详情

如果你能在自己的本地环境中看到这个屏幕,那么恭喜你!你刚刚完成了我们第一个 DL 模型的 MLflow 跟踪实现!在下一节中,我们将通过实际示例来探索 MLflow 的核心概念和组件。

探索 MLflow 的组件和使用模式

让我们使用上一节中实现的工作示例,探索 MLflow 中以下核心概念、组件和用法。这些包括实验、运行、实验的元数据、实验的工件、模型和代码。

在 MLflow 中探索实验和运行

实验 是 MLflow APIs 中的一等实体。这是有道理的,因为数据科学家和 ML 工程师需要运行大量实验,以构建符合要求的工作模型。然而,实验的概念不仅限于模型开发阶段,还延伸到整个 ML/DL 开发和部署的生命周期。因此,这意味着当我们对模型进行重训练或为生产版本进行训练时,我们需要将它们视为生产质量实验。这种对实验的统一视图建立了线下和线上生产环境之间的桥梁。每个实验由许多运行组成,您可以在每次运行时更改模型参数、输入数据,甚至是模型类型。因此,实验是一个包含一系列运行的总体实体。下图(图 2.9)说明了数据科学家可以在 ML/DL 模型生命周期的多个阶段进行线下实验和在线生产实验:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_009.jpg

图 2.9 – 跨机器学习(ML)/深度学习(DL)模型线下和线上生产生命周期的实验

如您从图 2.9中所见,在模型开发阶段,数据科学家可以根据项目情况运行多个相同实验的运行或多个实验。如果是一个小型 ML 项目,将所有运行放在一个单一的线下实验中可能足够了。如果是一个复杂的 ML 项目,则设计不同的实验并在每个实验下进行运行是合理的。关于设计 ML 实验的良好参考资料可以在machinelearningmastery.com/controlled-experiments-in-machine-learning/找到。然后,在模型生产阶段,最好设置生产质量的实验,因为我们需要进行模型改进和连续部署与模型重训练。生产实验将提供一个门控准确性检查,以防止新模型的回归。通常,这是通过运行自动模型评估和针对保留测试数据集的验证来实现的,以检查新模型在准确性方面是否仍符合发布标准。

现在,让我们以实际操作的方式探索 MLflow 实验。运行以下 MLflow 命令行与跟踪服务器进行交互:

mlflow experiments list 

如果您的MLFLOW_TRACKING_URI环境变量指向远程跟踪服务器,则将列出您具有读取权限的所有实验。如果要查看本地跟踪服务器中的内容,可以将MLFLOW_TRACKING_URI设置为空(即为空),如下所示(请注意,如果您的本地用户配置文件中从未有过此环境变量,则无需执行此操作;但是,执行此操作将确保您使用本地跟踪服务器):

export MLFLOW_TRACKING_URI=

在你第一次实现启用 MLflow 自动日志记录的 DL 模型之前,列出所有实验的输出应类似于 图 2.10(注意,这也取决于你运行命令行的位置;以下输出假设你在本地文件夹中运行该命令,并且可以查看 第二章 的 GitHub 代码):

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_010.jpg

图 2.10 – 本地环境中默认的 MLflow 实验列表

图 2.10 列出了实验属性的三列:mlruns 文件夹(位于执行 MLflow 命令的目录下)。mlruns 文件夹由基于文件系统的 MLflow 跟踪服务器使用,用于存储所有实验运行和工件的元数据。

命令行界面(CLI)与 REST API 与编程语言特定的 API

MLflow 提供了三种不同类型的工具和 API 与跟踪服务器进行交互。在这里,我们使用 CLI 来探索 MLflow 组件。

那么,让我们探索一个特定的 MLflow 实验,如下所示:

  1. 首先,使用 MLflow CLI 创建一个新的实验,如下所示:

    mlflow experiments create -n dl_model_chapter02
    

上述命令创建了一个名为 dl_model_chapter02 的新实验。如果你在前一节已经使用 MLflow 自动日志记录运行了第一个 DL 模型,执行上述命令将会报错,如下所示:

mlflow.exceptions.MlflowException: Experiment 'dl_model_chapter02' already exists.

这是预期的结果,你并没有做错什么。现在,如果你列出本地跟踪服务器上的所有实验,它应该会包括刚创建的实验,如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_011.jpg

图 2.11 – 创建新实验后,新的 MLflow 实验列表

  1. 现在,让我们检查实验与运行之间的关系。如果你仔细查看运行页面的 URL (图 2.8),你将看到类似以下内容:

http://127.0.0.1:5000/#/experiments/1/runs/2f2ec6e72a5042f891abe0d3a533eec7

正如你可能已经理解的那样,experiments 路径后的整数就是实验的 ID。然后,在实验 ID 后面,有一个 runs 路径,接着是一个类似 GUID 的随机字符串,这就是运行 ID。所以,现在我们了解了如何通过一个全球唯一的 ID(称为运行 ID)组织实验下的运行。

知道运行的全球唯一 ID 非常有用。因为我们可以通过 run_id 检索该运行的日志数据。如果你使用命令 mlflow runs describe --run-id <run_id>,你可以获得该运行记录的元数据列表。对于我们刚才运行的实验,下面显示了包含运行 ID 的完整命令:

mlflow runs describe –-run-id 2f2ec6e72a5042f891abe0d3a533eec7

该命令行的输出片段如下 (图 2.12):

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_012.jpg

图 2.12 – MLflow 命令行以 JSON 数据格式描述运行

请注意,图 2.12 展示了以 JSON 格式呈现的该运行的所有元数据。这些元数据包括模型训练过程中使用的参数;用于衡量模型在训练、验证和测试中的准确度的度量标准等。相同的数据也会在 MLflow 用户界面中以 图 2.8 的形式展示。请注意,强大的 MLflow 命令行界面(CLI)将允许非常方便地探索 MLflow 记录的元数据和工件,并支持基于 shell 脚本的自动化,这将在接下来的章节中进一步探讨。

探索 MLflow 模型及其用途

现在,让我们探索在 MLflow 跟踪服务器中如何记录 DL 模型工件。在同一运行页面上,如 图 2.8 所示,如果你向下滚动到页面底部,你将看到工件部分(图 2.13)。这里列出了有关模型的所有元数据和序列化模型本身:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_013.jpg

图 2.13 – MLflow 记录的模型工件

MLflow 跟踪服务器的后端存储和工件存储

MLflow 跟踪服务器有两种类型的存储:第一种是后端存储,用于存储实验和运行的元数据,以及运行的参数、度量标准和标签;第二种是工件存储,用于存储较大的文件,如序列化的模型、文本文件,甚至是生成的可视化图表。为了简化起见,本章中我们使用本地文件系统作为后端存储和工件存储。然而,像模型注册表等一些更高级的功能在基于文件系统的工件存储中不可用。在后续章节中,我们将学习如何使用模型注册表。

让我们逐一查看这些工件列表:

  • model_summary.txt:在 root 文件夹级别,这个文件点击后看起来类似于以下输出。它描述了模型的度量标准和深度学习(DL)模型的层次结构(请参见 图 2.14):

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_014.jpg

图 2.14 – MLflow 记录的模型摘要文件

图 2.14 提供了 DL 模型的简要概述,包括神经网络层的数量和类型、参数的数量和大小,以及训练和验证中使用的度量标准类型。当需要在团队成员或利益相关者之间共享和交流 DL 模型架构时,这非常有帮助。

  • model 文件夹:此文件夹包含一个名为 data 的子文件夹,并且包含三个文件:MLmodelconda.yamlrequirements.txt

    • MLmodel:此文件描述了 MLflow 支持的模型类型。MLmodel 文件(图 2.15):

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_015.jpg

图 2.15 – 我们首次使用 MLflow 运行的 DL 模型的 MLmodel 文件内容

图 2.15 展示了这是一个 PyTorch 类型的模型,并且我们刚刚运行了具有 run_id 的模型。

  • conda.yaml: 这是模型使用的 conda 环境定义文件,用于描述我们的依赖关系。图 2.16 列出了 MLflow 在我们刚刚完成的运行中记录的内容:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_02_016.jpg

图 2.16 – MLflow 记录的 conda.yaml 文件的内容

  • requirements.txt: 这是一个特定于 Python pip 的依赖定义文件。它就像 图 2.16conda.yaml 文件中的 pip 部分一样。

  • data: 这是一个包含实际序列化模型的文件夹,名为 model.pth,以及一个描述文件,名为 pickle_module_info.txt,我们第一个 DL 实验的内容如下:

    mlflow.pytorch.pickle_module
    

这意味着模型使用了 MLflow 提供的 PyTorch 兼容的 pickle 序列化方法进行序列化。这允许 MLflow 在需要时重新加载模型到内存中。

模型注册表与模型日志记录

MLflow 模型注册表需要关系数据库,如 MySQL,作为工件存储,而不仅仅是普通的文件系统。因此,在本章中我们不会深入探讨它。请注意,模型注册表与模型日志记录不同,对于每次运行,您希望记录模型的元数据和工件。但是,仅对符合您的生产要求的特定运行,您可能希望将它们注册到模型注册表以进行生产部署和版本控制。在后续章节中,我们将学习如何注册模型。

到目前为止,您应该对我们实验中的文件列表和关于模型以及序列化模型的元数据有了很好的理解(包括我们实验中的 .pth 文件扩展名,这指的是 PyTorch 序列化模型)。在接下来的章节中,我们将学习更多关于 MLflow 模型风格的工作原理以及如何将记录的模型用于模型注册和部署。MLflow 模型风格是 MLflow 支持的模型框架,如 PyTorch、TensorFlow 和 scikit-learn。有兴趣的读者可以在 MLflow 官方文档网站上找到关于当前内置模型风格的更多详细信息,网址为 www.mlflow.org/docs/latest/models.html#built-in-model-flavors

探索 MLflow 代码跟踪及其用法

当探索运行的元数据时,我们还可以发现代码是如何被跟踪的。如 MLflow UI 和 JSON 中的命令行输出所示,代码以三种方式进行跟踪:文件名、Git 提交哈希和源类型。您可以执行以下命令行:

mlflow runs describe --run-id 2f2ec6e72a5042f891abe0d3a533eec7 | grep mlflow.source

你应该能在输出中找到以下 JSON 键值对的片段:

"mlflow.source.git.commit": "ad6c7338a416ff4c2848d726b092057457c22408",
"mlflow.source.name": "first_dl_with_mlflow.py",
"mlflow.source.type": "LOCAL"

基于这个ad6c7338a416ff4c2848d726b092057457c22408的 Git 提交哈希,我们可以继续查找我们使用的 Python 代码的确切副本:github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLflow/blob/ad6c7338a416ff4c2848d726b092057457c22408/chapter02/first_dl_with_mlflow.py

请注意,这里的源类型是LOCAL。这意味着我们从代码的本地副本中执行启用 MLflow 的源代码。在后面的章节中,我们将了解其他源类型。

本地与远程 GitHub 代码

如果源代码是本地副本,那么在 MLflow 跟踪服务器中看到的 Git 提交哈希存在一个警告。如果你在本地做了代码更改,但忘记提交它们,然后立即开始 MLflow 实验跟踪运行,MLflow 只会记录最近的 Git 提交哈希。我们可以通过两种方式解决这个问题:

1. 在运行 MLflow 实验之前提交我们的代码更改。

2. 使用远程 GitHub 代码运行实验。

由于第一种方法不容易强制执行,因此推荐使用第二种方法。使用远程 GitHub 代码运行 DL 实验是一个高级话题,我们将在后面的章节中探讨。

到目前为止,我们已经了解了 MLflow 跟踪服务器、实验和运行。此外,我们还记录了关于运行的元数据,如参数和指标,检查了代码跟踪,并探讨了模型日志记录。这些跟踪和日志记录功能确保我们有一个扎实的 ML 实验管理系统,不仅用于模型开发,还为未来的模型部署提供支持,因为我们需要跟踪哪些运行生成了用于生产的模型。可重复性来源追踪是 MLflow 提供的标志性特点。除此之外,MLflow 还提供了其他组件,如用于标准化 ML 项目代码组织的MLproject、用于模型版本控制的模型注册表、模型部署功能和模型可解释性工具。所有这些 MLflow 组件涵盖了 ML/DL 开发、部署和生产的整个生命周期,我们将在后续章节中更深入地探讨这些内容。

总结

在本章中,我们学习了如何设置 MLflow,使其与本地 MLflow 跟踪服务器或远程 MLflow 跟踪服务器一起工作。然后,我们实现了第一个启用自动日志记录的 DL 模型。这样,我们就可以通过实践的方式探索 MLflow,理解一些核心概念和基础组件,如实验、运行、实验和运行的元数据、代码跟踪、模型日志记录和模型风味。本章中获得的知识和首次经验将帮助我们在下一章深入学习 MLflow 跟踪 API。

进一步阅读

为了进一步拓展你的知识,你可以参考以下资源和文档:

第二部分 – 大规模跟踪深度学习管道

在本节中,我们将学习如何使用 MLflow 来跟踪深度学习DL)管道,以回答与数据、模型和代码(包括笔记本和管道代码)相关的各种溯源问题。我们将从设置一个本地的完整 MLflow 跟踪服务器开始,该服务器将在本书的其余部分频繁使用。我们将介绍一个包括六种类型溯源问题的溯源跟踪框架,以指导我们的实现。接着,我们将学习如何使用 MLflow 跟踪模型溯源、指标和参数,以回答这些溯源问题。我们还将学习如何选择合适的笔记本和管道框架来实现可扩展且可跟踪的深度学习代码。然后,我们将使用 MLflow 的MLproject实现一个多步骤的深度学习训练/测试/注册管道。最后,我们将学习如何使用Delta Lake跟踪公共和私有构建的 Python 库及数据版本控制。

本节包括以下章节:

  • 第三章跟踪模型、参数和指标

  • 第四章跟踪代码和数据版本控制

第三章:第三章:跟踪模型、参数和指标

鉴于 MLflow 可以支持深度学习模型生命周期中的多种场景,通常会逐步使用 MLflow 的功能。通常,人们从 MLflow 跟踪开始,因为它易于使用,能够处理许多可复现性、来源追踪和审计目的的场景。此外,从模型的“摇篮到日落”跟踪其历史,不仅超越了数据科学实验管理领域,而且对于企业中的模型治理也至关重要,因为在生产中使用模型时,需要管理业务和监管风险。虽然在生产中跟踪模型的精确商业价值仍在发展中,但对跟踪模型整个生命周期的需求是不可置疑且日益增长的。为了实现这一点,我们将从本章开始,搭建一个完整的本地 MLflow 跟踪服务器。

然后,我们将深入探讨如何使用 MLflow 的跟踪和注册 API 跟踪模型及其参数和指标。在本章结束时,你应该能够熟练使用 MLflow 的跟踪和注册 API 进行各种可复现性和审计目的。

在本章中,我们将讨论以下主要内容:

  • 搭建一个完整的本地 MLflow 跟踪服务器

  • 跟踪模型来源

  • 跟踪模型指标

  • 跟踪模型参数

技术要求

以下是你需要遵循本章提供的指令所需的要求:

搭建一个完整的本地 MLflow 跟踪服务器

第二章使用 MLflow 进行深度学习入门中,我们通过实践操作,使用了基于本地文件系统的 MLflow 跟踪服务器,并检查了 MLflow 实验的组件。然而,基于默认本地文件系统的 MLflow 服务器存在一些限制,因为模型注册功能不可用。拥有模型注册的好处在于我们可以注册模型、进行版本控制,并为模型部署到生产环境做准备。因此,模型注册将填补离线实验与在线生产部署之间的空白。因此,我们需要一个功能齐全的 MLflow 跟踪服务器,并且该服务器需要以下存储来跟踪模型的完整生命周期:

  • 后端存储:需要一个关系型数据库后端来支持 MLflow 存储关于实验的元数据(如度量、参数等)。这还允许查询实验的功能。我们将使用 MySQL 数据库作为本地后端存储。

  • 工件存储:一个可以存储任意类型对象的对象存储,比如序列化模型、词汇文件、图表等。在生产环境中,常用的选择是 AWS S3 存储。我们将使用MinIOmin.io/),一个多云对象存储,作为本地工件存储,它完全兼容 AWS S3 存储 API,但可以在本地电脑上运行,无需访问云端。

为了尽可能简化本地设置,我们将使用docker-composedocs.docker.com/compose/)工具,只需一行命令即可启动和停止本地全功能的 MLflow 跟踪服务器,具体步骤如下。请注意,必须先在机器上安装并运行 Docker Desktop(docs.docker.com/get-docker/),才能执行这些步骤。Docker 有助于构建和分享容器化应用程序和微服务。以下步骤将在本地 Docker 容器中启动本地 MLflow 跟踪服务器:

  1. 查看chapter03代码库以便配置本地开发环境:github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLflow/tree/main/chapter03

  2. 进入mlflow_docker_setup子文件夹,该文件夹位于chapter03文件夹下。

  3. 执行以下命令:

    bash start_mlflow.sh
    

如果命令成功执行,屏幕上应显示类似以下内容的输出:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_001.jpg

图 3.1 – 本地全功能的 MLflow 跟踪服务器已启动并运行

  1. 访问http://localhost/,你应该能看到 MLflow UI 网页。然后,点击 UI 中的Models标签(图 3.2)。请注意,如果你的 MLflow 追踪服务器仅使用本地文件系统作为后端存储,该标签将无法使用。因此,MLflow UI 的后端现在正在运行在你刚启动的 Docker 容器服务上,而不是本地文件系统。由于这是一个全新的服务器,目前还没有注册的模型:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_002.jpg

图 3.2 – MLflow 模型注册 UI

  1. 访问http://localhost:9000/,此时应该会显示 MinIO 工件存储 Web UI 的页面(图 3.3)。在.env文件中,输入minio作为用户名,minio123作为密码,位于mlflow_docker_setup文件夹下:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_003.jpg

图 3.3 – MinIO Web UI 登录页面和登录后的浏览器页面

此时,你应该已经成功启动了一个完整的本地 MLflow 追踪服务器!如果你想停止服务器,只需输入以下命令:

bash stop_mlflow.sh

基于 Docker 的 MLflow 追踪服务器将停止。我们现在准备使用这个本地 MLflow 服务器来追踪模型的来源、参数和指标。

追踪模型来源

来源追踪在数字工件方面已经在文献中得到广泛研究。例如,在生物医学行业,当你使用一份患者诊断数据时,通常希望知道这些数据的来源,数据经过了哪些处理和清洗,数据的所有者是谁,以及其他有关数据的历史和谱系信息。随着工业和商业场景中 ML/DL 模型的兴起,来源追踪已成为一项必备功能。不同粒度的来源追踪对操作和管理不仅仅是数据科学离线实验至关重要,而且对模型在生产环境中部署前/中/后的管理也至关重要。那么,来源追踪需要追踪哪些内容呢?

了解开放来源追踪框架

让我们看一下一个通用的来源追踪框架,以理解为什么来源追踪是一个重大努力。以下图示基于开放来源模型词汇规范open-biomed.sourceforge.net/opmv/ns.html):

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_004.jpg

图 3.4 – 一个开放的来源追踪框架

在前述图示中,有三个重要的项目:

  • 工件:由过程生产或使用的事物(A1A2)。

  • 过程:通过使用或生产工件来执行的操作(P1P2)。

  • 因果关系:在工件和过程之间的边或关系,如前述图示中的usedwasGeneratedBywasDerivedFromR1R2R3)。

直观地说,开放来源模型 (OPM) 框架允许我们提出以下 5W1H(五个 W 和一个 H)问题,如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_005.jpg

](https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_005.jpg)

图 3.5 – 来源问题的类型

拥有一个系统化的来源框架和一组问题将帮助我们学习如何追踪模型来源并提供这些问题的答案。这将激励我们在下一节实现 MLflow 模型跟踪时。

实现 MLflow 模型跟踪

如果我们为所使用的深度学习模型实现了 MLflow 日志记录和注册功能,我们可以使用 MLflow 跟踪服务器回答这些来源问题中的大多数。首先,让我们回顾 MLflow 在模型来源跟踪方面提供的功能。MLflow 提供了两套用于模型来源的 API:

  • 日志记录 API:这允许每次实验或模型管道运行时将模型工件记录到工件存储中。

  • 注册表 API:这允许在一个集中位置跟踪模型的版本和模型生命周期的各个阶段(已归档暂存生产)。

    模型日志记录和模型注册表之间的区别

    尽管每次实验都需要进行日志记录,并且模型需要保存在工件存储中,但并非每个模型实例都需要在模型注册表中注册。这是因为对于许多早期的探索性模型实验来说,模型可能不好。因此,不一定要注册以跟踪版本。只有当模型在离线测试中表现良好并成为推向生产环境的候选模型时,才需要将其注册到模型注册表,以便通过模型提升流程。

    尽管 MLflow 的官方 API 文档将日志记录和注册表分成了两个组件,但在本书中我们将它们统称为 MLflow 的模型跟踪功能。

我们已经在 第二章《使用 MLflow 进行深度学习入门》中看到过 MLflow 的自动日志记录功能。尽管自动日志记录功能非常强大,但当前版本存在两个问题:

  • 它不会自动将模型注册到模型注册表。

  • 如果你仅按照 MLflow 的建议使用 mlflow.pyfunc.load_model API 来加载已记录的模型,它不能直接与原始输入数据(在我们的案例中是英文句子)一起工作。这是由于 MLflow 当前自动日志记录 API 的实验性质,可能是一个局限性。

让我们通过一个示例来回顾 MLflow 的功能以及自动日志记录的局限性,并讨论我们如何解决这些问题:

  1. 在 Bash 终端中设置以下环境变量,其中你的 MinIO 和基于 MySQL 的 Docker 组件正在运行:

    export MLFLOW_S3_ENDPOINT_URL=http://localhost:9000
    export AWS_ACCESS_KEY_ID=minio
    export AWS_SECRET_ACCESS_KEY=minio123
    

请注意,AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY 使用的是在 .env 文件中定义的相同值,该文件位于 mlflow_docker_setup 文件夹下。这是为了确保我们使用的是之前设置的 MLflow 服务器。由于这些环境变量是基于会话的,我们还可以在笔记本的代码中设置以下环境变量,如下所示:

os.environ["AWS_ACCESS_KEY_ID"] = "minio"
os.environ["AWS_SECRET_ACCESS_KEY"] = "minio123"
os.environ["MLFLOW_S3_ENDPOINT_URL"] = "http://localhost:9000"

前面三行代码可以在本章的笔记本文件中找到,紧接在导入所需的 Python 包之后。在执行笔记本之前,请确保运行以下命令来初始化虚拟环境 dl_model,该环境现在包含 requirements.txt 文件中定义的额外必需包:

conda create -n dl_model python==3.8.10
conda activate dl_model
pip install -r requirements.txt

如果你在之前的章节中设置了 dl_model 虚拟环境,你可以跳过创建名为 dl_model 的虚拟环境的第一行。然而,你仍然需要激活 dl_model 作为当前活动的虚拟环境,然后运行 pip install -r requirements.txt 来安装所有所需的 Python 包。一旦 dl_model 虚拟环境成功设置,你可以继续执行下一步。

  1. 要跟随此模型跟踪实现,请查看 VS Code 中的 dl_model_tracking.ipynb 笔记本文件,方法是访问本章的 GitHub 仓库:github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow/blob/main/chapter03/dl_model_tracking.ipynb

请注意,在 dl_model_tracking.ipynb 笔记本的第四个单元格中,我们需要将其指向我们刚刚在 Docker 中设置的正确的新 MLflow 跟踪 URI,并定义一个新的实验,如下所示:

EXPERIMENT_NAME = "dl_model_chapter03"
mlflow.set_tracking_uri('http://localhost')
  1. 我们仍然会使用 MLflow 提供的自动日志记录功能,但我们将给运行分配一个变量名,dl_model_tracking_run

    mlflow.pytorch.autolog()
    with mlflow.start_run(experiment_id=experiment.experiment_id, run_name="chapter03") as dl_model_tracking_run:
        trainer.finetune(classifier_model, datamodule=datamodule, strategy="freeze")
        trainer.test()
    

dl_model_tracking_run 允许我们以编程方式获取 run_id 参数和此运行的其他元数据,正如我们在下一步中将看到的那样。一旦执行了这段代码单元,我们将在 MLflow 跟踪服务器中记录一个训练好的模型,并包含所有所需的参数和指标。然而,模型还没有注册。我们可以在 MLflow 的 Web UI 中找到记录的实验以及所有相关的参数和指标,地址是 http://localhost/#/experiments/1/runs/37a3fe9b6faf41d89001eca13ad6ca47。你可以在 http://localhost:9000/minio/mlflow/1/37a3fe9b6faf41d89001eca13ad6ca47/artifacts/model/ 找到模型工件并查看存储 UI,如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_006.jpg

图 3.6 – 模型工件记录在 MinIO 存储后端

文件夹结构与我们在第二章使用 MLflow 开始深度学习》中看到的类似,当时我们使用普通文件系统来存储模型工件。然而,在这里,我们使用 MinIO 桶来存储这些模型工件。

  1. dl_model_tracking_run 中检索 run_id 参数以及其他元数据,如下所示:

    run_id = dl_model_tracking_run.info.run_id
    print("run_id: {}; lifecycle_stage: {}".format(run_id,
        mlflow.get_run(run_id).info.lifecycle_stage))
    

这将打印出类似以下内容:

run_id: 37a3fe9b6faf41d89001eca13ad6ca47; lifecycle_stage: active
  1. 通过定义已记录模型的 URI 来检索已记录的模型。这将允许我们在特定位置重新加载已记录的模型:

    logged_model = f'runs:/{run_id}/model'
    
  2. 使用 mlflow.pytorch.load_model 和以下 logged_model URI 将模型加载回内存,并为给定的输入句子做出新预测,如下所示:

    model = mlflow.pytorch.load_model(logged_model)
    model.predict({'This is great news'})
    

这将输出一个模型预测标签,如下所示:

['positive']

mlflow.pytorch.load_model 与 mlflow.pyfunc.load_model

默认情况下,在 MLflow 实验跟踪页面的工件部分,如果您有已记录的模型,MLflow 会建议使用 mlflow.pyfunc.load_model 来加载已记录的模型进行预测。然而,这仅适用于像 pandas DataFrame、NumPy 数组或张量这样的输入;不适用于 NLP 文本输入。由于 PyTorch Lightning 的自动记录使用 mlflow.pytorch.log_model 来保存模型,正确的加载已记录模型的方法是使用 mlflow.pytorch.load_model,正如我们在这里展示的那样。这是因为 MLflow 的默认设计是使用 mlflow.pyfunc.load_model,并且有一个已知的限制,要求输入格式必须是数值类型。对于文本和图像数据,它需要在预处理步骤中进行分词。然而,由于我们在此保存的 PyTorch 模型已经作为序列化模型的一部分执行了分词步骤,我们可以使用原生的 mlflow.pytorch.load_model 来直接加载接受文本输入的模型。

有了这些,我们就成功记录了模型并将其加载回来进行预测。如果我们认为这个模型表现足够好,那么我们可以将其注册。

  1. 让我们使用 mlflow.register_model API 来注册模型:

    model_registry_version = mlflow.register_model(logged_model, 'nlp_dl_model')
    print(f'Model Name: {model_registry_version.name}')
    print(f'Model Version: {model_registry_version.version}')
    

这将生成以下输出:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_007.jpg

图 3.7 – 模型注册成功消息

这表明模型已成功注册为版本 1,并存储在模型注册表中,模型名称为 nlp_dl_model

我们还可以通过点击 http://localhost/#/models/nlp_dl_model/versions/1 在 MLflow Web UI 中找到这个已注册的模型:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_008.jpg

图 3.8 – MLflow 跟踪服务器 Web UI 显示新注册的模型

默认情况下,新注册模型的阶段为 None,如前述截图所示。

通过为模型注册版本号和阶段标签,我们为部署到暂存(也称为预生产)和生产环境奠定了基础。我们将在本书的后面讨论基于已注册模型进行模型部署的方法。

到目前为止,我们已经解决了本节开头关于自动记录限制的两个问题:

  • 如何使用 mlflow.pytorch.load_model API 而不是 mlflow.pyfunc.load_model API 加载已记录的 DL PyTorch 模型

  • 如何使用 mlflow.register_model API 注册已记录的 DL PyTorch 模型

    MLflow DL 模型记录 API 的选择

    对于 DL 模型,PyTorch 的自动记录仅适用于 mlflow.pyfunc.log_model 用于记录模型,特别是当我们需要多步骤 DL 模型管道时。我们将在本书的后面实现这样的自定义 MLflow 模型风格。如果你不想使用 PyTorch 的自动记录,那么可以直接使用 mlflow.pytorch.log_model。PyTorch 的自动记录在其实现中使用了 mlflow.pytorch.log_model(请参阅官方 MLflow 开源实现:github.com/mlflow/mlflow/blob/290bf3d54d1e5ce61944455cb302a5d6390107f0/mlflow/pytorch/_pytorch_autolog.py#L314)。

如果我们不想使用自动记录,那么可以直接使用 MLflow 的模型记录 API。这还为我们提供了一种同时注册模型的替代方法。您可以使用以下代码行来记录和注册训练好的模型:

mlflow.pytorch.log_model(pytorch_model=trainer.model, artifact_path='dl_model', registered_model_name='nlp_dl_model')

请注意,此代码行不记录模型的任何参数或度量值。

通过这种方式,我们不仅在跟踪服务器中记录了许多实验和模型以进行离线实验,而且还注册了性能优越的模型,以便将来能够进行版本控制和溯源跟踪,并将其部署到生产环境中。我们现在可以回答本章开头提出的一些溯源问题:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_009.jpg

图 3.9 – 模型溯源问题的答案

为什么和在哪里的问题尚未完全解答,但将在本书后续部分中解答。这是因为生产模型的“为什么”问题只有在模型准备部署时才能被跟踪和记录,此时我们需要添加注释和理由来证明模型的部署合理性。至于“在哪里”的问题,当我们有多步骤模型流水线时可以完全解答。然而,在这里,我们只有一个单步骤流水线,这是最简单的情况。一个多步骤流水线包含明确分离的模块化代码,用于指定每个步骤执行什么功能,这样我们就可以轻松更改任何步骤的详细实现,而不改变流水线的流程。在接下来的两个部分中,我们将探讨如何在不使用自动日志记录的情况下跟踪模型的指标和参数。

跟踪模型指标

PyTorch lightning-flash包中的文本分类模型的默认指标是准确率。如果我们想将指标更改为F1 分数(精度和召回率的调和平均数),这是衡量分类器性能的常用指标,那么我们需要在开始模型训练过程之前更改分类器模型的配置。让我们学习如何进行此更改,并使用 MLflow 的非自动日志记录 API 来记录指标:

  1. 在定义分类器变量时,我们将传递一个名为torchmetrics.F1的度量函数作为变量,而不是使用默认的度量函数,如下所示:

    classifier_model = TextClassifier(backbone="prajjwal1/bert-tiny", num_classes=datamodule.num_classes, metrics=torchmetrics.F1(datamodule.num_classes))
    

这使用了torchmetrics的内置度量函数F1模块,并将数据中需要分类的类数作为参数。这确保了模型在训练和测试时使用了这个新指标。您将看到类似以下的输出:

{'test_cross_entropy': 0.785443127155304, 'test_f1': 0.5343999862670898}

这表明模型的训练和测试使用的是 F1 分数作为指标,而不是默认的准确率指标。有关如何使用torchmetrics自定义指标的更多信息,请参考其文档网站:torchmetrics.readthedocs.io/en/latest/

  1. 现在,如果我们想将所有指标记录到 MLflow 跟踪服务器中,包括训练、验证和测试指标,我们需要通过调用训练器的回调函数来获取所有当前的指标,如下所示:

        cur_metrics = trainer.callback_metrics
    

接下来,我们需要将所有度量值转换为float,以确保它们与 MLflow 的log_metrics API 兼容:

    metrics = dict(map(lambda x: (x[0], float(x[1])), cur_metrics.items()))
  1. 现在,我们可以调用 MLflow 的log_metrics来记录所有在跟踪服务器中的指标:

        mlflow.log_metrics(metrics)
    

使用 F1 分数作为分类器指标后,您将看到以下指标,这些指标将被记录在 MLflow 的跟踪服务器中:

{'train_f1': 0.5838666558265686, 
'train_f1_step': 0.75, 
'train_cross_entropy': 0.7465656399726868, 
'train_cross_entropy_step': 0.30964696407318115, 
'val_f1': 0.5203999876976013, 
'val_cross_entropy': 0.8168156743049622, 
'train_f1_epoch': 0.5838666558265686, 
'train_cross_entropy_epoch': 0.7465656399726868, 
'test_f1': 0.5343999862670898, 
'test_cross_entropy': 0.785443127155304}

使用 MLflow 的log_metrics API 让我们通过额外的代码行获得更多控制,但如果我们对其自动日志记录功能满意,那么我们只需要改变我们想要在模型训练和测试过程中使用的度量。此时,我们只需要在声明新的深度学习模型时定义一个新的度量(即使用 F1 分数而不是默认的准确率度量)。

  1. 如果您想同时跟踪多个模型度量,例如 F1 分数、准确率、精确度和召回率,那么您需要做的就是定义一个包含您想要计算和跟踪的度量的 Python 列表,如下所示:

    list_of_metrics = [torchmetrics.Accuracy(),
       torchmetrics.F1(num_classes=datamodule.num_classes),
       torchmetrics.Precision(num_classes=datamodule.num_classes),
       torchmetrics.Recall(num_classes=datamodule.num_classes)]
    

然后,在模型初始化语句中,您可以不传递单个度量到metrics参数,而是传递我们刚刚定义的list_of_metrics Python 列表,如下所示:

classifier_model = TextClassifier(backbone="prajjwal1/bert-tiny", num_classes=datamodule.num_classes, metrics=list_of_metrics)

剩下的代码无需再做任何更改。因此,在dl_model-non-auto-tracking.ipynb笔记本(https://github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow/blob/main/chapter03/dl_model-non-auto-tracking.ipynb)中,您会注意到前一行默认被注释掉。然而,您可以取消注释它,然后注释掉前面的那一行:

classifier_model = TextClassifier(backbone="prajjwal1/bert-tiny", num_classes=datamodule.num_classes, metrics=torchmetrics.F1(datamodule.num_classes))

然后,当您运行笔记本的其余部分时,您将在笔记本输出中获得模型测试报告,附带以下度量:

{'test_accuracy': 0.6424000263214111, 'test_cross_entropy': 0.6315688490867615, 'test_f1': 0.6424000263214111, 'test_precision': 0.6424000263214111, 'test_recall': 0.6424000263214111}

您可能会注意到准确率、F1 分数、精确度和召回率的数字是相同的。这是因为,默认情况下,torchmetrics使用一个不支持none方法的torchmetrics,该方法为每个类计算度量,并返回每个类的度量,即使是二分类模型。因此,这不会生成一个单一的标量值。然而,您总是可以调用 scikit-learn 的度量 API,通过传递两个值列表来根据二元平均方法计算 F1 分数或其他度量。在这里,我们可以使用y_truey_predict,其中y_true是地面真实标签值的列表,而y_predict是模型预测标签值的列表。这可以作为一个很好的练习,供您尝试,因为这是所有机器学习模型的常见做法,而不仅仅是深度学习模型的特殊处理。

跟踪模型参数

如我们所见,使用 MLflow 的自动日志记录有许多好处,但如果我们想要跟踪额外的模型参数,我们可以使用 MLflow 在自动日志记录记录的基础上记录额外的参数,或者直接使用 MLflow 记录我们想要的所有参数,而不使用自动日志记录。

让我们在不使用 MLflow 自动日志记录的情况下走一遍笔记本。如果我们想要完全控制 MLflow 记录哪些参数,可以使用两个 API:mlflow.log_parammlflow.log_params。第一个用于记录单个键值对参数,而第二个用于记录整个键值对参数的字典。那么,我们可能会感兴趣跟踪哪些类型的参数呢?以下是答案:

  • 使用 log_params API 在实验中记录它们。

  • 模型参数:这些参数是在模型训练过程中学习到的。对于深度学习模型,这通常指的是在训练过程中学习到的神经网络权重。我们不需要单独记录这些权重参数,因为它们已经包含在已记录的深度学习模型中。

让我们使用 MLflow 的 log_params API 来记录这些超参数,代码如下:

    params = {"epochs": trainer.max_epochs}
    if hasattr(trainer, "optimizers"):
        optimizer = trainer.optimizers[0]
        params["optimizer_name"] = optimizer.__class__.__name__
    if hasattr(optimizer, "defaults"):
        params.update(optimizer.defaults)
    params.update(classifier_model.hparams)
    mlflow.log_params(params)

请注意,在这里,我们记录了最大轮数、训练器的第一个优化器名称、优化器的默认参数以及整体分类器的超参数(classifier_model.hparams)。那行代码 mlflow.log_params(params)params 字典中的所有键值参数记录到 MLflow 跟踪服务器。如果你在 MLflow 跟踪服务器中看到以下超参数,说明它已经生效!

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_03_010.jpg

图 3.10 – MLflow 跟踪服务器的 Web UI 显示已记录的模型超参数

注意,这个参数列表比自动记录器记录的要多,因为我们在实验中添加了额外的超参数。如果你想记录其他自定义的参数,可以按照相同的模式在你的实验中进行。完整的笔记本(没有使用自动记录)可以在本章的 GitHub 仓库中查看,链接为 github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow/blob/main/chapter03/dl_model-non-auto-tracking.ipynb

如果你已经读到这一部分,说明你已经成功实现了一个 MLflow 跟踪模型以及其度量和参数!

总结

在本章中,我们设置了一个本地的 MLflow 开发环境,完全支持使用 MySQL 和 MinIO 对象存储进行后端存储和工件存储。这在我们本书中开发 MLflow 支持的深度学习模型时非常有用。我们首先介绍了开放的溯源跟踪框架,并提出了有关模型溯源的相关问题。我们解决了自动记录的问题,并成功地通过 mlflow.pytorch.load_model API 从已记录的模型中加载一个训练好的模型进行预测,完成了训练模型的注册工作。我们还尝试了如何在没有自动记录的情况下直接使用 MLflow 的 log_metricslog_paramslog_model API,这使我们能够更好地控制和灵活地记录额外的或自定义的度量和参数。通过执行模型溯源跟踪,我们能够回答许多溯源问题,并提出了几个需要进一步研究的问题,例如使用 MLflow 跟踪多步骤模型管道及其部署。

在下一章,我们将继续我们的学习旅程,学习如何使用 MLflow 进行代码和数据跟踪,这将为我们提供更多的能力,以回答与数据和代码相关的来源问题。

进一步阅读

若要了解本章涉及的更多主题,请查看以下资源:

第四章:第四章:跟踪代码和数据版本

深度学习(DL)模型不仅仅是模型——它们与训练和测试模型的代码,以及用于训练和测试的数据密切相关。如果我们不跟踪用于模型的代码和数据,就无法重现模型或改进它。此外,近期在整个行业范围内对数据中心 AI的觉醒和范式转变(www.forbes.com/sites/gilpress/2021/06/16/andrew-ng-launches-a-campaign-for-data-centric-ai/?sh=5cbacdc574f5)使得数据在构建机器学习(ML)和特别是深度学习(DL)模型中的重要性被提升为首要要素。因此,在本章节中,我们将学习如何使用 MLflow 跟踪代码和数据版本。我们将学习如何跟踪代码和管道版本,并了解如何使用 Delta Lake 进行数据版本管理。在本章节结束时,您将能够理解并实现使用 MLflow 跟踪代码和数据的技巧。

在本章中,我们将涵盖以下主要主题:

  • 跟踪笔记本和管道版本

  • 跟踪本地私有构建的 Python 库

  • 在 Delta Lake 中跟踪数据版本

技术要求

本章节的技术要求如下:

跟踪笔记本和管道版本

数据科学家通常从离线实验 Python 笔记本开始,交互式执行是其主要优势之一。自 .ipynb 时代以来,Python 笔记本已经取得了长足的进展。你可能也无法在 MLflow 跟踪服务器中看到每次使用 Jupyter 笔记本运行的确切 Git 哈希值。关于是否以及何时应该在生产环境中使用 Jupyter 笔记本,存在许多有趣的争论(可以在这里查看讨论:medium.com/mlops-community/jupyter-notebooks-in-production-4e0d38803251)。我们不应在生产环境中使用 Jupyter 笔记本的原因有很多,尤其是在我们需要端到端管道中的可复现性时,单元测试、代码版本控制和依赖管理在大量笔记本的情况下可能会变得困难。Netflix 的开源工具 papermillpapermill.readthedocs.io/en/latest/index.html)在调度、参数化和按工作流方式执行 Jupyter 笔记本方面做出了一些早期创新。然而,Databricks 和 VS Code 的最近创新使得笔记本更容易进行版本控制并与 MLflow 集成。让我们来看一下这两个工具引入的笔记本特性:

  • 交互式执行:Databricks 的笔记本和 VS Code 的笔记本可以像传统的 Jupyter 笔记本一样运行,采用逐单元格执行模式。通过这种方式,你可以立即看到结果的输出。

  • .py 文件扩展名。这允许对笔记本应用常规的 Python 代码检查(代码格式和风格检查)。

  • 渲染代码单元格和 Markdown 单元格的特殊符号:Databricks 和 VS Code 都利用一些特殊符号将 Python 文件渲染为交互式笔记本。在 Databricks 中,用于将代码分隔成不同可执行单元格的特殊符号如下:

    # COMMAND ---------- 
    import mlflow
    import torch
    from flash.core.data.utils import download_data
    from flash.text import TextClassificationData, TextClassifier
    import torchmetrics
    

特殊的 COMMAND 行下方的代码将在 Databricks Web UI 门户中作为可执行单元格进行渲染,如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.1.jpg

图 4.1 – Databricks 可执行单元格

要执行此单元格中的代码,你只需通过右上方的下拉菜单点击 运行单元格

要向 Databricks 中添加大量文本以描述和评论代码(也称为 Markdown 单元格),你可以在行的开头使用 # MAGIC 符号,如下所示:

# MAGIC %md
# MAGIC #### Notebooks for fine-tuning a pretrained language model to do text-based sentiment classification

然后,这将在 Databricks 笔记本中渲染为一个 Markdown 注释单元格,如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.2.jpg

图 4.2 – Databricks Markdown 文本单元格

在 VS Code 中,这两种类型的单元格使用略有不同的符号集。对于代码单元格,# %% 符号用于单元格块的开头:

# %%
download_data("https://pl-flash-data.s3.amazonaws.com/imdb.zip", "./data/")
datamodule = TextClassificationData.from_csv(
    input_fields="review",
    target_fields="sentiment",
    train_file="data/imdb/train.csv",
    val_file="data/imdb/valid.csv",
    test_file="data/imdb/test.csv"
)

然后,这将在 VS Code 的编辑器中呈现,如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.3.jpg

图 4.3 – VS Code 代码单元格

如您所见,在代码块前面有一个运行单元格按钮,您可以点击该按钮以交互式地运行代码块。如果点击运行单元格按钮,代码块将在编辑器窗口的侧边面板中开始执行,如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.4.jpg

图 4.4 – 在 VS Code 中交互式运行代码

要添加一个包含注释的 Markdown 单元格,请在行的开头添加以下内容,并使用必要的符号:

# %% Notebook for fine-tuning a pretrained language model and sentiment classification

这将确保文本在 VS Code 中不是一个可执行的代码块。

鉴于 Databricks 和 VS Code 笔记本的优点,我们建议使用其中任意一种进行版本控制。我们可以使用 GitHub 来跟踪任何一种类型笔记本的版本,因为它们使用的是常规的 Python 文件格式。

使用 Databricks 笔记本版本控制的两种方法

对于托管的 Databricks 实例,笔记本版本可以通过两种方式进行跟踪:通过查看 Databricks Web UI 中笔记本侧边面板的修订历史,或者通过链接到远程 GitHub 仓库。详细描述可以参考 Databricks 笔记本文档:docs.databricks.com/notebooks/notebooks-use.html#version-control

虽然 Databricks Web 门户为笔记本版本控制和与 MLflow 实验追踪的集成提供了出色的支持(参见本章中的两种使用 Databricks 笔记本版本控制的方法Databricks 笔记本中的两种 MLflow 实验类型的提示框),但是在 Databricks 笔记本 Web UI 中编写代码有一个主要的缺点。这是因为与 VS Code 相比,Web UI 不是一个典型的集成开发环境IDE),在 VS Code 中,代码样式和格式化工具如 flake8flake8.pycqa.org/en/latest/)和 autopep8(pypi.org/project/autopep8/)可以轻松执行。这对代码质量和可维护性有重大影响。因此,强烈建议使用 VS Code 来编写笔记本代码(无论是 Databricks 笔记本还是 VS Code 笔记本)。

Databricks 笔记本中的两种 MLflow 实验类型

对于一个托管的 Databricks Web 门户实例,您可以执行两种类型的 MLflow 实验:工作区实验和 notebook 实验。工作区实验主要用于一个共享的实验文件夹,该文件夹不绑定到单个 notebook。如果需要,远程代码执行可以写入工作区实验文件夹。另一方面,notebook 范围实验绑定到特定的 notebook,并且可以在 Databricks Web 门户的 notebook 页面上直接通过右上角的 Experiment 菜单项找到。有关更多细节,请查看 Databricks 文档网站:docs.databricks.com/applications/mlflow/tracking.html#experiments

使用本章的 VS Code notebook fine_tuning.py,该文件可以在本章的 GitHub 仓库中找到(github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow/blob/main/chapter04/notebooks/fine_tuning.py),您将能够在 VS Code 编辑器中交互式运行它,并在我们在 第三章 中设置的 MLflow Docker 服务器中记录实验,章节名称为 追踪模型、参数和指标。提醒一下,成功在 VS Code 中运行这个 notebook,您需要按照本章 GitHub 仓库中的 README.md 文件描述的步骤设置虚拟环境 dl_model。它包括以下三个步骤:

conda create -n dl_model python==3.8.10
conda activate dl_model
pip install -r requirements.txt

如果您从头到尾逐个运行此 notebook 单元格,您的实验页面将如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.5.jpg

图 4.5 – 在 VS Code 中交互式运行 notebook 后记录的实验页面

您可能会立即注意到前面的截图中的问题 —— fine_tuning.py 文件。这是因为 VS Code notebook 并未与 MLflow 跟踪服务器原生集成以进行源文件跟踪;它只能显示 VS Code 用于执行 notebook 的 ipykernelpypi.org/project/ipykernel/)(github.com/microsoft/vscode-jupyter)。不幸的是,这是一个限制,在撰写本文时,无法通过交互式运行 VS Code notebook 来解决实验代码跟踪问题。托管在 Databricks Web UI 中运行的 Databricks notebook 则没有此类问题,因为它们与 MLflow 跟踪服务器有原生集成,该服务器与 Databricks Web 门户捆绑在一起。

然而,由于 VS Code notebook 只是 Python 代码,我们可以在命令行中以 非交互式 方式运行 notebook,如下所示:

python fine_tuning.py

这将在 MLflow 实验页面上无任何问题地记录实际源代码的文件名和 Git 提交哈希,如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.6.jpg

图 4.6 – 在命令行运行 VS Code 笔记本后的已记录实验页面

上面的截图显示了正确的源文件名(fine_tuning.py)和正确的 git 提交哈希(661ffeda5ae53cff3623f2fcc8227d822e877e2d)。这个解决方法不需要我们修改笔记本的代码,并且如果我们已经完成了初步的交互式笔记本调试,并且希望获得完整的笔记本运行结果,同时在 MLflow 跟踪服务器中进行正确的代码版本跟踪,这个方法会非常有用。请注意,所有其他参数、指标和模型都会被正确跟踪,无论我们是否以交互式方式运行笔记本。

管道跟踪

在讨论完笔记本代码跟踪(版本和文件名)之后,让我们转到管道跟踪的话题。然而,在讨论管道跟踪之前,我们首先需要了解管道在 ML/DL 生命周期中的定义。从概念上讲,管道是一个多步骤的数据处理和任务工作流。然而,数据/任务工作流的实现可能有很大的不同。管道可以在一些机器学习包中被定义为一流的 Python API。最著名的两个管道 API 如下:

然而,当我们构建一个深度学习(DL)模型管道时,我们需要在管道的不同步骤中使用多个不同的 Python 包,因此,使用一个通用的单一管道 API 通常无法满足需求。此外,前面提到的管道 API 都不原生支持当前流行的深度学习包,如HuggingfacePyTorch-Lightning,这些需要额外的集成工作。尽管存在一些开源的深度学习管道 API,如Neuraxlegithub.com/Neuraxio/Neuraxle),它尝试提供类似 sklearn 的管道接口和框架,但并未得到广泛使用。此外,使用这些基于 API 的管道意味着当你需要向管道中添加更多步骤时,可能会被限制,从而降低了你在新需求出现时扩展或发展深度学习管道的灵活性。

在本书中,我们将采用不同的方法来定义并构建一个基于 MLflow 的fine_tuning.py的深度学习管道,并将其转变为一个多步骤管道。这个管道可以被可视化为一个三步流程图,如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.7.jpg

](https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.7.jpg)

图 4.7 – 一个三步的深度学习管道

这个三步流程如下:

  1. 将数据下载到本地执行环境

  2. 微调模型

  3. 注册模型

这些模块化步骤可能对我们当前的示例来说有些过于复杂,但当涉及更多复杂性或每个步骤需要更改时,具有独立功能步骤的优势就显现出来。如果我们定义了需要在步骤之间传递的参数,每个步骤都可以独立修改,而不会影响其他步骤。每个步骤都是一个独立的 Python 文件,可以通过一组输入参数独立执行。将会有一个主管道 Python 文件,可以运行整个管道或管道的一个子部分。在MLproject文件中,这是一个没有文件扩展名的标准 YAML 文件,我们可以定义四个入口点(maindownload_datafine_tuning_modelregister_model),它们所需的输入参数、参数类型和默认值,以及执行每个入口点的命令行。在我们的示例中,这些入口点将通过 Python 命令行执行命令提供。然而,如果某些步骤需要特定的执行方式,你也可以调用其他执行方式,比如批处理脚本。例如,本章中的MLproject文件的以下行(github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow/blob/main/chapter04/MLproject)描述了项目的名称、conda环境定义文件的文件名以及主入口点:

name: dl_model_chapter04
conda_env: conda.yaml
entry_points:
  main:
    parameters:
      pipeline_steps:
        description: Comma-separated list of dl pipeline steps to execute 
        type: str
        default: all
    command: "python main.py --steps {pipeline_steps}"

这里,项目名称是 dl_model_chapter04conda_env 指的是本地 conda 环境的 YAML 定义文件 conda.yaml,该文件与 MLproject 文件位于同一目录。entry_points 部分列出了第一个入口点,名为 main。在 parameters 部分,有一个名为 pipeline_steps 的参数,允许用户定义一个逗号分隔的 DL 流水线步骤列表以供执行。该参数的类型是 str,默认值是 all,意味着所有流水线步骤都会执行。最后,command 部分列出了如何在命令行中执行此步骤。

MLproject 文件的其余部分通过遵循与主入口点相同的语法规则来定义另外三个流水线步骤入口点。例如,以下几行在同一个 MLproject 文件中定义了 download_data 的入口点:

  download_data:
    parameters:
      download_url:
        description: a url to download the data for fine tuning a text sentiment classifier
        type: str
        default: https://pl-flash-data.s3.amazonaws.com/imdb.zip
      local_folder:
        description: a local folder to store the downloaded data
        type: str
        default: ./data
      pipeline_run_name:
        description: an mlflow run name
        type: str
        default: chapter04
    command:
      "python pipeline/download_data.py --download_url {download_url} --local_folder {local_folder} \
      --pipeline_run_name {pipeline_run_name}"

download_data 部分,类似于主入口点,也定义了参数列表、类型和默认值,以及执行此步骤的命令行。我们可以像在本书的 GitHub 仓库中查看的 MLproject 文件一样,按相同的方式定义其余步骤。更多详细信息,请查看该 MLproject 文件的完整内容。

在定义了 MLproject 文件后,很明显我们已经以声明的方式定义了一个多步骤的流水线。这就像是一个流水线的规范,说明了每个步骤的名称、期望的输入参数以及如何执行它们。现在,下一步是实现 Python 函数来执行流水线的每个步骤。所以,让我们看看主入口点的 Python 函数的核心实现,它叫做 main.py。以下代码行(不是 main.py 中的整个 Python 代码)展示了只用一个流水线步骤(download_data)来实现整个流水线的核心组件:

@click.command()
@click.option("--steps", default="all", type=str)
def run_pipeline(steps):
    with mlflow.start_run(run_name='pipeline', nested=True) as active_run:
        download_run = mlflow.run(".", "download_data", parameters={})
if __name__ == "__main__":
    run_pipeline()

此主函数代码片段包含一个run_pipeline函数,在命令行中执行main.py文件时将运行该函数。有一个称为steps的参数,在提供时将传递给该函数。在此示例中,我们使用了click Python 包 (click.palletsprojects.com/en/8.0.x/) 来解析命令行参数。run_pipeline函数通过调用mlflow.start_run启动一个 MLflow 实验运行,并传递两个参数(run_namenested)。我们之前使用过run_name,它是此运行的描述性短语。然而,nested参数是新的,这意味着这是一个父实验运行。这个父实验运行包含一些将在 MLflow 中层次跟踪的子实验运行。每个父运行可以包含一个或多个子运行。在示例代码中,这包含管道运行的一个步骤,称为download_data,通过调用mlflow.run来调用。这是调用 MLproject 入口点的关键 MLflow 函数。一旦调用了download_data并且运行已完成,父运行也将完成,从而结束管道的运行。

执行 MLproject 入口点的两种方式

执行 MLproject 入口点有两种方式。首先,您可以使用 MLflow 的 Python API,称为mlflow.run (www.mlflow.org/docs/latest/python_api/mlflow.projects.html#mlflow.projects.run)。另外,您可以使用 MLflow 的命令行界面工具,称为mlflow run,可以在命令行 shell 环境中直接调用以执行任何入口点 (www.mlflow.org/docs/latest/cli.html#mlflow-run)。

现在,让我们学习如何通用地实现管道中的每个步骤。对于每个管道步骤,我们将 Python 文件放在一个pipeline文件夹中。在本例中,我们有三个文件:download_data.pyfine_tuning_model.pyregister_model.py。因此,成功构建支持 MLflow 的管道项目所需的相关文件如下:

MLproject
conda.yaml
main.py
pipeline/download_data.py
pipeline/fine_tuning_model.py
pipeline/register_model.py

对于每个管道步骤的实现,我们可以使用以下 Python 函数模板。一个占位符部分用于实现实际的管道步骤逻辑:

import click
import mlflow
@click.command()
@click.option("input")
def task(input):
    with mlflow.start_run() as mlrun:
        # Implement pipeline step logic here 
        mlflow.log_parameter('parameter', parameter)
        mlflow.set_tag('pipeline_step', __file__)
        mlflow.log_artifacts(artifacts, artifact_path="data")
if __name__ == '__main__':
    task()

此模板允许我们标准化实施管道步骤任务的方式。主要思想是,对于每个管道步骤任务,需要从mlflow.start_run开始启动一个 MLflow 实验运行。一旦我们在函数中实现了具体的执行逻辑,我们需要使用mlflow.log_parameter记录一些参数,或者在工件存储中使用mlflow.log_artifacts记录一些工件,这些参数和工件可以传递并由管道的下一步使用。这就是所谓的mlflow.set_tag

例如,在download_data.py步骤中,核心实现如下:

import click
import mlflow
from flash.core.data.utils import download_data
@click.command()
@click.option("--download_url")
@click.option("--local_folder")
@click.option("--pipeline_run_name")
def task(download_url, local_folder, pipeline_run_name):
    with mlflow.start_run(run_name=pipeline_run_name) as mlrun:
        download_data(download_url, local_folder)
        mlflow.log_param("download_url", download_url)
        mlflow.log_param("local_folder", local_folder)
        mlflow.set_tag('pipeline_step', __file__)
        mlflow.log_artifacts(local_folder, artifact_path="data")
if __name__ == '__main__':
    task()

在这个download_data.py实现中,任务是将模型构建所需的数据从远程 URL 下载到本地文件夹(download_data(download_url, local_folder))。完成这一步后,我们将记录一些参数,比如download_urllocal_folder。我们还可以使用mlflow.log_artifacts将新下载的数据记录到 MLflow 工件存储中。对于这个例子来说,可能看起来不太必要,因为我们只是在本地开发环境中执行下一步。然而,在一个更现实的分布式执行环境中,每个步骤可能会在不同的执行环境中运行,这种做法是非常有用的,因为我们只需要将工件 URL 路径传递给管道的下一步使用;我们不需要知道上一步是如何以及在哪里执行的。在这个例子中,当mlflow.log_artifacts(local_folder, artifact_path="data")语句被调用时,下载的数据文件夹会被上传到 MLflow 工件存储。然而,在本章中,我们不会使用这个工件路径来执行下游管道步骤。稍后在本书中,我们将探讨如何使用这种工件存储将工件传递到管道的下一步。在这里,我们将使用日志记录的参数将下载的数据路径传递给管道的下一步(mlflow.log_param("local_folder", local_folder))。所以,让我们看看如何通过扩展main.py,使其包含下一步,即fine_tuning_model入口点,如下所示:

        with mlflow.start_run(run_name='pipeline', nested=True) as active_run:
            download_run = mlflow.run(".", "download_data", parameters={})
            download_run = mlflow.tracking.MlflowClient().get_run(download_run.run_id)
            file_path_uri = download_run.data.params['local_folder']
            fine_tuning_run = mlflow.run(".", "fine_tuning_model", parameters={"data_path": file_path_uri})

我们使用mlflow.tracking.MlflowClient().get_run来获取download_run的 MLflow 运行对象,然后使用download_run.data.params来获取file_path_uri(在这个例子中,它只是一个本地文件夹路径)。然后将其作为键值参数(parameters={"data_path": file_path_uri})传递给下一个mlflow.run,即fine_tuning_run。通过这种方式,fine_tuning_run管道步骤可以使用这个参数来作为数据源路径的前缀。这是一个非常简化的场景,用来说明我们如何将数据从一个步骤传递到下一个步骤。使用由 MLflow 提供的mlflow.tracking.MlflowClient()API(www.mlflow.org/docs/latest/python_api/mlflow.tracking.html),可以轻松访问运行信息(参数、指标和工件)。

我们还可以通过添加register_model步骤,扩展main.py文件中的管道第三步。这次,我们需要日志记录的模型 URI 来注册已训练的模型,这取决于fine_tuning_model步骤的run_id。因此,在fine_tuning_model步骤中,我们需要获取fine_tuning_model运行的run_id属性,然后将其作为输入参数传递给register_model运行,如下所示:

fine_tuning_run_id = fine_tuning_run.run_id
register_model_run = mlflow.run(".", "register_model", parameters={"mlflow_run_id": fine_tuning_run_id})

现在,register_model 步骤可以使用 fine_tuning_run_id 来定位已注册的模型。register_model 步骤的核心实现如下:

    with mlflow.start_run() as mlrun:
        logged_model = f'runs:/{mlflow_run_id}/model'
        mlflow.register_model(logged_model, registered_model_name)

这将在由 logged_model 变量定义的 URI 中注册一个微调模型到 MLflow 模型注册表。

如果您已经按照这些步骤操作,那么您应该已经有了一个可以由 MLflow 端到端跟踪的工作管道。提醒一下,前提条件是您已经设置好本地完整的 MLflow 服务器,如在 第三章*,跟踪模型、参数和指标* 中所示。您应该已经在上一节中设置了虚拟环境 dl_model。要测试这个管道,请访问本章的 GitHub 仓库 github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow/tree/main/chapter04 并运行以下命令:

python main.py

这将运行整个三步管道,并将管道的 run_id(即父运行)以及每个步骤的运行作为子运行记录在 MLflow 跟踪服务器中。当运行完成时,控制台屏幕的最后几行输出会显示如下内容(运行管道时,您将看到许多屏幕输出):

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.8.jpg

图 4.8 – 运行带有 MLflow run_ids 的管道的控制台输出

这显示了管道的 run_id,它是 f8f21fdf8fff4fd6a400eeb403b776c8;最后一步是 fine_tuning_modelrun_id 属性,它是 5ba38e059695485396e709b809e9bb8d。如果我们通过点击 http://localhost 进入 MLflow 跟踪服务器的 UI 页面,我们应该能看到如下在 dl_model_chapter04 实验文件夹中的嵌套实验运行:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.9.jpg

图 4.9 – 一个包含三步嵌套子步骤运行的管道在 MLflow 跟踪服务器中运行

上面的截图展示了管道运行,以及源 main.py 文件和管道三步的嵌套运行。每个步骤都有一个在 MLproject 中定义的对应入口点名称,并带有 register_model 运行页面的 GitHub 提交哈希代码版本,您将看到以下信息:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.10.jpg

图 4.10 – 在 MLflow 跟踪服务器上,入口点 register_model 的运行页面

上面的截图不仅展示了我们已经看到的一些熟悉信息,还展示了一些新信息,例如 file:///、GitHub 哈希码版本、入口点 (-e register_model)、执行环境(本地开发环境 -b local)以及 register_model 函数的预期参数(-P)。我们将在本书后面学习如何使用 MLflow 的 MLproject 运行命令来远程执行任务。这里,我们只需要理解源代码是通过入口点(register_model)引用的,而不是通过文件名本身,因为该引用在 MLproject 文件中已声明为入口点。

如果你在 MLflow 跟踪服务器中看到了图 4.9图 4.10中显示的输出,那么是时候庆祝了——你已经成功地使用 MLflow 执行了一个多步骤的深度学习管道!

总结一下,要在 MLflow 中跟踪一个多步骤的深度学习管道,我们可以使用 MLproject 来定义每个管道步骤的入口点以及主管道的入口点。在主管道函数中,我们实现方法以便可以在管道步骤之间传递数据。然后,每个管道步骤使用已经共享的数据以及其他输入参数来执行特定任务。主管道级别的函数以及管道的每个步骤都通过 MLflow 跟踪服务器进行跟踪,该服务器生成一个父级 run_id 来跟踪主管道的运行,并生成多个嵌套的 MLflow 运行来跟踪每个管道步骤。我们为每个管道步骤介绍了一个模板,以标准化地实现此任务。我们还通过 MLflow 的 run 参数和工件存储探索了强大的管道链式操作,学习了如何在管道步骤之间传递数据。

现在你已经知道如何跟踪笔记本和管道,接下来我们来学习如何跟踪 Python 库。

跟踪本地私有构建的 Python 库

现在,让我们将注意力转向跟踪本地私有构建的 Python 库。对于公开发布的 Python 库,我们可以在 requirements 文件或 conda.yaml 文件中明确指定其发布的版本,这些版本通常发布在 PyPI 上。例如,本章的 conda.yaml 文件 (github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow/blob/main/chapter04/conda.yaml) 定义了 Python 版本,并提供了对 requirements 文件的引用,如下所示:

name: dl_model 
channels:
  - conda-forge
dependencies:
  - python=3.8.10
  - pip
  - pip:
    - -r requirements.txt

Python 版本被定义为 3.8.10,并且被强制执行。该 conda.yaml 文件还引用了一个 requirements.txt 文件,其中包含以下版本化的 Python 包,该文件位于与 conda.yaml 文件相同的目录中:

ipykernel==6.4.1
lightning-flash[all]==0.5.0
mlflow==1.20.2
transformers==4.9.2
boto3==1.19.7
pytorch-lightning==1.4.9
datasets==1.9.0
click==8.0.3

如我们所见,所有这些包都在显式地使用它们发布的 PyPI(pypi.org/)版本号进行追踪。当你运行 MLflow 的MLproject时,MLflow 将使用conda.yaml文件和引用的requirements.txt文件动态创建 conda 虚拟环境。这确保了执行环境的可重现性,并且所有的深度学习模型管道都能够成功运行。你可能注意到,第一次运行上一节的 MLflow 管道项目时,已经为你创建了这样的虚拟环境。你可以通过运行以下命令再次执行此操作:

conda env list

这将生成当前机器中 conda 虚拟环境的列表。你应该能够找到一个以mlflow-为前缀,后跟一串字母和数字的虚拟环境,如下所示:

mlflow-95353930ddb7b60101df80a5d64ef8bf6204a808

这是由 MLflow 动态创建的虚拟环境,它遵循在conda.yamlrequirements.txt中指定的依赖项。随后,当你记录经过微调的模型时,conda.yamlrequirements.txt将自动记录到 MLflow 工件存储中,如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.11.jpg

图 4.11 – Python 包正在被记录并追踪到 MLflow 工件存储中

如我们所见,conda.yaml文件已自动扩展,包含了requirements.txt的内容,以及 conda 决定要包含的其他依赖项。

对于私有构建的 Python 包(即未公开发布到 PyPI 供公众使用和引用的 Python 包),推荐的方式是使用git+ssh来包含该 Python 包。假设你有一个名为cool-dl-utils的私有构建项目,你所在的组织叫做cool_org,并且你的项目仓库已在 GitHub 上设置。如果你想在 requirements 文件中包含该项目的 Python 包,你需要确保将你的公钥添加到 GitHub 设置中。如果你想了解如何生成公钥并将其加载到 GitHub,请参考 GitHub 的指南:docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account。在requirements.txt文件中,你可以添加如下内容,这将引用一个特定的 GitHub 哈希(81218891bbf5a447103884a368a75ffe65b17a44)和从该私有仓库构建的 Python .egg 包(如果愿意,也可以引用.whl包):

cool-dl-utils @ git+ssh://git@github.com/cool_org/cool-dl-utils.git@81218891bbf5a447103884a368a75ffe65b17a44#egg=cool-dl-utils

如果你在自己构建的包中有一个数字版本,你也可以直接在requirements.txt文件中引用该版本号,如下所示:

git+ssh://git@github.com/cool_org/cool-dl-utils.git@2.11.4

这里cool-dl-utils的发布版本是2.11.4。这使得 MLflow 能够将这个私有构建的包拉入虚拟环境中执行MLproject。在这一章中,我们不需要引用任何私有构建的 Python 包,但值得注意的是,MLflow 可以利用git+ssh方式来实现这一点。

现在,让我们学习如何跟踪数据版本。

在 Delta Lake 中跟踪数据版本

在这一部分,我们将学习如何在 MLflow 中跟踪数据。历史上,数据管理和版本控制通常被视为与机器学习和数据科学不同的领域。然而,数据驱动的 AI 的兴起在深度学习中扮演着越来越重要的角色。因此,了解数据如何以及用于何种目的来改进深度学习模型是至关重要的。在 2021 年夏季,由 Andrew Ng 组织的第一届数据驱动 AI 竞赛中,获胜的要求并不是改变和调优模型,而是改进一个固定模型的数据集(https-deeplearning-ai.github.io/data-centric-comp/)。这是竞赛网页上的一句话:

“数据驱动的 AI 竞赛颠覆了传统格式,要求你在给定固定模型的情况下改进数据集。我们将提供一个数据集,供你通过应用数据驱动的技术来改进,比如修正错误标签、添加代表边缘案例的示例、应用数据增强等。”

这一范式的转变突显了数据在深度学习中的重要性,尤其是在监督学习中,标签数据尤为重要。一个潜在的假设是,不同的数据将产生不同的模型指标,即使使用相同的模型架构和参数。这要求我们仔细跟踪数据版本控制过程,以便知道哪个版本的数据正在用来生成获胜的模型。

在 ML/DL 生命周期中,出现了几个用于跟踪数据版本的框架。DVCdvc.org)是这个领域的早期先驱之一。它使用类似 GitHub 的命令来拉取/推送数据,就像操作代码一样。它允许将数据远程存储在 S3 或 Google Drive 等多个流行存储中。然而,存储在远程存储中的数据会被哈希化,并且无法被人类阅读。这会产生“锁定”问题,因为访问数据的唯一方法是通过 DVC 工具和配置。此外,追踪数据及其架构如何变化也很困难。尽管可以将 MLflow 与 DVC 集成,但其可用性和灵活性不如我们所期望的那样好。因此,在本书中我们不会深入探讨这种方法。如果你对此感兴趣,我们建议你参考本章末尾的使用 DVC 和 AWS 进行 ML 项目中的数据和模型版本管理,以获得有关 DVC 的更多细节。

最近开源且基于开放格式的Delta Lakedelta.io/)是一个集成数据管理和版本控制的实际解决方案,特别是因为 MLflow 可以直接支持这种集成。这也是基础数据管理层,称为Lakehousedatabricks.com/blog/2020/01/30/what-is-a-data-lakehouse.html),它将数据仓库和流数据统一到一个数据基础层。它支持模式变更跟踪和数据版本控制,非常适合深度学习/机器学习数据使用场景。Delta 表是基于一种名为Parquetparquet.apache.org/)的开放标准文件格式,它在大规模数据存储中得到了广泛支持。

Databricks 中的 Delta 表

请注意,本节假设你可以访问 Databricks 服务,允许你在Databricks 文件系统DBFS)中实验 Delta Lake 格式。你可以通过访问 Databricks 门户:community.cloud.databricks.com/login.html来获取社区版的试用账户。

请注意,本节需要使用PySpark通过读取/写入数据与存储(如 S3)进行数据操作。Delta Lake 具有一个称为**时间旅行(Time Travel)**的功能,可以自动对数据进行版本控制。通过传递像时间戳或版本号这样的参数,你可以读取该特定版本或时间戳的任何历史数据。这使得重现和追踪实验变得更加容易,因为数据的唯一时间元数据就是数据的版本号或时间戳。查询 Delta 表有两种方式:

  • timestampAsOf:此选项让你能够读取 Delta 表,同时读取具有特定时间戳的版本。以下代码展示了如何使用 timestampAsOf 来读取数据:

    df = spark.read \
      .format("delta") \
      .option("timestampAsOf", "2020-11-01") \
      .load("/path/to/my/table")
    
  • versionAsOf:此选项定义了 Delta 表的版本号。你也可以选择读取具有特定版本的版本,从版本 0 开始。以下 PySpark 代码使用 versionAsOf 选项读取版本 52 的数据:

    df = spark.read \
      .format("delta") \
      .option("versionAsOf", "52") \
      .load("/path/to/my/table")
    

拥有这种带有时间戳或版本的访问权限是使用 Delta 表追踪任何文件版本的关键优势。所以,让我们在 MLflow 中看一个具体的例子,这样我们就可以追踪我们一直在使用的 IMDb 数据集。

使用 MLflow 追踪数据的示例

对于我们一直用来微调情感分类模型的 IMDb 数据集,我们将这些 CSV 文件上传到 Databricks 的数据存储中,或者上传到你可以从 Databricks 门户访问的任何 S3 存储桶中。完成后,按照以下步骤创建一个支持版本化和时间戳数据访问的 Delta 表:

  1. 将以下 CSV 文件读取到数据框中(假设你已将 train.csv 文件上传到 Databricks 的 FileStore/imdb/ 文件夹):

    imdb_train_df = spark.read.option('header', True).csv('dbfs:/FileStore/imdb/train.csv')
    
  2. imdb_train_df 数据框写入 DBFS 作为 Delta 表,如下所示:

    imdb_train_df.write.format('delta').option("mergeSchema", "true").mode("overwrite").save('/imdb/training.delta')
    
  3. 使用以下命令将 training.delta 文件重新读入内存:

    imdb_train_delta = spark.read.format('delta').load('/imdb/training.delta')
    
  4. 现在,通过 Databricks UI 查看 Delta 表的历史记录。在从存储读取 Delta 表到内存后,点击 History 标签:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.12.jpg

](https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_Figure_4.12.jpg)

图 4.12 – train_delta 表的历史记录,包含版本和时间戳列

上述截图显示版本为 0,时间戳为 2021-11-22。这是我们在传递版本号或时间戳给 Spark DataFrame 阅读器时,可以用来访问版本化数据的值。

  1. 使用以下命令读取版本化的imdb/train_delta文件:

    train_data_version = spark.read.format("delta").option("versionAsOf", "0").load('/imdb/train.delta')  
    

这将读取 train.delta 文件的版本 0。如果我们有其他版本的此文件,可以传递不同的版本号。

  1. 使用以下命令读取带时间戳的 imdb/train_delta 文件:

    train_data_timestamped = spark.read.format("delta").option("timestampAsOf", "2021-11-22T03:39:22").load('/imdb/train.delta')  
    

这将读取带有时间戳的数据。在写作时,这是我们拥有的唯一时间戳,没问题。如果我们有更多带时间戳的数据,可以传递不同的版本。

  1. 现在,如果我们需要在 MLflow 跟踪实验运行中记录这个数据版本,我们只需使用 mlflow.log_parameter() 记录数据的路径、版本号和/或时间戳。这将作为实验参数键值对的一部分进行记录:

    mlflow.log_parameter('file_path', '/imdb/train.delta')
    mlflow.log_parameter('file_version', '0')
    mlflow.log_parameter('file_timestamp', '2021-11-22T03:39:22') 
    

使用 Delta 表的唯一要求是数据需要存储在支持 Delta 表的存储形式中,例如 Databricks 支持的 Lakehouse。这对企业级 ML/DL 场景具有重要价值,因为我们可以与代码和模型版本一起跟踪数据版本。

总结一下,Delta Lake 提供了一种简单而强大的方式来进行数据版本管理。MLflow 可以轻松地将这些版本号和时间戳作为实验参数列表的一部分进行记录,以便一致地跟踪数据,以及所有其他参数、指标、工件、代码和模型。

总结

在本章中,我们深入探讨了如何在 MLflow 实验运行中跟踪代码和数据版本。我们首先回顾了不同类型的笔记本:Jupyter 笔记本、Databricks 笔记本和 VS Code 笔记本。我们对比了它们,并推荐使用 VS Code 来编写笔记本,因为它具有 IDE 支持,以及 Python 风格、自动补全和许多其他丰富功能。

然后,在回顾现有 ML 管道 API 框架的局限性之后,我们讨论了如何使用 MLflow 的run_id创建一个多步骤的 DL 管道,并为每个管道步骤使用子run_id。通过将参数或工件存储位置传递到下一个步骤,可以使用mlflow.run()mlflow.tracking.MlflowClient()来进行管道链式操作和跟踪。我们成功地使用 MLflow 的嵌套运行跟踪功能运行了一个端到端的三步管道。这也为我们未来章节中分布式方式运行不同步骤的 MLproject 使用扩展提供了可能。

我们还学习了如何使用git+ssh方法跟踪私有构建的 Python 包。然后,我们使用 Delta Lake 方法获得了版本化和时间戳的数据访问权限。这使得数据可以通过版本号或时间戳两种方式进行跟踪。然后,MLflow 可以在实验运行期间将这些版本号或时间戳作为参数记录。由于我们正进入数据驱动的 AI 时代,能够跟踪数据版本对于可重复性和时间旅行至关重要。

至此,我们已经完成了使用 MLflow 全面跟踪代码、数据和模型的学习。在下一章,我们将学习如何以分布式方式扩展我们的 DL 实验。

进一步阅读

关于本章所涵盖主题的更多信息,请查看以下资源:

  1. 在 Databricks 中使用 MLflow 笔记本实验跟踪:docs.databricks.com/applications/mlflow/tracking.html#create-notebook-experiment

  2. 构建多步骤工作流www.mlflow.org/docs/latest/projects.html#building-multistep-workflows

  3. 使用 MLflow 项目的端到端机器学习管道dzlab.github.io/ml/2020/08/09/mlflow-pipelines/

  4. 安装私有构建的 Python 包:medium.com/@ffreitasalves/pip-installing-a-package-from-a-private-repository-b57b19436f3e

  5. 在 ML 项目中使用 DVC 和 AWS 进行数据和模型版本控制medium.com/analytics-vidhya/versioning-data-and-models-in-ml-projects-using-dvc-and-aws-s3-286e664a7209

  6. 为大规模数据湖引入 Delta 时间旅行databricks.com/blog/2019/02/04/introducing-delta-time-travel-for-large-scale-data-lakes.html

  7. 我们如何赢得首届数据中心人工智能竞赛:Synaptic-AnNwww.deeplearning.ai/data-centric-ai-competition-synaptic-ann/

  8. 重现任何事物:机器学习遇上数据湖屋databricks.com/blog/2021/04/26/reproduce-anything-machine-learning-meets-data-lakehouse.html

  9. DATABRICKS COMMUNITY EDITION:初学者指南www.topcoder.com/thrive/articles/databricks-community-edition-a-beginners-guide

第三部分 – 在大规模下运行深度学习管道

在本节中,我们将学习如何在不同的执行环境中运行深度学习DL)管道,并进行超参数调优,或超参数优化HPO),以实现大规模处理。我们将从概述执行 DL 管道的场景和要求开始。接着,我们将学习如何使用 MLflow 的命令行接口CLI)在分布式环境中运行四种不同的执行场景。之后,我们将通过比较Ray TuneOptunaHyperOpt,学习如何选择最佳的 HPO 框架来调优 DL 管道的超参数。最后,我们将重点讨论如何利用最新的 HPO 框架,如 Ray Tune 和 MLflow,实施并运行大规模的 DL 超参数优化。

本节包括以下章节:

  • 第五章在不同环境中运行深度学习管道

  • 第六章大规模运行超参数调优

第五章:第五章:在不同环境中运行 DL 管道

在不同执行环境中运行 深度学习 (DL) 管道的灵活性至关重要,例如在本地、远程、企业内部或云中运行。这是因为在 DL 开发的不同阶段,可能会有不同的约束或偏好,目的是提高开发速度或确保安全合规性。例如,进行小规模模型实验时,最好在本地或笔记本环境中进行,而对于完整的超参数调优,我们需要在云托管的 GPU 集群上运行模型,以实现快速的迭代时间。考虑到硬件和软件配置中的多样化执行环境,过去在单一框架内实现这种灵活性是一个挑战。MLflow 提供了一个易于使用的框架,能够在不同环境中按规模运行 DL 管道。本章将教你如何实现这一点。

在本章中,我们将首先了解不同的 DL 管道执行场景及其执行环境。我们还将学习如何在不同的执行环境中运行 DL 管道的不同步骤。具体而言,我们将涵盖以下主题:

  • 不同执行场景和环境的概述

  • 本地运行本地代码

  • 在 GitHub 中远程运行本地代码

  • 在云中远程运行本地代码

  • 在云中远程运行,并使用 GitHub 中的远程代码

本章结束时,你将能够熟练地设置 DL 管道,以便在不同执行环境中本地或远程运行。

技术要求

完成本章学习所需的技术要求如下:

不同执行场景和环境的概述

在前几章中,我们主要集中在学习如何使用 MLflow 的跟踪功能来跟踪深度学习管道。我们的执行环境大多数是在本地环境中,比如本地笔记本电脑或桌面环境。然而,正如我们所知,深度学习完整生命周期由多个阶段组成,其中我们可能需要在不同的执行环境中完全、部分地或单独运行深度学习管道。以下是两个典型的例子:

  • 在访问用于模型训练的数据时,通常需要数据处于符合企业安全性和隐私合规要求的环境中,在这种环境下,计算和存储不能超出合规边界。

  • 在训练深度学习(DL)模型时,通常希望使用远程 GPU 集群来最大化模型训练的效率,因为本地笔记本电脑通常不具备所需的硬件能力。

这两种情况都需要仔细定义的执行环境,这种环境可能在深度学习生命周期的一个或多个阶段中有所需求。需要注意的是,这不仅仅是为了在从开发阶段过渡到生产环境时的灵活性要求,因生产环境中的执行硬件和软件配置通常会有所不同。这也是一个要求,即能够在开发阶段或不同的生产环境中切换运行环境,而不需要对深度学习管道进行重大修改。

在这里,我们根据深度学习管道源代码和目标执行环境的不同组合,将不同的场景和执行环境分类为以下四种场景,如下表所示:

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_05_01.jpg

](https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_05_01.jpg)

图 5.1 – 深度学习管道源代码和目标执行环境的四种不同场景

图 5.1 描述了在开发或生产环境中,我们可能遇到的使用本地或远程代码在不同执行环境中运行的情况。我们将逐一检查这些情况,如下所示:

  • 本地源代码在本地目标环境中运行:这通常发生在开发阶段,在本地环境中适度的计算能力足以支持快速原型开发或对现有管道的小变更进行测试运行。这也是我们在前几章中使用的场景,尤其是在学习如何跟踪管道时,使用了 MLflow 的实验。

  • 本地源代码在远程目标环境中运行:这通常发生在开发阶段或重新训练现有深度学习模型时,在这种情况下,需要使用 GPU 或其他类型的硬件加速器,如张量处理单元TPU)或现场可编程门阵列FPGA),来执行计算密集型和数据密集型的模型训练或调试,以便在合并 GitHub 仓库(首先使用本地代码更改)之前进行调试。

  • 远程源代码在本地目标环境中运行:通常发生在我们代码没有更改,但数据发生变化时,无论是在开发阶段还是生产阶段。例如,在 DL 开发阶段,我们可能会通过某些数据增强技术(例如,使用AugLy来增强现有训练数据:github.com/facebookresearch/AugLy)或新的注释训练数据来改变数据。在生产部署步骤中,我们经常需要运行回归测试,以评估待部署的 DL 管道在保留的回归测试数据集上的表现,这样我们就不会在模型性能精度指标未达到标准时部署一个性能下降的模型。在这种情况下,保留的测试数据集通常不大,因此执行可以在本地部署服务器上完成,而不需要启动到 Databricks 服务器的远程集群。

  • 远程源代码在远程目标环境中运行:这通常发生在开发阶段或生产阶段,当我们希望使用 GitHub 上的固定版本的 DL 管道代码,在远程 GPU 集群上进行模型训练、超参数调整或再训练时。此类大规模执行可能非常耗时,而远程 GPU 集群在这种情况下非常有用。

鉴于这四种不同的场景,最好能有一个框架,在这些条件下以最小的配置更改来运行相同的 DL 管道。在 MLflow 出现之前,支持这些场景需要大量的工程和手动工作。MLflow 提供了一个 MLproject 框架,通过以下三种可配置机制来支持这四种场景:

  1. 入口点:我们可以定义一个或多个入口点来执行深度学习(DL)管道的不同步骤。例如,以下是定义一个主入口点的示例:

    entry_points:
      main:
        parameters:
          pipeline_steps: { type: str, default: all }
        command: "python main.py –pipeline_steps {pipeline_steps}"
    

入口点的名称是main,默认情况下,当执行 MLflow 运行时如果没有指定 MLproject 的入口点,则会使用该入口点。在这个main入口点下,有一组参数列表。我们可以使用简短的语法定义参数的类型和默认值,如下所示:

parameter_name: {type: data_type, default: value}

我们还可以使用长语法,如下所示:

parameter_name:
  type: data_type
  default: value

在这里,我们只定义了一个参数,叫做pipeline_steps,使用简短的语法格式,类型为str,默认值为all

  1. yaml 配置文件或 Docker 镜像用来定义 MLproject 入口点可以使用的软件和库依赖。请注意,一个 MLproject 只能使用 conda yaml 文件或 Docker 镜像中的一个,而不能同时使用两者。根据深度学习管道的依赖,有时使用 conda .yaml 文件而非 Docker 镜像更为合适,因为它更加轻量,且更容易修改,无需额外的 Docker 镜像存储位置或在资源有限的环境中加载大型 Docker 镜像。然而,如果在运行时需要 Java 包(.jar),那么使用 Docker 镜像可能会有优势。如果没有这样的 JAR 依赖,那么更推荐使用 conda .yaml 文件来指定依赖。此外,MLflow 版本 1.22.0 以后,在 Databricks 上运行基于 Docker 的项目尚未得到 MLflow 命令行支持。如果确实有 Java 包依赖,可以通过 yaml 配置文件来定义执行环境依赖,本书中会有介绍。

  2. 硬件依赖:我们可以使用集群配置 JSON 文件来定义执行目标后端环境,无论是 GPU、CPU 还是其他类型的集群。当目标后端执行环境非本地时,才需要这个配置,无论是在 Databricks 服务器还是 KubernetesK8s)集群中。

之前,我们学习了如何使用 MLproject 创建一个多步骤的深度学习管道,在本地环境中运行,如 第四章《跟踪代码和数据版本控制》,用于跟踪目的。现在,我们将学习如何使用 MLproject 支持前面提到的不同运行场景。

在本地运行带有本地代码的项目

让我们从第一个运行场景开始,使用相同的 自然语言处理(NLP) 文本情感分类示例作为驱动案例。建议您从 GitHub 上获取以下版本的源代码,以便跟随步骤并学习: github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow/tree/26119e984e52dadd04b99e6f7e95f8dda8b59238/chapter05。请注意,这需要一个特定的 Git 哈希提交版本,如 URL 路径所示。这意味着我们要求您检查一个特定的提交版本,而不是主分支。

让我们从下载评论数据到本地存储的深度学习管道开始,作为第一次执行练习。检查完本章的代码后,您可以输入以下命令行来执行深度学习管道的第一步:

mlflow run . --experiment-name='dl_model_chapter05' -P pipeline_steps='download_data'

如果我们没有指定入口点,它默认是 main。在这种情况下,这是我们期望的行为,因为我们希望运行 main 入口点来启动父级深度学习管道。

表示当前本地目录。这告诉 MLflow 使用当前目录中的代码作为执行项目的源。如果此命令行运行成功,你应该能够在控制台中看到前两行输出,如下所示,同时也可以看到目标执行环境的位置:

2022/01/01 19:15:37 INFO mlflow.projects.utils: === Created directory /var/folders/51/whxjy4r92dx18788yp11ycyr0000gp/T/tmp3qj2kws2 for downloading remote URIs passed to arguments of type 'path' ===
2022/01/01 19:15:37 INFO mlflow.projects.backend.local: === Running command 'source /Users/yongliu/opt/miniconda3/bin/../etc/profile.d/conda.sh && conda activate mlflow-95353930ddb7b60101df80a5d64ef8bf6204a808 1>&2 && python main.py --pipeline_steps download_data' in run with ID 'f7133b916a004c508e227f00d534e136' ===

请注意,第二行输出显示了 mlflow.projects.backend.local,这意味着目标运行环境是本地的。你可能会好奇我们在初始命令行中在哪里定义了本地执行环境。事实证明,默认情况下,名为 --backend(或 -b)的参数的值是 local。因此,如果我们列出默认值,mlflow run 命令行将如下所示:

mlflow run . -e main -b local --experiment-name='dl_model_chapter05' -P pipeline_steps='download_data'

请注意,我们还需要在命令行中或通过名为 MLFLOW_EXPERIMENT_NAME 的环境变量指定 experiment-name,以定义此项目将运行的实验。或者,你可以指定一个 experiment-id 参数,或者一个名为 MLFLOW_EXPERIMENT_ID 的环境变量,以定义已经存在的实验整数 ID。你只需要定义环境的 ID 或名称之一,而不是两者。通常我们会定义一个人类可读的实验名称,然后在代码的其他部分查询该实验的 ID,以确保它们不会不同步。

运行 MLproject 的 MLflow 实验名称或 ID

要使用 CLI 或 mlflow.run Python API 运行一个 MLproject,如果我们没有通过环境变量或参数赋值指定 experiment-nameexperiment-id,它将默认使用 Default MLflow 实验。这并不是我们想要的,因为我们希望将实验组织成明确分开的实验。此外,一旦 MLproject 开始运行,任何子运行将无法切换到不同的实验名称或 ID。因此,最佳实践是始终在启动 MLflow 项目运行之前指定实验名称或 ID。

一旦你完成运行,你将看到如下的输出:

2022-01-01 19:15:48,249 <Run: data=<RunData: metrics={}, params={'download_url': 'https://pl-flash-data.s3.amazonaws.com/imdb.zip',
 'local_folder': './data',
 'mlflow run id': 'f9f74ebd80f246d58a5f7a3bfb3fc635',
 'pipeline_run_name': 'chapter05'}, tags={'mlflow.gitRepoURL': 'git@github.com:PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow.git',
 'mlflow.parentRunId': 'f7133b916a004c508e227f00d534e136',

请注意,这是一个嵌套的 MLflow 运行,因为我们首先启动一个 main 入口点来启动整个管道(这就是为什么有 mlflow.parentRunId),然后在这个管道下,我们运行一个或多个步骤。这里我们运行的步骤叫做 download_data,这是在 MLproject 中定义的另一个入口点,但它是通过 mlflow.run Python API 调用的,如下所示,在 main.py 文件中:

download_run = mlflow.run(".", "download_data", parameters={})

请注意,这还指定了要使用的代码源(local,因为我们指定了一个 ),并默认使用本地执行环境。这就是为什么你应该能够在控制台输出中看到以下内容的原因:

 'mlflow.project.backend': 'local',
 'mlflow.project.entryPoint': 'download_data',

你还应该能看到该入口点的运行参数的其他几个细节。命令行输出的最后两行应如下所示:

2022-01-01 19:15:48,269 finished mlflow pipeline run with a run_id = f7133b916a004c508e227f00d534e136
2022/01/01 19:15:48 INFO mlflow.projects: === Run (ID 'f7133b916a004c508e227f00d534e136') succeeded ===

如果你看到这个输出,你应该感到自豪,因为你已经成功运行了一个包含一个步骤的管道,并且已完成。

虽然我们之前也做过类似的事情,虽然没有了解其中的一些细节,但接下来的部分将让我们能够在本地环境中运行远程代码,你将看到 MLproject 的灵活性和功能越来越强大。

在本地运行 GitHub 上的远程代码

现在,让我们看看如何在本地执行环境中运行 GitHub 仓库中的远程代码。这让我们能够准确地运行一个特定版本,该版本已被提交到 GitHub 仓库中并使用提交哈希标识。我们继续使用之前的例子,在本章节中运行 DL 管道的单个 download_data 步骤。在命令行提示符中,运行以下命令:

mlflow run https://github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow#chapter05 -v 26119e984e52dadd04b99e6f7e95f8dda8b59238  --experiment-name='dl_model_chapter05' -P pipeline_steps='download_data'

注意这条命令行和前一节中的命令行之间的区别。我们不再用一个 来表示本地代码的副本,而是指向一个远程的 GitHub 仓库(github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow),并指定包含我们想要引用的 MLproject 文件的文件夹名称(chapter05)。# 符号表示相对于根文件夹的路径,这是根据 MLflow 的约定来定义的(详情请参考 MLflow 文档:www.mlflow.org/docs/latest/projects.html#running-projects)。然后我们通过指定 Git 提交哈希,使用 -v 参数来定义版本号。在这种情况下,它是我们在 GitHub 仓库中的这个版本:

github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow/tree/26119e984e52dadd04b99e6f7e95f8dda8b59238/chapter05

运行 GitHub 的主分支时,MLflow 项目的隐性 Bug

当我们在 MLflow 运行中省略 -v 参数时,MLflow 会假设我们想使用 GitHub 项目的默认 main 分支。然而,MLflow 的源代码中硬编码了对 GitHub 项目 main 分支的引用,作为 origin.refs.master,这要求 GitHub 项目中必须存在 master 分支。这在新的 GitHub 项目中不起作用,例如本书中的项目,因为默认分支已经不再叫 master,而是叫做 main,这是由于 GitHub 最近的更改所导致的(详见:github.com/github/renaming)。因此,在写这本书时,MLflow 版本 1.22.0 无法运行 GitHub 项目的默认 main 分支。我们需要在运行 GitHub 仓库中的 MLflow 项目时,明确声明 Git 提交哈希版本。

那么,当你在运行 MLflow 项目时使用远程 GitHub 项目仓库中的代码会发生什么呢?当你看到以下控制台输出的第一行时,这个问题就会变得清晰。

2021/12/30 18:57:32 INFO mlflow.projects.utils: === Fetching project from https://github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow#chapter05 into /var/folders/51/whxjy4r92dx18788yp11ycyr0000gp/T/tmpdyzaa1ye ===

这意味着 MLflow 会代表用户开始将远程项目克隆到一个本地临时文件夹,路径为/var/folders/51/whxjy4r92dx18788yp11ycyr0000gp/T/tmpdyzaa1ye

如果你导航到这个临时文件夹,你会看到整个 GitHub 项目的内容都已经被克隆到这个文件夹,而不仅仅是包含你要运行的 ML 项目的文件夹。

其余的控制台输出就像我们在使用本地代码时看到的一样。完成 download_data 步骤的运行后,你应该能够在 chapter05 下的临时文件夹中找到下载的数据,因为我们在 ML 项目文件中将本地目标文件夹定义为相对于路径 ./data

local_folder: { type: str, default: ./data }

MLflow 会自动将其转换为绝对路径,然后变成相对路径,指向 chapter05 下的克隆项目文件夹,因为 MLproject 文件就位于该文件夹中。

这种能够引用远程 GitHub 项目并在本地环境中运行的能力,无论这个本地环境是你的笔记本还是云端的虚拟机,都非常强大。它使得通过持续集成和持续部署CI/CD)实现自动化成为可能,因为这一过程可以直接在命令行中调用,并且可以被编写成 CI/CD 脚本。追踪部分也非常精准,因为我们有在 MLflow 跟踪服务器中记录的 Git 提交哈希,这使得我们能够准确知道执行的是哪个版本的代码。

请注意,在我们刚才讨论的两种场景中,执行环境是发出 MLflow run 命令的本地机器。MLflow 项目会同步执行至完成,这意味着它是一个阻塞调用,运行过程中会实时显示控制台输出的进度。

然而,我们需要支持一些额外的运行场景。例如,有时发出 MLflow 项目运行命令的机器不够强大,无法支持我们所需的计算任务,比如训练一个需要多个 epoch 的深度学习模型。另一个场景可能是,训练所需的数据达到多个 GB,你不希望将其下载到本地笔记本进行模型开发。这要求我们能够在远程集群中运行代码。接下来我们将看看如何实现这一点。

在云端远程运行本地代码

在前面的章节中,我们在本地笔记本环境中运行了所有代码,并且由于笔记本的计算能力有限,我们将深度学习微调步骤限制为仅三个 epoch。这能够实现代码的快速运行和本地环境的测试,但并不能真正构建一个高性能的深度学习模型。我们实际上需要在远程 GPU 集群中运行微调步骤。理想情况下,我们应该只需更改一些配置,仍然在本地笔记本控制台中发出 MLflow run 命令,但实际的流水线将提交到云端的远程集群。接下来,我们将看看如何在我们的深度学习流水线中实现这一点。

我们从向 Databricks 服务器提交代码开始。需要三个前提条件:

我们还在 chapter05requirements.txt 文件中包括了这个依赖,当您获取本章代码时。

  • .databrickscfg 文件位于您的本地主文件夹中。您不需要同时存在两者,但如果有两个,使用环境变量定义的文件会在 Databricks 命令行中优先被选取。使用环境变量和生成访问令牌的方法在第一章,“深度学习生命周期与 MLOps 挑战”中的 设置 MLflow 与远程 MLflow 服务器交互 部分有详细描述。请注意,这些环境变量可以直接在命令行中设置,也可以放入 .bash_profile 文件中,如果您使用的是 macOS 或 Linux 机器。

这里我们描述了如何使用 Databricks 命令行工具生成 .databrickscfg 文件:

  1. 运行以下命令来设置令牌配置:

    databricks configure --token
    
  2. 按照提示填写远程 Databricks 主机 URL 和访问令牌:

    Databricks Host (should begin with https://): https://????
    Token: dapi??????????
    
  3. 现在,如果您检查本地主文件夹,应该会找到一个名为 .databrickscfg 的隐藏文件。

如果您打开这个文件,应该能看到类似以下内容:

[DEFAULT]
host = https://??????
token = dapi???????
jobs-api-version = 2.0 

请注意,最后一行指示的是 Databricks 服务器正在使用的远程作业提交和执行 API 版本。

现在您已经正确设置了访问权限,让我们看看如何使用以下步骤在远程 Databricks 服务器上远程运行 DL 流水线:

  1. 由于我们将使用远程 Databricks 服务器,因此之前设置的本地 MLflow 服务器不再有效。这意味着我们需要在 main.py 文件中禁用并注释掉以下几行,这些行仅对本地 MLflow 服务器配置有用(从 GitHub 获取最新版本的 chapter05 代码以跟随步骤:github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow.git):

    os.environ["MLFLOW_TRACKING_URI"] = http://localhost
    os.environ["MLFLOW_S3_ENDPOINT_URL"] = http://localhost:9000
    os.environ["AWS_ACCESS_KEY_ID"] = "minio"
    os.environ["AWS_SECRET_ACCESS_KEY"] = "minio123"
    

相反,我们应该使用以下环境变量,它可以在 .bash_profile 文件中定义,或者直接在命令行中执行:

export MLFLOW_TRACKING_URI="databricks"

这将使用 Databricks 服务器上的 MLflow 跟踪服务器。如果您不指定这一点,它将默认为 localhost,但由于远程 Databricks 服务器上没有 localhost 版本的 MLflow,因此会失败。因此,请确保正确设置此项。现在,我们已准备好在远程运行本地代码。

  1. 现在,运行以下命令行将本地代码提交到远程 Databricks 服务器进行运行。我们将从download_data步骤开始,如下所示:

    mlflow run . -b databricks --backend-config cluster_spec.json --experiment-name='/Shared/dl_model_chapter05' -P pipeline_steps ='download_data'
    

您将看到这次命令行有两个新参数:-b databricks,它指定后端为 Databricks 服务器,和--backend-config cluster_spec.json,它详细说明了集群规范。这个cluster_spec.json文件的内容如下:

{
    "new_cluster": {
        "spark_version": "9.1.x-gpu-ml-scala2.12",
        "num_workers": 1,
        "node_type_id": "g4dn.xlarge"
    }
}

cluster_spec.json文件通常位于与 MLproject 文件相同的文件夹中,并且需要预先定义,以便 MLflow 运行命令可以找到它。我们在这里提供的示例仅定义了创建 Databricks 作业集群所需的最小参数集,使用 AWS 的 GPU 虚拟机作为单节点,但如果需要,您可以创建一个更丰富的集群规范(请参阅下面的Databricks 集群规范框以获取更多详细信息)。

Databricks 集群规范

在向 Databricks 提交作业时,需要创建一个新的作业集群,这与您已有的交互式集群不同,后者可以通过附加笔记本来运行交互式作业。集群规范是通过最小化指定 Databricks 运行时版本来定义的,在我们当前的示例中是9.1.x-gpu-ml-scala2.12,还包括工作节点的数量和节点类型 ID,如我们的示例所示。建议为学习目的使用g4dn.xlarge。在这个集群规范中,您还可以定义许多其他配置,包括存储和访问权限,以及init脚本。生成有效集群规范 JSON 文件的最简单方法是使用 Databricks 门户 UI 创建一个新集群,您可以选择 Databricks 运行时版本、集群节点类型以及其他参数(docs.databricks.com/clusters/create.html)。然后,您可以通过点击创建集群UI 页面右上角的 JSON 链接来获取集群的 JSON 表示(见图 5.2)。

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_05_02.jpg

图 5.2 - 在 Databricks 上创建集群的示例

还需要注意,前面命令中的experiment-name参数不再仅仅接受一个实验名称字符串,而是需要包括 Databricks 工作区中的绝对路径。这与本地的 MLflow 跟踪服务器不同。为了使远程作业提交能够正常工作,必须遵循这一约定。注意,如果你希望拥有多个级别的子文件夹结构,例如下面的结构,那么每个子文件夹必须已经在 Databricks 服务器中存在:

/rootPath/subfolder1/subfolder2/my_experiment_name

这意味着rootPathsubfolder1subfolder2文件夹必须已经存在。如果没有,这条命令会失败,因为它无法在 Databricks 服务器上自动创建父文件夹。最后那一串my_experiment_name如果不存在,可以自动创建,因为它是实际的实验名称,将承载所有实验的运行。注意,在这个例子中,我们使用命令行参数来指定实验名称,但也可以使用环境变量来指定,方法如下:

export MLFLOW_EXPERIMENT_NAME=/Shared/dl_model_chapter05
  1. 一旦执行此命令,你会发现这次控制台输出信息明显比上次在本地环境中的运行要简短。这是因为以这种方式执行代码时,它是异步执行的,这意味着作业被提交到远程的 Databricks 服务器并立即返回控制台,而不需要等待。让我们看一下输出的前三行:

    INFO: '/Shared/dl_model_chapter05' does not exist. Creating a new experiment
    2022/01/06 17:35:32 INFO mlflow.projects.databricks: === Uploading project to DBFS path /dbfs/mlflow-experiments/427565/projects-code/f1cbec57b21eabfca52f417f8482054bbea22be 9205b5bbde461780d809924c2.tar.gz ===
    2022/01/06 17:35:32 INFO mlflow.projects.databricks: === Finished uploading project to /dbfs/mlflow-experiments/427565/projects-code/f1cbec57b21eabfca52f417f8482054bbea22be 9205b5bbde461780d809924c2.tar.gz ===
    

第一行意味着实验在 Databricks 服务器上不存在,因此正在创建。如果你第二次运行该命令,这一行就不会出现。第二行和第三行描述了 MLflow 将 MLproject 打包成.tar.gz文件并上传到 Databricks 文件服务器的过程。注意,与 GitHub 项目需要从仓库中检出整个项目不同,这里只需要打包chapter05文件夹,因为我们的 MLproject 就位于该文件夹内。这可以通过查看 Databricks 集群中的作业运行日志来确认,我们将在接下来的几段中解释(如何获取作业 URL 以及如何查看日志)。

MLproject 的同步与异步运行

官方的 MLflow 运行 CLI 不支持一个参数来指定以异步或同步模式运行 MLflow 项目。然而,MLflow 运行 Python API 确实有一个名为synchronous的参数,默认设置为True。当使用 MLflow 的 CLI 通过 Databricks 作为后台运行 MLflow 作业时,默认行为是异步的。有时,在 CI/CD 自动化过程中,当您需要确保 MLflow 运行在移动到下一步之前成功完成时,同步行为是有用的。官方的 MLflow 运行 CLI 无法做到这一点,但您可以编写一个包装 CLI 的 Python 函数,调用 MLflow 的 Python API,并将同步模式设置为True,然后使用您自己的 CLI Python 命令以同步模式运行 MLflow 作业。还要注意,mlflow.run()mlflow.projects.run()API 的高级流畅(面向对象)API。为了保持一致性,我们在本书中广泛使用mlflow.run()API。有关 MLflow 运行 Python API 的详细信息,请参见官方文档页面:www.mlflow.org/docs/latest/python_api/mlflow.projects.html#mlflow.projects.run

输出的接下来的几行看起来类似于以下内容:

2022/01/06 17:48:31 INFO mlflow.projects.databricks: === Running entry point main of project . on Databricks ===
2022/01/06 17:48:31 INFO mlflow.projects.databricks: === Launched MLflow run as Databricks job run with ID 279456. Getting run status page URL... ===
2022/01/06 17:48:31 INFO mlflow.projects.databricks: === Check the run's status at https://???.cloud.databricks.com#job/168339/run/1 ===

这些行描述了作业已提交到 Databricks 服务器,并且作业运行 ID 和作业网址显示在最后一行(将???替换为您的实际 Databricks 网址,以便使其对您有效)。请注意,MLflow 运行 ID 是279456,与作业网址中看到的 ID(168339)不同。这是因为作业网址由 Databricks 作业管理系统管理,并且有不同的方式来生成和追踪每个实际的作业。

  1. 点击作业网址链接(https://???.cloud.databricks.com#job/168339/run/1)查看此作业的状态,该页面将显示进度以及标准输出和错误日志(参见图 5.3)。通常,这个页面需要几分钟才能开始显示运行进度,因为它需要先基于cluster_spec.json创建一个全新的集群,才能开始运行作业。

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_05_03.jpg

图 5.3 – MLflow 运行作业状态页面,显示标准输出

图 5.3显示作业已成功完成(chapter05文件夹已上传并在Databricks 文件系统DBFS)中提取)。如前所述,只有我们想要运行的 MLproject 被打包、上传并在 DBFS 中提取,而不是整个项目仓库。

在同一个作业状态页面上,您还会找到标准错误部分,显示描述我们要运行的管道步骤download_data的日志。这些不是错误,而只是信息性消息。所有 Python 日志都会在这里聚合。详情请参见图 5.4

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_05_04.jpg

图 5.4 – 在作业状态页面记录的 MLflow 作业信息

图 5.4 显示了与我们在本地交互式环境中运行时非常相似的日志,但现在这些运行是在我们提交作业时指定的集群中执行的。请注意,图 5.4 中的流水线实验 ID 是 427565。你应该能够在 Databricks 服务器上的集成 MLflow 跟踪服务器中,使用实验 ID 427565,通过以下 URL 模式找到成功完成的 MLflow DL 流水线运行:

https://[your databricks hostname]/#mlflow/experiments/427565

如果你看到与前几章中看到的熟悉的跟踪结果,给自己一个大大的拥抱,因为你刚刚完成了在远程 Databricks 集群中运行本地代码的一个重要学习里程碑!

此外,我们可以使用这种方法运行 DL 流水线的多个步骤,而无需更改每个步骤的实现代码。例如,如果我们想同时运行 DL 流水线的 download_datafine_tuning_model 步骤,我们可以发出以下命令:

mlflow run . -b databricks --backend-config cluster_spec.json --experiment-name='/Shared/dl_model_chapter05' -P pipeline_steps='download_data,fine_tuning_model'

输出控制台将显示以下简短信息:

2022/01/07 15:22:39 INFO mlflow.projects.databricks: === Uploading project to DBFS path /dbfs/mlflow-experiments/427565/projects-code/743cadfec82a55b8c76e9f27754cfdd516545b155254e990c2cc62650b8af959.tar.gz ===
2022/01/07 15:22:40 INFO mlflow.projects.databricks: === Finished uploading project to /dbfs/mlflow-experiments/427565/projects-code/743cadfec82a55b8c76e9f27754cfdd516545b155254e990c2cc62650b8af959.tar.gz ===
2022/01/07 15:22:40 INFO mlflow.projects.databricks: === Running entry point main of project . on Databricks ===
2022/01/07 15:22:40 INFO mlflow.projects.databricks: === Launched MLflow run as Databricks job run with ID 279540\. Getting run status page URL... ===
2022/01/07 15:22:40 INFO mlflow.projects.databricks: === Check the run's status at https://?????.cloud.databricks.com#job/168429/run/1 ===

然后,你可以转到控制台输出最后一行中显示的作业 URL 页面,等待它创建一个新集群并完成两个步骤。完成后,你应该能够在 MLflow 跟踪服务器中找到两个步骤,使用相同的实验 URL(因为我们使用的是相同的实验名称)。

https://[your databricks hostname]/#mlflow/experiments/427565

现在我们知道如何在远程 Databricks 集群中运行本地代码,我们将学习如何在远程 Databricks 集群中运行来自 GitHub 仓库的代码。

在云端远程运行,远程代码来自 GitHub

重现 DL 流水线最可靠的方法是指向 GitHub 中的项目代码的特定版本,然后在云端运行它,而不调用任何本地资源。这样,我们就能知道代码的确切版本,并使用项目中定义的相同运行环境。让我们看看如何在我们的 DL 流水线中实现这一点。

作为前提和提醒,在发出 MLflow 运行命令之前,以下三个环境变量需要设置好,以完成本节的学习:

export MLFLOW_TRACKING_URI=databricks
export DATABRICKS_TOKEN=[databricks_token]
export DATABRICKS_HOST='https://[your databricks host name/'

我们已经知道如何从上一节设置这些环境变量。可能还需要进行一次额外的设置,即如果你的 GitHub 仓库是非公开的,需要允许 Databricks 服务器访问该仓库(请参阅下面的 GitHub Token 用于让 Databricks 访问非公开或企业项目仓库 说明框)。

GitHub Token 用于让 Databricks 访问非公开或企业项目仓库

为了让 Databricks 访问 GitHub 上的项目仓库,还需要另一个令牌。可以通过访问个人 GitHub 页面(https://github.com/settings/tokens)并按照页面上描述的步骤生成。然后,您可以按照 Databricks 文档网站上的说明进行设置:docs.databricks.com/repos.html#configure-your-git-integration-with-databricks

现在,让我们使用 GitHub 仓库中的特定版本,在远程 Databricks 集群上运行完整的管道:

mlflow run https://github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow#chapter05 -v 395c33858a53bcd8ac217a962ab81e148d9f1d9a -b databricks --backend-config cluster_spec.json --experiment-name='/Shared/dl_model_chapter05' -P pipeline_steps='all'

然后我们会看到简短的六行输出。让我们看看每行显示的重要信息以及其工作原理:

  1. 第一行显示项目仓库的内容已下载到本地的位置:

    2022/01/07 17:36:54 INFO mlflow.projects.utils: === Fetching project from https://github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow#chapter05 into /var/folders/51/whxjy4r92dx18788yp11ycyr0000gp/T/tmpzcepn5h5 ===
    

如果我们去到本地机器上执行此命令时消息中显示的临时目录,我们会看到整个仓库已经下载到此文件夹:/var/folders/51/whxjy4r92dx18788yp11ycyr0000gp/T/tmpzcepn5h5

  1. 接下来的两行显示项目内容已被压缩并上传到 Databricks 服务器上的 DBFS 文件夹:

    2022/01/07 17:36:57 INFO mlflow.projects.databricks: === Uploading project to DBFS path /dbfs/mlflow-experiments/427565/projects-code/fba3d31e1895b78f40227b5965461faddb 61ec9df906fb09b161f74efaa90aa2.tar.gz ===
    2022/01/07 17:36:57 INFO mlflow.projects.databricks: === Finished uploading project to /dbfs/mlflow-experiments/427565/projects-code/fba3d31e1895b78f40227b5965461faddb61ec 9df906fb09b161f74efaa90aa2.tar.gz ===
    

如果我们使用 Databricks 的本地命令行工具,我们可以像对待本地文件一样列出这个 .tar.gz 文件(但实际上,它位于 Databricks 服务器的远程位置):

databricks fs ls -l dbfs:/mlflow-experiments/427565/projects-code/fba3d31e1895b78f40227b5965461faddb61ec 9df906fb09b161f74efaa90aa2.tar.gz

你应该看到类似下面的输出,它描述了文件的属性(大小、所有者/组 ID,及其是否为文件或目录):

file  3070  fba3d31e1895b78f40227b5965461faddb61ec 9df906fb09b161f74efaa90aa2.tar.gz  1641605818000
  1. 下一行显示它开始运行 GitHub 项目的 main 入口点:

    2022/01/07 17:36:57 INFO mlflow.projects.databricks: === Running entry point main of project https://github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow#chapter05 on Databricks ===
    

注意,当我们运行本地代码时的不同(项目后面有一个 ,表示当前目录),现在它列出了 GitHub 仓库位置的完整路径。

  1. 最后两行输出与前一部分的输出类似,列出了作业 URL:

    2022/01/07 17:36:57 INFO mlflow.projects.databricks: === Launched MLflow run as Databricks job run with ID 279660\. Getting run status page URL... ===
    2022/01/07 17:36:57 INFO mlflow.projects.databricks: === Check the run's status at https://????.cloud.databricks.com#job/168527/run/1 ===
    
  2. 如果我们点击控制台输出最后一行中的作业 URL,我们将能够在该网站上看到以下内容(图 5.5):

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_05_05.jpg

图 5.5 – 使用 GitHub 仓库中的代码的 MLflow 作业状态页面

图 5.5 显示了该作业的最终状态。请注意,页面的标题现在显示为 MLflow Run for https://github.com/PacktPublishing/Practical-Deep-Learning-at-Scale-with-MLFlow#chapter05,而不是之前使用本地代码运行时显示的 MLflow Run for .

任务的状态显示该任务已成功运行,你还会看到结果被记录在实验页面中,和之前一样,所有三个步骤都已完成。模型也已按预期注册在模型注册表中,在 Databricks 服务器下的以下 URL:

https://[your_databricks_hostname]/#mlflow/models/dl_finetuned_model

总结来说,这种方法的工作机制在下图中展示(图 5.6):

https://github.com/OpenDocCN/freelearn-dl-pt5-zh/raw/master/docs/prac-dl-scl-mlflow/img/B18120_05_06.jpg

图 5.6 – 在远程 Databricks 集群服务器上运行远程 GitHub 代码的总结视图

图 5.6显示了三个不同的位置(一个是我们发出 MLflow 运行命令的机器,一个是远程 Databricks 服务器,另一个是远程 GitHub 项目)。当发出 MLflow 运行命令时,远程 GitHub 项目的源代码会被克隆到发出命令的机器上,然后上传到远程 Databricks 服务器,并提交任务来执行深度学习管道的多个步骤。这是异步执行的,任务的状态需要根据创建的任务 URL 进行监控。

在其他后端上运行 MLflow 项目

目前,Databricks 支持两种类型的远程运行后端环境:Databricks 和 K8s。然而,从 MLflow 1.22.0 版本开始(www.mlflow.org/docs/latest/projects.html#run-an-mlflow-project-on-kubernetes-experimental),在 K8s 上运行 MLflow 项目仍处于实验模式,并且可能会发生变化。如果你有兴趣了解更多内容,请参考进一步阅读部分中的参考资料,探索提供的示例。此外,还有其他由第三方提供的后端(也称为社区插件),如hadoop-yarngithub.com/criteo/mlflow-yarn)。由于 Databricks 在所有主要云提供商中都有提供,并且在支持符合企业安全合规的生产场景方面较为成熟,本书目前重点讲解如何在 Databricks 服务器上远程运行 MLflow 项目。

摘要

在本章中,我们学习了如何在不同的执行环境(本地或远程 Databricks 集群)中运行 DL 管道,使用的是本地源代码或 GitHub 项目代码。这不仅对于重现性和执行 DL 管道的灵活性至关重要,还通过使用 CI/CD 工具提供了更好的生产力和未来自动化的可能性。能够在资源丰富的远程环境中运行一个或多个 DL 管道步骤,使我们能够以更快的速度执行大规模计算和数据密集型任务,这通常出现在生产质量的 DL 模型训练和微调中。这使我们在必要时能够进行超参数调优或交叉验证 DL 模型。接下来的章节我们将开始学习如何进行大规模的超参数调优,这是我们自然的下一步。

进一步阅读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值