Condividi tramite


Creazione avanzata di script di immissione

SI APPLICA A: Python SDK azureml v1

Questo articolo spiega come scrivere script di immissione per casi d'uso specifici.

Prerequisiti

Questo articolo presuppone la presenza di un modello di Machine Learning sottoposto a training da distribuire con Azure Machine Learning. Per informazioni sulla distribuzione modello, vedere Distribuire modelli di Machine Learning in Azure.

Si genera automaticamente uno schema di Swagger

Per generare automaticamente uno schema per il servizio Web, specificare un esempio di input e/o output nel costruttore per uno degli oggetti di tipo definiti. Il tipo e l'esempio vengono usati per creare automaticamente lo schema. Azure Machine Learning crea quindi una specifica OpenAPI (in precedenza Swagger) per il servizio Web durante la distribuzione.

Avviso

Non usare dati sensibili o privati per l'input o l'output di esempio. La pagina Swagger per l'inferenza ospitata in AML espone i dati di esempio.

Questi tipi sono attualmente supportati:

  • pandas
  • numpy
  • pyspark
  • Oggetto Python standard

Per usare la generazione dello schema, includere il pacchetto open source inference-schema versione 1.1.0 o successiva nel file delle dipendenze. Per altre informazioni su questo pacchetto, vedere InferenceSchema in GitHub. Per generare una specifica Swagger conforme per l'utilizzo del servizio Web automatizzato, la funzione run() dello script di assegnazione dei punteggi deve avere la seguente forma API:

  • Un primo parametro di tipo StandardPythonParameterType, denominato Inputs e annidato
  • Un secondo parametro facoltativo di tipo StandardPythonParameterType, denominato GlobalParameters
  • Restituire un dizionario di tipo StandardPythonParameterType, denominato Results e annidato

Definire i formati di esempio di input e output nelle variabili input_sample e output_sample, che rappresentano i formati di richiesta e risposta per il servizio Web. Usare questi esempi negli elementi Decorator di funzione di input e output nella funzione run(). Nell'esempio scikit-learn seguente viene usata la generazione dello schema.

Endpoint compatibile con Power BI

L'esempio seguente illustra come definire la forma API in base alle istruzioni precedenti. Questo metodo è supportato per l'utilizzo del servizio Web distribuito da 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

Suggerimento

Il valore restituito dallo script può essere un qualsiasi oggetto Python serializzabile in JSON. Ad esempio, se il modello restituisce un dataframe Pandas contenente più colonne, è possibile usare un elemento Decorator di output simile al codice seguente:

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

Dati binari (ovvero immagine)

Se il modello accetta dati binari, ad esempio un'immagine, è necessario modificare il file score.py usato per la distribuzione in modo che accetti richieste HTTP non elaborate. Per accettare dati non elaborati, usare la classe AMLRequest nello script di immissione e aggiungere l'elemento Decorator @rawhttp alla funzione run().

Ecco un esempio di un file score.py che accetta dati binari:

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

La classe AMLRequest si trova nello spazio dei nomi azureml.contrib. Le entità in questo spazio dei nomi cambiano di frequente poiché Microsoft è impegnata a migliorare il servizio. Qualsiasi elemento in questo spazio dei nomi deve essere pertanto considerato come anteprima non completamente supportata da Microsoft.

Se è necessario eseguire un test nell'ambiente di sviluppo locale, è possibile installare i componenti usando il comando seguente:

pip install azureml-contrib-services

Nota

500 non è consigliato come codice di stato personalizzato, perché sul lato azureml-fe il codice di stato verrà riscritto in 502.

  • Il codice di stato passa attraverso azureml-fe e quindi verrà inviato al client.
  • Azureml-fe riscrive solo il codice 500 restituito dal lato del modello come 502 e il client riceve 502.
  • Tuttavia, se azureml-fe restituisce 500, il lato client riceve comunque 500.

La classe AMLRequest consente solo di accedere ai dati pubblicati non elaborati presenti in score.py e non è presente alcun componente lato client. Da un client è possibile inserire i dati come al solito. Ad esempio, il codice Python seguente legge un file di immagine e inserisce i dati:

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)

Condivisione di risorse tra le origini (CORS)

La funzionalità Condivisione di risorse tra le origini (CORS) rappresenta un modo per abilitare la richiesta di un numero limitato di risorse in una pagina Web da parte di un altro dominio. Per funzionare, CORS usa intestazioni HTTP inviate con la richiesta del client e restituite con la risposta del servizio. Per altre informazioni su CORS e sulle intestazioni valide, vedere la voce Cross-origin resource sharing su Wikipedia.

Per configurare la distribuzione modello per supportare CORS, usare la classe AMLResponse nello script di immissione. Questa classe consente di impostare le intestazioni sull'oggetto risposta.

Nell'esempio seguente viene impostata l'intestazione Access-Control-Allow-Origin per la risposta dello script di immissione:

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

La classe AMLResponse si trova nello spazio dei nomi azureml.contrib. Le entità in questo spazio dei nomi cambiano di frequente poiché Microsoft è impegnata a migliorare il servizio. Qualsiasi elemento in questo spazio dei nomi deve essere pertanto considerato come anteprima non completamente supportata da Microsoft.

Se è necessario eseguire un test nell'ambiente di sviluppo locale, è possibile installare i componenti usando il comando seguente:

pip install azureml-contrib-services

Avviso

Azure Machine Learning instrada solo richieste POST e GET ai contenitori che eseguono il servizio di assegnazione dei punteggi. Questo può causare errori dovuti a browser che usano richieste OPTIONS per richieste CORS preliminari.

Caricare i modelli registrati

Sono disponibili due modalità per individuare i modelli nello script di immissione:

  • AZUREML_MODEL_DIR: una variabile di ambiente contenente il percorso della posizione del modello
  • Model.get_model_path: un'API che restituisce il percorso del file di modello usando il nome del modello registrato

AZUREML_MODEL_DIR

AZUREML_MODEL_DIR è una variabile di ambiente creata durante la distribuzione del servizio. È possibile usare questa variabile di ambiente per trovare il percorso dei modelli distribuiti.

La tabella seguente descrive il valore di AZUREML_MODEL_DIR in base al numero di modelli distribuiti:

Distribuzione Valore della variabile di ambiente
Modello singolo Percorso della cartella contenente il modello.
Modelli multipli Percorso della cartella contenente tutti i modelli. I modelli sono ordinati per nome e versione in questa cartella ($MODEL_NAME/$VERSION).

Durante la registrazione e la distribuzione del modello, i modelli vengono inseriti nel percorso AZUREML_MODEL_DIR e i relativi nomi file originali vengono mantenuti.

Per ottenere il percorso di un file di modello nello script di immissione, combinare la variabile di ambiente con il percorso del file desiderato.

Esempio di modello singolo

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

Esempio di modello multiplo

In questo scenario vengono registrati due modelli nell'area di lavoro:

  • my_first_model: contiene un file (my_first_model.pkl) ed è presente una sola versione, 1
  • my_second_model: contiene un file (my_second_model.pkl) e sono presenti due versioni, 1 e 2

Dopo la distribuzione del servizio, vengono forniti entrambi i modelli nell'operazione di distribuzione:

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)

Nell'immagine Docker che ospita il servizio la variabile di ambiente AZUREML_MODEL_DIR contiene la directory in cui si trovano i modelli. In questa directory ogni modello si trova in un percorso di directory di MODEL_NAME/VERSION. Dove MODEL_NAME è il nome del modello registrato e VERSION è la versione del modello. I file che costituiscono il modello registrato vengono archiviati in queste directory.

In questo esempio i percorsi saranno $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 si registra un modello, specificare un nome del modello usato per la gestione del modello nel Registro di sistema. Questo nome viene usato con il metodo Model.get_model_path() per recuperare il percorso del file o dei file del modello nel file system locale. Se si registra una cartella o una raccolta di file, questa API restituisce il percorso della directory che contiene tali file.

Quando si registra un modello, è necessario assegnargli un nome. Il nome corrisponde alla posizione in cui si trova il modello, ovvero in locale o durante la distribuzione del servizio.

Esempi specifici in base al framework

Vedere gli articoli seguenti per altri esempi di script di immissione per casi d'uso specifici di Machine Learning: