Partage via


Traitement d’images avec des modèles de déploiement par lots

S’APPLIQUE À :Extension Azure ML CLI v2 (actuelle)Kit de développement logiciel (SDK) Python azure-ai-ml v2 (préversion)

Les modèles de déploiement par lots peuvent être utilisés pour traiter des données tabulaires, mais aussi tout autre type de fichier, comme des images. Ces déploiements sont pris en charge dans les modèles MLflow et personnalisés. Dans ce tutoriel, nous allons apprendre à déployer un modèle qui classifie les images en fonction de la taxonomie ImageNet.

À propos de cet exemple

Le modèle avec lequel nous allons travailler a été créé à l’aide de TensorFlow avec l’architecture RestNet (Mappages d’identités dans les réseaux résiduels profonds). Vous pouvez télécharger un exemple de ce modèle ici. Le modèle présente les contraintes suivantes qu’il est important de garder à l’esprit pour le déploiement :

  • Il fonctionne avec des images de taille 244x244 (tenseurs de (224, 224, 3)).
  • Il nécessite que les entrées soient mises à l’échelle à la plage [0,1].

Les informations de cet article sont basées sur des exemples de code contenus dans le référentiel azureml-examples. Pour exécuter les commandes localement sans avoir à copier/coller le fichier YAML et d’autres fichiers, clonez le dépôt, puis définissez les répertoires sur cli/endpoints/batch/deploy-models/imagenet-classifier si vous utilisez l’interface Azure CLI ou sur sdk/python/endpoints/batch/deploy-models/imagenet-classifier si vous utilisez notre SDK pour Python.

git clone https://github.com/Azure/azureml-examples --depth 1
cd azureml-examples/cli/endpoints/batch/deploy-models/imagenet-classifier

Suivre dans les notebooks Jupyter

Vous pouvez suivre cet exemple dans un notebook Jupyter. Dans le dépôt cloné, ouvrez le notebook : imagenet-classifier-batch.ipynb.

Prérequis

