Entraînement distribué

Cette page explique comment exécuter des tâches d'entraînement distribué dans Vertex AI.

Exigences relatives au code

Utiliser un framework de ML compatible avec l'entraînement distribué. Dans votre code d'entraînement, vous pouvez utiliser les variables d'environnement CLUSTER_SPEC ou TF_CONFIG pour référencer des parties spécifiques de votre cluster d'entraînement.

Structure du cluster d'entraînement

Si vous exécutez une tâche d'entraînement distribué avec Vertex AI, vous spécifiez plusieurs machines (nœuds) dans un cluster d'entraînement. Le service d'entraînement alloue les ressources pour les types de machines que vous spécifiez. La tâche en cours d'exécution sur un nœud spécifique s'appelle une instance dupliquée. Un groupe d'instances dupliquées avec la même configuration est appelé pool de nœuds de calcul.

Chaque instance dupliquée du cluster d'entraînement se voit attribuer un rôle ou une tâche unique dans l'entraînement distribué. Exemple :

  • Instance dupliquée principale : une instance dupliquée est désignée comme instance dupliquée principale. Cette tâche gère les autres et consigne l'état de la tâche dans son ensemble.

  • Nœud(s) de calcul : une ou plusieurs instances dupliquées peuvent être désignées comme nœuds de calcul. Ces instances dupliquées exécutent ce qui leur incombe conformément à la configuration de la tâche.

  • Serveur(s) de paramètres : si votre framework de ML est compatible, une ou plusieurs instances dupliquées peuvent être désignées comme serveurs de paramètres. Ces instances dupliquées stockent les paramètres du modèle et coordonnent l'état du modèle partagé entre les nœuds de calcul.

  • Évaluateurs : si votre framework de ML est compatible, une ou plusieurs instances dupliquées peuvent être désignées comme évaluateurs. Ces instances dupliquées peuvent être utilisées pour évaluer votre modèle. Si vous utilisez TensorFlow, notez que celui-ci s'attend généralement à ce que vous n'utilisiez pas plus d'un évaluateur.

Configurer une tâche d'entraînement distribué

Vous pouvez configurer n'importe quelle tâche d'entraînement personnalisé en tant que tâche d'entraînement distribué en définissant plusieurs pools de nœuds de calcul. Vous pouvez également exécuter un entraînement distribué au sein d'un pipeline d'entraînement ou d'une tâche de réglage d'hyperparamètres.

Pour configurer une tâche d'entraînement distribué, définissez votre liste de pools de nœuds de calcul (workerPoolSpecs[]) en spécifiant un ID de machine (WorkerPoolSpec) pour chaque type de tâche :

Position dans workerPoolSpecs[] Tâche effectuée dans le cluster
Premier (workerPoolSpecs[0]) Principal, chef, programmeur ou "maître"
Seconde (workerPoolSpecs[1]) Instances secondaires, instances dupliquées, nœuds de calcul
Troisième (workerPoolSpecs[2]) Serveurs de paramètres, Reduction Server
Quatrième (workerPoolSpecs[3]) Évaluateurs

Vous devez spécifier une instance dupliquée principale, qui coordonne le travail effectué par toutes les autres instances dupliquées. Utilisez la spécification de premier pool de nœuds de calcul uniquement pour votre instance dupliquée principale, puis définissez son replicaCount sur 1 :

{
  "workerPoolSpecs": [
     // `WorkerPoolSpec` for worker pool 0, primary replica, required
     {
       "machineSpec": {...},
       "replicaCount": 1,
       "diskSpec": {...},
       ...
     },
     // `WorkerPoolSpec` for worker pool 1, optional
     {},
     // `WorkerPoolSpec` for worker pool 2, optional
     {},
     // `WorkerPoolSpec` for worker pool 3, optional
     {}
   ]
   ...
}

Spécifier des pools de nœuds de calcul supplémentaires

Selon votre framework de ML, vous pouvez spécifier des pools de nœuds de calcul supplémentaires à d'autres fins. Par exemple, si vous utilisez TensorFlow, vous pouvez spécifier des pools de nœuds de calcul pour configurer des instances dupliquées de nœuds de calcul, des instances dupliquées de serveurs de paramètres et des instances dupliquées d'évaluateurs.

L'ordre des pools de nœuds de calcul que vous spécifiez dans la liste workerPoolSpecs[] détermine le type de pool de nœuds de calcul. Définissez des valeurs vides pour les pools de nœuds de calcul que vous ne souhaitez pas utiliser afin de pouvoir les ignorer dans la liste workerPoolSpecs[] de manière à spécifier les pools de nœuds de calcul que vous souhaitez utiliser. Exemple :

Si vous souhaitez spécifier une tâche qui ne comporte qu'une instance dupliquée principale et un pool de nœuds de calcul de serveur de paramètres, vous devez définir une valeur vide pour le pool de nœuds de calcul 1 :

{
  "workerPoolSpecs": [
     // `WorkerPoolSpec` for worker pool 0, required
     {
       "machineSpec": {...},
       "replicaCount": 1,
       "diskSpec": {...},
       ...
     },
     // `WorkerPoolSpec` for worker pool 1, optional
     {},
     // `WorkerPoolSpec` for worker pool 2, optional
     {
       "machineSpec": {...},
       "replicaCount": 1,
       "diskSpec": {...},
       ...
     },
     // `WorkerPoolSpec` for worker pool 3, optional
     {}
   ]
   ...
}

Réduire le temps d'entraînement avec Reduction Server

Lorsque vous entraînez un modèle de ML volumineux à l'aide de plusieurs nœuds, la communication des gradients entre les nœuds peut augmenter considérablement la latence. Reduction Server est un algorithme entièrement réduit qui peut augmenter le débit et réduire la latence de l'entraînement distribué. Vertex AI rend Reduction Server disponible dans une image de conteneur Docker que vous pouvez utiliser pour l'un de vos pools de nœuds de calcul pendant l'entraînement distribué.

Pour en savoir plus sur le fonctionnement de Reduction Server, consultez la section Entraînement GPU distribué plus rapide avec Reduction Server sur Red AI.

Prérequis

Vous pouvez utiliser Reduction Server si vous remplissez les conditions suivantes :

  • Vous effectuez un entraînement distribué avec des nœuds de calcul GPU.

  • Votre code d'entraînement utilise TensorFlow ou PyTorch, et il est configuré pour un entraînement avec parallélisme des données sur plusieurs hôtes avec des GPU à l'aide de l'algorithme NCCL entièrement réduit. (Vous pouvez également utiliser d'autres frameworks de ML utilisant NCCL.)

  • Les conteneurs s'exécutant sur votre nœud principal (workerPoolSpecs[0]) et les nœuds de calcul (workerPoolSpecs[1]) sont compatibles avec Reduction Server. Plus précisément, chaque conteneur est l'un des éléments suivants :

    • Un conteneur d'entraînement TensorFlow prédéfini, version 2.3 ou ultérieure.

    • Un conteneur d'entraînement Pytorch prédéfini, version 1.4 ou ultérieure.

    • Un conteneur personnalisé avec la version NCCL 2.7 ou ultérieure et le package google-reduction-server installé. Vous pouvez installer ce package sur une image de conteneur personnalisé en ajoutant la ligne suivante à votre fichier Dockerfile :

      RUN echo "deb https://packages.cloud.google.com/apt google-fast-socket main" | tee /etc/apt/sources.list.d/google-fast-socket.list && \
          curl -s -L https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \
          apt update && apt install -y google-reduction-server
      

Entraîner avec Reduction Server

Pour utiliser Reduction Server, procédez comme suit lorsque vous créez une ressource d'entraînement personnalisée :

  1. Spécifiez l'un des URI suivants dans le champ containerSpec.imageUri du troisième pool de nœuds de calcul (workerPoolSpecs[2]) :

    • us-docker.pkg.dev/vertex-ai-restricted/training/reductionserver:latest
    • europe-docker.pkg.dev/vertex-ai-restricted/training/reductionserver:latest
    • asia-docker.pkg.dev/vertex-ai-restricted/training/reductionserver:latest

    Le choix de l'emplacement multirégional le plus proche de l'emplacement où vous effectuez l'entraînement personnalisé peut réduire la latence.

  2. Lorsque vous sélectionnez le type de machine et le nombre de nœuds pour le troisième pool de nœuds de calcul, assurez-vous que la bande passante réseau totale du troisième pool de nœuds correspond ou dépasse la bande passante réseau totale du premier et du deuxième pools de nœuds de calcul.

    Pour en savoir plus sur la bande passante maximale disponible de chaque nœud dans le deuxième pool de nœuds de calcul, consultez la section Bande passante réseau et GPU.

    Vous n'utilisez pas de GPU pour les nœuds de Reduction Server. Pour en savoir plus sur la bande passante maximale disponible de chaque nœud du troisième pool de nœuds de calcul, consultez les colonnes "Bande passante de sortie maximale (Gbit/s)" de la section Famille des machines à usage général.

    Par exemple, si vous configurez les premier et deuxième pools de nœuds de calcul pour qu'ils utilisent 5 nœuds n1-highmem-96, chacun avec 8 GPU NVIDIA_TESLA_V100, chaque nœud dispose d'une bande passante maximale de 100 Gbit/s, pour une bande passante totale de 500 Gbit/s. Pour correspondre à cette bande passante dans le troisième pool de nœuds de calcul, vous pouvez utiliser 16 nœuds n1-highcpu-16, chacun avec une bande passante maximale de 32 Gbit/s, pour une bande passante totale de 512 Gbit/s.

    Nous vous recommandons d'utiliser le type de machine n1-highcpu-16 pour les nœuds de Reduction Server, car il offre une bande passante relativement élevée pour ses ressources.

La commande suivante fournit un exemple de création d'une ressource CustomJob utilisant Reduction Server :

gcloud ai custom-jobs create \
  --region=LOCATION \
  --display-name=JOB_NAME \
  --worker-pool-spec=machine-type=n1-highmem-96,replica-count=1,accelerator-type=NVIDIA_TESLA_V100,accelerator-count=8,container-image-uri=CUSTOM_CONTAINER_IMAGE_URI \
  --worker-pool-spec=machine-type=n1-highmem-96,replica-count=4,accelerator-type=NVIDIA_TESLA_V100,accelerator-count=8,container-image-uri=CUSTOM_CONTAINER_IMAGE_URI \
  --worker-pool-spec=machine-type=n1-highcpu-16,replica-count=16,container-image-uri=us-docker.pkg.dev/vertex-ai-restricted/training/reductionserver:latest

Pour plus de contexte, consultez le

guide de création d'une ressource CustomJob.

Bonnes pratiques pour l'entraînement avec Reduction Server

Type et nombre de machines

Lors de l'entraînement avec Reduction Server, chaque nœud de calcul doit se connecter à tous les hôtes de réducteur. Pour réduire le nombre de connexions sur l'hôte de calcul, utilisez un type de machine avec la bande passante réseau la plus élevée pour votre hôte de réducteur.

Un bon choix pour les hôtes de réducteur est une VM N1/N2 à usage général avec au moins 16 processeurs virtuels fournissant une bande passante de sortie de 32 Gbit/s, telle que n1-highcpu-16 et n2-highcpu-16. La bande passante de VM de niveau 1 pour les VM N1/N2 augmente la bande passante de sortie maximale, allant de 50 Gbit/s à 100 Gbit/s, ce qui en fait un bon choix pour les nœuds de VM de réducteur.

La bande passante de sortie totale des nœuds de calcul et de réducteur doit être identique. Par exemple, si vous utilisez 8 VM a2-megagpu-8g en tant que nœuds de calcul, vous devez utiliser au moins 25 VM n1-highcpu-16 en tant que nœuds de réducteur.

`(8 worker VMs * 100 Gbps) / 32 Gbps egress = 25 reducer VMs`.

Regrouper les petits messages

