使用 REST 部署模型以进行批量评分

适用于:Azure CLI ml 扩展 v2(当前版本)

了解如何使用 Azure 机器学习 REST API 部署模型以进行批量评分。

REST API 使用标准 HTTP 谓词创建、检索、更新和删除资源。 REST API 适用于可发出 HTTP 请求的任何语言或工具。 REST 具有简单的结构,因此它是适合脚本编写环境和 MLOps 自动化的良好选择。

本文介绍如何使用新的 REST API 执行以下操作:

  • 创建机器学习资产
  • 创建批处理终结点和批处理部署
  • 调用批处理终结点以启动批量评分作业

先决条件

重要

本文中的代码片段假设使用的是 Bash shell。

代码片段摘自 Azure 机器学习示例存储库中的 /cli/batch-score-rest.sh 文件。

设置终结点名称

注意

批处理终结点名称在 Azure 区域级别需是唯一的。 例如,chinaeast2 中只能有一个名为 mybatchendpoint 的批处理终结点。

export ENDPOINT_NAME=endpt-`echo $RANDOM`

Azure 机器学习批处理终结点

批处理终结点简化了承载用于批量评分的模型的过程,使你可以将工作重心放在机器学习而不是基础结构上。 在本文中,你将创建一个批处理终结点和部署,并调用它来启动批量评分作业。 但首先必须注册部署所需的资产,包括模型、代码和环境。

可通过多种方式创建 Azure 机器学习批处理终结点,包括 Azure CLI、适用于 Python 的 Azure 机器学习 SDK,以及在工作室中以视觉方式创建。 以下示例使用 REST API 创建批处理终结点和批处理部署。

创建机器学习资产

首先设置 Azure 机器学习资产以配置作业。

在以下 REST API 调用中,我们使用了 SUBSCRIPTION_IDRESOURCE_GROUPLOCATIONWORKSPACE 作为占位符。 将占位符替换为自己的值。

管理 REST 请求一个服务主体身份验证令牌。 请将 TOKEN 替换为你自己的值。 可使用以下命令检索此令牌:

TOKEN=$(az account get-access-token --query accessToken -o tsv)

服务提供商使用 api-version 参数来确保兼容性。 api-version 参数因服务而异。 将 API 版本设置为变量以适应将来的版本:

API_VERSION="2022-05-01"

创建计算

批量评分仅在云计算资源上运行,而不在本地运行。 云计算资源是一个可重用的虚拟计算机群集,可在其中运行批量评分工作流。

创建计算群集:

response=$(curl --location --request PUT "https://management.chinacloudapi.cn/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/computes/batch-cluster?api-version=$API_VERSION" \
--header "Authorization: Bearer $TOKEN" \
--header "Content-Type: application/json" \
--data-raw "{
    \"properties\":{
        \"computeType\": \"AmlCompute\",
        \"properties\": {
            \"osType\": \"Linux\",
            \"vmSize\": \"STANDARD_D2_V2\",
            \"scaleSettings\": {
                \"maxNodeCount\": 5,
                \"minNodeCount\": 0
            },
            \"remoteLoginPortPublicAccess\": \"NotSpecified\"
        },
    },
    \"location\": \"$LOCATION\"
}")

提示

若要改用现有计算,必须在创建批处理部署时指定完整的 Azure 资源管理器 ID。 完整 ID 采用 /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/computes/<your-compute-name> 格式。

获取存储帐户详细信息

若要注册模型和代码,首先需要将其上传到存储帐户。 数据存储中提供了存储帐户的详细信息。 此示例将获取工作区的默认数据存储和 Azure 存储帐户。 使用 GET 请求查询工作区,以获取包含信息的 JSON 文件。

可以使用工具 jq 分析 JSON 结果并获取所需的值。 也可以使用 Azure 门户找到相同的信息:

# Get values for storage account
response=$(curl --location --request GET "https://management.chinacloudapi.cn/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/datastores?api-version=$API_VERSION&isDefault=true" \
--header "Authorization: Bearer $TOKEN")
DATASTORE_PATH=$(echo $response | jq -r '.value[0].id')
BLOB_URI_PROTOCOL=$(echo $response | jq -r '.value[0].properties.protocol')
BLOB_URI_ENDPOINT=$(echo $response | jq -r '.value[0].properties.endpoint')
AZUREML_DEFAULT_DATASTORE=$(echo $response | jq -r '.value[0].name')
AZUREML_DEFAULT_CONTAINER=$(echo $response | jq -r '.value[0].properties.containerName')
AZURE_STORAGE_ACCOUNT=$(echo $response | jq -r '.value[0].properties.accountName')
export AZURE_STORAGE_ACCOUNT $(echo $AZURE_STORAGE_ACCOUNT)
STORAGE_RESPONSE=$(echo az storage account show-connection-string --name $AZURE_STORAGE_ACCOUNT)
AZURE_STORAGE_CONNECTION_STRING=$($STORAGE_RESPONSE | jq -r '.connectionString')

BLOB_URI_ROOT="$BLOB_URI_PROTOCOL://$AZURE_STORAGE_ACCOUNT.blob.$BLOB_URI_ENDPOINT/$AZUREML_DEFAULT_CONTAINER"

上传和注册代码

获取数据存储后,可以上传评分脚本。 使用 Azure 存储 CLI 将 Blob 上传到默认容器中:

az storage blob upload-batch -d $AZUREML_DEFAULT_CONTAINER/score -s endpoints/batch/mnist/code/  --connection-string $AZURE_STORAGE_CONNECTION_STRING

提示

也可以使用其他方法来上传,例如 Azure 门户或 Azure 存储资源管理器

上传代码后,可以使用 PUT 请求指定代码:

response=$(curl --location --request PUT "https://management.chinacloudapi.cn/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/codes/score-mnist/versions/1?api-version=$API_VERSION" \
--header "Authorization: Bearer $TOKEN" \
--header "Content-Type: application/json" \
--data-raw "{
  \"properties\": {
    \"description\": \"Score code\",
    \"codeUri\": \"$BLOB_URI_ROOT/score\"
  }
}")

上传和注册模型

与代码类似,上传模型文件:

az storage blob upload-batch -d $AZUREML_DEFAULT_CONTAINER/model -s endpoints/batch/mnist/model  --connection-string $AZURE_STORAGE_CONNECTION_STRING

现在注册模型:

response=$(curl --location --request PUT "https://management.chinacloudapi.cn/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/models/mnist/versions/1?api-version=$API_VERSION" \
--header "Authorization: Bearer $TOKEN" \
--header "Content-Type: application/json" \
--data-raw "{
    \"properties\": {
        \"modelUri\":\"azureml://subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/workspaces/$WORKSPACE/datastores/$AZUREML_DEFAULT_DATASTORE/paths/model\"
    }
}")

创建环境

部署需要在具有所需依赖关系的环境中运行。 使用 PUT 请求创建环境。 使用 Azure 容器注册表中的 docker 映像。 可以使用 image 配置 Docker 映像,并使用 condaFile 添加 Conda 依赖项。

运行以下代码以读取 json 中定义的 condaFile。 源文件位于示例存储库中的 /cli/endpoints/batch/mnist/environment/conda.json

CONDA_FILE=$(cat endpoints/batch/mnist/environment/conda.json | sed 's/"/\\"/g')

现在,运行以下代码片段来创建环境:

ENV_VERSION=$RANDOM
response=$(curl --location --request PUT "https://management.chinacloudapi.cn/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/environments/mnist-env/versions/$ENV_VERSION?api-version=$API_VERSION" \
--header "Authorization: Bearer $TOKEN" \
--header "Content-Type: application/json" \
--data-raw "{
    \"properties\":{
        \"condaFile\": $(echo \"$CONDA_FILE\"),
        \"image\": \"mcr.microsoft.com/azureml/openmpi3.1.2-ubuntu18.04:latest\"
    }
}")

使用批处理终结点进行部署

接下来,创建一个批处理终结点,一个批处理部署,并为该终结点设置默认部署。

创建批处理终结点

创建批处理终结点:

response=$(curl --location --request PUT "https://management.chinacloudapi.cn/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/batchEndpoints/$ENDPOINT_NAME?api-version=$API_VERSION" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $TOKEN" \
--data-raw "{
    \"properties\": {
        \"authMode\": \"aadToken\"
    },
    \"location\": \"$LOCATION\"
}")

创建批处理部署

在终结点下创建批处理部署:

DEPLOYMENT_NAME="nonmlflowedp"
response=$(curl --location --request PUT "https://management.chinacloudapi.cn/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/batchEndpoints/$ENDPOINT_NAME/deployments/$DEPLOYMENT_NAME?api-version=$API_VERSION" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $TOKEN" \
--data-raw "{
    \"location\": \"$LOCATION\",
    \"properties\": {        
        \"model\": {
            \"referenceType\": \"Id\",
            \"assetId\": \"/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/models/mnist/versions/1\"
        },
        \"codeConfiguration\": {
            \"codeId\": \"/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/codes/score-mnist/versions/1\",
            \"scoringScript\": \"digit_identification.py\"
        },
        \"environmentId\": \"/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/environments/mnist-env/versions/$ENV_VERSION\",
        \"compute\": \"/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/computes/batch-cluster\",
        \"resources\": {
            \"instanceCount\": 1
        },
        \"maxConcurrencyPerInstance\": \"4\",
        \"retrySettings\": {
            \"maxRetries\": 3,
            \"timeout\": \"PT30S\"
        },
        \"errorThreshold\": \"10\",
        \"loggingLevel\": \"info\",
        \"miniBatchSize\": \"5\",
    }
}")

在终结点下设置默认批处理部署

一个终结点下只有一个默认的批处理部署,调用运行批量评分作业时将使用该部署。

response=$(curl --location --request PUT "https://management.chinacloudapi.cn/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/batchEndpoints/$ENDPOINT_NAME?api-version=$API_VERSION" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $TOKEN" \
--data-raw "{
    \"properties\": {
        \"authMode\": \"aadToken\",
        \"defaults\": {
            \"deploymentName\": \"$DEPLOYMENT_NAME\"
        }
    },
    \"location\": \"$LOCATION\"
}")

operation_id=$(echo $response | jq -r '.properties' | jq -r '.properties' | jq -r '.AzureAsyncOperationUri')
wait_for_completion $operation_id $TOKEN

运行批量评分

调用批处理终结点会触发批量评分作业。 响应中会返回作业 id,该作业可用于跟踪批量评分进度。 在以下代码片段中,jq 用于获取作业 id

调用批处理终结点以启动批量评分作业

获取评分 URI 和访问令牌

获取评分 URI 和访问令牌以调用批处理终结点。 首先获取评分 uri:

response=$(curl --location --request GET "https://management.chinacloudapi.cn/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/batchEndpoints/$ENDPOINT_NAME?api-version=$API_VERSION" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $TOKEN")

SCORING_URI=$(echo $response | jq -r '.properties.scoringUri')

获取批处理终结点访问令牌:

SCORING_TOKEN=$(az account get-access-token --resource https://studio.ml.azure.cn --query accessToken -o tsv)

使用不同输入选项调用批处理终结点

接下来,调用批处理终结点以启动批量评分作业。 如果数据是一个文件夹(可能包含多个文件)并且可通过网络公开访问,则可使用以下代码段:

response=$(curl --location --request POST $SCORING_URI \
--header "Authorization: Bearer $SCORING_TOKEN" \
--header "Content-Type: application/json" \
--data-raw "{
    \"properties\": {
    	\"InputData\": {
    		\"mnistinput\": {
    			\"JobInputType\" : \"UriFolder\",
    			\"Uri\":  \"https://pipelinedata.blob.core.windows.net/sampledata/mnist\"
    		}
        }
    }
}")

JOB_ID=$(echo $response | jq -r '.id')
JOB_ID_SUFFIX=$(echo ${JOB_ID##/*/})

现在,让我们看看用于调用批处理终结点的其他选项。 当涉及到输入数据时,可选择多种方案,具体取决于输入类型(是指定文件夹还是单个文件)和 URI 类型(是使用 Azure 机器学习已注册的数据存储上的路径(是对 Azure 机器学习已注册的 V2 数据资产的引用)还会使用公共 URI)。

  • InputData 属性具有 JobInputTypeUri 键。 指定单个文件时,可使用 "JobInputType": "UriFile",指定文件夹时,可使用 'JobInputType": "UriFolder"

  • 当文件或文件夹位于已注册 Azure 机器学习的数据存储上时,Uri 的语法为 azureml://datastores/<datastore-name>/paths/<path-on-datastore>(适用于文件夹)和 azureml://datastores/<datastore-name>/paths/<path-on-datastore>/<file-name>(适用于特定文件)。 还可使用长格式来表示同一路径,例如 azureml://subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/workspaces/<workspace-name>/datastores/<datastore-name>/paths/<path-on-datastore>/

  • 当文件或文件夹作为 uri_folderuri_file 注册为 V2 数据资产时,Uri 的语法为 \"azureml://locations/<location-name>/workspaces/<workspace-name>/data/<data-name>/versions/<data-version>"(资产 ID 格式)或 \"/subscriptions/<subscription-id>/resourcegroups/<resource-group-name>/providers/Microsoft.MachineLearningServices/workspaces/<workspace-name>/data/<data-name>/versions/<data-version>\"(ARM ID 格式)。

  • 当文件或文件夹位于可公开访问的路径上时,URI 的语法为 https://<public-path>(适用于文件夹)和 https://<public-path>/<file-name>(适用于特定文件)。

注意

有关数据 URI 的详细信息,请参阅 Azure 机器学习数据引用 URI

下面是使用不同类型的输入数据的一些示例。

  • 如果数据是已注册 Azure 机器学习的数据存储上的文件夹,你则可以:

    • 使用短格式表示 URI:
    response=$(curl --location --request POST $SCORING_URI \
    --header "Authorization: Bearer $SCORING_TOKEN" \
    --header "Content-Type: application/json" \
    --data-raw "{
        \"properties\": {
            \"InputData\": {
                \"mnistInput\": {
                    \"JobInputType\" : \"UriFolder\",
                    \"Uri": \"azureml://datastores/workspaceblobstore/paths/$ENDPOINT_NAME/mnist\"
                }
            }
        }
    }")
    
    JOB_ID=$(echo $response | jq -r '.id')
    JOB_ID_SUFFIX=$(echo ${JOB_ID##/*/})
    
    • 或者对同一 URI 使用长格式:
    response=$(curl --location --request POST $SCORING_URI \
    --header "Authorization: Bearer $SCORING_TOKEN" \
    --header "Content-Type: application/json" \
    --data-raw "{
        \"properties\": {
        	\"InputData\": {
        		\"mnistinput\": {
        			\"JobInputType\" : \"UriFolder\",
        			\"Uri\": \"azureml://subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/workspaces/$WORKSPACE/datastores/workspaceblobstore/paths/$ENDPOINT_NAME/mnist\"
        		}
            }
        }
    }")
    
    JOB_ID=$(echo $response | jq -r '.id')
    JOB_ID_SUFFIX=$(echo ${JOB_ID##/*/})
    
  • 如果要将数据作为已注册 Azure 机器学习的 V2 数据资产进行管理,即 uri_folder,可以按照以下两个步骤操作:

    1. 创建 V2 数据资产:
    DATA_NAME="mnist"
    DATA_VERSION=$RANDOM
    
    response=$(curl --location --request PUT https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/data/$DATA_NAME/versions/$DATA_VERSION?api-version=$API_VERSION \
    --header "Content-Type: application/json" \
    --header "Authorization: Bearer $TOKEN" \
    --data-raw "{
        \"properties\": {
            \"dataType\": \"uri_folder\",
      \"dataUri\": \"https://pipelinedata.blob.core.windows.net/sampledata/mnist\",
      \"description\": \"Mnist data asset\"
        }
    }")
    
    1. 引用批处理评分作业中的数据资产:
    response=$(curl --location --request POST $SCORING_URI \
    --header "Authorization: Bearer $SCORING_TOKEN" \
    --header "Content-Type: application/json" \
    --data-raw "{
        \"properties\": {
            \"InputData\": {
                \"mnistInput\": {
                    \"JobInputType\" : \"UriFolder\",
                    \"Uri": \"azureml://locations/$LOCATION_NAME/workspaces/$WORKSPACE_NAME/data/$DATA_NAME/versions/$DATA_VERSION/\"
                }
            }
        }
    }")
    
    JOB_ID=$(echo $response | jq -r '.id')
    JOB_ID_SUFFIX=$(echo ${JOB_ID##/*/})
    
  • 如果数据是可通过 Web 公开访问的单个文件,则可以使用以下代码片段:

    response=$(curl --location --request POST $SCORING_URI \
    --header "Authorization: Bearer $SCORING_TOKEN" \
    --header "Content-Type: application/json" \
    --data-raw "{
        \"properties\": {
            \"InputData\": {
                \"mnistInput\": {
                    \"JobInputType\" : \"UriFile\",
                    \"Uri": \"https://pipelinedata.blob.core.windows.net/sampledata/mnist/0.png\"
                }
            }
        }
    }")
    
    JOB_ID=$(echo $response | jq -r '.id')
    JOB_ID_SUFFIX=$(echo ${JOB_ID##/*/})
    

注意

强烈建议使用最新的 REST API 版本进行批量评分。

  • 如果要使用本地数据,可以将其上传到Azure 机器学习已注册的数据存储,并对云数据使用 REST API。
  • 如果对批处理终结点使用现有的 V1 FileDataset,建议将它们迁移到 V2 数据资产,并在调用批处理终结点时直接引用它们。 目前仅支持类型 uri_folderuri_file 的数据资产。 使用 GA CLIv2(2.4.0 及更高版本)或 GA REST API(2022-05-01 及更高版本)创建的批处理终结点不支持 V1 数据集。
  • 还可使用 az ml dataset show 命令和 --query 参数提取从 V1 FileDataset 中提取的数据存储上的 URI 或路径,并将该信息用于调用。
  • 虽然使用早期 API 创建的批处理终结点将继续支持 V1 FileDataset,但我们将使用最新 API 版本添加更多 V2 数据资产支持,以提高可用性和灵活性。 有关 V2 数据资产的详细信息,请参阅使用 SDK v2 处理数据。 有关新的 V2 体验的详细信息,请参阅什么是 v2

配置输出位置并覆盖设置

默认情况下,批量评分结果存储在工作区的默认 Blob 存储中按作业名称(系统生成的 GUID)命名的某个文件夹内。 可以配置在调用批处理终结点时用于存储评分输出的位置。 使用 OutputData 在 Azure 机器学习已注册的数据存储上配置输出文件路径。 OutputData 具有 JobOutputTypeUri 键。 UriFileJobOutputType 唯一支持的值。 Uri 的语法与 InputData 的语法相同,即都为 azureml://datastores/<datastore-name>/paths/<path-on-datastore>/<file-name>

下面是用于为批处理评分结果配置输出位置的代码片段示例。

response=$(curl --location --request POST $SCORING_URI \
--header "Authorization: Bearer $SCORING_TOKEN" \
--header "Content-Type: application/json" \
--data-raw "{
    \"properties\": {
        \"InputData\":
        {
            \"mnistInput\": {
                \"JobInputType\" : \"UriFolder\",
                \"Uri": \"azureml://datastores/workspaceblobstore/paths/$ENDPOINT_NAME/mnist\"
            }
        },
        \"OutputData\":
        {
            \"mnistOutput\": {
                \"JobOutputType\": \"UriFile\",
                \"Uri\": \"azureml://datastores/workspaceblobstore/paths/$ENDPOINT_NAME/mnistOutput/$OUTPUT_FILE_NAME\"
            }
        }
    }
}")

JOB_ID=$(echo $response | jq -r '.id')
JOB_ID_SUFFIX=$(echo ${JOB_ID##/*/})

重要

必须使用唯一的输出位置。 如果输出文件已存在,批量评分作业将失败。

检查批量评分作业

批量评分作业通常会花费一段时间来处理整个输入集。 监视作业状态,并在作业完成后检查结果:

提示

该示例调用批处理终结点的默认部署。 若要调用非默认部署,请使用 azureml-model-deployment HTTP 头,并将值设置为部署名称。 例如,在 curl 中使用 --header "azureml-model-deployment: $DEPLOYMENT_NAME" 参数。

wait_for_completion $SCORING_URI/$JOB_ID_SUFFIX $SCORING_TOKEN

删除批处理终结点

如果你将来不再使用该批处理终结点,应使用以下命令将其删除(这会删除该批处理终结点和所有基础部署):

curl --location --request DELETE "https://management.chinacloudapi.cn/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.MachineLearningServices/workspaces/$WORKSPACE/batchEndpoints/$ENDPOINT_NAME?api-version=$API_VERSION" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $TOKEN" || true