使用 Azure 机器学习推理 HTTP 服务器调试评分脚本

Azure 机器学习推理 HTTP 服务器是一个 Python 包,它以 HTTP 终结点的形式公开评分函数,并将 Flask 服务器代码和依赖项包装到单一包中。 该服务器包含在用于推理的预生成 Docker 映像中,通过 Azure 机器学习部署模型时,会使用这些映像。 单独使用包,可以在本地部署模型以供生产,并在本地开发环境中轻松验证评分(入口)脚本。 如果评分脚本有问题,服务器将返回错误和出错位置。

该服务器还可以用于在持续集成和部署管道中创建验证入口。 例如,可以使用候选脚本启动服务器,并针对本地终结点运行测试套件。

本文帮助开发人员使用推理服务器进行本地调试,并介绍如何在 Windows 上将推理服务器与联机终结点配合使用。

先决条件

若要使用 Azure 机器学习推理 HTTP 服务器进行本地调试,配置中必须包含以下组件:

  • Python 3.8 或更高版本
  • Anaconda

Azure 机器学习推理服务器在基于 Windows 和 Linux 的操作系统上运行。

了解联机终结点的本地调试选项

在部署到云之前在本地调试终结点,可以提前发现代码和配置中的错误。 若要在本地调试终结点,可以使用多个选项,包括:

本文介绍如何在 Windows 上使用 Azure 机器学习推理 HTTP 服务器。

下表概述了各种方案,以帮助你选择最佳选项:

场景 推理 HTTP 服务器 本地终结点
更新本地 Python 环境,而无需重新生成 Docker 映像
更新评分脚本
更新部署配置(部署、环境、代码、模型)
集成 Microsoft Visual Studio Code (VS Code) 调试器

在本地运行推理 HTTP 服务器时,你可以专注于调试评分脚本,而无需考虑部署容器的配置。

安装 azureml-inference-server-http 包

若要安装 azureml-inference-server-http 包,请运行以下命令:

python -m pip install azureml-inference-server-http

注意

为了避免包冲突,请在虚拟环境中安装推理 HTTP 服务器。 可以使用 pip install virtualenv 命令为配置启用虚拟环境。

在本地调试评分脚本

若要在本地调试评分脚本,可以使用多个选项来测试服务器行为:

使用虚拟评分脚本测试服务器行为

  1. 创建一个名为 server_quickstart 的目录来保存文件

    mkdir server_quickstart
    cd server_quickstart
    
  2. 为了避免包冲突,请创建一个虚拟环境(例如 myenv)并激活它

    python -m virtualenv myenv
    

    注意

    在 Linux 上,运行 source myenv/bin/activate 命令来激活虚拟环境。

    测试服务器后,可以运行 deactivate 命令来停用 Python 虚拟环境。

  3. pypi 源安装 azureml-inference-server-http 包:

    python -m pip install azureml-inference-server-http
    
  4. 创建入口脚本。 以下示例创建一个基本入口脚本,并将其保存到名为 score.py 的文件中

    echo -e "import time def init(): \n\t time.sleep(1) \n\n def run(input_data): \n\t return {"message":"Hello, World!"}" > score.py
    
  5. 使用 azmlinfsrv 命令启动服务器,并将 score.py 文件设置为入口脚本

    azmlinfsrv --entry_script score.py
    

    注意

    服务器托管在 0.0.0.0 上,这意味着它将侦听宿主计算机的所有 IP 地址。

  6. 使用 curl 实用工具向服务器发送评分请求:

    curl -p 127.0.0.1:5001/score
    

    服务器发布以下响应:

    {"message": "Hello, World!"}
    
  7. 测试后,按 Ctrl+C 终止服务器。

现在,可以修改评分脚本文件 (score.py),然后使用 azmlinfsrv --entry_script score.py 命令再次运行服务器来测试更改

与 Visual Studio Code 集成

若要使用 VS Code 和 Python 扩展通过 azureml-inference-server-http 包进行调试,可以使用启动和附加模式

  • 对于启动模式,请在 VS Code 中设置 launch.json 文件,并在 VS Code 中启动 Azure 机器学习推理 HTTP 服务器

    1. 启动 VS Code 并打开包含脚本 (score.py) 的文件夹

    2. 在 VS Code 中将以下配置添加到该工作区的 launch.json 文件中

      launch.json

      {
          "version": "0.2.0",
          "configurations": [
              {
                  "name": "Debug score.py",
                  "type": "python",
                  "request": "launch",
                  "module": "azureml_inference_server_http.amlserver",
                  "args": [
                      "--entry_script",
                      "score.py"
                  ]
              }
          ]
        }
      
    3. 在 VS Code 中,通过选择“运行”>“开始调试”或按键盘快捷键 F5 启动调试会话

  • 对于附加模式,请在命令窗口中启动 Azure 机器学习推理 HTTP 服务器,并使用 VS Code 和 Python 扩展附加到进程

    注意

    对于 Linux,请先运行 sudo apt-get install -y gdb 命令安装 gdb 包。

    1. 在 VS Code 中将以下配置添加到该工作区的 launch.json 文件中

      launch.json

      {
          "version": "0.2.0",
          "configurations": [
              {
                  "name": "Python: Attach using Process Id",
                  "type": "python",
                  "request": "attach",
                  "processId": "${command:pickProcess}",
                  "justMyCode": true
              }
          ]
        }
      
    2. 在命令窗口中,使用 azmlinfsrv --entry_script score.py 命令启动推理 HTTP 服务器。

    3. 在 VS Code 中启动调试会话:

      1. 选择“运行”>“开始调试”或按键盘快捷键 F5

      2. 在命令窗口中,查看来自推理服务器的日志并找到 azmlinfsrv 命令(而不是 gunicorn)的进程 ID:

        显示命令窗口的屏幕截图,其中显示了来自推理 HTTP 服务器的日志,并突出显示了 azmlinfsrv 命令的进程 ID。

      3. 在 VS Code 调试器中,输入 azmlinfsrv 命令的进程 ID。

        如果未看到 VS Code 进程选择器,可以在该工作区的 launch.json 文件的 processId 字段中手动输入进程 ID

对于这两种模式,都可以设置断点并逐步调试脚本。

使用端到端示例

以下过程使用 Azure 机器学习示例存储库中的示例文件(评分脚本、模型文件和环境)在本地运行服务器。 有关如何使用这些示例文件的更多示例,请参阅使用联机终结点部署机器学习模型并对其进行评分

  1. 克隆示例存储库:

    git clone --depth 1 https://github.com/Azure/azureml-examples
    cd azureml-examples/cli/endpoints/online/model-1/
    
  2. 通过 conda 创建并激活虚拟环境:

    在此示例中,azureml-inference-server-http 包已自动安装。 该包作为 azureml-defaults 包的依赖库包含在 conda.yml 文件中

    # Create the environment from the YAML file
    conda env create --name model-env -f ./environment/conda.yml
    # Activate the new environment
    conda activate model-env
    
  3. 查看评分脚本:

onlinescoring/score.py

import os
import logging
import json
import numpy
import joblib


def init():
    """
    This function is called when the container is initialized/started, typically after create/update of the deployment.
    You can write the logic here to perform init operations like caching the model in memory
    """
    global model
    # AZUREML_MODEL_DIR is an environment variable created during deployment.
    # It is the path to the model folder (./azureml-models/$MODEL_NAME/$VERSION)
    # Please provide your model's folder name if there is one
    model_path = os.path.join(
        os.getenv("AZUREML_MODEL_DIR"), "model/sklearn_regression_model.pkl"
    )
    # deserialize the model file back into a sklearn model
    model = joblib.load(model_path)
    logging.info("Init complete")