Reduction Server fonctionne mieux si les messages à agréger sont suffisamment volumineux. La plupart des frameworks de ML fournissent déjà des techniques selon une terminologie différente pour regrouper des petits Tensors de gradient avant d'effectuer une réduction globale (all-reduce).

Horovod

Hoovod est compatible avec Tensor Fusion pour regrouper des petits Tensors pour effectuer une réduction globale (all-reduce). Les Tensors sont remplis dans un tampon de fusion jusqu'à ce que le tampon soit entièrement rempli et que l'opération de réduction globale sur le tampon s'exécute. Vous pouvez ajuster la taille du tampon de fusion en définissant la variable d'environnement HOROVOD_FUSION_THRESHOLD.

La valeur recommandée pour la variable d'environnement HOROVOD_FUSION_THRESHOLD est d'au moins 128 Mo. Dans ce cas, définissez la variable d'environnement HOROVOD_FUSION_THRESHOLD sur 134217728 (128 * 1024 * 1024).

PyTorch

PyTorch DistributedDataParallel accepte les messages par lot en tant que "binning de gradient". Définissez le paramètre bucket_cap_mb dans le constructeur DistributedDataParallel pour contrôler la taille de vos buckets par lots. La taille par défaut est de 25 Mo.

BONNE PRATIQUE : la valeur recommandée de bucket_cap_mb est de 64 (64 Mo).

Variables d'environnement pour votre cluster

Vertex AI insère une variable d'environnement, CLUSTER_SPEC, sur chaque instance dupliquée afin de décrire la façon dont le cluster global est configuré. À l'instar de la variable d'environnement TF_CONFIG de TensorFlow, CLUSTER_SPEC décrit chaque instance dupliquée du cluster, y compris son index et son rôle (instance dupliquée principale, nœud de calcul, serveur de paramètres ou évaluateur).

Lorsque vous procédez à un entraînement distribué avec TensorFlow, TF_CONFIG est analysé pour créer tf.train.ClusterSpec. De même, lorsque vous réalisez un entraînement distribué avec d'autres frameworks de ML, vous devez analyser CLUSTER_SPEC pour insérer les variables d'environnement ou les paramètres requis par le framework.

Format de CLUSTER_SPEC

La variable d'environnement CLUSTER_SPEC est une chaîne JSON au format suivant :

Clé Description
"cluster"

Description du cluster pour votre conteneur personnalisé. Comme avec TF_CONFIG, cet objet est au format de spécification de cluster TensorFlow et peut être transmis au constructeur de tf.train.ClusterSpec.

La description du cluster contient une liste de noms d'instances dupliquées pour chaque pool de nœuds de calcul que vous spécifiez.

"workerpool0" Toutes les tâches d'entraînement distribué ont une instance dupliquée principale dans le premier pool de nœuds de calcul.
"workerpool1" Ce pool de nœuds de calcul contient des instances dupliquées de nœuds de calcul, si vous les avez spécifiées lors de la création de votre tâche.
"workerpool2" Ce pool de nœuds de calcul contient des serveurs de paramètres, si vous les avez spécifiés lors de la création de votre tâche.
"workerpool3" Ce pool de nœuds de calcul contient des évaluateurs, si vous les avez spécifiés lors de la création de votre tâche.
"environment" Correspond à la chaîne cloud.
"task" Décrit la tâche du nœud spécifique sur lequel s'exécute votre code. Vous pouvez exploiter ces informations pour rédiger du code pour des nœuds de calcul spécifiques dans une tâche distribuée. Cette entrée est un dictionnaire contenant les clés suivantes :
"type" Type de pool de nœuds de calcul dans lequel cette tâche s'exécute. Par exemple, "workerpool0" fait référence à l'instance dupliquée principale.
"index"

Index basé sur zéro de la tâche. Par exemple, si votre tâche d'entraînement inclut deux nœuds de calcul, cette valeur est définie sur 0 sur l'un d'entre eux et sur 1 sur l'autre.

