跳到主要内容

快速入门:比较运行、选择模型并将其部署到 REST API

在本快速入门中,你将学习

  • 对训练脚本运行超参数搜索
  • 在 MLflow UI 中比较运行结果
  • 选择最佳运行并将其注册为模型
  • 将模型部署到 REST API
  • 构建适合部署到云平台的容器镜像

作为 ML 工程师或 MLOps 专业人员,你可以使用 MLflow 比较、共享和部署团队生成的最佳模型。在本快速入门中,你将使用 MLflow Tracking UI 比较超参数搜索的结果,选择最佳运行,并将其注册为模型。然后,你将把模型部署到 REST API。最后,你将创建一个适合部署到云平台的 Docker 容器镜像。

Diagram showing Data Science and MLOps workflow with MLflow

设置

有关如何设置 MLflow 环境以及如何配置 MLflow tracking 功能的详细指南,你可以在此处阅读该指南。

运行超参数搜索

本示例尝试优化基于酒质数据集的 Keras 深度学习模型的 RMSE 指标。它尝试优化两个超参数:learning_ratemomentum。我们将使用 Hyperopt 库对 learning_ratemomentum 的不同值运行超参数搜索,并将结果记录在 MLflow 中。

在运行超参数搜索之前,让我们将 MLFLOW_TRACKING_URI 环境变量设置为 MLflow tracking 服务器的 URI

export MLFLOW_TRACKING_URI=http://localhost:5000
注意

如果你想探索其他 tracking 服务器部署的可能性,包括使用 Databricks 免费试用提供的完全托管的免费解决方案,请参阅此页面

导入以下包

import keras
import numpy as np
import pandas as pd
from hyperopt import STATUS_OK, Trials, fmin, hp, tpe
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split

import mlflow
from mlflow.models import infer_signature

现在加载数据集并将其拆分为训练集、验证集和测试集。

# Load dataset
data = pd.read_csv(
"https://raw.githubusercontent.com/mlflow/mlflow/master/tests/datasets/winequality-white.csv",
sep=";",
)

# Split the data into training, validation, and test sets
train, test = train_test_split(data, test_size=0.25, random_state=42)
train_x = train.drop(["quality"], axis=1).values
train_y = train[["quality"]].values.ravel()
test_x = test.drop(["quality"], axis=1).values
test_y = test[["quality"]].values.ravel()
train_x, valid_x, train_y, valid_y = train_test_split(
train_x, train_y, test_size=0.2, random_state=42
)
signature = infer_signature(train_x, train_y)

然后定义模型架构并训练模型。train_model 函数使用 MLflow 将每次试验的参数、结果和模型本身作为子运行进行跟踪。

def train_model(params, epochs, train_x, train_y, valid_x, valid_y, test_x, test_y):
# Define model architecture
mean = np.mean(train_x, axis=0)
var = np.var(train_x, axis=0)
model = keras.Sequential(
[
keras.Input([train_x.shape[1]]),
keras.layers.Normalization(mean=mean, variance=var),
keras.layers.Dense(64, activation="relu"),
keras.layers.Dense(1),
]
)

# Compile model
model.compile(
optimizer=keras.optimizers.SGD(
learning_rate=params["lr"], momentum=params["momentum"]
),
loss="mean_squared_error",
metrics=[keras.metrics.RootMeanSquaredError()],
)

# Train model with MLflow tracking
with mlflow.start_run(nested=True):
model.fit(
train_x,
train_y,
validation_data=(valid_x, valid_y),
epochs=epochs,
batch_size=64,
)
# Evaluate the model
eval_result = model.evaluate(valid_x, valid_y, batch_size=64)
eval_rmse = eval_result[1]

# Log parameters and results
mlflow.log_params(params)
mlflow.log_metric("eval_rmse", eval_rmse)

# Log model
mlflow.tensorflow.log_model(model, "model", signature=signature)

return {"loss": eval_rmse, "status": STATUS_OK, "model": model}

