ExecuTorch
ExecuTorch exécute des programmes PyTorch sur des cibles edge. LibreYOLO capture le modèle avec torch.export en mode strict, fait le lowering vers XNNPACK, et écrit le programme .pte et un sidecar de métadonnées JSON comme un seul ensemble.
- Flag
export(format="executorch")- Écrit
- Un programme .pte plus un sidecar de métadonnées .pte.json
- Extra
pip install "libreyolo[executorch]"- Se recharge avec
LibreYOLO("weights/LibreYOLO9t.pte")- Formes
- Fixes. dynamic=True et batch != 1 sont refusés.
- Précision
- FP32 uniquement. half=True et int8=True sont refusés.
- Délégué
- XNNPACK, CPU. delegate='xnnpack' est la seule valeur acceptée.
Installation
# Volontairement hors de libreyolo[all], car ExecuTorch contraint la# version de Torch avec laquelle il peut être associé.pip install "libreyolo[executorch]"Cet extra est délibérément en dehors de libreyolo[all], parce qu'ExecuTorch fige
la version de Torch avec laquelle il fonctionne et que l'installer entraînerait
tout l'environnement sur ce couple. Installez-le dans un environnement que vous
acceptez de contraindre.
Sous Windows, l'étape de lowering appelle l'exécutable flatc livré avec
ExecuTorch. S'il n'est pas dans le PATH, l'export lève un RuntimeError qui le
signale, et la solution est de lancer depuis une Developer PowerShell de Visual
Studio 2022.
Export
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # Écrit weights/LibreYOLO9t.pte et weights/LibreYOLO9t.pte.jsonpath = model.export(format="executorch", imgsz=640)print(path)libreyolo export --model LibreYOLO9t.pt --format executorch --imgsz 640model.export( format="executorch", imgsz=640, # int, ou (hauteur, largeur) batch=1, # toute autre valeur lève ValueError dynamic=False, # True lève ValueError delegate="xnnpack", # la seule valeur acceptée device="cpu", # tout autre périphérique lève ValueError output_path=None, # None écrit weights/<stem>.pte)La capture est torch.export.export(..., strict=True), c'est-à-dire une vraie
capture de graphe avec des guards plutôt qu'un trace enregistré. Les lectures de
scalaires sur l'hôte et le contrôle de flux dépendant des données sont refusés au
lieu d'être figés en silence, si bien que plusieurs familles échouent ici alors
qu'elles se tracent sans problème ailleurs ; les raisons sont consignées par
combinaison dans la matrice de support.
Le lowering exécute to_edge_transform_and_lower avec le partitioner XNNPACK. Si
le résultat ne contient aucune partition déléguée, l'export lève une erreur plutôt
que d'étiqueter comme XNNPACK un programme qui n'utilise que des kernels portables.
Le programme et le sidecar sont écrits ensemble. Les deux sont préparés, les deux sont mis en place, et un échec revient à ce qui était là avant, si bien qu'une paire incomplète n'atteint jamais le disque.
Exécuter l'artefact
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("weights/LibreYOLO9t.pte")result = model.predict(SAMPLE_IMAGE)print(result.boxes.xyxy[:3])import jsonfrom pathlib import Path import torchfrom executorch.runtime import Runtime runtime = Runtime.get()print(runtime.backend_registry.is_available("XnnpackBackend")) program = runtime.load_program(Path("weights/LibreYOLO9t.pte").read_bytes())method = program.load_method("forward") # Sur ce chemin, le prétraitement et le post-traitement sont à vous.outputs = method.execute((torch.zeros(1, 3, 640, 640),))print([tensor.shape for tensor in outputs]) meta = json.load(open("weights/LibreYOLO9t.pte.json"))print(meta["model_family"], meta["task"], meta["executorch_delegate"])LibreYOLO() s'oriente sur le suffixe .pte et renvoie le même objet Results
que le checkpoint. Le sidecar est obligatoire au chargement : sans
<program>.pte.json, le backend lève FileNotFoundError, parce que le programme
ne porte de lui-même ni noms de classes, ni tâche, ni taille d'entrée. Le backend
vérifie aussi que le runtime installé fournit XnnpackBackend avant de charger, et
lit le programme depuis des octets plutôt que de mapper le fichier, ce qui évite de
garder un verrou de fichier Windows pendant toute la durée de vie du backend.
Le second snippet est le chemin du runtime direct. Le prétraitement, le décodage, le NMS et le redimensionnement des coordonnées y deviennent votre affaire.
Contraintes
Batch 1, forme fixe, FP32, CPU. batch != 1 et dynamic=True lèvent tous deux
ValueError avant que l'export ne modifie quoi que ce soit, half=True et
int8=True sont refusés pendant la validation, et un périphérique autre que le CPU
est rejeté.
delegate accepte "xnnpack" et rien d'autre dans cette version.
Les exports de classification portent deux clés de métadonnées supplémentaires,
crop_pct et interpolation, pour que le runtime puisse reproduire la politique de
redimensionnement et de recadrage central de la famille.
Les entrées bloquées nomment l'échec concret plutôt qu'une catégorie. La détection
et la segmentation D-FINE atteignent une lecture de ContextVar non prise en charge
dans l'attention déformable sous capture strict, et forcer le chemin manuel du
grid-sample sérialise mais échoue ensuite à l'exécution sur un ordre de dimensions
invalide pour un tenseur délégué. DEIM et DEIMv2 se capturent, passent le lowering
et se sérialisent, puis échouent pendant l'exécution. La segmentation sémantique
EoMT échoue sur une expression symbolique dépendante des données dans le chemin des
masques. Le matting BiRefNet se capture en 1024 par 1024 mais n'a pas de variante
out pour torchvision::deform_conv2d. La restauration SwinIR se recharge puis
échoue dans aten::alias_copy.out sur des ordres de dimensions qui ne correspondent
pas.
Pour la grille complète des familles et des tâches, voir la matrice d'export. Pour une seule combinaison :
libreyolo formats --family yolo9 --task detect