在 Azure 上大规模部署模型(第一部分):部署 XGBoost 模型





5.00/5 (2投票s)
如何使用 Azure App Service、Flask、FastAPI 和机器学习在线终结点发布 XGBoost 和 PyTorch 模型
机器学习 (ML)、数据科学和 Python 语言在近几年越来越受欢迎并具有相关性。截至 2021 年,Python 比 Java、C# 和 C++ 等老牌语言更受欢迎。它在 2021 年 11 月的 TIOBE 指数中位居榜首,比同年 9 月的第二名有所提升。在 StackOverflow 的 2021 年开发者调查中,Python 也是第三大最受欢迎的编程语言。
每当您想到机器学习代码时,您最有可能想到的是 Python。您几乎可以在任何设备上运行 Python 代码:PC、Mac 或 Raspberry Pi,使用 x86、x86-64、AMD64 或 ARM/ARM64 处理器,在 Windows、Linux 或 macOS 操作系统上。
机器学习通常需要大量的处理能力,这可能会超出您当前计算机的能力。幸运的是,这并不重要,只要您能够访问互联网和云。Azure 是运行几乎任何规模的 Python ML 工作负载的绝佳场所。
我们在之前的系列中使用了 Python 和 Azure 服务来训练了一些机器学习模型。在本系列的三部分中,我们将把这些模型发布到 Azure。稍后我们将部署 PyTorch 和 TensorFlow 模型。但在本文中,我们将演示如何使用托管终结点部署 XGBoost 模型。
我们的目标是为机器学习模型创建自定义的 REST API 服务,用于实时推理。我们将使用经过训练的模型来识别手写数字,使用著名的 MNIST 数据集。
我们将首先使用 Python、Flask 和 Azure App Service 发布 XGBoost 模型。然后,我们将使用相同的模型创建一个在线终结点,这是一个相对较新的 Azure Machine Learning 功能,目前仍处于预览状态。
我们依赖 Azure CLI 来编写易于理解且可重复的脚本,这些脚本可以与我们的其他代码一起存储和版本化。在本篇文章的示例代码、脚本、模型和一些测试图像中,您可以在专用的 GitHub 仓库中找到它们。
入门
您需要 Visual Studio Code、最近的 Python 版本(3.7+)以及用于 Python 包管理的 Conda 来学习本教程。如果您没有其他偏好,可以从 Miniconda 和 Python 3.9 开始。
安装 Conda 后,创建一个新环境并激活它。
$ conda create -n azureml python=3.9
$ conda activate azureml
除了 Python 和 Conda,我们还使用 2.15.0 或更高版本的 Azure 命令行工具,以及机器学习扩展。
$ az extension add -n ml -y
最后但同样重要的是,如果您还没有 Azure 帐户,请注册一个 Azure 帐户。您将获得免费积分和许多服务的访问权限。
在所有这些资源都准备就绪后,登录到您的订阅。
$ az login
设置以下环境变量供脚本使用
export AZURE_SUBSCRIPTION="<your-subscription-id>"
export RESOURCE_GROUP="azureml-rg"
export AML_WORKSPACE="demo-ws"
export LOCATION="westeurope"
如果您没有遵循上一个系列中的示例,您还需要使用此命令创建一个 Azure Machine Learning 工作区。
$ az ml workspace create --name $AML_WORKSPACE --subscription $AZURE_SUBSCRIPTION
--resource-group $RESOURCE_GROUP --location $LOCATION
现在,我们准备开始准备模型以进行部署。
注册模型
由于我们的模型很小,我们可以将其与应用程序代码捆绑并进行部署。但这并非最佳实践。现实生活中的模型可能非常大。几兆字节甚至几千兆字节并不少见!此外,我们应该独立于代码来管理模型的版本。
因此,我们使用 Azure Machine Learning 工作区内置的模型注册表。我们在上一个系列第一篇文章中已经将其保存在那里。如果您还没有,可以轻松完成
$ az ml model create --name "mnist-xgb-model" --local-path "./mnist.xgb_model"
--subscription $AZURE_SUBSCRIPTION --resource-group $RESOURCE_GROUP
--workspace-name $AML_WORKSPACE
请注意,我们将模型的名称从“mnist.xgb_model
”更改为“mnist-xgb-model
”。模型名称不能包含句点。
每次执行此命令时,它都会添加一个新模型版本,无论您上传的是否是同一个文件。
当您登录 Azure Machine Learning Studio 时,它应该会列出所有版本,如下图所示。
构建模型推理代码
我们的 REST API 服务的核心部分是加载模型并在图像数据上运行推理的代码。
我们将代码保存在 inference_model.py 文件中。
import numpy
import xgboost as xgb
from PIL import Image
class InferenceModel():
def __init__(self, model_path):
self.model = xgb.XGBRFClassifier()
self.model.load_model(model_path)
def _preprocess_image(self, image_bytes):
image = Image.open(image_bytes)
# Reshape the image and convert it to monochrome
image = image.resize((28,28)).convert('L')
# Normalize data
image_np = (255 - numpy.array(image.getdata())) / 255.0
# Return image data reshaped to a vector
return image_np.reshape(1, -1)
def predict(self, image_bytes):
image_data = self._preprocess_image(image_bytes)
prediction = self.model.predict(image_data)
return prediction
此文件包含一个名为 InferenceModel
的单个类,其中包含三个方法:__init__
、_preprocess_image
和 predict
。__init__
方法从文件中加载模型并将其存储以供将来使用。_preprocess_image
方法将图像调整大小并将其转换为模型期望的格式。在我们的例子中,它是一个 784 个浮点数的向量,范围在 0.0 到 1.0 之间。
predict
方法对提供的图像数据运行推理。
创建 Flask 服务代码
现在我们有了处理预测的代码,我们可以在 REST API 中使用它。首先,我们将创建一个定制的 Flask 服务来处理这项工作。
我们从新文件 app.py 中的 import
开始。
import json
from flask import Flask, request
from inference_model import InferenceModel
from azureml.core.authentication import MsiAuthentication
from azureml.core import Workspace
from azureml.core.model import Model
我们使用 MsiAuthentication 类进行身份验证,以便访问我们的 Azure Machine Learning 工作区资源。MsiAuthentication
类依赖于 Azure Active Directory (AD) 中的托管标识。我们将托管标识分配给 Azure 资源,例如虚拟机 (VM) 或 App Service。使用托管标识使我们无需维护凭据和密钥。
接下来,我们创建一个方法从 Azure Machine Learning 模型注册表中加载我们的模型。
def get_inference_model():
global model
if model == None:
auth = MsiAuthentication()
ws = Workspace(subscription_id="<your-subscription-id>",
resource_group="azureml-rg",
workspace_name="demo-ws",
auth=auth)
aml_model = Model(ws, 'mnist-xgb-model', version=1)
model_path = aml_model.download(target_dir='.', exist_ok=True)
model = InferenceModel(model_path)
return model
model = None
我们在该方法中使用一个 global
变量,以确保模型只加载一次。
为了加载模型,我们使用 Azure ML 的 Workspace
和 Model
类。最后,我们将下载的模型文件路径传递给 InferenceModel
类。
有了所有这些组件,我们就可以添加代码来使用 Flask
服务我们的模型了。
app = Flask(__name__)
@app.route("/score", methods=['POST'])
def score():
image_data = request.files.get('image')
model = get_inference_model()
preds = model.predict(image_data)
return json.dumps({"preds": preds.tolist()})
在通过 app
变量声明我们的 Flask
应用程序后,我们定义了到 score
方法的路由。此方法读取提供的图像,获取推理模型,并运行预测。最后,它以 JSON 格式返回预测结果。
使用 Python 和 Flask 发布 App Service
我们几乎有了在 Azure 上使用 App Service 和 Python 和 Flask 运行 REST API 服务的所有代码。我们需要最后的文件是 requirements.txt,内容如下:
Flask==1.0.2
gunicorn==19.9.0
xgboost==1.5.0
pillow==8.3.2
numpy==1.19.5
scikit-learn==1.0.1
azureml-core==1.35.0
现在,我们可以使用 Azure CLI 发布我们的 App Service 应用程序,使用来自包含我们文件的文件夹的以下命令:
$ export APP_SERVICE_PLAN="<your-app-service-plan-name>"
$ export APP_NAME="<your-app-name>"
$ az webapp up --name $APP_NAME --plan $APP_SERVICE_PLAN --sku F1
--os-type Linux --runtime "python|3.8" --subscription $AZURE_SUBSCRIPTION
--resource-group $RESOURCE_GROUP
请注意,APP_NAME
值必须是全局唯一的,因为它将成为服务 URL 的一部分。
此命令会创建一个 App Service 计划和 App Service,将我们的本地文件上传到 Azure,并启动应用程序。不过,这可能需要一些时间,有时可能会无缘无故地失败。在极少数情况下,即使部署似乎已成功完成,服务也无法正确识别您的代码为 Flask 应用程序,从而导致服务无法使用。
如有疑问,您可以检查应用程序的部署中心选项卡中的部署进度和日志。
如果存在问题,特别是与超时相关的问题,只需重试部署步骤。
命令完成后,您应该会收到类似以下的 JSON。
{
"URL": "http://<your-app-name>.azurewebsites.net",
"appserviceplan": "<your-app-service-plan-name>",
"location": "centralus",
"name": "<your-app-name>",
"os": "Linux",
"resourcegroup": "azureml-rg",
"runtime_version": "python|3.8",
"runtime_version_detected": "-",
"sku": "FREE",
"src_path": "<local-path-to-your-files>"
}
请记住“URL
”值。我们很快就需要它来调用服务。
授予 App Service 应用程序权限
我们的服务需要从我们的 Azure Machine Learning 工作区下载已训练的模型。然而,此操作需要授权。我们使用托管标识来避免凭据管理。
为我们的应用程序分配新的托管标识非常简单。添加以下代码:
$ az webapp identity assign --name $APP_NAME --resource-group $RESOURCE_GROUP
完成后,我们应该会收到一个 JSON。
{
"principalId": "<new-principal-id>",
"tenantId": "<tenant-id>",
"type": "SystemAssigned",
"userAssignedIdentities": null
}
当我们有了返回的“<new-principal-id>
”值后,就可以添加所需的权限了。
$ az role assignment create --role reader --assignee <new-principal-id>
--scope /subscriptions/$AZURE_SUBSCRIPTION/resourceGroups/$RESOURCE_GROUP/providers/
Microsoft.MachineLearningServices/workspaces/$AML_WORKSPACE
对于我们的目的,读取者角色应该足够了。如果我们的应用程序需要创建其他资源,则应改用参与者角色。
测试服务
使用我们已经用于训练模型的图像将不会很有趣。所以,让我们使用一些新的图像。您可以在示例代码文件夹中找到它们,文件名为 test-images/d0.png 到 d9.png。它们看起来像这样:
这些图像没有严格的要求。我们的服务的代码将调整它们的大小并将其转换为预期的格式。
我们需要发送 POST
请求来调用我们的服务。我们可以使用 Postman 或 curl。我们可以直接从命令行使用 curl
执行我们的请求。
$ curl -X POST -F 'image=@./test-images/d6.png' https://$APP_NAME.azurewebsites.net/score
如果一切顺利,我们可以预期收到以下响应:
{"preds": [6]}
太棒了!在我们的简单 XGBoost 模型中,答案似乎是正确的。但这不会是常态。
检查 App Service 日志
如果您遇到任何问题,可能需要检查您的服务日志。您可以通过 Azure 门户的监控 > 日志流选项卡进行此操作。
或者,您也可以使用 Azure CLI 访问日志。
$ az webapp log tail --name $APP_NAME --subscription $AZURE_SUBSCRIPTION
--resource-group $RESOURCE_GROUP
使用 Azure Machine Learning 和托管终结点
使用作为 Azure App Service 托管的自定义 Flask 应用程序发布我们的模型非常简单。通过这样做,我们可以体验所有标准的 App Service 优势,例如手动或自动缩放,具体取决于定价套餐。
尽管如此,在使用 App Service 解决方案时,我们需要编写和维护与模型推理相关的代码以及负责将其作为 REST 服务进行提供、安全性和其他低级细节的代码。
Azure Machine Learning (仍处于预览状态) 提供的新选择之一是托管终结点机制。在以下部分中,我们将使用我们的模型创建一个在线终结点。
创建在线终结点代码
我们将把与 Flask 应用程序相同的 InferenceModel
类复制到新文件夹 endpoint-code 中的 inference_model.py 文件中。
与之前的 Flask
应用程序一样,我们需要一些代码来定义我们的服务。我们将代码放在 endpoint-code/aml-score.py 文件中。这次,我们可以依赖 Azure Machine Learning 的内置机制。
import os
import json
from inference_model import InferenceModel
from azureml.contrib.services.aml_request import rawhttp
from azureml.contrib.services.aml_response import AMLResponse
def init():
global model
model_path = os.path.join(
os.getenv("AZUREML_MODEL_DIR"), "mnist.xgb_model"
)
model = InferenceModel(model_path)
@rawhttp
def run(request):
if request.method != 'POST':
return AMLResponse(f"Unsupported verb: {request.method}", 400)
image_data = request.files['image']
preds = model.predict(image_data)
return AMLResponse(json.dumps({"preds": preds.tolist()}), 200)
我们只需要两个方法。代码在终结点启动时调用第一个方法 init
。这是加载模型的绝佳位置。我们使用 AZUREML_MODEL_DIR
环境变量来执行此操作,该变量指示我们模型文件的位置。
以下 run
方法非常简单。首先,我们确保只接受 POST
请求。然后,实际逻辑开始:我们从请求中检索图像,然后运行并返回预测。
请注意 @rawhttp
装饰器。我们需要它来访问原始请求数据(在我们的例子中是二进制图像内容)。没有它,传递给 run
方法的 request
参数将仅限于解析后的 JSON。
配置在线终结点
除了代码,我们还需要三个配置文件。第一个是 endpoint-code/aml-env.yml,它存储 Conda 环境定义。
channels:
- conda-forge
- defaults
dependencies:
- python=3.7.10
- numpy=1.19.5
- xgboost=1.5.0
- scikit-learn=1.0.1
- pillow=8.3.2
- pip
- pip:
- azureml-defaults==1.35.0
- inference-schema[numpy-support]==1.3.0
接下来的两个文件包含终结点及其部署的配置。
终结点配置文件 aml-endpoint.yml 包含:
$schema: https://azuremlschemas.azureedge.net/latest/managedOnlineEndpoint.schema.json
name: mnistxgboep
auth_mode: key
除了终结点名称(我们可以在脚本中更改)之外,此文件仅包含一个有意义的行。它将 auth_mode
设置为 key,以便我们在调用终结点时使用随机生成的“secret
” string
作为身份验证令牌。
最后一个文件 aml-endpoint-deployment.yml 包含以下代码:
$schema: https://azuremlschemas.azureedge.net/latest/managedOnlineDeployment.schema.json
name: blue
endpoint_name: mnistxgboep
model: azureml:mnist-xgb-model:1
code_configuration:
code:
local_path: ./endpoint-code
scoring_script: aml-score.py
environment:
conda_file: ./endpoint-code/aml-env.yml
image: mcr.microsoft.com/azureml/minimal-ubuntu18.04-py37-cpu-inference:latest
instance_type: Standard_F2s_v2
instance_count: 1
在部署配置中,我们设置了关键详细信息,例如模型名称和版本。code_configuration
部分指示了我们终结点的路径及其主脚本文件。请注意,您需要明确定义模型版本,以便代码自动下载并保存此确切的模型版本,为我们的服务做好准备。
接下来,我们定义 Docker 镜像、Conda 环境配置文件、VM 实例大小和实例计数。
创建托管在线终结点
所有这些文件都准备就绪后,我们就可以开始部署了。我们从终结点开始。
$ export ENDPOINT_NAME="<your-endpoint-name>"
$ az ml online-endpoint create -n $ENDPOINT_NAME -f aml-endpoint.yml
--subscription $AZURE_SUBSCRIPTION --resource-group $RESOURCE_GROUP
--workspace-name $AML_WORKSPACE
与之前的 App Service 应用程序名称一样,终结点名称在每个 Azure 区域中必须是唯一的。它将成为 URL 的一部分,格式如下:
https://<your-endpoint-name>.<region-name>.inference.ml.azure.com/score
创建终结点后,我们终于可以部署推理代码了。
$ az ml online-deployment create -n blue
--endpoint $ENDPOINT_NAME -f aml-endpoint-deployment.yml
--all-traffic --subscription $AZURE_SUBSCRIPTION
--resource-group $RESOURCE_GROUP --workspace-name $AML_WORKSPACE
经过一段时间后,命令应返回确认部署已完成且终结点已准备好使用的消息。现在,我们可以检查它的工作原理。
测试在线终结点
我们需要终结点 URL、终结点密钥和用于调用终结点的示例图像。我们已经使用了示例图像,因此只需要终结点 URL 和密钥。获取它们的一种方法是通过 Azure Machine Learning Studio,在终结点的消耗选项卡中。
一如既往(几乎),我们也可以使用 Azure CLI 获取这些值。
$ SCORING_URI=$(az ml online-endpoint show -n $ENDPOINT_NAME -o tsv
--query scoring_uri --resource-group $RESOURCE_GROUP --workspace $AML_WORKSPACE)
$ ENDPOINT_KEY=$(az ml online-endpoint get-credentials --name $ENDPOINT_NAME
--subscription $AZURE_SUBSCRIPTION --resource-group $RESOURCE_GROUP
--workspace-name $AML_WORKSPACE -o tsv --query primaryKey)
无论如何,在填充了 SCORING_URI
和 ENDPOINT_KEY
变量后,我们就可以调用我们的服务了。
$ curl -X POST -F 'image=@./test-images/d6.png'
-H "Authorization: Bearer $ENDPOINT_KEY" $SCORING_URI
如果一切顺利,我们应该得到与我们 Flask
应用程序相同的答案。
{"preds": [6]}
删除 Azure 资源
您可以删除不再需要的所有资源,以减少 Azure 费用。特别是要记住 App Service 计划和托管终结点(尽管在本例中我们使用了免费的 App Service 计划)。
后续步骤
我们已经成功地发布了一个用于识别手写数字的 XGBoost 模型,使用了 Flask 与 App Service,以及托管的在线终结点。下一篇文章类似,但我们将使用 PyTorch 而不是 XGBoost,并使用 FastAPI
而不是 Flask
。此外,我们将快速浏览 FastAPI
代码,重点关注一些额外的托管在线终结点功能。
继续阅读本系列的第二部分,了解如何部署和扩展 PyTorch 模型。
要了解如何使用在线终结点(预览版)部署机器学习模型,请参阅使用在线终结点部署和评分机器学习模型。