作者:昇腾实战派

小模型在NPU上的推理部署: 【知识地图】

概述

本文旨在解决开发者在昇腾NPU上使用PaddleX推理动态模型时遇到的问题:目前PaddleX暂不支持在NPU上直接推理动态模型。现有三种实现方式中,onnxruntime在CPU上推理支持动态模式,onnxruntime_cann在NPU上推理仅支持静态模式(也需要适配),而PaddleX虽支持推理om模型,但也仅限于静态模式。实际上,昇腾NPU具备推理动态om模型的能力,可通过昇腾的aisbench推理框架实现。因此,本文通过对PaddleX部分源码进行修改,使其集成aisbench,从而实现在昇腾NPU上对动态模型的高效推理。

PaddleX介绍

https://github.com/PaddlePaddle/PaddleX/blob/release/3.4/README.md

整体架构与设计理念

PaddleX是飞桨(PaddlePaddle)生态下的全流程深度学习开发工具,其设计初衷是打通深度学习模型从数据准备、模型训练、参数调优到最终部署的全链路流程,降低开发者的使用门槛。

PaddleX采用前后端分离的架构设计,由PaddleX Client可视化前端PaddleX Core后端技术内核两个核心部分组成:

  • PaddleX Client:提供图形化操作界面,开发者无需编写代码即可完成深度学习模型的完整开发流程,特别适合快速原型验证和低代码场景。
  • PaddleX Core:开源的后端技术内核,提供统一的任务API,集成了飞桨核心框架、模型库、工具组件(如PaddleHub迁移学习工具、VisualDL可视化工具、PaddleSlim模型压缩工具等),开发者可根据实际需求进行二次开发或直接调用。

这种架构设计既保证了入门阶段的易用性(通过前端界面),又提供了生产阶段所需的灵活性(通过后端API和源码级定制)。

PaddleX Client可视化前端

PaddleX Client采用本地化安装方式,支持Windows、Mac、Linux系统,完全在本地运行,确保数据安全性。其全流程开发包含以下关键步骤:

  1. 数据准备与导入:支持图像分类、目标检测、语义分割、实例分割四种任务类型。开发者需将标注好的数据按规范组织,客户端会自动校验数据格式合规性,并支持按比例划分训练集、验证集和测试集。
  2. 项目创建与参数配置:项目与数据集绑定,一个项目可包含多条任务(不同参数配置的训练运行)。参数配置涵盖三个层面:
    • 模型参数:选择具体的模型结构(如YOLOv3、ResNet50等)
    • 训练参数:设置batch size、学习率等训练超参数
    • 优化策略:配置数据增强、学习率衰减策略等
  3. 训练可视化:集成VisualDL工具,实时监控loss曲线、精度指标等训练过程信息。
  4. 模型评估与发布:在验证集上评估模型效果(混淆矩阵、精度、召回率等),满意后可导出为Inference格式用于部署。

PaddleX Core后端技术内核

统一任务API设计

PaddleX Core的核心设计理念是提供标准化的任务接口,开发者可以通过统一的编程范式调用不同任务类型的模型:

import paddlex as pdx

# 图像分类示例
model = pdx.cls.ResNet50(num_classes=10)
model.train(train_dataset, epochs=50)

# 目标检测示例
model = pdx.det.YOLOv3(num_classes=5)
model.train(train_dataset, epochs=100, batch_size=16)

# 模型导出为Inference格式
model.export(export_dir='output', encrypt=False)

这种统一接口的设计使得开发者无需深入理解每个模型的具体实现细节,只需关注任务类型和数据格式,大幅降低了开发复杂度。

核心组件架构

PaddleX Core集成了飞桨生态的多个核心组件:

组件 功能 在PaddleX中的角色
Paddle Inference 飞桨原生推理引擎 模型训练后的高性能推理后端
PaddleHub 迁移学习工具 预训练模型管理和微调
VisualDL 可视化工具 训练过程监控和指标展示
PaddleSlim 模型压缩工具 量化、剪枝等模型优化
推理模块架构与后端选型

PaddleX的推理模块采用多后端可插拔的设计,通过高性能推理插件(HPI)实现对不同硬件和推理引擎的统一抽象。其推理架构包含以下关键层次:

1. 后端抽象层

PaddleX HPI支持四种推理后端,开发者可根据硬件环境和性能需求灵活选择:

后端引擎 适用硬件 特点
Paddle Inference CPU/GPU/NPU(部分) 对Paddle模型原生支持最好
TensorRT NVIDIA GPU 极致性能优化,支持FP16/INT8
ONNX Runtime CPU/GPU 跨框架兼容性强
OpenVINO Intel CPU CPU场景优化最佳

2. 推理流程

在源码层面,PaddleX的推理模块通过create_pipeline接口统一创建推理流水线:

from paddlex import create_pipeline

# 创建推理流水线
pipeline = create_pipeline(
    pipeline="image_classification",
    device="gpu",
    use_hpip=True  # 启用高性能推理插件
)

# 执行推理
result = pipeline.predict("test.jpg")

首次推理时,HPI会自动进行计算图优化和引擎编译,并缓存优化结果以加速后续推理。

当前推理能力的局限性

根据源码结构和官方文档分析,PaddleX当前的推理能力存在以下特点:

支持的推理路径

  • CPU推理:通过ONNX Runtime后端,完整支持动态输入形状。
  • GPU推理:通过TensorRT或Paddle Inference后端,可通过HPI的动态形状配置支持动态输入。

NPU推理的现状

  • PaddleX通过ONNX Runtime CANN插件支持昇腾NPU推理,但此路径目前仅支持静态形状。
  • PaddleX虽已集成对om模型(昇腾离线模型格式)的推理能力,但仍局限于静态输入。
  • 昇腾NPU硬件本身支持动态om模型推理(可通过 aisbench 推理框架实现),但PaddleX尚未原生集成此能力。

源码层面的扩展点

paddlex/inference/models/ 目录下,每个模型子目录(如 text_detection/text_recognition/object_detection/ 等)都包含一个 predictor.py 文件,该文件负责定义该模型的推理类(通常继承自 base/ 中的基类),并实现了模型加载、输入预处理、推理执行、输出后处理等核心逻辑。

要实现对昇腾动态om模型推理的支持,我们需要在各模型子目录的 predictor.py 中修改推理执行部分,以及在base目录下修改基类(BasePredictor)中的create_static_infer函数,将原有的静态推理路径替换或扩展为调用 aisbench 框架的能力。具体而言:

  • predictor.py 中修改一个分支,当检测到后端为 "om" 且device为npu时,调用 aisbench 的API执行推理。
  • 统一在基类中定义接口,确保所有模型子类的 predictor.py 都能复用该适配逻辑,避免重复修改。

通过这种方式,我们可以为每一类模型(文本检测、文本识别、目标检测等)都赋予动态om模型的推理能力,同时保持PaddleX原有推理架构的清晰与可扩展性。

适配步骤

为了实现 PaddleX 在昇腾 NPU 上通过 aisbench 推理动态 om 模型,我们需要对 PaddleX 的源码进行两处关键修改:一是在模型基类 BasePredictorcreate_static_infer 方法中替换推理引擎为 aisbenchInferSession;二是在各模型子类的 process 方法中调整推理调用方式,以适配 aisbench 的动态推理接口。整个流程分为以下五个步骤。

安装 aisbench 依赖包

首先确保目标环境中已安装昇腾 AI 推理框架 aisbenchaisbench 支持动态形状的 om 模型加载与执行。安装方式请前往 aisbench 官网

注意aisbench 依赖昇腾 CANN 软件栈,请确保 CANN 已正确安装且环境变量已配置(如 LD_LIBRARY_PATH 包含 CANN 的库路径)。

定位关键修改文件

在 PaddleX 源码中,每个模型的推理逻辑(预处理、推理引擎、后处理)都封装在 paddlex/inference/models/ 下对应子目录的 predictor.py 文件中,并且均继承自 BasePredictor 类。因此,需要修改基类中加载模型的函数(create_static_infer)以及各个子类中执行推理的函数(process)。以目标检测模型为例,需要修改的文件路径为:

paddlex/inference/models/base/predictor/base_predictor.py
paddlex/inference/models/object_detection/predictor.py

对于其他任务(如文本检测、文本识别、图像分类等),修改位置类似,均需在其 predictor.py 中的 process 方法进行改动。

修改 create_static_infer 方法,引入 aisbench 推理引擎

原始 create_static_infer 方法中,根据是否启用 HPI 分别返回 PaddleInferHPInfer,此时仅支持静态形状的 om 模型。原始代码如下:

def create_static_infer(self):
    if not self._use_hpip:
        return PaddleInfer(
            self.model_name, self.model_dir, self.MODEL_FILE_PREFIX, self._pp_option
        )
    else:
        return HPInfer(
            self.model_dir,
            self.MODEL_FILE_PREFIX,
            self._hpi_config,
        )

我们需要在此处增加一个判断分支:如果启用了 HPI 且设备为 NPU,则使用 aisbenchInferSession 来加载模型;否则沿用原有逻辑。修改后的代码如下:

def create_static_infer(self):
    if self._use_hpip and self.hpi_config.device_type == "npu":
        from ais_bench.infer.interface import InferSession
        MODEL_PATH_DET = self.model_dir / \
            f"{self.MODEL_FILE_PREFIX}.{self.hpi_config.backend}"
        return InferSession(
                device_id=self.hpi_config.device_id, model_path=str(MODEL_PATH_DET))
    elif not self._use_hpip:
        return PaddleInfer(
            self.model_name, self.model_dir, self.MODEL_FILE_PREFIX, self._pp_option
        )
    else:
        return HPInfer(
            self.model_dir,
            self.MODEL_FILE_PREFIX,
            self._hpi_config,
        )

关键点说明

  • self._use_hpipself.hpi_config 由上层 create_pipelinecreate_predictor 传入,可通过配置 use_hpip=Truehpi_config={"backend": "om", "device_type": "npu"} 来触发该分支。
  • InferSessionaisbench 提供的推理会话类,支持动态形状输入,加载 om 模型后可直接调用 infer() 方法进行推理。
  • 模型路径 model_path 需要根据实际文件命名规则拼接。PaddleX 的模型目录中通常包含 inference.pdmodelinference.pdiparams 等文件,但 om 模型可能是单独的 *.om 文件,因此需在配置中指定或统一命名规则。

修改 process 方法,适配 aisbench 的推理调用

aisbenchInferSession 对象与 PaddleX 原有的 infer 对象调用方式不同。原有静态图推理器通常是一个可调用对象(如 self.infer(batch_inputs)),而 InferSession 需要调用其 infer 方法并传入额外参数(如 mode='dymshape' 以启用动态形状支持)。因此,我们需要在 process 方法中增加分支,根据推理引擎的类型选择正确的调用方式。

原始 process 方法中推理部分的代码片段如下:

# do infer
if self._use_static_model:
    batch_preds = self.infer(batch_inputs)
else:
    with TemporaryDeviceChanger(self.device):
        batch_preds = self.infer(batch_inputs)

修改后,我们需要判断当前使用的推理引擎是否为 InferSession 实例,若是,则调用其 infer 方法并传入动态形状参数;否则保持原调用方式。修改后的代码如下:

# do infer
if self._use_static_model:
    # 判断是否使用了 npu 推理
    if self._use_hpip and self.hpi_config.device_type == "npu":
        # aisbench 动态推理
        batch_preds = self.infer.infer(batch_inputs, mode='dymshape', custom_sizes=100000000)
        # aisbench 静态推理(若模型为静态形状,可去掉 mode 参数)
        # batch_preds = self.infer.infer(batch_inputs, custom_sizes=100000000)
    else:
        # 原有静态图推理器(如 create_static_infer 返回的对象)
        batch_preds = self.infer(batch_inputs)
else:
    with TemporaryDeviceChanger(self.device):
        batch_preds = self.infer(batch_inputs)

说明mode='dymshape'aisbench 用于启用动态形状推理的参数,custom_sizes 用于指定动态输入的最大内存分配(可根据实际需求调整)。这两处代码修改的核心逻辑是使用 aisbench 替换 PaddleX 原有的 om 推理路径。这样既支持动态模型也支持静态模型(根据模型实际形状而定),同时不影响其他后端(如 CPU/GPU)的推理逻辑。

验证与测试

修改完成后,可通过以下方式验证:

  1. 准备动态 om 模型:将已训练的 Paddle 模型转换为支持动态形状的 om 文件(例如使用 atc 工具时设置 --input_shape 为动态维度,如 "x:1,3,-1,-1")。
  2. 编写测试脚本
    from paddlex import create_pipeline
    
    pipeline = create_pipeline(
        pipeline="OCR",               # 或其他流水线
        device="npu",
        use_hpip=True,
        hpi_config={"backend": "om", "device_type": "npu", "device_id": 0}
    )
    
    # 测试不同尺寸的图片
    for img in ["small.jpg", "large.jpg"]:
        result = pipeline.predict(img)
        for res in result:
            res.print()
    
  3. 观察结果:确认推理成功执行且输入尺寸可变(如连续输入不同尺寸的图片均能正确返回结果),无形状匹配错误。

总结

通过上述修改,PaddleX 成功集成了 aisbench 的 om 模型推理能力,且未对原有推理逻辑造成影响,与 CPU、GPU 等后端完全兼容。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