小模型在昇腾NPU上的推理部署:【Triton server适配wenet模型最佳实践 】
这使得在实际应用中难以将基于昇腾后端的小模型推理能力与业务场景无缝结合,例如在视频流处理等实时性要求较高的场景中,无法实现高效的在线推理服务,依赖定制脚本脚本完善Triton Server对接pytorch backend推理。尽管该框架原生不支持NPU后端,但其服务化功能与推理后端实现了良好的解耦设计,这使得可以通过扩展推理后端实现逻辑,基于已支持的CPU后端轻松实现对NPU后端的支持。通过本次
作者:昇腾实战派
小模型在NPU上的推理部署: 【知识地图】
背景与挑战
针对昇腾后端的模型服务化部署,目前在大模型(如LLM)领域已具备较为完善的推理引擎支持,例如MindIE和vLLM Ascend等,能够实现高效的推理服务化。然而,在计算机视觉(CV)和语音处理等小模型的部署场景中,缺乏完整的服务化部署方案,尤其在动态批处理、多实例部署等关键能力方面存在明显不足。
这使得在实际应用中难以将基于昇腾后端的小模型推理能力与业务场景无缝结合,例如在视频流处理等实时性要求较高的场景中,无法实现高效的在线推理服务,依赖定制脚本脚本完善Triton Server对接pytorch backend推理。
问题分析过程与解决方案
NV通过其服务化框架有效解决了小模型部署问题。尽管该框架原生不支持NPU后端,但其服务化功能与推理后端实现了良好的解耦设计,这使得可以通过扩展推理后端实现逻辑,基于已支持的CPU后端轻松实现对NPU后端的支持。
AIS_bench作为一款开源的模型推理工具,不仅支持模型调用和动态Batch功能,还为在Triton Inference Server中实现NPU后端推理逻辑提供了良好的开发基础。该工具在社区上的开源特性,为模型推理功能的扩展和优化提供了便利条件。
整个适配工作主要分为两个阶段:首先是模型离线推理性能优化,其次是Triton服务化适配。
Wenet模型离线推理性能调优
Wenet模型PT转为OM
(1) PT转非流式ONNX
修改export_onnx_npu.py:558行添加 configs["is_json_cmvn"]= True
export PYTHONPATH=./wenet #wenet源码路径
python3 export_onnx_npu.py --config ./train.yaml --checkpoint ./final.pt --output_onnx_dir ./onnx/ --num_decoding_left_chunks 4 --reverse_weight 0.3 --cmvn_file ./train/global_cmvn
转换成功后得到offline_encoder.onnx和decoder.onnx
(2) Encoder转非流式动态分档OM
export batch_size=1
atc --input_format=ND --framework=5 --model=/disk1/liangliang/wenet/onnx_model/onnx/offline_encoder.onnx --input_shape="speech:${batch_size},-1,80;speech_lengths:${batch_size}" --dynamic_dims="262;326;390;454;518;582;646;710;774;838;902;966;1028;1284;1478" --output=/disk1/liangliang/om-wenet/offline_encoder_static_bs${batch_size} --log=error --soc_version=Ascend910B3
(3) Decoder转OM
修改附带脚本中的onnx路径和om output路径,执行:
bash static_decoder.sh
(4) 使用OM进行离线推理
安装依赖:
- ais_bench推理包(必需,离线/服务化都需要)
# 安装aclruntime
pip3 install ./aclruntime-0.0.2-cp311-cp311-linux_aarch64.whl
# 安装ais_bench推理程序
pip3 install ./ais_bench-0.0.2-py3-none-any.whl
- ctc_decoder(非必需)
git clone https://github.com/Slyne/ctc_decoder.git
apt-get update
apt-get install swig
apt-get install python3-dev
cd ctc_decoder/swig && bash setup.sh
- recognize_om离线推理
export batch_size=1
export ASCEND_RT_VISIBLE_DEVICES=0
export PYTHONPATH=./wenet
python3 recognize_om.py --config=./train.yaml --test_data=./aishell/s0/data/test/data.list --dict=onnx_model/lang_char.txt --mode=attention_rescoring --result_file=static_result.log --encoder_om=./offline_encoder_static_bs1.om --decoder_om=./offline_decoder_static_1.om --batch_size=1 --device_id=0 --static --test_file=static_test_result.txt --num_process=8 --encoder_gears="262, 326, 390, 454, 518, 582, 646, 710, 774, 838, 902, 966, 1028, 1284, 1478" --decoder_gears="96, 144, 384"
Wenet-OM转换源码参考: ModelZoo-Wenet2_for_Pytorch
性能提升效果验证
使用aishell数据集(7k+)测试recognize_om离线推理性能:
| 测试条件 | 解码方式 | 耗时 | 推理效率 | NPU利用率 |
|---|---|---|---|---|
| 16进程 | decode_ctc_greedy | 19s | 1897x | 88% |
| 16进程 | decode_attention_rescoring | 29s | 1243x | 70% |
Triton Server服务化适配
Triton Server Model Repository目录结构
model_repository/
└── model/
├── 1/
│ └── model.py
└── config.pbtxt
Config.pbtxt配置
name: "asr_om_model"
backend: "python"
# 输入配置(WAV文件路径列表)
input [
{
name: "wav_paths"
data_type: TYPE_STRING
dims: [-1] # 动态维度,支持批量输入多个WAV路径
}
]
# 输出配置(ASR识别结果列表)
output [
{
name: "asr_results"
data_type: TYPE_STRING
dims: [-1]
}
]
# 模型参数配置
parameters [
# 必选参数:OM模型路径
{
key: "encoder_om_path"
value: { string_value: "./om-wenet/offline_encoder_static_bs1.om" }
}
# ... 其他参数
]
# 资源限制(根据实际硬件调整)
instance_group [
{
count: 8 # server实例数量
kind: KIND_CPU # Python后端运行在CPU,特征提取和推理调用NPU
}
]
Model.py适配关键代码
import triton_python_backend_utils as pb_utils
import torch
import torchaudio
import torchaudio.compliance.kaldi as kaldi
import torch_npu
from ais_bench.infer.interface import InferSession
def get_dict(dict_path: str) -> tuple[List[str], Dict[int, str]]:
"""加载字符字典"""
def preprocess_audio(audio_path: str, device_id: int = 0) -> tuple[np.ndarray, np.ndarray, float]:
# 音频文件特征提取
def _extract_feats_multi_thread(wav_paths: List[str], device_id: int, feature_workers: int) -> List[tuple]:
# 音频文件多线程特征提取
class TritonPythonModel:
def initialize(self, args: Dict):
"""初始化OM实例"""
def _parse_parameters(self) -> Dict:
"""参数解析(必选/可选参数校验)"""
def _init_single_om_session(self, om_path: str) -> InferSession:
"""初始化单OM实例"""
def _static_shape_pad(self, feats: np.ndarray, feats_lengths: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
"""静态形状填充:适配OM输入要求,动态dims"""
def _decode_ctc_greedy(self, beam_log_probs_idx: np.ndarray, encoder_out_lens: np.ndarray) -> str:
"""CTC贪心解码:基于char_dict映射"""
def execute(self, requests):
"""推理核心:1. 解析输入:wav_paths 2.调用_preprocess_audio 3. 单OM实例推理 4. 构造Triton响应"""
def finalize(self):
"""模型销毁:仅输出日志(OM实例由InferSession自动释放)"""
性能优化:将FBANK迁移到NPU上运行
完成wenet-om的model_repo适配后,性能测试发现在线服务和离线性能有一定差距,通过分析发现FBANK特征提取方法耗用了大部分时间。
解决方案:替换torchaudio文件,适配NPU
- 修改FFT计算方式:
# 原代码:spectrum = torch.fft.rfft(strided_input).abs()
# 替换为:
real_view = torch.view_as_real(torch.fft.rfft(strided_input))
spectrum = torch.sqrt(real_view[..., 0].pow(2) + real_view[..., 1].pow(2))
- 应用补丁:
# 将patch放到torchaudio的路径下
pip show torchaudio
cp fbank_npu.patch /usr/local/lib/python3.11/site-packages/torchaudio/
# 在torchaudio路径下执行补丁
patch -p1 < fbank_npu.patch
- 修改FBANK使用方法:
waveform = waveform.to(torch.device("npu:0"))
# ... 处理过程
speech = features.unsqueeze(0).cpu().numpy()
解决效果与价值
解决效果
- 服务化部署支持:通过Triton Server实现了昇腾模型的在线推理服务,支持动态批处理和多实例部署
- 性能显著提升:经过性能调优,推理效率获得明显提升
价值体现
- 降低部署复杂度:提供完整的部署样例和脚本,简化模型的服务化部署流程,降低开发成本
- 增强生态兼容性:扩展Triton Server功能,支持更多类型的模型和后端,丰富昇腾生态应用场景
经验教训
- 性能优化是持续过程:从模型加载、推理流程到结果输出,每个环节都需要细致分析和调优
- 全面考虑性能瓶颈:初期优化主要集中在单点,未能全面考虑整个推理流程的性能瓶颈
- 硬件特性充分利用:将特征提取等计算密集型任务迁移到NPU,能显著提升整体性能
- 工具链适配重要性:第三方库的NPU适配是确保整体方案可行性的关键环节
通过本次实践,我们成功构建了完整的昇腾后端小模型服务化部署方案,为类似场景提供了可复用的技术路径和最佳实践。
更多推荐



所有评论(0)