def run(raw_data):
    """
    This function is called for every invocation of the endpoint to perform the actual scoring/prediction.
    In the example we extract the data from the json input and call the scikit-learn model's predict()
    method and return the result back
    """
    logging.info("model 1: request received")
    data = json.loads(raw_data)["data"]
    data = numpy.array(data)
    result = model.predict(data)
    logging.info("Request processed")
    return result.tolist()
  1. 通过指定评分脚本和模型文件来运行推理 HTTP 服务器:model_dir 参数中指定的模型目录是使用 AZUREML_MODEL_DIR 变量定义的,并在评分脚本中检索。

    在本例中,指定的是当前目录 ./,因为子目录在评分脚本中指定为 model/sklearn_regression_model.pkl

    azmlinfsrv --entry_script ./onlinescoring/score.py --model_dir ./
    

    当服务器启动并成功调用评分脚本时,示例启动日志将会打开。 否则,日志会显示错误消息。

  2. 使用示例数据测试评分脚本:

    打开另一个命令窗口,并切换到你在其中运行了命令的同一工作目录。

    使用 curl 实用工具将示例请求发送到服务器并接收评分结果:

    curl --request POST "127.0.0.1:5001/score" --header "Content-Type:application/json" --data @sample-request.json
    

    如果评分脚本没有问题,该脚本将返回评分结果。 如果出现问题,你可以尝试更新评分脚本,并再次启动服务器以测试更新的脚本。

查看服务器路由

默认情况下,推理 HTTP 服务器会在端口 5001 上侦听以下路由:

名称 路由
运行情况探测 127.0.0.1:5001/
分数 127.0.0.1:5001/score
OpenAPI (swagger) 127.0.0.1:5001/swagger.json

查看服务器参数

推理 HTTP 服务器接受以下参数:

参数 必须 默认 说明
entry_script True 空值 标识评分脚本的相对路径或绝对路径。
model_dir False 空值 标识用于推理的模型所在的目录的相对路径或绝对路径。
port False 5001 指定服务器的服务端口。
worker_count False 1 提供用于处理并发请求的工作线程数。
appinsights_instrumentation_key False 空值 提供要在其中发布日志的 Application Insights 的检测密钥。
access_control_allow_origins False 空值 为指定的源启用 CORS,多个源之间用逗号 (,) 分隔,例如 microsoft.com, bing.com

了解服务器请求处理

以下步骤说明了 Azure 机器学习推理 HTTP 服务器 (azmlinfsrv) 如何处理传入请求:

  1. Python CLI 包装器位于服务器的网络堆栈周围,用于启动服务器。

  2. 客户端向服务器发送请求。

  3. 服务器通过 Web 服务器网关接口 (WSGI) 服务器发送请求,该 WSGI 服务器将请求分派给 Flask 辅助角色应用程序:

  4. Flask 辅助角色应用处理请求,这包括加载入口脚本和所有依赖项。

  5. 入口脚本接收请求。 入口脚本对已加载的模型进行推理调用并返回响应:

显示推理 HTTP 服务器如何处理传入请求的示意图。

浏览服务器日志

可通过两种方式获取推理 HTTP 服务器测试的日志数据:

  • 在本地运行 azureml-inference-server-http 包并查看日志输出。
  • 使用联机终结点并查看容器日志。 推理服务器的日志名为“Azure 机器学习推理 HTTP 服务器 <版本>”

注意

自版本 0.8.0 以来,日志记录格式已更改。 如果日志使用的样式与预期不同,请将 azureml-inference-server-http 包更新到最新版本。

查看启动日志

当服务器启动时,日志会显示初始服务器设置,如下所示:

Azure Machine Learning Inferencing HTTP server <version>

Server Settings
---------------
Entry Script Name: <entry_script>
Model Directory: <model_dir>
Worker Count: <worker_count>
Worker Timeout (seconds): None
Server Port: <port>
Application Insights Enabled: false
Application Insights Key: <appinsights_instrumentation_key>
Inferencing HTTP server version: azmlinfsrv/<version>
CORS for the specified origins: <access_control_allow_origins>

Server Routes
---------------
Liveness Probe: GET   127.0.0.1:<port>/
Score:          POST  127.0.0.1:<port>/score

<logs>

例如,按照端到端示例启动服务器时,日志将显示如下内容:

Azure Machine Learning Inferencing HTTP server v0.8.0

Server Settings
---------------
Entry Script Name: /home/user-name/azureml-examples/cli/endpoints/online/model-1/onlinescoring/score.py
Model Directory: ./
Worker Count: 1
Worker Timeout (seconds): None
Server Port: 5001
Application Insights Enabled: false
Application Insights Key: None
Inferencing HTTP server version: azmlinfsrv/0.8.0
CORS for the specified origins: None

Server Routes
---------------
Liveness Probe: GET   127.0.0.1:5001/
Score:          POST  127.0.0.1:5001/score

