Partager via


Résolution des problèmes de ParallelRunStep

S’APPLIQUE À :Kit de développement logiciel (SDK) Azure Machine Learning v1 pour Python

Important

Cet article fournit des informations sur l’utilisation du Kit de développement logiciel (SDK) Azure Machine Learning v1. Sdk v1 est déconseillé depuis le 31 mars 2025. La prise en charge prendra fin le 30 juin 2026. Vous pouvez installer et utiliser le Kit de développement logiciel (SDK) v1 jusqu’à cette date. Vos flux de travail existants utilisant le Kit de développement logiciel (SDK) v1 continueront à fonctionner après la date de fin de support. Toutefois, elles peuvent être exposées à des risques de sécurité ou à des modifications disruptives en cas de changements architecturaux du produit.

Nous vous recommandons de passer au SDK v2 avant le 30 juin 2026. Pour plus d’informations sur le SDK v2, consultez Qu’est-ce qu’Azure Machine Learning CLI et le SDK Python v2 ? et la référence du SDK v2.

Dans cet article, vous allez découvrir comment résoudre les erreurs liées à l’utilisation de la classe ParallelRunStep à partir du Kit de développement logiciel (SDK) Azure Machine Learning.

Pour obtenir des conseils généraux sur la résolution des problèmes liés à un pipeline, consultez Résolution des problèmes de pipeline Machine Learning.

Tester les scripts localement

ParallelRunStep s’exécute en tant qu’étape dans les pipelines ML. Si vous le souhaitez, vous pouvez tester vos scripts localement dans un premier temps.

Exigences relatives au scénario d'entrée

Le script d’entrée pour un ParallelRunStepdoit contenir une fonction run() et éventuellement une fonction init() :

  • init() : utilisez cette fonction pour toute préparation coûteuse ou commune en vue d’un traitement ultérieur. Par exemple, utilisez-la pour charger le modèle dans un objet global. Cette fonction est appelée une seule fois au début du processus.

    Remarque

    Si votre initméthode crée un répertoire de sortie, spécifiez que parents=True et exist_ok=True. La méthode init est appelée à partir de chaque processus Worker sur chaque nœud sur lequel le travail est en cours d’exécution.

  • run(mini_batch) : Cette fonction s’exécute pour chaque instance de mini_batch.
    • mini_batch : ParallelRunStep appelle une méthode d’exécution, et passe à la méthode une liste ou un pandas DataFrame en tant qu’argument. Chaque entrée de mini_batch peut être un chemin de fichier si l’entrée est un FileDataset, ou un DataFrame Pandas si l’entrée est un TabularDataset.
    • response : la méthode run() doit retourner un DataFrame Pandas ou un tableau. Pour append_row output_action, les éléments retournés sont ajoutés au fichier de sortie commun. Pour summary_only, le contenu des éléments est ignoré. Pour toutes les actions de sortie, chaque élément de sortie retourné indique la réussite de l’exécution d’une entrée dans le mini-lot d’entrée. Vérifiez que suffisamment de données sont incluses dans le résultat de l’exécution pour mapper l’entrée au résultat de la sortie de l’exécution. Les sorties de l’exécution sont écrites dans un fichier de sortie, mais pas nécessairement dans l’ordre. Vous devez utiliser une clé dans la sortie pour la mapper à l’entrée.

      Remarque

      Un élément de sortie est attendu pour un élément d’entrée.

%%writefile digit_identification.py
# Snippets from a sample script.
# Refer to the accompanying digit_identification.py
# (https://github.com/Azure/MachineLearningNotebooks/tree/master/how-to-use-azureml/machine-learning-pipelines/parallel-run)
# for the implementation script.

import os
import numpy as np
import tensorflow as tf
from PIL import Image
from azureml.core import Model


def init():
    global g_tf_sess

    # Pull down the model from the workspace
    model_path = Model.get_model_path("mnist")

    # Construct a graph to execute
    tf.reset_default_graph()
    saver = tf.train.import_meta_graph(os.path.join(model_path, 'mnist-tf.model.meta'))
    g_tf_sess = tf.Session()
    saver.restore(g_tf_sess, os.path.join(model_path, 'mnist-tf.model'))


