Criação de scripts de entrada avançados

APLICA-SE A: SDK do Python azureml v1

Este artigo explica como escrever scripts de entrada para casos de uso especializados.

Pré-requisitos

Este artigo pressupõe que você já tenha um modelo de machine learning treinado que pretende implantar com o Azure Machine Learning. Para saber mais sobre a implantação de modelos, confira Implantar modelos de machine learning no Azure.

Gerar automaticamente um esquema Swagger

Para gerar automaticamente um esquema para o serviço Web, forneça um exemplo da entrada e/ou da saída no construtor para um dos objetos de tipo definidos. O tipo e o exemplo são usados para criar automaticamente o esquema. Em seguida, o Azure Machine Learning cria uma especificação OpenAPI (anteriormente especificação do Swagger) para o serviço Web durante a implantação.

Aviso

Não utilize dados confidenciais ou privados para o exemplo de entrada ou saída. A página do Swagger para inferência hospedada em AML expõe os dados de exemplo.

Atualmente, há suporte para estes tipos:

  • pandas
  • numpy
  • pyspark
  • Objeto Python padrão

Para usar a geração de esquema, inclua a versão 1.1.0 ou superior do pacote de software livre inference-schema no arquivo de dependências. Para obter mais informações sobre este pacote, confira InferenceSchema no GitHub. Para gerar o Swagger em conformidade para consumo de serviço Web automatizado, a função run() de script de pontuação deve ter a forma de API de:

  • Um primeiro parâmetro do tipo StandardPythonParameterType, denominado Entradas e aninhado
  • Um segundo parâmetro opcional do tipo StandardPythonParameterType, chamado GlobalParameters
  • Retornar um dicionário do tipo StandardPythonParameterType, denominado Resultados e aninhado

Defina os formatos de exemplo de entrada e saída nas variáveis input_sample e output_sample, que representam os formatos de solicitação e resposta para o serviço Web. Use esses exemplos nos decoradores da função de entrada e saída na função run(). O exemplo scikit-learn a seguir usa geração de esquema.

Ponto de extremidade compatível com Power BI

O exemplo a seguir demonstra como definir a forma da API de acordo com a instrução anterior. Esse método tem suporte para consumo do serviço Web implantado com base no Power BI.

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

Dica

O valor retornado do script pode ser qualquer objeto Python que possa ser serializado para JSON. Por exemplo, se o modelo retornar um dataframe Pandas que contém várias colunas, você poderá usar um decorador de saída semelhante ao seguinte código:

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

Dados binários (ou seja, imagem)

Se o modelo aceitar dados binários, como uma imagem, você precisará modificar o arquivo score.py usado para a sua implantação aceitar solicitações HTTP brutas. Para aceitar dados brutos, use a classe AMLRequest no script de entrada e adicione o decorador @rawhttp à função run().

Veja a seguir um exemplo de score.py que aceita dados binários:

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)

Importante

A classe AMLRequest está no namespace azureml.contrib. As entidades nesse namespace mudam com frequência à medida que trabalhamos para melhorar o serviço. Tudo nesse namespace deve ser considerado como uma versão prévia que não tem suporte total da Microsoft.

Caso precise testar no seu ambiente de desenvolvimento local, instale os componentes usando o seguinte comando:

pip install azureml-contrib-services

Observação

500 não é recomendado como um código de status personalizado, pois no lado azureml-fe, o código de status será reescrito como 502.

  • O código de status é passado pelo azureml-fe e, em seguida, enviado ao cliente.
  • O azureml-fe reescreve apenas o 500 retornado do lado do modelo como 502, o cliente recebe 502.
  • Mas se o azureml-fe retornar 500, o lado do cliente ainda receberá 500.

A classe AMLRequest só permite que você acesse os dados brutos postados no arquivo score.py, não há nenhum componente do lado do cliente. Em um cliente, você posta dados normalmente. Por exemplo, o código Python a seguir lê um arquivo de imagem e posta os dados:

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 (Compartilhamento de Recursos entre Origens)

