Leitfaden zum verteilten GPU-Training SDK (v2)

GILT FÜR: Python SDK azure-ai-ml v2 (aktuell)

Hier erfahren Sie mehr über die Verwendung von verteiltem GPU-Trainingscode in Azure Machine Learning. Dieser Artikel hilft Ihnen beim Ausführen des vorhandenen verteilten Trainingscodes und bietet Tipps und Beispiele für jedes Framework:

  • Message Passing Interface (MPI)
    • Horovod
    • Umgebungsvariablen aus Open MPI
  • PyTorch
  • TensorFlow
  • Beschleunigen des GPU-Trainings mit InfiniBand

Voraussetzungen

Lernen Sie diese grundlegenden Konzepte des verteilten GPU-Trainings wie Datenparallelität, verteilte Datenparallelität und Modellparallelität kennen.

Tipp

Wenn Sie nicht wissen, welche Art von Parallelität verwendet werden soll, sollten Sie in mehr als 90 % der Fälle verteilte Datenparallelität verwenden.

MPI

Azure Machine Learning bietet einen MPI-Auftrag zum Starten einer bestimmten Anzahl von Prozessen in jedem Knoten. Azure Machine Learning erstellt den vollständigen Befehl für den MPI-Start (mpirun) im Hintergrund. Sie können keine eigenen vollständigen Hauptknoten-Startprogramm-Befehle wie mpirun oder DeepSpeed launcher bereitstellen.

Tipp

Für das Docker-Basisimage, das von einem Azure Machine Learning-MPI-Auftrag verwendet wird, muss eine MPI-Bibliothek installiert sein. Open MPI ist in allen GPU-Basisimages für Azure Machine Learning enthalten. Wenn Sie ein benutzerdefiniertes Docker-Image verwenden, müssen Sie sicherstellen, dass das Image eine MPI-Bibliothek enthält. Open MPI wird empfohlen, aber Sie können auch eine andere MPI-Implementierung wie Intel MPI verwenden. Azure Machine Learning stellt auch kuratierte Umgebungen für beliebte Frameworks bereit.

Führen Sie die folgenden Schritte aus, um verteiltes Training mit MPI ausführen zu können:

  1. Verwenden Sie eine Azure Machine Learning-Umgebung mit dem bevorzugten Deep Learning-Framework und MPI. Azure Machine Learning stellt kuratierte Umgebungen für beliebte Frameworks bereit. Oder erstellen Sie eine benutzerdefinierte Umgebung mit dem bevorzugten Deep Learning Framework und MPI.
  2. Definieren Sie einen command mit instance_count. instance_count sollte gleich der Anzahl von GPUs pro Knoten für den Start pro Prozess sein oder auf 1 (Standard) für den Start pro Knoten festgelegt sein, wenn das Benutzerskript für das Starten der Prozesse pro Knoten verantwortlich ist.
  3. Verwenden Sie den distribution-Parameter von command, um Einstellungen für MpiDistribution anzugeben.
from azure.ai.ml import command, MpiDistribution

job = command(
    code="./src",  # local path where the code is stored
    command="python train.py --epochs ${{inputs.epochs}}",
    inputs={"epochs": 1},
    environment="AzureML-tensorflow-2.7-ubuntu20.04-py38-cuda11-gpu@latest",
    compute="gpu-cluster",
    instance_count=2,
    distribution=MpiDistribution(process_count_per_instance=2),
    display_name="tensorflow-mnist-distributed-horovod-example"
    # experiment_name: tensorflow-mnist-distributed-horovod-example
    # description: Train a basic neural network with TensorFlow on the MNIST dataset, distributed via Horovod.
)

Horovod

Verwenden Sie die MPI-Auftragskonfiguration, wenn Sie Horovod für verteiltes Training mit dem Deep Learning-Framework verwenden.

Stellen Sie sicher, dass Ihr Code den folgenden Tipps entspricht:

  • Der Trainingscode wird ordnungsgemäß mit Horovod instrumentiert, bevor die Azure Machine Learning-Bestandteile hinzugefügt werden.
  • Ihre Azure Machine Learning-Umgebung enthält Horovod und MPI. Die kuratierten GPU-Umgebungen PyTorch und TensorFlow sind mit Horovod und seinen Abhängigkeiten vorkonfiguriert.
  • Erstellen Sie eine command mit Ihrer gewünschten Verteilung.

Horovod-Beispiel

Umgebungsvariablen aus Open MPI