"trial" Identifiant de l'essai de réglage des hyperparamètres en cours d'exécution. Lorsque vous configurez le réglage des hyperparamètres pour votre tâche, vous définissez un nombre d'essais d'entraînement. Cette valeur vous permet de différencier, au sein de votre code, les essais qui s'exécutent. L'identifiant est une valeur de chaîne contenant le numéro d'essai qui démarre à partir de 1.
job

Données de saisie d'entraînement (CustomJobSpec) que vous avez fournies pour créer la tâche d'entraînement actuelle, représentées sous forme de dictionnaire.

Exemple CLUSTER_SPEC

Voici un exemple de valeur :


{
   "cluster":{
      "workerpool0":[
         "cmle-training-workerpool0-ab-0:2222"
      ],
      "workerpool1":[
         "cmle-training-workerpool1-ab-0:2222",
         "cmle-training-workerpool1-ab-1:2222"
      ],
      "workerpool2":[
         "cmle-training-workerpool2-ab-0:2222",
         "cmle-training-workerpool2-ab-1:2222"
      ],
      "workerpool3":[
         "cmle-training-workerpool3-ab-0:2222",
         "cmle-training-workerpool3-ab-1:2222",
         "cmle-training-workerpool3-ab-2:2222"
      ]
   },
   "environment":"cloud",
   "task":{
      "type":"workerpool0",
      "index":0,
      "trial":"TRIAL_ID"
   },
   "job": {
      ...
   }
}

Format de TF_CONFIG

En plus de CLUSTER_SPEC, Vertex AI définit la variable d'environnement TF_CONFIG sur chaque instance dupliquée de toutes les tâches d'entraînement distribué. Vertex AI ne définit pas la valeur TF_CONFIG pour les tâches d'entraînement à instance dupliquée unique.

CLUSTER_SPEC et TF_CONFIG partagent certaines valeurs mais ont des formats différents. Les deux variables d'environnement incluent des champs supplémentaires qui ne sont pas nécessairement requis par TensorFlow.

L'entraînement distribué avec TensorFlow fonctionne de la même manière avec les conteneurs personnalisés qu'avec un conteneur prédéfini.

La variable d'environnement TF_CONFIG est une chaîne JSON au format suivant :

Champs TF_CONFIG
cluster

Description du cluster TensorFlow. Dictionnaire associant un ou plusieurs noms de tâches (chief, worker, ps ou master) à des listes d'adresses réseau où ces tâches sont exécutées. Pour une tâche d'entraînement donnée, ce dictionnaire est identique sur toutes les VM.

Il s'agit d'un premier argument valide pour le constructeur tf.train.ClusterSpec. Notez que ce dictionnaire ne contient jamais l'élément evaluator en tant que clé, car les évaluateurs ne sont pas considérés comme faisant partie du cluster d'entraînement, même si vous les utilisez pour votre tâche.

task

Description de la tâche de la VM sur laquelle cette variable d'environnement est définie. Pour une tâche d'entraînement donnée, ce dictionnaire est identique sur toutes les VM. Vous pouvez vous servir de ces informations pour personnaliser le code exécuté sur chaque VM dans une tâche d'entraînement distribuée. Vous pouvez également l'utiliser pour modifier le comportement de votre code d'entraînement pour différents essais d'une tâche de réglage d'hyperparamètres.

Ce dictionnaire inclut les paires clé-valeur suivantes :

Champs task
type

Type de tâche effectuée par cette VM. Cette valeur est définie sur worker pour les nœuds de calcul, sur ps pour les serveurs de paramètres et sur evaluator pour les évaluateurs. Pour le nœud de calcul maître de votre tâche, la valeur est définie sur chief ou master. Découvrez la différence entre chief et master dans une autre section de ce document.

index

Index basé sur zéro de la tâche. Par exemple, si votre tâche d'entraînement inclut deux nœuds de calcul, cette valeur est définie sur 0 sur l'un d'entre eux et sur 1 sur l'autre.

trial

ID de l'essai de réglage d'hyperparamètres en cours d'exécution sur cette VM. Ce champ n'est défini que si la tâche d'entraînement en cours est une tâche de réglage d'hyperparamètres.

Pour les tâches de réglage d'hyperparamètres, Vertex AI exécute votre code d'entraînement de manière répétée dans de nombreux essais avec des hyperparamètres différents à chaque fois. Ce champ contient le numéro d'essai en cours et commence à 1 pour le premier essai.

cloud

ID utilisé en interne par Vertex AI. Vous pouvez ne pas tenir compte de ce champ.

job

Données de saisie d'entraînement (CustomJobSpec) que vous avez fournies pour créer la tâche d'entraînement actuelle, représentées sous forme de dictionnaire.

environment

Correspond à la chaîne cloud.

Exemple TF_CONFIG

L'exemple de code suivant imprime la variable d'environnement TF_CONFIG dans vos journaux d'entraînement :

import json
import os

tf_config_str = os.environ.get('TF_CONFIG')
tf_config_dict  = json.loads(tf_config_str)

# Convert back to string just for pretty printing
print(json.dumps(tf_config_dict, indent=2))

Dans une tâche de réglage d'hyperparamètres exécutée dans la version d'exécution 2.1 ou ultérieure et utilisant un nœud de calcul maître, deux nœuds de calcul et un serveur de paramètres, ce code génère le journal suivant pour l'un des nœuds de calcul lors du premier essai de réglage d'hyperparamètres. L'exemple de résultat masque le champ job pour plus de concision et remplace certains ID par des valeurs génériques.

{
  "cluster": {
    "chief": [
      "training-workerpool0-[ID_STRING_1]-0:2222"
    ],
    "ps": [
      "training-workerpool2-[ID_STRING_1]-0:2222"
    ],
    "worker": [
      "training-workerpool1-[ID_STRING_1]-0:2222",
      "training-workerpool1-[ID_STRING_1]-1:2222"
    ]
  },
  "environment": "cloud",
  "job": {
    ...
  },
  "task": {
    "cloud": "[ID_STRING_2]",
    "index": 0,
    "trial": "1",
    "type": "worker"
  }
}

Dans quel contexte utiliser TF_CONFIG ?

TF_CONFIG n'est défini que pour les tâches d'entraînement distribué.

Vous n'aurez probablement pas besoin d'interagir directement avec la variable d'environnement TF_CONFIG dans votre code d'entraînement. N'accédez à la variable d'environnement TF_CONFIG que si les stratégies de distribution de TensorFlow et le workflow standard de réglage d'hyperparamètres de Vertex AI décrits dans les sections suivantes ne fonctionnent pas.

Entraînement distribué

Vertex AI définit la variable d'environnement TF_CONFIG afin d'étendre les spécifications requises par TensorFlow pour un entraînement distribué.

Pour effectuer un entraînement distribué avec TensorFlow, utilisez l'API tf.distribute.Strategy. En particulier, nous vous recommandons d'utiliser l'API Keras avec MultiWorkerMirroredStrategy ou, si vous spécifiez des serveurs de paramètres pour votre tâche, avec ParameterServerStrategy. Toutefois, notez que TensorFlow ne fournit actuellement qu'une compatibilité expérimentale pour ces stratégies.

Ces stratégies de distribution utilisent la variable d'environnement TF_CONFIG pour attribuer des rôles à chaque VM dans votre tâche d'entraînement et pour faciliter la communication entre les VM. Vous n'avez pas besoin d'accéder à la variable d'environnement TF_CONFIG directement dans votre code d'entraînement, car TensorFlow s'en charge pour vous.

N'analysez la variable d'environnement TF_CONFIG directement que si vous souhaitez personnaliser le comportement des différentes VM exécutant votre tâche d'entraînement.

Réglages d'hyperparamètres

Lorsque vous exécutez une tâche de réglage d'hyperparamètres, Vertex AI fournit des arguments différents à votre code d'entraînement pour chaque essai. Votre code d'entraînement ne doit pas nécessairement savoir quel essai est en cours. En outre, vous pouvez surveiller la progression des tâches de réglage d'hyperparamètres dans la console.

Si nécessaire, votre code peut lire le numéro d'essai en cours dans le champ trial du champ task de la variable d'environnement TF_CONFIG.

Étape suivante