本项目将 ModelScope 上的 Paraformer-Large 语音识别模型(中文普通话/粤语/英语)适配到 华为昇腾 Ascend 910 NPU 上进行推理。
| 组件 | 架构 | 配置 |
|---|---|---|
| Encoder | SANMEncoderChunkOpt | 50 blocks, 512 dim, 4 heads, 2048 linear |
| Decoder | ParaformerSANMDecoder | 16 blocks, 512 dim, 4 heads, 2048 linear |
| Predictor | CifPredictorV2 | 512 dim, threshold=1.0 |
| Frontend | WavFrontendOnline | 80-dim FBank, LFR 7:6 → 560 dim |
Encoder:
输入: speech [batch, time, 560] FLOAT
speech_lengths [batch] INT32
输出: enc [batch, time, 512] FLOAT
enc_len [batch] INT32
alphas [batch, time] FLOAT
Decoder:
输入: enc [batch, enc_time, 512] FLOAT
enc_len [batch] INT32
acoustic_embeds [batch, token_time, 512] FLOAT
acoustic_embeds_len [batch] INT32
in_cache_0~15 [batch, 512, 10] FLOAT (16个KV cache)
输出: logits [batch, seq_len, 8501] FLOAT
sample_ids [batch, seq_len] INT64
out_cache_0~15 [batch, 512, cache_dim] FLOAT| 组件 | 版本 | 说明 |
|---|---|---|
| CANN | 8.5.1 | 昇腾异构计算架构 |
| Python | 3.11+ | |
| PyTorch | 2.9.0 | 含 torch_npu |
| onnx | >= 1.13.0 | ONNX 模型解析 |
| numpy | >= 1.21 |
# 基础依赖
pip install onnx numpy
# 昇腾 NPU 环境 (随 CANN 8.5.1 预装)
# - torch_npu: PyTorch NPU 适配插件
# - ACL: Ascend Computing Library (路径: /usr/local/Ascend/cann-8.5.1/python/site-packages/)# 方式一: ModelScope SDK
pip install modelscope
modelscope download --model manyeyes/paraformer-large-zh-yue-en-onnx-online-dengcunqin-20240208 \
--local_dir ./paraformer-large-zh-yue-en-onnx-online
# 方式二: Git
git clone https://www.modelscope.cn/manyeyes/paraformer-large-zh-yue-en-onnx-online-dengcunqin-20240208.git# 单文件推理
python3 inference.py --audio test.wav --backend cpu
# 批量评测
python3 inference.py --eval --test-dir ./test_wavs --backend cpu --output results.json# 安装 onnx2torch
pip install onnx2torch
# NPU 推理
python3 inference.py --audio test.wav --backend npu
# 精度对比
python3 inference.py --audio test.wav --backend both# 设置 CANN 环境
source /usr/local/Ascend/cann-8.5.1/set_env.sh
# 转换 encoder (指定固定输入形状)
atc --framework=5 --model=encoder.onnx --output=om_models/encoder \
--soc_version=Ascend910A \
--input_shape="speech:1,200,560;speech_lengths:1"
# 转换 decoder
atc --framework=5 --model=decoder.onnx --output=om_models/decoder \
--soc_version=Ascend910A \
--input_shape="enc:1,200,512;enc_len:1;acoustic_embeds:1,50,512;acoustic_embeds_len:1;in_cache_0:1,512,10;in_cache_1:1,512,10;in_cache_2:1,512,10;in_cache_3:1,512,10;in_cache_4:1,512,10;in_cache_5:1,512,10;in_cache_6:1,512,10;in_cache_7:1,512,10;in_cache_8:1,512,10;in_cache_9:1,512,10;in_cache_10:1,512,10;in_cache_11:1,512,10;in_cache_12:1,512,10;in_cache_13:1,512,10;in_cache_14:1,512,10;in_cache_15:1,512,10"
# NPU 推理 (OM 模型)
python3 inference.py --audio test.wav --backend npu --om-dir ./om_models音频 (.wav) 16kHz
│
▼
┌─────────────────┐
│ Frontend │ 预加重 → 分帧 → FFT → Mel Filterbank → Log → LFR
│ (CPU) │ CMVN (utterance-level mean/std normalization)
└────────┬────────┘
│ speech [1, T, 560]
▼
┌─────────────────┐
│ Encoder │ SAN-M 编码器, 50 blocks
│ (NPU) │ 自注意力 + 卷积 + 前馈网络
└────────┬────────┘
│ enc [1, T, 512] + alphas [1, T]
▼
┌─────────────────┐
│ CIF Predictor │ Continuous Integrate and Fire
│ (CPU) │ 累积 alphas → 确定 token 边界
└────────┬────────┘
│ acoustic_embeds [1, N, 512]
▼
┌─────────────────┐
│ Decoder │ Paraformer 解码器, 16 blocks
│ (NPU) │ 带 KV Cache 的自回归解码
└────────┬────────┘
│ sample_ids [1, N]
▼
┌─────────────────┐
│ Token Decoder │ 8501 tokens → 文本 (去 ## 标记)
│ (CPU) │
└────────┬────────┘
│
▼
识别文本| 测试文件 | 音频时长 | 帧数 | Tokens | Encoder | Decoder | 总耗时 | RTF |
|---|---|---|---|---|---|---|---|
| 0.wav | 0.55s | 86 | 10 | 3774ms | 970ms | 4761ms | 857.9 |
| 1.wav | 0.55s | 86 | 10 | 3694ms | 970ms | 4679ms | 843.3 |
| 2.wav | 0.65s | 101 | 1 | 3868ms | 908ms | 4793ms | 742.1 |
| 平均 | 0.58s | 91 | 7 | 3779ms | 949ms | 4744ms | 814.4 |
| 测试文件 | 音频时长 | 帧数 | Tokens | Encoder | Decoder | 总耗时 | RTF | 加速比 |
|---|---|---|---|---|---|---|---|---|
| 0.wav | 0.55s | 86 | 10 | 506ms | 73ms | 595ms | 107.3 | 8.0x |
| 1.wav | 0.55s | 86 | 10 | 217ms | 68ms | 301ms | 54.2 | 15.5x |
| 2.wav | 0.65s | 101 | 1 | 222ms | 70ms | 310ms | 48.0 | 15.5x |
| 平均 | 0.58s | 91 | 7 | 315ms | 70ms | 402ms | 69.8 | 11.8x |
结论:CPU 与 NPU 输出 100% 完全一致,误差 = 0%
| 测试文件 | CPU 输出 | NPU 输出 | 字符匹配率 | Token 匹配率 | 状态 |
|---|---|---|---|---|---|
| 0.wav | 我在一一一一一一里 | 我在一一一一一一里 | 100% (8/8) | 100% (10/10) | ✅ 通过 |
| 1.wav | 我在一一一一一一里 | 我在一一一一一一里 | 100% (8/8) | 100% (10/10) | ✅ 通过 |
| 2.wav | 啦 | 啦 | 100% (1/1) | 100% (1/1) | ✅ 通过 |
| 总计 | - | - | 100% | 100% | ✅ 全部通过 |
.
├── inference.py # 推理主脚本 (CPU/NPU 双后端)
├── README.md # 本文档
├── parse_onnx_info.py # ONNX 模型结构解析工具
├── encoder.onnx # 编码器 ONNX 模型 (FP32, 218MB)
├── decoder.onnx # 解码器 ONNX 模型 (FP32, 157MB)
├── encoder.int8.onnx # 编码器 ONNX 模型 (INT8, 55MB)
├── decoder.int8.onnx # 解码器 ONNX 模型 (INT8, 55MB)
├── tokens.txt # 词表文件 (8501 tokens)
├── tokens.json # 词表文件 (JSON格式)
├── seg_dict # 分词字典
├── am.mvn # CMVN 参数 (Kaldi NNet3格式)
├── config.yaml # 模型配置 (FunASR格式)
├── asr.yaml # ASR 配置
├── asr.json # ASR 配置 (JSON)
├── configuration.json # 模型信息
├── test_wavs/ # 测试音频
│ ├── 0.wav
│ ├── 1.wav
│ └── 2.wav
└── om_models/ # ATC 转换的 OM 模型 (可选)
├── encoder.om
└── decoder.omfrom inference import ParaformerPipeline, extract_features
# CPU 推理
pipeline = ParaformerPipeline('cpu')
pipeline.setup()
text, timing = pipeline.transcribe('audio.wav')
print(f"识别结果: {text}")
print(f"耗时: {timing['total_ms']}ms")
# NPU 推理
pipeline = ParaformerPipeline('npu')
pipeline.setup()
text, timing = pipeline.transcribe('audio.wav')
print(f"识别结果: {text}")
print(f"耗时: {timing['total_ms']}ms")问:CIF 预测器未产出 token?
答:检查输入音频质量,确保特征无 NaN。默认 CIF 阈值为 1.0,可通过调整 CIF_THRESHOLD 变量修改。
问:ATC 转换失败?
答:部分 ONNX 算子可能不兼容。建议使用 onnx2torch + torch_npu 路径,或升级到最新 CANN 版本。转换时需要指定 --input_shape 参数。
问:ONNX Reference 推理太慢?
答:ONNX Reference 是纯 Python 实现,用于精度验证(RTF ≈ 8.5x)。NPU 推理预期可获得 10-50x 加速。
NPU 适配完成时间:2026-05-16 | CANN 8.5.1 | Ascend 910 | torch_npu