【腾讯犀牛鸟26】Qwen3ASR ncnn多平台移植 #6879
Days-gone
started this conversation in
Show and tell
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.
Qwen3ASR ncnn多平台移植
项目地址:days-gone/qwen3_asr
摘要
本项目完成了 Qwen3-ASR-0.6B 从 Torch 参考实现到 NCNN 纯 C++ 推理链路的落地。实现覆盖 WAV 读取、Mel 频谱与 Log-Mel 归一化、音频卷积和编码、音频 embedding 注入、Decoder prefill、28 层 KV Cache 增量解码、LM Head、BBPE tokenizer、语言解析与流式文本回调。运行时不依赖 Python 或 Torch,默认在 CPU 上以 float32 执行。
除了验证最终转写文本,项目还提供逐 decode step 的完整 logits 一致性测试。测试以 Torch 为 teacher trajectory,让 NCNN 在相同 token 历史上执行 forward,从而把模型数值误差与生成路径分叉严格区分开。在
example.wav上,Torch 与 NCNN 最终都输出“甚至出现交易几乎停滞的情况。”,语言均为 Chinese;11 个实际生成 step 的 logits argmax 一致率为 100%,整体余弦相似度保持在很高水平,说明 NCNN 实现与 Torch forward 在数值和生成决策上具有良好一致性。1. 项目目标
Qwen3-ASR 不是单一声学网络,而是一条多阶段的多模态生成链路。要让它脱离 Torch 在 C++ 中运行,需要同时复现音频前处理、音频编码、文本 prompt、多模态 embedding 替换、自回归 Decoder、KV Cache、采样和 tokenizer。
当前实现聚焦 Qwen3-ASR-0.6B 的已转换 NCNN 权重,提供以下能力:
model.json统一加载模型路径、网络结构、音频参数、RoPE 参数和 tokenizer 配置;项目不包含模型转换和权重,也暂未提供重采样、VAD、长音频自动切片或批量推理。当前默认执行后端为 NCNN CPU float32。
2. 整体推理架构
完整数据流如下:
实现按职责拆分为几层:
src/main.cpp:命令行入口、WAV reader,以及一致性测试使用的 logits 导出入口;src/Qwen3ASR/qwen3_asr.cpp:端到端 ASR 编排、prefill 和 generate;qwen_asr_config.*:解析和校验model.json;qwen_asr_context.*:保存 KV Cache、生成 token、当前位置和结束状态;ncnn_text_runtime.*:Text Embed、Decoder、LM Head 等公共 NCNN 调用;sampling.*:采样及 repetition penalty;rope_embed.*:prefill 和增量解码所需的 RoPE cache;utils/tokenizer/:BBPE tokenizer。3. 音频preprocess
输入首先被转换为一维 float32 单声道 PCM。命令行 reader 支持 16-bit PCM 和 32-bit IEEE Float WAV;多声道输入按每帧平均值下混。采样率必须与
model.json一致,当前通常为 16 kHz。Mel 网络输出后,C++ 侧继续复现参考 processor 的后处理:
log10,下限为1e-10;(value + 4) / 4归一化。Audio Conv 按配置中的 100 帧进行分块,最后一个不足长度的分块补零,再根据与 Python 相同的卷积长度公式裁剪有效输出。随后 Audio Encoder 把声学特征映射到与文本 Decoder 隐藏维度相同的 embedding 空间。
4. Prompt 与多模态 embedding 注入
项目通过 BBPE tokenizer 编码与 Torch processor 相同的 Qwen Chat Template:
中间按照 Audio Encoder 的输出长度插入
<|audio_pad|>token。Text Embed 网络先为完整 token 序列生成 embedding,再将 audio pad 对应行替换为真实音频 embedding。这样送入 Decoder 的序列在长度、位置和模态排列上与 Torch forward 对齐。特殊 token ID 不硬编码,而是根据基础词表及
model.json中additional_special_tokens的顺序解析。这个顺序必须与模型转换时一致,否则 audio pad、ASR 文本标记和 EOS 都可能映射到错误 ID。5. Decoder Prefill、KV Cache 与增量生成
Prefill 一次处理完整多模态 prompt,并构造上三角 causal mask。每层 Decoder 输出 key/value cache,当前模型共保存 28 层。最后一个 prompt 位置的 hidden state 进入 LM Head,得到第一个生成 token 的 logits。
后续每个 decode step 只处理一个 token:
Qwen3ASRContext保存 KV Cache、已生成 token、token history、当前位置及 finished 状态。generate()克隆输入 Context 而不原地修改,因此既能暂停后继续,也能从同一 prefill 状态创建不同采样配置的分支。6. Tokenizer、语言和流式输出
模型首先生成语言描述,随后生成
<asr_text>,再输出正文。C++ 侧在最终 token 序列中定位该标记:标记之前的内容用于解析语言,标记之后的内容作为 transcription。流式 callback 只返回
<asr_text>后新增的正文,不暴露语言前缀、EOS 或其他特殊 token。为避免 BBPE 字节片段导致错误增量,程序每次解码当前正文,并仅在新结果以旧结果为前缀时返回后缀。默认配置为确定性的 greedy decode:
同时保留 temperature、top-k、top-p 和 repetition penalty,供非确定性生成实验使用。
7. 一致性测试设计
7.1 Torch 作为 teacher trajectory
测试严格使用 Torch 作为 teacher:
teacher forcing 很重要。假设两端在第 4 步 argmax 不同,如果之后各自沿自己的 token 继续生成,那么第 5 步开始输入上下文已经不同,后续 logits 差异不再能代表相同 forward 的实现误差。让 NCNN 始终使用 Torch token 后,即便某一步发生分叉,后续比较仍建立在相同 token 历史上。
测试模式通过临时环境变量启用,不改变普通命令行推理:
QWEN_ASR_TEACHER_TOKENS:Torch teacher token 文件;QWEN_ASR_LOGITS_DUMP:NCNN 原始 logits 输出文件。7.2 指标
每一步计算:
max(|ncnn - torch|);mean(|ncnn - torch|);max(|torch|, 1e-6);相对误差会被接近零的 Torch logits 放大,因此不宜单独作为一致性结论。更可靠的判断需要同时观察绝对误差、RMSE、余弦相似度和 argmax。
运行命令:
8. 具体测试结果
8.1 最终输出
两端独立执行正常 greedy decode 的结果完全一致:
8.2 逐步 logits 指标
全局汇总:
8.3 结果分析
测试中所有 step 的 argmax 都一致,包括语言 token、
<asr_text>、正文 token 和 EOS。这意味着 NCNN 与 Torch 不仅得到相同最终文本,也在每一个生成决策点选择了相同 token。余弦相似度全部高于 0.9988,说明 151,936 维完整 logits 的整体方向高度一致。全局平均绝对误差约 0.0871,RMSE 约 0.1145;考虑到 Torch 使用 bfloat16/CUDA,而 NCNN 使用 float32/CPU,两端算子实现、舍入方式和归约顺序不同,这一量级的误差是可解释的。
Step 9 的平均绝对误差和 RMSE 最大,余弦相似度也相对最低,但其 argmax 仍然一致。Step 0 的单点最大绝对误差最高,为 0.7640。误差没有随着 decode step 单调放大,说明当前 KV Cache 增量更新未表现出持续累积漂移。相对误差在 Step 9 达到 0.5761,主要应结合接近零 logits 对该指标的放大效应理解;其高余弦相似度和一致 argmax 表明生成判断仍然稳定。
综合最终文本、逐步 argmax 和完整向量指标,可以得出:在该测试样本与当前精度配置下,NCNN 推理链路与 Torch forward 实现具有良好一致性,未观察到 prompt 对齐、位置编码、KV Cache 更新或 tokenizer 导致的生成分叉。
All reactions