Qwen3-ASR 课程 · 第 9 课 · 路线第 5 站 · 推理篇(下)
流式与非流式 —— 边说边出字是怎么做到的
上一课的推理旅程有一个隐含前提:整段音频一次性给齐(Prefill 全量 → Decode 到底)。那是「非流式」。但通话场景要的是边说边出字——音频还在录,字已经在屏幕上跳。同一个模型怎么同时做到两种?答案是三个设计:2 秒块、不封死块、动态窗口。这一课把它们全部拆开。
一、两种模式,两个场景
| 非流式(离线) | 流式 | |
|---|---|---|
| 场景 | 录音文件转写、事后归档 | 通话实时字幕、语音助手 |
| 输入 | 整段音频一次提交 | 按 2 秒块 逐块推进 |
| 输出 | 一次给全文 | 增量给出,最近的内容可能被回改 |
| 上下文 | 全局(整段互为上下文) | 受注意力窗口限制(动态 1–8s) |
| 质量 | 最好(WER 基线) | 差 0.6–1.0 个点(官方 Table 8) |
同一个模型、同一份权重,靠推理时的块调度切换两种模式——这就是第 6 课说的「单模型统一流式/离线」。
二、流式流水线:块是怎么变成字的
- 攒块:音频按 2 秒一块切好排队(说话不停,块不停);
- 增量编码:新块经 AuT 编码,与动态注意力窗口内的历史帧(最长 8 秒)一起注意;
- 增量解码:LLM 只对新块对应的语音包做增量生成,吐出本块的文字;
- 修订或定稿:每块产出的是暂定结果——只有当它离开「不封死窗口」后才定稿;窗口内的内容会被后续上下文回改。
不封死块(unfixed chunks):流式正确性的核心
声学上「四」和「十」、「记录」和「纪录」靠后文才能区分。流式引擎保留最近几块的暂定权(官方评测配置:最后 4 块不封死;示例代码
unfixed_chunk_num=2),配合 5-token fallback(一个位置超 5 个词元未定,强制取当前最优)——在「敢改」和「不能无限等」之间取平衡。
三、走一遍:回改长什么样
例句:「这次事故的纪录要归档保存」。「记/纪」同音,前文无法区分,靠「归档」的语义才能定。三块到达:
| 块到达 | 音频内容 | 屏幕显示(暂定=斜体,定稿=正常) | 发生了什么 |
|---|---|---|---|
| 块 1 | 「这次事故的记…」 | 这次事故的记录已… | 「记录」是当前最优猜测,暂定 |
| 块 2 | 「…录要归…」 | 这次事故的记录要归… | 「归档」语义到达:「记录」进入候选修订 |
| 块 3 | 「…档保存」 | 这次事故的纪录要归档保存 | 不封死窗口内回改:「记录」→「纪录」,前文定稿 |
(示意:回改在真实引擎中作用于语音包对应的词元槽位,且只有在不封死窗口内才允许。)这个机制也解释了为什么流式 WER 更差:窗口截断了部分全局上下文,且凡是没有被及时回改的错误,一旦出窗就永远定稿。
四、延迟解剖与积压数学(可运行)
流式的每块延迟有硬下限:块长本身(2 秒攒齐才编码)。在此之上叠加处理时间——而处理时间与 RTF 直接挂钩:每块处理耗时 = 块长 × RTF。跑这个模拟,看 RTF 过 1 之后发生什么:
# 流式积压模拟:块 2 秒到达一次,处理每块耗时 = 块长 x RTF
def backlog(rtf, chunks=8, chunk=2.0):
proc = chunk * rtf # 每块处理耗时
finish = 0.0
lats = []
for i in range(chunks):
ready = (i + 1) * chunk # 这块音频说完的时刻
start = max(ready, finish) # 要么立刻算,要么排队等上一块
finish = start + proc
lats.append(finish - ready) # 该块的延迟 = 处理完成 - 说完
return lats
for r in [0.5, 1.0, 1.2]:
lats = backlog(r)
print(f"RTF {r:>4}: 各块延迟 = {[round(x,1) for x in lats]}"
f" -> 最终 {lats[-1]:.1f}s" + (" <== 积压!延迟越滚越大" if r > 1 else " (稳定)"))
print("\n读法:RTF<1 时延迟恒定(= 处理耗时);RTF>1 时每块欠 0.4s,")
print("欠账线性累积——第 8 块延迟已达 5.2s。这就是压测里『RTF p95<=0.5』这条 SLO 的出处。")
积压数学就是容量数学
每块处理耗时 = 块长 × RTF。RTF < 1 → 每块都有空闲,延迟恒定;RTF > 1 → 每块欠 (RTF−1) × 块长 的账,线性累积,永不还清。压测报告(0011)里"RTF>1 = 延迟无限堆积,流必死"的定量版本就是这张表——这也是为什么 SLO 把 RTF 卡在 0.5 而不是 1.0:给排队和抖动留一倍余量。
五、端点检测:什么时候算「一句话说完了」
流式输出需要一个「断句」信号,否则文字会一直悬在暂定态。两道工序:
- VAD(语音活动检测):区分说话与静音——静音超过阈值(常见 300–800ms)判定一句结束;
- 端点策略:引擎层面的断句规则(静音时长 + 最大句长 + 语义信号)。断句触发 Final 结果,并重置暂定窗口。
通话场景里端点调参直接影响体感:阈值太短,说话人停顿被切碎;太长,Final 迟迟不出。这一参数在你的业务里要用真实通话录音调。
六、怎么选:一张决策表
| 场景 | 选择 | 理由 |
|---|---|---|
| 通话实时字幕/坐席辅助 | 流式 | 延迟即体验;接受 0.6–1.0 点 WER 代价 |
| 录音归档/质检复盘 | 非流式 | 精度最好,延迟无所谓 |
| 实时 + 高准确归档,两者都要 | 流式先行 + 录音离线复写 | 屏幕即时可用,归档用离线结果覆盖(成本×2) |
checkpoint
至此推理篇(0008/0009/0010)完整:全过程账本 → 两种工作模式 → 亲手跑通。你已经具备读懂压测报告每一条曲线的知识——TTFT 的涨落是 prefill 排队,块间延迟是流式节拍,RTF 积压是容量数学。
检索练习
本课主读材料
去读(约 15 分钟)
报告 §4.5 Streaming Evaluation + Table 8:本课所有数字(2 秒块、5-token fallback、4 块不封死、流式 WER 代价)的原始出处。读法:对照第三节的走例表,看每个配置项各自管住流水线的哪一环。
本课新术语已入 术语表:端点检测/VAD、不封死块/回改。
我是你的老师,别客气
「回改」走例表是我手造的示意——如果你在真实通话里观察到更刁钻的修订案例(数字串改、专名改),贴回来,我们一起拆它撞上了哪个机制的边界。