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

高级入口脚本创作

适用于:Python SDK azureml v1

本文介绍如何为特殊用例编写入口脚本。

先决条件

本文假设你已有一个要使用 Azure 机器学习进行部署的经过训练的机器学习模型。 若要了解有关模型部署的详细信息,请参阅将机器学习模型部署到 Azure

自动生成 Swagger 架构

若要为 Web 服务自动生成架构,请在一个已定义的类型对象的构造函数中提供输入和/或输出的示例。 该类型和示例用于自动创建架构。 Azure 机器学习随后会在部署过程中为 Web 服务创建 OpenAPI 规范(前 Swagger 规范)。

警告

对于示例输入或输出,不得使用敏感或专用数据。 AML 托管推理的 Swagger 页公开了示例数据。

当前支持以下类型:

  • pandas
  • numpy
  • pyspark
  • 标准 Python 对象

若要使用架构生成,请在依赖项文件中包括开源 inference-schema 包 1.1.0 或更高版本。 有关此包的详细信息,请参阅 GitHub 上的 InferenceSchema。 若要生成符合条件的 Swagger 以自动使用 Web 服务,评分脚本 run() 函数的 API 形状必须为:

  • 类型为 StandardPythonParameterType 的第一个参数,名为“Inputs”并嵌套
  • 类型为 StandardPythonParameterType 的可选第二个参数,名为 GlobalParameters
  • 返回类型为 StandardPythonParameterType 的字典,名为“Results”并嵌套

定义 input_sampleoutput_sample 变量中的输入和输出示例格式,它们表示 Web 服务的请求和响应格式。 在 run() 函数的输入和输出函数修饰器中使用这些示例。 以下 scikit-learn 示例使用架构生成功能。

Power BI 兼容终结点

下面的示例演示如何根据前面的说明定义 API 形状。 此方法支持使用 Power BI 中已部署的 Web 服务。

import json
import pickle
import numpy as np
import pandas as pd
import azureml.train.automl
from sklearn.externals import joblib
from sklearn.linear_model import Ridge

from inference_schema.schema_decorators import input_schema, output_schema
from inference_schema.parameter_types.standard_py_parameter_type import StandardPythonParameterType
from inference_schema.parameter_types.numpy_parameter_type import NumpyParameterType
from inference_schema.parameter_types.pandas_parameter_type import PandasParameterType


def init():
    global model
    # Replace filename if needed.
    model_path = os.path.join(os.getenv('AZUREML_MODEL_DIR'), 'sklearn_regression_model.pkl')
    # Deserialize the model file back into a sklearn model.
    model = joblib.load(model_path)


# providing 3 sample inputs for schema generation
numpy_sample_input = NumpyParameterType(np.array([[1,2,3,4,5,6,7,8,9,10],[10,9,8,7,6,5,4,3,2,1]],dtype='float64'))
pandas_sample_input = PandasParameterType(pd.DataFrame({'name': ['Sarah', 'John'], 'age': [25, 26]}))
standard_sample_input = StandardPythonParameterType(0.0)

# This is a nested input sample, any item wrapped by `ParameterType` will be described by schema
sample_input = StandardPythonParameterType({'input1': numpy_sample_input, 
                                        'input2': pandas_sample_input, 
                                        'input3': standard_sample_input})

sample_global_parameters = StandardPythonParameterType(1.0) # this is optional
sample_output = StandardPythonParameterType([1.0, 1.0])
outputs = StandardPythonParameterType({'Results':sample_output}) # 'Results' is case sensitive

@input_schema('Inputs', sample_input) 
# 'Inputs' is case sensitive

@input_schema('GlobalParameters', sample_global_parameters) 
# this is optional, 'GlobalParameters' is case sensitive

@output_schema(outputs)

def run(Inputs, GlobalParameters): 
    # the parameters here have to match those in decorator, both 'Inputs' and 
    # 'GlobalParameters' here are case sensitive
    try:
        data = Inputs['input1']
        # data will be convert to target format
        assert isinstance(data, np.ndarray)
        result = model.predict(data)
        return result.tolist()
    except Exception as e:
        error = str(e)
        return error

提示

脚本的返回值可以是可序列化为 JSON 的任何 Python 对象。 例如,如果模型返回包含多个列的 Pandas 数据帧,则可以使用类似于以下代码的输出修饰器:

output_sample = pd.DataFrame(data=[{"a1": 5, "a2": 6}])
@output_schema(PandasParameterType(output_sample))
...
result = model.predict(data)
return result

二进制(即图像)数据

如果模型接受二进制数据(如映像),则必须修改用于部署的 score.py 文件以接受原始 HTTP 请求。 若要接受原始数据,请在入口脚本中使用 AMLRequest 类,并向 run() 函数添加 @rawhttp 修饰器。

下面是接受二进制数据的 score.py 的示例:

from azureml.contrib.services.aml_request import AMLRequest, rawhttp
from azureml.contrib.services.aml_response import AMLResponse
from PIL import Image
import json


def init():
    print("This is init()")
    

@rawhttp
def run(request):
    print("This is run()")
    
    if request.method == 'GET':
        # For this example, just return the URL for GETs.
        respBody = str.encode(request.full_path)
        return AMLResponse(respBody, 200)
    elif request.method == 'POST':
        file_bytes = request.files["image"]
        image = Image.open(file_bytes).convert('RGB')
        # For a real-world solution, you would load the data from reqBody
        # and send it to the model. Then return the response.

        # For demonstration purposes, this example just returns the size of the image as the response.
        return AMLResponse(json.dumps(image.size), 200)
    else:
        return AMLResponse("bad request", 500)

重要

AMLRequest 类位于 azureml.contrib 命名空间中。 此命名空间中的实体会频繁更改,因为我们正在改进服务。 此命名空间中的任何内容都应被视为预览版,Microsoft 并不完全支持这些内容。

如果需要在本地开发环境中对此进行测试,可以使用以下命令安装这些组件:

pip install azureml-contrib-services

注意

建议不要将 500 作为自定义状态代码,因为 azureml-fe 端的状态代码将重写为 502。

  • 状态代码通过 azureml-fe 传递,然后发送到客户端。
  • azureml-fe 只会将模型端返回的 500 重写为 502,客户端会收到 502。
  • 但是,如果 azureml-fe 本身返回 500,则客户端仍会收到 500。

AMLRequest 类仅允许访问 score.py 文件中的原始发布数据,没有客户端组件。 从客户端中,像往常一样发布数据。 例如,以下 Python 代码读取图像文件并发布数据:

import requests

uri = service.scoring_uri
image_path = 'test.jpg'
files = {'image': open(image_path, 'rb').read()}
response = requests.post(uri, files=files)

print(response.json)

跨域资源共享 (CORS)

通过跨源资源共享 (CORS) 可以从其他域请求网页上的资源。 CORS 通过 HTTP 标头工作,这些标头通过客户端请求发送并随服务响应返回。 若要详细了解 CORS 和有效标头,请参阅维基百科上的跨域资源共享 (CORS)

若要配置模型部署以支持 CORS,请在入口脚本中使用 AMLResponse 类。 使用此类,可设置响应对象的标头。

以下示例在入口脚本中设置响应的 Access-Control-Allow-Origin 标头:

from azureml.contrib.services.aml_request import AMLRequest, rawhttp
from azureml.contrib.services.aml_response import AMLResponse


def init():
    print("This is init()")

@rawhttp
def run(request):
    print("This is run()")
    print("Request: [{0}]".format(request))
    if request.method == 'GET':
        # For this example, just return the URL for GET.
        # For a real-world solution, you would load the data from URL params or headers
        # and send it to the model. Then return the response.
        respBody = str.encode(request.full_path)
        resp = AMLResponse(respBody, 200)
        resp.headers["Allow"] = "OPTIONS, GET, POST"
        resp.headers["Access-Control-Allow-Methods"] = "OPTIONS, GET, POST"
        resp.headers['Access-Control-Allow-Origin'] = "http://www.example.com"
        resp.headers['Access-Control-Allow-Headers'] = "*"
        return resp
    elif request.method == 'POST':
        reqBody = request.get_data(False)
        # For a real-world solution, you would load the data from reqBody
        # and send it to the model. Then return the response.
        resp = AMLResponse(reqBody, 200)
        resp.headers["Allow"] = "OPTIONS, GET, POST"
        resp.headers["Access-Control-Allow-Methods"] = "OPTIONS, GET, POST"
        resp.headers['Access-Control-Allow-Origin'] = "http://www.example.com"
        resp.headers['Access-Control-Allow-Headers'] = "*"
        return resp
    elif request.method == 'OPTIONS':
        resp = AMLResponse("", 200)
        resp.headers["Allow"] = "OPTIONS, GET, POST"
        resp.headers["Access-Control-Allow-Methods"] = "OPTIONS, GET, POST"
        resp.headers['Access-Control-Allow-Origin'] = "http://www.example.com"
        resp.headers['Access-Control-Allow-Headers'] = "*"
        return resp
    else:
        return AMLResponse("bad request", 400)

重要

AMLResponse 类位于 azureml.contrib 命名空间中。 此命名空间中的实体会频繁更改,因为我们正在改进服务。 此命名空间中的任何内容都应被视为预览版,Microsoft 并不完全支持这些内容。

如果需要在本地开发环境中对此进行测试,可以使用以下命令安装这些组件:

pip install azureml-contrib-services

警告

Azure 机器学习只会将 POST 和 GET 请求路由到运行评分服务的容器。 这可能导致错误,因为浏览器使用 OPTIONS 请求预检 CORS 请求。

加载已注册的模型

可以通过两种方法在入口脚本中查找模型:

  • AZUREML_MODEL_DIR:一个包含模型位置路径的环境变量
  • Model.get_model_path:一个 API,使用注册的模型名称返回指向模型文件的路径

AZUREML_MODEL_DIR

AZUREML_MODEL_DIR 是在服务部署过程中创建的环境变量。 可以使用此环境变量来查找部署的模型的位置。

下表描述了 AZUREML_MODEL_DIR 的值,它取决于部署的模型数:

部署 环境变量值
单个模型 包含模型的文件夹的路径。
多个模型 包含所有模型的文件夹的路径。 各个模型按名称和版本放置在此文件夹中 ($MODEL_NAME/$VERSION)

在模型注册和部署过程中,会将模型放置在 AZUREML_MODEL_DIR 路径中,并保留它们的原始文件名。

若要在入口脚本中获取某个模型文件的路径,请将此环境变量与要查找的文件路径组合在一起。

单个模型示例

# Example when the model is a file
model_path = os.path.join(os.getenv('AZUREML_MODEL_DIR'), 'sklearn_regression_model.pkl')

# Example when the model is a folder containing a file
file_path = os.path.join(os.getenv('AZUREML_MODEL_DIR'), 'my_model_folder', 'sklearn_regression_model.pkl')

多个模型示例

在此方案中,向工作区注册两个模型:

  • my_first_model:包含一个文件 (my_first_model.pkl),只有一个版本 1
  • my_second_model:包含一个文件 (my_second_model.pkl),有两个版本:12

部署服务后,部署操作中将同时提供两种模型:

first_model = Model(ws, name="my_first_model", version=1)
second_model = Model(ws, name="my_second_model", version=2)
service = Model.deploy(ws, "myservice", [first_model, second_model], inference_config, deployment_config)

在托管服务的 Docker 映像中,AZUREML_MODEL_DIR 环境变量包含模型所在的目录。 在此目录中,每个模型都位于 MODEL_NAME/VERSION 的目录路径中。 其中 MODEL_NAME 是已注册的模型的名称,VERSION 是模型的版本。 构成已注册的模型的文件存储在这些目录中。

在此示例中,路径将是 $AZUREML_MODEL_DIR/my_first_model/1/my_first_model.pkl$AZUREML_MODEL_DIR/my_second_model/2/my_second_model.pkl

# Example when the model is a file, and the deployment contains multiple models
first_model_name = 'my_first_model'
first_model_version = '1'
first_model_path = os.path.join(os.getenv('AZUREML_MODEL_DIR'), first_model_name, first_model_version, 'my_first_model.pkl')
second_model_name = 'my_second_model'
second_model_version = '2'
second_model_path = os.path.join(os.getenv('AZUREML_MODEL_DIR'), second_model_name, second_model_version, 'my_second_model.pkl')

get_model_path

注册模型时,请提供用于在注册表中管理该模型的模型名称。 将此名称与 Model.get_model_path() 方法结合使用,以检索本地文件系统上一个或多个模型文件的路径。 如果注册文件夹或文件集合,此 API 会返回包含这些文件的目录的路径。

注册模型时,请为其指定一个名称。 该名称对应于模型的放置位置(本地位置或在服务部署过程中指定的位置)。

特定于框架的示例

如需特定机器学习用例的更多入口脚本示例,请参阅以下文章: