使用 REST 部署模型以进行批量评分
了解如何使用 Azure 机器学习 REST API 部署模型以进行批量评分。
REST API 使用标准 HTTP 谓词创建、检索、更新和删除资源。 REST API 适用于可发出 HTTP 请求的任何语言或工具。 REST 具有简单的结构,因此它是适合脚本编写环境和 MLOps 自动化的良好选择。
本文介绍如何使用新的 REST API 执行以下操作:
- 创建机器学习资产
- 创建批处理终结点和批处理部署
- 调用批处理终结点以启动批量评分作业
先决条件
- 你对其拥有管理权限的 Azure 订阅。 如果没有此类订阅,请尝试注册试用版订阅。
- 一个 Azure 机器学习工作区。
- 工作区中的服务主体。 管理 REST 请求使用服务主体身份验证。
- 服务主体身份验证令牌。 请按照检索服务主体身份验证令牌中的步骤检索此令牌。
- curl 实用工具。 在适用于 Linux 的 Windows 子系统或任何 UNIX 分发版中均已提供了 curl 程序。 在 PowerShell 中,curl 是 Invoke-WebRequest 的别名,并且
curl -d "key=val" -X POST uri
变成了Invoke-WebRequest -Body "key=val" -Method POST -Uri uri
。 - jq JSON 处理器。
设置终结点名称
注意
批处理终结点名称在 Azure 区域级别需是唯一的。 例如,chinaeast2 中只能有一个名为 mybatchendpoint 的批处理终结点。
export ENDPOINT_NAME=endpt-`echo $RANDOM`
Azure 机器学习批处理终结点
批处理终结点简化了承载用于批量评分的模型的过程,使你可以将工作重心放在机器学习而不是基础结构上。 在本文中,你将创建一个批处理终结点和部署,并调用它来启动批量评分作业。 但首先必须注册部署所需的资产,包括模型、代码和环境。
可通过多种方式创建 Azure 机器学习批处理终结点,包括 Azure CLI、适用于 Python 的 Azure 机器学习 SDK,以及在工作室中以视觉方式创建。 以下示例使用 REST API 创建批处理终结点和批处理部署。
创建机器学习资产
首先设置 Azure 机器学习资产以配置作业。
在以下 REST API 调用中,我们使用了 SUBSCRIPTION_ID
、RESOURCE_GROUP
、LOCATION
和 WORKSPACE
作为占位符。 将占位符替换为自己的值。
管理 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
属性具有JobInputType
和Uri
键。 指定单个文件时,可使用"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_folder
或uri_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
,可以按照以下两个步骤操作:- 创建 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\" } }")
- 引用批处理评分作业中的数据资产:
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_folder
或uri_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
具有 JobOutputType
和 Uri
键。 UriFile
是 JobOutputType
唯一支持的值。 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