Condividi tramite


Guida al training della GPU distribuita (SDK v1)

Si applica a: Python SDK azureml v1

Altre informazioni su come usare il codice di training GPU distribuito in Azure Machine Learning (ML). Questo articolo non illustra il training distribuito. Consente di eseguire il codice di training distribuito esistente in Azure Machine Learning. Offre suggerimenti ed esempi da seguire per ogni framework:

  • Message Passing Interface (MPI)
    • Horovod
    • DeepSpeed
    • Variabili di ambiente da Open MPI
  • PyTorch
    • Inizializzazione del gruppo di processi
    • Opzioni di avvio
    • DistributedDataParallel (avvio per processo)
    • Uso di torch.distributed.launch (avvio a nodo)
    • PyTorch Lightning
    • Hugging Face Transformers
  • TensorFlow
    • Variabili di ambiente per TensorFlow (TF_CONFIG)
  • Accelerare il training GPU con InfiniBand

Prerequisiti

Esaminare questi concetti di base del training GPU distribuito, ad esempio parallelismo dei dati, parallelismo dei dati distribuitie parallelismo del modello.

Suggerimento

Se non si conosce il tipo di parallelismo da usare, più del 90% del tempo è consigliabile usare Parallelismo dei dati distribuiti.

MPI

Azure Machine Learning offre un processo MPI per avviare un determinato numero di processi in ogni nodo. È possibile adottare questo approccio per eseguire il training distribuito usando per ogni utilità di avvio per processo o per ogni avvio per nodo, a seconda che process_count_per_node sia impostato su 1 (impostazione predefinita) per ogni utilità di avvio dei nodi o sia uguale al numero di dispositivi/GPU per ogni utilità di avvio per processo. Azure Machine Learning costruisce il comando di avvio MPI completo (mpirun) dietro le quinte. Non è possibile fornire comandi head-node-launcher completi, ad esempio mpirun o DeepSpeed launcher.

Suggerimento

L'immagine Docker di base usata da un processo MPI di Azure Machine Learning deve avere una libreria MPI installata. Open MPI è incluso in tutte le immagini di base GPU di Azure Machine Learning. Quando si usa un'immagine Docker personalizzata, è necessario assicurarsi che l'immagine includa una libreria MPI. È consigliabile usare Open MPI, ma è anche possibile usare un'implementazione MPI diversa, ad esempio Intel MPI. Azure Machine Learning offre anche ambienti curati per i framework più diffusi.

Per eseguire il training distribuito tramite MPI, seguire questa procedura:

  1. Usare un ambiente di Azure Machine Learning con il framework di Deep Learning preferito e MPI. Azure Machine Learning offre ambienti curati per i framework più diffusi.
  2. Definire MpiConfiguration con process_count_per_node e node_count. process_count_per_node deve essere uguale al numero di GPU per nodo per avvio per processo o impostato su 1 (impostazione predefinita) per ogni avvio per nodo se lo script utente sarà responsabile dell'avvio dei processi per nodo.
  3. Passare l'oggetto MpiConfiguration al parametro distributed_job_config di ScriptRunConfig.
from azureml.core import Workspace, ScriptRunConfig, Environment, Experiment
from azureml.core.runconfig import MpiConfiguration

curated_env_name = 'AzureML-PyTorch-1.6-GPU'
pytorch_env = Environment.get(workspace=ws, name=curated_env_name)
distr_config = MpiConfiguration(process_count_per_node=4, node_count=2)

run_config = ScriptRunConfig(
  source_directory= './src',
  script='train.py',
  compute_target=compute_target,
  environment=pytorch_env,
  distributed_job_config=distr_config,
)

# submit the run configuration to start the job
run = Experiment(ws, "experiment_name").submit(run_config)

Horovod

Usare la configurazione del processo MPI quando si usa Horovod per il training distribuito con il framework di Deep Learning.

Assicurarsi che il codice segua questi suggerimenti:

  • Il codice di training viene instrumentato correttamente con Horovod prima di aggiungere le parti di Azure Machine Learning
  • L'ambiente di Azure Machine Learning contiene Horovod e MPI. Gli ambienti GPU curati da PyTorch e TensorFlow sono preconfigurati con Horovod e le relative dipendenze.
  • Creare un MpiConfiguration con la distribuzione desiderata.

Esempio Horovod

DeepSpeed

Non usare l'utilità di avvio personalizzata di DeepSpeed per eseguire il training distribuito con la libreria di DeepSpeed in Azure Machine Learning. Configurare invece un processo MPI per avviare il processo di training con MPI.

Assicurarsi che il codice segua questi suggerimenti:

  • L'ambiente di Azure Machine Learning contiene DeepSpeed e le relative dipendenze, Open MPI e mpi4py.
  • Creare un MpiConfiguration con la distribuzione.

Esempio di DeepSpeed

Variabili di ambiente da Open MPI

Quando si eseguono processi MPI con immagini Open MPI, le variabili di ambiente seguenti per ogni processo avviato sono:

  1. OMPI_COMM_WORLD_RANK: classificazione del processo
  2. OMPI_COMM_WORLD_SIZE: dimensione globale
  3. AZ_BATCH_MASTER_NODE: indirizzo primario con porta, MASTER_ADDR:MASTER_PORT
  4. OMPI_COMM_WORLD_LOCAL_RANK: classificazione locale del processo nel nodo
  5. OMPI_COMM_WORLD_LOCAL_SIZE: numero di processi nel nodo

Suggerimento

Nonostante il nome, la variabile di ambiente OMPI_COMM_WORLD_NODE_RANK non corrisponde al NODE_RANK. Per usare l'utilità di avvio per nodo, impostare process_count_per_node=1 e usare OMPI_COMM_WORLD_RANK come NODE_RANK.

PyTorch

Azure Machine Learning supporta l'esecuzione di processi distribuiti usando le funzionalità di training distribuite native di PyTorch (torch.distributed).

Suggerimento

Per il parallelismo dei dati, il materiale sussidiario ufficiale di PyTorch prevede l’uso di DistributedDataParallel (DDP) su DataParallel per il training distribuito a nodo singolo e multinodo. PyTorch consiglia anche di usare DistributedDataParallel sul pacchetto multielaborazione. La documentazione e gli esempi di Azure Machine Learning saranno quindi incentrati sul training DistributedDataParallel.

Inizializzazione del gruppo di processi

La parte principale di qualsiasi training distribuito si basa su un gruppo di processi che si conoscono tra loro e possono comunicare tra loro usando un back-end. Per PyTorch, il gruppo di processi viene creato chiamando torch.distributed.init_process_group in tutti i processi distribuiti per formare collettivamente un gruppo di processi.

torch.distributed.init_process_group(backend='nccl', init_method='env://', ...)

I back-end di comunicazione più comuni usati sono mpi, nccl e gloo. Per il training basato su GPU, è consigliato nccl per ottenere prestazioni ottimali e deve essere usato quando possibile.

init_method indica in che modo i processi possono individuarsi tra loro, come si inizializzano e verificano il gruppo di processi usando il back-end di comunicazione. Per impostazione predefinita, se init_method non è specificato PyTorch userà il metodo di inizializzazione della variabile di ambiente (env://). init_method è il metodo di inizializzazione consigliato da usare nel codice di training per eseguire PyTorch distribuito in Azure Machine Learning. PyTorch cercherà le variabili di ambiente seguenti per l'inizializzazione:

  • MASTER_ADDR: indirizzo IP del computer che ospiterà il processo con classificazione 0.
  • MASTER_PORT: una porta libera sul computer che ospiterà il processo con classificazione 0.
  • WORLD_SIZE: il numero totale di processi. Deve essere uguale al numero totale di dispositivi (GPU) usati per il training distribuito.
  • RANK: classificazione (globale) del processo corrente. I valori possibili sono da 0 a (dimensione globale -1).

Per altre informazioni sull'inizializzazione del gruppo di processi, vedere la documentazione di PyTorch.

Oltre a queste, molte applicazioni necessitano anche delle variabili di ambiente seguenti:

  • LOCAL_RANK: classificazione locale (relativa) del processo all'interno del nodo. I valori possibili sono da 0 a (numero di processi nel nodo - 1). Queste informazioni sono utili perché molte operazioni, ad esempio la preparazione dei dati, devono essere eseguite una sola volta per ogni nodo --- in genere in local_rank = 0.
  • NODE_RANK: classificazione del nodo per il training multinodo. I valori possibili sono da 0 a (numero totale di nodi -1).

Opzioni di avvio PyTorch

Il processo PyTorch di Azure Machine Learning supporta due tipi di opzioni per l'avvio del training distribuito:

  • Utilità di avvio per processo: il sistema avvierà automaticamente tutti i processi distribuiti, con tutte le informazioni rilevanti (ad esempio le variabili di ambiente) per configurare il gruppo di processi.
  • Avvio per nodo: si fornisce ad Azure Machine Learning l'utilità di avvio che verrà eseguita in ogni nodo. L'utilità di avvio gestirà l'avvio di ognuno dei processi in un determinato nodo. In locale all'interno di ogni nodo, RANK e LOCAL_RANK vengono configurati dall'utilità di avvio. L'utilità torch.distributed.launch e PyTorch Lightning appartengono entrambi a questa categoria.

Non esistono differenze fondamentali tra queste opzioni di avvio. La scelta dipende in gran parte dalle preferenze o dalle convenzioni dei framework/librerie basate su vanilla PyTorch (ad esempio Lightning o Hugging Face).

Le sezioni seguenti illustrano in dettaglio come configurare i processi PyTorch di Azure Machine Learning per ognuna delle opzioni di avvio.

DistributedDataParallel (avvio per processo)

Non è necessario usare un'utilità di avvio come torch.distributed.launch. Per eseguire un processo PyTorch distribuito:

  1. Specificare lo script di training e gli argomenti
  2. Creare un PyTorchConfiguration e specificare il process_count e node_count. Il process_count corrisponde al numero totale di processi da eseguire per il processo stesso. process_count in genere deve essere uguale a # GPUs per node x # nodes. Se process_count non è specificato, Azure Machine Learning avvierà per impostazione predefinita un processo per nodo.

Azure Machine Learning imposta le variabili di ambiente MASTER_ADDR, MASTER_PORT, WORLD_SIZEe NODE_RANK in ogni nodo e imposta le variabili di ambiente RANK e LOCAL_RANK a livello di processo.

Per usare questa opzione per il training su più processi per nodo, usare Azure Machine Learning Python SDK >= 1.22.0. Process_count è stato introdotto nella versione 1.22.0.

from azureml.core import ScriptRunConfig, Environment, Experiment
from azureml.core.runconfig import PyTorchConfiguration

curated_env_name = 'AzureML-PyTorch-1.6-GPU'
pytorch_env = Environment.get(workspace=ws, name=curated_env_name)
distr_config = PyTorchConfiguration(process_count=8, node_count=2)

run_config = ScriptRunConfig(
  source_directory='./src',
  script='train.py',
  arguments=['--epochs', 50],
  compute_target=compute_target,
  environment=pytorch_env,
  distributed_job_config=distr_config,
)

run = Experiment(ws, 'experiment_name').submit(run_config)

Suggerimento

Se lo script di training passa informazioni come classificazione locale o classificazione come argomenti script, è possibile fare riferimento alle variabili di ambiente negli argomenti:

arguments=['--epochs', 50, '--local_rank', $LOCAL_RANK]

Esempio di Pytorch di avvio per processo

Uso di torch.distributed.launch (avvio per nodo)

PyTorch offre un'utilità di avvio in torch.distributed.launch che è possibile usare per avviare più processi per nodo. Il modulo torch.distributed.launch genera più processi di training in ognuno dei nodi.

La procedura seguente illustra come configurare un processo PyTorch con un'utilità di avvio per nodo in Azure Machine Learning. Il processo ottiene l'equivalente dell'esecuzione del comando seguente:

python -m torch.distributed.launch --nproc_per_node <num processes per node> \
  --nnodes <num nodes> --node_rank $NODE_RANK --master_addr $MASTER_ADDR \
  --master_port $MASTER_PORT --use_env \
  <your training script> <your script arguments>
  1. Specificare il comando torch.distributed.launch al parametro command del costruttore ScriptRunConfig. Azure Machine Learning esegue questo comando in ogni nodo del cluster di training. --nproc_per_node deve essere minore o uguale al numero di GPU disponibili in ogni nodo. MASTER_ADDR, MASTER_PORT e NODE_RANK sono tutti impostati da Azure Machine Learning, quindi è sufficiente fare riferimento alle variabili di ambiente nel comando. Azure Machine Learning imposta MASTER_PORT su 6105, ma è possibile passare un valore diverso all'argomento --master_port del comando torch.distributed.launch, se lo si desidera. (L'utilità di avvio reimposta le variabili di ambiente.)
  2. Creare un PyTorchConfiguration e specificare il node_count.
from azureml.core import ScriptRunConfig, Environment, Experiment
from azureml.core.runconfig import PyTorchConfiguration

curated_env_name = 'AzureML-PyTorch-1.6-GPU'
pytorch_env = Environment.get(workspace=ws, name=curated_env_name)
distr_config = PyTorchConfiguration(node_count=2)
launch_cmd = "python -m torch.distributed.launch --nproc_per_node 4 --nnodes 2 --node_rank $NODE_RANK --master_addr $MASTER_ADDR --master_port $MASTER_PORT --use_env train.py --epochs 50".split()

run_config = ScriptRunConfig(
  source_directory='./src',
  command=launch_cmd,
  compute_target=compute_target,
  environment=pytorch_env,
  distributed_job_config=distr_config,
)

run = Experiment(ws, 'experiment_name').submit(run_config)

Suggerimento

Training su più GPU a nodo singolo: se si usa l'utilità di avvio per eseguire il training PyTorch multi GPU a nodo singolo, non è necessario specificare il parametro distributed_job_config di ScriptRunConfig.

launch_cmd = "python -m torch.distributed.launch --nproc_per_node 4 --use_env train.py --epochs 50".split()

run_config = ScriptRunConfig(
 source_directory='./src',
 command=launch_cmd,
 compute_target=compute_target,
 environment=pytorch_env,
)

Esempio di PyTorch per processo-avvio

PyTorch Lightning

PyTorch Lightning è una libreria open source leggera che fornisce un'interfaccia di alto livello per PyTorch. Lightning astrae molte delle configurazioni di training distribuite di livello inferiore necessarie per vanilla PyTorch. Lightning consente di eseguire gli script di training con impostazioni a GPU singola, multi GPU a nodo singolo e multinodo multi GPU. Di nascosto, avvia più processi simili a torch.distributed.launch per l'utente.

Per il training a nodo singolo (incluso multi GPU a nodo singolo), è possibile eseguire il codice in Azure Machine Learning senza dover specificare un distributed_job_config. Per eseguire un esperimento usando più nodi con più GPU, sono disponibili 2 opzioni:

  • Uso della configurazione di PyTorch (scelta consigliata): definire PyTorchConfiguration e specificare communication_backend="Nccl", node_count e process_count (si noti che si tratta del numero totale di processi, ad esempio num_nodes * process_count_per_node). Nel modulo Lightning Trainer specificare sia num_nodes che gpus per essere coerenti con PyTorchConfiguration. Ad esempio, num_nodes = node_count e gpus = process_count_per_node.

  • Uso della configurazione MPI:

    • Definire MpiConfiguration e specificare sia node_count che process_count_per_node. In Lightning Trainer specificare sia num_nodes che gpus rispettivamente come node_count e process_count_per_node da MpiConfiguration.

    • Per il training multinodo con MPI, Lightning richiede l'impostazione delle variabili di ambiente seguenti in ogni nodo del cluster di training:

      • MASTER_ADDR
      • MASTER_PORT
      • NODE_RANK
      • LOCAL_RANK

      Impostare manualmente queste variabili di ambiente richieste da Lightning negli script di training principali:

    import os
    from argparse import ArgumentParser
    
    def set_environment_variables_for_mpi(num_nodes, gpus_per_node, master_port=54965):
         if num_nodes > 1:
             os.environ["MASTER_ADDR"], os.environ["MASTER_PORT"] = os.environ["AZ_BATCH_MASTER_NODE"].split(":")
         else:
             os.environ["MASTER_ADDR"] = os.environ["AZ_BATCHAI_MPI_MASTER_NODE"]
             os.environ["MASTER_PORT"] = str(master_port)
    
         try:
             os.environ["NODE_RANK"] = str(int(os.environ.get("OMPI_COMM_WORLD_RANK")) // gpus_per_node)
             # additional variables
             os.environ["MASTER_ADDRESS"] = os.environ["MASTER_ADDR"]
             os.environ["LOCAL_RANK"] = os.environ["OMPI_COMM_WORLD_LOCAL_RANK"]
             os.environ["WORLD_SIZE"] = os.environ["OMPI_COMM_WORLD_SIZE"]
         except:
             # fails when used with pytorch configuration instead of mpi
             pass
    
    if __name__ == "__main__":
         parser = ArgumentParser()
         parser.add_argument("--num_nodes", type=int, required=True)
         parser.add_argument("--gpus_per_node", type=int, required=True)
         args = parser.parse_args()
         set_environment_variables_for_mpi(args.num_nodes, args.gpus_per_node)
    
         trainer = Trainer(
          num_nodes=args.num_nodes,
          gpus=args.gpus_per_node
      )
    

    Lightning gestisce il calcolo delle dimensioni globali dai flag Trainer --gpus e --num_nodes.

    from azureml.core import ScriptRunConfig, Experiment
    from azureml.core.runconfig import MpiConfiguration
    
    nnodes = 2
    gpus_per_node = 4
    args = ['--max_epochs', 50, '--gpus_per_node', gpus_per_node, '--accelerator', 'ddp', '--num_nodes', nnodes]
    distr_config = MpiConfiguration(node_count=nnodes, process_count_per_node=gpus_per_node)
    
    run_config = ScriptRunConfig(
       source_directory='./src',
       script='train.py',
       arguments=args,
       compute_target=compute_target,
       environment=pytorch_env,
       distributed_job_config=distr_config,
    )
    
    run = Experiment(ws, 'experiment_name').submit(run_config)
    

Hugging Face Transformers

Hugging Face offre molti esempi per l'uso della libreria Transformers con torch.distributed.launch per eseguire il training distribuito. Per eseguire questi esempi e gli script di training personalizzati usando l'API Transformers Trainer, seguire la sezione Utilizzo ditorch.distributed.launch.

Codice di configurazione del processo di esempio per ottimizzare il modello BERT di grandi dimensioni nell'attività MNLI di classificazione del testo usando lo script run_glue.py in un nodo con 8 GPU:

from azureml.core import ScriptRunConfig
from azureml.core.runconfig import PyTorchConfiguration

distr_config = PyTorchConfiguration() # node_count defaults to 1
launch_cmd = "python -m torch.distributed.launch --nproc_per_node 8 text-classification/run_glue.py --model_name_or_path bert-large-uncased-whole-word-masking --task_name mnli --do_train --do_eval --max_seq_length 128 --per_device_train_batch_size 8 --learning_rate 2e-5 --num_train_epochs 3.0 --output_dir /tmp/mnli_output".split()

run_config = ScriptRunConfig(
  source_directory='./src',
  command=launch_cmd,
  compute_target=compute_target,
  environment=pytorch_env,
  distributed_job_config=distr_config,
)

È anche possibile usare l'opzione avvio per processo per eseguire il training distribuito senza usare torch.distributed.launch. Un aspetto da tenere presente se si usa questo metodo è che i trasformatori TrainingArguments si aspettano che la classificazione locale venga passata come argomento (--local_rank). torch.distributed.launch si occupa di questa situazione quando --use_env=False, ma se si usa l'avvio per processo, è necessario passare in modo esplicito la classificazione locale come argomento allo script di training --local_rank=$LOCAL_RANK perché Azure Machine Learning imposta solo la variabile di ambiente LOCAL_RANK.

TensorFlow

Se si usa TensorFlow distribuito nativo nel codice di training, ad esempio l'API tf.distribute.Strategy di TensorFlow 2.x, è possibile avviare il processo distribuito tramite Azure Machine Learning usando il TensorflowConfiguration.

A tale scopo, specificare un oggetto TensorflowConfiguration per il parametro distributed_job_config del costruttore ScriptRunConfig. Se si usa tf.distribute.experimental.MultiWorkerMirroredStrategy, specificare il worker_count nella TensorflowConfiguration corrispondente al numero di nodi per il processo di training.

from azureml.core import ScriptRunConfig, Environment, Experiment
from azureml.core.runconfig import TensorflowConfiguration

curated_env_name = 'AzureML-TensorFlow-2.3-GPU'
tf_env = Environment.get(workspace=ws, name=curated_env_name)
distr_config = TensorflowConfiguration(worker_count=2, parameter_server_count=0)

run_config = ScriptRunConfig(
  source_directory='./src',
  script='train.py',
  compute_target=compute_target,
  environment=tf_env,
  distributed_job_config=distr_config,
)

# submit the run configuration to start the job
run = Experiment(ws, "experiment_name").submit(run_config)

Se lo script di training usa la strategia del server dei parametri per il training distribuito, ad esempio per TensorFlow 1.x legacy, sarà necessario specificare anche il numero di server di parametri da usare nel processo, ad esempio tf_config = TensorflowConfiguration(worker_count=2, parameter_server_count=1).

TF_CONFIG

In TensorFlow la variabile di ambiente TF_CONFIG è necessaria per il training su più computer. Per i processi TensorFlow, Azure Machine Learning configurerà e imposterà la variabile TF_CONFIG in modo appropriato per ogni ruolo di lavoro prima di eseguire lo script di training.

È possibile accedere TF_CONFIG dallo script di training se è necessario: os.environ['TF_CONFIG'].

Esempio TF_CONFIG impostato in un nodo di lavoro principale:

TF_CONFIG='{
    "cluster": {
        "worker": ["host0:2222", "host1:2222"]
    },
    "task": {"type": "worker", "index": 0},
    "environment": "cloud"
}'

Esempio di TensorFlow

Accelerazione del training della GPU distribuita con InfiniBand

Man mano che aumenta il numero di macchine virtuali che eseguono il training di un modello, il tempo necessario per eseguire il training del modello stesso deve diminuire. La diminuzione del tempo, idealmente, deve essere proporzionale in modo lineare al numero di macchine virtuali di training. Ad esempio, se il training di un modello in una macchina virtuale richiede 100 secondi, il training dello stesso modello in due macchine virtuali dovrebbe richiedere idealmente 50 secondi. Il training del modello su quattro macchine virtuali richiederà 25 secondi e così via.

InfiniBand può essere un fattore importante per ottenere questo ridimensionamento lineare. InfiniBand consente la comunicazione da GPU a GPU a bassa latenza tra nodi in un cluster. Il funzionamento di InfiniBand richiede hardware specializzato. Alcune serie di macchine virtuali di Azure, in particolare NC, ND e serie H, dispongono ora di macchine virtuali con capacità RDMA e con il supporto SR-IOV e InfiniBand. Queste macchine virtuali comunicano tramite la bassa latenza e la rete InfiniBand a larghezza di banda elevata, che è molto più efficiente rispetto alla connettività basata su Ethernet. SR-IOV per InfiniBand consente prestazioni quasi bare metal per qualsiasi libreria MPI (MPI viene usato da molti framework di training distribuiti e strumenti, incluso il software NCCL di NVIDIA). Questi SKU sono progettati per soddisfare le esigenze di carichi di lavoro di Machine Learning a elevato utilizzo di calcolo e di apprendimento automatico con GPU. Per altre informazioni, vedere Accelerare il Training distribuito in Azure Machine Learning con SR-IOV.

In genere, gli SKU delle macchine virtuali con un nome "r" contengono l'hardware InfiniBand richiesto, differentemente da quelli senza "r" in genere. ("r" è un riferimento a RDMA, che è l'acronimo di "remote direct memory access.") Ad esempio, lo SKU della macchina virtuale Standard_NC24rs_v3 è abilitato per InfiniBand, ma lo SKU Standard_NC24s_v3 non lo è. A parte le funzionalità InfiniBand, le specifiche tra questi due SKU sono in gran parte le stesse: hanno 24 core, 448 GB di RAM, 4 GPU dello stesso SKU e così via. Altre informazioni sugli SKU dei computer abilitati per RDMA e InfiniBand.

Avviso

Lo SKU del computer di generazione precedente Standard_NC24r è abilitato per RDMA, ma non contiene un hardware SR-IOV necessario per InfiniBand.

Se si crea un cluster AmlCompute di una di queste dimensioni abilitate per RDMA, le dimensioni abilitate per InfiniBand, l'immagine del sistema operativo verrà fornita con il driver Mellanox OFED necessario per abilitare InfiniBand preinstallato e preconfigurato.

Passaggi successivi