☰
Ultralytics YOLO ExecuTorchBackend 详解:加载 .pte 模型并在移动端/边缘设备运行推理
2026/11/2 6:57:46 网站建设 项目流程

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))

逐步解析:

  1. 依赖自检:调用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包,保证两者版本对齐。
  2. 惰性导入 Runtime:from executorch.runtime import Runtime放在函数体内,意味着只有真正走到 ExecuTorch 推理时才会要求executorch包存在,其他后端的用户不受影响。
  3. 模型路径解析:weight支持两种形态——直接指向.pte文件,或指向包含.pte的目录(导出产物*_executorch_model/就是目录形态)。若w.is_dir()为真,用w.rglob("*.pte")递归查找并取第一个.pte文件;否则直接使用w。这是它兼容“裸.pte文件”与“带元数据的目录包”两种工件的关键。
  4. 绑定前向方法:Runtime.get()获取全局运行时单例,load_program(...)加载程序得到program,再program.load_method("forward")取出名为forward的方法,保存为self.model。也就是说,导出的模型必须暴露forward方法名,这与导出侧用示例输入torch.export.export(model, (im,))追踪的行为一致。
  5. 应用元数据: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,两者各司其职。

实操:导出、预测与验证

环境要求(以仓库文档与源码为准):

项目要求依据
Python3.10–3.13docs/en/integrations/executorch.md “Installation” 一节
PyTorch>= 2.9.0exporter.py#L1489 的assert TORCH_2_9
executorch 包按需自动安装,torch 版本与已装 torch 精确对齐checks.py#L651-L657
推理设备仅 CPUautobackend.py#L218-L224 设备改写逻辑
精度固定 FP32autobackend.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 的参数表):

参数类型默认说明
formatstr'executorch'目标导出格式
imgszint/tuple640输入尺寸,导出时以该尺寸构造示例输入张量
batchint1导出模型的批量大小(该格式唯一可透传的功能参数)
devicestrNone导出使用的设备(0/cpu/mps),仅影响导出过程
quantizeint/strNone无效——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)
YOLO26nPyTorch5.30.4790314.80
YOLO26nExecuTorch9.40.4800142.0
YOLO26sPyTorch19.50.5730930.90
YOLO26sExecuTorch36.50.5780376.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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询