你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

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

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

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

本文支持想要使用推理服务器在本地调试的开发人员。 本文介绍如何将推理服务器与联机终结点配合使用。

先决条件

  • Python 3.8 或更高版本
  • 蟒蛇

推理服务器在基于 Windows 和 Linux 的作系统上运行。

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

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

下表概述了每个选项针对各种调试方案提供的支持:

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

本文介绍如何使用推理服务器。

在本地运行推理服务器时,可以专注于调试评分脚本,而不必担心部署容器配置。

在本地调试评分脚本

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

以下部分提供有关每个选项的信息。

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

  1. 创建名为server_quickstart的目录以保存文件:

    mkdir server_quickstart
    cd server_quickstart
    
  2. 若要避免包冲突,请创建虚拟环境,例如 myenv,并激活它:

    python -m virtualenv myenv
    

    注意

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

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

  3. Python 包索引 (PyPI) 源安装 azureml-inference-server-http 包:

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

    echo -e 'import time\ndef init(): \n\ttime.sleep(1) \n\ndef run(input_data): \n\treturn {"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 命令再次运行推理服务器,以测试更改。

与 VS Code 集成

在 VS Code 中,可以使用 Python 扩展 通过 azureml-inference-server-http 包进行调试。 VS Code 提供两种调试模式: 启动和附加

在使用任一模式之前,请运行以下命令安装 azureml-inference-server-http 包:

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

注意

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

启动模式

对于启动模式,请执行以下步骤来设置 VS Code launch.json 配置文件,并在 VS Code 中启动推理服务器:

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

  2. 对于 VS Code 中的该工作区,请将以下配置添加到 launch.json 文件:

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

附加模式

对于附加模式,请执行以下步骤,将 VS Code 与 Python 扩展一起使用以附加到推理服务器进程:

注意

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

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

  2. 对于 VS Code 中的该工作区,请将以下配置添加到 launch.json 文件:

    {
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Python: Attach using Process ID",
                "type": "debugpy",
                "request": "attach",
                "processId": "${command:pickProcess}",
                "justMyCode": true
            }
        ]
      }
    
  3. 在命令窗口中,通过运行 azmlinfsrv --entry_script score.py 命令启动推理服务器。

  4. 执行以下步骤,在 VS Code 中启动调试会话:

    1. 选择 “运行>开始调试”,或选择 F5

    2. 在命令窗口中,搜索推理服务器中的日志以查找进程的进程 ID azmlinfsrv

      显示推理服务器日志的命令窗口的屏幕截图。在一个日志语句中,突出显示 azmlinfsrv 命令的进程 ID。

      请确保找到 azmlinfsrv 进程的 ID,而不是 gunicorn 进程的 ID。

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

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

对于启动和附加模式,可以设置 断点 并逐步调试脚本。

使用端到端示例

以下过程使用 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.yaml 文件中。

    # Create the environment from the YAML file.
    conda env create --name model-env -f ./environment/conda.yaml
    # 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()
    
  4. 通过指定评分脚本和模型文件夹的路径来运行推理服务器。

    在部署期间,将 AZUREML_MODEL_DIR 定义变量以存储模型文件夹的路径。 在参数中 model_dir 指定该值。 评分脚本运行时,它会从 AZUREML_MODEL_DIR 变量中检索值。

    在这种情况下,请使用当前目录作为./model_dir值,因为评分脚本将子目录指定为 model/sklearn_regression_model.pkl

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

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

  5. 执行以下步骤,使用示例数据测试评分脚本:

    1. 打开另一个命令窗口,转到运行该 azmlinfsrv 命令的同一工作目录。

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

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

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

查看推理服务器路由

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

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

查看推理服务器参数

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

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

探索推理服务器请求处理

以下步骤演示推理服务器 azmlinfsrv如何处理传入请求:

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

  2. 客户端将请求发送到推理服务器。

  3. 推理服务器通过 Web 服务器网关接口 (WSGI) 服务器发送请求,该服务器将请求调度到以下 Flask 辅助角色应用程序之一:

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

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

此图显示了推理服务器如何启动,以及请求如何流向 Flask 工作器应用,然后流向用户代码。

浏览推理服务器日志

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

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

注意

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

查看启动日志

推理服务器启动时,日志会显示以下初始服务器设置:

Azure ML Inferencing HTTP server <version>


Server Settings
---------------
Entry Script Name: <entry-script>
Model Directory: <model-directory>
Config File: <configuration-file>
Worker Count: <worker-count>
Worker Timeout (seconds): None
Server Port: <port>
Health Port: <port>
Application Insights Enabled: false
Application Insights Key: <Application-Insights-instrumentation-key>
Inferencing HTTP server version: azmlinfsrv/<version>
CORS for the specified origins: <access-control-allow-origins>
Create dedicated endpoint for health: <health-check-endpoint>


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

