Переглянути як Markdown

Продуктивність навчання

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

Вимірювання перед змінами

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

Профілювати й продовжити навчання
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") # Профілює коротке вікно справжніх кроків, виводить вердикт, а потім# продовжує запуск із видаленими hooks.model.train(data="my-dataset.yaml", epochs=100, profile=True)
Лише виміряти й зупинити
# Установлює no_aug_epochs=0 і виконує достатню для заповнення вікна кількість епох.libreyolo profile run coco128 --weights LibreYOLO9s.pt --size s
Детально дослідити результат
libreyolo profile summary runs/profile/prof/profile.jsonlibreyolo profile phases runs/profile/prof/profile.jsonlibreyolo profile kernels runs/profile/prof/profile.json --top 10

profile=True вимірює вікно справжніх кроків навчання, типово відкидає п'ять кроків і вимірює двадцять, виводить звіт, записує артефакти, а потім продовжує навчання з видаленими hooks. Коли профілювання вимкнене, воно нічого не коштує, а під час розподіленого навчання ігнорується.

Звіт завершується одним із чотирьох вердиктів:

ВердиктЗначенняВажелі
dataloaderGPU очікує вхідні данібільше workers, cache="ram" або "disk", легша аугментація, більший батч
host / launchGPU отримує дані надто повільно, багато дрібних ядербільший батч, графи CUDA, менше синхронізацій хоста на крок
computeGPU повністю завантаженийAMP або bfloat16 чи прийняти результат
memory-pressureперевантаження розподільника, VRAM на межіменший батч; показники використання тут ненадійні

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

До каталогу запуску потрапляють чотири файли: timeline.html, який сам відкривається в браузері, profile_trace.json для Perfetto чи Nsight, profile_summary.json і самодостатній profile.json, який можна переносити й передавати назад підкомандам libreyolo profile.

Варто знати дві особливості profile run. Він установлює no_aug_epochs=0, оскільки профайлер вимірює епоху 0, а короткий запуск із типовим no_aug_epochs профілював би полегшений завантажувач без аугментації замість фактично використовуваного під час навчання. Крім того, --repeat N повідомляє середнє та стандартне відхилення, що важливо через шум кроку, обмеженого запуском ядер, через який один запуск вводить в оману. Команда записує каталоги окремих спроб prof_1, prof_2 тощо, а також сукупний profile_repeat.json.

Змішана точність

amp=True є типовим значенням для більшості сімейств і виконує прямий прохід у контексті CUDA autocast. amp_dtype вибирає float16 або bfloat16.

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt")model.train(data="my-dataset.yaml", amp=True, amp_dtype="bfloat16")
CLI
libreyolo train model=LibreYOLO9s.pt data=my-dataset.yaml \  amp_dtype=bfloat16

Float16 потребує динамічного масштабування функції втрат і отримує активний масштабувальник градієнтів; ширший діапазон експоненти bfloat16 цього не потребує, тому його масштабувальник вимкнено. Чотири сімейства постачаються з amp=False: D-FINE, DEIM, YOLO-NAS і FOMO, а налаштування DEIM успадковує RT-DETRv4. D-FINE пояснює причину: його декодер обмежує активації значенням 65504, найбільшим скінченним значенням float16.

Семантику аргументів, зокрема поведінку запиту bfloat16 на обладнанні без його підтримки, описано в розділі Гіперпараметри.

Графи CUDA

cuda_graph=True захоплює навчальні прямий і зворотний проходи мережі в граф CUDA, усуваючи витрати на запуск ядер на кожному кроці.

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt")model.train(data="my-dataset.yaml", epochs=100, cuda_graph=True)
CLI
libreyolo train model=LibreYOLO9s.pt data=my-dataset.yaml \  epochs=100 cuda_graph=true

Прапорець завжди безпечно передавати. Сімейство, задача чи конфігурація, які не можна захопити, записують один рядок у лог і без змін навчаються в eager-режимі.

Захоплюється лише мережа. Функція втрат навмисно залишається в eager-режимі, оскільки функції втрат виявлення виконують вибір за булевими масками, угорське зіставлення й розгалуження за результатами призначення, які граф не може записати. Крок оптимізатора, обрізання градієнтів, оновлення EMA та розклад швидкості навчання також залишаються в eager-режимі.

Це обмежує виграш часткою кроку, яку займає мережа, а ця частка значно різниться. Вимірювання на RTX 5070 Ti за 640 px і батча 8 показали: мережа займає 84 відсотки кроку YOLOv9-t, 44 відсотки кроку YOLOv7-b, 31 відсоток кроку YOLOX-t і 26 відсотків кроку RTMDet-t. Останні дві моделі витрачають більшу частину кроку в засобах призначення міток, тому захоплення мережі допомагає їм найменше.

Практична користь

Умови для кожного показника нижче: RTX 5070 Ti, Windows, AMP, по одному процесу на варіант зі спільного збереженого стану, повторне виконання одного справжнього батча без залучення завантажувача даних, найшвидший із 24 кроків після прогрівання. Виявлення за 640 px, класифікація за 224 px. Розмір батча вказано для кожного рядка.

СімействоРозмірБатчEagerІз графомПрискорення
FOMOs167.0 ms1.9 ms3.63x
MobileNetV4s1614.5 ms5.3 ms2.74x
EfficientNetV2b01629.0 ms11.9 ms2.44x
YOLOv9t893.6 ms47.0 ms1.99x
NAFNets8132.5 ms105.5 ms1.26x
PicoDets8145.0 ms118.7 ms1.22x
D-FINEn4185.3 ms159.2 ms1.16x
RF-DETRn4276.3 ms239.8 ms1.15x
YOLOXt8102.2 ms90.5 ms1.13x
RTMDett8149.7 ms136.2 ms1.10x
YOLOv7b4102.5 ms98.0 ms1.05x

Ці числа ізолюють крок GPU. Повне донавчання також витрачає час на завантажувач даних і валідацію. Для YOLOv9-t на наборі виявлення з 406 зображень протягом 20 епох, з батчем 8, розміром 640 px і 4 робочими процесами завантажувача на тій самій машині реальний час становив 428.4 s в eager-режимі проти 367.7 s із графом, тобто виграш 1.16x, а mAP50-95 дорівнював 0.6394 в обох варіантах.

На ці числа впливають три чинники. Малі батчі обмежуються запуском ядер, а великі обчисленнями, тому RT-DETR-r18 отримує 1.19x із батчем 2 і 1.04x із батчем 8. Витрати на запуск найвищі у Windows, а виграш у Linux становить приблизно від третини до половини значень таблиці. Запуск, обмежений завантажувачем даних, узагалі не змінює реальний час, тому профайлер слід використовувати першим.

Захоплення так само активується за amp=False, але ядра fp32 працюють довше, тому крок менше обмежений запуском і більшість сімейств отримує менший виграш. На тому самому обладнанні MobileNetV4-s із батчем 16 переходить від 2.74x за AMP до 3.61x за fp32, тоді як YOLOv9-t із батчем 8 переходить від 1.99x до 1.69x, а RT-DETR-r18 із батчем 4 від 1.12x до 0.99x.

Область застосування захоплення

ЗадачаСімейства
detectyolo9, yolo9_p2, yolo9_e2e, yolox, yolo7, yolonas, picodet, rtmdet, rfdetr, dfine, deim, deimv2, rtdetr, rtdetrv2, rtdetrv4, ec
classifyresnet, convnext, mobilenetv4, efficientnetv2
semanticsegformer, lingbotvision
pointfomo
restorenafnet

Усі інші варіанти повертаються до eager-режиму з одним рядком у лозі: інші задачі цих сімейств, не перелічені сімейства, розподілені запуски та запуски з дистиляцією. Помилка захоплення під час виконання також переводить решту запуску в eager-режим, а не завершує його з помилкою.

Для детекторів енкодер-декодер, D-FINE, DEIM, DEIMv2, RT-DETR v1, v2 і v4 та EC, захоплюються лише бекбон і енкодер. Їхній декодер читає еталонну розмітку для побудови контрастно-шумопригнічувальних запитів, а кількість цих запитів відповідає найбільшій кількості еталонних об'єктів у батчі, тому кількість токенів змінюється між батчами.

Форми

Граф дійсний лише для тієї форми вхідних даних, з якою його захоплено. Засіб навчання рахує форми батчів і виконує захоплення після третього повторення форми. Батчі будь-якої іншої форми виконуються в eager-режимі: багатомасштабні батчі й останній неповний батч епохи.

Це пастка для сімейств DETR, які типово змінюють розмір кожного батча. За multi_scale=True короткий запуск може жодного разу не побачити одну форму достатню кількість разів для захоплення. Передайте multi_scale=False, якщо метою є прискорення.

YOLOX змінює обчислення захопленої ділянки під час запуску, вмикаючи гілку регресії L1 після закриття мозаїки на no_aug_epochs. Засіб навчання скасовує захоплення в цій точці й виконує повторне після стабілізації нової форми.

Числова поведінка й пам'ять

Більшість сімейств побітово відтворює свою траєкторію функції втрат eager-режиму за AMP. FOMO й LingBot-Vision відрізняються в останньому біті float32 через інший порядок підсумовування. Детектори з деформівною увагою, D-FINE, DEIM, DEIMv2, RT-DETR, RF-DETR і EC, не відтворюють навіть власні eager-запуски, бо їхній зворотний прохід накопичує значення атомарними операціями, а згортки TF32 вибирають порядок редукції для кожного запуску; запуск із графом залишається в межах цього розкиду. RTMDet має відносну різницю приблизно 3e-4 у двох зі 139 градієнтів, бо використовує спільні згортки голови на різних рівнях піраміди, а два зворотні шляхи підсумовують три внески в різному порядку. SegFormer має стохастичну глибину в захопленій ділянці, тому повторно виконаний граф використовує власний випадковий потік і статистично еквівалентний eager-режиму, а не ідентичний йому; менеджер один раз записує це в лог під час захоплення.

За amp=False побітова ідентичність недоступна для жодного варіанта на цьому обладнанні незалежно від захоплення. Два однакові ініціалізовані eager-запуски YOLOv9-t розходяться на 36 відсотків відносно за 20 кроків, а YOLOX-t на 2.6 відсотка, оскільки cuDNN вибирає недетермінований алгоритм градієнта ваг для деяких форм згортки fp32.

Захоплений граф закріплює статичні буфери вхідних даних, результатів і робочої пам'яті, тому пікове використання VRAM зростає приблизно на один додатковий набір активацій. Для наведених вище сімейств пікове виділення змінилося від -5 до +19 відсотків. Відносні витрати найбільші для малих класифікаційних моделей, чиї активації початково малі: ResNet-18 за 224 px і батча 16 перейшла від 0.48 GB в eager-режимі до 0.57 GB із графом. Якщо це перевищує межу, зменште батч або не вмикайте прапорець.

Пов'язані матеріали

Перевірено з LibreYOLO v1.5.0.