ONNX

ONNX est un format de graphe portable. LibreYOLO trace le modèle avec torch.onnx.export, simplifie éventuellement le graphe, et écrit la famille, la tâche, les noms de classes et la taille d'entrée dans les métadonnées du fichier lui-même, de sorte que n'importe quel backend LibreYOLO peut reconstruire le postprocessing.

Flag
export(format="onnx")
Écrit
Un seul fichier .onnx, métadonnées embarquées dans le graphe
Extra
pip install "libreyolo[onnx]"
Se recharge avec
LibreYOLO("weights/LibreYOLO9t.onnx")
Formes
Batch dynamique par défaut en Python ; exceptions par tâche ci-dessous
Précision
FP32, FP16 (half=True), INT8 (int8=True, détection YOLO9)

Installation

Installation
pip install "libreyolo[onnx]"

L'extra installe onnx, onnxsim et onnxruntime. onnx seul suffit à écrire le fichier ; onnxsim exécute la passe de simplification, et onnxruntime exécute l'artefact et réalise la calibration INT8.

Export

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # Écrit weights/LibreYOLO9t.onnxpath = model.export(format="onnx")print(path)
CLI
libreyolo export --model LibreYOLO9t.pt --format onnx
Arguments
model.export(    format="onnx",    imgsz=640,        # int, ou (hauteur, largeur)    batch=1,    dynamic=True,     # défaut Python, la CLI vaut False    simplify=True,    # passe onnxsim sur le graphe    opset=None,       # None choisit 13, ou 17 pour les familles DETR    half=False,       # poids et activations FP16    int8=False,       # INT8 QDQ, détection YOLO9 uniquement    data=None,        # data.yaml de calibration, INT8 uniquement    device=None,      # device de trace, None = celui du modèle    output_path=None, # None écrit weights/<stem>.onnx)

Sans output_path, le fichier atterrit dans weights/ sous le nom de base du checkpoint, avec _fp16 ou _int8 ajouté quand cette précision a été demandée.

dynamic vaut True par défaut en Python et False sur la CLI. Quand il est actif, l'axe batch devient symbolique et quelques tâches vont plus loin : la segmentation sémantique ouvre aussi la hauteur et la largeur du masque, la restauration Real-ESRGAN ouvre les axes spatiaux, et les détecteurs à deux étages gardent la hauteur et la largeur source dynamiques parce que leur redimensionnement se fait à l'intérieur du graphe.

opset est choisi selon la famille quand il est omis. Les familles de type DETR (detr, deformable_detr, dinodetr, dfine, deim, deimv2, ec, lwdetr, rfdetr, rtdetr, rtdetrv2, rtdetrv4) plus deit, midas et moge2 reçoivent l'opset 17, celui où aten::scaled_dot_product s'abaisse. Tout le reste reçoit 13. Le matting passe à 19 quoi qu'il arrive, parce que le décodeur de BiRefNet a besoin de l'opérateur DeformConv, qu'ONNX définit à partir de l'opset 19.

simplify=True exécute onnxsim et conserve le graphe d'origine si la passe échoue, si bien qu'une erreur de simplification est un avertissement plutôt qu'un échec d'export. Sur macOS arm64 avec onnx 1.22 ou plus récent et onnxsim 0.6.5 ou plus ancien, la passe est entièrement sautée, parce que cette combinaison peut faire planter le processus Python.

NMS embarqué

Embarquer le NMS dans le graphe
from libreyolo import LibreYOLO # Détection YOLO9 uniquement, batch 1. dynamic est forcé à False.LibreYOLO("LibreYOLO9t.pt").export(    format="onnx",    nms=True,    conf=0.25,    iou=0.45,    max_det=300,)
CLI
libreyolo export --model LibreYOLO9t.pt --format onnx --nms \  --conf 0.25 --iou 0.45 --max-det 300

nms=True ne concerne que la détection YOLO9 et exige batch 1 ; le demander avec dynamic=True produit un avertissement et désactive dynamic. Le graphe a alors deux sorties : output, de forme (batch, max_det, 6), et raw, le tenseur détecteur non décodé qu'utilise le backend propre à LibreYOLO pour que le postprocessing reste identique au chemin PyTorch.

DeepStream

deepstream=True est une option propre à ONNX. Elle exporte le graphe dans la disposition qu'attend le parser de NVIDIA DeepStream et écrit deux fichiers annexes à côté, config_infer_primary_<stem>.txt et <stem>_labels.txt, pour que l'artefact s'insère dans un pipeline sans configuration écrite à la main.

Elle est mutuellement exclusive avec nms=True, et demander les deux lève une ValueError : DeepStream effectue la suppression dans son propre étage de clustering. La passer à un format autre qu'ONNX lève également. Voir DeepStream pour la grille des familles et des tâches prises en charge et pour la compilation du parser.

INT8

INT8 avec données de calibration
from libreyolo import LibreYOLO LibreYOLO("LibreYOLO9t.pt").export(    format="onnx",    int8=True,    data="coco128.yaml",   # quelques centaines d'images représentatives    fraction=1.0,)

int8=True exécute la quantification statique d'ONNX Runtime et écrit un graphe QDQ dont les entrées et les sorties sont en float32. Seuls les nœuds Conv et Gemm sont quantifiés. Laisser le décodage de la tête de détection en float32 est délibéré : cette concaténation mélange des coordonnées de boîtes à l'échelle du pixel avec des scores de classe dans la plage 0 à 1, et une unique échelle d'activation par tenseur dominée par la magnitude des boîtes ramènerait tous les scores à zéro.

Ce flag ne s'applique pour l'instant qu'à la détection YOLO9, et tout le reste lève NotImplementedError au préflight. Omettre data retombe sur coco8.yaml avec un avertissement ; huit images ne forment pas un jeu de calibration représentatif. Un modèle déjà quantifié dans PyTorch suit un autre chemin, décrit sur Quantification.

Exécuter l'artefact

Via LibreYOLO
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("weights/LibreYOLO9t.onnx")result = model.predict(SAMPLE_IMAGE)print(result.boxes.xyxy[:3])
ONNX Runtime seul
import numpy as npimport onnximport onnxruntime as ort session = ort.InferenceSession(    "weights/LibreYOLO9t.onnx",    providers=["CPUExecutionProvider"],) # Le prétraitement et le postprocessing sont à votre charge ici.batch = np.zeros((1, 3, 640, 640), dtype=np.float32)outputs = session.run(None, {session.get_inputs()[0].name: batch})print([out.shape for out in outputs]) # Le graphe porte la famille, la tâche, les classes et la taille.meta = {p.key: p.value for p in onnx.load("weights/LibreYOLO9t.onnx").metadata_props}print(meta["model_family"], meta["task"], meta["imgsz"])

LibreYOLO() aiguille sur le suffixe .onnx et renvoie le même objet Results qu'un checkpoint .pt, parce que les noms de classes, la tâche, la taille d'entrée et le schéma de pose ont été écrits dans les metadata_props du graphe au moment de l'export. Avec device="auto", la session prend CUDAExecutionProvider quand ONNX Runtime le signale, et retombe sur le CPU sinon.

Le second snippet s'adresse aux lecteurs qui n'ont pas LibreYOLO installé. Le prétraitement, le décodage, le NMS et la remise à l'échelle des coordonnées sont alors à votre charge ; le bloc de métadonnées reste là, prêt à être lu.

Contraintes

Les noms des tenseurs de sortie sont fixés par tâche, et c'est à eux que doit correspondre un consommateur sans métadonnées :

TâcheNoms de sortie
Détection, têtes à grille et à ancresoutput
Détection, type DETRpred_logits, pred_boxes
Détection, RF-DETRdets, labels
Classificationoutput
Segmentation sémantiquesemantic_logits
Profondeurdepth
Normales de surfacenormal
Contoursedges
Restaurationrestored
Mattingmatte
Regardyaw_logits, pitch_logits

RF-DETR est aussi la seule famille dont le tenseur d'entrée s'appelle input plutôt que images.

Plusieurs tâches portent un contrat de runtime à résolution fixe dans cette version. La profondeur, les normales de surface et les contours refusent batch != 1 et forcent dynamic=False. Le matting force le carré natif de 1024, parce que les tables de positions relatives du Swin de BiRefNet sont liées à leur résolution. La restauration force un canevas fixe pour toutes les familles sauf Real-ESRGAN, dont le générateur est entièrement convolutif.

Un imgsz rectangulaire fonctionne pour les familles YOLO9, HRNet, NAFNet et Real-ESRGAN. Les familles au contrat carré fixe (clip, deformable_detr, detr, dinodetr, dfine, deim, deimv2, ec, lwdetr, moge2, rtdetr, rtdetrv2, rtdetrv4, rfdetr, siglip2, ssd) le refusent purement et simplement.

Deux combinaisons sont refusées avant le tracing : la segmentation YOLO9, parce que YOLO9 ne fait que de la détection dans LibreYOLO, et la segmentation RTMDet-Ins, dont le décodage de masques à noyaux dynamiques n'a pas de contrat de runtime exporté.

Pour la grille complète des familles et des tâches, voir la matrice d'export. Pour une seule combinaison, interrogez directement la bibliothèque :

Vérifier une famille et une tâche avant d'exporter
libreyolo formats --family yolo9 --task detect

Lu depuis libreyolo/export/onnx.py, libreyolo/export/exporter.py, libreyolo/export/support.py, libreyolo/backends/onnx.py et libreyolo/cli/commands/export.py sur la branche dev.