10 分钟跑通 Audio8 ASR Infinite 流式转写,它比 Whisper 到底快在哪?
【免费下载链接】Audio8-ASR-Infinite项目地址: https://ai.gitcode.com/hf_mirrors/Edge0/Audio8-ASR-Infinite
如果说 Whisper 解决的是"把一段音频变成文字",那么 Audio8 ASR Infinite 解决的是另一个更苛刻的问题:让文字随着声音一起流出来——边说边出字,延迟可配置,且可以 24/7 不间断转写而不漂移。这不是把 Whisper 的窗口切小一点就能做到的,它是一套为"流式"重写的架构:原生流式音频塔、滚动 KV Cache、按帧解耦的音频时钟,以及把"延迟"本身当作训练条件注入解码器。本文以仓库源码为证据,带你 10 分钟跑通第一条流式转写,并回答标题里那个问题:它比 Whisper 到底快在哪。
流式 ASR 的"延迟账本":为什么 Whisper 不够快
Whisper 的经典工作方式是整段音频一次性编码:即使做实时转写,也只能把音频切成 30 秒窗口逐个处理,窗口之间还要做重叠拼接、去重、修错,尾字丢失和重复是社区里反复被吐槽的老问题。这种"分块-拼接"模式决定了它的延迟下不去:延迟主要由"攒够一个窗口"的时间决定,而不是由模型本身决定。
Audio8 ASR Infinite 走的是另一条路——原生流式。它在 README.md 里明确写着几个关键设计:
- 可选音频时钟(audio clock):80 / 120 / 160 ms,每拍输出一个文本 token,对应每秒 12.5 / 8.3 / 6.25 次解码决策;
- 可配置转写延迟(transcription delay):240–560 ms,用延迟换准确率,两者都是显式参数;
- 滚动 KV Cache:上下文窗口固定 30 秒,通过 exact RoPE re-basing 保证显存和延迟恒定,24/7 长转写不漂移。
一句话:Whisper 的延迟是"攒窗口"攒出来的,Audio8 的延迟是"可选项"。
10 分钟清单:环境与模型加载
仓库就是一个可直接加载的完整检查点(非 adapter 风格,必须整体加载),核心文件包括 modeling_audio8_asr_infinite.py、configuration_audio8_asr_infinite.py、config.json,权重则是 8.17 GB 的 model.safetensors 外加独立的语义 VAD 头 semantic_vad_heads.safetensors。
模型加载走标准的transformers接口,auto_map 在 config.json 里已配好:
import torch from transformers import AutoFeatureExtractor, AutoTokenizer from audio8_asr_infinite.modeling.modeling_audio8_asr_infinite import ( Audio8ASRInfiniteForConditionalGeneration, resolve_qwen_language_token_id, resolve_qwen_streaming_special_token_ids, ) checkpoint = "Edge0/Audio8-ASR-Infinite" tokenizer = AutoTokenizer.from_pretrained(checkpoint, trust_remote_code=True) feature_extractor = AutoFeatureExtractor.from_pretrained(checkpoint, trust_remote_code=True) model = Audio8ASRInfiniteForConditionalGeneration.from_pretrained( checkpoint, trust_remote_code=True, torch_dtype=torch.bfloat16 ).eval().cuda()值得注意的一点:from_pretrained是零容忍的。在 modeling_audio8_asr_infinite.py 的Audio8ASRInfiniteForConditionalGeneration.from_pretrained中,ignore_mismatched_sizes被直接禁止,加载后只要存在 missing / unexpected / mismatched keys 就抛RuntimeError,并强制校验weight_format_version=2;加载完成后还会用正弦初始化重建 time embedding 并检查有限性。这意味着"部分转换的权重、临时 hack 的加载"全部不可行——仓库只认自己这份完整权重。
模拟流式推理:核心 API 一次看懂
本地快速验证不需要麦克风,仓库提供了"模拟流式"解码函数simulated_streaming_greedy_decode_batch。它的核心是一个鸭子类型 AudioConfig,把流式时钟的参数显式暴露出来:
class AudioConfig: # duck-typed: raw_audio_samples_per_token / streaming_n_left_pad_tokens / sampling_rate raw_audio_samples_per_token = 1280 # 80 ms @ 16 kHz streaming_n_left_pad_tokens = 18 sampling_rate = 16000 results = simulated_streaming_greedy_decode_batch( model=model, tokenizer=tokenizer, feature_extractor=feature_extractor, waveforms=[waveform], # [-1, 1], 16 kHz mono language_token_ids=[resolve_qwen_language_token_id(tokenizer, "zh")], special_ids=resolve_qwen_streaming_special_token_ids(tokenizer), audio_config=AudioConfig(), num_delay_tokens=[480 // 80], # 6 个延迟 token right_pad_text_tokens=10, dtype=torch.bfloat16, device=next(model.parameters()).device, max_new_tokens=512, ) print(results[0]["final_text"])三个参数请重点理解:
raw_audio_samples_per_token = 1280:16 kHz 采样率下,1280 个采样点正好是 80 ms——这就是"音频时钟"的物理含义,一个流式 token 对应 80 ms 音频。streaming_n_left_pad_tokens = 18:流式解码需要给当前 token 留出左侧上下文窗口,18 个 token ≈ 1.44 秒。它与时钟是配套调优的(见下节表格)。num_delay_tokens = [480 // 80] = 6:目标延迟 480 ms ÷ 时钟 80 ms = 6,即解码器在输出最终 token 前先"多看" 6 拍的音频。延迟参数通过t_cond注入——这正是它"可配置"的来源。
围绕这套 API,仓库内部是一条完整的流式链路:get_audio_features → group_audio_hidden_states → multi_modal_projector → build_t_cond → forward_language_model_with_delay。其中group_audio_hidden_states负责把音频塔的 20 ms 帧(audio_tower_frame_ms=20,见 config.json)按frame_len分组拼成流式 token;build_t_cond用 Voxtral 风格的正弦 time embedding 生成延迟条件向量,逐层经ada_rms_norm调制 Qwen 解码器(对应 modeling_audio8_asr_infinite.py 中的Qwen2RealtimeV1DecoderLayer/Qwen3RealtimeV1DecoderLayer)。模型对生成过程也是流式友好的:_prepare_model_inputs支持input_features生成器(generator),音频耗尽时_has_unfinished_sequences自动判定流结束。
命令行版本更省事:
python -m audio8_asr_infinite.examples.torch_streaming_decode \ --checkpoint /path/to/checkpoint \ --audio sample.wav --language zh --transcription-delay-ms 480音频时钟 × 转写延迟:一张表看懂调优
流式参数不是随意组合的。仓库在 config.json 和 README.md 里给出了后训练优化过的组合(其他组合可用但性能未必最优):
| 音频时钟 | frame_len | streaming_n_left_pad_tokens | 可选target_delay_ms |
|---|---|---|---|
| 80 ms | 4 | 18 | 240 / 320 / 480 / 560 |
| 120 ms | 6 | 12 | 240 / 480 |
| 160 ms | 8 | 9 | 320 / 480 |
注意config.json里还有一张num_delay_tokens_by_frame_len映射表,把毫秒延迟换算成延迟 token 数(如 80 ms 时钟下 480 ms = 6 个 token)。换算规则在 configuration_audio8_asr_infinite.py 的resolve_num_delay_tokens中硬性校验:target_delay_ms必须是时钟的整数倍,streaming_frame_ms必须是音频塔帧时钟 20 ms 的整数倍。所以在 80 ms 时钟下你能选 240/320/480/560 ms,但选不了 300 ms——这不是 bug,是架构约束:每个流式 token 携带的延迟信息必须以整拍为单位。
调优的直观含义是:时钟越细,感知粒度越密(每秒 12.5 次决策),但左侧 pad 上下文也要越多(80 ms 时钟需要 18 个 pad token,160 ms 时钟只要 9 个),资源开销随之上升。延迟则是准确率的"价签"——README 的评估表给出了 480 ms 延迟 + 80 ms 时钟下的结果:
| 测试集 | 指标 | Audio8 ASR Infinite | Voxtral-Mini-4B-Realtime | nemotron-3.5-asr-streaming |
|---|---|---|---|---|
| aishell1/test | CER | 1.750 | 16.795 | 12.927 @560ms |
| aishell4/test | CER | 2.893 | 16.456 | 14.677 @560ms |
| librispeech test.clean | WER | 3.042 | 2.210 | 3.353 @560ms |
| librispeech test.other | WER | 6.808 | 5.552 | 7.140 @560ms |
| 平均 | 3.623 | 10.253(2 组) | 9.524 |
同样是流式模型,中文集上 Audio8 把 CER 压到了个位数以内,而英文集上它落后 Voxtral-Mini 一截——中文是它的主战场。README 特别注明这些结果是 greedy decode + 抑制 EOS、无重复循环、无尾字丢失。
语义 VAD:谁在判断"说完了"?
流式转写最容易翻车的地方是断句:用户思考停顿会被当成说完,结巴重复会被当成新句子。传统声学 VAD 看的是能量阈值,Audio8 换了个思路——在文本解码器的最终隐藏状态上挂了语义 VAD 头(semantic_vad_heads.safetensors)。
配置在 config.json 中声明了 4 个预测时域:0.5 / 1.0 / 2.0 / 3.0 秒,每个时域一个 8 类分类器,预测"该时域内将出现的语义单元数量",类别 0 即"话轮结束"。实现上,Audio8ASRInfiniteForConditionalGeneration.__init__会按配置的 horizons 动态attach_semantic_vad_heads,forward 时用一个 forward hook 只捕获最后一层 norm 的输出(不保留全部激活,省显存),然后逐头打分输出semantic_vad_logits。也就是说,VAD 和转写共享同一条音频前向链路,不需要额外跑一个音频模型——这是"原生"二字的又一个体现。
快在哪:三个机制层面的答案
回到标题的问题,结合源码可以给出三个明确的机制答案:
- 增量解码,而不是整段重算。每 80 ms 只对新增的音频帧做一次前向,音频塔在 20 ms 帧时钟 + 750 帧滑动窗口上持续前进(config.json 中
sliding_window: 750),解码器复用 KV Cache 增量出 token。Whisper 每次都要处理整个 30 秒窗口,计算量随音频长度增长;Audio8 的每拍代价是常数。 - 滚动 KV + exact RoPE re-basing,让"无限长"可行。README 明确指出滚动窗口为 30 秒,配合精确 RoPE 重定基,保证 24/7 长转写中显存和延迟不随时长增长——这正是社区情报里反复出现的"24/7 不漂移"的直接来源。而 Whisper 分块拼接的痛点(尾字丢失、窗口重叠处的重复)在这里被架构上消除了。
- 延迟被"训练"进模型,而不是靠排队。240–560 ms 的可选延迟在训练时就是条件输入(
t_cond),推理时换一个num_delay_tokens就等于换了延迟档位,不需要重新部署。延迟与准确率的权衡是模型本身学出来的,而不是工程上"等多久再吐字"的妥协。
把流式服务真正跑起来:vLLM + Docker
单机模拟流式验证逻辑,生产级 24/7 服务则走仓库的 vLLM + Docker Compose 部署(README 将其列为 canonical 部署路径):
cd docker AUDIO8_MODEL_DIR=/path/to/checkpoint docker compose up -d服务对外暴露 WebSocket 实时接口,同一套栈还附带 Web 演示页面(HTTP 8080 / TLS 8443)。终端验证用仓库自带的客户端:
python -m audio8_asr_infinite.examples.vllm_realtime_client \ --ws-url ws://127.0.0.1:18191/v1/realtime \ --audio sample.wav --language zh --target-delay-ms 480 --pace仓库内还附带了效果演示视频 Audio8-Asr-Infinite-Demo.mp4,可以直观看到边说话边出字、无漂移的实时效果。
从加载权重到跑通第一条模拟流式转写,10 分钟是够用的——前提是理解它和 Whisper 的根本差异:Whisper 是"把一段听完再写",Audio8 是"边听边写,延迟任选"。当你的场景是实时字幕、语音助手、会议转写这类低延迟、长时长、中英双语的需求时,这个原生流式模型给出的答案是:240 ms 起步的延迟、恒定显存、语义级断句,以及写死在权重格式里的可靠性约束。这也是"快"最扎实的注脚——不是跑分快,而是每一拍都在增量前进。
【免费下载链接】Audio8-ASR-Infinite项目地址: https://ai.gitcode.com/hf_mirrors/Edge0/Audio8-ASR-Infinite
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考