小模型在昇腾NPU上的推理部署:【小模型Triton Inference Server服务化方案】

📅 2026/7/9 4:23:17
小模型在昇腾NPU上的推理部署:【小模型Triton Inference Server服务化方案】
​作者​昇腾实战派​知识地图​https://blog.csdn.net/Lumos_Lovegood/article/details/161601003背景概述本文主要介绍目前小模型领域的服务化情况和Triton Inference Server方案这也是目前昇腾在小模型服务化的主打方案并详细介绍三条路径Python backend、GE custom backend和ONNX-RUNTIME backend子方案。一、小模型服务化综述1.1 小模型服务化的价值在各行各业中小模型的应用越来越广泛凭借其精准、高效、低成本等优势小模型始终占据一定的地位同时与之关联密切的推理小卡也存在一定的市场空间小模型对于整个昇腾生态也是非常重要的一环。当前小模型类别和架构百花齐放OCR、语音、CV等模型快速迭代在各自领域的模型不断刷新SOTA小模型在完成昇腾上的迁移适配后模型服务化调用成为刚需需提供服务接口进行业务编排/串流如提供API集成到客户ERP软件中调用成为一项基础能力提供。1.2 小模型服务化总览目前可行和可用的小模型服务化主要有Triton Inference Server、FastAPI、Flask、vLLM-Ascend、vLLM-Omni、FunASR、TEI等本文主要介绍Triton Inference Server方案以下简称triton注意不是OpenAI的triton这也是传统小模型的通用服务化方案triton在小模型领域也是生态较好的框架本身较为成熟适合商用化。1.3 服务化适配总体流程对于拿到一个模型总体两步走服务化的基础是模型功能已完成迁移适配如有性能诉求需先完成离线推理的性能调优再接入服务化以及后续的服务调优。二、Triton Inference Server总览2.1 简介Triton Inference Server是 NVIDIA 开发的开源高性能推理服务框架专为生产环境中的 AI 模型部署设计作为一个服务化框架客户端可以通过http/gRPC请求给triton server发送请求triton server底层可以用tensorRTpytorchonnxruntime等不同的后端做具体的模型推理并将推理后的结果返回给客户端。官方GitHub仓https://github.com/triton-inference-server2.2 核心模块triton server主要分为以下几个主要的模块模块名功能serverTriton服务主程序core核心组件Triton核心逻辑和调度common公共工具和基础设施backend后端统一接口定义***backendonnxruntime_backend、tensorrt_backend等具体backend实现2.3 架构图与核心流程其中Model Repository可以存放不同的模型Triton Inference Server通过该仓库提供推理模型。推理请求通过HTTP/REST、GRPC或C API到达服务器然后被路由到相应的模型调度器。Triton Inference Server实现了多种调度和批处理算法这些算法可以根据模型进行配置。每个模型的调度器可选择地对推理请求进行批处理然后将请求传递给与模型类型对应的后端。后端使用批处理请求中提供的输入进行推理以生成所需的输出并将输出返回。三、Backend详解3.1 简介backend是triton中的核心推理部件是triton中用来进行具体的模型推理的模块backend可以是由一些推理框架比如onnxruntime/tensorRT等封装得到也可以按照backend要求的api格式自己写一个backend每个backend对应一个.so动态库库文件的名字必须遵循libtriton_.so的命名格式在config.pbtxt配置文件中配置backend:“mybackend”则对应为libtriton_mybackend.so。3.2 Backend机制Triton Server启动时会扫描默认后端目录下的子目录每个子目录对应一个后端通过目录名称和其中的配置文件确定后端信息读取并解析 config.pbtxt 配置文件选择对应的后端通过dlopen接口动态加载后端动态库libtriton_.so加载成功后后端相关的代码与资源被映射到进程空间。Triton通过dlsym等机制获取后端动态库库必须实现暴露的一组标准接口函数并保存到后端管理结构中执行后续调用。其中libtriton_.so动态库搜索路径优先在模型版本路径搜索依次会搜索/model//libtriton_.somodel_repository/model/libtriton_.soglobal_backend_directory/backends/libtriton_.so3.3 Backend选择推荐triton server虽然原生不支持NPU但是得益于服务化与推理后端的解耦通过扩展推理后端即可在NPU上运行triton server。本方案中可选三种不同的backend分别是Python backend、GE custom backend和ONNX-RUNTIME backend。简要对比Python backend和GE custom backendtriton service本身是C开发的调用python性能无法达到最好有Python侧的开销这是Python backend天生的劣势Python backend涉及适配model.py文件的三个接口支持接入推理路线的种类最多但有一定适配工作量而GE backend单独针对onnx格式基本没有代码开发量GE backend基于图进行深度优化并且提供了图下沉、多流并行、锁核等方法Python backend中通常使用aisbench工具的Python api进行om模型的推理本身性能就比不过GE且不支持异步推理针对不同的模型推理技术路线选取不同的backend方案1ONNX路线一通过onnxge_backend的方式进行服务化路线二通过onnxonnxruntime_backend的方式进行服务化对于pytorch、paddle、tensorflow、mindspore等框架下的模型均可转换为onnx后接入使用该方案2OM/torch路线一通过om python backend的方式进行服务化路线二通过torch_npu python backend的方式进行服务化路线三通过torchAirpython backend的方式进行服务化分别对应torch在线推理与OM的离线推理四、Triton Inference Server特性4.1 动态批处理动态批处理 (Dynamic batching) 允许将一个或多个推理请求合并成一个batch批量处理请求提高资源利用率提高吞吐在config.pbtxt文件中添加下面即可开启该特性dynamic_batching { max_batch_size: 32 max_queue_delay_microseconds: 10000 preferred_batch_size: [4, 8, 16] }max_batch_size最大的batch大小preferred_batch_size期望达到的batchsize大小可以是一个数也可以是一个数组max_queue_delay_microseconds单位是微秒组batch的时间限制超过该时间会停止组batch。详情查看官方说明https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/tutorials/Conceptual_Guide/Part_2-improving_resource_utilization/README.html以下示例展示了不使能动态批处理、使能动态批处理、使能动态批处理并设置延迟三种场景下的差异4.2 多实例在模型服务化部署中单实例推理容易成为性能瓶颈尤其是在小模型高QPS或多NPU场景下。Triton通过 Instance Group 提供多实例部署能力多实例特性支持生成单模型的多个副本允许并行处理以提高资源利用率提高模型吞吐在config.pbtxt文件中添加下面即可开启该特性count: 每个设备上的实例数量kind: 设备类型GPU/CPUgpus: 指定GPU设备 ID默认使用所有GPUinstance_group [{ count: 2 kind: KIND_CPU }]结合动态批处理和模型并行以下示例展示了两个模型示例并行执行下不使能动态批处理、使能动态批处理、使能动态批处理并设置延迟三种场景下的差异4.3 模型组合可以将多个模型如 预处理模型、中间模型、后处理串联组合由Triton统一调用并且进行内部的数据流转控制简化复杂的流水线部署tokenizer - encoder - decoder。五、Python backend方案5.1 环境说明对于Python backend在NPU上使用triton service的环境可以基于两种方式官方triton service镜像适配昇腾推荐21.09版本之后自带Python Backend镜像标签中以-py3 结束基于已经安装编译triton service的官方镜像安装昇腾底层软件栈CANNtorch_npu等组件triton service官方镜像https://catalog.ngc.nvidia.com/orgs/nvidia/containers/tritonserver/tags昇腾官方镜像编译triton service基于昇腾基础CANN镜像编译triton service仓库该方式涉及的编译情况比较复杂5.2 仓库整体结构triton service规定了model_repository实现时的目录结构以如下形式/model-name/ [config.pbtxt] [output-labels-file ...] version/ model-definition-file version/ model-definition-file ... model-name/ [config.pbtxt] [output-labels-file ...] version/ model-definition-file version/ model-definition-file ... ...简单的例子model_repository/├── model1│ ├── 1│ │ └── model.py│ └── config.pbtxt└── model2├── 1│ └── model.py└── config.pbtxtmodel_repository是模型仓库根目录可以包含多个模型model*表示一个模型文件夹名即为模型名称1 表示版本信息一个模型可以有多个版本triton service可以同时管理多个版本官方规定需要单纯的数字命名model.py是Python backend推理实现文件放在对应的版本目录下config.pbtxt是每个模型的配置文件5.3 model.py结构model.py是Python backend对应的核心推理文件实现模型的初始化推理释放等流程具体需要实现TritonPythonModel类包含initialize、execute、finalize方法execute是核心推理逻辑用户可以最大程度的自定义推理方法这也是Python backend灵活的原因。initialize可选在模型首次加载到 Triton 后端进程时执行用于完成一次性的初始化操作例如权重加载、缓存结构构建、设备资源绑定等。通常这里完成所有需要在请求处理前准备好的工作。execute必选每次收到推理请求时由 Triton 调用是模型的核心入口。在此方法中读取输入张量、执行前向计算、生成输出并返回响应。该方法必须实现因为它直接承载模型的推理逻辑。finalize可选当模型实例从后台卸载或进程退出时调用用于释放资源、关闭句柄、清理缓存等收尾任务。此阶段只执行一次以确保不会遗留系统资源或内存泄露。importtriton_python_backend_utilsaspb_utilsclassTritonPythonModel:definitialize(self,args): 模型初始化逻辑类似构造函数 - args: dict包含 model_repository 路径、model 名称、version 等信息 - 这里可以加载模型权重、初始化依赖库等 passdefexecute(self,requests): 推理核心函数 - requests: list包含多个 inference request可能是 batch - 必须返回 list of pb_utils.InferenceResponse responses[]forrequestinrequests:# 获取输入张量in_tensorpb_utils.get_input_tensor_by_name(request,INPUT_NAME)in_numpyin_tensor.as_numpy()# 模型计算逻辑这里可以调用 PyTorch、TensorFlow、NumPyout_numpyin_numpy1# 示例逻辑# 构造输出out_tensorpb_utils.Tensor(OUTPUT_NAME,out_numpy)responses.append(pb_utils.InferenceResponse(output_tensors[out_tensor]))returnresponsesdeffinalize(self): 资源清理逻辑类似析构函数 - 在模型卸载时调用 - 可以释放 GPU/CPU 资源关闭文件句柄 pass5.4 config.pbtxt结构config.pbtxt是一个protobuf格式的配置文件定义了模型的名称、输入、输出等参数。name: model1 # 模型名称必须与文件夹名一致 backend: python # 后端类型Python 模型用 python max_batch_size: 8 # 最大 batch 大小0 表示不支持 batching input [ { name: INPUT_NAME data_type: TYPE_FP32 # 输入数据类型 (FP32, INT64 等) dims: [ -1, 224, 224, 3 ] # 输入维度-1 表示动态维度 } ] output [ { name: OUTPUT_NAME data_type: TYPE_FP32 dims: [ -1, 1000 ] # 输出维度 } ]5.5 推理方式由于推理执行的解耦triton service的Python backend下的推理方式即为NPU上模型推理的方式总共分为三种在线推理即基于pytorch框架下的推理直接基于torch_npu推理或者使用torchair成图分别对应pytorch下的eager和图模式推理方式离线推理即基于OM的推理方式可以使用aisbench提供的infer interface或直接基于pyacl api来写推理逻辑例如对于yolo模型我们使用两种推理方式接入Python backed六、GE Custom backend方案6.1 自定义backend实现流程Triton Inference Server提供了自定义backend的接入方式对于NPU上使用除了使用自带的Python backend还可以使用自定义的NPU专属backend此方案就是使用的GE图引擎直接对onnx模型进行服务化。通过对TRITONBACKEND_Backend类的实现我们就可以自动定义backend具体需要实现8个接口函数接口功能TRITONBACKEND_Initializebackend初始化资源分配、配置加载TRITONBACKEND_Finalizebackend收尾工作资源释放、状态清理TRITONBACKEND_ModelInitialize解析并存储模型配置信息如模型名称、输入输出shape、config.pbtxt 中的参数等TRITONBACKEND_ModelFinalize模型级别的收尾清理函数TRITONBACKEND_ModelInstanceInitialize模型实例初始化解析实例的配置、初始化实例的运行环境TRITONBACKEND_ModelInstanceFinalize清理实例初始化时分配的资源TRITONBACKEND_ModelInstanceExecute真正执行模型推理的核心函数TRITONBACKEND_BackendControl 可选封装后端的配置、资源管理、生命周期交互其中triton对于模型管理分为了层次抽象的四层六类ModelRepositoryManager负责扫描模型仓库目录解析模型配置config.pbtxt识别模型版本并触发模型加载流程ModelLifeCycle是模型生命周期调度管理负责模型的真正加载/卸载/更新TritonModel类对应一个具体模型管理该模型的所有推理实例TritonModelInstanceModelState是与TritonModel紧密关联的状态管理类一个TritonModel对象会对应一个唯一的ModelState通过TRITONBACKEND_ModelSetState绑定在一起后续后端的推理调用、资源分配、状态查询等操作都由ModelState来承担TritonModelInstance是实际执行推理的最小调度单元拥有自己的线程上下文、device绑定、batch策略等对应instance_group中设置的实例数ModelInstanceState同样对应一个TritonModelInstance掌控了单个推理实例的执行状态和资源细节聚焦于实例级的运行时管理。上层接口与类之间的调用关系如下6.2 GE backend方案架构图开源仓链接https://gitcode.com/cann/triton-inference-server-ge-backend6.3 GE backend特性名称介绍支持情况多实例模型可同时处理多个请求此特性需搭配多流并行或多卡使用√多卡支持一个模型可同时跑在多张卡上每张卡可配置1 的实例√多卡负载均衡多卡情况下能根据每张卡上任务数量动态分配请求目前仅支持所有请求shape一致场景动态batch支持input、output 的0轴为可变场景√GE静态图通过shape固定实现初始化图时分配好所有显存提高图执行效率√多流并行静态图搭配√锁核配置每一条stream使用Cube以及Vector核心数量以便多stream情况下提高吞吐√非0轴动态支持非0轴情况下的动态shape√6.4 GE backend具体实现ge_backend实现主要有以下几个文件npu_ge.cpp 对应Triton 的7个Custom backend规范接口model_state.cpp 完成 ModelState创建、以及均衡器Scheduler的初始化scheduler.cpp 实现了执行块的计算、分配过程model_instance_state.cpp实现了Instance的初始化以及推理接口。通过cmake 编译后会生成相应.so 动态链接库即GE backend的动态库核心调用函数如下ONNX图解析通过Parser接口将原始ONNX模型解析为图可参考链接使用Parser接口将原始模型解析为Graph-CANN商用版8.3.RC1-昇腾社区异步图执行通过GE提供的图开发C接口ExecuteGraphWithStreamAsync实现可参考链接带参数配置的图运行单进程单卡-CANN商用版8.3.RC1-昇腾社区调用流程如下调用GEInitialize进行系统初始化也可在Graph构建前调用申请系统资源调用Session构造函数创建Session类对象申请Session资源调用AddGraph在Session类对象中添加定义好的图调用aclInit接口初始化acl调用CompileGraph完成图编译调用aclrtSetDevice指定运行的Device调用aclrtCreateStream创建Stream然后调用aclrtMallocHost、aclrtMalloc分别申请Host和Device内存调用LoadGraph异步执行Graph场景加载图模型到6创建的Stream上调用ExecuteGraphWithStreamAsync异步执行接口运行Graph调用aclrtSynchronizeStream阻塞程序运行直到指定Stream中的所有任务都完成调用aclrtFree、aclrtFreeHost释放内存调用GEFinalize释放系统资源调用aclFinalize释放acl相关资源6.5 模型适配流程模型接入GE backend的流程如下仓库目录结构如下在config.pbtxt文件中backend要填写我们自己的backendnpu_ge并填写onnx对应的输入输出信息六、服务启动与客户端侧逻辑使用下述格式来拉起服务其中–model-repository指定模型仓库–load-model指定加载的模型tritonserver --model-repository*** --load-model***客户端负责发送请求到triton service需借助tritonclient组件客户端侧代码示例如下importtritonclient.grpcasgrpcclientimportnumpyasnpimportlogging# 配置日志...# Triton服务器配置urllocalhost:8000model_namemodel# 创建Triton客户端logger.info(f连接到Triton服务器:{url})clienthttpclient.InferenceServerClient(urlurl)#client grpcclient.InferenceServerClient(urlurl)# 准备输入数据...# 创建Triton输入...# 准备输出...# 执行推理logger.info(f发送推理请求到模型:{model_name})responseclient.infer(model_name,inputsinputs,outputsoutputs)# 检查响应状态...# 获取并打印结果...七、案例本节以yolov9为例介绍TritonPython backendom的服务化部署方法获得onnx格式模型yolov9模型通过torch.onnx.export接口导出加载验证精度无误导出正确。填写confi.pbtxt文件注意backend为python这里的max_batch_size是10图片分辨率为640*640。3.实现model.py文件使用ais_bench提供的推理api实现模型推理设置为static模式调用non_max_suppression实现后处理再返回请求。fromais_bench.infer.interfaceimportInferSessionfromtorch.utils.dlpackimportto_dlpack,from_dlpackimporttriton_python_backend_utilsaspb_utilsimportsysimporttorchimportnumpyasnp sys.path.append(/home/yolo9/yolov9)fromutils.generalimportnon_max_suppressionclassTritonPythonModel:definitialize(self,args):print(args)self.model_path/home/yolo9/yolov9s.omself.device_id0self.sessionInferSession(self.device_id,self.model_path)print(load om success)defexecute(self,requests):responses[]forresinrequests:imagesfrom_dlpack(pb_utils.get_input_tensor_by_name(res,images).to_dlpack())outputsself.session.infer([images],modestatic)predstorch.tensor(outputs[0])output_tensornon_max_suppression(preds,0.25,0.45)[0]output_tensorpb_utils.Tensor(preds,np.array(output_tensor))responsepb_utils.InferenceResponse(output_tensors[output_tensor])responses.append(response)returnresponsesdeffinalize(self):print(Inference finalized)服务启动成功示例截图