@YichiZ03: https://x.com/YichiZ03/status/2078588932191895976
摘要
MOSS-TD是一个说话人感知的ASR系统,在SGLang-Omni服务栈中进行了优化,能够在单张H100上以约49秒转录38分钟的多说话人音频,并支持16个会议的并发处理。
查看缓存全文
缓存时间: 2026/07/20 13:26
优化ASR模型以转录90分钟多说话人音频
概述
MOSS-TD 是一个说话人感知的 ASR(自动语音识别)系统:它不仅将语音转换为文本,还能识别出谁在说话以及说话的时间,适用于长达约 90 分钟的录音。贡献者将 MOSS-TD 集成到 SGLang-Omni 服务栈中,在此过程中修正了正确性问题,并叠加了一系列性能优化。关键成果:在单张 H100 上,一段 38 分钟的多说话人会议经过处理后,输出一份带有说话人标签和时间戳的转录文本,耗时约 49 秒——这比单纯听完录音的第一分钟还要快。如果同时运行 16 个这样的会议,同一 GPU 每秒处理约 97.5 秒的音频。本文档的其余部分将介绍这些数字背后的工程技术。
ASR 模型基础
ASR 输入音频波形,输出文本。MOSS-TD 采用“音频 LLM”设计:Whisper 风格的编码器将音频转换为连续嵌入,FFN 适配器将这些嵌入投影到仅解码器 LLM 的令牌空间中,然后 LLM 自回归地生成转录文本——包括说话人标签和时间戳。
整个流水线分三个阶段:
-
编码器 — 将波形转换为 80 维对数梅尔频谱图,经过 24 层 Whisper Transformer 处理,通过合并相邻帧进行 4 倍下采样,再经过 FFN 适配器(Linear → SiLU → Linear → LayerNorm,4096→1024)投影为 LLM 嵌入空间中的连续嵌入向量。
-
LLM 预填充 — 这些嵌入替换提示中的占位令牌,Qwen3 处理整个提示以构建其 KV 缓存——对于短音频是一次前向传播,对于长音频则分为多个 4096 令牌的块(详见下文)。
-
自回归解码 — Qwen3 逐个令牌生成转录文本,包括 [S01]/[S02] 等说话人标签和时间戳,直到遇到结束符。
| 组件 | 规格 |
|---|---|
| 模型架构 | MossTranscribeDiarizeForConditionalGeneration |
| 音频编码器 | Whisper 编码器(24 层,d_model=1024) |
| 适配器 | FFN:Linear→SiLU→Linear→LayerNorm,4096→1024 |
| 文本解码器 | Qwen3(28 层,hidden=1024,GQA 16/8) |
| 输出 | 带说话人标签和起止时间戳的转录文本 |
| 端点 | /v1/audio/transcriptions |
长音频的分块预填充
一段 90 分钟的录音可能产生数万个编码后的令牌。一次性预填充所有这些令牌会长时间占用 GPU 并导致激活内存激增——而且由于预填充和解码共享同一个调度循环,其他正在进行的请求的解码会在这段时间内全部停止。分块预填充避免了这个问题:它将序列拆分为每块 4096 个令牌,每个调度步骤处理一个块,并在其间穿插其他请求的解码步骤。代价是长请求本身的预填充会稍晚完成(分布在更多调度轮次中),但换来的是有界的解码停顿,以及共享 GPU 的其他请求的流畅处理。当请求处于分块预填充过程中时,流式输出会被完全抑制,因此中间内部状态不会泄漏出来像是转录文本。
ASR 与 TTS 对比
ASR 和 TTS 在 SGLang-Omni 中共享大部分服务机制——相同的 OmniScheduler、CUDA graph 处理、KV 缓存管理和连续批处理,并且两者都是带有 LLM 主干的自回归模型(MOSS-TD 和 MOSS-TTS 都恰好使用 Qwen3 是巧合——主干选择因模型而异)。它们真正不同的地方在于编码内容、生成内容以及流水线形状:
| 维度 | ASR (MOSS-TD) | TTS (Higgs / MOSS-TTS) |
|---|---|---|
| 音频表示 | 连续特征(mel → 编码器隐藏状态) | 离散编解码令牌(RVQ 多码本) |
| 数据流 | 音频 → 文本 | 文本 → 音频 |
| 音频解码器/声码器 | 不需要——输出是纯文本 | 需要用于重建波形 |
| 典型输入长度 | 非常长(MOSS-TD 可处理约 90 分钟) | 短(参考语音:数秒) |
| 流水线阶段 | 单阶段(编码器 + LLM) | 多阶段(编码器 → AR 引擎 → 声码器) |
| 流式输出 | 增量文本输出 | 流式音频 + 流式声码器 |
由于 ASR 从不涉及声码器,其工程挑战转移到了其他地方:长输入将真正的优化工作推向了自回归解码循环和长序列内存管理,而不是音频重建质量。关于 TTS 方面的故事,请参阅姊妹文章《优化 TTS 推理》(tts-optimization.md)。
时间消耗分析:性能剖析
在进行任何更改之前,团队在单张 H100(开启 CUDA graphs,bf16)上对 MOSS-TD 进行了性能剖析,以观察时间到底花在哪里。
| 音频长度 | 并发数 | 编码器 | LLM 预填充 | 自回归解码 |
|---|---|---|---|---|
| 5 秒 | 1 | 18.9% | 14.7% | 76.4% |
| 5 秒 | 4 | 20.0% | 22.3% | 57.7% |
| 5 秒 | 16 | 38.2% | 29.7% | 32.1% |
| 60 秒 | 1 | 4.0% | 2.1% | 94.0% |
| 60 秒 | 4 | 5.0% | 4.6% | 90.4% |
| 60 秒 | 16 | 13.7% | 9.5% | 76.8% |
| 20 分钟 | 1 | 4.7% | 0.8% | 94.5% |
| 20 分钟 | 4 | 9.2% | 1.9% | 88.9% |
| 20 分钟 | 16 | 11.6% | 2.6% | 85.7% |
两点突出:对于长音频且并发数为 1 时,解码占用超过 94% 的总时间,几乎全部的优化潜力都在这里;对于短音频且并发数为 16 时,编码器和预填充合计占比 68%,因此编码器侧的工作也值得做。
优化策略
大量优化工作复用了已为 TTS 服务构建的基础设施,并针对 ASR 更简单的流水线(无声码器、无多码本采样)进行了适配——CUDA graphs、异步解码和编码器缓存再次出现。
编码器
-
CUDA Graph. 由于 Whisper 编码器始终处理固定的 30 秒窗口(音频被切分为 30 秒的块,最后一块填充),每个块都具有相同的形状——因此编码器按每个请求的块数(默认最多 8 块,约 4 分钟音频)进行分桶,每个桶捕获自己的 CUDA graph,以消除每次调用的内核启动开销。
-
Torch Compile(可选启用). 作为 CUDA graph 的替代方案,
torch.compile(self.whisper_encoder, dynamic=False)在顶部增加了内核融合。默认的编译模式被有意跳过:reduce-overhead 模式管理自己的内部 CUDA graphs,这些图会与同一进程中运行的解码侧 CUDA graphs 冲突。这会牺牲第一次调用(每桶一次编译)的速度,以换取更稳定的稳态吞吐量,对于编码器密集、高并发、短音频的工作负载值得开启。 -
LRU 缓存. 编码器的输出对于给定输入是确定性的,因此输出被缓存在 CPU 上(最多 64 个条目,4 GB),以波形哈希为键;缓存命中时完全跳过编码器,并将缓存的嵌入异步复制回 GPU。与 TTS 不同(相同的参考语音经常在多个提示中重复使用),ASR 输入在生产环境中通常是唯一的——因此此缓存对重试、A/B 测试不同解码设置以及本地开发更有效,生产环境命中率不高。
自回归解码
-
CUDA Graph. 解码批量大小被填充到固定桶(1、2、4、8…),每次生成一个令牌时重放捕获的 CUDA graph——与 TTS 解码使用的技术相同。
-
异步解码. 与 TTS 使用的一步超前技巧相同:启动当前解码步骤的 GPU 工作,同时并行处理上一步的主机侧工作(设备到主机拷贝、完成检查、分发结果)。批量大小为 1 时退化为同步执行,因为没有足够的主机侧工作来使重叠有意义。两个交替的固定主机缓冲区确保 GPU 的异步写入和 CPU 的读取不会竞争。这在高并发下显著提升了吞吐量,同一更改还修复了由超前机制超出范围导致的 KV 缓存槽泄漏。
-
流式输出. 转录文本通过 SSE 在生成时流式输出,受三条规则约束:令牌按请求缓冲,默认在 50 毫秒窗口后刷新(第一个令牌立即输出,结束符始终强制刷新);所有流式输出在请求处于分块预填充时被抑制,因此中间状态永远不会看起来像转录文本;如果缓冲的令牌中途包含多字节 UTF-8 字符,则刷新会等待下一个令牌完成该字符。
批量推理
在编码器侧,不同长度的梅尔频谱图被对齐,以便多个请求可以共享一次批处理 Whisper 前向传播。在 LLM 侧,多个请求的令牌序列被打包到共享批次中,用于预填充和解码,从而在并发负载下保持高 GPU 利用率。
基准测试结果
基准测试使用了两个私有多说话人数据集,涵盖输入长度范围:
- Movies(movies800times)—— 800 个短对话片段,每个约 12 秒。
- AISHELL-4 Long(aishell4_long)—— 20 个长会议录音,每个约 38 分钟。
两个数据集均为私有许可;请联系 MOSS 团队获取访问权限。
这里关注两个指标:RTF(实时因子)是处理时间除以音频时长——小于 1 表示快于实时。audio_s/s 是每秒壁钟时间处理的音频秒数,这真正反映了批处理带来的吞吐量提升。
设置: 单张 H100 80GB,单 GPU 同地部署;MOSS-Transcribe-Diarize 采用 bf16,开启 CUDA graphs,贪心解码;服务器设置为 max_running_requests = cuda_graph_max_bs = 16,mem_fraction_static = 0.80。Movies 数据为 3 次运行的平均值;AISHELL-4 Long 每个数据点运行一次,因为每个请求是一段约 38 分钟的会议。精度在并发数 16 时测量一次(贪心解码使其与并发数无关)。
Movies——短多说话人对话(N=800)
| 并发数 | 吞吐量 (req/s) | RTF 均值 | audio_s/s | 延迟均值 (s) | 延迟 p95 (s) |
|---|---|---|---|---|---|
| 1 | 4.55 | 0.022 | 52.7 | 0.219 | 0.500 |
| 2 | 8.40 | 0.024 | 97.2 | 0.238 | 0.544 |
| 4 | 14.96 | 0.027 | 173.2 | 0.267 | 0.610 |
| 8 | 24.90 | 0.033 | 288.1 | 0.321 | 0.714 |
| 16 | 32.79 | 0.043 | 379.5 | 0.422 | 0.935 |
从并发数 1 到 16,吞吐量和 audio_s/s 均扩展约 7.2 倍(4.6→32.8 req/s;53→379 audio_s/s)。RTF 全程远低于 1(0.022→0.043,即大约 23–45 倍快于实时),平均延迟始终在亚秒级别。随着批次填满,短音频 ASR 更依赖编码器和预填充,因此每请求 RTF 逐渐上升,但总吞吐量继续攀升。
AISHELL-4 Long——长会议(N=20,每个约 38 分钟)
| 并发数 | 吞吐量 (req/s) | RTF 均值 | audio_s/s | 延迟均值 (s) | 延迟 p95 (s) |
|---|---|---|---|---|---|
| 1 | 0.021 | 0.021 | 47.0 | 48.77 | 71.1 |
| 2 | 0.030 | 0.028 | 69.7 | 64.97 | 78.5 |
| 4 | 0.034 | 0.048 | 77.7 | 110.2 | 142.7 |
| 8 | 0.037 | 0.081 | 85.4 | 184.8 | 239.2 |
| 16 | 0.043 | 0.127 | 97.5 | 291.0 | 374.5 |
每个请求都是一段约 38 分钟的会议,解码占主导地位,因此每秒请求数自然低,RTF 是关键指标。单流已能将 38 分钟的会议在 约 49 秒 内转写完成(RTF 0.021,约 47 倍快于实时)。将并发数推到 16,总 audio_s/s 增加了一倍以上(47→97.5),req/s 也从 0.021 增加到 0.043;在批处理争用下,每请求延迟和 RTF 有所增长,但仍保持在 约 8 倍快于实时(RTF 0.127)——这是预期中的权衡:更高的总吞吐量以换取长音频单请求更高的延迟。
精度
在并发数 16 下使用贪心解码测量。CER 是字符错误率;cpCER 添加了最小排列对齐以计入说话人分配;Δ CER 是两者之差,归因于说话人分配错误;DER 是说话人时间戳的日志化错误率。
| 数据集 | 样本数 | CER (%) | cpCER (%) | Δ CER (%) | 说话人时间戳 DER (%) |
|---|---|---|---|---|---|
| Movies | 800 | 5.92 | 13.12 | 7.20 | 21.20 |
| AISHELL-4 Long | 20 | 13.76 | 15.07 | 1.31 | 10.25 |
模型使用
请参阅 MOSS-TD 操作指南以获取部署和使用说明。
后续计划
仍有若干优化工作在持续进行中:
- 流式音频输入 — 音频到达时逐步转录,而不是等待整个片段上传完毕。
- 分块预填充 CUDA Graph — 将分块预填充本身捕获为 CUDA graph。
- 张量并行 — 多 GPU TP 支持,针对非常长的音频文件,其 KV 缓存可能超出单 GPU 内存。
致谢
这项工作体现了 OpenMOSS 团队与 SGLang-Omni 团队的共同努力。
MOSS 团队: 董浩宇、林政远、陈汉夫、张毅阳、高阳、费昭烨、程钦远、李仕敏、邱锡鹏。
SGLang-Omni 团队: 田逸江、景新力、柯祥瑞、郭志豪、张若一、沈立凡、曲金涛、田旭翔、李凯歌、Ratish P、蔡浩广、夏子杰、洪辰辰、叶学松、顾靖雯、邓嘉欣、罗嘉轩、卢心雨、金浩、赵辰阳、张一驰。
了解更多
- 模型: OpenMOSS-Team/MOSS-Transcribe-Diarize
- 服务框架: SGLang-Omni on GitHub
- 操作指南: MOSS-TD in SGLang-Omni
- ASR 优化路线图: tracking issue on GitHub
- TTS 优化博客: Optimizing TTS Inference (tts-optimization.md)
- 编码器偏差分析: The Root Cause of RL Training-Serving Skew (moss-tts-local-batch-encoder-skew.md)
相似文章
@lmsysorg: SGLang-Omni 现已于第0天提供来自 @Open_MOSS 的 MOSS-TTS-Local Transformer v1.5!这是一个开源的 48 kHz 立体声 TTS 模式…
MOSS-TTS-Local Transformer v1.5 是一个开源的 48 kHz 立体声 TTS 模型,具有零样本语音克隆、原生流式传输,并支持31种语言,基于 Qwen3-4B 骨干网构建,通过 SGLang-Omni 提供。
OpenMOSS-Team/MOSS-TTS-Nano-100M
MOSS-TTS-Nano是一个开源的多语言语音生成模型,仅0.1B参数,专为实时TTS设计,可直接在CPU上运行而无需GPU。由OpenMOSS团队和MOSI.AI发布,它支持简单的本地部署,用于Web服务和产品集成。
@tom_doerr: 以70倍实时速度转录音频 https://github.com/m-bain/whisperX
WhisperX是一个用于快速自动语音识别的工具,提供词级时间戳和说话人分离,使用Whisper large-v2实现70倍实时转录。
OpenMOSS-Team/MOSS-TTS-v1.5 · Hugging Face
MOSS-TTS v1.5是一个更新的开源文本转语音模型,具有改进的多语言合成(支持31种语言)、更稳定的零样本语音克隆以及显式的内联停顿控制。
@oliviscusAI: Microsoft 刚发布了一个工具,可以一次性转录整整一小时的音频,并追踪谁在什么时候说话。它叫……
Microsoft 发布了 vibevoice,这是一个7B参数的模型,可以一次性转录长达一小时的音频,内置说话人日志和时间戳,支持50多种语言,并且本地运行,无需API费用。