objective 函数接收超参数并返回该组超参数对应的 train_model 函数结果。

def objective(params):
# MLflow will track the parameters and results for each run
result = train_model(
params,
epochs=3,
train_x=train_x,
train_y=train_y,
valid_x=valid_x,
valid_y=valid_y,
test_x=test_x,
test_y=test_y,
)
return result

接下来,我们将为 Hyperopt 定义搜索空间。在这种情况下,我们想尝试不同的 learning-ratemomentum 值。Hyperopt 通过选择一组初始超参数来开始其优化过程,这些超参数通常是随机选择的或基于指定的域空间。该域空间定义了每个超参数可能值的范围和分布。在评估初始集后,Hyperopt 使用结果更新其概率模型,以更明智的方式指导后续超参数集的选择,旨在收敛到最优解。

space = {
"lr": hp.loguniform("lr", np.log(1e-5), np.log(1e-1)),
"momentum": hp.uniform("momentum", 0.0, 1.0),
}

最后,我们将使用 Hyperopt 运行超参数搜索,传入 objective 函数和搜索空间。Hyperopt 将尝试不同的超参数组合并返回最佳结果。我们将最佳参数、模型和评估指标存储在 MLflow 中。

mlflow.set_experiment("/wine-quality")
with mlflow.start_run():
# Conduct the hyperparameter search using Hyperopt
trials = Trials()
best = fmin(
fn=objective,
space=space,
algo=tpe.suggest,
max_evals=8,
trials=trials,
)

# Fetch the details of the best run
best_run = sorted(trials.results, key=lambda x: x["loss"])[0]

# Log the best parameters, loss, and model
mlflow.log_params(best)
mlflow.log_metric("eval_rmse", best_run["loss"])
mlflow.tensorflow.log_model(best_run["model"], "model", signature=signature)

# Print out the best parameters and corresponding loss
print(f"Best parameters: {best}")
print(f"Best eval rmse: {best_run['loss']}")

比较结果

在浏览器中打开 MLFLOW_TRACKING_URI 对应的 MLflow UI。你应该会看到一个嵌套的运行列表。在默认的表格视图中,选择按钮,并添加指标 | eval_rmse 列以及参数 | lr参数 | momentum 列。要按 RMSE 升序排序,点击 eval_rmse 列标题。最佳运行通常在测试数据集上的 RMSE 约为 0.70。你可以在参数列中看到最佳运行的参数。

Screenshot of MLflow tracking UI table view showing runs

选择图表视图。选择平行坐标图,并配置它显示 lrmomentum 坐标以及 eval_rmse 指标。该图中的每条线代表一次运行,并将每次超参数评估运行的参数与其评估的误差指标关联起来。

Screenshot of MLflow tracking UI parallel coordinates graph showing runs

该图中的红色线条表示表现不佳的运行。最低的一条是 lrmomentum 都设置为 0.0 的基线运行。该基线运行的 RMSE 约为 0.89。其他红色线条表明,高 momentum 在此问题和架构下也可能导致结果不佳。

偏向蓝色的线条表示表现更好的运行。将鼠标悬停在单个运行上可以查看其详细信息。

注册你的最佳模型

选择最佳运行并将其注册为模型。在表格视图中,选择最佳运行。在运行详情页面,打开Artifacts部分,选择注册模型按钮。在注册模型对话框中,输入模型的名称,例如 wine-quality,然后点击注册

现在,你的模型已可用于部署。你可以在 MLflow UI 的模型页面中看到它。打开你刚刚注册的模型页面。

你可以为模型添加描述、添加标签,并轻松导航回生成该模型的源运行。你还可以将模型转换到不同的阶段(stage)。例如,你可以将模型转换到Staging阶段,表示它已准备好进行测试。你可以将其转换到Production阶段,表示它已准备好进行部署。

通过选择Stage下拉菜单将模型转换到Staging阶段

Screenshot of MLflow tracking UI models page showing the registered model

在本地提供模型服务

MLflow 允许你轻松地为任何运行或模型版本生成的模型提供服务。你可以通过运行以下命令为刚刚注册的模型提供服务

mlflow models serve -m "models:/wine-quality/1" --port 5002

(请注意,如果你在同一台机器上以默认端口 5000 运行 tracking 服务器,则需要如上所示指定端口。)

你也可以使用 runs:/<run_id> URI 提供模型服务,或者使用 Artifact Store 中描述的任何支持的 URI。

请注意,在生产环境中,由于资源限制,我们不建议将模型部署在与 tracking 服务器相同的虚拟机上;在本指南中,为了简化,我们只是在同一台机器上运行所有内容。

要测试模型,你可以使用 curl 命令向 REST API 发送请求

curl -d '{"dataframe_split": {
"columns": ["fixed acidity","volatile acidity","citric acid","residual sugar","chlorides","free sulfur dioxide","total sulfur dioxide","density","pH","sulphates","alcohol"],
"data": [[7,0.27,0.36,20.7,0.045,45,170,1.001,3,0.45,8.8]]}}' \
-H 'Content-Type: application/json' -X POST localhost:5002/invocations

推理(Inferencing)是通过向 localhost 上指定端口的 invocations 路径发送 JSON POST 请求完成的。columns 键指定输入数据中的列名。data 值是列表的列表,其中每个内部列表是一行数据。为简洁起见,上面仅请求对酒质(范围 3-8)进行一次预测。响应是一个 JSON 对象,其 predictions 键包含一个预测列表,每行数据对应一个预测。在这种情况下,响应是

{ "predictions": [{ "0": 5.310967445373535 }] }

输入和输出的 schema 在 MLflow UI 的Artifacts | Model 描述中可用。schema 可用是因为 train.py 脚本使用了 mlflow.infer_signature 方法并将结果传递给了 mlflow.log_model 方法。强烈建议将 signature 传递给 log_model 方法,因为它可以在输入请求格式不正确时提供清晰的错误消息。

为你的模型构建容器镜像

大多数部署途径都会使用容器来打包你的模型、其依赖项以及运行时环境的相关部分。你可以使用 MLflow 为你的模型构建 Docker 镜像。

mlflow models build-docker --model-uri "models:/wine-quality/1" --name "qs_mlops"

此命令构建一个名为 qs_mlops 的 Docker 镜像,其中包含你的模型及其依赖项。在这种情况下,model-uri 指定的是版本号(/1),而不是生命周期阶段(/staging),但你可以使用与你的工作流程最契合的方式。构建镜像需要几分钟。完成后,你可以运行该镜像以在本地、本地部署(on-prem)、定制的 Internet 服务器或云平台提供实时推理。你可以在本地使用以下命令运行它

docker run -p 5002:8080 qs_mlops

Docker run 命令运行你刚刚构建的镜像,并将本地机器上的端口 5002 映射到容器中的端口 8080。你现在可以使用与之前相同的 curl 命令向模型发送请求

curl -d '{"dataframe_split": {"columns": ["fixed acidity","volatile acidity","citric acid","residual sugar","chlorides","free sulfur dioxide","total sulfur dioxide","density","pH","sulphates","alcohol"], "data": [[7,0.27,0.36,20.7,0.045,45,170,1.001,3,0.45,8.8]]}}' -H 'Content-Type: application/json' -X POST localhost:5002/invocations

部署到云平台

几乎所有云平台都允许你部署 Docker 镜像。部署过程差异很大,因此你需要查阅云提供商的文档以获取详细信息。

此外,一些云提供商对 MLflow 提供内置支持

云平台通常支持多种部署工作流程:命令行、基于 SDK 和基于 Web。你可以在任何这些工作流程中使用 MLflow,尽管详细信息因平台和版本而异。同样,你需要查阅云提供商的文档以获取详细信息。