2022-12-24 07:37:53,318 I [32726] gunicorn.error - Starting gunicorn 20.1.0
2022-12-24 07:37:53,319 I [32726] gunicorn.error - Listening at: http://0.0.0.0:5001 (32726)
2022-12-24 07:37:53,319 I [32726] gunicorn.error - Using worker: sync
2022-12-24 07:37:53,322 I [32756] gunicorn.error - Booting worker with pid: 32756
Initializing logger
2022-12-24 07:37:53,779 I [32756] azmlinfsrv - Starting up app insights client
2022-12-24 07:37:54,518 I [32756] azmlinfsrv.user_script - Found user script at /home/user-name/azureml-examples/cli/endpoints/online/model-1/onlinescoring/score.py
2022-12-24 07:37:54,518 I [32756] azmlinfsrv.user_script - run() is not decorated. Server will invoke it with the input in JSON string.
2022-12-24 07:37:54,518 I [32756] azmlinfsrv.user_script - Invoking user's init function
2022-12-24 07:37:55,974 I [32756] azmlinfsrv.user_script - Users's init has completed successfully
2022-12-24 07:37:55,976 I [32756] azmlinfsrv.swagger - Swaggers are prepared for the following versions: [2, 3, 3.1].
2022-12-24 07:37:55,977 I [32756] azmlinfsrv - AML_FLASK_ONE_COMPATIBILITY is set, but patching is not necessary.

了解日志数据格式

推理 HTTP 服务器的所有日志(启动器脚本除外)都会按以下格式显示数据:

<UTC Time> | <level> [<pid>] <logger name> - <message>

该条目包含以下组成部分:

  • <UTC Time>:将该条目输入到日志中的时间。
  • <pid>:与该条目关联的进程的 ID。
  • <level>:该条目的日志记录级别的第一个字符,例如 E 表示错误,I 表示信息,依此类推。
  • <logger name>:与该日志条目关联的资源的名称。
  • <message>:日志消息的内容。

Python 中有六个日志记录级别,并根据严重性为级别分配数字值:

日志记录级别 数值
严重 50
ERROR 40
警告 30
信息 20
调试 10
未设置 0

排查服务器问题

以下部分提供了 Azure 机器学习推理 HTTP 服务器的基本故障排除技巧。 若要排查联机终结点问题,请参阅排查联机终结点部署问题

检查已安装的包

按照以下步骤解决已安装的包的问题。

  1. 收集有关为 Python 环境安装的包和版本的信息。

  2. 确认环境文件中指定的 azureml-inference-server-http Python 包版本与启动日志中显示的 Azure 机器学习推理 HTTP 服务器版本相匹配。

    在某些情况下,pip 依赖项解析程序会安装意外的包版本。 可能需要运行 pip 来更正已安装的包和版本。

  3. 如果在环境中指定了 Flask 或其依赖项,请移除这些项。

    • 依赖包包括 flaskjinja2itsdangerouswerkzeugmarkupsafeclick
    • flask 在服务器包中列为依赖项。 最佳方法是允许推理服务器安装 flask 包。
    • 当推理服务器配置为支持新版本的 Flask 时,服务器会在包更新可用时自动接收更新。

检查服务器版本

azureml-inference-server-http 服务器包会发布到 PyPI。 PyPI 页面将列出更改日志和所有先前版本。

如果你使用早期版本的包,请将配置更新到最新版本。 下表汇总了稳定版本、常见问题和建议的调整:

包版本 说明 问题 解决方案
0.4.x 捆绑在 20220601 或更早发布的训练映像以及 azureml-defaults 包版本 .1.341.43 中。 最新稳定版本为 0.4.13 对于 0.4.11 以下的服务器版本,你可能会遇到 Flask 依赖项问题,例如 "can't import name Markup from jinja2" 如果可能,请升级到版本 0.4.13 或 0.8.x(最新版本)
0.6.x 已预装在 20220516 及更早发布的推理映像中。 最新稳定版本为 0.6.1 空值 空值
0.7.x 支持 Flask 2。 最新稳定版本为 0.7.7 空值 空值
0.8.x 日志格式已更改。 Python 3.6 支持已结束。 空值 空值

检查包依赖项

