ncnn

ncnn 是腾讯面向移动端的 CPU 推理库。LibreYOLO 通过 PNNX 转换,写出一份 model.ncnn.param 图文件,旁边放上 model.ncnn.bin 权重文件,以及一个记录家族、任务和类别名的 metadata.yaml。

参数
export(format="ncnn")
输出
一个目录,里面有 model.ncnn.param、model.ncnn.bin 和 metadata.yaml
额外依赖
pip install "libreyolo[ncnn]"
重新加载方式
LibreYOLO("weights/LibreYOLO9t_ncnn")
形状
固定。无论标志怎么设置,元数据都记录 dynamic=False。
精度
仅 FP32。half=True 和 int8=True 会被拒绝。

安装

安装
# pnnx 负责转换,ncnn 负责运行转换结果pip install "libreyolo[ncnn]"

这个额外依赖会把工具链的两半都装上:pnnx 完成转换,ncnn 执行转换结果。主路径上两者都不经过 ONNX。

导出

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # 写出目录 weights/LibreYOLO9t_ncnnpath = model.export(format="ncnn", imgsz=640)print(path)
CLI
libreyolo export --model LibreYOLO9t.pt --format ncnn --imgsz 640
参数
model.export(    format="ncnn",    imgsz=640,        # int,或 (height, width)    batch=1,    simplify=True,    # 只作用于 ONNX 回退路径    opset=None,       # 自动;只作用于 ONNX 回退路径    output_path=None, # None 表示写入 weights/<stem>_ncnn) # half=True 和 int8=True 会在校验阶段被拒绝

产物是一个目录。weights/LibreYOLO9t_ncnn 里放着 model.ncnn.parammodel.ncnn.binmetadata.yaml;这三个文件是同一份产物,要一起移动。

转换会先尝试直接从 PyTorch 走 PNNX。如果失败,就把一张静态 ONNX 图导出到临时目录,再对它调用 pnnx 命令行工具,只有两条路径都失败时导出才会报错,并把两个错误一起报出来。因此 opsetsimplify 只影响回退路径。

YOLOX 需要一处改写才能转换成功。它的 Focus 层用的是带步长的切片,PNNX 无法把它下降(lower)为底层算子,所以导出会把它换成 pixel_unshuffle,并对后一层卷积的输入通道做置换,以补偿通道顺序的差异。输出在数值上完全相同,导出结束后原始权重会被恢复。

运行产物

通过 LibreYOLO
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("weights/LibreYOLO9t_ncnn")result = model.predict(SAMPLE_IMAGE)print(result.boxes.xyxy[:3])
裸 ncnn
import ncnnimport numpy as npimport yaml directory = "weights/LibreYOLO9t_ncnn"net = ncnn.Net()net.load_param(f"{directory}/model.ncnn.param")net.load_model(f"{directory}/model.ncnn.bin") # ncnn 接受单张 CHW 图像,而不是一个批次mat_in = ncnn.Mat(np.zeros((3, 640, 640), dtype=np.float32))extractor = net.create_extractor()extractor.input("in0", mat_in)ret, mat_out = extractor.extract("out0")print(ret, np.array(mat_out).shape) meta = yaml.safe_load(open(f"{directory}/metadata.yaml"))print(meta["model_family"], meta["task"], meta["names"]) # 这条路径上的预处理和后处理由你自己负责

LibreYOLO() 能识别任何含有 model.ncnn.parammodel.ncnn.bin 的目录,读取 metadata.yaml,并返回与检查点(checkpoint)相同的 Results 对象。

第二段代码是裸运行时路径,其中有两处细节和这里其他所有格式都不一样。ncnn 处理的是单张 CHW 图像而不是一个批次,所以没有前面那根批次轴。Blob 名字来自 .param 文件;PNNX 按惯例写的是 in0out0,而后端会去解析这个文件,而不是直接假定名字就是这两个。这条路径上的预处理、解码、NMS 和坐标缩放都由你自己负责。

约束

FP32,固定画布。half=Trueint8=True 在校验阶段都会被拒绝,而且不管标志怎么设置,导出的元数据都记录 dynamic=False,这样就不会有后端去假定一根图里根本没有的轴。

所有 DETR 风格的家族都会在预检阶段被拒绝:detrdeformable_detrdinodetrdfinelwdetrdeimdeimv2rtdetrrtdetrv2rtdetrv4rfdetrec。它们的提示信息都一样,说模型需要 ncnn 里没有的解码器或采样算子,并转而指向 ONNX、OpenVINO、TorchScript 或 TensorRT。

卷积一侧能转换的范围很广:YOLO9 和 YOLO9-E2E、YOLOX、PicoDet、YOLO-NAS 的检测和姿态、更早的 YOLO1、YOLO3、YOLO4 和 YOLO7 检测器、四个 CNN 分类家族、PIDNet 语义分割、固定 96×96 的 FOMO 点检测、ZipDepth、NAFNet 和 Real-ESRGAN。

被屏蔽的条目会写明具体的失败原因。transformer 图通常会留下不受支持的 pnnx.Expression 节点,产生一个没有可运行输入 blob 的网络,DINOv2、CLIP、SigLIP2 和 SegFormer 就是卡在这里。BiRefNet 需要 torchvision 的可变形卷积,PNNX 同样无法下降。YOLO2 转换出来的图在 Windows 上会在输出提取时触发原生的整数除零,直接终止 ncnn 运行时。

完整的家族与任务对照表见导出矩阵。若只想查某一个组合:

导出前先检查某个家族和任务
libreyolo formats --family yolo9 --task detect

依据 dev 分支上的 libreyolo/export/ncnn.py、libreyolo/export/exporter.py、libreyolo/export/support.py、libreyolo/backends/ncnn.py 和 pyproject.toml 校对。