30B 稀疏、0.6B 端侧、流式小模型:三条开源转写路线同题实测
【免费下载链接】NVIDIA-Nemotron-3.5-Transcribe-30B-A4B项目地址: https://ai.gitcode.com/hf_mirrors/nvidia/NVIDIA-Nemotron-3.5-Transcribe-30B-A4B
开源语音转写这两年出现了一个少见的局面:三条技术路线同时成熟,而且彼此的取舍完全不同。一条是本次仓库里的主角 NVIDIA-Nemotron-3.5-Transcribe-30B-A4B——34B 总参数、4.3B 激活的稀疏 MoE 语音大模型,主打"一段录音里所有事一次做完";第二条是 0.6B 级别的端侧模型路线,把体积压到 CPU 和核显都能接受;第三条是原生流式小模型,追求的是"说完就出字"的延迟,而不是榜单分数。
把它们放进同一张表里比较时最容易犯的错误,是拿榜单上的 WER 去比单请求延迟,再拿单请求延迟去推吞吐。这篇按"先把测量口径统一、再分三轴对照、最后给场景清单"的顺序过一遍,所有数字都能在仓库文件或公开情报里找到出处,无法对齐的部分也会明确标出。
三条路线,先分清它们各自在解什么题
三条路线并非"同一题目的三个解法",它们的输入约束从一开始就不同。
| 维度 | 30B 稀疏路线(本仓库) | 0.6B 端侧路线 | 流式小模型路线 |
|---|---|---|---|
| 代表形态 | Nemotron-3.5-Transcribe-30B-A4B | Parakeet-TDT-0.6B 系列 | Nemotron 3.5 ASR 0.6B / Voxtral Realtime |
| 参数 | 34B 总参数,4.3B 激活(含音频编码器) | 约 600M | 约 600M |
| 上下文 | 65,536 tokens,单段录音最长 1 小时 | 短片段为主 | 流式 chunk |
| 语言 | 40 种 | 早期版本聚焦英文 | 社区口径约 32 种 |
| 原生能力 | 转写、翻译、语种识别、说话人标签、词级时间戳、音频问答 | 纯转写 | 纯流式转写(需外挂说话人分离) |
| 部署下限 | 1×H100 80GB(BF16) | CPU / 入门 GPU | CPU 可跑,量化后核显可跑 |
| 许可 | OpenMDW-1.1,可商用 | 开源权重 | 开源权重 |
先把关键前提说清楚:本仓库是一个离线(offline)模型,不是实时流式 ASR 服务。这一点在 README 里被反复强调——统计口径里那串encoder_chunk_size_seconds: 30.0(见 config.json)只是编码器的内部切块粒度,不代表 30 秒延迟,也不构成流式承诺。仓库甚至给特征提取器起了个容易误解的名字NemotronAsrStreamingFeatureExtractor(见 processor_config.json),但它的作用只是 mel 特征的前端实现,与流式解码无关。
同题同音频:把测量口径对齐之后还剩什么
真正的"同题实测"要控制四个变量,而公开数据往往只满足其中两个。
第一,任务边界。30B 路线的一次调用可以同时输出转写文本、<spk:N>说话人标签和词级时间戳,流式小模型的一次调用只输出文本。如果把"带说话人分离的 cpWER"和"纯文本 WER"放在同一列比较,得到的是伪结论。
第二,批处理吞吐与单请求延迟。仓库给出的RTFx是批量负载下的总音频时长 ÷ 处理时间,官方明确写了"这不是单请求延迟估计"。
第三,语料差异。LibriSpeech test-clean 的 1.09% 和 URGENT24 的 11.00% 差了十倍,同一个模型、同一套 prompt,差别全在录音条件。
第四,prompt 也是被测变量。这个模型对指令高度敏感。仓库记录的对照实验很能说明问题:LibriSpeech test-clean 语音混入远场 NOTSOFAR-1 音频后,用"只转写靠近麦克风的说话人"这条 prompt,WER 是 4.78%;换成普通单人转写 prompt,WER 直接升到 27.32%。也就是说,在你的实测脚本里换一句 prompt,就能让结论反转。
所以下面这张"统一口径表"里,我把每一格都标了它的真实含义,而不是只放一个数:
| 指标 | 30B 稀疏 | 0.6B 端侧 | 流式小模型 |
|---|---|---|---|
| 英文 9 数据集平均 WER | 4.71% | 约 6.05%(v2 公开口径) | 未按同口径公布 |
| 长音频平均 WER | 10.98% | 13.37%(v3) | 不适用(分段流式) |
| 多语种 7 语言平均 WER | 4.62% | 早期版本不覆盖 | 未按同口径公布 |
| 多人会议 cpWER 均值 | 15.80% | 需外挂分离模型 | 需外挂分离模型 |
| 单请求响应 | 30 秒音频约 0.13 秒(NIM,NVFP4+投机) | 秒级以内 | 约 0.32 秒增量延迟 |
| 批量吞吐 | 最高 2,238 秒音频/秒(RTX PRO 6000) | 视引擎而定 | 不适用 |
这张表里最值得盯的不是加粗的那几个 4.x,而是"cpWER 15.80"这一行:它是 AMI、CALLHOME、Mixer 6、NOTSOFAR-1 完整会话的说话人归因后词错率,和纯文本 WER 完全不是一个难度。竞品最好成绩是 DiCoW v3.3 + DiariZen 的 16.83,差距只有 1 个点——这说明"转写 + 分人"这个组合任务的难度天花板已经被抬得很高,30B 的优势是有限但真实的。
速度轴:RTFx 的三档,和"开源版慢一倍"这件事
仓库给了一张很克制的吞吐表,全部在单张 H100 80GB、批量英文 ASR、关闭 reasoning 的条件下测得:
| 解码模式 | 近似 BF16 RTFx |
|---|---|
| MTP2 | 410 |
| MTP8 | 270 |
| DFlash2 K=7 | 340 |
| DFlash2 K=15 | 260 |
有两个反直觉的点值得写进选型文档。一是投机 token 数不是越多越好:MTP8 反而比 MTP2 慢(270 vs 410),DFlash2 的 K=15 也比 K=7 慢(260 vs 340)。原因是验证开销随提议长度上升,而 ASR 输出本身冗余度高、短提议的接受率已经够用。仓库对此的建议很直接:K=7 是推荐起点。
二是"官方最靓的数字"和"你能拿到的数字"不是一回事。那份每秒 2,238 秒音频、30 秒录音 0.13 秒出稿的成绩,来自 Nemotron Transcribe NIM 里优化过的 vLLM 实现,官方明确写了这些优化尚未进入开源发布,开源侧随 NeMo Speech 一起发布的 vLLM 集成"预计大约慢一倍",定位是模型开发用途。做容量规划时如果按 2,238 去算,机器会不够。
至于 0.6B 路线,速度优势不来自模型结构而来自部署形态。社区在纯 C++ 推理引擎 audio.cpp 上的实测口径是:同一台机器、同一批量化模型,相对原始 Python 推理环境有2×~9× 加速,输出与 Python 版本逐字节一致,流式低延迟特性保持不变。这三条里,"逐字节一致"其实是最有工程价值的一条——它意味着量化 + 换引擎不会引入新的回归面,你的评测基线还能用。
流式路线的速度指标根本不该用 RTFx 衡量的,它卖的是增量延迟。社区口径是 0.32 秒级、人耳基本无感,配套的说话人分离模型约 1 亿参数、最多 8 人、相比前代 DER 平均下降约 42%,重叠语音场景最多下降七成,推理速度提升 3.7~4.7 倍。这些是宣传与实测混合口径,选型时应以自己场景的重叠语音比例重新测。
显存轴:67.5 GB 是硬门槛,量化档位决定你买什么卡
不需要看参数表,直接看权重文件大小。仓库里的 model.safetensors 是 LFS 指针,记录的原始体积是67,496,013,734 字节,约 67.5 GB——正好对上 34B 参数 × 2 字节的 BF16 布局。这意味着:
- 单卡 BF16 推理的硬下限是 80GB 级(H100/H200/A100 80G)。仓库默认启动参数就是
--gpu-memory-utilization 0.94,几乎吃满。 - 想上更便宜的卡必须走 FP8 或 NVFP4 量化变体,而量化档位和架构强绑定:按仓库的硬件兼容表,L40S 48GB 只支持 FP8,RTX 5090 只支持 NVFP4,A100 只支持 BF16。这张表比任何 benchmark 都更能决定采购单。
- 想在笔记本级别跑,这条路走不通。RTX PRO 6000 Blackwell、DGX Spark(GB10)、Jetson Thor 都在支持列表里,消费级台式卡只有 RTX 5090 的 NVFP4 一档。
对照另外两条路线,量级差距是数量级的:0.6B 模型 BF16 约 1.2 GB,8-bit 量化后 0.6 GB 上下,配合 KV/SSM 缓存也在 GB 级以内;流式小模型的常驻显存同样在 GB 级。30B 路线的显存需求大约是另两条的 50 倍,这就是它必须用稀疏 MoE 换激活效率的原因。
另外别忘了 Mamba 的隐性成本。仓库 config 里--mamba-cache-mode none必须和--use-replayssm --mamba-backend flashinfer一起用,且 prefix caching 要关闭(见启动参数--no-enable-prefix-caching)。混合架构的缓存策略和纯 Transformer 完全不同,照抄 Llama 系的 vLLM 调参经验会踩坑。
准确率轴:中文读者最该看的三行
榜单头部是英文,但中文场景的真实数字藏在小表里,而且不太好看:
| 中文相关基准 | 指标 | 结果 |
|---|---|---|
| FLEURS 中文 | CER | 8.62% |
| CoVoST2 中文 | CER | 9.74% |
| ASCEND(中英混说) | 混合错误率 | 18.74% |
| AISHELL-4(会议,含分人) | cpCER | 32.91% |
| AliMeeting(会议,含分人) | cpCER | 34.95% |
配套的语言支持矩阵把中文标为Supported,而不是英文、法语、德语、日语所在的 Leading / Production-ready 档。这个分级很诚实:中文能转,但不是它最强的地方;中英混说和中文会议分人,错误率会跳到 20%~35% 区间。
这也解释了 0.6B 端侧路线为什么在中文社区仍然有市场:当你的音频是清晰单人、以英文或少数语种为主、对成本极度敏感时,一个 6.05% WER 的 0.6B 模型可能已经超过你的可用阈值,而 30B 的边际收益换不来 50 倍显存。反过来,如果你要的是"一场两小时中英夹杂的会议,输出带说话人和时间戳的稿子",那 32.91 的 cpCER 反而是目前能拿到的最好水平之一——注意是"之一",不是"唯一"。
架构视角:4.3B 激活是怎么省出来的
把 config.json 和 llm_backbone/config.json 对照着读,稀疏的来源一目了然:
- 语言骨干:
layers_block_type一共 52 个 block,其中只有7 层是全注意力,其余是 Mamba-2 与 MoE 交替。n_routed_experts: 128、num_experts_per_tok: 6、另有 1 个共享专家(n_shared_experts: 1,中间维 3712)。每 token 只走 6/128 的路由专家,这是 34B 总参数只有 4.3B 激活的主要来源。 - 感知模块:ASR 编码器是 32 层 Transformer、d_model 1280、subsampling_factor 8;
modality_adapter用IdentityConnector(d_model 1280)直接接到 2688 维的语言骨干输入——中间没有复杂的重采样网络,靠的是训练期对齐。 - 说话人分支:
diarization_model_cfg里是一个 Sortformer,max_num_of_spks: 8,streaming_mode: true,chunk_len: 264(约 2.6 秒)、spkcache_len: 264。整个说话人编码器约 99M 参数,被冻结(freeze_diar: true)后作为特征提供方。 - 时间戳:
ctc_timestamps头有 23,862 类,词表里除文本 token 外还塞进了<spk:0>~<spk:15>、<ev:laughter>、<ev:throat_cough>这类事件标签和<sty:*>风格标签。这就是"富转写(rich transcription)"能一次输出的底层原因。 - 投机解码:
mtp.enabled: true、num_nextn_predict_layers: 2、use_repeated_layer: true。两个训练出的 MTP 深度,推理时复用同一层可以撑到 8 个投机 token——所以 MTP8 不需要 8 个独立的 MTP 层。
这套设计的目标很明确:把"一小时长音频 + 分人 + 时间戳 + 多任务指令"塞进单张 80GB 卡。牺牲的是流式能力(Mamba 状态 + 离线批处理天然不适合增量解码)和端侧可行性。
落地时会撞上的五个坑
坑一:把 30 秒内部切块当成 30 秒延迟。README 专门辟谣过这一点。encoder_chunk_size_seconds: 30.0是编码器处理特征的分块,模型整体仍是"整段音频进、整段文本出"。
坑二:时间戳和投机解码互斥。词级时间戳依赖 CTC 对齐,且必须走 V1 model runner:
export VLLM_PLUGINS=nemo_speechlm export VLLM_MAX_AUDIO_DECODE_DURATION_S=3600 # 默认 600 秒,长音频必须放开 export VLLM_MAX_AUDIO_CLIP_FILESIZE_MB=180 export VLLM_USE_V2_MODEL_RUNNER=0 # 时间戳必需 vllm serve ./NVIDIA-Nemotron-3.5-Transcribe-30B-A4B \ --trust-remote-code --dtype bfloat16 --gpu-memory-utilization 0.94 \ --max-model-len 65536 --max-num-batched-tokens 4096 --max-num-seqs 192 \ --use-replayssm --mamba-backend flashinfer --mamba-cache-mode none \ --limit-mm-per-prompt '{"audio":{"count":6,"length":120000000}}' \ --speculative-config '{"method":"mtp","num_speculative_tokens":8}' \ --middleware nemo.collections.speechlm2.vllm.salm.ctc_serving.ctc_timestamp_middleware而 DFlash2 只能跑在 V2 runner 上,官方明确"使用 DFlash2 时不可用词级时间戳",还要用--hf-overrides '{"ctc_timestamps":{"enabled":false}}'关掉对齐头。要在吞吐和时间戳之间做选择,这是一个二选一,不是可以叠加的开关。
坑三:SGLang-Omni 是能力裁剪版。它支持/v1/audio/transcriptions整文件转写,一张卡、BF16/FP8 均可,但词级时间戳、说话人归因、翻译和投机解码都不在这个集成里。如果你的产品依赖分人,别指望换个 serving 框架就能平移。
坑四:长音频输出 token 比想象的多。官方提示一小时的高密度对话可能需要约 20,000 个输出 token。默认max_tokens=16384会截断,长录音场景要么调高,要么直接不传。
坑五:模型会陷入复读。仓库给了一段前置清理函数,官方说明转写、翻译、语种识别这三类结果在评测时都套用了它:
import re CHAR_RUN = re.compile(r"(.)\1{20,}", re.DOTALL) BLOCK_RUN = re.compile(r"(.{1,4096}?)\1{19,}", re.DOTALL) def cleanup_generation_loops(text: str) -> str: # 连续重复 ≥21 次的单字符压成一个 text = CHAR_RUN.sub(lambda m: m.group(1), text) # 1~4096 字符的整块重复 ≥20 次的压成一份 return BLOCK_RUN.sub(lambda m: m.group(1), text)这段代码改编自 Qwen3-ASR 的重复修复实现,许可为 Apache-2.0 而非 OpenMDW-1.1,仓库在 THIRD_PARTY_NOTICES.md 里做了标注,licenses/目录下也放了对应的许可证文本。生产环境里这段必须前置,否则一次复读就能把下游的字幕时间轴全部带偏。
自己跑一轮同题实测的最小骨架
如果你要复现"同题同音频"的对照,别用各自的 demo 页面各点一次。用一个脚本把三条路线拉到同一份输入、同一套度量上:
import base64, time from pathlib import Path from openai import OpenAI import jiwer AUDIO = Path("meeting_10min.wav") REF = Path("meeting_10min.txt").read_text(encoding="utf-8") DURATION = 600.0 # 秒 def run(base_url, model, prompt, timestamps=False): client = OpenAI(base_url=base_url, api_key="EMPTY") audio = base64.b64encode(AUDIO.read_bytes()).decode() extra = {"chat_template_kwargs": {"enable_thinking": False}, "skip_special_tokens": False, "seed": 0} if timestamps: extra["mm_processor_kwargs"] = {"capture_ctc_timestamps": True} t0 = time.perf_counter() r = client.chat.completions.create( model=model, temperature=0, messages=[{"role": "user", "content": [ {"type": "text", "text": prompt}, {"type": "audio_url", "audio_url": {"url": f"data:audio/wav;base64,{audio}"}}]}], extra_body=extra) elapsed = time.perf_counter() - t0 text = r.choices[0].message.content return {"text": text, "sec": round(elapsed, 3), "rtfx": round(DURATION / elapsed, 1), "wer": round(jiwer.wer(REF, text) * 100, 2)}三个必须写进报告的细节:
temperature=0且enable_thinking=False——转写任务的官方推荐采样就是这个组合,开了 reasoning 会改变输出分布,横向对比失去意义。skip_special_tokens=False——不开这个,<spk:N>和<ev:*>会被吞掉,分人能力根本没被测到。- 测 RTFx 时要先把预热请求打掉。官方提醒首次请求包含 kernel 编译与预热,会显著偏慢。
三条路线各自的适用场景清单
选 30B 稀疏路线,当且仅当你同时满足:
- 有 80GB 级单卡,或愿意接受 FP8/NVFP4 量化变体的硬件限制;
- 输入是整段文件而不是麦克风实时流;
- 任务复杂度超过"把话变成字"——需要说话人标签、词级时间戳、翻译、语种识别、或者"这段录音里提到了哪些金额"这类音频问答;
- 能接受中文处于 Supported 档、中文会议 cpCER 在 30% 上下的现实,并准备好了人工校对环节。
选 0.6B 端侧路线,当且仅当:
- 音频以清晰单人、少数语种为主,6% 左右的 WER 已在可用线以内;
- 部署环境是笔记本、开发板、核显,甚至纯 CPU;
- 你要的是"转写引擎"而不是"语音助手",不需要模型理解语义或回答问题;
- 单机要跑很多并发的小片段,显存和功耗是真约束。
选流式小模型路线,当且仅当:
- 延迟是产品指标本身——字幕、直播、实时会议、语音输入法;
- 你能接受"分离模型 + ASR 模型"的两段式拼装,承担两条流水线的对齐与合流成本;
- 说话人数量有上界(社区口径 8 人),重叠语音比例在你的场景里可控。
把三轴压成一句话:30B 稀疏路线用 50 倍显存换"一次调用解决全部语义任务",0.6B 端侧路线用准确率换"任何设备都能跑",流式小模型路线用能力边界换"人类感知不到延迟"。三者不是替代关系,选错的代价通常不是效果差一点,而是架构要重做一遍。
最后提醒一个容易被忽略的事实:这个 30B 模型是 2026 年 10 月 28 日发布的离线模型,它的定位是"最高准确率、可定制、可微调"(README 的 Best For 一栏原文如此),而不是实时服务。如果你的需求清单里"实时"排在第一位,那无论榜单分数多好看,它都不该是你的第一个候选——这一点,模型卡自己写得很清楚。
【免费下载链接】NVIDIA-Nemotron-3.5-Transcribe-30B-A4B项目地址: https://ai.gitcode.com/hf_mirrors/nvidia/NVIDIA-Nemotron-3.5-Transcribe-30B-A4B
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考