def run(mini_batch):
    print(f'run method start: {__file__}, run({mini_batch})')
    resultList = []
    in_tensor = g_tf_sess.graph.get_tensor_by_name("network/X:0")
    output = g_tf_sess.graph.get_tensor_by_name("network/output/MatMul:0")

    for image in mini_batch:
        # Prepare each image
        data = Image.open(image)
        np_im = np.array(data).reshape((1, 784))
        # Perform inference
        inference_result = output.eval(feed_dict={in_tensor: np_im}, session=g_tf_sess)
        # Find the best probability, and add it to the result list
        best_result = np.argmax(inference_result)
        resultList.append("{}: {}".format(os.path.basename(image), best_result))

    return resultList

Si vous avez un autre fichier ou dossier dans le même répertoire que votre script d’inférence, vous pouvez le référencer en recherchant le répertoire de travail actuel. Si vous souhaitez importer vos packages, vous pouvez également ajouter le dossier de votre package à sys.path.

script_dir = os.path.realpath(os.path.join(__file__, '..',))
file_path = os.path.join(script_dir, "<file_name>")

packages_dir = os.path.join(file_path, '<your_package_folder>')
if packages_dir not in sys.path:
    sys.path.append(packages_dir)
from <your_package> import <your_class>

Paramètres de ParallelRunConfig

ParallelRunConfig est la configuration principale de l’instance ParallelRunStep dans le pipeline Azure Machine Learning. Elle permet de wrapper votre script et de configurer les paramètres nécessaires, dont toutes les entrées suivantes :

  • entry_script : script utilisateur utilisé comme un chemin de fichier local qui est exécuté en parallèle sur plusieurs nœuds. Si source_directory est présent, le chemin d’accès relatif doit être utilisé. Dans le cas contraire, utilisez un chemin accessible sur la machine.

  • mini_batch_size: taille du mini-lot passé à un appel run() unique. (Facultatif ; la valeur par défaut est 10 fichiers pour FileDataset et 1MB pour TabularDataset.)

    • Pour FileDataset, il s’agit du nombre de fichiers avec une valeur minimale de 1. Vous pouvez combiner plusieurs fichiers dans un mini-lot.
    • Pour TabularDataset, il s’agit de la taille des données. Par exemple, il peut s’agir des valeurs 1024, 1024KB, 10MB ou 1GB. 1MB est la valeur recommandée. Le mini-lot de TabularDataset ne franchira jamais les limites du fichier. Par exemple, s’il existe plusieurs fichiers .csv de différentes tailles, le plus petit est de 100 Ko et le plus grand de 10 Mo. Si mini_batch_size = 1MB est défini, les fichiers inférieurs à 1 Mo seront traités comme un mini-lot et les fichiers de plus de 1 Mo seront divisés en plusieurs mini-lots.

      Remarque

      Les TabularDatasets sauvegardés par SQL ne peuvent pas être partitionnés. Les TabularDatasets d’un seul fichier Parquet et d’un seul groupe de lignes ne peuvent pas être partitionnés.

  • error_threshold: nombre d’échecs d’enregistrement pour TabularDataset et d’échecs de fichiers pour FileDataset qui doivent être ignorés pendant le traitement. Une fois que le nombre d’erreurs pour la totalité de l’entrée dépasse cette valeur, le travail est abandonné. Le seuil d’erreur concerne la totalité de l’entrée et non le mini-lot envoyé à la méthode run(). La plage est la suivante : [-1, int.max]. -1 indique qu’il faut ignorer tous les échecs au cours du traitement.

  • output_action : L’une des valeurs suivantes indique comment la sortie est organisée :

    • summary_only : le script utilisateur doit stocker les fichiers de sortie. Les sorties de run() sont uniquement utilisées pour le calcul du seuil d’erreurs.
    • append_row : pour toutes les entrées, ParallelRunStep crée un fichier unique dans le dossier de sortie pour ajouter toutes les sorties séparées par une ligne.
  • append_row_file_name: permet de personnaliser le nom du fichier de sortie pour append_row output_action (facultatif ; la valeur par défaut est parallel_run_step.txt).

  • source_directory: chemins des dossiers qui contiennent tous les fichiers à exécuter sur la cible de calcul (facultatif).

  • compute_target: Seul AmlCompute est pris en charge.

  • node_count: nombre de nœuds de calcul à utiliser pour l’exécution du script utilisateur.

  • process_count_per_node : le nombre de processus Worker par nœud pour exécuter le script d’entrée en parallèle. Pour un ordinateur GPU, la valeur par défaut est 1. Pour un ordinateur UC, la valeur par défaut est le nombre de cœurs par nœud. Un processus Worker appelle run() de manière répétée en transmettant le mini lot qu’il obtient en tant que paramètre. Le nombre total de processus Worker dans votre travail est process_count_per_node * node_count, qui détermine le nombre maximal de run() à exécuter en parallèle.

  • environment: définition de l’environnement Python. Vous pouvez la configurer de manière à utiliser un environnement Python existant ou un environnement temporaire. La définition est également chargée de définir les dépendances d’application nécessaires (facultatif).

  • logging_level: Verbosité du journal. Les valeurs permettant d’augmenter le niveau de verbosité sont les suivantes : WARNING, INFO et DEBUG. (Facultatif ; la valeur par défaut est INFO.)

  • run_invocation_timeout: délai d’attente de l’appel de la méthode run(), en secondes. (Facultatif ; la valeur par défaut est 60.)

  • run_max_try: nombre maximal de tentatives de run() pour un mini-lot. run() a échoué si une exception est levée, ou si rien n’est retourné lorsque run_invocation_timeout est atteint (facultatif ; la valeur par défaut est 3).

Vous pouvez spécifier mini_batch_size, node_count, process_count_per_node, logging_level, run_invocation_timeout et run_max_try en tant que PipelineParameter ; ainsi, lorsque vous soumettez à nouveau une exécution de pipeline, vous pouvez ajuster les valeurs des paramètres.

Visibilité des appareils CUDA

Pour les cibles de calcul dotées de GPU, la variable d’environnement CUDA_VISIBLE_DEVICES est définie dans les processus Worker. Dans AmlCompute, vous pouvez trouver le nombre total de périphériques GPU dans la variable d’environnement AZ_BATCHAI_GPU_COUNT_FOUND, qui est définie automatiquement. Si vous souhaitez que chaque processus Worker ait un GPU dédié, définissez process_count_per_node égal au nombre de périphériques GPU sur un ordinateur. Ensuite, chaque processus Worker est affecté avec un index unique à CUDA_VISIBLE_DEVICES. Lorsqu’un processus Worker s’arrête pour une raison quelconque, le prochain processus Worker qui démarre adopte l’index GPU libéré.

Lorsque le nombre total de périphériques GPU est inférieur à process_count_per_node, les processus Worker avec un index plus petit peuvent être affectés à l’index GPU jusqu’à ce que tous les GPU soient occupés.

Si le nombre total de périphériques GPU est 2 et process_count_per_node = 4, par exemple, le processus 0 et le processus 1 prennent l’index 0 et l’index 1. Les processus 2 et 3 n’ont pas la variable d’environnement. Pour une bibliothèque qui utilise cette variable d’environnement pour l’attribution des GPU, les processus 2 et 3 n’auront pas de GPU et n’essaieront pas d’acquérir des périphériques GPU. Le processus 0 libère l’index GPU 0 lorsqu’il s’arrête. Le cas échéant, le processus suivant, à savoir le processus 4, se voit attribuer l’index GPU 0.

Pour plus d’informations, consultez CUDA Pro Tip: Control GPU Visibility with CUDA_VISIBLE_DEVICES.

Paramètres de création de l'étape ParallelRunStep

