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