Beim Ausführen von MPI-Aufträgen mit Open MPI-Images können Sie die folgenden Umgebungsvariablen für jeden gestarteten Prozess verwenden:

  1. OMPI_COMM_WORLD_RANK: der Rang des Prozesses
  2. OMPI_COMM_WORLD_SIZE: die Weltgröße
  3. AZ_BATCH_MASTER_NODE: die primäre Adresse mit Port, MASTER_ADDR:MASTER_PORT
  4. OMPI_COMM_WORLD_LOCAL_RANK: der lokale Rang des Prozesses auf dem Knoten
  5. OMPI_COMM_WORLD_LOCAL_SIZE: die Anzahl der Prozesse auf dem Knoten

Tipp

Trotz des Namens entspricht die Umgebungsvariable OMPI_COMM_WORLD_NODE_RANK nicht NODE_RANK. Um das Pro-Knoten-Startprogramm zu verwenden, legen Sie process_count_per_node=1 fest, und verwenden Sie OMPI_COMM_WORLD_RANK als NODE_RANK.

PyTorch

Azure Machine Learning unterstützt die Ausführung verteilter Aufträge mithilfe der nativen verteilten Trainingsfunktionen von PyTorch (torch.distributed).

Tipp

Für Datenparallelität ist die offizielle PyTorch-Anleitung die Verwendung von DistributedDataParallel (DDP) über DataParallel für verteiltes Training sowohl mit einem einzelnen als auch mehreren Knoten. PyTorch empfiehlt auch die Verwendung von DistributedDataParallel über das Multiprozessorpaket. Azure Machine Learning-Dokumentation und -Beispiele konzentrieren sich daher auf das DistributedDataParallel-Training.

Initialisierung von Prozessgruppen

Eine Gruppe von Prozessen, die sich gegenseitig kennen und über ein Back-End miteinander kommunizieren können, bilden das Rückgrat eines verteilten Trainings. Für PyTorch wird die Prozessgruppe erstellt, indem torch.distributed.init_process_group in allen verteilten Prozessen aufgerufen wird, um zusammen eine Prozessgruppe zu bilden.

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

Die am häufigsten verwendeten Kommunikations-Back-Ends sind mpi, nccl und gloo. Für GPU-basiertes Training wird nccl empfohlen, um die beste Leistung zu erzielen, und sollte nach Möglichkeit verwendet werden.

