将 MLflow 模型部署为本地推理服务器

MLflow 允许你使用单个命令将模型部署到本地。这种方法非常适合轻量级应用,或者在将模型迁移到暂存或生产环境之前在本地测试模型。

如果你是MLflow模型部署的新手,请先阅读 MLflow部署指南 以了解MLflow模型和部署的基本概念。

部署推理服务器

在部署之前,您必须拥有一个 MLflow 模型。如果您没有,可以按照 MLflow 跟踪快速入门 创建一个示例的 scikit-learn 模型。记得记下模型 URI,例如 runs:/<run_id>/<artifact_path> (或者如果您在 MLflow 模型注册表 中注册了模型,则为 models:/<model_name>/<model_version>)。

一旦模型准备就绪,部署到本地服务器是直截了当的。使用 mlflow models serve 命令进行一步部署。此命令启动一个本地服务器,该服务器监听指定端口并提供您的模型。

mlflow models serve -m runs:/<run_id>/model -p 5000

然后,您可以按如下方式向服务器发送测试请求:

curl http://127.0.0.1:5000/invocations -H "Content-Type:application/json"  --data '{"inputs": [[1, 2], [3, 4], [5, 6]]}'

有几个命令行选项可用于自定义服务器的行为。例如,--env-manager 选项允许您选择特定的环境管理器,如 Anaconda,来创建虚拟环境。mlflow models 模块还提供了其他有用的命令,例如构建 Docker 镜像或生成 Dockerfile。如需全面详细信息,请参阅 MLflow CLI 参考

推理服务器规范

端点

推理服务器提供4个端点:

  • /invocations: 一个接受带有输入数据的POST请求并返回预测结果的推理端点。

  • /ping: 用于健康检查。

  • /health: 与 /ping 相同

  • /version: 返回 MLflow 版本。

接受的输入格式

/invocations 端点接受 CSV 或 JSON 输入。输入格式必须在 Content-Type 头中指定为 application/jsonapplication/csv

CSV 输入

CSV 输入必须是有效的 pandas.DataFrame CSV 表示。例如:

curl http://127.0.0.1:5000/invocations -H 'Content-Type: application/csv' --data '1,2,3,4'

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

你可以传递一个对应于所需模型负载的扁平字典,或者将负载包装在一个字典中,该字典包含一个指定负载格式的键。

包装的有效载荷字典

如果你的模型格式不被上述支持,或者你想避免将输入数据转换为所需的载荷格式,你可以利用下面的字典载荷结构。

字段

描述

示例

dataframe_split

split 方向上的 Pandas DataFrame。

{"dataframe_split": pandas_df.to_dict(orient="split")}

dataframe_records

Pandas DataFrame 在 records 方向。我们不推荐使用这种格式,因为它不能保证保留列的顺序。

{"dataframe_records": pandas_df.to_dict(orient="records")}

instances

张量输入格式如 TF Serving 的 API 文档 中所述,其中提供的输入将被转换为 Numpy 数组。

{"instances": [1.0, 2.0, 5.0]}

inputs

instances 相同,但使用不同的键。

{"inputs": [["Cheese"], ["and", "Crackers"]]}
示例
# Prerequisite: serve a custom pyfunc OpenAI model (not mlflow.openai) on localhost:5678
#   that defines inputs in the below format and params of `temperature` and `max_tokens`

import json
import requests

payload = json.dumps(
    {
        "inputs": {"messages": [{"role": "user", "content": "Tell a joke!"}]},
        "params": {
            "temperature": 0.5,
            "max_tokens": 20,
        },
    }
)
response = requests.post(
    url=f"http://localhost:5678/invocations",
    data=payload,
    headers={"Content-Type": "application/json"},
)
print(response.json())

JSON 输入还可以包含一个可选的 params 字段,用于传递附加参数。有效的参数类型是 Union[DataType, List[DataType], None],其中 DataType 是 MLflow 数据类型。要传递参数,必须定义一个包含 params 的有效 模型签名

curl http://127.0.0.1:5000/invocations -H 'Content-Type: application/json' -d '{
    "inputs": {"question": ["What color is it?"],
               "context": ["Some people said it was green but I know that it is pink."]},
    "params": {"max_answer_len": 10}
}'

备注

由于 JSON 会丢弃类型信息,MLflow 将在模型架构中指定的输入类型可用时,将 JSON 输入转换为该输入类型。如果您的模型对输入类型敏感,建议为模型提供一个架构,以确保在推理时不发生类型不匹配错误。特别是,深度学习模型通常对输入类型要求严格,并且需要模型架构才能正确评分。对于复杂的数据类型,请参见下面的 编码复杂数据

原始载荷字典

如果你的负载数据格式是你的 mlflow 服务模型所接受的,并且它属于下面支持的模型之一,你可以传递一个原始的负载字典。

支持的请求格式

描述

示例

OpenAI 聊天

OpenAI 聊天请求负载.

{
    "messages": [{"role": "user", "content": "Tell a joke!"}],  # noqa
    "temperature": 0.0,
}

† 请注意,由于其配置由 MLflow 模型实例设置,使用 OpenAI API 时 不应 包含 model 参数。所有其他参数可以自由使用,前提是它们在记录的模型签名中的 params 参数内定义。

示例
# Prerequisite: serve a Pyfunc model accepts OpenAI-compatible chat requests on localhost:5678 that defines
#   `temperature` and `max_tokens` as parameters within the logged model signature

import json
import requests

payload = json.dumps(
    {
        "messages": [{"role": "user", "content": "Tell a joke!"}],
        "temperature": 0.5,
        "max_tokens": 20,
    }
)
requests.post(
    url=f"http://localhost:5678/invocations",
    data=payload,
    headers={"Content-Type": "application/json"},
)
print(requests.json())

编码复杂数据

复杂的数据类型,如日期或二进制数据,没有原生的 JSON 表示。如果你包含一个模型签名,MLflow 可以自动从 JSON 解码支持的数据类型。以下数据类型转换是被支持的:

  • binary: 数据预期为 base64 编码,MLflow 将自动进行 base64 解码。

  • datetime: 数据应按照 ISO 8601 规范 编码为字符串。MLflow 将在给定平台上解析为适当的日期时间表示。

示例请求:

# record-oriented DataFrame input with binary column "b"
curl http://127.0.0.1:5000/invocations -H 'Content-Type: application/json' -d '[
    {"a": 0, "b": "dGVzdCBiaW5hcnkgZGF0YSAw"},
    {"a": 1, "b": "dGVzdCBiaW5hcnkgZGF0YSAx"},
    {"a": 2, "b": "dGVzdCBiaW5hcnkgZGF0YSAy"}
]'

# record-oriented DataFrame input with datetime column "b"
curl http://127.0.0.1:5000/invocations -H 'Content-Type: application/json' -d '[
    {"a": 0, "b": "2020-01-01T00:00:00Z"},
    {"a": 1, "b": "2020-02-01T12:34:56Z"},
    {"a": 2, "b": "2021-03-01T00:00:00Z"}
]'

服务框架

默认情况下,MLflow 使用 Flask,一个轻量级的 Python WSGI Web 应用程序框架来提供推理端点。然而,Flask 主要设计用于轻量级应用程序,可能不适合大规模生产用例。为了解决这一差距,MLflow 集成了 MLServer 作为替代的服务引擎。MLServer 通过利用异步请求/响应范式和工作负载卸载来实现更高的性能和可扩展性。此外,MLServer 被用作 Kubernetes 原生框架(如 Seldon Core`KServe(以前称为 KFServing)<https://kserve.github.io/website/>`_)中的核心 Python 推理服务器,因此提供了诸如金丝雀部署和自动扩展等高级功能。

用例

轻量级用途,包括本地测试。

高规模生产环境。

设置

Flask 默认随 MLflow 安装。

需要单独安装。

性能

适用于轻量级应用,但未针对高性能进行优化,因为它是一个 WSGI 应用。WSGI 基于同步请求/响应范式,由于其阻塞性质,不适合用于机器学习工作负载。机器学习预测通常涉及大量计算,可能需要很长时间才能完成,因此在请求处理过程中阻塞服务器并不理想。虽然 Flask 可以通过 Uvicorn 等异步框架进行增强,但 MLflow 并不原生支持这些框架,而是简单地使用 Flask 的默认同步行为。

专为高性能机器学习工作负载设计,通常能提供更好的吞吐量和效率。MLServer 支持异步请求/响应范式,通过将机器学习推理工作负载卸载到单独的工作池(进程),使得服务器在处理推理时可以继续接受新请求。有关他们如何实现这一点的更多细节,请参阅 MLServer 并行推理。此外,MLServer 支持 自适应批处理,可以透明地将请求批量处理以提高吞吐量和效率。

可扩展性

由于与性能相同的原因,本身不具备可扩展性。

除了上述支持并行推理外,MLServer 还被用作 Kubernetes 原生框架(如 Seldon CoreKServe <https://kserve.github.io/website/>`_(原名 KFServing))中的核心推理服务器。通过 `使用 MLServer 将 MLflow 模型部署到 Kubernetes,您可以利用这些框架的高级功能,如自动扩展,以实现高扩展性。

MLServer 通过 /invocations 端点暴露相同的评分 API。要使用 MLServer 部署,首先使用 pip install mlflow[extras] 安装额外的依赖项,然后使用 --enable-mlserver 选项执行部署命令。例如,

mlflow models serve -m runs:/<run_id>/model -p 5000 --enable-mlserver

要了解更多关于 MLflow 和 MLServer 之间集成的信息,请查看 MLServer 文档中的 端到端示例。您还可以在 将模型部署到 Kubernetes 中找到使用 MLServer 将 MLflow 模型部署到 Kubernetes 集群的指南。

运行批量推理

你可以使用 mlflow models predict 命令在本地文件上执行单个批量推理作业,而不是运行在线推理端点。以下命令在 input.csv 上运行模型预测,并将结果输出到 output.csv

mlflow models predict -m runs:/<run_id>/model -i input.csv -o output.csv

故障排除