Partager via


Création de scripts d’entrée avancés

S’APPLIQUE À :SDK Python azureml v1

Cet article explique comment écrire des scripts d’entrée pour des cas d’usage spécialisés.

Prérequis

Cet article suppose que vous disposez déjà d’un modèle Machine Learning formé que vous envisagez de déployer avec Azure Machine Learning. Pour en savoir plus sur le déploiement de modèles, consultez Déployer des modèles Machine Learning sur Azure.

Générer automatiquement un schéma Swagger

Si vous voulez générer automatiquement un schéma pour votre service web, spécifiez un exemple d’entrée et/ou de sortie dans le constructeur pour l’un des objets de type définis. Le type et l’exemple sont utilisés pour créer automatiquement le schéma. Azure Machine Learning crée ensuite une spécification OpenAPI (anciennement spécification Swagger) pour le service web pendant le déploiement.

Avertissement

Vous ne devez pas utiliser de données sensibles ou privées comme exemples d’entrée ou de sortie. La page Swagger pour l’inférence hébergée par AML expose les exemples de données.

Les types suivants sont pris en charge :

  • pandas
  • numpy
  • pyspark
  • Objet Python standard

Pour utiliser la génération de schéma, incluez le package inference-schema open source version 1.1.0 ou ultérieure dans votre fichier de dépendances. Pour plus d’informations sur ce package, consultez InferenceSchema sur GitHub. Pour générer un Swagger conforme pour une consommation de service web automatisé, la fonction run() du script de scoring doit avoir la forme d’API suivante :

  • Un premier paramètre de type StandardPythonParameterType, nommé Inputs et imbriqué
  • Un deuxième paramètre facultatif de type StandardPythonParameterType, nommé GlobalParameters
  • Retourne un dictionnaire de type StandardPythonParameterType, nommé Results et imbriqué

Définissez les exemples de formats d’entrée et de sortie dans les variables input_sample et output_sample, qui représentent les formats de requête et de réponse pour le service web. Utilisez ces exemples dans les éléments décoratifs des fonctions d’entrée et de sortie sur la fonction run(). L’exemple scikit-learn suivant utilise la génération de schéma.

Point de terminaison compatible Power BI

L’exemple suivant montre comment définir la forme d’API en fonction de l’instruction précédente. Cette méthode est prise en charge pour l’utilisation du service web déployé à partir de 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

Conseil

La valeur de retour du script peut être n’importe quel objet Python sérialisable en JSON. Par exemple, si votre modèle retourne une tramedonnées Pandas contenant plusieurs colonnes, vous pouvez utiliser un élément décoratif de sortie similaire au code suivant :

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

Données binaires (autrement dit, image)

Si votre modèle accepte des données binaires, comme une image, vous devez modifier le fichier score.py utilisé pour votre déploiement afin d’accepter les requêtes HTTP brutes. Pour accepter des données brutes, utilisez la classe AMLRequest dans votre script d’entrée et ajoutez l’ élément décoratif @rawhttp à la fonction run().

Voici l’exemple d’un score.py qui accepte des données binaires :

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)

Important

La classe AMLRequest se trouve dans l’espace de noms azureml.contrib. Les entités dans cet espace de noms changent fréquemment car nous cherchons à améliorer le service. Tout ce qui est compris dans cet espace de noms doit être considéré comme une préversion et n’est pas entièrement pris en charge par Microsoft.

Si vous avez besoin d’effectuer un test dans votre environnement de développement local, vous pouvez installer les composants à l’aide de la commande suivante :

pip install azureml-contrib-services

Notes

500 n’est pas recommandé comme code d’état personnalisé, car côté azureml-fe, le code d’état sera réécrit en 502.

  • Le code d’état est transmis via azureml-fe, puis envoyé au client.
  • Azureml-fe réécrit uniquement le code 500 retourné côté modèle pour devenir 502, le client reçoit 502.
  • Mais si azureml-fe lui-même retourne 500, le côté client reçoit toujours 500.

La classe AMLRequest vous permet d’accéder uniquement aux données publiées brutes dans score.py. Il n’y a aucun composant côté client. À partir d’un client, vous publiez des données normalement. Par exemple, le code Python suivant lit un fichier image et publie les données :

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)

Partage des ressources cross-origin (CORS)

Le partage des ressources Cross-Origin permet de demander des ressources dans une page web à partir d’un autre domaine. CORS fonctionne par le biais d’en-têtes HTTP envoyés avec la demande du client et retournés avec la réponse du service. Pour plus d’informations sur CORS et les en-têtes valides, consultez Cross-origin resource sharing sur Wikipédia.

Pour configurer que votre modèle de déploiement prend en charge le partage des ressources Cross-Origin, utilisez la classe AMLResponse dans votre script d’entrée. Cette classe vous permet de définir les en-têtes de l’objet de réponse.

L’exemple suivant définit l’en-tête Access-Control-Allow-Origin de la réponse à partir du script d’entrée :

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)

Important

La classe AMLResponse se trouve dans l’espace de noms azureml.contrib. Les entités dans cet espace de noms changent fréquemment car nous cherchons à améliorer le service. Tout ce qui est compris dans cet espace de noms doit être considéré comme une préversion et n’est pas entièrement pris en charge par Microsoft.

Si vous avez besoin d’effectuer un test dans votre environnement de développement local, vous pouvez installer les composants à l’aide de la commande suivante :

pip install azureml-contrib-services

Avertissement

Azure Machine Learning route uniquement les requêtes POST et GET vers les conteneurs exécutant le service de scoring. Cela peut provoquer des erreurs dues à des navigateurs qui utilisent des requêtes OPTIONS pour des requêtes CORS préalables.

Charger les modèles inscrits

Il existe deux façons de localiser les modèles dans votre script d’entrée :

  • AZUREML_MODEL_DIR : variable d’environnement contenant le chemin d’accès à l’emplacement du modèle
  • Model.get_model_path : API qui retourne le chemin d’accès au fichier de modèle d’après le nom de modèle inscrit

AZUREML_MODEL_DIR

AZUREML_MODEL_DIR est une variable d’environnement créée pendant le déploiement du service. Vous pouvez utiliser cette variable d’environnement pour rechercher l’emplacement de chaque modèle déployé.

Le tableau suivant décrit la valeur de AZUREML_MODEL_DIR en fonction du nombre de modèles déployés :

Déploiement Valeur de la variable d’environnement
Modèle unique Chemin du dossier contenant le modèle.
Modèles multiples Chemin du dossier contenant tous les modèles. Les modèles sont localisés par nom et version dans ce dossier ($MODEL_NAME/$VERSION)

Pendant l'inscription et le déploiement des modèles, ceux-ci sont placés à l'emplacement AZUREML_MODEL_DIR et ils conservent leur nom de fichier d'origine.

Pour obtenir le chemin d'accès à un fichier modèle dans votre script d'entrée, combinez la variable d'environnement avec le chemin du fichier que vous recherchez.

Exemple avec un modèle unique

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

Exemple avec des modèles multiples

Dans ce scénario, deux modèles sont inscrits auprès de l’espace de travail :

  • my_first_model : contient un fichier (my_first_model.pkl) et il n’existe qu’une seule version, 1
  • my_second_model : contient un fichier (my_second_model.pkl) et il existe deux versions, 1 et 2

Quand le service est déployé, les deux modèles sont fournis dans l’opération de déploiement :

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)

Dans l’image Docker qui héberge le service, la variable d’environnement AZUREML_MODEL_DIR contient le répertoire où se trouvent les modèles. Dans ce répertoire, chacun des modèles se trouve à un emplacement de répertoire MODEL_NAME/VERSION. Où MODEL_NAME est le nom du modèle inscrit et VERSION est la version du modèle. Les fichiers qui composent le modèle inscrit sont stockés dans ces répertoires.

Dans cet exemple, les chemins seraient $AZUREML_MODEL_DIR/my_first_model/1/my_first_model.pkl et $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

Quand vous inscrivez un modèle, vous fournissez un nom de modèle qui est utilisé pour gérer le modèle dans le Registre. Vous utilisez ce nom avec la méthode Model.get_model_path() pour récupérer le chemin du ou des fichiers de modèle sur le système de fichiers local. Si vous inscrivez un dossier ou une collection de fichiers, cette API retourne le chemin du répertoire contenant ces fichiers.

Quand vous inscrivez un modèle, vous lui donnez un nom. Le nom correspond à l’endroit où le modèle est placé, localement ou durant le déploiement du service.

Exemples spécifiques de l’infrastructure

Consultez les articles suivants pour obtenir d’autres exemples de script d’entrée pour des cas d’usage spécifiques du Machine Learning :