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

Upstream-чекпойнты

Семейства LibreYOLO портированы из upstream-проектов, чьи опубликованные чекпойнты почти загружаются, но не несут метаданных LibreYOLO. Автоконвертация распознаёт такие файлы, оборачивает их в схему v1.0 и записывает результат рядом с исходником.

Что происходит при загрузке

Когда LibreYOLO() встречает файл .pt, ещё не оформленный как полный чекпойнт v1.0, вызывается автоконвертер, который:

  1. распаковывает словарь тензоров из распространённых upstream-раскладок;
  2. спрашивает каждое зарегистрированное семейство, распознаёт ли оно эту раскладку, переназначая ключи там, где именование в upstream отличается от нативного порта;
  3. оборачивает победителя в строгий чекпойнт с метаданными v1.0, считывая размер, задачу и число классов из самих тензоров, чтобы дообученные чекпойнты конвертировались корректно;
  4. записывает его рядом с исходником под именем <source>-<Prefix><size>[-task].pt и возвращает этот путь, чтобы фабрика загрузила его обычным образом.

От вызывающего кода ничего не требуется. Файл, на который не претендует ни одно семейство, не даёт никакого результата, и фабрика сообщает, что не смогла его загрузить.

Передача файла прямо фабрике
from libreyolo import LibreYOLO # Распознанный upstream-файл конвертируется при загрузке, а# сконвертированный чекпойнт записывается рядом с ним.# model = LibreYOLO("yolov9-t-converted.pt") # Любой чекпойнт LibreYOLO загружается без изменений.model = LibreYOLO("LibreYOLO9t.pt")print(model.family, model.size, model.task, model.nb_classes)

Какие раскладки распаковываются

Словарь тензоров ищется в следующем порядке приоритета, EMA первым, и каждый кандидат проверяется, пока не найдётся тот, что действительно содержит тензоры. Поэтому пустой или состоящий из одних метаданных блок EMA не скрывает валидные веса, лежащие ниже.

КлючПримечание
ema.moduleРаспространённая EMA-обёртка
emaСтарые плоские EMA-обёртки, которые хранят тензоры напрямую
ema_state_dictУ записей с префиксом module. он срезается
params_ema
params
ema_net
net
model
state_dict
Сам файлОбычный state dict

Затем каждый кандидат сужается до записей со значениями-тензорами и нормализуется: ведущий префикс module. или _orig_mod. срезается, а у словаря, все ключи которого начинаются с model.model., этот префикс убирается.

Какие семейства что распознают

Распознавание — classmethod на уровне семейства. Реализация по умолчанию претендует на раскладку, ключи которой уже совпадают с нативным портом. Семейство, у которого именование ключей в upstream отличается, переопределяет её переназначением ключей и возвращает пустой результат для раскладок, которые не распознаёт.

Семейства, у которых есть распознаватель с переназначением: centernet, deeplabv3, deformable_detr, dexined, moge2, picodet, rtdetr, rtdetrv2, rtdetrv4, rtmdet, segformer, swin, teed, yolo7, yolo9, yolo9_e2e, yolo9_p2.

Семейства, которые отказываются от автоконвертации сразу: efficientdet, eomt и pidnet возвращают из распознавателя пустой результат, поэтому их upstream-файлы проходят через скрипт конвертации. l2cs исключён из общего распознавателя, потому что предназначен только для инференса, а распространение его весов ограничено.

У RF-DETR собственный распознаватель, потому что для определения размера и переназначения классов COCO ему нужен весь чекпойнт, а не только словарь тензоров. Он регистрируется, только если установлены его необязательные зависимости.

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

Какое семейство побеждает

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

Заявка подкласса перевешивает заявку его базового класса. Порядок регистрации следует порядку создания классов, поэтому производное семейство регистрируется после базового, которое оно уточняет, и его положительные маркеры не должны проигрывать более широкой сквозной заявке (passthrough) базового семейства.

Дальше решает порядок в реестре, потому что он кодирует специфичность: самая ранняя заявка — самое специфичное совпадение.

Единственная ничья, которую порядок в реестре разрешить не может, — DEIM против D-FINE: ключи архитектуры у них одинаковые. Там, и только там, решающий сигнал — имя файла, и файл, чьё имя ничего не подсказывает, отклоняется, а не угадывается. Больше нигде имя файла намеренно не учитывается, поэтому широкая ложноположительная заявка никогда не сможет обойти более специфичную только за счёт того, как назван файл.

Безопасная загрузка

Upstream-файлы загружаются через unpickler в режиме weights-only. Некоторые upstream-чекпойнты обучения содержат объекты библиотек, которые этот unpickler отвергает. Такие объекты — метаданные обучения, а не веса, поэтому каждый заблокированный global повторно обрабатывается с инертным классом-заглушкой, который устраивает unpickler и при этом ничего не выполняет. Перехваченное имя используется только как строковая метка: оно никогда не импортируется, не вычисляется и не вызывается.

Чувствительные имена модулей отклоняются сразу и никогда не заменяются заглушкой: builtins, os, sys, posix, nt и subprocess. Цикл повторов ограничен 32 попытками, поэтому файл, сконструированный так, чтобы подставлять неограниченную серию разных globals, завершается отказом, а не зацикливается. В сконвертированный чекпойнт попадают только тензоры.

Куда попадает сконвертированный файл

Результат записывается рядом с исходником под именем <source>-<Prefix><size>[-task].pt. Он всегда перезаписывается, а не переиспользуется: так повторные загрузки одного и того же исходника остаются актуальными и при этом не возникает коллизий с официальными весами или с другим дообучением того же семейства, размера и задачи в том же каталоге.

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

Существующие чекпойнты LibreYOLO

Файл с маркером, специфичным для LibreYOLO, — libreyolo_version или model_family — идёт по обычному пути загрузки и заново не конвертируется. Пропуск применяется только к сквозной заявке, то есть такой, где набор ключей не изменился. Заявка, конвертация которой изменила набор ключей, — доказательство чужой upstream-раскладки, и она принимается даже на файле с маркером.

schema_version намеренно не считается маркером, потому что это общее имя используют и другие инструменты обучения и экспорта; не считаются маркерами и names, nc, size, task или imgsz, потому что их может нести и upstream-дообучение. Поэтому чужое дообучение, у которого есть лишь общий ключ names, маркером не помечено: его заявка по нативным ключам конвертируется обычным образом и берёт число классов из тензоров головы, а не загружается ошибочно как 80 классов.

Метаданные, которые читает конвертер

Имена классов берутся из ключа names верхнего уровня или из class_names внутри блока args или hyper_parameters. Отображение имён, ключами которого служат метки, а не индексы классов, использовать нельзя, и оно заменяется сгенерированными значениями по умолчанию. Список имён, который длиннее обнаруженного числа классов, обрезается, потому что индексы вне диапазона не прошли бы строгую валидацию и молча прервали бы конвертацию.

Upstream-блок args переносится как обычные метаданные, причём любое значение, которое не строка, число, булево, список или словарь, отбрасывается, так что в сохранённый файл не попадает ничего небезопасного.

Нормализация COCO для RF-DETR

Upstream-чекпойнты RF-DETR содержат голову классификации на 91 выход — это 90 классов COCO плюс фон. Автоконвертация приводит COCO-версию RF-DETR к соглашению COCO-80, а переназначение применяется на постобработке.

Чекпойнт считается COCO, если он несёт ровно 80 имён, или объявляет число классов 80, или имеет подсказку датасета coco, или вообще не имеет метаданных о классах и датасете. Последний случай важен: голый upstream state dict — это канонический предобученный на COCO чекпойнт, и это единственный распространяемый RF-DETR на 91 выход без метаданных.

Настоящий пользовательский RF-DETR на 90 классов сохраняется как 90 классов. Он опознаётся по списку имён, явному числу классов, отличному от 80, или подсказке датасета, отличной от COCO, поэтому запасной путь для голого чекпойнта на нём не срабатывает. Пустые значения-заполнители не учитываются при решении о том, есть ли подсказка датасета.

Ограничения

Автоконвертация распознаёт опубликованные upstream-раскладки. Она не переписывает архитектуру и не делает загружаемой непортированную модель. Когда на файл не претендует ни одно семейство, ответ — скрипт конвертации, а не аргумент фабрики: в репозитории есть weights/convert_*.py для тех семейств, которым он нужен, включая EoMT, PIDNet и EfficientDet.

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

Поведение считано из libreyolo/models/autoconvert.py и BaseModel.convert_upstream_state_dict; распознаватели по семействам проверены чтением переопределения convert_upstream_state_dict в каждом семействе, всё на версии 1.5.0. Правила COCO для RF-DETR из docs/checkpoint_schema.md.