Créez ParallelRunStep à l’aide du script, de la configuration de l’environnement et des paramètres. Spécifiez la cible de calcul que vous avez déjà jointe à votre espace de travail en tant que cible d’exécution de votre script d’inférence. Utilisez ParallelRunStep pour créer l’étape de pipeline d’inférence par lots, qui accepte tous les paramètres suivants :

  • name : nom de l’étape, avec les restrictions de nommage suivantes : unique, de 3 à 32 caractères et expression régulière ^[a-z]([-a-z0-9]*[a-z0-9])?$.
  • parallel_run_config: objet ParallelRunConfig, comme défini précédemment.
  • inputs: un ou plusieurs jeux de données Azure Machine Learning à un seul type, à partitionner pour un traitement parallèle.
  • side_inputs: une ou plusieurs données, ou jeux de données de référence utilisés comme entrées supplémentaires sans avoir besoin d’être partitionnés.
  • output : objet OutputFileDatasetConfig qui représente le chemin d’accès au répertoire dans lequel les données de sortie doivent être stockées.
  • arguments: liste d’arguments passés au script utilisateur. Utilisez unknown_args pour les récupérer dans votre script d’entrée (facultatif).
  • allow_reuse: indique si l’étape doit réutiliser les résultats précédents lorsqu’elle est exécutée avec les mêmes paramètres ou entrées. Si ce paramètre a la valeur False, une nouvelle exécution sera toujours générée pour cette étape pendant l’exécution du pipeline. (Facultatif ; la valeur par défaut est True.)
from azureml.pipeline.steps import ParallelRunStep

parallelrun_step = ParallelRunStep(
    name="predict-digits-mnist",
    parallel_run_config=parallel_run_config,
    inputs=[input_mnist_ds_consumption],
    output=output_dir,
    allow_reuse=True
)

Déboguer les scripts à partir du contexte distant

La transition du débogage d’un script de scoring localement au débogage d’un script de scoring dans un pipeline réel peut être difficile. Pour plus d’informations sur la recherche de vos journaux dans le portail, consultez la section Pipelines de Machine Learning sur le débogage de scripts à partir d’un contexte distant. Les informations contenues dans cette section s’appliquent également à un ParallelRunStep.

En raison de la nature distribuée des travaux ParallelRunStep, les journaux peuvent provenir de plusieurs sources différentes. Toutefois, deux fichiers consolidés sont créés et fournissent des informations de haut niveau :

  • ~/logs/job_progress_overview.txt: Ce fichier fournit des informations de haut niveau sur le nombre de mini-lots (également appelés tâches) créés jusqu’à présent et le nombre de mini-lots traités jusqu’à présent. À cette fin, il affiche le résultat du travail. Si le travail échoue, le message d’erreur s’affiche et indique où démarrer la résolution des problèmes.

  • ~/logs/job_result.txt : affiche le résultat de la tâche. Si le travail a échoué, le message d’erreur s’affiche et indique où démarrer la résolution des problèmes.

  • ~/logs/job_error.txt : ce fichier récapitule les erreurs dans votre script.

  • ~/logs/sys/master_role.txt: Ce fichier fournit la vue du nœud principal (également connu sous le nom d’orchestrateur) du travail en cours d’exécution. Comprend la création de tâches, la supervision de la progression et le résultat de l’exécution.

  • ~/logs/sys/job_report/processed_mini-batches.csv : tableau montrant tous les mini-lots traités. Il affiche le résultat de chaque exécution de mini-lot, son ID de nœud d’agent d’exécution et son nom de processus. Le temps écoulé et les messages d’erreur sont également inclus. Vous retrouverez les journaux d'activité pour chaque exécution de mini-lots en suivant l’ID de nœud et le nom du processus.

Les journaux générés à partir du script d’entrée avec l’assistance EntryScript et les instructions print se trouvent dans les fichiers suivants :

  • ~/logs/user/entry_script_log/<node_id>/<process_name>.log.txt: Ces fichiers sont les journaux écrits à partir d’entry_script avec le helper EntryScript.

  • ~/logs/user/stdout/<node_id>/<process_name>.stdout.txt : ces fichiers sont les journaux de stdout (par exemple, l’instruction print) d’entry_script.

  • ~/logs/user/stderr/<node_id>/<process_name>.stderr.txt : ces fichiers sont les journaux de stderr d’entry_script.

