Als Markdown anzeigen

Multi-GPU-Training

Multi-GPU-Training in LibreYOLO verwendet PyTorch DistributedDataParallel: Ein Prozess pro GPU hält jeweils eine vollständige Modellreplik und einen Teil jedes Batches. Die Gradienten werden bei jedem Schritt über alle Ränge gemittelt.

Ausführung auf zwei GPUs

Übergib eine Geräteliste. Alles andere bleibt unverändert.

Python
from libreyolo import LibreYOLO # Dieser Guard bleibt unterstützt; normale Skripte funktionieren auch ohne ihn.# Für Callbacks oder Logger mit Standard-Pickle-Fallback muss er bleiben.if __name__ == "__main__":    model = LibreYOLO("LibreYOLO9s.pt")    model.train(        data="my-dataset.yaml",        epochs=100,        batch=32,     # globaler Batch: 16 Bilder je GPU bei zwei GPUs        device="0,1",    )
CLI
libreyolo train model=LibreYOLO9s.pt data=my-dataset.yaml \  epochs=100 batch=32 device=0,1

Bei mehr als einem Gerät und ohne torchrun-Umgebung speichert train() des Modells die Gewichte in einer temporären Datei, bestimmt bei Bedarf Autobatch und startet vom Koordinator verwaltete Worker-Prozesse, einen pro GPU. Jeder Worker importiert die Modellklasse erneut, baut sie aus den gespeicherten Gewichten auf und verwendet den normalen Einzelgerätepfad, weil innerhalb eines gestarteten Workers die torchrun-Umgebungsvariablen gesetzt sind. Nach dem Lauf wird der beste Checkpoint von Rank 0 in die Modellinstanz des Aufrufers geladen.

device akzeptiert "0,1", [0, 1], 0, "cuda:0", "cpu", "mps" und "auto". Nur eine Liste mit mehr als einem CUDA-Index startet mehrere Prozesse.

libreyolo train device=0,1 oder device=[0,1] baut das Modell auf der ersten aufgeführten GPU auf und übergibt die Liste an train(), das die Worker auf dieselbe Weise startet.

Automatischer Start und Main-Guard

model.train(device=[0, 1]) und device="0,1" verwenden vom Koordinator verwaltete Ranks, ohne den Top-Level-Code eines gewöhnlichen ungeschützten Skripts erneut auszuführen. Skripte mit Main-Guard und explizites torchrun bleiben unterstützt. Koordinatorjobs verwenden cloudpickle; Callback- oder Logger-Objekte, die den Standard-pickle-Fallback benötigen, erfordern weiterhin einen Guard if __name__ == "__main__":.

batch als globale Batch-Größe

batch ist die Anzahl der Bilder pro Optimizer-Schritt über alle GPUs hinweg. Der Dataloader jedes Rangs wird mit batch // world_size und einem DistributedSampler erstellt. batch=32 auf zwei GPUs bedeutet daher 16 Bilder pro GPU und nicht 32.

Eine Batch-Größe, die nicht ohne Rest durch die Weltgröße teilbar ist, löst einen Fehler aus, statt unbemerkt mit einer anderen Größe zu trainieren:

batch=6 is the global batch and must be divisible by world_size=4: each ranktrains at batch // world_size, so this value would silently train at adifferent global batch than requested. Use batch=4 or batch=8.

DDP mittelt die Gradienten selbst. Deshalb wird der Loss ohne Skalierung übergeben. Eine zusätzliche Multiplikation mit der Weltgröße würde die effektive Lernrate ungefähr um die Anzahl der GPUs erhöhen.

Autobatch unter DDP

batch=-1 funktioniert und gibt eine durch die Weltgröße teilbare globale Batch-Größe zurück.

Python
from libreyolo import LibreYOLO if __name__ == "__main__":    model = LibreYOLO("LibreYOLO9s.pt")    # Einmal auf GPU 0 testen und auf ein Vielfaches der Weltgröße skalieren.    model.train(data="my-dataset.yaml", batch=-1, device="0,1")

Beim Spawn-Pfad läuft die Messung im übergeordneten Prozess auf dem ersten Gerät, bevor ein Worker existiert. Jeder Worker erhält daher eine konkrete Ganzzahl und es ist keine Koordination zwischen Prozessen erforderlich. Unter torchrun misst Rang 0 und verteilt das Ergebnis als einzelnen Long-Tensor.

Die Messung ermittelt die Kapazität einer GPU und multipliziert sie mit der Weltgröße. Wenn nbs gesetzt ist, wird der globale Batch auf nbs begrenzt und auf ein Vielfaches der Weltgröße abgerundet. Beim Hinzufügen von GPUs sinkt daher die Anzahl der Akkumulationsschritte, statt dass der Batch pro GPU kleiner wird. Die Funktionsweise der Messung selbst wird unter Hyperparameter beschrieben.

SyncBatchNorm

Unter DDP sehen die BatchNorm-Schichten jedes Rangs nur dessen eigenen Teil. Bei batch // world_size kann dieser Teil so klein sein, dass die laufenden Statistiken das konvergierte Modell gegenüber einem Single-GPU-Lauf verschlechtern.

sync_bn=True wandelt jede BatchNorm in SyncBatchNorm um, sodass die Statistiken über den globalen Batch berechnet werden. Die Umwandlung erfolgt nur bei aktivem verteiltem Training. Ein Single-GPU-Lauf bleibt daher unabhängig vom Flag unverändert.

Für die BatchNorm-lastigen Convolutional-Familien ist die Option bereits standardmäßig aktiviert: YOLOX, YOLOv7, YOLOv9 und seine Varianten, YOLO-NAS, PicoDet, RTMDet und FOMO. Bei allen anderen Familien ist sie standardmäßig deaktiviert. Der Trainer warnt, wenn ein Modell BatchNorm enthält, sync_bn deaktiviert ist und der Batch pro Rang unter 16 liegt.

Python
from libreyolo import LibreYOLO if __name__ == "__main__":    model = LibreYOLO("LibreRTDETRr18.pt")    model.train(        data="my-dataset.yaml",        batch=32,        device="0,1",        sync_bn=True,    )

Für sync_bn gibt es kein CLI-Flag. Es ist ein Python-Argument.

Start mit torchrun

torchrun funktioniert ebenfalls und ist die richtige Wahl, wenn bereits ein Cluster-Scheduler den Prozessstart steuert. Schreibe das Skript für ein einzelnes Gerät und lasse torchrun die Rangumgebung festlegen.

train.py
from libreyolo import LibreYOLO if __name__ == "__main__":    model = LibreYOLO("LibreYOLO9s.pt")    model.train(data="my-dataset.yaml", epochs=100, batch=32)
Start
torchrun --nproc_per_node=2 train.py

Kombiniere die beiden Verfahren nicht. Wenn die torchrun-Umgebung vorhanden ist, startet device="0,1" keine Prozesse. Der Trainer verwendet cuda:LOCAL_RANK, während torchrun die Prozessanzahl steuert.

Verhalten der Ränge

Rang 0 steuert jeden Seiteneffekt. Er ermittelt das Ausgabeverzeichnis und verteilt den aufgelösten Namen, damit alle Ränge übereinstimmen. Außerdem schreibt er Checkpoints und Artefakte und löst Nutzer-Callbacks sowie Logger aus. Die anderen Ränge trainieren und tragen Gradienten bei.

Jeder Rang initialisiert seinen Dataloader und den Zufallszahlengenerator der Augmentierung anders. Die Werte werden aus dem konfigurierten seed abgeleitet, damit die Ränge keine identischen Augmentierungen ziehen.

Plattform und Backend

Das Backend wird automatisch gewählt: NCCL, wenn sowohl CUDA als auch NCCL verfügbar sind, andernfalls Gloo. NCCL ist unter Windows nicht enthalten. Windows-Läufe verwenden daher ohne weitere Konfiguration Gloo. Die Prozessgruppe wird mit einem Timeout von drei Stunden initialisiert.

Nicht unter DDP ausgeführte Funktionen

  • CUDA-Graph-Erfassung. cuda_graph=True protokolliert eine Zeile und trainiert im Eager-Modus. Siehe Trainingsperformance.
  • Der Trainings-Profiler. profile=True wird mit einer Warnung ignoriert.

Nicht jede Familie unterstützt den automatischen Spawn. 24 Familien tun dies und decken die trainierbaren Familien für Erkennung, Klassifikation, semantische Segmentierung und Restaurierung ab. Erhält eine Familie ohne diese Unterstützung mehrere GPUs, löst sie einen Fehler aus, der die Modell-API und den torchrun-Befehl nennt, statt unbemerkt nur auf einer GPU zu trainieren.

  • Unter Hyperparameter findest du Informationen zu batch, nbs und dem Fortsetzen eines Laufs.
  • Experiment-Logger beschreibt die Anforderung an die Pickle-Kompatibilität von Callbacks.
  • Cloud-GPUs beschreibt das Mieten eines Multi-GPU-Rechners.

Mit LibreYOLO v1.6.0 verifiziert.