Дообучение с LoRA
LoRA замораживает тяжёлые предобученные части модели и обучает рядом с ними небольшие низкоранговые адаптеры, а вместе с ними и те слои, которые должны остаться плотными. В LibreYOLO весь публичный интерфейс — один булев флаг.
Установка
LoRA держится на необязательной зависимости peft.
pip install "libreyolo[lora]"Без неё lora=True выбрасывает ImportError с указанием этой команды, а не
запускает случайно полное дообучение.
Использование
from libreyolo import LibreYOLO model = LibreYOLO("LibreRFDETRs.pt")model.train(data="my-dataset.yaml", epochs=50, lora=True)libreyolo train model=LibreRFDETRs.pt data=my-dataset.yaml \ epochs=50 lora=truelora=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().