Avant de suivre les étapes décrites dans cet article, vérifiez que vous disposez des composants requis suivants :

  • Un abonnement Azure. Si vous n’avez pas d’abonnement Azure, créez un compte gratuit avant de commencer. Essayez la version gratuite ou payante d’Azure Machine Learning.

  • Un espace de travail Azure Machine Learning. Si vous n’en avez pas, suivez les étapes décrites dans l’article Gérer des espaces de travail Azure Machine Learning pour en créer un.

  • Vérifiez que vous disposez des autorisations suivantes dans l’espace de travail :

    • Créez ou gérez des points de terminaison et des déploiements par lots : utilisez un rôle propriétaire, contributeur ou personnalisé autorisant Microsoft.MachineLearningServices/workspaces/batchEndpoints/*.

    • Créez des déploiements ARM dans le groupe de ressources de l’espace de travail : utilisez un rôle propriétaire, contributeur ou personnalisé autorisant Microsoft.Resources/deployments/write dans le groupe de ressources où l’espace de travail est déployé.

  • Vous devez installer le logiciel suivant pour utiliser Azure Machine Learning :

    L’interface Azure CLI et l’mlextension pour Azure Machine Learning.

    az extension add -n ml
    

    Notes

    Les déploiements de composants de pipeline pour des points de terminaison Batch ont été introduits dans la version 2.7 de l’extension ml pour Azure CLI. Utilisez az extension update --name ml pour en obtenir la dernière version.

Se connecter à un espace de travail

L’espace de travail est la ressource de niveau supérieur pour Azure Machine Learning. Il fournit un emplacement centralisé dans lequel exploiter tous les artefacts que vous créez lorsque vous utilisez Azure Machine Learning. Dans cette section, nous allons nous connecter à l’espace de travail dans lequel vous allez effectuer des tâches de déploiement.

Transmettez les valeurs de votre ID d’abonnement, de votre espace de travail, de votre emplacement et de votre groupe de ressources dans le code suivant :

az account set --subscription <subscription>
az configure --defaults workspace=<workspace> group=<resource-group> location=<location>

Classification d’images avec déploiements par lots

Dans cet exemple, nous allons apprendre à déployer un modèle d’apprentissage automatique qui peut classifier une image donnée en fonction de la taxonomie d’ImageNet.

Créer le point de terminaison

Commençons par créer le point de terminaison qui hébergera le modèle :

Choisissez le nom du point de terminaison :

ENDPOINT_NAME="imagenet-classifier-batch"

Le fichier YAML suivant définit un point de terminaison de lot :

endpoint.yml

$schema: https://azuremlschemas.azureedge.net/latest/batchEndpoint.schema.json
name: imagenet-classifier-batch
description: A batch endpoint for performing image classification using a TFHub model ImageNet model.
auth_mode: aad_token

Exécutez le code suivant pour créer le point de terminaison.

az ml batch-endpoint create --file endpoint.yml  --name $ENDPOINT_NAME

Inscription du modèle

Un modèle de déploiement ne peut déployer que des modèles inscrits, nous devons donc l’inscrire. Vous pouvez ignorer cette étape si le modèle que vous essayez de déployer est déjà inscrit.

  1. Téléchargement d’une copie du modèle :

    wget https://azuremlexampledata.blob.core.windows.net/data/imagenet/model.zip
    unzip model.zip -d .
    
  2. Inscrivez le modèle :

    MODEL_NAME='imagenet-classifier'
    az ml model create --name $MODEL_NAME --path "model"
    

Création d’un script de scoring

Nous devons créer un script de scoring qui peut lire les images fournies par le déploiement par lot et retourner les scores du modèle. Le script suivant :

  • Indique une fonction init qui charge le modèle à l’aide du module keras dans tensorflow.
  • Indique une fonction run exécutée pour chaque mini-lot fourni par le déploiement par lot.
  • La fonction run lit une image du fichier à la fois
  • La méthode run redimensionne les images aux tailles attendues du modèle.
  • La méthode run redimensionne les images vers le domaine de plage [0,1], ce qui est ce que le modèle attend.
  • Elle retourne les classes et les probabilités associées aux prédictions.

code/score-by-file/batch_driver.py

import os
import numpy as np
import pandas as pd
import tensorflow as tf
from os.path import basename
from PIL import Image
from tensorflow.keras.models import load_model


def init():
    global model
    global input_width
    global input_height

    # AZUREML_MODEL_DIR is an environment variable created during deployment
    model_path = os.path.join(os.environ["AZUREML_MODEL_DIR"], "model")

    # load the model
    model = load_model(model_path)
    input_width = 244
    input_height = 244


def run(mini_batch):
    results = []

    for image in mini_batch:
        data = Image.open(image).resize(
            (input_width, input_height)
        )  # Read and resize the image
        data = np.array(data) / 255.0  # Normalize
        data_batch = tf.expand_dims(
            data, axis=0
        )  # create a batch of size (1, 244, 244, 3)

        # perform inference
        pred = model.predict(data_batch)

        # Compute probabilities, classes and labels
        pred_prob = tf.math.reduce_max(tf.math.softmax(pred, axis=-1)).numpy()
        pred_class = tf.math.argmax(pred, axis=-1).numpy()

        results.append([basename(image), pred_class[0], pred_prob])

    return pd.DataFrame(results)

Conseil

Bien que les images soient fournies dans des mini-lots par le déploiement, ce script de scoring traite une image à la fois. Il s’agit d’un modèle courant, car une tentative de charger l’intégralité du lot et de l’envoyer au modèle en une fois peut entraîner une pression de mémoire élevée sur l’exécuteur de lot (exceptions de mémoire insuffisante). Toutefois, il existe certains cas où cela permet un débit élevé dans les tâches de scoring. Il s’agit de l’exemple de déploiements par lots sur un matériel GPU où nous voulons obtenir une utilisation élevée du GPU. Consultez Déploiements à haut débit pour obtenir un exemple de script de scoring qui en tire parti.

Notes

Si vous essayez de déployer un modèle génératif (qui génère des fichiers), découvrez comment créer un script de scoring dans Déploiement de modèles qui produisent plusieurs fichiers.

Création du déploiement

Un script de scoring est créé ; il est temps de créer un déploiement par lots pour celui-ci. Suivez les étapes suivantes pour le créer :

  1. Vérifiez que vous avez créé un cluster de calcul dans lequel nous pouvons créer le déploiement. Dans cet exemple, nous allons utiliser un cluster de calcul nommé gpu-cluster. Bien que ce ne soit pas obligatoire, nous utilisons des GPU pour accélérer le traitement.

  2. Nous devons indiquer sur quel environnement nous allons exécuter le déploiement. Dans notre cas, notre modèle s’exécute sur TensorFlow. Azure Machine Learning dispose déjà d’un environnement avec les logiciels requiss installés. Nous pouvons donc réutiliser cet environnement. Nous allons simplement ajouter quelques dépendances dans un fichier conda.yml.

    La définition d’environnement sera incluse dans le fichier de déploiement.

    compute: azureml:gpu-cluster
    environment:
      name: tensorflow212-cuda11-gpu
      image: mcr.microsoft.com/azureml/curated/tensorflow-2.12-cuda11:latest
    
  3. Maintenant, attendez la création du déploiement.

    Pour créer un nouveau déploiement sous le point de terminaison créé, créez une configuration YAML comme suit. Vous pouvez vérifier le schéma YAML du point de terminaison de lot complet pour obtenir des propriétés supplémentaires.

    $schema: https://azuremlschemas.azureedge.net/latest/modelBatchDeployment.schema.json
    endpoint_name: imagenet-classifier-batch
    name: imagenet-classifier-resnetv2
    description: A ResNetV2 model architecture for performing ImageNet classification in batch
    type: model
    model: azureml:imagenet-classifier@latest
    compute: azureml:gpu-cluster
    environment:
      name: tensorflow212-cuda11-gpu
      image: mcr.microsoft.com/azureml/curated/tensorflow-2.12-cuda11:latest
      conda_file: environment/conda.yaml
    code_configuration:
      code: code/score-by-file
      scoring_script: batch_driver.py
    resources:
      instance_count: 2
    settings:
      max_concurrency_per_instance: 1
      mini_batch_size: 5
      output_action: append_row
      output_file_name: predictions.csv
      retry_settings:
        max_retries: 3
        timeout: 300
      error_threshold: -1
      logging_level: info
    

    Ensuite, créez le déploiement avec la commande suivante :

    az ml batch-deployment create --file deployment-by-file.yml --endpoint-name $ENDPOINT_NAME --set-default
    
  4. Bien que vous puissiez appeler un déploiement spécifique à l’intérieur d’un point de terminaison, vous souhaiterez généralement appeler le point de terminaison lui-même et laisser le point de terminaison décider du déploiement à utiliser. Ce déploiement s’appelle le déploiement « par défaut ». Cela vous donne la possibilité de modifier le déploiement par défaut et donc de modifier le modèle qui sert le déploiement sans modifier le contrat avec l’utilisateur appelant le point de terminaison. Utilisez l’instruction suivante pour mettre à jour le déploiement par défaut :

    az ml batch-endpoint update --name $ENDPOINT_NAME --set defaults.deployment_name=$DEPLOYMENT_NAME
    
  5. À ce stade, notre point de terminaison batch est prêt à être utilisé.

Test du déploiement

Pour tester notre point de terminaison, nous allons utiliser un échantillon de 1 000 images à partir du jeu de données ImageNet d’origine. Les points de terminaison de lot peuvent uniquement traiter les données situées dans le cloud et accessibles à partir de l’espace de travail Azure Machine Learning. Dans cet exemple, nous allons les charger dans un magasin de données Azure Machine Learning. En particulier, nous allons créer une ressource de données qui peut être utilisée pour appeler le point de terminaison pour le scoring. Toutefois, notez que les points de terminaison de lot acceptent les données pouvant être placées dans plusieurs types d’emplacements.

  1. Nous allons télécharger les exemples de données associés :

    wget https://azuremlexampledata.blob.core.windows.net/data/imagenet/imagenet-1000.zip
    unzip imagenet-1000.zip -d data
    
  2. Maintenant, nous allons créer la ressource de données à partir des données que nous venons de télécharger

    Créez une définition de ressource de données dans YAML :

    imagenet-sample-unlabeled.yml

    $schema: https://azuremlschemas.azureedge.net/latest/data.schema.json
    name: imagenet-sample-unlabeled
    description: A sample of 1000 images from the original ImageNet dataset. Download content from https://azuremlexampledata.blob.core.windows.net/data/imagenet-1000.zip.
    type: uri_folder
    path: data
    

    Ensuite, créez la ressource de données :

    az ml data create -f imagenet-sample-unlabeled.yml
    
  3. Maintenant que les données sont chargées et prêtes à être utilisées, appelons le point de terminaison :

    JOB_NAME=$(az ml batch-endpoint invoke --name $ENDPOINT_NAME --input azureml:imagenet-sample-unlabeled@latest --query name -o tsv)
    

    Notes

    L’utilitaire jq peut ne pas être installé sur chaque installation. Vous pouvez obtenir plus d’instructions sur ce lien.

    Conseil

    Notez comment nous n’indiquons pas le nom de déploiement dans l’opération d’appel. Cela est dû au fait que le point de terminaison achemine automatiquement le travail vers le déploiement par défaut. Étant donné que notre point de terminaison n’a qu’un seul déploiement, il s’agit de celui par défaut. Vous pouvez cibler un déploiement spécifique en indiquant l’argument/le paramètre deployment_name.

  4. Une tâche de lot est démarrée dès que la commande retourne son résultat. Vous pouvez surveiller l’état du travail jusqu’à ce qu’il se termine :

    az ml job show -n $JOB_NAME --web
    
  5. Une fois le déploiement terminé, nous pouvons télécharger les prédictions :

    Utilisez les commandes suivantes pour télécharger les prédictions :

    az ml job download --name $JOB_NAME --output-name score --download-path ./
    
  6. Les prédictions de sortie se présentent comme suit : Notez que les prédictions ont été combinées avec les étiquettes pour la commodité du lecteur. Pour en savoir plus sur l’obtention de ce résultat, consultez ce bloc-notes associé.

    import pandas as pd
    score = pd.read_csv("named-outputs/score/predictions.csv", header=None,  names=['file', 'class', 'probabilities'], sep=' ')
    score['label'] = score['class'].apply(lambda pred: imagenet_labels[pred])
    score
    
    fichier class probabilities label
    n02088094_Afghan_hound.JPEG 161 0.994745 Afghan hound
    n02088238_basset 162 0.999397 basset
    n02088364_beagle.JPEG 165 0.366914 bluetick
    n02088466_bloodhound.JPEG 164 0.926464 bloodhound
    ... ... ... ...

Déploiements à haut débit

Comme mentionné précédemment, le déploiement que nous venons de créer traite une image par heure, même lorsque le déploiement par lots en fournit un lot. Dans la plupart des cas, il s’agit de la meilleure approche, car elle simplifie l’exécution des modèles et évite les problèmes éventuels de manque de mémoire. Toutefois, dans certains autres cas, nous pouvons vouloir saturer autant que possible l’utilisation du matériel sous-jacent. C’est le cas des GPU par exemple.

Dans ces cas, nous pouvons souhaiter effectuer une inférence sur l’ensemble du lot de données. Cela implique de charger le jeu d’images entier en mémoire et de les envoyer directement au modèle. L’exemple suivant utilise TensorFlow pour lire le lot d’images et les noter en même temps. Il utilise également des opérations TensorFlow pour effectuer un prétraitement des données afin que l’ensemble du pipeline se produise sur le même appareil utilisé (UC/GPU).

Avertissement

Certains modèles ont une relation non linéaire avec la taille des entrées en termes de consommation de mémoire. Recréez les lots (comme dans cet exemple) ou réduisez la taille des lots créés par le déploiement de lots pour éviter les exceptions de manque de mémoire.

  1. Création du script de scoring :

    code/score-by-batch/batch_driver.py

    import os
    import numpy as np
    import pandas as pd
    import tensorflow as tf
    from tensorflow.keras.models import load_model
    
    
    def init():
        global model
        global input_width
        global input_height
    
        # AZUREML_MODEL_DIR is an environment variable created during deployment
        model_path = os.path.join(os.environ["AZUREML_MODEL_DIR"], "model")
    
        # load the model
        model = load_model(model_path)
        input_width = 244
        input_height = 244
    
    
    def decode_img(file_path):
        file = tf.io.read_file(file_path)
        img = tf.io.decode_jpeg(file, channels=3)
        img = tf.image.resize(img, [input_width, input_height])
        return img / 255.0
    
    
    def run(mini_batch):
        images_ds = tf.data.Dataset.from_tensor_slices(mini_batch)
        images_ds = images_ds.map(decode_img).batch(64)
    
        # perform inference
        pred = model.predict(images_ds)
    
        # Compute probabilities, classes and labels
        pred_prob = tf.math.reduce_max(tf.math.softmax(pred, axis=-1)).numpy()
        pred_class = tf.math.argmax(pred, axis=-1).numpy()
    
        return pd.DataFrame(
            [mini_batch, pred_prob, pred_class], columns=["file", "probability", "class"]
        )
    

    Conseil

    • Notez que ce script construit un jeu de données de tenseur à partir du mini-lot envoyé par le déploiement par lots. Ce jeu de données est prétraité pour obtenir les tenseurs attendus pour le modèle à l’aide de l’opération map avec la fonction decode_img.
    • Le jeu de données est remis en lot (16) pour envoyer les données au modèle. Utilisez ce paramètre pour contrôler la quantité d’informations que vous pouvez charger en mémoire et envoyer au modèle à la fois. Si vous travaillez sur un GPU, vous devez ajuster soigneusement ce paramètre pour obtenir l’utilisation maximale du GPU juste avant d’obtenir une exception de mémoire insuffisante.
    • Une fois les prédictions calculées, les tenseurs sont convertis en numpy.ndarray.
  2. Maintenant, attendez la création du déploiement.

    Pour créer un nouveau déploiement sous le point de terminaison créé, créez une configuration YAML comme suit. Vous pouvez vérifier le schéma YAML du point de terminaison de lot complet pour obtenir des propriétés supplémentaires.

    $schema: https://azuremlschemas.azureedge.net/latest/modelBatchDeployment.schema.json
    endpoint_name: imagenet-classifier-batch
    name: imagenet-classifier-resnetv2
    description: A ResNetV2 model architecture for performing ImageNet classification in batch
    type: model
    model: azureml:imagenet-classifier@latest
    compute: azureml:gpu-cluster
    environment:
      name: tensorflow212-cuda11-gpu
      image: mcr.microsoft.com/azureml/curated/tensorflow-2.12-cuda11:latest
      conda_file: environment/conda.yaml
    code_configuration:
      code: code/score-by-batch
      scoring_script: batch_driver.py
    resources:
      instance_count: 2
    tags:
      device_acceleration: CUDA
      device_batching: 16
    settings:
      max_concurrency_per_instance: 1
      mini_batch_size: 5
      output_action: append_row
      output_file_name: predictions.csv
      retry_settings:
        max_retries: 3
        timeout: 300
      error_threshold: -1
      logging_level: info
    

    Ensuite, créez le déploiement avec la commande suivante :

    az ml batch-deployment create --file deployment-by-batch.yml --endpoint-name $ENDPOINT_NAME --set-default
    
  3. Vous pouvez utiliser ce nouveau déploiement avec les exemples de données présentés précédemment. N’oubliez pas que pour appeler ce déploiement, vous devez indiquer le nom du déploiement dans la méthode d’appel ou le définir comme méthode par défaut.

Considérations relatives au traitement des images de modèles MLflow

Les modèles MLflow dans les points de terminaison de lot prennent en charge la lecture d’images en tant que données d’entrée. Étant donné que les déploiements MLflow ne nécessitent pas de script de scoring, tenez compte des considérations suivantes au moment de les utiliser :

  • Les fichiers image pris en charge comprennent : .png, .jpg, .jpeg, .tiff, .bmp et .gif.
  • Les modèles MLflow doivent s’attendre à recevoir une entrée np.ndarray qui correspondra aux dimensions de l’image d’entrée. Pour prendre en charge plusieurs tailles d’image sur chaque lot, l’exécuteur de lot appelle le modèle MLflow une fois par fichier image.
  • Il est fortement recommandé que les modèles MLflow incluent une signature, et s’ils le font, qu’elle soit de type TensorSpec. Les entrées sont remodelées pour correspondre à la forme du tenseur si disponible. Si aucune signature n’est disponible, les tenseurs de type np.uint8 sont déduits.
  • Pour les modèles qui incluent une signature et sont supposés gérer une taille variable d’images, incluez une signature qui peut le garantir. Par exemple, l’exemple de signature suivant autorise les lots de 3 images canalisées.
import numpy as np
import mlflow
from mlflow.models.signature import ModelSignature
from mlflow.types.schema import Schema, TensorSpec

input_schema = Schema([
  TensorSpec(np.dtype(np.uint8), (-1, -1, -1, 3)),
])
signature = ModelSignature(inputs=input_schema)

(...)

mlflow.<flavor>.log_model(..., signature=signature)

Vous pouvez trouver un exemple fonctionnel dans le notebook Jupyter imagenet-classifier-mlflow.ipynb. Pour plus d’informations sur l’utilisation de modèles MLflow dans les déploiements par lots, lisez Utilisation de modèles MLflow dans les déploiements par lots.

Étapes suivantes