理解 MLflow 中的 PyFunc
在 MLflow 领域中,虽然具名风味 (named flavors) 提供了针对流行框架的特定功能,但在某些情况下,需求会超出这些预定义的路径。这时,就需要自定义 pyfunc
(Python 函数),它是一个通用接口,允许您通过定义自定义 Python 函数将任何框架的模型封装到 MLflow 模型中。
PyFunc 版本的模型与任何其他 MLflow 模型类型以相同的方式进行交互,都提供 save_model()
和 log_model()
接口,以便分别创建(保存)和访问(加载)模型。
由于这些自定义模型包含 python_function
风味,因此可以将它们部署到任何 MLflow 支持的生产环境,例如 SageMaker、AzureML、Databricks、Kubernetes 或本地 REST 终结点。
为什么要使用 PyFunc?
-
灵活性:它提供了使用任何机器学习库或框架的自由,确保 MLflow 能够适应各种用例。
-
统一接口:使用 pyfunc,您可以获得一致的 API。一旦您的模型符合此接口,您就可以利用 MLflow 的所有部署工具,而无需担心底层框架。
-
自定义逻辑:除了模型之外,pyfunc 还允许进行预处理和后处理,从而增强模型的部署能力。
PyFunc 的组成部分
-
Python 函数风味:这是 MLflow Python 模型的默认模型接口。它确保可以使用一致的 API 加载每个 MLflow Python 模型并与之交互。
-
文件系统格式:一个结构化的目录,包含所有必需的数据、代码和配置,确保封装的模型及其依赖项是自包含且可重现的。
-
MLModel 配置:一个重要的描述符,MLmodel 文件提供有关模型的详细信息,包括其加载器模块、代码、数据和环境。
-
自定义 Pyfunc 模型:一个强大的功能,超越了具名风味,允许创建具有自定义逻辑、数据转换等的模型。
自定义 Pyfunc 模型的威力
虽然 MLflow 的具名风味为许多框架提供了开箱即用的解决方案,但它们可能无法满足所有需求。这就是自定义 pyfunc 模型发挥作用的地方。无论您是使用利基框架、需要实现专门的预处理,还是希望集成后推理逻辑,自定义 pyfunc 模型都提供了执行这些操作的工具。
通过定义一个继承自 PythonModel 的 Python 类并实现必要的方法,您可以创建一个根据您的特定需求定制的自定义 pyfunc 模型。
自定义 Pyfunc 可能是最佳选择的情况
在许多情况下,自定义 Pyfunc 变得非常宝贵
-
使用大型模型进行分布式推理:
- 在 Apache Spark 或 Ray 等分布式系统中,推理并行化到多个核心,存在加载模型多个副本的风险,每个核心一个副本。 这会大大消耗系统的资源,尤其是对于大型模型。
- 使用自定义 Pyfunc,您可以确保每个 worker 节点或 executor 仅加载模型的单个副本,从而优化资源使用并加快推理速度。
-
不支持的模型:
- 虽然 MLflow 为流行的框架提供了广泛的具名风味,但机器学习生态系统是巨大的。 可能存在尚未支持的利基或新兴框架。
- 自定义 Pyfunc 提供了一种无缝地封装和管理来自任何此类不受支持的框架的模型的方法。
-
自定义推理方法:
- 默认的 .predict() 方法可能并不总是满足特定要求。 也许您需要一种生成 logits、不确定性或其他指标的方法。
- 自定义 Pyfunc 可以包装任何推理方法,确保部署的模型的行为完全符合需要。
-
加载辅助数据或外部系统:
- 有时,模型的推理不仅仅是关于模型本身。 它可能需要引用未与模型一起保存的外部数据,或者可能需要连接到其他系统。
- 考虑这样一种情况:模型需要在预测期间查找向量数据库中的条目。 自定义 Pyfunc 可以利用 load_context 方法来加载配置文件。 这为自定义 predict 方法提供了配置数据,使其能够在推理期间连接到外部服务。
自定义 Pyfunc 的内部工作原理
了解 mlflow.pyfunc.load_model() 调用期间的事件顺序对于充分利用自定义 Pyfuncs 的强大功能至关重要。 以下是加载自定义 pyfunc 时发生的事件顺序的逐步分解,以及如何在保存模型期间声明覆盖以访问和引用以控制加载的模型对象的行为。
-
启动:
- 当调用 mlflow.pyfunc.load_model() 时,该过程开始,表明打算加载自定义 Pyfunc 模型以供使用。
-
模型配置检索:
- 系统获取与保存的模型关联的 MLmodel 配置文件。 此描述符提供有关模型的基本详细信息,包括其加载器模块、代码、数据和环境。
-
工件映射:
- 映射保存的模型工件,其中可能包括序列化的模型对象、辅助数据或其他必要文件。 此映射确保自定义 Pyfunc 知道在哪里可以找到它需要的一切。
-
Python 模型初始化:
- 初始化定义自定义 Pyfunc 的 Python 类(通常继承自 PythonModel)。 在此阶段,模型尚未准备好进行推理,但已为后续加载步骤做好准备。
-
上下文加载:
- 调用自定义 Pyfunc 的 load_context 方法。 此方法旨在加载任何外部引用或执行初始化任务。 例如,它可以反序列化模型对象,加载用于连接到外部服务的配置文件,或准备模型需要的任何其他资源。
-
模型就绪:
- 加载上下文后,自定义 Pyfunc 模型现在已完全初始化并准备好进行推理。 随后对其 predict 方法的任何调用都将执行其中定义的自定义逻辑,并按设计生成结果。
值得注意的是,此序列确保自定义 Pyfunc 模型一旦加载,就是一个完全自包含的单元,不仅封装了模型,还封装了它需要的任何自定义逻辑、数据转换和外部引用。 此设计确保了可重现性和一致性,无论模型部署在何处。
后续步骤
现在您了解了 pyfunc 的重要性和组成部分,下一步是深入了解如何构建它们。
浏览教程笔记本