跳到主要内容

使用 MLflow 开发 ML 模型并部署到 Kubernetes

注意

本教程假设您有权访问 Kubernetes 集群。但是,您也可以在本地机器上使用 KindMinikube 等本地集群模拟工具完成本教程。

本指南演示如何使用 MLflow 进行端到端操作,包括

本教程将涵盖端到端模型开发过程,包括模型训练和测试。如果您已经有一个模型并且只想学习如何将其部署到 Kubernetes,则可以跳到步骤 6 - 本地测试模型服务

简介:使用 KServe 和 MLServer 进行可扩展的模型服务

MLflow 提供了一个易于使用的接口,用于在基于 Flask 的推理服务器中部署模型。您可以使用 mlflow models build-docker 命令将其容器化,从而将同一推理服务器部署到 Kubernetes 集群。但是,这种方法可能无法扩展,并且不适合生产用例。Flask 未针对高性能和规模进行设计(为什么?),而且手动管理推理服务器的多个实例非常费力。

幸运的是,MLflow 为此提供了一个解决方案。MLflow 提供了另一种推理引擎,它通过支持 MLServer 更适合更大规模的推理部署,从而实现一键部署到 Kubernetes 上流行的无服务器模型服务框架,例如 KServeSeldon Core

什么是 KServe?

KServe,前身为 KFServing,为 Tensorflow、XGBoost、scikit-learn 和 Pytorch 等常见机器学习框架提供高性能、可扩展且高度抽象的接口。它利用 Kubernetes 生态系统(包括 KNativeIstio)提供了有助于运营大规模机器学习系统的高级功能,例如自动扩展金丝雀部署A/B 测试监控可解释性等。

将 MLflow 与 KServe 结合使用的好处

虽然 KServe 实现了高度可扩展的生产就绪型模型服务,但将模型部署到其中可能需要一些努力。MLflow 简化了使用 KServe 和 MLServer 将模型部署到 Kubernetes 集群的过程。此外,它还提供无缝的端到端模型管理,作为管理整个 ML 生命周期的单一平台。这包括实验跟踪模型打包版本控制评估部署,我们将在本教程中介绍这些内容。

步骤 1:安装 MLflow 和附加依赖项

首先,请使用以下命令将 mlflow 安装到您的本地机器:

pip install mlflow[mlserver]

[extras] 将安装本教程所需的附加依赖项,包括 mlserverscikit-learn。请注意,部署不需要 scikit-learn,仅用于训练本教程中使用的示例模型。

您可以通过运行以下命令检查 MLflow 是否正确安装:

mlflow --version

步骤 2:设置 Kubernetes 集群

如果您已经有权访问 Kubernetes 集群,则可以按照官方说明将 KServe 安装到集群。

现在您已经有了作为部署目标的运行中的 Kubernetes 集群,让我们继续创建要部署的 MLflow Model。

步骤 3:训练模型

在本教程中,我们将训练并部署一个简单的回归模型来预测葡萄酒的质量。

让我们首先使用默认超参数训练模型。在 notebook 或 Python 脚本中执行以下代码。

注意

为方便起见,我们使用 mlflow.sklearn.autolog() 函数。此函数允许 MLflow 在训练期间自动记录一组适当的模型参数和指标。要了解有关自动日志记录功能的更多信息或如何手动记录,请参阅MLflow Tracking 文档

import mlflow

import numpy as np
from sklearn import datasets, metrics
from sklearn.linear_model import ElasticNet
from sklearn.model_selection import train_test_split


def eval_metrics(pred, actual):
rmse = np.sqrt(metrics.mean_squared_error(actual, pred))
mae = metrics.mean_absolute_error(actual, pred)
r2 = metrics.r2_score(actual, pred)
return rmse, mae, r2


# Set th experiment name
mlflow.set_experiment("wine-quality")

# Enable auto-logging to MLflow
mlflow.sklearn.autolog()

# Load wine quality dataset
X, y = datasets.load_wine(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)

# Start a run and train a model
with mlflow.start_run(run_name="default-params"):
lr = ElasticNet()
lr.fit(X_train, y_train)

y_pred = lr.predict(X_test)
metrics = eval_metrics(y_pred, y_test)

现在您已经训练了一个模型,让我们通过 MLflow UI 检查参数和指标是否正确记录。您可以通过在终端中运行以下命令启动 MLflow UI:

mlflow ui --port 5000

然后访问 https://:5000 打开 UI。

请在左侧打开名为 "wine-quality" 的实验,然后单击表中名为 "default-params" 的运行。在这种情况下,您应该会看到包括 alphal1_ratio 在内的参数以及 training_scoremean_absolute_error_X_test 等指标。

