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

Дообучение с LoRA

LoRA замораживает тяжёлые предобученные части модели и обучает рядом с ними небольшие низкоранговые адаптеры, а вместе с ними и те слои, которые должны остаться плотными. В LibreYOLO весь публичный интерфейс — один булев флаг.

Установка

LoRA держится на необязательной зависимости peft.

pip
pip install "libreyolo[lora]"

Без неё lora=True выбрасывает ImportError с указанием этой команды, а не запускает случайно полное дообучение.

Использование

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

lora=True — это весь интерфейс. Ранг, alpha, dropout и целевые модули зафиксированы для каждого семейства так, чтобы совпадать с исходной реализацией, и в пользовательские настройки не вынесены.

Семейство, которое не поддерживает LoRA, падает с ошибкой на этапе подготовки, а не игнорирует флаг:

LoRA fine-tuning (lora=True) is not supported for yolo9. LoRA targets
transformer components with nn.Linear layers (e.g. RF-DETR, D-FINE, DEIM).

CLI отклоняет флаг раньше, ещё до сборки модели, по собственному списку тех же девяти семейств.

Какие семейства

RF-DETR, D-FINE, DEIM, DEIMv2, RT-DETR v1, v2 и v4, EC и ConvNeXt. Решает атрибут supports_lora у класса-тренера каждого семейства, а в CLI лежит соответствующий ему список.

Покрытие по задачам уже, чем по семействам. D-FINE и EC поддерживают только детекцию, а их ветки сегментации и позы падают с ошибкой. Ветка семантической сегментации в RF-DETR падает. ConvNeXt — это классификация.

Всё остальное падает с ошибкой. Частичного или тихого режима нет.

Что делает каждый рецепт

Рецепты различаются, потому что различаются архитектуры, и рецепту, который работает на ViT-бэкбоне, не к чему прицепиться на свёрточном.

RF-DETR использует DoRA, LoRA с разложением весов, с рангом 16 и alpha 16 на проекциях внимания query, key и value в бэкбоне DINOv2 — как в исходной реализации RF-DETR. ViT-бэкбон замораживается; проектор, декодер и голова детекции продолжают обучаться как обычно.

В D-FINE, DEIM и RT-DETR v1, v2 и v4 свёрточный бэкбон соединён с гибридным трансформерным энкодером и деформируемым декодером, поэтому граница проходит иначе. Свёрточный бэкбон замораживается целиком, а значит, для него пропускается и обратный проход. Трансформерные блоки замораживают свои базовые веса и обучают обычные LoRA-адаптеры с тем же рангом 16 и alpha 16 на своих линейных слоях: feed-forward linear1 и linear2, гейт и проекции деформируемого внимания. Всё остальное — свёрточное слияние в энкодере, входные проекции, головы предсказания и эмбеддинги запросов — продолжает обучаться плотно.

Две детали этого рецепта сделаны намеренно. Self-attention декодера остаётся замороженным без адаптеров, потому что nn.MultiheadAttention из PyTorch читает out_proj.weight напрямую и молча обошёл бы внедрённый адаптер. И это обычная LoRA, а не DoRA, потому что несколько линейных слоёв декодера по замыслу инициализируются нулями, а нормализация магнитуды в DoRA делит на норму весов.

DEIMv2 берёт тот же рецепт, но целями становятся его SwiGLU-слои feed-forward w12 и w3. В размерах S, M, L и X есть ещё и ViT-бэкбон DINOv3: база ViT замораживается, её объединённые слои внимания qkv получают адаптеры, а свёрточная пирамида Spatial Tuning Adapter продолжает обучаться как аналог проектора. Адаптеры на qkv ставятся даже тогда, когда в конфиге ViT изначально заморожен, — в том и смысл, чтобы адаптировать замороженный бэкбон. Размеры меньше S используют свёрточный бэкбон и берут обычный рецепт.

EC — это DETR, у которого бэкбон — ViT в окружении обучаемой свёрточной пирамиды-проектора. База ViT замораживается, её слои qkv получают адаптеры, трансформерные блоки берут общий рецепт, а проектор и головы остаются плотными.

Блоки ConvNeXt несут в себе линейные MLP в раскладке channels-last, fc1 и fc2, и на них ставятся обычные адаптеры. Depthwise-свёртки, нормализации и параметры layer-scale замораживаются. Голова классификации остаётся плотной, чтобы продолжало работать произвольное число классов.

Головы детекции и классификации остаются обучаемыми в любом рецепте, потому что под своё число классов голову нужно обучать заново.

Чекпойнты и экспорт

best.pt и last.pt сохраняют тензоры адаптеров, поэтому запуск с LoRA можно возобновить или изучить так же, как любой другой. Для загрузки такого чекпойнта нужен установленный extra lora, потому что загрузчик повторяет внедрение адаптеров, чтобы ключи сошлись.

export() объединяет адаптеры с плотными весами, поэтому экспортированный артефакт не зависит от peft. То же объединение доступно напрямую для модели в памяти.

Экспорт объединяет адаптеры
from libreyolo import LibreYOLO model = LibreYOLO("runs/train/exp/weights/best.pt")model.export(format="onnx")
Объединение на месте
from libreyolo import LibreYOLOfrom libreyolo.training.lora import merge_lora_adapters model = LibreYOLO("runs/train/exp/weights/best.pt")merged = merge_lora_adapters(model.model) print(f"{merged} adapter layers folded into dense weights")

После объединения дерево модулей полностью плотное, а второе объединение уже ничего не меняет.

Что это экономит, а что нет

LoRA сокращает память под оптимизатор и градиенты, а в семействах, где бэкбон замораживается целиком, ещё и пропускает его обратный проход.

Память под активации не меняется. Активации прямого прохода всё равно приходится хранить для всего, что осталось обучаемым, и обычно именно это задаёт пик. При самом жёстком бюджете VRAM дополнительно уменьшите batch или imgsz.

Связанные страницы

  • Заморозка слоёв — другой способ обучать часть весов; он работает для всех семейств и не требует дополнительных зависимостей. freeze и lora=True сочетаются: параметры адаптеров остаются обучаемыми, даже когда их родительская группа бэкбона заморожена.
  • Гиперпараметры — про batch, imgsz и остальные аргументы train().

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