Par exemple, la capture d’écran montre que le mini-lot 0 a échoué sur le nœud 0 process001. Les journaux d’activité correspondants pour votre script d’entrée sont disponibles dans ~/logs/user/entry_script_log/0/process001.log.txt, ~/logs/user/stdout/0/process001.log.txt et ~/logs/user/stderr/0/process001.log.txt

Capture d’écran d’un exemple de fichier processed_mini-batches.csv.

Lorsque vous avez besoin de comprendre en détail la façon dont chaque nœud a exécuté le script de score, examinez les journaux de processus individuels pour chaque nœud. Les journaux de processus se trouvent dans le dossier ~/logs/sys/node, regroupés par nœuds Worker :

  • ~/logs/sys/node/<node_id>/<process_name>.txt : ce fichier fournit des informations détaillées sur chaque mini-lot récupéré ou terminé par un Worker. Pour chaque mini-lot, ce fichier comprend les éléments suivants :

    • L’adresse IP et le PID du processus Worker.
    • Le nombre total d’éléments, le nombre d’éléments traités avec succès et le nombre d’éléments ayant échoué.
    • L’heure de début, la durée, le temps de traitement et la durée de la méthode d’exécution.

Vous pouvez également afficher les résultats de vérifications périodiques de l’utilisation de ressources pour chaque nœud. Les fichiers journaux et les fichiers d’installation se trouvent dans ce dossier :

  • ~/logs/perf : Définissez --resource_monitor_interval pour modifier l’intervalle de vérification en secondes. La valeur définir pour l’intervalle par défaut est 600, soit environ 10 minutes. Pour arrêter la surveillance, définissez la valeur sur 0. Chaque dossier <node_id> comprend les éléments suivants :

    • os/ : informations sur tous les processus en cours d’exécution dans le nœud. Une vérification exécute une commande du système d’exploitation et enregistre le résultat dans un fichier. Sous Linux, la commande est ps. Sous Windows, utilisez la commande tasklist.
      • %Y%m%d%H : le nom du sous-dossier correspond à l’heure.
        • processes_%M : le nom du fichier se termine par la minute à laquelle la vérification a été effectuée.
    • node_disk_usage.csv : utilisation détaillée du disque du nœud.
    • node_resource_usage.csv : vue d’ensemble de l’utilisation des ressources du nœud.
    • processes_resource_usage.csv : vue d’ensemble de l’utilisation des ressources de chaque processus.

Raisons courantes d’échec de tâche

SystemExit : 42

Les sorties 41 et 42 sont des codes de sortie conçus par PRS. Les nœuds Worker quittent avec le code 41 pour informer le gestionnaire de calcul qu’il s’est arrêté indépendamment. Ceci est normal. Il se peut qu’un nœud de ligne d'étiquette quitte avec le code 0 ou 42, qui indique le résultat de tâche. La sortie 42 signifie l’échec de la tâche. La raison de l’échec se trouve dans ~/logs/job_result.txt. Vous pouvez suivre la section précédente pour déboguer votre tâche.

Autorisation de données

L’erreur de la tâche indique que le calcul ne peut pas accéder aux données d’entrée. Si l’identité est utilisée comme base pour votre cluster de calcul et votre stockage, vous pouvez faire référence à l’Authentification des données basée sur l’identité.

Processus arrêtés de façon inattendue

Les processus peuvent se bloquer en raison d’exceptions inattendues ou non gérées. Le système termine les processus en raison d’exceptions de mémoire insuffisante. Dans les journaux d'activité ~/logs/sys/node/<node-id>/_main.txt du système PRS, vous trouverez des erreurs comme celles ci-dessous.

<process-name> exits with returncode -9.

Mémoire insuffisante

~/logs/perf journalise la consommation des ressources de calcul des processus. L’utilisation de la mémoire de chaque processeur de tâche est disponible. Vous pouvez estimer l’utilisation totale de la mémoire sur le nœud.

Vous trouverez les erreurs de mémoire insuffisante dans ~/system_logs/lifecycler/<node-id>/execution-wrapper.txt.

Nous vous suggérons de réduire le nombre de processus par nœud ou de mettre à niveau la taille de machine virtuelle si les ressources de calcul sont proches des limites.

Exceptions non gérées

Dans certains cas, les processus Python ne peuvent pas détecter la pile défaillante. Vous pouvez ajouter une variable d’environnement env["PYTHONFAULTHANDLER"]="true" pour activer le gestionnaire d’erreurs intégré de Python.

Expiration du mini-lot

Vous pouvez ajuster l’argument run_invocation_timeout en fonction de vos tâches de mini-lot. Voici quelques conseils lorsque vous voyez que les fonctions run() prennent plus de temps que prévu.

  • Vérifiez le temps écoulé et le temps de traitement du mini-lot. Le temps de processus mesure le temps processeur du processus. Lorsque le temps de processus est beaucoup plus court que le temps écoulé, vous pouvez vérifier si de lourdes opérations d’E/S ou des requêtes de réseau sont présentes dans les tâches. L’expiration du mini-lot s’explique souvent par la longue latence de ces opérations.

  • Certains mini-lots spécifiques prennent plus de temps que d’autres. Vous pouvez mettre à jour la configuration ou essayer de travailler avec les données d’entrée pour équilibrer le temps de traitement de mini-lot.

Comment se connecter à son script utilisateur depuis un contexte distant ?

ParallelRunStep peut exécuter plusieurs processus sur un nœud en fonction de process_count_per_node. Pour organiser les journaux de chaque processus sur un nœud et combiner les instructions print et log, l’enregistreur d’événements ParallelRunStep illustré ci-dessous est recommandé. Vous recevez d’EntryScript un enregistreur d’événements et affichez les journaux dans le dossier logs/user dans le portail.

exemple de script d’entrée utilisant l’enregistreur d’événements :

from azureml_user.parallel_run import EntryScript

def init():
    """Init once in a worker process."""
    entry_script = EntryScript()
    logger = entry_script.logger
    logger.info("This will show up in files under logs/user on the Azure portal.")


def run(mini_batch):
    """Call once for a mini batch. Accept and return the list back."""
    # This class is in singleton pattern. It returns the same instance as the one in init()
    entry_script = EntryScript()
    logger = entry_script.logger
    logger.info(f"{__file__}: {mini_batch}.")
    ...

    return mini_batch

Où le message de Python logging est-il reçu ?

ParallelRunStep définit un gestionnaire sur le journaliseur racine, qui reçoit le message dans logs/user/stdout/<node_id>/processNNN.stdout.txt.

logging a par défaut le niveau INFO. Par défaut, les niveaux inférieurs à INFO ne s’affichent pas ; tel est le cas de DEBUG.

Comment écrire dans un fichier pour qu’il apparaisse dans le portail ?

Les fichiers écrits dans le dossier /logs seront chargés et affichés dans le portail. Vous pouvez accéder au dossier logs/user/entry_script_log/<node_id> comme ci-dessous et composer le chemin du fichier en vue de l’écriture :

from pathlib import Path
from azureml_user.parallel_run import EntryScript

def init():
    """Init once in a worker process."""
    entry_script = EntryScript()
    log_dir = entry_script.log_dir
    log_dir = Path(entry_script.log_dir)  # logs/user/entry_script_log/<node_id>/.
    log_dir.mkdir(parents=True, exist_ok=True) # Create the folder if not existing.

    proc_name = entry_script.agent_name  # The process name in pattern "processNNN".
    fil_path = log_dir / f"{proc_name}_<file_name>" # Avoid conflicting among worker processes with proc_name.

Comment gérer la connexion dans les nouveaux processus ?

Vous pouvez générer de nouveaux processus dans votre script d'entrée avec le module subprocess, vous connecter à leurs canaux d'entrée/sortie/erreur et obtenir leurs codes de retour.

L'approche recommandée est d'utiliser la run()fonction avec capture_output=True. Les erreurs s’affichent dans logs/user/error/<node_id>/<process_name>.txt.

Si vous souhaitez utiliser Popen(), stdout/stderr doit être redirigé vers des fichiers, comme :

from pathlib import Path
from subprocess import Popen

from azureml_user.parallel_run import EntryScript


def init():
    """Show how to redirect stdout/stderr to files in logs/user/entry_script_log/<node_id>/."""
    entry_script = EntryScript()
    proc_name = entry_script.agent_name  # The process name in pattern "processNNN".
    log_dir = Path(entry_script.log_dir)  # logs/user/entry_script_log/<node_id>/.
    log_dir.mkdir(parents=True, exist_ok=True) # Create the folder if not existing.
    stdout_file = str(log_dir / f"{proc_name}_demo_stdout.txt")
    stderr_file = str(log_dir / f"{proc_name}_demo_stderr.txt")
    proc = Popen(
        ["...")],
        stdout=open(stdout_file, "w"),
        stderr=open(stderr_file, "w"),
        # ...
    )

Remarque

Un processus de travail exécute le code « système » et le code de script d’entrée dans le même processus.

Si aucun stdout ou stderr n’est spécifié, les sous-processus créés avec Popen() dans votre script d’entrée héritent du paramètre du processus Worker.

stdout écrit vers ~/logs/sys/node/<node_id>/processNNN.stdout.txt et stderr dans ~/logs/sys/node/<node_id>/processNNN.stderr.txt.

Comment puis-je écrire un fichier dans le répertoire de sortie, puis l’afficher dans le portail ?

Vous pouvez obtenir le répertoire de sortie depuis la classe EntryScript et y écrire. Pour afficher les fichiers écrits, dans l’affichage Exécution de l’étape du portail Azure Machine Learning, sélectionnez l’onglet Sorties + journaux d’activité. Sélectionnez le lien Sorties de données, puis effectuez les étapes décrites dans la boîte de dialogue.

Utilisez EntryScript dans votre script d’entrée comme dans l’exemple suivant :

from pathlib import Path
from azureml_user.parallel_run import EntryScript

def run(mini_batch):
    output_dir = Path(entry_script.output_dir)
    (Path(output_dir) / res1).write...
    (Path(output_dir) / res2).write...

Comment transmettre un side_input, par exemple un ou plusieurs fichiers contenant une table de choix, à tous mes Workers ?

L’utilisateur peut passer des données de référence au script à l’aide du paramètre side_inputs de ParalleRunStep. Tous les jeux de données fournis en tant que side_inputs sont montés sur chaque nœud Worker. L’utilisateur peut récupérer l’emplacement du montage en passant l’argument.

Construisez un Jeu de données contenant les données de référence, spécifiez un chemin de montage local et inscrivez-le auprès de votre espace de travail. Transmettez-le au paramètre side_inputs de votre ParallelRunStep. En outre, vous pouvez ajouter son chemin d’accès dans la section arguments pour accéder facilement à son chemin monté.

Remarque

Utilisez FileDatasets uniquement pour side_inputs.

local_path = "/tmp/{}".format(str(uuid.uuid4()))
label_config = label_ds.as_named_input("labels_input").as_mount(local_path)
batch_score_step = ParallelRunStep(
    name=parallel_step_name,
    inputs=[input_images.as_named_input("input_images")],
    output=output_dir,
    arguments=["--labels_dir", label_config],
    side_inputs=[label_config],
    parallel_run_config=parallel_run_config,
)

Après cela, vous pouvez y accéder dans votre script (par exemple, dans votre méthode init()) comme suit :

parser = argparse.ArgumentParser()
parser.add_argument('--labels_dir', dest="labels_dir", required=True)
args, _ = parser.parse_known_args()

labels_path = args.labels_dir

Comment utiliser des jeux de données d’entrée avec l’authentification du principal du service ?

L’utilisateur peut passer des jeux de données d’entrée avec l’authentification du principal du service utilisée dans l’espace de travail. L’utilisation d’un tel jeu de données dans ParallelRunStep nécessite que le jeu de données soit inscrit pour pouvoir construire une configuration ParallelRunStep.

service_principal = ServicePrincipalAuthentication(
    tenant_id="***",
    service_principal_id="***",
    service_principal_password="***")

ws = Workspace(
    subscription_id="***",
    resource_group="***",
    workspace_name="***",
    auth=service_principal
    )

default_blob_store = ws.get_default_datastore() # or Datastore(ws, '***datastore-name***')
ds = Dataset.File.from_files(default_blob_store, '**path***')
registered_ds = ds.register(ws, '***dataset-name***', create_new_version=True)

Comment vérifier la progression et l’analyser

Cette section explique comment vérifier la progression d’un travail ParallelRunStep et rechercher la cause d’un comportement inattendu.

Comment vérifier la progression d’un travail ?

Outre l’examen de l’état général du StepRun, vous pouvez consulter le nombre de mini-lots planifiés ou traités et la progression de la génération de la sortie dans ~/logs/job_progress_overview.<timestamp>.txt. Le fichier est mis à jour quotidiennement. Vous pouvez consulter celui dont le timestamp est le plus élevé pour obtenir les informations les plus récentes.

Que dois-je vérifier s’il n’y a aucune progression pendant un certain temps ?

Vous pouvez accéder à ~/logs/sys/error pour voir s’il y a une exception. S’il n’y en a aucune, il est probable que votre script d’entrée soit chronophage. Vous pouvez imprimer les informations relatives à la progression dans votre code pour localiser la partie chronophage ou vous pouvez ajouter "--profiling_module", "cProfile" à la section arguments de ParallelRunStep pour générer un fichier de profil nommé <process_name>.profile sous le dossier ~/logs/sys/node/<node_id>.

Quand un travail s’arrête-t-il  ?

S’il n’est pas annulé, le travail peut s’arrêter avec l’état :

  • Terminé. Tous les mini-lots sont traités avec succès et la sortie est générée pour le mode append_row.
  • Échec. Si error_threshold dans Parameters for ParallelRunConfig est dépassé ou que l’erreur système se produit pendant le travail.

Où trouver la cause racine de l’échec ?

Vous pouvez suivre la piste dans ~/logs/job_result.txt pour rechercher la cause et le journal détaillé des erreurs.

La défaillance d’un nœud aura-t-elle un impact sur le résultat du travail ?

Non, à condition que d’autres nœuds soient disponibles dans le cluster de calcul désigné. ParallelRunStep peut s’exécuter indépendamment sur chaque nœud. L’échec d’un nœud unique ne fait pas échouer le travail dans son ensemble.

Que se passe-t-il si la fonction init du script d’entrée échoue ?

ParallelRunStep dispose d’un mécanisme pour effectuer une nouvelle tentative pendant un certain temps afin de permettre la récupération en cas de problèmes temporaires, sans trop retarder l’échec de la tâche. Le mécanisme est le suivant :

  1. Si, après le démarrage d’un nœud, init sur tous les agents continue d’échouer, nous arrêterons les tentatives après 3 * process_count_per_node échecs.
  2. Si, après le démarrage du travail, init sur tous les agents de tous les nœuds continue d’échouer, nous arrêterons les tentatives si le travail dure plus de deux minutes et qu’il y a 2 * node_count * process_count_per_node échecs.
  3. Si tous les agents sont bloqués sur init pendant plus de 3 * run_invocation_timeout + 30 secondes, le travail échoue en raison d’une absence de progression sur une période trop longue.

Que se passe-t-il sur OutOfMemory ? Comment puis-je en vérifier la cause ?

Le processus peut être arrêté par le système. ParallelRunStep indique que la tentative actuelle de traitement du mini-lot est en échec et tente de redémarrer le processus qui a échoué. Vous pouvez vérifier ~logs/perf/<node_id> pour rechercher le processus gourmand en mémoire.

Pourquoi ai-je un grand nombre de fichiers processNNN ?

ParallelRunStep démarre de nouveaux processus Worker en remplacement de ceux qui ont échoué de manière anormale. Chaque processus génère un ensemble de fichiers processNNN sous forme de journal. Toutefois, si le processus a échoué en raison d’une exception survenue pendant la fonction init du script utilisateur et que l’erreur s’est répétée continuellement sur 3 * process_count_per_node tentatives, aucun nouveau processus Worker ne sera démarré.

Étapes suivantes