O compartilhamento de recursos entre origens é uma maneira de permitir que os recursos em uma página da Web sejam solicitados de outro domínio. O CORS funciona por meio de cabeçalhos HTTP enviados com a solicitação do cliente e retornados com a resposta do serviço. Para obter mais informações sobre CORS e cabeçalhos válidos, consulte compartilhamento de recursos entre origens na Wikipédia.

Para configurar a implantação de modelo para dar suporte a CORS, use a classe AMLResponse no script de entrada. Essa classe permite definir os cabeçalhos no objeto de resposta.

O exemplo a seguir define o cabeçalho Access-Control-Allow-Origin para a resposta com base no script de entrada:

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)

Importante

A classe AMLResponse está no namespace azureml.contrib. As entidades nesse namespace mudam com frequência à medida que trabalhamos para melhorar o serviço. Tudo nesse namespace deve ser considerado como uma versão prévia que não tem suporte total da Microsoft.

Caso precise testar no seu ambiente de desenvolvimento local, instale os componentes usando o seguinte comando:

pip install azureml-contrib-services

Aviso

O Azure Machine Learning roteia apenas solicitações POST e GET para os contêineres que executam o serviço de pontuação. Isso pode causar erros devido a navegadores que usam solicitações OPTIONS para solicitações preliminares de CORS.

Carregar modelos registrados

Existem duas maneiras de localizar modelos no script de entrada:

  • AZUREML_MODEL_DIR: uma variável de ambiente que contém o caminho para o local do modelo
  • Model.get_model_path: uma API que retorna o caminho para o arquivo de modelo usando o nome do modelo registrado

AZUREML_MODEL_DIR

AZUREML_MODEL_DIR é uma variável de ambiente criada durante a implantação do serviço. Pode ser usada para encontrar o local dos modelos implantados.

A tabela a seguir descreve o valor de AZUREML_MODEL_DIR dependendo do número de modelos implantados:

Implantação Valor da variável de ambiente
Um único modelo O caminho para a pasta que contém o modelo.
Vários modelos O caminho para a pasta que contém todos os modelos. Os modelos estão localizados por nome e versão nesta pasta ($MODEL_NAME/$VERSION).

Durante o registro e a implantação, os modelos são colocados no caminho de AZUREML_MODEL_DIR e seus nomes de arquivo originais são preservados.

Para obter o caminho para um arquivo de modelo no script de entrada, combine a variável de ambiente com o caminho de arquivo que você está procurando.

Exemplo de um único modelo

# 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')

Exemplo de vários modelos

Nesse cenário, dois modelos são registrados no espaço de trabalho:

  • my_first_model: contém um arquivo (my_first_model.pkl) e há apenas uma versão, 1
  • my_second_model: contém um arquivo (my_second_model.pkl) e há duas versões, 1 e 2

Quando o serviço foi implantado, ambos os modelos foram fornecidos na operação de implantação:

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)

Na imagem Docker que hospeda o serviço, a variável de ambiente AZUREML_MODEL_DIR contém o diretório em que os modelos estão localizados. Nesse diretório, cada um dos modelos está localizado em um caminho de diretório de MODEL_NAME/VERSION, em que MODEL_NAME é o nome do modelo registrado e VERSION é a versão do modelo. Os arquivos que compõem o modelo registrado são armazenados nesses diretórios.

Neste exemplo, os caminhos seriam $AZUREML_MODEL_DIR/my_first_model/1/my_first_model.pkl e $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

Quando você registra um modelo, fornece um nome de modelo que é usado para gerenciar o modelo no registro. Use esse nome com o método Model.get_model_path() para recuperar o caminho do arquivo ou arquivos de modelo no sistema de arquivos local. Se você registrar uma pasta ou uma coleção de arquivos, essa API retornará o caminho do diretório que contém esses arquivos.

Quando você registra um modelo, atribui um nome a ele. O nome corresponde ao local em que o modelo foi colocado, seja localmente ou durante a implantação do serviço.

Exemplos específicos de estrutura

Confira os seguintes artigos para obter mais exemplos de script de entrada para casos específicos de uso de machine learning: