Trenowanie na wielu GPU

Trenowanie na wielu GPU w LibreYOLO korzysta z PyTorch DistributedDataParallel: na każdy GPU przypada jeden proces z pełną repliką modelu i fragmentem każdego batcha, a gradienty są uśredniane między rangami w każdym kroku.

Uruchamianie na dwóch GPU

Przekaż listę urządzeń. Nic więcej się nie zmienia.

Python
from libreyolo import LibreYOLO # Ten blok pozostaje obsługiwany; zwykłe skrypty działają też bez niego.# Zachowaj go dla callbacków i loggerów wymagających standard-pickle.if __name__ == "__main__":    model = LibreYOLO("LibreYOLO9s.pt")    model.train(        data="my-dataset.yaml",        epochs=100,        batch=32,     # globalny batch: 16 obrazów na GPU przy dwóch GPU        device="0,1",    )
CLI
libreyolo train model=LibreYOLO9s.pt data=my-dataset.yaml \  epochs=100 batch=32 device=0,1

Przy więcej niż jednym urządzeniu i bez środowiska torchrun metoda train() modelu zapisuje wagi do pliku tymczasowego, ustala autobatch, jeśli go zażądano, i uruchamia procesy robocze zarządzane przez koordynator, po jednym na GPU. Każdy proces ponownie importuje klasę modelu, odtwarza ją z zapisanych wag i wykonuje zwykłą ścieżkę jednego urządzenia, ponieważ wewnątrz uruchomionego procesu zmienne środowiskowe torchrun są ustawione. Po zakończeniu najlepszy checkpoint procesu rank 0 jest wczytywany z powrotem do instancji modelu wywołującego.

device przyjmuje wartości "0,1", [0, 1], 0, "cuda:0", "cpu", "mps" i "auto". Tylko lista zawierająca więcej niż jeden indeks CUDA uruchamia procesy.

libreyolo train device=0,1 lub device=[0,1] buduje model na pierwszym wymienionym GPU i przekazuje listę do train(), który uruchamia procesy robocze w ten sam sposób.

Automatyczne uruchamianie i blok main

model.train(device=[0, 1]) i device="0,1" używają procesów rank zarządzanych przez koordynator, bez ponownego wykonywania kodu najwyższego poziomu zwykłego skryptu bez bloku ochronnego. Skrypty z blokiem ochronnym i jawne torchrun pozostają obsługiwane. Zadania koordynatora używają cloudpickle; obiekty callbacków lub loggerów wymagające zastępczej ścieżki standard-pickle nadal potrzebują bloku if __name__ == "__main__":.

batch jest globalnym batchem

batch to liczba obrazów przypadających na krok optymalizatora na wszystkich GPU. Dataloader każdej rangi jest tworzony z batch // world_size i DistributedSampler, więc batch=32 na dwóch GPU oznacza 16 obrazów na GPU, a nie 32.

Batch, którego rozmiar nie dzieli się bez reszty przez liczbę procesów, powoduje błąd, zamiast po cichu uruchomić trenowanie z innym rozmiarem:

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.

Gradienty są uśredniane przez samo DDP, dlatego funkcja straty jest przekazywana bez skalowania. Dodatkowe pomnożenie jej przez liczbę procesów zwiększyłoby efektywny współczynnik uczenia mniej więcej tyle razy, ile jest GPU.

Autobatch w DDP

batch=-1 działa i zwraca globalny batch podzielny przez liczbę procesów.

Python
from libreyolo import LibreYOLO if __name__ == "__main__":    model = LibreYOLO("LibreYOLO9s.pt")    # Pomiar wykonywany raz na GPU 0, wynik skalowany do wielokrotności liczby procesów.    model.train(data="my-dataset.yaml", batch=-1, device="0,1")

Na ścieżce automatycznego uruchamiania pomiar odbywa się w procesie nadrzędnym na pierwszym urządzeniu, zanim powstanie jakikolwiek proces roboczy. Dzięki temu każdy proces otrzymuje konkretną liczbę całkowitą i nie jest potrzebna koordynacja między procesami. W środowisku torchrun ranga 0 wykonuje pomiar i rozsyła wynik jako pojedynczy tensor typu long.

Pomiar określa pojemność jednego GPU i mnoży ją przez liczbę procesów. Gdy ustawiono nbs, globalny batch jest ograniczany do nbs i zaokrąglany w dół do wielokrotności liczby procesów. Dodanie GPU zmniejsza więc liczbę kroków akumulacji, zamiast zmniejszać batch przypadający na GPU. Mechanikę samego pomiaru opisano w sekcji Hiperparametry.

SyncBatchNorm

W DDP warstwy BatchNorm każdej rangi widzą tylko jej własny fragment danych. Przy batch // world_size fragment ten może być na tyle mały, że statystyki bieżące pogorszą zbieżny model względem trenowania na jednym GPU.

sync_bn=True przekształca każdą warstwę BatchNorm w SyncBatchNorm, aby statystyki były obliczane dla globalnego batcha. Konwersja następuje wyłącznie przy aktywnym trenowaniu rozproszonym, więc ta flaga nie wpływa na trenowanie na jednym GPU.

Jest już domyślnie włączona dla rodzin konwolucyjnych intensywnie korzystających z BatchNorm: YOLOX, YOLOv7, YOLOv9 i jego wariantów, YOLO-NAS, PicoDet, RTMDet oraz FOMO. Dla każdej innej rodziny jest domyślnie wyłączona. Jeśli model zawiera BatchNorm, sync_bn jest wyłączone, a batch na rangę jest mniejszy niż 16, trener wyświetla ostrzeżenie.

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

Dla sync_bn nie ma flagi CLI. Jest to argument Pythona.

Uruchamianie za pomocą torchrun

torchrun również działa i jest właściwym wyborem, gdy harmonogram klastra już zarządza uruchamianiem procesów. Napisz skrypt dla jednego urządzenia i pozwól, aby torchrun ustawił środowisko rang.

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

Nie łącz obu metod. Przy obecnym środowisku torchrun ustawienie device="0,1" nie uruchamia procesów. Trener wybiera cuda:LOCAL_RANK, a torchrun zarządza liczbą procesów.

Zachowanie rang

Ranga 0 odpowiada za wszystkie efekty uboczne. Ustala katalog uruchomienia i rozsyła jego ostateczną nazwę, aby wszystkie rangi były zgodne, zapisuje checkpointy i artefakty oraz wywołuje callbacki użytkownika i loggery. Pozostałe rangi trenują i dostarczają gradienty.

Każda ranga inicjalizuje swój dataloader i generator liczb losowych augmentacji inną wartością wyprowadzoną ze skonfigurowanego seed, dzięki czemu rangi nie losują identycznych augmentacji.

Platforma i backend

Backend jest wybierany automatycznie: NCCL, gdy dostępne są zarówno CUDA, jak i NCCL, a w przeciwnym razie Gloo. NCCL nie jest kompilowane w systemie Windows, więc trenowanie w systemie Windows korzysta z Gloo bez dodatkowej konfiguracji. Grupa procesów jest inicjalizowana z limitem czasu wynoszącym trzy godziny.

Funkcje niedostępne w DDP

  • Przechwytywanie grafu CUDA. cuda_graph=True zapisuje jeden komunikat i trenuje w trybie eager. Zobacz Wydajność trenowania.
  • Profiler trenowania. profile=True jest ignorowane z ostrzeżeniem.

Nie każda rodzina obsługuje automatyczne uruchamianie. Obsługują je dwadzieścia cztery rodziny, obejmujące trenujące rodziny do detekcji, klasyfikacji, segmentacji semantycznej i rekonstrukcji. Przekazanie wielu GPU rodzinie, która nie ma tej obsługi, powoduje błąd podający API modelu i polecenie torchrun, zamiast po cichu trenować na jednym GPU.

Zweryfikowano z LibreYOLO v1.6.0.