步骤 4:运行超参数调优

现在我们已经建立了一个基线模型,让我们尝试通过调优超参数来提高其性能。我们将进行随机搜索以确定 alphal1_ratio 的最佳组合。

from scipy.stats import uniform
from sklearn.model_selection import RandomizedSearchCV

lr = ElasticNet()

# Define distribution to pick parameter values from
distributions = dict(
alpha=uniform(loc=0, scale=10), # sample alpha uniformly from [-5.0, 5.0]
l1_ratio=uniform(), # sample l1_ratio uniformlyfrom [0, 1.0]
)

# Initialize random search instance
clf = RandomizedSearchCV(
estimator=lr,
param_distributions=distributions,
# Optimize for mean absolute error
scoring="neg_mean_absolute_error",
# Use 5-fold cross validation
cv=5,
# Try 100 samples. Note that MLflow only logs the top 5 runs.
n_iter=100,
)

# Start a parent run
with mlflow.start_run(run_name="hyperparameter-tuning"):
search = clf.fit(X_train, y_train)

# Evaluate the best model on test dataset
y_pred = clf.best_estimator_.predict(X_test)
rmse, mae, r2 = eval_metrics(clf.best_estimator_, y_pred, y_test)
mlflow.log_metrics(
{
"mean_squared_error_X_test": rmse,
"mean_absolute_error_X_test": mae,
"r2_score_X_test": r2,
}
)

当您重新打开 MLflow UI 时,您应该会注意到运行 "hyperparameter-tuning" 包含 5 个子运行。MLflow 利用父子关系,这对于对一组运行(例如超参数调优中的运行)进行分组特别有用。在此示例中,已启用自动日志记录,MLflow 会根据 scoring 指标自动为排名前 5 的运行创建子运行(在此示例中为负平均绝对误差)。您也可以手动创建父运行和子运行,请参阅创建子运行以了解更多详细信息。

要比较结果并确定最佳模型,您可以使用 MLflow UI 中的可视化功能。

  1. 选择第一个任务("default-params")和超参数调优的父任务("hyperparameter-tuning")。
  2. 单击“图表”选项卡以在图表中可视化指标。
  3. 默认情况下,会显示一些针对预定义指标集的条形图。
  4. 您可以添加不同的图表(例如散点图)来比较多个指标。例如,我们可以看到超参数调优中获得的最佳模型在测试数据集上的均方误差优于默认参数模型。

您可以通过查看父运行 "hyperparameter-tuning" 来检查超参数的最佳组合。在此示例中,最佳模型为 alpha=0.11714084185001972l1_ratio=0.3599780644783639(您可能会看到不同的结果)。

注意

要了解有关使用 MLflow 进行超参数调优的更多信息,请参阅使用 MLflow 和 Optuna 进行超参数调优

步骤 5:打包模型和依赖项

由于我们使用了自动日志记录,MLflow 会自动为每个运行记录Model。此过程方便地将模型权重和依赖项打包为随时可部署的格式。

注意

在实践中,还建议使用MLflow Model Registry(模型注册表)来注册和管理您的模型。

让我们简要了解一下此格式的外观。您可以通过运行详细信息页面上的 Artifacts 选项卡查看已记录的模型。

model
├── MLmodel
├── model.pkl
├── conda.yaml
├── python_env.yaml
└── requirements.txt

model.pkl 是包含序列化模型权重的文件。MLmodel 包含指示 MLflow 如何加载模型的通用元数据。其他文件指定运行模型所需的依赖项。

注意

如果您选择手动日志记录,则需要使用 mlflow.sklearn.log_model 函数显式记录模型,如下所示

mlflow.sklearn.log_model(lr, "model")

步骤 6:本地测试模型服务

在部署模型之前,我们首先在本地测试模型是否可以服务。如本地部署 MLflow Model 中所述,您只需一个命令即可运行本地推理服务器。请记住使用 enable-mlserver 标志,该标志指示 MLflow 使用 MLServer 作为推理服务器。这确保模型在 Kubernetes 中运行的方式与在本地运行的方式相同。

mlflow models serve -m runs:/<run_id_for_your_best_run>/model -p 1234 --enable-mlserver

此命令启动一个在端口 1234 上侦听的本地服务器。您可以使用 curl 命令向服务器发送请求

$ curl -X POST -H "Content-Type:application/json" --data '{"inputs": [[14.23, 1.71, 2.43, 15.6, 127.0, 2.8, 3.06, 0.28, 2.29, 5.64, 1.04, 3.92, 1065.0]]}' http://127.0.0.1:1234/invocations

