Utbildningsguide för distribuerad GPU

GÄLLER FÖR:Python SDK azureml v1

Läs mer om hur du använder distribuerad GPU-träningskod i Azure Machine Learning (ML). Den här artikeln lär dig inte om distribuerad utbildning. Det hjälper dig att köra din befintliga distribuerade träningskod på Azure Machine Learning. Den innehåller tips och exempel som du kan följa för varje ramverk:

  • MPI (Message Passing Interface)
    • Horovod
    • Djupspeed
    • Miljövariabler från Open MPI
  • PyTorch
    • Initiering av processgrupp
    • Startalternativ
    • DistributedDataParallel (per processstart)
    • Använda torch.distributed.launch (per nodstart)
    • PyTorch Lightning
    • Krama ansiktstransformatorer
  • TensorFlow
    • Miljövariabler för TensorFlow (TF_CONFIG)
  • Påskynda GPU-träning med InfiniBand

Förutsättningar

Granska dessa grundläggande begrepp för distribuerad GPU-utbildning , till exempel dataparallellitet, distribuerad dataparallellitet och modellparallellitet.

Tips

Om du inte vet vilken typ av parallellitet du ska använda bör du använda distribuerad dataparallellitet mer än 90 % av tiden.

Mpi

Azure ML erbjuder ett MPI-jobb för att starta ett visst antal processer i varje nod. Du kan använda den här metoden för att köra distribuerad träning med antingen per processstartare eller startprogrammet per nod, beroende på om process_count_per_node är inställt på 1 (standard) för startprogrammet per nod eller lika med antalet enheter/GPU:er för start per process. Azure ML skapar det fullständiga MPI-startkommandot (mpirun) i bakgrunden. Du kan inte ange egna fullständiga head-node-launcher-kommandon som mpirun eller DeepSpeed launcher.

Tips

Docker-basavbildningen som används av ett Azure Machine Learning MPI-jobb måste ha ett MPI-bibliotek installerat. Öppna MPI ingår i alla AzureML GPU-basavbildningar. När du använder en anpassad Docker-avbildning ansvarar du för att se till att avbildningen innehåller ett MPI-bibliotek. Öppna MPI rekommenderas, men du kan också använda en annan MPI-implementering, till exempel Intel MPI. Azure ML tillhandahåller även utvalda miljöer för populära ramverk.

Följ dessa steg för att köra distribuerad träning med MPI:

  1. Använd en Azure ML-miljö med önskat ramverk för djupinlärning och MPI. AzureML tillhandahåller en kuraterad miljö för populära ramverk.
  2. Definiera MpiConfiguration med process_count_per_node och node_count. process_count_per_node ska vara lika med antalet GPU:er per nod för varje processstart eller anges till 1 (standardvärdet) för start per nod om användarskriptet ansvarar för att starta processerna per nod.
  3. Skicka objektet MpiConfiguration till parametern distributed_job_configScriptRunConfigför .
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

Använd MPI-jobbkonfigurationen när du använder Horovod för distribuerad träning med ramverket för djupinlärning.

Se till att koden följer dessa tips:

  • Träningskoden instrumenteras korrekt med Horovod innan du lägger till Azure ML-delarna
  • Din Azure ML-miljö innehåller Horovod och MPI. PyTorch- och TensorFlow-kurerade GPU-miljöer är förkonfigurerade med Horovod och dess beroenden.
  • Skapa en MpiConfiguration med önskad distribution.

Horovod-exempel

Djupspeed

Använd inte DeepSpeeds anpassade startprogrammet för att köra distribuerad träning med DeepSpeed-biblioteket i Azure ML. Konfigurera i stället ett MPI-jobb för att starta träningsjobbet med MPI.

Se till att koden följer dessa tips:

  • Din Azure ML-miljö innehåller DeepSpeed och dess beroenden, Open MPI och mpi4py.
  • Skapa ett MpiConfiguration med din distribution.

DeepSeed-exempel

Miljövariabler från Open MPI

När du kör MPI-jobb med Öppna MPI-avbildningar startar följande miljövariabler för varje process:

  1. OMPI_COMM_WORLD_RANK - processens rangordning
  2. OMPI_COMM_WORLD_SIZE - Världsstorleken
  3. AZ_BATCH_MASTER_NODE - primär adress med port, MASTER_ADDR:MASTER_PORT
  4. OMPI_COMM_WORLD_LOCAL_RANK – den lokala rangordningen för processen på noden
  5. OMPI_COMM_WORLD_LOCAL_SIZE – antal processer på noden