init_method gibt an, wie die einzelnen Prozesse sich gegenseitig entdecken und wie sie die Prozessgruppe mithilfe des Kommunikations-Back-Ends initialisieren und überprüfen. Wenn init_method nicht angegeben ist, verwendet PyTorch standardmäßig die Methode zum Initialisieren der Umgebungsvariablen (env://). init_method ist die empfohlene Initialisierungsmethode in Ihrem Trainingscode zum Ausführen verteilter PyTorch-Aufträge in Azure Machine Learning. PyTorch sucht für die Initialisierung nach den folgenden Umgebungsvariablen:

  • MASTER_ADDR: IP-Adresse des Computers, auf dem der Prozess mit dem Rang 0 gehostet wird
  • MASTER_PORT: Ein freier Port auf dem Computer, auf dem der Prozess mit dem Rang 0 gehostet wird
  • WORLD_SIZE: Gesamtzahl von Prozessen. Dieser Wert sollte der Gesamtzahl von Geräten (GPU) entsprechen, die für das verteilte Training verwendet werden.
  • RANK: Der (globale) Rang des aktuellen Prozesses. Die möglichen Werte sind 0 bis „WORLD_SIZE - 1“.

Weitere Informationen zur Initialisierung von Prozessgruppen finden Sie in der PyTorch-Dokumentation.

Viele Anwendungen benötigen auch die folgenden Umgebungsvariablen:

  • LOCAL_RANK: Der lokale (relative) Rang des Prozesses auf dem Knoten. Die möglichen Werte sind 0 bis „Anzahl von Prozessen auf dem Knoten - 1“. Diese Informationen sind nützlich, da viele Vorgänge wie die Datenaufbereitung nur einmal pro Knoten ausgeführt werden sollten – und in der Regel auf local_rank = 0.
  • NODE_RANK: Der Rang des Knotens in Bezug auf das Training mit mehreren Knoten. Die möglichen Werte sind 0 bis „Gesamtknotenzahl - 1“.

Sie müssen kein Starthilfsprogramm wie torch.distributed.launch verwenden. So führen Sie einen verteilten PyTorch-Auftrag aus:

  1. Geben Sie das Trainingsskript und die Argumente an.
  2. Erstellen Sie einen command, und geben Sie den Typ als PyTorch sowie die process_count_per_instance im distribution-Parameter an. process_count_per_instance steht für die Gesamtzahl von Prozessen, die Sie für Ihren Auftrag ausführen möchten. process_count_per_instance sollte in der Regel gleich # of GPUs per node sein. Wenn process_count_per_instance nicht angegeben ist, startet Azure Machine Learning standardmäßig einen Prozess pro Knoten.

Azure Machine Learning legt die Umgebungsvariablen MASTER_ADDR, MASTER_PORT, WORLD_SIZE und NODE_RANK auf jedem Knoten sowie die Umgebungsvariablen RANK und LOCAL_RANK auf Prozessebene fest.

from azure.ai.ml import command
from azure.ai.ml.entities import Data
from azure.ai.ml import Input
from azure.ai.ml import Output
from azure.ai.ml.constants import AssetTypes

# === Note on path ===
# can be can be a local path or a cloud path. AzureML supports https://`, `abfss://`, `wasbs://` and `azureml://` URIs.
# Local paths are automatically uploaded to the default datastore in the cloud.
# More details on supported paths: https://docs.microsoft.com/azure/machine-learning/how-to-read-write-data-v2#supported-paths

inputs = {
    "cifar": Input(
        type=AssetTypes.URI_FOLDER, path=returned_job.outputs.cifar.path
    ),  # path="azureml:azureml_stoic_cartoon_wgb3lgvgky_output_data_cifar:1"), #path="azureml://datastores/workspaceblobstore/paths/azureml/stoic_cartoon_wgb3lgvgky/cifar/"),
    "epoch": 10,
    "batchsize": 64,
    "workers": 2,
    "lr": 0.01,
    "momen": 0.9,
    "prtfreq": 200,
    "output": "./outputs",
}

from azure.ai.ml.entities import ResourceConfiguration

job = command(
    code="./src",  # local path where the code is stored
    command="python train.py --data-dir ${{inputs.cifar}} --epochs ${{inputs.epoch}} --batch-size ${{inputs.batchsize}} --workers ${{inputs.workers}} --learning-rate ${{inputs.lr}} --momentum ${{inputs.momen}} --print-freq ${{inputs.prtfreq}} --model-dir ${{inputs.output}}",
    inputs=inputs,
    environment="azureml-acpt-pytorch-2.0-cuda11.7:26",
    instance_count=2,  # In this, only 2 node cluster was created.
    distribution={
        "type": "PyTorch",
        # set process count to the number of gpus per node
        # NC6s_v3 has only 1 GPU
        "process_count_per_instance": 1,
    },
)
job.resources = ResourceConfiguration(
    instance_type="Standard_NC6s_v3", instance_count=2
)  # Serverless compute resources

Pytorch-Beispiel

DeepSpeed

Azure Machine Learning unterstützt DeepSpeed als wichtige Komponente, um verteilte Aufträge mit nahezu linearer Skalierbarkeit in Bezug auf Folgendes auszuführen:

  • Erhöhung der Modellgröße
  • Erhöhung der GPU-Anzahl

DeepSpeed kann entweder mithilfe der Pytorch-Verteilung oder mithilfe von MPI für die Ausführung des verteilten Trainings aktiviert werden. Azure Machine Learning unterstützt den DeepSpeed-Launcher, um verteiltes Training und automatische Optimierung zu starten und eine optimale ds-Konfiguration zu erzielen.

Sie können eine kuratierte Umgebung für eine sofort einsatzbereite Umgebung mit den neuesten Technologien wie DeepSpeed, ORT, MSSCCL und Pytorch für Ihre DeepSpeed-Trainingsaufträge verwenden.

DeepSpeed-Beispiel

  • Beispiele für DeepSpeed-Training und automatische Optimierung finden Sie in diesen Ordnern.

TensorFlow

Wenn Sie die native verteilte TensorFlow-Bibliothek in Ihrem Trainingscode verwenden, z. B. die tf.distribute.Strategy-API von TensorFlow 2.x, können Sie den verteilten Auftrag über Azure Machine Learning mithilfe von distribution-Parametern oder dem TensorFlowDistribution-Objekt starten.

# create the command
job = command(
    code="./src",  # local path where the code is stored
    command="python main.py --epochs ${{inputs.epochs}} --model-dir ${{inputs.model_dir}}",
    inputs={"epochs": 1, "model_dir": "outputs/keras-model"},
    environment="AzureML-tensorflow-2.4-ubuntu18.04-py37-cuda11-gpu@latest",
    compute="cpu-cluster",
    instance_count=2,
    # distribution = {"type": "mpi", "process_count_per_instance": 1},
    # distribution={
    #     "type": "tensorflow",
    #     "parameter_server_count": 1,  # for legacy TensorFlow 1.x
    #     "worker_count": 2,
    #     "added_property": 7,
    # },
    # distribution = {
    #        "type": "pytorch",
    #        "process_count_per_instance": 4,
    #        "additional_prop": {"nested_prop": 3},
    #    },
    display_name="tensorflow-mnist-distributed-example"
    # experiment_name: tensorflow-mnist-distributed-example
    # description: Train a basic neural network with TensorFlow on the MNIST dataset, distributed via TensorFlow.
)

# can also set the distribution in a separate step and using the typed objects instead of a dict
job.distribution = TensorFlowDistribution(worker_count=2)

Wenn Ihr Trainingsskript die Parameterserverstrategie für verteiltes Training wie für Legacy-TensorFlow 1.x verwendet, müssen Sie auch die Anzahl der Parameterserver angeben, die im Auftrag verwendet werden sollen, und zwar innerhalb des distribution-Parameters von command. Im obigen Beispiel: "parameter_server_count" : 1 und "worker_count": 2

TF_CONFIG

In TensorFlow ist die TF_CONFIG-Umgebungsvariable für das Training auf mehreren Computern erforderlich. Für TensorFlow-Aufträge konfiguriert und bestimmt Azure Machine Learning die TF_CONFIG-Variable individuell für jeden Worker, bevor das Trainingsskript ausgeführt wird.

Sie können auf TF_CONFIG aus Ihrem Trainingsskript zugreifen, falls dies erforderlich ist: os.environ['TF_CONFIG'].

Beispiel für das Festlegen von TF_CONFIG auf einem Chief-Workerknoten:

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

TensorFlow-Beispiel

Beschleunigen des verteilten GPU-Trainings mit InfiniBand

Je mehr VMs ein Modell trainieren, desto weniger Zeit sollte zum Trainieren dieses Modells benötigt werden. Die Zeit sollte im Idealfall linear und proportional zur Anzahl der Trainings-VMs abnehmen. Wenn das Trainieren eines Modells auf einer VM beispielsweise 100 Sekunden dauert, sollte das Training desselben Modells auf zwei VMs idealerweise 50 Sekunden dauern. Das Trainieren des Modells auf vier VMs sollte 25 Sekunden dauern usw.

InfiniBand kann ein wichtiger Faktor zur Erreichung dieser linearen Skalierung sein. InfiniBand ermöglicht eine latenzarme GPU-zu-GPU-Kommunikation über mehrere Knoten in einem Cluster hinweg. Für den Betrieb von InfiniBand ist spezielle Hardware erforderlich. Bestimmte Azure-VM-Serien, insbesondere die NC-, ND- und H-Serie, verfügen jetzt über VMs mit RDMA-Unterstützung mit SR-IOV- und InfiniBand-Unterstützung. Diese VMs kommunizieren über das InfiniBand-Netzwerk mit geringer Latenz und hoher Bandbreite, was deutlich leistungsfähiger ist als ethernetbasierte Konnektivität. SR-IOV für InfiniBand ermöglicht nahezu Bare-Metal-Leistung für jede MPI-Bibliothek (MPI wird von vielen verteilten Trainingsframeworks und Tools verwendet, einschließlich der NCCL-Software von NVIDIA.) Diese SKUs sollen die Anforderungen rechenintensiver, GPU-gestützter Machine Learning-Workloads erfüllen. Weitere Informationen finden Sie unter Beschleunigen des verteilten Trainings in Azure Machine Learning mit SR-IOV.

In der Regel enthalten VM-SKUs mit einem „r“ im Namen die erforderliche InfiniBand-Hardware, und solche ohne „r“ in der Regel nicht. (Das „r“ ist ein Verweis auf das Akronym „RDMA“, das wiederum für Remote Direct Memory Access steht.) Die VM-SKU Standard_NC24rs_v3 ist beispielsweise InfiniBand-fähig, die SKU Standard_NC24s_v3 jedoch nicht. Abgesehen von den InfiniBand-Funktionen sind die Spezifikationen zwischen diesen beiden SKUs größtenteils identisch. Beide verfügen über 24 Kerne, 448 GB RAM, 4 GPUs derselben SKU usw. Erfahren Sie mehr über RDMA- und InfiniBand-fähige Computer-SKUs.

Warnung

Die Computer-SKU Standard_NC24r der älteren Generation ist RDMA-fähig, enthält jedoch keine SR-IOV-Hardware, die für InfiniBand erforderlich ist.

Wenn Sie einen AmlCompute-Cluster mit einer dieser Größen mit RDMA- und InfiniBand-Unterstützung erstellen, wird das Betriebssystemimage mit dem Mellanox OFED-Treiber bereitgestellt, der erforderlich ist, um InfiniBand vorinstalliert und vorkonfiguriert zu aktivieren.

Nächste Schritte