{"predictions": [-0.03416275504140387]}

有关请求格式和响应格式的更多信息,请参阅推理服务器规范

步骤 7:将模型部署到 KServe

最后,我们已经准备好将模型部署到 Kubernetes 集群。

创建命名空间

首先,创建一个测试命名空间来部署 KServe 资源和您的模型

kubectl create namespace mlflow-kserve-test

创建部署配置

创建一个描述模型部署到 KServe 的 YAML 文件。

在 KServe 配置文件中有两种指定部署模型的方法

  1. 使用模型构建 Docker 镜像并指定镜像 URI。
  2. 直接指定模型 URI(仅当您的模型存储在远程存储中时才起作用)。

请打开下面的选项卡以了解每种方法的详细信息。

注册 Docker 账户

由于 KServe 无法解析本地构建的 Docker 镜像,因此您需要将镜像推送到 Docker 注册表。在本教程中,我们将镜像推送到 Docker Hub,但您可以使用任何其他 Docker 注册表,例如 Amazon ECR 或私有注册表。

如果您还没有 Docker Hub 账户,请在 https://hub.docker.com/signup 创建一个。

构建 Docker 镜像

使用 mlflow models build-docker 命令构建一个随时可部署的 Docker 镜像

mlflow models build-docker -m runs:/<run_id_for_your_best_run>/model -n <your_dockerhub_user_name>/mlflow-wine-classifier --enable-mlserver

此命令构建一个包含模型和依赖项的 Docker 镜像,并将其标记为 mlflow-wine-classifier:latest

推送 Docker 镜像

构建镜像后,将其推送到 Docker Hub(或使用适当的命令推送到其他注册表)

docker push <your_dockerhub_user_name>/mlflow-wine-classifier

编写部署配置

然后创建这样一个 YAML 文件

apiVersion: "serving.kserve.io/v1beta1"
kind: "InferenceService"
metadata:
name: "mlflow-wine-classifier"
namespace: "mlflow-kserve-test"
spec:
predictor:
containers:
- name: "mlflow-wine-classifier"
image: "<your_docker_user_name>/mlflow-wine-classifier"
ports:
- containerPort: 8080
protocol: TCP
env:
- name: PROTOCOL
value: "v2"

部署推理服务

运行以下 kubectl 命令将新的 InferenceService 部署到您的 Kubernetes 集群

$ kubectl apply -f YOUR_CONFIG_FILE.yaml

inferenceservice.serving.kserve.io/mlflow-wine-classifier created

您可以通过运行以下命令检查部署状态

$ kubectl get inferenceservice mlflow-wine-classifier

NAME URL READY PREV LATEST PREVROLLEDOUTREVISION LATESTREADYREVISION
mlflow-wine-classifier http://mlflow-wine-classifier.mlflow-kserve-test.local True 100 mlflow-wine-classifier-100
注意

部署状态可能需要几分钟才能变为就绪。有关详细的部署状态和日志,请运行 kubectl get inferenceservice mlflow-wine-classifier -oyaml

测试部署

部署就绪后,您可以向服务器发送测试请求。

首先,创建一个包含测试数据的 JSON 文件,并将其保存为 test-input.json。确保请求数据符合 V2 推理协议格式,因为我们使用 protocolVersion: v2 创建了模型。请求应如下所示

{
"inputs": [
{
"name": "input",
"shape": [13],
"datatype": "FP32",
"data": [14.23, 1.71, 2.43, 15.6, 127.0, 2.8, 3.06, 0.28, 2.29, 5.64, 1.04, 3.92, 1065.0]
}
]
}

然后将请求发送到您的推理服务

假设您的集群通过 LoadBalancer 公开,请按照这些说明查找 Ingress IP 和端口。然后使用 curl 命令发送测试请求

$ SERVICE_HOSTNAME=$(kubectl get inferenceservice mlflow-wine-classifier -n mlflow-kserve-test -o jsonpath='{.status.url}' | cut -d "/" -f 3)
$ curl -v \
-H "Host: ${SERVICE_HOSTNAME}" \
-H "Content-Type: application/json" \
-d @./test-input.json \
http://${INGRESS_HOST}:${INGRESS_PORT}/v2/models/mlflow-wine-classifier/infer

故障排除

如果您在部署过程中遇到任何问题,请查阅 KServe 官方文档及其MLflow 部署指南

结论

恭喜您完成本指南!在本教程中,您学习了如何使用 MLflow 训练模型、运行超参数调优并将模型部署到 Kubernetes 集群。

进一步阅读: