Débogage du script de scoring avec le serveur HTTP d’inférence Azure Machine Learning

Le serveur HTTP d’inférence Azure Machine Learning est un package Python qui expose votre fonction de scoring en tant que point de terminaison HTTP et encapsule le code et les dépendances du serveur Flask dans un même package. Il est inclus dans les images conteneur Docker prédéfinies pour l’inférence qui sont utilisées lors du déploiement d’un modèle avec Azure Machine Learning. En utilisant le package seul, vous pouvez déployer le modèle localement pour la production, et vous pouvez aussi valider facilement votre script de scoring (entrée) dans un environnement de développement local. En cas de problème avec le script de scoring, le serveur retourne une erreur et l’emplacement où l’erreur s’est produite.

Le serveur peut également être utilisé pour créer des portes de validation dans un pipeline d’intégration et de déploiement continus. Par exemple, vous pouvez démarrer le serveur avec le script candidat et exécutez la suite de tests sur le point de terminaison local.

Cet article s’adresse principalement aux utilisateurs qui veulent utiliser le serveur d’inférence pour déboguer localement, mais il vous aidera aussi à comprendre comment utiliser le serveur d’inférence avec des points de terminaison en ligne.

Débogage local du point de terminaison en ligne

Le débogage des points de terminaison localement avant le déploiement dans le cloud peut vous aider à détecter plus tôt les erreurs dans votre code et votre configuration. Pour déboguer des points de terminaison localement, vous pouvez utiliser :

Cet article se concentre sur le serveur HTTP d’inférence Azure Machine Learning.

Le tableau suivant fournit une vue d’ensemble des scénarios pour vous aider à choisir la solution la mieux adaptée.

Scénario Serveur HTTP d’inférence Point de terminaison local
Mettre à jour l’environnement Python local, sans regénération de l’image Docker Oui Non
Mettre à jour le script de scoring Oui Oui
Mettre à jour les configurations de déploiement (déploiement, environnement, code, modèle) Non Oui
Intégrer le débogueur VS Code Oui Oui

En exécutant le serveur HTTP d’inférence localement, vous pouvez vous concentrer sur le débogage de votre script de scoring sans être affecté par les configurations du conteneur de déploiement.

Prérequis

  • Requis : Python >=3.8
  • Anaconda

Conseil

Le serveur HTTP d’inférence Azure Machine Learning s’exécute sur les systèmes d’exploitation Windows et Linux.

Installation

Notes

Pour éviter les conflits de packages, installez le serveur dans un environnement virtuel.

Pour installer le azureml-inference-server-http package, exécutez la commande suivante à votre invite de commande ou dans votre terminal :

python -m pip install azureml-inference-server-http

Déboguer votre script de scoring localement

Pour déboguer votre script de scoring localement, vous pouvez tester le comportement du serveur avec un script de scoring factice, utiliser VS Code pour déboguer avec le package azureml-inference-server-http ou tester le serveur avec un script de scoring réel, un fichier de modèle et un fichier d’environnement provenant de notre dépôt d’exemples.

Tester le comportement du serveur avec un script de scoring factice

  1. Créez un répertoire pour stocker vos fichiers :

    mkdir server_quickstart
    cd server_quickstart
    
  2. Pour éviter les conflits de package, créez un environnement virtuel et activez-le :

    python -m venv myenv
    source myenv/bin/activate
    

    Conseil

    Après le test, exécutez deactivate pour désactiver l’environnement virtuel Python.

  3. Installez le package azureml-inference-server-http à partir du flux pypi :

    python -m pip install azureml-inference-server-http
    
  4. Créez votre script d’entrée (score.py). L’exemple suivant crée un script d’entrée de base :

    echo '
    import time
    
    def init():
        time.sleep(1)
    
    def run(input_data):
        return {"message":"Hello, World!"}
    ' > score.py
    
  5. Démarrez le serveur (azmlinfsrv) et définissez score.py comme script d’entrée :

    azmlinfsrv --entry_script score.py
    

    Notes

    Le serveur est hébergé à 0.0.0.0, ce qui signifie qu’il doit écouter toutes les adresses IP de l’ordinateur hôte.

  6. Envoyez une requête de scoring au serveur à l’aide de curl :

    curl -p 127.0.0.1:5001/score
    

    Le serveur doit répondre comme ceci.

    {"message": "Hello, World!"}
    

Après le test, vous pouvez appuyer sur Ctrl + C pour arrêter le serveur. Vous pouvez maintenant modifier le script de scoring (score.py) et tester vos modifications en réexécutant le serveur (azmlinfsrv --entry_script score.py).

Comment procéder à l’intégration à Visual Studio Code

Il existe deux façons d’utiliser Visual Studio Code (VS Code) et l’extension Python pour déboguer avec le package azureml-inference-server-http (Modes Lancement et Attachement).

  • Mode Lancement : configurez le fichier launch.json dans VS Code et démarrez le serveur HTTP d’inférence Azure Machine Learning au sein de VS Code.

    1. Démarrez VS Code et ouvrez le dossier contenant le script (score.py).

    2. Ajoutez la configuration suivante à launch.json pour cet espace de travail dans VS Code :

      launch.json

      {
          "version": "0.2.0",
          "configurations": [
              {
                  "name": "Debug score.py",
                  "type": "python",
                  "request": "launch",
                  "module": "azureml_inference_server_http.amlserver",
                  "args": [
                      "--entry_script",
                      "score.py"
                  ]
              }
          ]
      }
      
    3. Démarrez une session de débogage dans VS Code. Sélectionnez « Exécuter » -> « Démarrer le débogage » (ou F5).

  • Mode Attacher : démarrez le serveur HTTP d’inférence Azure Machine Learning dans une ligne de commande et utilisez VS Code + l’extension Python pour effectuer l’attachement au processus.

    Notes

    Si vous utilisez un environnement Linux, installez d’abord le package gdb en exécutant sudo apt-get install -y gdb.

    1. Ajoutez la configuration suivante à launch.json pour cet espace de travail dans VS Code :

      launch.json

      {
          "version": "0.2.0",
          "configurations": [
              {
                  "name": "Python: Attach using Process Id",
                  "type": "python",
                  "request": "attach",
                  "processId": "${command:pickProcess}",
                  "justMyCode": true
              },
          ]
      }
      
    2. Démarrez le serveur d’inférence en utilisant l’interface CLI (azmlinfsrv --entry_script score.py).

    3. Démarrez une session de débogage dans VS Code.

      1. Dans VS Code, sélectionnez « Exécuter » -> « Démarrer le débogage » (ou F5).
      2. Entrez l’ID de processus de azmlinfsrv (et non pas de gunicorn) en utilisant les journaux (provenant du serveur d’inférence) affichés dans l’interface CLI. Capture d’écran de l’interface CLI montrant l’ID de processus du serveur.

      Notes

      Si le sélecteur de processus ne s’affiche pas, entrez manuellement l’ID de processus dans le champ processId du fichier launch.json.

Dans ces deux méthodes, vous pouvez définir un point d’arrêt et déboguer pas à pas.

Exemple de bout en bout

Dans cette section, nous allons exécuter le serveur localement avec des exemples de fichiers (script de scoring, fichier de modèle et environnement) dans notre dépôt d’exemple. Les exemples de fichiers sont également utilisés dans notre article Déployer et établir le score d’un modèle Machine Learning en utilisant un point de terminaison en ligne

  1. Clonez l'exemple de référentiel.

    git clone --depth 1 https://github.com/Azure/azureml-examples
    cd azureml-examples/cli/endpoints/online/model-1/
    
  2. Créez et activez un environnement virtuel avec conda. Dans cet exemple, le package azureml-inference-server-http est installé automatiquement, car il est inclus en tant que bibliothèque dépendante du package azureml-defaults dans conda.yml, comme suit.

    # Create the environment from the YAML file
    conda env create --name model-env -f ./environment/conda.yml
    # Activate the new environment
    conda activate model-env
    
  3. Passez en revue votre script de scoring.

    onlinescoring/score.py

    import os
    import logging
    import json
    import numpy
    import joblib
    
    
    def init():
        """
        This function is called when the container is initialized/started, typically after create/update of the deployment.
        You can write the logic here to perform init operations like caching the model in memory
        """
        global model
        # AZUREML_MODEL_DIR is an environment variable created during deployment.
        # It is the path to the model folder (./azureml-models/$MODEL_NAME/$VERSION)
        # Please provide your model's folder name if there is one
        model_path = os.path.join(
            os.getenv("AZUREML_MODEL_DIR"), "model/sklearn_regression_model.pkl"
        )
        # deserialize the model file back into a sklearn model
        model = joblib.load(model_path)
        logging.info("Init complete")
    
    
    def run(raw_data):
        """
        This function is called for every invocation of the endpoint to perform the actual scoring/prediction.
        In the example we extract the data from the json input and call the scikit-learn model's predict()
        method and return the result back
        """
        logging.info("model 1: request received")
        data = json.loads(raw_data)["data"]
        data = numpy.array(data)
        result = model.predict(data)
        logging.info("Request processed")
        return result.tolist()
    
  4. Exécutez le serveur d’inférence en spécifiant le script de scoring et le fichier de modèle. Le répertoire du modèle spécifié (le paramètre model_dir) est défini en tant que variable AZUREML_MODEL_DIR et récupéré dans le script de scoring. Dans le cas présent, nous spécifions le répertoire actif (./), car le sous-répertoire est spécifié dans le script de scoring en tant que model/sklearn_regression_model.pkl.

    azmlinfsrv --entry_script ./onlinescoring/score.py --model_dir ./
    

    L’exemple de journal de démarrage s’affiche si le serveur a été lancé et que le script de scoring a été appelé correctement. Sinon, des messages d’erreur apparaîtront dans le journal.

  5. Testez le script de scoring avec un exemple de données. Ouvrez un autre terminal et accédez au même répertoire de travail pour exécuter la commande. Utilisez la commande curl pour envoyer un exemple de demande au serveur et recevoir un résultat de scoring.

    curl --request POST "127.0.0.1:5001/score" --header "Content-Type:application/json" --data @sample-request.json
    

    Le résultat du scoring est retourné s’il n’y a pas de problème dans votre script de scoring. Si vous voyez quelque chose d’incorrect, vous pouvez essayer de mettre à jour le script de scoring et relancer le serveur pour tester le script mis à jour.

Itinéraires du serveur

Le serveur est à l’écoute sur le port 5001 (le port par défaut) pour ces routes.

Nom Route
Diagnostic Probe Liveness 127.0.0.1:5001/
Score 127.0.0.1:5001/score
OpenAPI (Swagger) 127.0.0.1:5001/swagger.json

Paramètres de serveur

Le tableau suivant contient les paramètres acceptés par le serveur :

Paramètre Obligatoire Default Description
entry_script Vrai N/A Chemin relatif ou absolu du script de scoring.
model_dir Faux N/A Chemin relatif ou absolu du répertoire contenant le modèle utilisé pour l’inférence.
port False 5001 Port de service du serveur.
worker_count False 1 Nombre de threads de travail qui traiteront les requêtes simultanées.
appinsights_instrumentation_key Faux N/A Clé d’instrumentation vers Application Insights où les journaux seront publiés.
access_control_allow_origins False N/A Activez CORS pour les origines spécifiées. Séparez plusieurs origines par « , ».
Exemple : « microsoft.com, bing.com »

Flux de demande

Les étapes suivantes expliquent comment le serveur HTTP d’inférence Azure Machine Learning (azmlinfsrv) gère les demandes entrantes :

  1. Un wrapper de l’interface CLI Python réside autour de la pile réseau du serveur, et est utilisé pour démarrer le serveur.
  2. Un client envoie une requête au serveur.
  3. Lorsqu’une requête est reçue, elle passe par le serveur WSGI et est ensuite distribuée à l’un des workers.
  4. Les requêtes sont ensuite gérées par une application Flask, qui charge le script d’entrée et les éventuelles dépendances.
  5. Pour finir, la requête est envoyée à votre script d’entrée. Le script d’entrée effectue ensuite un appel d’inférence au modèle chargé, et retourne une réponse.

Diagramme du processus du serveur HTTP.

Présentation des journaux

Nous décrivons ici les journaux du serveur HTTP d’inférence Azure Machine Learning. Vous pouvez obtenir le journal quand vous exécutez azureml-inference-server-http localement, ou obtenir des journaux du conteneur si vous utilisez des points de terminaison en ligne.

Notes

Le format de journalisation a changé depuis la version 0.8.0. Si vous trouvez votre journal dans un style différent, mettez à jour le package azureml-inference-server-http vers la dernière version.

Conseil

Si vous utilisez des points de terminaison en ligne, le journal du serveur d’inférence commence par Azure Machine Learning Inferencing HTTP server <version>.

Journaux de démarrage

Quand le serveur est démarré, les paramètres du serveur sont affichés en premier par les journaux, comme suit :

Azure Machine Learning Inferencing HTTP server <version>


Server Settings
---------------
Entry Script Name: <entry_script>
Model Directory: <model_dir>
Worker Count: <worker_count>
Worker Timeout (seconds): None
Server Port: <port>
Application Insights Enabled: false
Application Insights Key: <appinsights_instrumentation_key>
Inferencing HTTP server version: azmlinfsrv/<version>
CORS for the specified origins: <access_control_allow_origins>


Server Routes
---------------
Liveness Probe: GET   127.0.0.1:<port>/
Score:          POST  127.0.0.1:<port>/score

<logs>

Par exemple, quand vous lancez le serveur, suivez l’exemple de bout en bout :

Azure Machine Learning Inferencing HTTP server v0.8.0


Server Settings
---------------
Entry Script Name: /home/user-name/azureml-examples/cli/endpoints/online/model-1/onlinescoring/score.py
Model Directory: ./
Worker Count: 1
Worker Timeout (seconds): None
Server Port: 5001
Application Insights Enabled: false
Application Insights Key: None
Inferencing HTTP server version: azmlinfsrv/0.8.0
CORS for the specified origins: None


Server Routes
---------------
Liveness Probe: GET   127.0.0.1:5001/
Score:          POST  127.0.0.1:5001/score

2022-12-24 07:37:53,318 I [32726] gunicorn.error - Starting gunicorn 20.1.0
2022-12-24 07:37:53,319 I [32726] gunicorn.error - Listening at: http://0.0.0.0:5001 (32726)
2022-12-24 07:37:53,319 I [32726] gunicorn.error - Using worker: sync
2022-12-24 07:37:53,322 I [32756] gunicorn.error - Booting worker with pid: 32756
Initializing logger
2022-12-24 07:37:53,779 I [32756] azmlinfsrv - Starting up app insights client
2022-12-24 07:37:54,518 I [32756] azmlinfsrv.user_script - Found user script at /home/user-name/azureml-examples/cli/endpoints/online/model-1/onlinescoring/score.py
2022-12-24 07:37:54,518 I [32756] azmlinfsrv.user_script - run() is not decorated. Server will invoke it with the input in JSON string.
2022-12-24 07:37:54,518 I [32756] azmlinfsrv.user_script - Invoking user's init function
2022-12-24 07:37:55,974 I [32756] azmlinfsrv.user_script - Users's init has completed successfully
2022-12-24 07:37:55,976 I [32756] azmlinfsrv.swagger - Swaggers are prepared for the following versions: [2, 3, 3.1].
2022-12-24 07:37:55,977 I [32756] azmlinfsrv - AML_FLASK_ONE_COMPATIBILITY is set, but patching is not necessary.

Format de journal

Les journaux du serveur d’inférence sont générés au format suivant, à l’exception des scripts de lanceur, car ils ne font pas partie du package Python :

<UTC Time> | <level> [<pid>] <logger name> - <message>

Ici, <pid> est l’ID de processus et <level> est le premier caractère du niveau de journalisation : E pour ERREUR, I pour INFORMATION, etc.

Il existe six niveaux de journalisation dans Python, avec des numéros associés à la gravité :

Niveau de journalisation Valeur numérique
CRITIQUE 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

Guide de résolution des problèmes

Dans cette section, nous allons fournir des conseils de résolution des problèmes de base pour le serveur HTTP d’inférence Azure Machine Learning. Si vous voulez résoudre les problèmes des points de terminaison en ligne, consultez également Résolution des problèmes de déploiement des points de terminaison en ligne

Étapes de base

Les étapes de base pour la résolution des problèmes sont les suivantes :

  1. Collectez des informations de version pour votre environnement Python.
  2. Assurez-vous que la version du package python azureml-inference-server-http spécifiée dans le fichier d’environnement correspond à la version du serveur HTTP d’inférence AzureML affichée dans le journal de démarrage. Parfois, le programme de résolution de dépendances de pip conduit à des versions inattendues des packages installés.
  3. Si vous spécifiez Flask (et/ou ses dépendances) dans votre environnement, supprimez-les. Les dépendances incluent Flask, Jinja2, itsdangerous, Werkzeug, MarkupSafe, et click. Flask est répertorié en tant que dépendance dans le package de serveur et il est préférable de laisser notre serveur l’installer. De cette façon, lorsque le serveur prend en charge de nouvelles versions de Flask, vous les obtiendrez automatiquement.

Version du serveur

Le package de serveur azureml-inference-server-http est publié dans PyPI. Vous trouverez notre journal des modifications et toutes les versions précédentes sur notre page PyPI. Mettez à jour vers la dernière version si vous utilisez une version antérieure.

  • 0.4.x : version groupée dans les images d’entraînement ≤ 20220601 et dans azureml-defaults>=1.34,<=1.43. 0.4.13 est la dernière version stable. Si vous utilisez le serveur avant la version 0.4.11, vous pouvez voir des problèmes de dépendance Flask tels que l’impossibilité d’importer le nom Markup à partir de jinja2. Il est recommandé d’effectuer une mise à niveau vers 0.4.13 ou 0.8.x (la dernière version), si possible.
  • 0.6.x : version préinstallée dans les images d’inférence ≤ 20220516. La dernière version stable est la version 0.6.1.
  • 0.7.x : première version qui prend en charge Flask 2. La dernière version stable est la version 0.7.7.
  • 0.8.x : le format du journal a changé et la prise en charge de Python 3.6 n’est plus effective.

Dépendances de package

Les packages les plus pertinents pour le serveur azureml-inference-server-http sont les packages suivants :

  • flask
  • opencensus-ext-azure
  • inference-schema

Si vous avez spécifié azureml-defaults dans votre environnement Python, le package azureml-inference-server-http en dépendra et sera installé automatiquement.

Conseil

Si vous utilisez le kit de développement logiciel (SDK) Python v1 et que vous ne spécifiez pas azureml-defaults explicitement dans votre environnement Python, le SDK peut ajouter le package à votre place. Toutefois, il le verrouille avec la version sur laquelle le SDK est activé. Par exemple, si la version du SDK est 1.38.0, elle ajoutera azureml-defaults==1.38.0 aux exigences pip de l’environnement.

Forum aux questions

1. J’ai rencontré l’erreur suivante lors du démarrage du serveur :


TypeError: register() takes 3 positional arguments but 4 were given

  File "/var/azureml-server/aml_blueprint.py", line 251, in register

    super(AMLBlueprint, self).register(app, options, first_registration)

TypeError: register() takes 3 positional arguments but 4 were given

Flask 2 est installé dans votre environnement Python, mais vous exécutez une version de azureml-inference-server-http qui ne prend pas en charge Flask 2. La prise en charge de Flask 2 est ajoutée dans azureml-inference-server-http>=0.7.0, qui se trouve également dans azureml-defaults>=1.44.

  • Si vous ne vous servez pas de ce package dans une image Docker AzureML, utilisez la dernière version de azureml-inference-server-http ou de azureml-defaults.

  • Si vous utilisez ce package avec une image Docker AzureML, vérifiez que celle-ci a été générée en juillet 2022 ou après. La version de l’image est disponible dans les journaux du conteneur. Vous devriez trouver un journal qui se présente ainsi :

    2022-08-22T17:05:02,147738763+00:00 | gunicorn/run | AzureML Container Runtime Information
    2022-08-22T17:05:02,161963207+00:00 | gunicorn/run | ###############################################
    2022-08-22T17:05:02,168970479+00:00 | gunicorn/run | 
    2022-08-22T17:05:02,174364834+00:00 | gunicorn/run | 
    2022-08-22T17:05:02,187280665+00:00 | gunicorn/run | AzureML image information: openmpi4.1.0-ubuntu20.04, Materializaton Build:20220708.v2
    2022-08-22T17:05:02,188930082+00:00 | gunicorn/run | 
    2022-08-22T17:05:02,190557998+00:00 | gunicorn/run | 
    

    La date de génération de l’image s’affiche après « Build de matérialisation » : 20220708 dans l’exemple ci-dessus, soit le 8 juillet 2022. Cette image est compatible avec Flask 2. Si vous ne voyez pas de bannière comme celle-ci dans le journal de votre conteneur, cela signifie que votre image est obsolète et doit être mise à jour. Si vous utilisez une image CUDA et que vous ne parvenez pas à trouver une image plus récente, vérifiez si elle est déconseillée dans AzureML-Containers. Si c’est le cas, vous devriez trouver des remplacements.

  • Si vous utilisez le serveur avec un point de terminaison en ligne, vous trouverez également les journaux sous « Journaux de déploiement » sur la page des points de terminaison en ligne d’Azure Machine Learning studio. Si vous effectuez le déploiement avec le kit SDK v1 et que vous ne spécifiez pas explicitement d’image dans la configuration de votre déploiement, le kit SDK utilise par défaut une version de openmpi4.1.0-ubuntu20.04 correspondant à votre ensemble d’outils SDK local. Il ne s’agit pas nécessairement de la dernière version de l’image. Par exemple, le kit SDK 1.43 emploie par défaut openmpi4.1.0-ubuntu20.04:20220616, qui est incompatible. Veillez à utiliser la dernière version du kit SDK pour votre déploiement.

  • Si, pour une raison quelconque, vous ne parvenez pas à mettre à jour l’image, vous pouvez éviter temporairement le problème en épinglant azureml-defaults==1.43 ou azureml-inference-server-http~=0.4.13. Cette action aura pour effet d’installer le serveur de l’ancienne version avec Flask 1.0.x.

2. J’ai rencontré une erreur ImportError ou ModuleNotFoundError sur des modules opencensus, jinja2, MarkupSafe ou click pendant le démarrage :

ImportError: cannot import name 'Markup' from 'jinja2'

Les versions antérieures (<= 0.4.10) du serveur n’épinglaient pas la dépendance de Flask à des versions compatibles. Ce problème est corrigé dans la dernière version du serveur.

Étapes suivantes