azureml-inference-server-http 服务器包的最相关依赖包包括:

  • flask
  • opencensus-ext-azure
  • inference-schema

如果你在 Python 环境中指定了 azureml-defaults 包,则 azureml-inference-server-http 包是依赖包。 依赖项已自动安装。

提示

如果你使用 Python SDK v1 并且未在 Python 环境中显式指定 azureml-defaults 包,则 SDK 可能会自动添加该包。 但是,打包器版本相对于 SDK 版本而言是锁定的。 例如,如果 SDK 版本为 1.38.0,则 azureml-defaults==1.38.0 条目将添加到环境的 pip 要求中。

服务器启动期间出现 TypeError

你可能在服务器启动期间遇到以下 TypeError

TypeError: register() takes 3 positional arguments but 4 were given

  File "/var/azureml-server/aml_blueprint.py", line 251, in register

    super(AMLBlueprint, self).register(app, options, first_registration)

TypeError: register() takes 3 positional arguments but 4 were given

如果在 Python 环境中安装了 Flask 2,但 azureml-inference-server-http 包版本不支持 Flask 2,则会出现此错误。 azureml-inference-server-http 包版本 0.7.0 及更高版本,以及 azureml-defaults 包版本 1.44 及更高版本均支持 Flask 2。

  • 如果你未在 Azure 机器学习 Docker 映像中使用 Flask 2 包,请使用最新版本的 azureml-inference-server-httpazureml-defaults 包。

  • 如果你在 Azure 机器学习 Docker 映像中使用 Flask 2 包,请确认映像生成版本为 2022 年 7 月或更高版本

    可以在容器日志中找到映像版本。 例如:

    2022-08-22T17:05:02,147738763+00:00 | gunicorn/run | AzureML Container Runtime Information
    2022-08-22T17:05:02,161963207+00:00 | gunicorn/run | ###############################################
    2022-08-22T17:05:02,168970479+00:00 | gunicorn/run | 
    2022-08-22T17:05:02,174364834+00:00 | gunicorn/run | 
    2022-08-22T17:05:02,187280665+00:00 | gunicorn/run | AzureML image information: openmpi4.1.0-ubuntu20.04, Materialization Build:20220708.v2
    2022-08-22T17:05:02,188930082+00:00 | gunicorn/run | 
    2022-08-22T17:05:02,190557998+00:00 | gunicorn/run | 
    

    映像生成日期显示在 Materialization Build 表示法后面。 在前面的示例中,映像版本为 20220708,即 2022 年 7 月 8 日。 在此示例中,该映像与 Flask 2 兼容。

    如果在容器日志中未看到类似的消息,则表明该映像已过期,应进行更新。 如果你使用计算统一设备体系结构 (CUDA) 映像,并且找不到较新的映像,请检查你的映像是否在 AzureML-Containers 中已被弃用。 可以查找已弃用映像的指定替换项。

    如果将服务器与联机终结点配合使用,则还可以在 Azure 机器学习工作室中的“终结点”页的“日志”下找到日志

如果你使用 SDK v1 进行部署,并且未在部署配置中显式指定映像,则服务器将应用版本与本地 SDK 工具集匹配的 openmpi4.1.0-ubuntu20.04 包。 但是,安装的版本可能不是该映像的最新可用版本。

对于 SDK 版本 1.43,服务器默认会安装 openmpi4.1.0-ubuntu20.04:20220616 包版本,但此包版本与 SDK 1.43 不兼容。 请确保使用最新 SDK 进行部署。

如果无法更新映像,可以通过在环境文件中固定 azureml-defaults==1.43azureml-inference-server-http~=0.4.13 条目来暂时避免此问题。 这些条目指示服务器使用 flask 1.0.x 安装旧版本。

在服务器启动期间出现 ImportError 或 ModuleNotFoundError

在服务器启动期间,特定的模块(例如 opencensusjinja2markupsafeclick)可能会出现 ImportErrorModuleNotFoundError。 以下示例显示了此错误消息:

ImportError: cannot import name 'Markup' from 'jinja2'

如果你使用服务器版本 0.4.10 及更低版本,而这些版本不会将 Flask 依赖项固定到兼容的版本,则会发生导入和模块错误。 为防止出现此问题,请安装更高的服务器版本。