Посмотреть как Markdown

Гиперпараметры

Каждый аргумент обучения — поле датакласса TrainConfig. Базовый класс задаёт само поле и его значение по умолчанию; каждое семейство моделей наследуется от него и переопределяет те значения, которые меняет его опубликованный рецепт.

Задание аргументов

train() принимает именованные аргументы, а CLI — те же имена в форме key=value.

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt")results = model.train(    data="my-dataset.yaml",    epochs=100,    batch=16,    imgsz=640,    lr0=0.01,) print(results["best_mAP50_95"])
CLI
libreyolo train model=LibreYOLO9s.pt data=my-dataset.yaml \  epochs=100 batch=16 imgsz=640 lr0=0.01

Оба пути ведут в одно и то же место. Аргументы передаются в TrainConfig.from_kwargs(), который собирает датакласс конфигурации семейства.

Опечатка не вызывает ошибку

from_kwargs() отбрасывает любой ключ, которого нет среди полей конфигурации, и выдаёт UserWarning с его именем. Обучение после этого стартует со значением по умолчанию:

python
# UserWarning: Unknown training config keys (ignored): ['learning_rate']
model.train(data="my-dataset.yaml", learning_rate=0.001)

Ничего не падает, запуск доходит до конца, а скорость обучения так и не стала той, которую запросил вызывающий код. Читайте предупреждения на первой эпохе нового рецепта. CLI строже, потому что проверяет имена флагов до сборки конфигурации, так что флаг CLI с опечаткой отклоняется сразу.

Значения по умолчанию свои у каждого семейства

TrainConfig задаёт поле и базовое значение по умолчанию. Каждое семейство наследуется от него и переопределяет то, что меняет его опубликованный рецепт, поэтому у вопроса «какая скорость обучения по умолчанию» нет одного правильного ответа.

Базовые значения по умолчанию — optimizer="sgd", lr0=0.01, momentum=0.937, weight_decay=5e-4, scheduler="yoloxwarmcos", epochs=300, batch=16, imgsz=640 и amp=True. Три примера того, как далеко семейство от них уходит:

ПолеБазаYOLOv9D-FINEYOLO-NAS
optimizersgdsgdadamwadamw
lr00.010.012e-45e-4
weight_decay5e-45e-41e-41e-5
scheduleryoloxwarmcoslinearflat_cosinecos
epochs300300132300
ampTrueTrueFalseFalse

D-FINE и DEIM поставляются с amp=False, потому что декодер D-FINE ограничивает активации значением 65504 — это наибольшее конечное значение float16. У YOLO-NAS и FOMO он по умолчанию тоже выключен. Флаг --amp в CLI по умолчанию равен True для каждого семейства, поэтому он считается заданным пользователем и переопределяет значение семейства; не трогайте его, если не собираетесь его менять.

Чтобы прочитать настоящие значения по умолчанию семейства, а не гадать:

Чтение итоговых значений по умолчанию для семейства
from dataclasses import fields from libreyolo import LibreYOLO9from libreyolo.training.config import TrainConfig family_cfg = LibreYOLO9.TRAIN_CONFIG()base_cfg = TrainConfig() for f in fields(family_cfg):    family_value = getattr(family_cfg, f.name)    base_value = getattr(base_cfg, f.name, None)    if not hasattr(base_cfg, f.name) or family_value != base_value:        print(f"{f.name}: {family_value}")
CLI
# Печатает значения по умолчанию для train, val и predict, включая переопределения семейств.libreyolo cfg

Размер батча

batch — это глобальный батч. При обучении на нескольких GPU каждый ранг загружает batch // world_size, поэтому переданное число — это число изображений на шаг оптимизатора независимо от того, сколько GPU задействовано. См. Обучение на нескольких GPU.

batch=-1 включает autobatch. Модель прогоняется в режиме обучения с настоящим обратным проходом на степенях двойки, по кривой потребления памяти строится прямая, и берётся наибольшая степень двойки строго ниже экстраполированного значения, которая укладывается в 60 процентов всей VRAM.

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") # batch=-1 замеряет память GPU и превращается в конкретную степень двойки.model.train(data="my-dataset.yaml", batch=-1, imgsz=640)
CLI
libreyolo train model=LibreYOLO9s.pt data=my-dataset.yaml batch=-1

Замер именно в режиме обучения и с обратным проходом — это принципиально: замер в режиме инференса не учитывает сохранённые активации и тензоры градиентов, а для глубокой CNN они в несколько раз превышают объём, нужный для инференса. RF-DETR снижает целевую долю до 45 процентов, потому что синтетический обратный проход при замере всё равно недооценивает, во что обходятся его функция потерь и вспомогательные слои декодера.

Autobatch работает только с CUDA. На CPU или MPS он пишет в лог одну строку и оставляет батч по умолчанию.

Накопление градиентов

nbs задаёт номинальный, он же эффективный, размер батча. На один шаг оптимизатора накапливается round(nbs / batch) микробатчей.

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") # 4 микробатча по 16 на шаг оптимизатора, эффективный батч 64.model.train(data="my-dataset.yaml", batch=16, nbs=64)

Если оставить None — значение по умолчанию, — накопление выключено и обучение идёт как обычно.

Скорость обучения и расписание

lr0 — начальная скорость обучения, а optimizer принимает sgd, adam и adamw. momentum — это момент SGD или beta1 у Adam, weight_decay — L2-слагаемое, а nesterov относится к SGD.

Форму расписания задают scheduler, warmup_epochs, warmup_lr_start и min_lr_ratio. no_aug_epochs задаёт, сколько последних эпох идёт без сильной аугментации, и несколько расписаний используют его ещё и для формы своего хвоста, так что это не только настройка аугментации. Что каждое семейство делает с аугментационной половиной этого параметра, описано на странице Аугментации.

Некоторые семейства добавляют свои настройки скорости обучения. backbone_lr_mult масштабирует группу бэкбона относительно головы, clip_max_norm задаёт обрезку градиентов, а SegFormer использует head_lr_mult, чтобы его декодирующая голова обучалась со скоростью в десять раз выше, чем у бэкбона. Эти поля живут в подклассе конфигурации семейства, а не в базовом.

EMA

ema=True держит экспоненциальное скользящее среднее весов рядом с обученными. По умолчанию оно включено везде, кроме FOMO.

ema_decay — целевой коэффициент затухания. Затухание нарастает, а не начинается сразу с целевого значения: фактическая величина на обновлении n равна ema_decay * (1 - exp(-n / tau)), где tau по умолчанию 2000, поэтому ранние обновления точнее следуют за моделью, а поздние её сглаживают. Значения по умолчанию у семейств идут от 0.997 у YOLO-NAS pose через 0.9998 у YOLOX до 0.9999 у YOLOv9 и линейки DETR.

Валидируются именно веса EMA, и они же лежат в best.pt и last.pt. Исходные обученные веса тоже сохраняются, под ключом train_model, поэтому возобновление продолжает обученную траекторию, а не среднее.

Точность вычислений

amp=True выполняет прямой проход под CUDA autocast. amp_dtype выбирает float16 (по умолчанию) или bfloat16; написания fp16 и bf16 тоже принимаются.

Float16 нуждается в динамическом масштабировании функции потерь и получает работающий GradScaler. Bfloat16 с его более широким диапазоном экспоненты в этом не нуждается, поэтому его scaler создаётся, но остаётся выключенным, а путь оптимизатора при этом не меняется. Запрос bfloat16 на устройстве с CUDA без поддержки bfloat16 приводит к ошибке на этапе настройки, а не к тихой деградации.

Вывод, чекпойнты и остановка

Запуски пишутся в project/name. project везде по умолчанию равен runs/train, а вот name — одно из полей, переопределяемых на уровне семейства: базовое значение по умолчанию — exp, тогда как YOLOv9 использует yolo9_exp, а D-FINE — dfine_exp. При exist_ok=False, значении по умолчанию, к существующему каталогу добавляется числовой суффикс, вместо того чтобы перезаписать его.

save_period пишет дополнительный weights/epoch_<N>.pt каждые N эпох — вдобавок к weights/last.pt после каждой эпохи и weights/best.pt всякий раз, когда отслеживаемая метрика улучшается. eval_interval задаёт, как часто запускается валидация, а patience останавливает запуск после указанного числа эпох без улучшения; 0 отключает раннюю остановку.

cache ускоряет повторные эпохи, держа декодированные изображения в RAM (True или "ram") либо в виде файлов .npy рядом с исходниками ("disk"). Чтение из кэша побайтово совпадает с обычным. Если работают воркеры загрузчика данных, из двух вариантов безопаснее "disk".

Возобновление

resume=True продолжает прерванный запуск. Чекпойнт нужно загрузить заранее, потому что resume берёт его из модели, а не из отдельного аргумента.

Python
from libreyolo import LibreYOLO # Загрузить чекпойнт прерванного запуска, затем попросить продолжить.model = LibreYOLO("runs/train/exp/weights/last.pt")model.train(data="my-dataset.yaml", epochs=100, resume=True)
CLI
libreyolo train model=runs/train/exp/weights/last.pt \  data=my-dataset.yaml epochs=100 resume=true

Возобновление восстанавливает обученные веса, состояние оптимизатора, веса EMA и счётчик обновлений, отслеживание лучшей метрики, масштаб GradScaler, а также состояния генераторов случайных чисел PyTorch, CUDA и NumPy. Оно стартует с эпохи чекпойнта плюс один и прокручивает расписание до этой точки.

Двух вещей оно не сделает. resume=True нельзя сочетать с pretrained — это приводит к ошибке. И если ключ лучшей метрики в чекпойнте отличается от ключа текущего запуска, отслеживание лучшей метрики сбрасывается в ноль с предупреждением, вместо того чтобы сравнивать величины, которые означают разное.

Рецепты в файле

cfg= загружает YAML-словарь с именами полей TrainConfig и подкладывает его под явные именованные аргументы, так что kwarg всегда побеждает файл.

Python
from libreyolo import LibreYOLO # Ключи в yaml — имена полей TrainConfig. Явные kwargs побеждают.model = LibreYOLO("LibreYOLO9s.pt")model.train(data="my-dataset.yaml", cfg="my-recipe.yaml", epochs=50)

size и num_classes из файла вырезаются, потому что они уже принадлежат экземпляру модели. Флага --cfg в CLI нет; путь к файлу — это аргумент Python.

Смотрите также

Проверено с LibreYOLO v1.5.0.