[犀牛鸟 #6790] Qwen3-ASR 的 ncnn 移植 #6805
linkeLi0421
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Qwen3-ASR 转 ncnn
实现分支:
https://github.com/linkeLi0421/ncnn_llm/tree/qwen3-asr-export
当前实现分支最新提交:
记录仓库:
https://github.com/linkeLi0421/Qwen3-ASR-ncnn
1. 背景
目标是验证
Qwen/Qwen3-ASR-0.6B是否可以通过 pnnx 转换到 ncnn,并在C++ ncnn runtime 中完成 ASR 推理。
Hugging Face 版本的 Qwen3-ASR 不是一个简单静态图。官方
Qwen3ASRModel.transcribe()里包含音频加载、重采样、log-mel 特征、音频切块、prompt 构造、特殊 audio token、generation wrapper、输出解析等
Python 逻辑。因此不能直接把完整 pipeline 当成一个模型转给 ncnn。
当前做法是把模型拆成几个静态计算模块,再由 C++ runtime 补齐外围逻辑。
2. 模型拆分
当前导出并转换的模块包括:
audio_encodertext_embedtext_backbonelm_head其中:
audio_encoder:把 128-bin log-mel 音频特征转成音频 embedding。text_embed:把 token id 转成 text embedding。text_backbone:Qwen/Qwen3 主 transformer 层,处理混合后的 text/audio embedding。lm_head:把 hidden state 转成词表 logits,用于生成下一个 token。C++ runtime 负责:
language和text。3. 环境
验证机器:
570.124.0612.8Qwen/Qwen3-ASR-0.6Bfp32qwen3_asr_mainbf16TorchScript 可以用于观察,但 pnnx/ncnn runtime 验证目前使用fp32更稳定。
4. 导出命令
当前 text64 runtime 对应的导出命令:
runtime 模型目录:
5. ncnn runtime 和 CMake 命令
示例:
当前 CLI 输入要求:16 kHz、mono、PCM16 WAV。
CMake 构建命令:
正式 CMake build 出来的 binary 已验证
pdx_voice_16k.wav:6. 模块级验证
转换后的 ncnn 模块和 TorchScript 输出做过对比:
text_embedmax_abs 0.0,mean_abs 0.0lm_headmax_abs 6.6757e-06,mean_abs 3.6068e-07text_backbonemax_abs 0.026946,mean_abs 0.0030005,p99_abs 0.0120745audio_encodermax_abs 2.5416e-05,mean_abs 1.2091e-06,p99_abs 8.7405e-06C++ log-mel 前处理也和 Hugging Face 路径做过对比:
max_abs1.704692840576172e-05mean_abs1.3335738913156092e-07p99_abs2.384185791015625e-077. 端到端结果
对比方法:
Qwen3ASRModel.transcribe(..., language="English")pdx_voiceThis is a test of me recording my voice.This is a test of me recording my voice.hello_worldHello world.Hello world.natural_zeroZero.Zero.digit_fiveFive.Five.long_text_numbers_fastOne two three four five six seven eight nine ten eleven twelve thirteen fourteen.One two three four five six seven eight nine ten eleven twelve thirteenlong_text_numbers_fastOne two three four five six seven eight nine ten eleven twelve thirteen fourteen.One two three four five six seven eight nine ten eleven twelve thirteen fourteen.long_digit_fiveBy byBy by by by by by five, five, five.long_digit_five是把同一个很短的 spoken digit 样本重复 40 次得到的人工压力样本,不适合作为语义正确性的主要 benchmark。官方 PyTorch 也没有把它识别成
重复的
Five.,而是输出By by。ncnn 当前按带 overlap 的固定窗口逐块解码,再做词级去重拼接,所以重复音频仍可能产生重复文本。这个问题应记录为长音频
chunk stitching 限制,而不是 ncnn 模块转换失败。
当前长音频 runtime 已加入 32-frame overlap 和词级 suffix/prefix 去重。该改动
保持
pdx_voice和long_text_numbers_fast回归通过,并把long_digit_five的输出改善为
By by by by by by five, five, five.。由于这个样本本身是人工重复压力样例,仍不把它视为语义正确性通过。
8. 已修复的问题
--language English导致立即 EOS、输出为空的问题。原因是之前错误地把
language English<asr_text>预填到了 assistant answer前缀里。现在不再这样预填。
pdx_voice这种 4.95 秒真实语音样本可以完整匹配 PyTorch。
This is a test. Of me ...。text_seq_len=128解决了当前长文本样例在 text64 下被截断的问题。--chunk-overlap-frames 32,并在拼接时做词级 suffix/prefix 去重。
9. 当前限制
text_backbone已验证text_seq_len=64和text_seq_len=128。静态 text64会截断长文本,text128 可以覆盖当前长文本样例。
默认静态 decode;仍需要更多导出长度矩阵。
10. 当前判断
核心结论:Qwen3-ASR 的主要神经网络模块可以通过 pnnx 转成 ncnn,并且当前
C++ runtime 已经可以在多个短英文语音样本上对齐官方 PyTorch 输出。
这说明转换路径是可行的。但当前分支仍应视为 prototype,而不是完整生产级 ASR
runtime。下一步应重点补齐:
11. KV cache 补充实验
后续实现了一个实验性 KV cache 路径:
text_prefill_kv:一次性处理 prompt/audio embeddings,并输出每层 K/V cache。text_decode_kv:每次处理一个新 token,输入上一轮 cache,并输出更新后的 cache。--use-kv-cache显式启用;默认路径仍保持原来的静态 decode。关键修复点是 pnnx 会把 one-token decode 中 cache concat 后的 repeated-KV reshape
固定为
cache_len + 1。这样第一步 decode 可以工作,但第二步 cache 已增长后会发生 shape 语义错误。当前 exporter 在 pnnx 后处理
text_decode_kv.ncnn.param,把这些 reshape 的序列维度改为
-1,让 ncnn 根据实际 cache 长度推断。当前用正式 CMake build 的
qwen3_asr_main做了默认静态 decode 与 KV cache decode对比:
pdx_voice_16k.wavThis is a test of me recording my voice.This is a test of me recording my voice.pdx_voice-note_16k.wav啊。啊。0_jackson_0_16k.wavZero.Zero.1_jackson_0_16k.wav一。一。natural_digit_0_16k.wavZero.Zero.random_digit_5_jackson_0_16k.wavFive.Five.长输出补充结果:
long_text_numbers_fast_16k.wavlong_text_numbers_30_16k.wav不同静态长度结果:text64 在长文本上会截断,text128 可以覆盖当前样例。KV48 的
prefill 长度等于当前 prompt/audio 长度 48,后续 token 通过 cache 增长,因此没有
静态 decode 那种
prompt + generated长度耗尽问题。KV64 重新导出尝试过一次,但当前 VM 上长时间停在模型加载/初始化阶段,输出目录为空。
该路径仍应视为 prototype:目前还需要 KV64/KV128 导出矩阵的进一步排查,
以及更多平台/样例覆盖。
12. 第二平台验证
除 Linux/RTX 4090 VM 外,已在本机 macOS 平台完成 CPU-only build/smoke test:
NCNN_VULKAN=OFFqwen3_asr_0_6b_runtime_text128pdx_voice_16k.wav运行结果:
因此当前已经覆盖两个平台:
13. KV cache timing
为了确认 KV cache 是否带来实际性能收益,给
qwen3_asr_main加了--timing选项,并在 Linux VM 上对比 static text128 与 KV48。这里使用 CPU ncnn runtime
和
--threads 8 --timing,没有启用 Vulkan。pdx_voicepdx_voicelong_text_numbers_fastlong_text_numbers_fastlong_text_numbers_30long_text_numbers_30速度比:
pdx_voicelong_text_numbers_fastlong_text_numbers_30结论:KV cache 在当前 CPU runtime 上已经有明显收益,主要来自避免每个 token
重复跑完整 text backbone。4090 GPU 加速仍未验证:当前
--vulkan只枚举到llvmpipe软件 Vulkan device,而不是 RTX 4090,并且 smoke 输出不可靠。Beta Was this translation helpful? Give feedback.
All reactions