GPU для ваших проєктів · оплата криптою без KYC Як орендувати
Українська
Відкрити консоль
Практичний посібник / KERNODECK

DataLoader блокується: перевірте дані перед воркерами

Почніть із num_workers=0 і фіксованим порядком, перевірте один зразок, потім повний батч і відокремте читання, трансформації, збирання та передачу на GPU. Потім поступово повертайте воркери. GPU, який чекає, не доводить, що сховище повільне: помилка в даних, серіалізація чи дороге збирання можуть заблокувати ланцюжок ще до обчислень.

6 хв читання · Посібник для розробників

Визначте, що має видавати завантажувач

Напишіть контракт виходу перед оптимізацією: кількість елементів, тип кожного поля, розмірності, діапазон цілей і правило неповних входів. Відрізняйте ідентифікатор прикладу від його позиції в батчі. Трансформація може змінити форму чи відфільтрувати вхід; програма навчання має знати, чи це дозволено.

Візьміть репрезентативний зразок, що містить звичайний файл, граничний випадок і останній елемент набору. Відкрийте кожен елемент з точно такою ж підготовкою, як у Dataset. Потім огляньте їхнє збирання. Успішний індивідуальний доступ не доводить, що кілька результатів можна скласти в стек. Для тексту задокументуйте padding і маску; для зображення — канали, розмірності та порядок осей.

Визначте межі: дані локальні чи віддалені, чи включено декодування, перетворення фіксовані чи випадкові. Тримайтеся між двома налаштуваннями; уявний виграш може походити від вилученої роботи.

Повернення до одного процесу, щоб прочитати помилку

Спочатку відтворіть з num_workers=0, shuffle=False та малим batch. Тоді завантаження відбувається в головному процесі, і трасування помилки зазвичай читабельніше. Документація DataLoader рекомендує цю можливість для налагодження. Запишіть ідентифікатор елемента, який зазнає невдачі, до його декодування, не копіюючи його чутливий вміст у журнали.

Дійте шляхом розділення: сирий доступ, перетворення, collate_fn, потім передавання. Якщо проходження зазнає невдачі до передавання, зміна CUDA — не перший слід. Якщо воно блокується лише з кількома workers, дослідіть об’єкти та ресурси, передані цим процесам. Порівняйте першу ітерацію та наступні: запуск робітників може пояснювати початкове очікування, не встановлюючи повторюваної проблеми.

Timeout може зробити очікування видимим, але не виправляє ні недоступне джерело, ні заблокований worker. Збережіть останній відомий крок і зменште кількість входів замість нескінченного збільшення цього часу очікування.

Опрацьований приклад: три очікувані канали, інше зображення

Розглянемо чотири навчальні записи. Перші три дають тензор форми [3, 16, 16], четвертий — [1, 16, 16]. За контракту, що вимагає три канали, четвертий елемент має бути ідентифікований до стекінгу. Цей сценарій тут не виконувався; він описує очікуваний результат на основі вибраних форм.

Функція нижче припускає, що кожен запис має поля id, x і y, що x є тензором CPU, а y — цілочисельним індексом. Вона відхиляє невідповідність, а не вилучає зображення непомітно. Для вашого проєкту явно вирішіть, чи монохромне зображення потрібно перетворити на три канали, чи відхилити під час імпорту. Це рішення залежить від змісту даних і попередньої обробки, яку очікує модель.

Після виправлення всі чотири ідентифікатори мають залишитися присутніми, а зібраний тензор повинен мати форму [4, 3, 16, 16]. Додайте перевірку, придатну для цілей: правильно розмірене зображення все ще може містити недійсну анотацію.

Запропоноване навчальне збирання, не виконане
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"Неочікувана форма для {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 — це ваш Dataset, що створює описані записи.
# У багатопроцесному скрипті створюйте loader під guard main.
if __name__ == "__main__":
    loader = DataLoader(dataset, batch_size=4, num_workers=0,
                        shuffle=False, collate_fn=assemble)
    iterator = iter(loader)
    batch = next(iterator)

Повертаємо workers без зміни даних

Перейдіть від нуля до невеликої кількості workers, зберігаючи batch, порядок і перетворення. Протестуйте одну повну епоху, а потім другу: деякі помилки виникають лише під час перезапуску ітератора або коли ресурси вже були використані. Збільшення паралелізму корисне лише тоді, коли підготовча робота справді може виконуватися паралельно.

Методи запуску залежать від системи та версії Python. Зі spawn захищайте точку входу програми через if __name__ == '__main__' і визначайте Dataset, collate_fn та функції workers на рівні модуля, а не в локальних лямбдах. Документація щодо процесів також пояснює, чому успадковані блокування чи потоки можуть спричиняти зависання. Зберігайте ініціалізацію доступу окремо для кожного процесу, якщо цього вимагає бібліотека.

Для IterableDataset перевіряйте розподіл між workers за допомогою ідентифікаторів: кілька працівників не повинні кожен споживати той самий потік. Оцінюйте не лише кількість batches; також шукайте дублікати та відсутні елементи.

Вимірюємо очікування та пропускну здатність у чітких одиницях

Використовуйте два взаємодоповнювальні спостереження. Прохід лише через завантажувач рахує приклади, підготовлені за визначений інтервал. Інтегрований прохід показує, що відбувається, коли модель споживає ці дані. Перший допомагає відокремити підготовку; він не відображає автоматично пропускну здатність навчання.

У вашому протоколі рахуйте фактично доставлені приклади, а потім діліть на витрачені секунди. Оголошуйте виключені проходи для запуску, кеш даних, перетворення та кількість повторень. Зберігайте значення кожного проходу, а не обирайте лише найкраще. Таблиця нижче є аркушем для запису: жодних показників продуктивності не заповнено.

Якщо форми змінюються, кількість прикладів за секунду може приховати зміну навантаження. Додайте відповідну одиницю, як-от декодовані пікселі або фактично підготовлені токени, зберігаючи також приклади. Щоб локалізувати очікування в повному циклі, назвіть зчитування наступного batch окремо від обчислення.

Прокрутіть таблицю, щоб побачити всі стовпці.
Запис для заповнення вашим навантаженням, без передбачуваних показників продуктивності
НалаштуванняПеревірені елементиСпостережувана тривалістьОчікуваний висновок
workers=0Ідентифікатори, форми, ціліВимірювати в секундахПравильна референція
Невелика кількість workersТой самий набір входівВимірювати в секундахРеальний виграш або додаткові витрати
Ті самі налаштування, друга епохаЖодних втрат чи дублюванняВимірювати в секундахВплив запуску та кешів

Опрацьовуємо пам'ять, попереднє завантаження та передачі окремо

Workers і batches в очікуванні споживають пам'ять хост-системи. Стежте за нею під час тесту, перш ніж зробити висновок, що має значення лише VRAM. Глибше попереднє завантаження може перемістити очікування, збільшуючи зайнятість; воно не гарантує більше результатів за секунду. Спочатку зменште підозрювану змінну й порівняйте той самий обсяг.

pin_memory та неблокуючі передачі стосуються переміщення даних на прискорювач. Рецепт оптимізації PyTorch представляє їх як важелі, які слід розглядати разом із обладнанням і навантаженням. Вони не виправляють помилкове декодування. Почніть із даних на CPU у workers, а потім організуйте передачу в процесі, який керує обчисленням. Користь і фактичне перекриття потрібно спостерігати, а не припускати.

Якщо використовується persistent_workers, зважте на ресурси та стан, що зберігаються між двома епохами. Задовільне налаштування на одному батчі не дає змоги перевірити закриття файлів або оновлення джерела.

Приймайте налаштування лише якщо дані залишаються коректними

Очікуваний результат — цикл, який отримує всі передбачені входи в заданих межах без тихої помилки. Порівняйте ідентифікатори та цілі до і після оптимізації. Поясніть drop_last, якщо ви відкидаєте останній неповний батч. Якщо перетворення випадкові, контролюйте їхню політику, а не вимагайте рівності пікселів, що суперечила б цій політиці.

Зберігайте найпростіше налаштування, яке відповідає виміряній потребі. Збільшення кількості воркерів може нічого не покращити, якщо сховище, декодування або модель уже накладають обмеження. Ресурси CPU, RAM і сховища сервера не випливають із назви його GPU: указуйте ці потреби окремо, коли готуєте своє середовище Kernodeck.

Ваші запитання

Чи вимикає num_workers=0 навчання на GPU?

Ні. Він переносить завантаження даних у головний процес. Модель усе одно може обчислювати на GPU. Це налаштування передусім дає змогу читати помилки читання, перетворення та збирання більш безпосередньо.

Чи потрібно вибирати стільки воркерів, скільки ядер CPU?

Не автоматично. Правильне налаштування залежить від роботи з підготовки, пам'яті, доступу до даних і швидкості споживання моделлю. Порівняйте кілька значень, зберігаючи те саме навантаження та контролюючи надані входи.

Чи виправляє менший батч воркер, який зупиняється?

Він може змінити тиск на пам'ять, але не виправляє недійсну анотацію, ресурс, що не піддається серіалізації, або нечитабельний файл. Спочатку відтворіть із нульовою кількістю воркерів, щоб визначити етап, який спричиняє проблему.

Чи вимірює час, витрачений у next(iterator), диск?

Ні. Після iterator=iter(loader) next(iterator) очікує на батч. Можуть втручатися декодування, перетворення, збирання, обмін між процесами та попереднє завантаження. Вимірювання лише читання та трасування повного циклу відповідають на різні запитання.