Ultralytics YOLO ExecuTorchBackend 详解:加载 .pte 模型并在移动端/边缘设备运行推理
【免费下载链接】ultralyticsUltralytics YOLO26, YOLO11, YOLOv8 — object detection, instance segmentation, semantic segmentation, image classification, pose estimation, object tracking项目地址: https://gitcode.com/GitHub_Trending/ul/ultralytics
Ultralytics 的ExecuTorchBackend(ultralytics/nn/backends/executorch.py)是框架面向 Meta ExecuTorch 的推理后端,负责加载.pte(PyTorch ExecuTorch Program)模型文件,并通过 ExecuTorch Runtime 完成在设备上的前向推理。读完本文,你能理解该后端在AutoBackend统一调度体系中的定位、load_model与forward两个核心方法的源码级行为、元数据(metadata.yaml)的读取与生效机制,以及如何通过yolo export format=executorch产出它所消费的模型工件,并在 Python/CLI 中直接用它完成预测与验证。
后端定位:BaseBackend 体系中的一员
ExecuTorchBackend继承自所有推理后端的抽象基类BaseBackend(ultralytics/nn/backends/base.py),并在后端包中统一导出(ultralytics/nn/backends/init.py)。它遵循基类定义的契约:
- 构造函数(base.py#L84-L105)先初始化一组公共属性——
device、fp16、nhwc、stride(默认 32)、names、task、batch(默认 1)、channels(默认 3)、end2end、dynamic、metadata等——然后立即调用子类实现的load_model(weight)完成模型加载; - 子类只需实现两个抽象方法:
load_model(加载权重)与forward(执行推理),并可通过__call__被直接当作可调用对象使用; BaseBackend还提供read_metadata与apply_metadata两个通用工具方法,供各后端复用(详见下文“元数据机制”)。
在统一调度层,AutoBackend通过_BACKEND_MAP将模型格式映射到具体后端(ultralytics/nn/autobackend.py#L152-L177),其中注册了"executorch": ExecuTorchBackend。按AutoBackend的格式表,ExecuTorch 模型的文件后缀约定为*.pte(autobackend.py#L104-L128)。因此当你用YOLO("yolo26n_executorch_model")或yolo predict model=yolo26n_executorch_model加载一个.pte目录/文件时,框架会自动路由到ExecuTorchBackend,无需手动选择引擎。
有两个与设备相关的默认行为值得注意,均写在AutoBackend.__init__中:
- FP16 不生效:
fp16 &= format in {"pt", "torchscript", "onnx", "openvino", "engine", "triton"}(autobackend.py#L214-L215)。ExecuTorch 不在白名单内,即导出为固定 FP32 推理(与导出侧的约束一致,见下文); - 强制 CPU 设备:对于不在
{"pt", "torchscript", "engine", "onnx", "paddle"}中的格式,若用户传入非 CPU 设备会被改写为torch.device("cpu")(autobackend.py#L218-L224)。ExecuTorch 后端因此总是在 CPU 上运行,这也符合 XNNPACK 作为 CPU 优化后端的定位。
load_model:从路径到可执行前向方法
ExecuTorchBackend.load_model(executorch.py#L22-L36)的完整逻辑如下:
def load_model(self, weight: str | Path) -> None: LOGGER.info(f"Loading {weight} for ExecuTorch inference...") check_executorch_requirements() from executorch.runtime import Runtime w = Path(weight) program = Runtime.get().load_program(str(next(w.rglob("*.pte")) if w.is_dir() else w)) self.model = program.load_method("forward") self.apply_metadata(self.read_metadata(w))逐步解析:
- 依赖自检:调用
check_executorch_requirements()(ultralytics/utils/checks.py#L651-L657)。该函数会先检查平台特例——在 Linux ARM64 的 Docker 环境中确保packaging>=22.0(源码注释标明这是修复 arm64 Docker 下 executorch 构建问题的已知 bug)——然后执行check_requirements("executorch", cmds=f"torch=={TORCH_VERSION}"),即用当前已安装的 PyTorch 精确版本作为安装约束来补齐executorch包,保证两者版本对齐。 - 惰性导入 Runtime:
from executorch.runtime import Runtime放在函数体内,意味着只有真正走到 ExecuTorch 推理时才会要求executorch包存在,其他后端的用户不受影响。 - 模型路径解析:
weight支持两种形态——直接指向.pte文件,或指向包含.pte的目录(导出产物*_executorch_model/就是目录形态)。若w.is_dir()为真,用w.rglob("*.pte")递归查找并取第一个.pte文件;否则直接使用w。这是它兼容“裸.pte文件”与“带元数据的目录包”两种工件的关键。 - 绑定前向方法:
Runtime.get()获取全局运行时单例,load_program(...)加载程序得到program,再program.load_method("forward")取出名为forward的方法,保存为self.model。也就是说,导出的模型必须暴露forward方法名,这与导出侧用示例输入torch.export.export(model, (im,))追踪的行为一致。 - 应用元数据:
self.apply_metadata(self.read_metadata(w))从模型旁读取元数据并落到实例属性上(下一节展开)。
forward:输入输出契约
forward实现非常短(executorch.py#L38-L47):
def forward(self, im: torch.Tensor) -> list: return self.model.execute([im])其契约(与BaseBackend的抽象定义一致,base.py#L116-L126)是:
- 输入:
im为 BCHW(NCHW)格式的torch.Tensor,像素值已归一化到[0, 1]区间; - 执行方式:将输入包装成单元素列表
[im]传给 ExecuTorch 的EValue列表执行接口self.model.execute(...),与.pte程序forward方法的单输入签名对应; - 输出:返回模型原始预测值组成的list(ExecuTorch 输出的 EValue 序列)。检测/分割等任务的原始输出(如
[1, 11, 8400]这类张量)随后由框架上游的Predictor/Validator统一做后处理(解码、NMS 等),后端本身不做任务级解释——这是所有BaseBackend子类的共同约定。
由于该后端输出的是未经 NMS 的原始张量,配合框架内置的后处理,yolo predict/yolo val的输出结果与 PyTorch 推理路径保持一致的接口(Results对象)。
元数据机制:read_metadata 与 apply_metadata
ExecuTorchBackend没有自定义元数据逻辑,直接复用基类的两个静态/实例方法:
BaseBackend.read_metadata(file)(base.py#L152-L192)按导出格式选择读取策略:.engine读长度前缀 JSON 头、.tflite/.torchscript读 zip 内条目、.onnx/.mlpackage解析 protobuf 字符串映射,而其余所有格式(包括 ExecuTorch 目录)统一读取导出物旁(或导出物内部)的metadata.yaml侧车文件;任何解析失败(第三方导出、文件截断、无元数据)都安全地返回空 dict,不会让推理失败。这正对应导出侧在模型目录中写入metadata.yaml的行为(见下文)。
BaseBackend.apply_metadata(metadata)(base.py#L194-L222)负责把元数据字段转换成正确类型并写回实例属性:
stride、batch、channels转为int;imgsz、names、kpt_shape、kpt_names、args、end2end若为字符串则用ast.literal_eval还原为 Python 对象;end2end会额外参考args中的nms标志(metadata.get("end2end", False) or metadata.get("args", {}).get("nms", False)),dynamic从args中解析;- 最后将所有键值对
setattr到后端实例上,例如加载后backend.names、backend.imgsz、backend.task即可直接用于下游后处理与绘图。
若元数据中未提供类名,AutoBackend还有兜底:self.backend.names = default_class_names(data)(autobackend.py#L266-L269),保证第三方.pte(没有metadata.yaml)也能以默认 COCO 类名运行。
工件来源:export 链路如何产出 .pte
ExecuTorchBackend消费的典型工件是yolo export format=executorch生成的目录。整条导出链路(ultralytics/engine/exporter.py + ultralytics/utils/export/executorch.py)值得与后端对照来看:
1. 格式注册与参数约束(exporter.py#L220):
["ExecuTorch", "executorch", "_executorch_model", True, False, ["batch"], "executorch"],即:format=executorch生成后缀_executorch_model/的目录;CPU 支持为True、GPU 为False(导出后的模型只能 CPU 推理);支持透传的导出参数只有batch——imgsz、device等基础参数仍可用,但不接受quantize/fraction等精度转换参数。
2. 精度与 end2end 限制。导出前检查中,executorch与 rknn、ncnn、paddle、imx、edgetpu、qnn 同组被强制关闭端到端 NMS 分支(exporter.py#L681-L684),源码注释说明原因是该格式缺少 top-k 算子支持:
if fmt in {"rknn", "ncnn", "executorch", "paddle", "imx", "edgetpu", "qnn"}: # Disable the end2end branch for formats without top-k support model.end2end = False LOGGER.warning("This export format does not support end2end models, disabling the end2end branch.")同时export_executorch入口有硬性版本断言(exporter.py#L1487-L1495):
def export_executorch(self, prefix=colorstr("ExecuTorch:")): """Export YOLO model to ExecuTorch *.pte format.""" assert TORCH_2_9, f"ExecuTorch requires torch>=2.9.0 but torch=={TORCH_VERSION} is installed" from ultralytics.utils.export.executorch import torch2executorch return torch2executorch( model=..., im=..., output_dir=str(self.file).replace(self.file.suffix, "_executorch_model/"), ... )即要求 PyTorch >= 2.9.0(torch.export是整条链路的依赖),输出目录为原权重名替换后缀为_executorch_model/。
3. 核心转换函数torch2executorch(utils/export/executorch.py#L41-L80):
et_program = to_edge_transform_and_lower( torch.export.export(model, (im,)), partitioner=[XnnpackPartitioner()], ).to_executorch() pte_file.write_bytes(et_program.buffer) if metadata is not None: YAML.save(output_dir / "metadata.yaml", metadata)流程为:先torch.export.export用示例输入张量im(其 batch 维度即batch参数值)追踪模型 →to_edge_transform_and_lower以XnnpackPartitioner()做算子分区并降级为 EdgeIR 可执行 →to_executorch()生成程序缓冲区写入model.pte→ 将imgsz、names、stride、batch、task、args等元数据以 YAML 形式写入同目录的metadata.yaml。这正是后端read_metadata读取的侧车文件,形成“导出写、推理读”的闭环。
4. Pose 模型的 XNNPACK 兼容性补丁。导出前框架会调用executorch_wrapper(exporter.py#L869-L872 注册,实现在 utils/export/executorch.py#L15-L38):遍历模型所有模块,把Pose/Pose26头部的kpts_decode替换为_executorch_kpts_decode。该替换版将 2D 的anchors/strides显式扩展成 4D(self.anchors[None, None]),源码注释说明原因是“XNNPACK requires explicit dim matching for broadcasting”——即 XNNPACK 后端要求广播操作各维度显式对齐。这也是姿态估计模型能顺利走 XNNPACK 分区的关键细节。
5. 输出目录结构:
yolo26n_executorch_model/ ├── model.pte # ExecuTorch 模型文件(Runtime 加载对象) └── metadata.yaml # 模型元数据(imgsz、names、stride、task、args 等)后端加载目录时rglob("*.pte")找到model.pte,read_metadata找到metadata.yaml,两者各司其职。
实操:导出、预测与验证
环境要求(以仓库文档与源码为准):
| 项目 | 要求 | 依据 |
|---|---|---|
| Python | 3.10–3.13 | docs/en/integrations/executorch.md “Installation” 一节 |
| PyTorch | >= 2.9.0 | exporter.py#L1489 的assert TORCH_2_9 |
| executorch 包 | 按需自动安装,torch 版本与已装 torch 精确对齐 | checks.py#L651-L657 |
| 推理设备 | 仅 CPU | autobackend.py#L218-L224 设备改写逻辑 |
| 精度 | 固定 FP32 | autobackend.py#L215 fp16 白名单 |
Python API 三步走:
from ultralytics import YOLO # 1. 导出:生成 yolo26n_executorch_model/(含 model.pte + metadata.yaml) model = YOLO("yolo26n.pt") model.export(format="executorch") # 等价于 CLI: yolo export model=yolo26n.pt format=executorch # 2. 预测:直接把导出目录传给 YOLO(),AutoBackend 自动路由到 ExecuTorchBackend model = YOLO("yolo26n_executorch_model") results = model("bus.jpg") # 或 yolo predict model=yolo26n_executorch_model source=bus.jpg # 3. 验证:在 COCO8 上核对精度 metrics = model.val(data="coco8.yaml") # 或 yolo val model=yolo26n_executorch_model data=coco8.yaml导出时可用的常用参数(完整清单见 docs/en/integrations/executorch.md 的参数表):
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
format | str | 'executorch' | 目标导出格式 |
imgsz | int/tuple | 640 | 输入尺寸,导出时以该尺寸构造示例输入张量 |
batch | int | 1 | 导出模型的批量大小(该格式唯一可透传的功能参数) |
device | str | None | 导出使用的设备(0/cpu/mps),仅影响导出过程 |
quantize | int/str | None | 无效——ExecuTorch 导出为固定 FP32,不支持导出期 FP16/INT8/W8A16 |
边缘端部署路径:.pte文件本身可直接交给移动/嵌入式端的 ExecuTorch Runtime(iOS/Android 的Module.load+forward调用,或嵌入式 Linux 的 C++ModuleAPI),输入同样要求[1, 3, H, W]、[0, 1]归一化,与ExecuTorchBackend.forward的契约一致;更完整的集成步骤见仓库内的 docs/en/integrations/executorch.md。
实测参考:Raspberry Pi 5 上的 PyTorch 与 ExecuTorch 对比
仓库集成文档(docs/en/integrations/executorch.md,使用 Ultralytics 8.4.9 基准测试,推理时间不含前后处理)给出了 ExecuTorch 后端在树莓派 5 上的表现参考:
| 模型 | 格式 | 体积 (MB) | mAP50-95 (B) | 推理耗时 (ms/im) |
|---|---|---|---|---|
| YOLO26n | PyTorch | 5.3 | 0.4790 | 314.80 |
| YOLO26n | ExecuTorch | 9.4 | 0.4800 | 142.0 |
| YOLO26s | PyTorch | 19.5 | 0.5730 | 930.90 |
| YOLO26s | ExecuTorch | 36.5 | 0.5780 | 376.1 |
数据表明:走ExecuTorchBackend的 XNNPACK 推理在精度上与 PyTorch 基本持平的前提下,单帧耗时显著低于设备上的原生 PyTorch 解释执行;代价是.pte文件体积大于.pt权重。该数据来自仓库文档的实测记录,复测结果会随具体设备与软件版本浮动。
小结:关键事实速查
ExecuTorchBackend位于 ultralytics/nn/backends/executorch.py,继承 BaseBackend,经AutoBackend._BACKEND_MAP注册,由*.pte后缀/_executorch_model目录自动触发;load_model支持“裸.pte文件”或“含.pte的目录”两种输入,内部走Runtime.get().load_program(...)+load_method("forward"),并自动做依赖检查(check_executorch_requirements)与元数据应用;forward的输入契约是 BCHW、[0, 1]归一化张量,输出为原始预测列表,任务级后处理由框架上游完成;- 工件由
yolo export format=executorch生成:torch2executorch用torch.export+ XNNPACK 分区器产出model.pte,同目录写metadata.yaml;Pose 头会被打上 XNNPACK 广播兼容补丁; - 明确的限制:仅 CPU 推理、固定 FP32、
batch是唯一可透传的功能参数、end2end NMS 分支被禁用(无 top-k 支持)、要求 PyTorch >= 2.9.0 且 Python 3.10–3.13。
【免费下载链接】ultralyticsUltralytics YOLO26, YOLO11, YOLOv8 — object detection, instance segmentation, semantic segmentation, image classification, pose estimation, object tracking项目地址: https://gitcode.com/GitHub_Trending/ul/ultralytics
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考