ExecuTorch
ExecuTorch runs PyTorch programs on edge targets. LibreYOLO captures the model with torch.export in strict mode, lowers it to XNNPACK, and commits the .pte program together with a JSON metadata sidecar as one unit.
- Flag
export(format="executorch")- Writes
- One .pte program plus a .pte.json metadata sidecar
- Extra
pip install "libreyolo[executorch]"- Loads back
LibreYOLO("weights/LibreYOLO9t.pte")- Shapes
- Fixed. dynamic=True and batch != 1 are rejected.
- Precision
- FP32 only. half=True and int8=True are rejected.
- Delegate
- XNNPACK, CPU. delegate='xnnpack' is the only accepted value.
Install
# Kept out of libreyolo[all] on purpose: ExecuTorch constrains which# Torch version it can be paired with.pip install "libreyolo[executorch]"This extra is deliberately outside libreyolo[all], because ExecuTorch pins which
Torch version it works with and installing it would drag the whole environment
onto that pair. Install it into an environment you are willing to constrain.
On Windows the lowering step calls the flatc executable that ships with
ExecuTorch. If it is not on PATH the export raises a RuntimeError saying so,
and running from a Visual Studio 2022 Developer PowerShell is the fix.
Export
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # Writes weights/LibreYOLO9t.pte and 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, or (height, width) batch=1, # any other value raises ValueError dynamic=False, # True raises ValueError delegate="xnnpack", # the only accepted value device="cpu", # any other device raises ValueError output_path=None, # None writes weights/<stem>.pte)Capture is torch.export.export(..., strict=True), which is a real graph capture
with guards rather than a recorded trace. Host scalar reads and data-dependent
control flow are rejected instead of being silently baked in, so several families
fail here that trace successfully elsewhere; the reasons are recorded per
combination in the support matrix.
Lowering runs to_edge_transform_and_lower with the XNNPACK partitioner. If the
result contains zero delegate partitions the export raises rather than labeling a
portable-kernel-only program as XNNPACK.
The program and the sidecar are committed together. Both are staged, both are swapped in, and a failure rolls back to whatever was there before, so a partial pair never reaches disk.
Run the artifact
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") # Preprocessing and postprocessing are yours on this path.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() dispatches on the .pte suffix and returns the same Results object
as the checkpoint. The sidecar is mandatory on load: without
<program>.pte.json the backend raises FileNotFoundError, because the program
carries no class names, task or input size of its own. The backend also checks
that the installed runtime provides XnnpackBackend before loading, and reads the
program from bytes rather than mapping the file, which avoids holding a Windows
file lock for the backend's lifetime.
The second snippet is the bare-runtime path. Preprocessing, decoding, NMS and coordinate rescaling become yours there.
Constraints
Batch 1, fixed shape, FP32, CPU. batch != 1 and dynamic=True both raise
ValueError before the export mutates anything, half=True and int8=True are
rejected during validation, and a device other than CPU is refused.
delegate accepts "xnnpack" and nothing else in this version.
Classification exports carry two extra metadata keys, crop_pct and
interpolation, so the runtime can reproduce the family's resize and center-crop
policy.
The blocked entries name the concrete failure rather than a category. D-FINE
detection and segmentation reach an unsupported ContextVar read in deformable
attention under strict capture, and forcing the manual grid-sample path serializes
but then fails at run time on an invalid delegated tensor dimension order. DEIM
and DEIMv2 capture, lower and serialize, then fail during execution. EoMT semantic
segmentation fails on a data-dependent symbolic expression in the mask path.
BiRefNet matting captures at 1024 by 1024 but has no out variant for
torchvision::deform_conv2d. SwinIR restoration reloads and then fails in
aten::alias_copy.out on mismatched dimension orders.
For the full family and task grid, see the export matrix. For one combination:
libreyolo formats --family yolo9 --task detect