Zdefiniuj, co ma dostarczać loader
Napisz kontrakt wyjściowy, zanim zaczniesz optymalizować: liczba elementów, typ każdego pola, wymiary, zakres celów i reguła dla niekompletnych wejść. Odróżnij identyfikator przykładu od jego pozycji w batchu. Transformacja może zmienić kształt lub odfiltrować wejście; program treningowy musi wiedzieć, czy jest to dozwolone.
Weź reprezentatywny przykład obejmujący zwykły plik, przypadek brzegowy i ostatni element zbioru. Otwórz każdy element z dokładnie takim samym przygotowaniem jak Dataset. Następnie zbadaj ich złożenie. Udany pojedynczy dostęp nie dowodzi, że kilka wyników da się ułożyć w stos. Dla tekstu udokumentuj padding i maskę; dla obrazu kanały, wymiary i kolejność osi.
Ustal zakres: dane lokalne czy zdalne, dekodowanie wliczone czy nie, transformacje stałe czy losowe. Trzymaj go między dwoma pomiarami; pozorny zysk może pochodzić z usuniętej pracy.
Wróć do jednego procesu, aby odczytać błąd
Najpierw odtwórz problem z num_workers=0, shuffle=False i małym batchem. Ładowanie odbywa się wtedy w procesie głównym, a ślad błędu jest zwykle czytelniejszy. Dokumentacja DataLoader zaleca tę możliwość do debugowania. Zapisuj identyfikator elementu, który zawodzi, przed jego dekodowaniem, nie kopiując jego wrażliwej zawartości do logów.
Działaj przez rozdzielenie: surowy dostęp, transformacja, collate_fn, a potem transfer. Jeśli ścieżka zawodzi przed transferem, modyfikowanie CUDA nie jest pierwszym tropem. Jeśli blokuje się tylko przy wielu workerach, zbadaj obiekty i zasoby przekazywane do tych procesów. Porównaj pierwszą iterację z kolejnymi: start workerów może wyjaśniać początkowe oczekiwanie, nie świadcząc o nawracającym problemie.
Timeout może uwidocznić oczekiwanie, ale nie naprawi ani niedostępnego źródła, ani zablokowanego workera. Zapamiętaj ostatni znany krok i zmniejsz liczbę wejść, zamiast wydłużać ten limit w nieskończoność.
Przykład przeanalizowany: trzy oczekiwane kanały, inny obraz
Rozważmy cztery rekordy dydaktyczne. Pierwsze trzy dają tensor o kształcie [3, 16, 16], czwarty [1, 16, 16]. Przy kontrakcie wymagającym trzech kanałów czwarty element musi zostać zidentyfikowany przed złożeniem. Ten scenariusz nie został tu uruchomiony; opisuje oczekiwany wynik na podstawie wybranych kształtów.
Poniższa funkcja zakłada, że każdy rekord ma pola id, x i y, że x jest tensorem CPU, a y jest indeksem całkowitym. Odrzuca niespójność, zamiast po cichu usuwać obraz. W swoim projekcie zdecyduj wyraźnie, czy obraz monochromatyczny ma być konwertowany na trzy kanały, czy odrzucany przy imporcie. Decyzja zależy od znaczenia danych i oczekiwanego przez model przetwarzania wstępnego.
Po poprawce wszystkie cztery identyfikatory powinny pozostać obecne, a złożony tensor powinien mieć kształt [4, 3, 16, 16]. Dodaj zabezpieczenie dopasowane do celów: poprawnie zwymiarowany obraz może wciąż nieść nieprawidłową adnotację.
import torch
from torch.utils.data import DataLoader
def assemble(records):
for item in records:
if tuple(item["x"].shape) != (3, 16, 16):
raise ValueError(f"Nieoczekiwany kształt dla {item['id']}")
return {
"ids": [item["id"] for item in records],
"x": torch.stack([item["x"] for item in records]),
"y": torch.tensor([item["y"] for item in records],
dtype=torch.long),
}
# dataset to Twój Dataset zwracający opisane rekordy.
# W skrypcie wieloprocesowym utwórz loader pod osłoną main.
if __name__ == "__main__":
loader = DataLoader(dataset, batch_size=4, num_workers=0,
shuffle=False, collate_fn=assemble)
iterator = iter(loader)
batch = next(iterator)Przywróć workery bez zmiany danych
Przejdź od zera do małej liczby workerów, zachowując batch, kolejność i transformacje. Przetestuj pełną epokę, a potem drugą: niektóre błędy pojawiają się dopiero przy ponownym uruchomieniu iteratora albo gdy zasoby zostały już zużyte. Zwiększanie równoległości ma sens tylko wtedy, gdy praca przygotowawcza rzeczywiście może postępować równolegle.
Metody startu zależą od systemu i wersji Pythona. Przy spawn zabezpiecz wejście programu przez if __name__ == '__main__' i definiuj Dataset, collate_fn oraz funkcje workerów na poziomie modułu, a nie w lokalnych lambdach. Dokumentacja procesów wyjaśnia też, dlaczego odziedziczone blokady lub wątki mogą powodować zakleszczenia. Utrzymuj inicjalizację dostępów właściwą dla każdego procesu, gdy biblioteka tego wymaga.
W przypadku IterableDataset sprawdź podział między workerami za pomocą identyfikatorów: kilku pracowników nie powinno każdy z osobna konsumować tego samego strumienia. Nie oceniaj tylko liczby batchy; szukaj też duplikatów i brakujących elementów.
Zmierz oczekiwanie i przepustowość w jasnej jednostce
Użyj dwóch uzupełniających się obserwacji. Sam przebieg przez loader liczy przykłady przygotowane w zdefiniowanym przedziale. Przebieg zintegrowany bada, co się dzieje, gdy model konsumuje te dane. Pierwszy pomaga wyizolować przygotowanie; nie odzwierciedla automatycznie przepustowości trenowania.
W swoim protokole licz przykłady faktycznie dostarczone, a następnie podziel przez upływające sekundy. Zadeklaruj przebiegi wykluczone na rozruch, cache danych, transformacje i liczbę powtórzeń. Zachowaj wartości z każdego przebiegu zamiast wybierać tylko najlepszy. Tabela poniżej to arkusz pomiarowy: żadna wydajność nie jest wypełniona.
Jeśli kształty się zmieniają, liczba przykładów na sekundę może maskować zmianę obciążenia. Dodaj właściwą jednostkę, taką jak zdekodowane piksele lub faktycznie przygotowane tokeny, zachowując również przykłady. Aby zlokalizować oczekiwania w pełnej pętli, nazwij odczyt następnego batcha oddzielnie od obliczeń.
Przewiń tabelę, aby zobaczyć wszystkie kolumny.| Ustawienie | Sprawdzone elementy | Zaobserwowany czas | Oczekiwany wniosek |
|---|---|---|---|
| workers=0 | Identyfikatory, kształty, cele | Do zmierzenia w sekundach | Prawidłowe odniesienie |
| Mała liczba workerów | Ten sam zestaw wejść | Do zmierzenia w sekundach | Rzeczywisty zysk lub narzut |
| To samo ustawienie, druga epoka | Brak utraty i duplikacji | Do zmierzenia w sekundach | Wpływ rozruchu i cache'ów |
Traktuj pamięć, wstępne ładowanie i transfery osobno
Workery i batchе w oczekiwaniu zużywają pamięć hosta. Obserwuj ją podczas testu, zanim uznasz, że liczy się tylko VRAM. Głębsze wstępne ładowanie może przesunąć oczekiwanie, zwiększając jednocześnie zajętość; nie gwarantuje więcej wyników na sekundę. Najpierw zredukuj podejrzaną zmienną i porównaj ten sam zakres.
pin_memory i nieblokujące transfery dotyczą przejścia danych do akceleratora. Przepis optymalizacji PyTorch przedstawia je jako dźwignie, które należy zbadać wraz ze sprzętem i obciążeniem. Nie naprawiają błędnego dekodowania. Zacznij od danych CPU w workerach, a następnie zorganizuj transfer w procesie sterującym obliczeniami. Korzyść i faktyczne nakładanie się muszą być zaobserwowane, a nie założone.
Jeśli używasz persistent_workers, pamiętaj o zasobach i stanie zachowywanym między epokami. Zadowalające ustawienie na jednym batchu nie wystarczy, aby zweryfikować zamykanie plików czy odświeżanie źródła.
Akceptuj ustawienie tylko wtedy, gdy dane pozostają poprawne
Oczekiwany rezultat to pętla, która otrzymuje wszystkie przewidziane dane wejściowe, w wybranych ramach, bez cichego błędu. Porównaj identyfikatory i cele przed optymalizacją i po niej. Wyjaśnij drop_last, jeśli pomijasz ostatni niepełny batch. Jeśli transformacje są losowe, kontroluj ich politykę, zamiast wymagać równości pikseli, która byłaby z tą polityką sprzeczna.
Zachowaj najprostsze ustawienie, które odpowiada zmierzonej potrzebie. Zwiększenie liczby workerów może nic nie poprawić, jeśli pamięć masowa, dekodowanie lub model już narzucają limit. Zasobów CPU, RAM i pamięci masowej serwera nie można wywnioskować z nazwy jego GPU: określ te potrzeby osobno, gdy przygotowujesz swoje środowisko Kernodeck.