Tips

Trots namnet motsvarar NODE_RANKmiljövariabeln OMPI_COMM_WORLD_NODE_RANK inte . Om du vill använda startprogrammet per nod anger process_count_per_node=1 du och använder OMPI_COMM_WORLD_RANK som NODE_RANK.

PyTorch

Azure ML stöder körning av distribuerade jobb med PyTorchs interna distribuerade träningsfunktioner (torch.distributed).

Tips

För dataparallellitet är den officiella PyTorch-vägledningen att använda DDP (DistributedDataParallel) över DataParallel för distribuerad träning med både en nod och flera noder. PyTorch rekommenderar också att du använder DistributedDataParallel i paketet för flera processer. Dokumentationen och exemplen i Azure Machine Learning fokuserar därför på DistributedDataParallel-utbildning.

Initiering av processgrupp

Ryggraden i all distribuerad träning baseras på en grupp processer som känner varandra och kan kommunicera med varandra med hjälp av en serverdel. För PyTorch skapas processgruppen genom att anropa torch.distributed.init_process_group i alla distribuerade processer för att gemensamt bilda en processgrupp.

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

De vanligaste kommunikationsserverdelarna som används är mpi, nccloch gloo. För GPU-baserad träning nccl rekommenderas för bästa prestanda och bör användas när det är möjligt.

init_method anger hur varje process kan identifiera varandra, hur de initierar och verifierar processgruppen med hjälp av kommunikationsserverdelen. Om inte anges använder PyTorch som standard init_method initieringsmetoden för miljövariabeln (env://). init_method är den rekommenderade initieringsmetoden som ska användas i träningskoden för att köra distribuerade PyTorch i Azure ML. PyTorch söker efter följande miljövariabler för initiering:

  • MASTER_ADDR – IP-adressen för den dator som ska vara värd för processen med rangordning 0.
  • MASTER_PORT – En kostnadsfri port på datorn som ska vara värd för processen med rank 0.
  • WORLD_SIZE - Det totala antalet processer. Ska vara lika med det totala antalet enheter (GPU) som används för distribuerad träning.
  • RANK – Rangordningen (globalt) för den aktuella processen. Möjliga värden är 0 till (världsstorlek - 1).

Mer information om initiering av processgrupper finns i PyTorch-dokumentationen.

Utöver dessa behöver många program även följande miljövariabler:

  • LOCAL_RANK – Den lokala (relativa) rangordningen för processen inom noden. Möjliga värden är 0 till (antal processer på noden – 1). Den här informationen är användbar eftersom många åtgärder, till exempel förberedelse av data, endast bör utföras en gång per nod --- vanligtvis på local_rank = 0.
  • NODE_RANK – Rangordningen för noden för träning med flera noder. Möjliga värden är 0 till (totalt antal noder – 1).

Startalternativ för PyTorch

Azure ML PyTorch-jobbet stöder två typer av alternativ för att starta distribuerad utbildning:

  • Start per process: Systemet startar alla distribuerade processer åt dig, med all relevant information (till exempel miljövariabler) för att konfigurera processgruppen.
  • Startprogrammet per nod: Du tillhandahåller Azure ML med den verktygsstartare som ska köras på varje nod. Verktygsstartaren hanterar start av var och en av processerna på en viss nod. Lokalt inom varje nod RANK och LOCAL_RANK konfigureras av startprogrammet. Både torch.distributed.launch-verktyget och PyTorch Lightning hör hemma i den här kategorin.

Det finns inga grundläggande skillnader mellan dessa startalternativ. Valet är till stor del upp till din preferens eller konventionerna i de ramverk /bibliotek som bygger på vanilj PyTorch (till exempel Lightning eller Hugging Face).

Följande avsnitt innehåller mer information om hur du konfigurerar Azure ML PyTorch-jobb för var och en av startalternativen.

DistributedDataParallel (per process-launch)

Du behöver inte använda ett startverktyg som torch.distributed.launch. Så här kör du ett distribuerat PyTorch-jobb:

  1. Ange träningsskriptet och argumenten
  2. Skapa en PyTorchConfiguration och ange process_count och node_count. process_count motsvarar det totala antalet processer som du vill köra för jobbet. process_count ska vanligtvis vara lika med # GPUs per node x # nodes. Om process_count inte anges startar Azure ML som standard en process per nod.

Azure ML ställer in MASTER_ADDRmiljövariablerna , MASTER_PORT, WORLD_SIZEoch NODE_RANK på varje nod och anger variablerna för processnivå RANK och LOCAL_RANK miljö.

Om du vill använda det här alternativet för träning med flera processer per nod använder du Azure ML Python SDK >= 1.22.0. Process_count introducerades i 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)

Tips

Om träningsskriptet skickar information som lokal rangordning eller rangordning som skriptargument kan du referera till miljövariablerna i argumenten:

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

Pytorch-exempel för per-process-launch

Använda torch.distributed.launch (per nodstart)

PyTorch tillhandahåller ett startverktyg i torch.distributed.launch som du kan använda för att starta flera processer per nod. Modulen torch.distributed.launch skapar flera träningsprocesser på var och en av noderna.

Följande steg visar hur du konfigurerar ett PyTorch-jobb med en startprogrammet per nod i Azure ML. Jobbet uppnår motsvarigheten till att köra följande kommando:

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. torch.distributed.launch Ange kommandot till parametern command för ScriptRunConfig konstruktorn. Azure ML kör det här kommandot på varje nod i träningsklustret. --nproc_per_node ska vara mindre än eller lika med antalet GPU:er som är tillgängliga på varje nod. MASTER_ADDR, MASTER_PORT och NODE_RANK anges av Azure ML, så du kan bara referera till miljövariablerna i kommandot . Azure ML anger MASTER_PORT till 6105, men du kan skicka ett annat värde till --master_port argumentet för kommandot torch.distributed.launch om du vill. (Startverktyget återställer miljövariablerna.)
  2. Skapa en PyTorchConfiguration och ange 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)

Tips

Träning med flera GPU:ar med en nod: Om du använder startverktyget för att köra träning med flera GPU:er med en nod behöver du inte ange parametern distributed_job_config för 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,
)

PyTorch-exempel för start av nod

PyTorch Lightning

PyTorch Lightning är ett enkelt bibliotek med öppen källkod som tillhandahåller ett högnivågränssnitt för PyTorch. Lightning abstraherar bort många av de distribuerade träningskonfigurationer på lägre nivå som krävs för vanilla PyTorch. Med Lightning kan du köra dina träningsskript i inställningar för enkel GPU, multi-GPU med en nod och flera noder med flera GPU:er. Bakom scenen startas flera processer åt dig som liknar torch.distributed.launch.

För träning med en nod (inklusive multi-GPU med en nod) kan du köra koden i Azure ML utan att behöva ange en distributed_job_config. Det finns två alternativ för att köra ett experiment med flera noder med flera GPU:er:

  • Använda PyTorch-konfiguration (rekommenderas): Definiera PyTorchConfiguration och ange communication_backend="Nccl", node_countoch process_count (observera att det här är det totala antalet processer, dvs. num_nodes * process_count_per_node). I Lightning Trainer-modulen anger du både num_nodes och gpus för att vara konsekvent med PyTorchConfiguration. Exempelvis num_nodes = node_count och gpus = process_count_per_node.

  • Använda MPI-konfiguration:

    • Definiera MpiConfiguration och ange både node_count och process_count_per_node. I Lightning Trainer anger du både num_nodes och gpus som ska vara samma som node_count och process_count_per_node från MpiConfiguration.

    • För träning med flera noder med MPI kräver Lightning att följande miljövariabler anges på varje nod i träningsklustret:

      • MASTER_ADDR
      • MASTER_PORT
      • NODE_RANK
      • LOCAL_RANK

      Ange de här miljövariablerna manuellt som Lightning kräver i huvudträningsskripten:

    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 hanterar databehandling av världsstorleken från Trainer-flaggorna --gpus och --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)
    

Krama ansiktstransformatorer

Hugging Face innehåller många exempel på hur du använder biblioteket Transformers med torch.distributed.launch för att köra distribuerad träning. Om du vill köra de här exemplen och dina egna anpassade träningsskript med hjälp av TRANSFORMERS Trainer-API:et följer du avsnittet Using (Använda torch.distributed.launch ).

Exempel på jobbkonfigurationskod för att finjustera den stora BERT-modellen för MNLI-aktiviteten för textklassificering med hjälp av skriptet run_glue.py på en nod med 8 GPU:er:

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,
)

Du kan också använda alternativet för att starta per process för att köra distribuerad träning utan att använda torch.distributed.launch. En sak att tänka på om du använder den här metoden är att transformerarna TrainingArguments förväntar sig att den lokala rangordningen skickas som ett argument (--local_rank). torch.distributed.launch tar hand om detta när --use_env=False, men om du använder per process-start måste du uttryckligen skicka den lokala rangordningen som ett argument till träningsskriptet --local_rank=$LOCAL_RANK eftersom Azure ML bara anger LOCAL_RANK miljövariabeln.

TensorFlow

Om du använder inbyggt distribuerat TensorFlow i din träningskod, till exempel TensorFlow 2.xs tf.distribute.Strategy API, kan du starta det distribuerade jobbet via Azure ML med hjälp av TensorflowConfiguration.

Det gör du genom att ange ett TensorflowConfiguration -objekt till -parametern distributed_job_config för ScriptRunConfig konstruktorn. Om du använder tf.distribute.experimental.MultiWorkerMirroredStrategyanger worker_count du i TensorflowConfiguration som motsvarar antalet noder för träningsjobbet.

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)

Om ditt träningsskript använder parameterserverstrategin för distribuerad träning, till exempel för äldre TensorFlow 1.x, måste du också ange antalet parameterservrar som ska användas i jobbet, till exempel tf_config = TensorflowConfiguration(worker_count=2, parameter_server_count=1).

TF_CONFIG

I TensorFlow krävs miljövariabeln TF_CONFIG för träning på flera datorer. För TensorFlow-jobb konfigurerar och ställer Azure ML in TF_CONFIG variabeln på lämpligt sätt för varje arbetare innan du kör träningsskriptet.

Du kan komma åt TF_CONFIG från träningsskriptet om du behöver: os.environ['TF_CONFIG'].

Exempel TF_CONFIG som angetts på en arbetsnod:

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

TensorFlow-exempel

Accelerera distribuerad GPU-träning med InfiniBand

När antalet virtuella datorer som tränar en modell ökar bör den tid som krävs för att träna modellen minska. Den minskade tiden bör helst vara linjärt proportionell mot antalet virtuella träningsdatorer. Om det till exempel tar 100 sekunder att träna en modell på en virtuell dator bör det ta 50 sekunder att träna samma modell på två virtuella datorer. Det bör ta 25 sekunder att träna modellen på fyra virtuella datorer och så vidare.

InfiniBand kan vara en viktig faktor för att uppnå den här linjära skalningen. InfiniBand möjliggör GPU-till-GPU-kommunikation med låg latens mellan noder i ett kluster. InfiniBand kräver specialiserad maskinvara för att fungera. Vissa Azure VM-serier, särskilt NC-, ND- och H-serien, har nu RDMA-kompatibla virtuella datorer med stöd för SR-IOV och InfiniBand. Dessa virtuella datorer kommunicerar via InfiniBand-nätverket med låg latens och hög bandbredd, vilket är mycket mer högpresterande än Ethernet-baserad anslutning. SR-IOV för InfiniBand möjliggör nästan bare metal-prestanda för alla MPI-bibliotek (MPI används av många distribuerade träningsramverk och verktyg, inklusive NVIDIA:s NCCL-programvara.) Dessa SKU:er är avsedda att uppfylla behoven hos beräkningsintensiva, GPU-acclererade maskininlärningsarbetsbelastningar. Mer information finns i Accelerating Distributed Training in Azure Machine Learning with SR-IOV (Accelerera distribuerad träning i Azure Machine Learning med SR-IOV).

Vanligtvis innehåller VM-SKU:er med ett "r" i namnet den InfiniBand-maskinvara som krävs, och de som inte har ett "r" gör vanligtvis inte det. ('r' är en referens till RDMA, som står för "direktåtkomst till fjärrminne")) Den virtuella datorns SKU Standard_NC24rs_v3 är till exempel InfiniBand-aktiverad, men SKU Standard_NC24s_v3 :n är inte det. Förutom InfiniBand-funktionerna är specifikationerna mellan dessa två SKU:er i stort sett desamma – båda har 24 kärnor, 448 GB RAM-minne, 4 GPU:er av samma SKU osv. Läs mer om RDMA- och InfiniBand-aktiverade dator-SKU:er.

Varning

Den äldre generationens dator-SKU Standard_NC24r är RDMA-aktiverad, men den innehåller inte den SR-IOV-maskinvara som krävs för InfiniBand.

Om du skapar ett AmlCompute kluster av någon av dessa RDMA-kompatibla, InfiniBand-aktiverade storlekar kommer OS-avbildningen att levereras med den Mellanox OFED-drivrutin som krävs för att aktivera InfiniBand förinstallerat och förkonfigurerat.

Nästa steg