# Fine-tuning multi-GPU avec Distributed Data Parallel (DDP)

Supposons que nous disposions de plusieurs GPU et que nous souhaitions affiner un modèle en utilisant tous ces GPU ! Pour ce faire, la stratégie la plus simple est d'utiliser Distributed Data Parallel (DDP), qui crée une copie du modèle sur chaque dispositif GPU, alimente chaque copie avec des échantillons distincts du jeu de données pendant l'entraînement et agrège leurs contributions aux mises à jour des poids à chaque étape de l'optimiseur.

Pourquoi voudrions-nous faire cela ? Eh bien, à mesure que nous ajoutons des GPU au processus d'entraînement, nous augmentons le nombre d'échantillons sur lesquels nos modèles s'entraînent par étape, ce qui rend chaque mise à jour de gradient plus stable et augmente considérablement notre débit d'entraînement à chaque GPU ajouté.

Voici un guide étape par étape sur la façon de procéder en utilisant l'interface en ligne de commande (CLI) d'Unsloth !

**Remarque :** Unsloth DDP fonctionnera avec n'importe lequel de vos scripts d'entraînement, pas seulement via notre CLI ! Plus de détails ci-dessous.

#### Installer Unsloth depuis les sources

Nous allons cloner Unsloth depuis GitHub et l'installer. Veuillez envisager d'utiliser un [environnement virtuel](https://docs.python.org/3/tutorial/venv.html); nous aimons utiliser `uv venv –python 3.12 && source .venv/bin/activate`, mais n'importe quel outil de création d'environnement virtuel conviendra.

```bash
git clone https://github.com/unslothai/unsloth.git
cd unsloth
pip install .
```

#### Choisir le modèle cible et le jeu de données pour l'affinage

Dans cette démonstration, nous allons affiner [Qwen/Qwen3-8B](https://huggingface.co/Qwen/Qwen3-8B) sur le [yahma/alpaca-cleaned](https://huggingface.co/datasets/yahma/alpaca-cleaned) jeu de données de chat. Il s'agit d'une charge de travail de Supervised Fine-Tuning (SFT) couramment utilisée lorsqu'on tente d'adapter un modèle de base à un style conversationnel souhaité, ou d'améliorer les performances du modèle sur une tâche en aval.

### Utilisez la CLI Unsloth !

Tout d'abord, jetons un coup d'œil au message d'aide intégré à la CLI (nous avons abrégé ici par “...” à divers endroits pour la concision) :

{% code expandable="true" %}

```bash
$ python unsloth-cli.py --help
usage : unsloth-cli.py [-h] [--model_name MODEL_NAME] [--max_seq_length MAX_SEQ_LENGTH] [--dtype DTYPE]
                      [--load_in_4bit] [--dataset DATASET] [--r R] [--lora_alpha LORA_ALPHA]
                      [--lora_dropout LORA_DROPOUT] [--bias BIAS]
                      [--use_gradient_checkpointing USE_GRADIENT_CHECKPOINTING]
…

🦥 Affinez votre LLM plus rapidement en utilisant unsloth !

options :
  -h, --help            afficher ce message d'aide et quitter

🤖 Options du modèle :
  --model_name MODEL_NAME
                        Nom du modèle à charger
  --max_seq_length MAX_SEQ_LENGTH
                        Longueur maximale de la séquence, la valeur par défaut est 2048. Nous gérons automatiquement le RoPE Scaling
                        en interne !
…

🧠 Options LoRA :
  Ces options sont utilisées pour configurer le modèle LoRA.

  --r R                 Rang pour le modèle LoRA, la valeur par défaut est 16. (valeurs courantes : 8, 16, 32, 64, 128)
  --lora_alpha LORA_ALPHA
                        Paramètre alpha de LoRA, la valeur par défaut est 16. (valeurs courantes : 8, 16, 32, 64, 128)
…

🎓 Options d'entraînement :
  --per_device_train_batch_size PER_DEVICE_TRAIN_BATCH_SIZE
                        Taille de lot par dispositif pendant l'entraînement, la valeur par défaut est 2.
  --per_device_eval_batch_size PER_DEVICE_EVAL_BATCH_SIZE
                        Taille de lot par dispositif pendant l'évaluation, la valeur par défaut est 4.
  --gradient_accumulation_steps GRADIENT_ACCUMULATION_STEPS
                        Nombre d'étapes d'accumulation de gradients, la valeur par défaut est 4.
…
```

{% endcode %}

Cela devrait vous donner une idée des options disponibles que vous pouvez passer à la CLI pour entraîner votre modèle !

Pour l'entraînement multi-GPU (DDP dans ce cas), nous utiliserons le [torchrun](https://docs.pytorch.org/docs/stable/elastic/run.html) lanceur, qui vous permet de lancer plusieurs processus d'entraînement distribués en environnements mono-nœud ou multi-nœuds. Dans notre cas, nous nous concentrerons sur le cas mono-nœud (c'est-à-dire, une machine) avec deux GPU H100.

Vérifions également l'état de nos GPU en utilisant l'outil en ligne de commande `nvidia-smi` :

{% code expandable="true" %}

```bash
$ nvidia-smi
Mon Nov 24 12:53:00 2025       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.95.05              Version du pilote : 580.95.05      Version CUDA : 13.0     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Nom                  Persistance-M | Bus-Id          Disp.A | ECC non corrigé volatile |
| Ventil.  Temp   Perf          Pwr:Usage/Cap |           Utilisation mémoire | GPU-Util  Mode calc. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA H100 80GB HBM3          On  |   00000000:04:00.0 Off |                    0 |
| N/A   32C    P0             69W /  700W |       0MiB /  81559MiB |      0%      Par défaut |
|                                         |                        |             Désactivé |
+-----------------------------------------+------------------------+----------------------+
|   1  NVIDIA H100 80GB HBM3          On  |   00000000:05:00.0 Off |                    0 |
| N/A   30C    P0             68W /  700W |       0MiB /  81559MiB |      0%      Par défaut |
|                                         |                        |             Désactivé |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processus :                                                                             |
|  GPU   GI   CI              PID   Type   Nom du processus                        Mémoire GPU |
|        ID   ID                                                               Utilisation  |
|=========================================================================================|
|  Aucun processus en cours d'exécution trouvé                                              |
+-----------------------------------------------------------------------------------------+
```

{% endcode %}

Super ! Nous avons deux GPU H100, comme prévu. Les deux affichent 0MiB d'utilisation mémoire car nous n'entraînons actuellement rien et aucun modèle n'est chargé en mémoire.

Pour démarrer votre exécution d'entraînement, lancez une commande comme la suivante :

{% code expandable="true" %}

```bash
# requis :
#   --model_name
#   --dataset
# optionnel ; expérimentez avec ceux-ci :
#   --learning_rate, --max_seq_length, --per_device_train_batch_size, --gradient_accumulation_steps, --max_steps
# pour sauvegarder le modèle à la fin de l'entraînement :
#   --save_model

torchrun --nproc_per_node=2 unsloth-cli.py \
  --model_name=Qwen/Qwen3-8B \
  --dataset=yahma/alpaca-cleaned \
  --learning_rate=2e-5 \
  --max_seq_length=2048 \
  --per_device_train_batch_size=1 \
  --gradient_accumulation_steps=4 \
  --max_steps=1000 \
  --save_model
```

{% endcode %}

Si vous avez plus de GPU, vous pouvez définir `--nproc_per_node` en conséquence pour les utiliser.

**Remarque :** Vous pouvez utiliser le `torchrun` lanceur avec n'importe lequel de vos scripts d'entraînement Unsloth, y compris les [scripts](https://github.com/unslothai/notebooks/tree/main/python_scripts) convertis depuis nos notebooks Colab gratuits, et DDP sera activé automatiquement lorsque vous vous entraînerez avec >1 GPU !

Regardons à nouveau `nvidia-smi` pendant que l'entraînement est en cours :

{% code expandable="true" %}

```bash
$ nvidia-smi
Mon Nov 24 12:58:42 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.95.05              Version du pilote : 580.95.05      Version CUDA : 13.0     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Nom                  Persistance-M | Bus-Id          Disp.A | ECC non corrigé volatile |
| Ventil.  Temp   Perf          Pwr:Usage/Cap |           Utilisation mémoire | GPU-Util  Mode calc. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA H100 80GB HBM3          On  |   00000000:04:00.0 Off |                    0 |
| N/A   38C    P0            193W /  700W |   18903MiB /  81559MiB |     25%      Par défaut |
|                                         |                        |             Désactivé |
+-----------------------------------------+------------------------+----------------------+
|   1  NVIDIA H100 80GB HBM3          On  |   00000000:05:00.0 Off |                    0 |
| N/A   37C    P0            199W /  700W |   18905MiB /  81559MiB |     28%      Par défaut |
|                                         |                        |             Désactivé |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processus :                                                                             |
|  GPU   GI   CI              PID   Type   Nom du processus                        Mémoire GPU |
|        ID   ID                                                               Utilisation  |
|=========================================================================================|
|    0   N/A  N/A            4935      C   ...und/unsloth/.venv/bin/python3      18256MiB |
|    0   N/A  N/A            4936      C   ...und/unsloth/.venv/bin/python3        630MiB |
|    1   N/A  N/A            4935      C   ...und/unsloth/.venv/bin/python3        630MiB |
|    1   N/A  N/A            4936      C   ...und/unsloth/.venv/bin/python3      18258MiB |
+-----------------------------------------------------------------------------------------+
```

{% endcode %}

Nous pouvons voir que les deux GPU utilisent maintenant \~19 Go de VRAM par GPU H100 !

En inspectant les journaux d'entraînement, nous constatons que nous pouvons entraîner à un rythme d'environ \~1,1 itérations/s. Cette vitesse d'entraînement est \~constante même lorsque nous ajoutons plus de GPU, donc notre débit d'entraînement augmente \~linéairement avec le nombre de GPU !

### Métriques d'entraînement

Nous avons effectué quelques courts affinages LoRA de rang 16 sur [unsloth/Llama-3.2-1B-Instruct](https://huggingface.co/unsloth/Llama-3.2-1B-Instruct) sur le [yahma/alpaca-cleaned](https://huggingface.co/datasets/yahma/alpaca-cleaned) jeu de données pour démontrer l'amélioration du débit d'entraînement lors de l'utilisation de l'entraînement DDP avec plusieurs GPU.

<figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FdySJnhNUzVD3gsWmPqHR%2Funknown.png?alt=media&#x26;token=9905cccb-04c8-45b1-bfb1-680823713319" alt="" width="375"><figcaption></figcaption></figure>

La figure ci-dessus compare la perte d'entraînement entre deux affinages LoRA Llama-3.2-1B-Instruct sur 500 étapes d'entraînement, avec un entraînement sur GPU unique (rose) vs. un entraînement DDP multi-GPU (bleu).

Remarquez que les courbes de perte correspondent en échelle et en tendance, mais sont par ailleurs *un peu* différentes, puisque *les processus d'entraînement multi-GPU traitent deux fois plus de données d'entraînement par étape*. Cela se traduit par une courbe d'entraînement légèrement différente avec moins de variabilité étape par étape.

<figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fz4XgknzMgljaFInMEzHc%2Funknown.png?alt=media&#x26;token=4e28e2b1-8bc8-4049-983d-e4f980f3f4cf" alt="" width="375"><figcaption></figcaption></figure>

La figure ci-dessus trace la progression de l'entraînement pour les mêmes deux affinages.

Remarquez que l'entraînement DDP multi-GPU parcourt une époque des données d'entraînement en deux fois moins d'étapes que l'entraînement sur GPU unique. C'est parce que chaque GPU peut traiter un lot distinct (de taille `per_device_train_batch_size`) par étape. Cependant, le temps par étape pour l'entraînement DDP est légèrement plus lent en raison de la communication distribuée pour les mises à jour des poids du modèle. À mesure que vous augmentez le nombre de GPU, le débit d'entraînement continuera d'augmenter \~linéairement (mais avec une petite pénalité croissante pour les communications distribuées).

Ces mêmes comportements de perte et de progression d'époque d'entraînement s'appliquent aux affinages QLoRA, dans lesquels nous avons chargé les modèles de base en précision 4 bits afin d'économiser de la mémoire GPU supplémentaire. Cela est particulièrement utile pour entraîner de grands modèles avec des quantités limitées de VRAM GPU :

<figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FUrCEgA7OBVhc8ICkMaP6%2Funknown.png?alt=media&#x26;token=0f5de3df-77df-4ee5-bf7a-68dead857c9a" alt="" width="375"><figcaption></figcaption></figure>

Comparaison des pertes d'entraînement entre deux affinages QLoRA Llama-3.2-1B-Instruct sur 500 étapes d'entraînement, avec un entraînement sur GPU unique (orange) vs. un entraînement DDP multi-GPU (violet).

<figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2F8cG6rjmjeznNfgWrYdnG%2Funknown.png?alt=media&#x26;token=d1c2c1fe-c117-49b5-8e9d-fdc01154cc01" alt="" width="375"><figcaption></figcaption></figure>

Comparaison de la progression de l'entraînement pour les mêmes deux affinages.
