目录
这是3部分系列文章的第3部分,演示如何使用各种Python AI框架构建的AI模型,并使用Azure ML托管终结点部署和扩展它们。在本文中,我们使用在线端点发布TensorFlow模型。然后,我们创建一个Azure函数作为此端点的公共代理。此外,我们探索了托管端点的配置选项,例如自动缩放和蓝绿部署概念。
Azure可帮助运行几乎任何规模的机器学习(ML)工作负载,因此您的模型不再受计算机处理器的限制。在这个三部分系列的前几篇文章中,我们使用Azure App Service、Flask、FastAPI和机器学习在线端点发布了XGBoost和PyTorch模型。在本文中,我们将继续使用上一个系列的最后一个模型:TensorFlow模型。
我们的目标是为机器学习模型的实时推理创建自定义Rest API服务。我们将使用经过训练的模型,使用众所周知的MNIST数据集来识别手写数字。
首先,我们将使用Azure机器学习在线端点发布一个TensorFlow模型。然后,我们将创建一个Azure函数来充当我们应用服务的公共代理,说明如何在代码中使用在线终结点。
我们依靠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命令行工具(azure-cli)以及机器学习扩展。
$ az extension add -n ml -y
最后但同样重要的是,如果您还没有Azure帐户,请注册一个免费帐户。您的新帐户包括免费积分和对各种服务的访问。
准备好所有这些资源后,登录您的订阅。
$ az login
设置以下环境变量,我们将在脚本中使用它们:
export AZURE_SUBSCRIPTION="<your-subscription-id>"
export RESOURCE_GROUP="azureml-rg"
export AML_WORKSPACE="demo-ws"
export LOCATION="westeurope"
如果您没有遵循上一个系列中的示例,您还需要创建一个Azure机器学习工作区:
$ az ml workspace create --name $AML_WORKSPACE --subscription $AZURE_SUBSCRIPTION
--resource-group $RESOURCE_GROUP --location $LOCATION
现在,我们已准备好开始准备模型部署。
注册模型
为了避免将模型与我们的应用程序绑定和部署,我们使用Azure机器学习工作区中内置的模型注册表。如果您已经阅读了上一个系列的第三篇文章 ,那么您的TensorFlow模型可能已经存在。如果没有,请使用以下命令:
$ az ml model create --name "mnist-tf-model" --local-path "./mnist-tf-model.h5"
--subscription $AZURE_SUBSCRIPTION --resource-group $RESOURCE_GROUP
--workspace-name $AML_WORKSPACE
构建模型推理代码
与之前的文章一样,我们服务的核心部分将是加载模型并对图像数据运行推理的代码。我们将代码存储在endpoint-code/inference_model.py文件中:
import numpy as np
import tensorflow as tf
from PIL import Image
class InferenceModel():
def __init__(self, model_path):
self.model = tf.keras.models.load_model(model_path)
def _preprocess_image(self, image_bytes):
image = Image.open(image_bytes)
image = image.resize((28,28)).convert('L')
image_np = (255 - np.array(image.getdata())) / 255.0
return image_np.reshape(-1,28,28,1)
def predict(self, image_bytes):
image_data = self._preprocess_image(image_bytes)
prediction = self.model.predict(image_data)
return np.argmax(prediction, axis=1)
与本系列的前几篇文章一样,该文件包含一个类,InferenceModel,具有三个方法:__init__、_preprocess_image和predict。
该__init__方法从文件加载模型并将其存储以供以后使用。
该_preprocess_image方法调整图像大小并将其转换为可接受的格式。我们的TensorFlow模型接受单通道28x28张量,浮点数在0.0到1.0之间。
注意像素强度的反转。我们计划在标准的黑白图像上使用我们的模型,而MNIST训练数据集具有反转的黑白值。
最后一种predict方法对提供的图像数据进行推理。
创建在线端点代码
要创建托管在线端点,我们需要用端点的服务代码包装我们的InferenceModel类。我们将其存储在endpoint-code/aml-score.py文件中:
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-tf-model.h5" # model-path/model-file-name
)
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)
除了模型名称(mnist-tf-model.h5)外,其余代码与前两篇相同。
代码在端点启动时调用第一个init方法,仅一次。这是加载我们模型的好地方。为此,我们使用AZUREML_MODEL_DIR环境变量,它指示我们的模型文件的存储位置。
下面的run方法很简单。首先,我们确保它只接受POST请求。我们从请求中检索图像,然后进行run和return预测。
请注意,@rawhttp装饰器需要访问原始请求数据(在我们的例子中是二进制图像内容)。传递给该run方法的request参数将仅限于没有装饰器的解析JSON。
配置在线端点
除了代码,我们还需要三个额外的配置文件。
第一个文件endpoint-code/aml-env.yml存储了Conda环境定义:
channels:
- conda-forge
- defaults
dependencies:
- python=3.7.10
- pillow=8.3.2
- gunicorn=20.1.0
- numpy=1.19.5
- pip
- pip:
- h5py==3.1.0 # We need to use the same version we have used for training
- keras==2.6.0 # We need to add it manually,
- because a different (newer) version could be installed with the tensorflow otherwise!
- tensorflow==2.6.0
- azureml-defaults==1.35.0
- inference-schema[numpy-support]==1.3.0
请注意,要使用来自H5格式的Keras加载经过训练的模型,我们需要使用在模型训练期间使用的相同版本的h5py、Keras和TensorFlow库。这就是我们将这三个显式添加到YAML文件的原因。如果没有,其他一些版本将作为TensorFlow依赖项安装。
剩下的配置文件定义了端点和端点的部署,相当于我们在前两篇文章中介绍的文件:
文件aml-endpoint.yml:
$schema: https://azuremlschemas.azureedge.net/latest/managedOnlineEndpoint.schema.json
name: mnisttfoep
auth_mode: key
File aml-endpoint-deployment.yml:
$schema: https://azuremlschemas.azureedge.net/latest/managedOnlineDeployment.schema.json
name: blue
endpoint_name: mnisttfoep
model: azureml:mnist-tf-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
部署在线端点
准备好所有这些文件后,我们就可以开始部署了。为此,我们使用之前文章中使用的相同脚本,从端点开始:
$ 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
请记住,端点名称将成为URL的一部分,因此它必须是全局唯一的。
接下来,我们可以部署我们的推理代码:
$ 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
很长一段时间后,该命令应返回确认部署已完成且端点已准备好使用。请注意,配置VM、下载基础镜像和设置环境需要一段时间。
然后,键入以下命令以检查端点日志:
$ az ml online-deployment get-logs -n blue --endpoint $ENDPOINT_NAME
--subscription $AZURE_SUBSCRIPTION --resource-group $RESOURCE_GROUP
测试在线端点
我们使用示例代码中的图像(test-images/d0.png到d9.png)来测试我们的服务。
此外,我们需要端点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/d8.png' -H
"Authorization: Bearer $ENDPOINT_KEY" $SCORING_URI
如果一切顺利,我们应该得到预期的答案:
{"preds": [8]}
扩展托管端点
使用云而不是本地基础设施的一个好处是扩展的灵活性。新的Azure机器学习托管终结点实现了这种灵活性和扩展性。
当我们在Azure门户中访问我们的部署资源时,我们可以手动扩展它或配置自动扩展。我们在Scaling > Configure中手动设置集群大小,如下面的屏幕截图所示:
我们为自动缩放设置缩放规则。例如,当CPU内存利用率指标超过70%超过10分钟时,我们可以将实例数增加1,如下面的屏幕截图所示:
使用蓝绿部署
最后,我们来谈谈为什么我们使用蓝色作为我们的部署名称。此名称来自蓝绿部署,我们在其中部署系统的新版本(例如绿色),而之前的系统(蓝色)仍在运行并可供用户使用。绿色部署完成后,我们可以将所有流量从旧的蓝色切换到新的绿色版本。如果有任何问题,我们可以立即反转此流量切换,而无需进行耗时的回滚操作。
所有这些更改都对服务调用者隐藏。调用者不断使用相同的URL,并且可能不知道任何更改。
这种技术的演变称为金丝雀发布。主要区别在于,与蓝绿部署相反,我们不会在金丝雀版本中一次将所有流量从一个版本切换到另一个版本。相反,我们逐渐转移流量。我们可以对A/B测试使用类似的机制,我们希望在少量请求(例如,5%)上测试新服务。
托管端点本机支持所有这些方法。要添加新的部署,我们只需要调用我们的az ml online-deployment create命令,提供一个新名称,例如green:
$ az ml online-deployment create -n green
--endpoint $ENDPOINT_NAME -f aml-endpoint-deployment.yml --all-traffic
--subscription $AZURE_SUBSCRIPTION --resource-group $RESOURCE_GROUP
--workspace-name $AML_WORKSPACE
请注意,只要我们在那里使用该--all-traffic属性,所有针对我们端点的流量都会在操作完成后自动重定向到新green版本。在实践中,这意味着蓝绿部署。
如果我们更喜欢金丝雀版本,我们可以在部署完成后设置流量拆分。
这种方法的主要缺点是增加了Azure资源的使用。在每个版本期间和之后,已使用和尚未使用的VM实例都会影响Azure vCPU配额和生成的成本。只要您不断扩展部署以匹配您当前的流量分配,您就可以使用金丝雀版本限制这种影响。
从Azure函数访问在线终结点
在现实生活中的生产系统中,例如Web应用程序,您很可能不会将在线端点直接暴露给公共客户端。通常,端点作为微服务位于防火墙后面,其他服务使用它们。在本文的最后一部分,我们将创建一个简单的Azure Function应用程序来使用在线终结点并充当其公共代理。
我们将使用带有Azure Functions扩展的Visual Studio Code :
安装扩展后,我们需要选择Azure选项卡,然后单击Functions部分中的New Project图标:
接下来,我们为我们的项目选择一个文件夹、编程语言、函数模板、第一个函数名称,最后是函数的授权级别。
我们选择Python语言、名为AmlMnistHttpTrigger的HTTP触发器模板和匿名授权级别。
稍后,我们的函数将填充示例代码。
现在,我们需要做的就是添加一个import并替换__init__.py文件的内容:
import requests
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
image_file = req.files['image']
files = { "image": image_file }
url = '<your-endpoint-uri>'
api_key = '<your-api-key>'
headers = {'Authorization':('Bearer '+ api_key)}
response = requests.post(url, files=files, headers=headers)
return func.HttpResponse(response.content, status_code=response.status_code)</your-api-key></your-endpoint-uri>
主要方法的逻辑在这里很简单。首先,它从请求中提取图像文件,然后创建并执行对我们托管端点URL的请求,将图像和标头与api_key值一起传递。URL与api_key都是我们之前在curl中使用的值。
为了便于理解,这个例子被简化了。在实践中,您至少应该改变两件事。首先,您不应该将api_key值直接包含在代码中。相反,请考虑使用Key Vault或类似机制来存储您的机密。其次,您可能需要一些异常处理和日志记录。
为了使我们的函数正常工作,我们还需要通过添加requests库来更新函数应用程序文件夹中的requirements.txt文件:
azure-functions
requests
现在我们有了代码,我们可以通过选择Deploy to Function App来发布我们的函数。
在为我们的Azure函数、运行时堆栈(例如Python 3.9)和位置选择一个全局唯一名称后,部署过程在后台开始。部署完成后,新触发器的URL将显示在输出日志的末尾。
我们还可以使用Azure门户检查我们的函数并获取其 URL。在Code + Test中,选择Get function URL。
现在,我们可以调用我们的函数:
$ curl -X POST -F 'image=@test-images/d4.png' <your-function-url>
就是这样!因为我们的函数接受匿名调用者,所以我们不需要在请求中传递任何授权令牌。
删除Azure资源
删除不再需要的所有资源以减少Azure费用。特别要记住托管终结点和Azure Functions。你可能已经使用函数应用的名称在专用资源组中创建了Azure Functions。
下一步
我们在本文中发布了一个经过训练可识别手写数字的TensorFlow模型。首先,我们使用Azure机器学习在线端点并解释了基本的相关扩展和部署选项。接下来,我们以简单的Azure函数为例,演示了如何从您的代码中调用此端点。
使用Azure部署XGBoost、PyTorch或TensorFlow模型非常简单。此外,您还可以通过运行模型所需的所有处理能力访问无限扩展。
既然您知道它有多么简单,请注册并使用Azure来部署您自己的机器学习模型。不需要运营团队。
要了解如何使用在线端点(预览版)部署机器学习模型,请查看使用在线端点部署机器学习模型并对其进行评分。
https://www.codeproject.com/Articles/5328265/Deploying-Models-at-Scale-on-Azure-Part-3-Deployin