Felsökning av ParallelRunStep

GÄLLER FÖR:Python SDK azureml v1

I den här artikeln får du lära dig hur du felsöker när du får fel med klassen ParallelRunStep från Azure Machine Learning SDK.

Allmänna tips om hur du felsöker en pipeline finns i Felsöka maskininlärningspipelines.

Testa skript lokalt

ParallelRunStep körs som ett steg i ML-pipelines. Du kanske vill testa dina skript lokalt som ett första steg.

Krav för postskript

Postskriptet för en ParallelRunStepmåste innehålla en run() funktion och innehåller eventuellt en init() funktion:

  • init(): Använd den här funktionen för eventuella kostsamma eller vanliga förberedelser för senare bearbetning. Använd den till exempel för att läsa in modellen i ett globalt objekt. Den här funktionen anropas bara en gång i början av processen.

    Anteckning

    Om din init metod skapar en utdatakatalog anger du den parents=True och exist_ok=True. Metoden init anropas från varje arbetsprocess på varje nod där jobbet körs.

  • run(mini_batch): Funktionen körs för varje mini_batch instans.
    • mini_batch: ParallelRunStep anropar körningsmetoden och skickar antingen en lista eller pandas DataFrame som ett argument till metoden. Varje post i mini_batch blir en filsökväg om indata är en FileDataset eller ett Pandas DataFrame om indata är en TabularDataset.
    • response: run()-metoden ska returnera en Pandas DataFrame eller en matris. För append_row output_action läggs dessa returnerade element till i den gemensamma utdatafilen. För summary_only ignoreras innehållet i elementen. För alla utdataåtgärder anger varje returnerat utdataelement en lyckad körning av indataelementet i mini-batchen för indata. Se till att tillräckligt med data ingår i körningsresultatet för att mappa indata för att köra utdataresultatet. Körningsutdata skrivs i utdatafilen och är inte garanterade i ordning. Du bör använda en nyckel i utdata för att mappa den till indata.

      Anteckning

      Ett utdataelement förväntas för ett indataelement.

%%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

Om du har en annan fil eller mapp i samma katalog som ditt slutsatsdragningsskript kan du referera till den genom att hitta den aktuella arbetskatalogen. Om du vill importera paketen kan du också lägga till paketmappen i 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>

Parametrar för ParallelRunConfig

ParallelRunConfig är den viktigaste konfigurationen för ParallelRunStep till exempel i Azure Machine Learning-pipelinen. Du använder det för att omsluta skriptet och konfigurera nödvändiga parametrar, inklusive alla följande poster:

  • entry_script: Ett användarskript som en lokal filsökväg som ska köras parallellt på flera noder. Om source_directory finns använder du en relativ sökväg. Annars kan du använda alla sökvägar som är tillgängliga på datorn.

  • mini_batch_size: Storleken på mini-batchen som skickas till ett enda run() anrop. (valfritt; standardvärdet är 10 filer för FileDataset och 1MB för TabularDataset.)

    • För FileDatasetär det antalet filer med ett minsta värde på 1. Du kan kombinera flera filer i en mini-batch.
    • För TabularDatasetär det storleken på data. Exempelvärden är 1024, 1024KB, 10MBoch 1GB. Det rekommenderade värdet är 1MB. Mini-batchen från TabularDataset kommer aldrig att korsa filgränserna. Om du till exempel har .csv filer med olika storlekar är den minsta filen 100 kB och den största är 10 MB. Om du anger mini_batch_size = 1MBbehandlas filer med en storlek som är mindre än 1 MB som en mini-batch. Filer med en storlek som är större än 1 MB delas upp i flera minibatchar.

      Anteckning

      TabularDatasets som backas upp av SQL kan inte partitioneras. TabularDatasets från en enda parkettfil och en radgrupp kan inte partitioneras.

  • error_threshold: Antalet postfel för TabularDataset och filfel för FileDataset som ska ignoreras under bearbetningen. Om felantalet för hela indata överskrider det här värdet avbryts jobbet. Feltröskeln är för hela indata och inte för enskilda mini-batch som skickas run() till metoden. Intervallet är [-1, int.max]. Delen -1 anger att alla fel ignoreras under bearbetningen.

  • output_action: Ett av följande värden anger hur utdata ska ordnas:

    • summary_only: Användarskriptet lagrar utdata. ParallelRunStep använder endast utdata för beräkningen av feltröskeln.
    • append_row: För alla indata skapas endast en fil i utdatamappen för att lägga till alla utdata avgränsade med rad.
  • append_row_file_name: Om du vill anpassa utdatafilens namn för append_row output_action (valfritt; standardvärdet är parallel_run_step.txt).

  • source_directory: Sökvägar till mappar som innehåller alla filer som ska köras på beräkningsmålet (valfritt).

  • compute_target: Endast AmlCompute stöds.

  • node_count: Antalet beräkningsnoder som ska användas för att köra användarskriptet.

  • process_count_per_node: Antalet arbetsprocesser per nod för att köra postskriptet parallellt. För en GPU-dator är standardvärdet 1. För en CPU-dator är standardvärdet antalet kärnor per nod. En arbetsprocess anropar run() upprepade gånger genom att skicka den mini-batch som den får. Det totala antalet arbetsprocesser i jobbet är process_count_per_node * node_count, som bestämmer det maximala antalet run() som ska köras parallellt.

  • environment: Python-miljödefinitionen. Du kan konfigurera den så att den använder en befintlig Python-miljö eller för att konfigurera en tillfällig miljö. Definitionen ansvarar också för att ange nödvändiga programberoenden (valfritt).

  • logging_level: Logga utförlighet. Värden i ökande verbositet är: WARNING, INFOoch DEBUG. (valfritt; standardvärdet är INFO)

  • run_invocation_timeout: Tidsgränsen run() för metodanrop i sekunder. (valfritt; standardvärdet är 60)

  • run_max_try: Maximalt antal run() försök för en mini-batch. A run() misslyckas om ett undantag utlöses, eller om inget returneras när run_invocation_timeout har uppnåtts (valfritt; standardvärdet är 3).

Du kan ange mini_batch_size, node_count, process_count_per_node, logging_level, run_invocation_timeoutoch run_max_try som PipelineParameter, så att du kan finjustera parametervärdena när du skickar en pipelinekörning igen. I det här exemplet använder PipelineParameter du för mini_batch_size och Process_count_per_node och ändrar dessa värden när du skickar en ny körning.

SYNLIGHET FÖR CUDA-enheter

För beräkningsmål som är utrustade med GPU:er anges miljövariabeln CUDA_VISIBLE_DEVICES i arbetsprocesser. I AmlCompute hittar du det totala antalet GPU-enheter i miljövariabeln AZ_BATCHAI_GPU_COUNT_FOUND, som anges automatiskt. Om du vill att varje arbetsprocess ska ha en dedikerad GPU anger du process_count_per_node lika med antalet GPU-enheter på en dator. Varje arbetsprocess tilldelar ett unikt index till CUDA_VISIBLE_DEVICES. Om en arbetsprocess stoppas av någon anledning använder nästa påbörjade arbetsprocess det utgivna GPU-indexet.

Om det totala antalet GPU-enheter är mindre än process_count_per_nodetilldelas arbetsprocesserna GPU-index tills alla har använts.

Med tanke på att det totala antalet GPU-enheter är 2 och process_count_per_node = 4 som ett exempel har process 0 och process 1 index 0 och 1. Process 2 och 3 har ingen miljövariabel. För ett bibliotek som använder den här miljövariabeln för GPU-tilldelning har process 2 och 3 inte GPU:er och försöker inte hämta GPU-enheter. Om processen 0 stoppas släpper den GPU-index 0. Nästa process, som är process 4, har GPU-index 0 tilldelat.

Mer information finns i CUDA Pro Tips: Kontrollera GPU-synlighet med CUDA_VISIBLE_DEVICES.

Parametrar för att skapa ParallelRunStep

Skapa ParallelRunStep med hjälp av skript, miljökonfiguration och parametrar. Ange det beräkningsmål som du redan har kopplat till din arbetsyta som mål för körning för ditt slutsatsdragningsskript. Använd ParallelRunStep för att skapa steget för batchinferenspipeline, som tar alla följande parametrar:

  • name: Namnet på steget med följande namngivningsbegränsningar: unikt, 3–32 tecken och regex ^[a-z]([-a-z0-9]*[a-z0-9])?$.
  • parallel_run_config: Ett ParallelRunConfig objekt som definierats tidigare.
  • inputs: En eller flera Azure Machine Learning-datauppsättningar som ska partitioneras för parallell bearbetning.
  • side_inputs: En eller flera referensdata eller datauppsättningar som används som sidoindata utan att behöva partitioneras.
  • output: Ett OutputFileDatasetConfig objekt som representerar katalogsökvägen där utdata ska lagras.
  • arguments: En lista med argument som skickas till användarskriptet. Använd unknown_args för att hämta dem i ditt postskript (valfritt).
  • allow_reuse: Om steget ska återanvända tidigare resultat när det körs med samma inställningar/indata. Om den här parametern är Falsegenereras alltid en ny körning för det här steget under pipelinekörningen. (valfritt; standardvärdet är 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
)

Felsöka skript från fjärrkontext

Övergången från att felsöka ett bedömningsskript lokalt till att felsöka ett bedömningsskript i en faktisk pipeline kan vara ett svårt steg. Information om hur du hittar loggarna i portalen finns i avsnittet om maskininlärningspipelines om felsökning av skript från en fjärrkontext. Informationen i det avsnittet gäller även för en ParallelRunStep.

Loggfilen 70_driver_log.txt innehåller till exempel information från kontrollanten som startar ParallelRunStep-koden.

På grund av den distribuerade typen av ParallelRunStep-jobb finns det loggar från flera olika källor. Två konsoliderade filer skapas dock som ger information på hög nivå:

  • ~/logs/job_progress_overview.txt: Den här filen innehåller information på hög nivå om antalet minibatchar (även kallade uppgifter) som skapats hittills och antalet minibatchar som bearbetats hittills. I det här slutet visas resultatet av jobbet. Om jobbet misslyckades visas felmeddelandet och var felsökningen ska startas.

  • ~/logs/sys/master_role.txt: Den här filen tillhandahåller huvudnoden (kallas även orchestrator) för det jobb som körs. Innehåller uppgiftsskapande, förloppsövervakning, körningsresultatet.

Loggar som genereras från postskript med hjälp av EntryScript-hjälpen och utskriftsinstruktioner finns i följande filer:

  • ~/logs/user/entry_script_log/<node_id>/<process_name>.log.txt: Dessa filer är loggarna som skrivits från entry_script med hjälp av EntryScript-hjälpen.

  • ~/logs/user/stdout/<node_id>/<process_name>.stdout.txt: Dessa filer är loggarna från stdout (till exempel utskriftsinstruktor) för entry_script.

  • ~/logs/user/stderr/<node_id>/<process_name>.stderr.txt: Dessa filer är loggarna från stderr för entry_script.

För en kortfattad förståelse av fel i skriptet finns det:

  • ~/logs/user/error.txt: Den här filen försöker sammanfatta felen i skriptet.

Mer information om fel i skriptet finns:

  • ~/logs/user/error/: Innehåller fullständiga stackspårningar av undantag som genereras vid inläsning och körning av postskript.

När du behöver en fullständig förståelse för hur varje nod körde poängskriptet kan du titta på de enskilda processloggarna för varje nod. Processloggarna finns i sys/node mappen, grupperade efter arbetsnoder:

  • ~/logs/sys/node/<node_id>/<process_name>.txt: Den här filen innehåller detaljerad information om varje mini-batch när den hämtas eller slutförs av en arbetare. För varje mini-batch innehåller den här filen:

    • IP-adressen och PID för arbetsprocessen.
    • Det totala antalet objekt, antalet bearbetade objekt och antalet misslyckade objekt.
    • Starttid, varaktighet, processtid och körningsmetodtid.

Du kan också visa resultatet av regelbundna kontroller av resursanvändningen för varje nod. Loggfilerna och installationsfilerna finns i den här mappen:

  • ~/logs/perf: Ange --resource_monitor_interval för att ändra kontrollintervallet i sekunder. Standardintervallet är 600, vilket är cirka 10 minuter. Om du vill stoppa övervakningen anger du värdet till 0. Varje <node_id> mapp innehåller:

    • os/: Information om alla processer som körs i noden. En kontroll kör ett operativsystemkommando och sparar resultatet i en fil. I Linux är pskommandot . I Windows använder du tasklist.
      • %Y%m%d%H: Undermappens namn är tid till timme.
        • processes_%M: Filen slutar med minut för kontrolltiden.
    • node_disk_usage.csv: Detaljerad diskanvändning av noden.
    • node_resource_usage.csv: Översikt över resursanvändning för noden.
    • processes_resource_usage.csv: Översikt över resursanvändning för varje process.

Hur gör jag för att logg från mitt användarskript från en fjärrkontext?

ParallelRunStep kan köra flera processer på en nod baserat på process_count_per_node. För att organisera loggar från varje process på noden och kombinera utskrifts- och logguttryck rekommenderar vi att du använder ParallelRunStep-loggning enligt nedan. Du får en loggare från EntryScript och gör så att loggarna visas i loggar/användarmapp i portalen.

Ett exempelinmatningsskript med hjälp av loggern:

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 and will return same instance as the one in init()
    entry_script = EntryScript()
    logger = entry_script.logger
    logger.info(f"{__file__}: {mini_batch}.")
    ...

    return mini_batch

Var sjunker meddelandet från Python logging till?

ParallelRunStep ställer in en hanterare på rotloggaren, som sänker meddelandet till logs/user/stdout/<node_id>/processNNN.stdout.txt.

logging standardvärdet är nivå INFO . Som standard visas inte nivåerna nedan INFO , till exempel DEBUG.

Hur kan jag skriva till en fil för att visas i portalen?

Filer i logs mappen laddas upp och visas i portalen. Du kan hämta mappen logs/user/entry_script_log/<node_id> som nedan och skriva din filsökväg för att skriva:

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.

Hur hanterar jag inloggning i nya processer?

Du kan skapa nya processer i ditt postskript med subprocess modulen, ansluta till deras indata-/utdata-/felpipor och hämta deras returkoder.

Den rekommenderade metoden är att använda run() funktionen med capture_output=True. Fel visas i logs/user/error/<node_id>/<process_name>.txt.

Om du vill använda Popen()bör du omdirigera stdout/stderr till filer, till exempel:

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"),
        # ...
    )

Anteckning

En arbetsprocess kör "systemkod" och postskriptkoden i samma process.

Om inget stdout eller stderr anges ärver en underprocess som skapats med Popen() i ditt postskript inställningen för arbetsprocessen.

stdout skriver till logs/sys/node/<node_id>/processNNN.stdout.txt och stderr till logs/sys/node/<node_id>/processNNN.stderr.txt.

Hur gör jag för att skriva en fil till utdatakatalogen och sedan visa den i portalen?

Du kan hämta utdatakatalogen EntryScript från klassen och skriva till den. Om du vill visa de skrivna filerna går du till steget Kör i Azure Machine Learning-portalen och väljer fliken Utdata + loggar . Välj länken Datautdata och slutför sedan stegen som beskrivs i dialogrutan.

Använd EntryScript i ditt postskript som i det här exemplet:

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...

Hur skickar jag indata på sidan, till exempel en fil eller fil som innehåller en uppslagstabell, till alla mina arbetare?

Användaren kan skicka referensdata till skript med hjälp av side_inputs parametern ParalleRunStep. Alla datauppsättningar som tillhandahålls som side_inputs monteras på varje arbetsnod. Användaren kan hämta monteringsplatsen genom att skicka argument.

Skapa en datauppsättning som innehåller referensdata, ange en lokal monteringssökväg och registrera den med din arbetsyta. Skicka den till parametern side_inputs för din ParallelRunStep. Dessutom kan du lägga till sökvägen i arguments avsnittet för att enkelt komma åt den monterade sökvägen.

Anteckning

Använd endast FileDatasets för 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,
)

Därefter kan du komma åt det i ditt slutsatsdragningsskript (till exempel i din init()-metod) på följande sätt:

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

labels_path = args.labels_dir

Hur använder du indatauppsättningar med autentisering med tjänstens huvudnamn?

Användaren kan skicka indatauppsättningar med autentisering med tjänstens huvudnamn som används på arbetsytan. Om du använder en sådan datauppsättning i ParallelRunStep måste datauppsättningen registreras för att den ska kunna konstruera ParallelRunStep-konfigurationen.

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)

Så här kontrollerar du förloppet och analyserar det

Det här avsnittet handlar om hur du kontrollerar förloppet för ett ParallelRunStep-jobb och kontrollerar orsaken till ett oväntat beteende.

Så här kontrollerar du jobbets förlopp?

Förutom att titta på den övergripande statusen för StepRun kan antalet schemalagda/bearbetade minibatchar och förloppet för att generera utdata visas i ~/logs/job_progress_overview.<timestamp>.txt. Filen roterar dagligen. Du kan kontrollera den med den största tidsstämpeln för den senaste informationen.

Vad ska jag kontrollera om det inte finns några framsteg på ett tag?

Du kan gå in ~/logs/sys/errror för att se om det finns något undantag. Om det inte finns något, är det troligt att ditt postskript tar lång tid, du kan skriva ut förloppsinformation i koden för att hitta den tidskrävande delen eller lägga till "--profiling_module", "cProfile" i arguments för ParallelRunStep att generera en profilfil med namnet under <process_name>.profile~/logs/sys/node/<node_id> mappen.

När stoppas ett jobb?

om det inte avbryts stoppas jobbet med status:

  • Avslutade. Om alla mini-batchar har bearbetats och utdata har genererats för append_row läge.
  • Misslyckades. Om error_threshold in Parameters for ParallelRunConfig överskrids eller om systemfel uppstod under jobbet.

Var hittar du rotorsaken till felet?

Du kan följa leadet i ~logs/job_result.txt för att hitta orsaksloggen och den detaljerade felloggen.

Påverkar nodfel jobbresultatet?

Inte om det finns andra tillgängliga noder i det avsedda beräkningsklustret. Orkestreraren startar en ny nod som ersättning och ParallelRunStep är motståndskraftig mot den här åtgärden.

Vad händer om init funktionen i inmatningsskriptet misslyckas?

ParallelRunStep har en mekanism för att försöka igen under en viss tid för att ge möjlighet till återställning från tillfälliga problem utan att fördröja jobbfelet för länge. Mekanismen är följande:

  1. Om alla agenter misslyckas när en nod startar init slutar vi att försöka efter 3 * process_count_per_node fel.
  2. Om alla agenter för alla noder misslyckas när jobbet startar init slutar vi att försöka om jobbet körs mer än 2 minuter och det uppstår 2 * node_count * process_count_per_node fel.
  3. Om alla agenter har fastnat i init mer än 3 * run_invocation_timeout + 30 sekunder misslyckas jobbet på grund av att inga förlopp har för lång tid.

Vad kommer att hända på OutOfMemory? Hur kontrollerar jag orsaken?

ParallelRunStep anger det aktuella försöket att bearbeta mini-batchen till felstatus och försöker starta om den misslyckade processen. Du kan söka efter ~logs/perf/<node_id> den minneskrävande processen.

Varför har jag många processNNN-filer?

ParallelRunStep startar nya arbetsprocesser i stället för de som avslutas onormalt, och varje process genererar en processNNN fil som logg. Men om processen misslyckades på grund av ett undantag under init funktionen för användarskriptet och felet upprepas kontinuerligt under 3 * process_count_per_node tider, kommer ingen ny arbetsprocess att startas.

Nästa steg