<logs>

例如,通过 执行端到端示例 步骤来运行推理服务器时,日志包含以下信息:

Azure ML Inferencing HTTP server v1.2.2


Server Settings
---------------
Entry Script Name: /home/user-name/azureml-examples/cli/endpoints/online/model-1/onlinescoring/score.py
Model Directory: ./
Config File: None
Worker Count: 1
Worker Timeout (seconds): None
Server Port: 5001
Health Port: 5001
Application Insights Enabled: false
Application Insights Key: None
Inferencing HTTP server version: azmlinfsrv/1.2.2
CORS for the specified origins: None
Create dedicated endpoint for health: 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,976 I [32756] azmlinfsrv - Scoring timeout is set to 3600000
2022-12-24 07:37:55,976 I [32756] azmlinfsrv - Worker with pid 32756 ready for serving traffic

了解日志数据格式

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

<UTC-time> <level> [<process-ID>] <logger-name> - <message>

每个条目由以下组件组成:

  • <UTC-time>:输入条目进入日志的时间
  • <level>:条目 的日志记录级别 的第一个字符,例如 E ERROR、 I INFO 等
  • <process-ID>:与条目关联的进程的 ID
  • <logger-name>:与日志条目关联的资源的名称
  • <message>:日志消息的内容

Python 中有六个级别的日志记录。 每个级别都有一个根据其严重性分配的数值:

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

排查推理服务器问题

以下部分提供了推理服务器的基本故障排除提示。 若要对联机终结点进行故障排除,请参阅 联机终结点部署和评分疑难解答

检查已安装的包

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

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

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

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

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

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

检查推理服务器版本

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

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

包版本 说明 问题 解决方案
0.4.x 捆绑在 20220601 或更早日期的训练图像以及包 azureml-defaults 版本 0.1.34 到 1.43 中的内容。 最新稳定版本为 0.4.13。 对于低于 0.4.11 的服务器版本,可能会遇到 Flask 依赖项问题,例如 can't import name Markup from jinja2 如果可能,请升级到版本 0.4.13 或 1.4.x(最新版本)。
0.6.x 已预装在 20220516 及更早发布的推理映像中。 最新稳定版本为 0.6.1。 空值 空值
0.7.x 支持 Flask 2。 最新稳定版本为 0.7.7。 空值 空值
0.8.x 使用更新后的日志格式。 结束对 Python 3.6 的支持。 空值 空值
1.0.x 结束对 Python 3.7 的支持。 空值 空值
1.1.x 迁移到 pydantic 2.0。 空值 空值
1.2.x 添加了对 Python 3.11 的支持。 将 gunicorn 更新到版本 22.0.0。 更新 werkzeug 至 3.0.3 版本及更高版本。 空值 空值
1.3.x 添加了对 Python 3.12 的支持。 将certifi升级到版本 2024.7.4。 将 flask-cors 升级到版本 5.0.0。 升级 gunicornpydantic 包。 空值 空值
1.4.x waitress 升级到版本 3.0.1。 结束对 Python 3.8 的支持。 删除阻止 Flask 2.0 升级中断请求对象代码的兼容性层。 如果依赖于兼容性层,则请求对象代码可能不起作用。 将分数脚本迁移到 Flask 2。

检查包依赖项

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

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

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

提示

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

推理服务器启动期间出现类型错误

在推理服务器启动期间可能会遇到以下 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,则会出现此错误。 0.7.0 包及更高版本中提供了 azureml-inference-server-http 对 Flask 2 的支持,以及 azureml-defaults 1.44 包及更高版本。

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

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

    可以在容器日志中找到映像版本。 例如,请参阅以下日志语句:

    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 表示法后面。 在前面的示例中,映像版本为 202207082022 年 7 月 8 日。 在此示例中,该映像与 Flask 2 兼容。

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

    如果将推理服务器与联机终结点配合使用,还可以在 Azure 机器学习工作室中找到日志。 在终结点的页面上,选择“ 日志 ”选项卡。

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

对于 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

在推理服务器启动期间,可能会遇到特定模块(例如 ImportErrorModuleNotFoundErroropencensusjinja2)上的 markupsafeclick。 以下示例显示了此错误消息:

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

使用版本 0.4.10 或更早版本的推理服务器时,如果没有将 Flask 依赖项固定到兼容版本,就会发生导入和模块错误。 若要防止此问题,请安装更高版本的推理服务器。