arXiv'26 | 流式 SpeechLLM:MoChA 让 LLM 边听边转写,延迟降 62.5%
arXiv’26 | 流式 SpeechLLM:MoChA 让 LLM 边听边转写,延迟降 62.5%
原文:Streaming Speech Recognition with Decoder-Only Large Language Models and Latency Optimization(USTC + iFLYTEK,arXiv 2026.01)
1. 前言吐槽
最近 ASR 这个方向的进展基本被一类东西统治了——SpeechLLM。
所谓 SpeechLLM,不是某个模型的名字,是一类架构的统称:把一个音频编码器接到 decoder-only 的 LLM 前面,音频先编成 embedding,当成 token 喂给 LLM,由 LLM 自回归地把文字解码出来。Qwen3-ASR、Kimi-Audio、SALM 这些都属于这一类。这个范式的好处很实在:精度高、prompt 天然就是条件通道,不改架构就能加各种额外信息。
但这套东西有个让人头疼的地方:它天然不支持流式识别。
非流式 SpeechLLM 的工作流是:音频进来 → 全量编码 → 所有 audio token 拼成 prefix 塞进 LLM → 自回归解码出完整文字。这意味着模型必须等整段音频输完才能开始出字。
在 benchmark 上没问题,但一碰到真实场景就卡住了。字幕机、同声传译、语音助手,这些场景要求边说边出字,不能等人把话说完了再统一告知。传统的 encoder-decoder ASR(RNN-T、CTC、MoChA attention)在这方面有一套成熟的”读/写策略”:什么时候等更多音频(read),什么时候把当前音频解码成文字输出(write)。但这套逻辑怎么搬进 decoder-only 的 LLM 里,一直没有干净的解法。
现有的几个尝试(BESTOW、SpeechLLM-XL、BTI)大多绕不过一个坑:先跑一个 CTC 或者混合 ASR 做 forced alignment(强制对齐——用额外模型预先算出每个字对应哪几帧音频),再把这个对齐结果喂给 LLM。这是个级联管道,前一步的错误会传到后一步,两阶段合在一起根本没法端到端优化。
这篇来自中科大和讯飞的工作想做的事情是:把流式能力直接内化进 SpeechLLM,端到端训练,不依赖外部 forced alignment(只有 minLT 损失那一步例外,后面说)。
2. 非流式 vs 流式:先把本质区别说清楚
读这篇论文之前,必须先想清楚一件事:非流式和流式 SpeechLLM 的根本区别在哪。这个想清楚了,后面所有设计决策都是自然而然的。
先看非流式(Figure 1):

工作流程:
- 等整段音频说完(比如 5 秒的一句话)
- 整段音频送进 Conformer(一种结合了卷积和 Transformer 的语音编码器)编码,得到全量的 audio embedding,记作 $h_1, h_2, \ldots, h_N$(N 帧)
- 这 N 个 audio token 全部拼成 prefix,加上 BOS(句首标记)送进 LLM
- LLM 自回归解码:每次生成一个字,直到 EOS(句末标记)
LLM 是 Qwen2.5-1.5B,用 LoRA(Low-Rank Adaptation,只在注意力层加两个低秩矩阵做微调,不动原始权重)适配,rank=32,α=64。
这个架构的关键特点:每次生成字时,LLM 的 attention 能覆盖所有 N 帧音频。所以它知道整句话的全局声学信息,再来决定每个字怎么写。比如”机器学习”里的”习”,它能看到后面没有更多字了,不会误认成”机器学习气”。
代价: 必须等整段音频说完才能开始出字。5 秒的话 latency 至少 5 秒,10 秒的话 10 秒。
流式的目标是:音频还在继续录入时,已经说完的部分就能出字。
但这里有个内在矛盾:你看不到未来的音频,就必须用已有的信息做决策。
这不是这篇论文特有的问题——RNN-T、CTC、所有流式 ASR 系统都面对这个矛盾。解决方案不是消除这个限制,而是接受它:用仅到当前时刻为止的音频来识别,允许一定程度的精度损失,换取实时输出。
这也是为什么论文里流式 CER(5.5%)比非流式(5.0%)高 0.5 个点——那 0.5% 就是”没看到未来音频”的代价,没有办法完全消除,只能优化。
3. 流式架构的三个核心设计
搞清楚了”流式必然牺牲未来上下文”这个前提,再来看这篇怎么把 SpeechLLM 改成流式的。改造后的架构如下:

图从底部往上看:音频输入 → Chunking(分块)→ Speech encoder → Adaptor → Policy network(MoChA)→ 对齐矩阵 → 交错序列 Hy → LLM → 输出文字 + 三路损失($L_{LLM}$、$L_{MoChA}$、$L_{minLT}$)。
三个核心设计依次解释。
3.1 音频分块编码:0.4 秒等一次
“流式”不是逐帧实时处理,而是按固定时间窗口批量处理——这是所有实际流式 ASR 系统的工作方式,Conformer 这类模型计算量太大,帧级触发不现实。
先定几个具体数字方便理解(实际参数类似):
- Conformer 处理后帧率:每 40ms 一帧
- chunk 大小:0.4s = 10 帧
- 历史 context:1.6s = 40 帧
- Conformer 输出维度:每帧是一个 d 维向量(比如 256)
以处理第 2 个 chunk(0.4s~0.8s)为例,走一遍完整流程:
第一步:拼接输入
把”历史 40 帧 + 当前 10 帧”拼成一个序列送进 Conformer:
输入 shape: [50, d]
位置 0~39:第 1 个 chunk 的音频(历史 context)
位置 40~49:第 2 个 chunk 的音频(当前,要编码的目标)
第二步:Conformer 前向
Conformer 是序列模型,每个输出位置对应同一位置的输入时间步:
输出 shape: [50, d]
output[0~39]:历史帧的表示(这些上一个 chunk 已经算过了)
output[40~49]:当前帧的表示,关键——这 10 个向量在做 attention 时
能"回头看"位置 0~39 的历史帧,所以质量比孤立处理更好
第三步:切片,只保留当前帧
chunk_embedding = output[40:] # shape: [10, d]
# 等价于 output[-10:]
就是一个普通的张量切片,output[0:40] 直接丢掉。
第四步:拼到全局序列
处理完 chunk 1 后:全局序列 shape [10, d]
处理完 chunk 2 后:全局序列 shape [20, d] ← 把这 10 帧 append 进来
处理完 chunk 3 后:全局序列 shape [30, d]
...
每个 chunk 无论编号多少,都产出固定 10 帧,维度完全一致。
“历史帧输出直接丢掉”为什么可行?
Conformer 在流式模式下使用因果掩码(causal mask):位置 i 的 attention 只能看到位置 0..i,不能看未来。这意味着 output[39](历史最后一帧)的值完全由位置 0..39 的输入决定,不受位置 40..49 影响——无论这次有没有把当前帧拼进来,历史帧的输出结果完全一样。
所以历史帧的输出是冗余重算,上个 chunk 已经算过了,丢掉没有任何信息损失。喂历史帧进来的唯一目的,是让 output[40~49](当前帧)的 attention 能回头看它们,从而得到更好的表示。
实现上的常见优化:可以缓存历史帧的 Key/Value(类似 LLM 推理时的 KV cache),新 chunk 进来只算 query,历史帧不需要重新前向——结果等价,计算量更小。
为什么 1.6 秒历史 context 不增加延迟?
延迟 = 你要等多久才能开始处理。1.6 秒历史是已经发生的音频,处理第 k 个 chunk 时它早就在缓冲区里,不需要等任何东西。反过来,如果要看”未来 0.4 秒”来辅助当前 chunk,就必须等那段音频说完——每看一个 chunk 的未来就多等 0.4 秒。整个系统的最小延迟只由 chunk 大小(0.4 秒) 决定,context 窗口多大完全不影响。
所以”0.4 秒切成 chunk”的意思是:音频来了 0.4 秒就处理一批,不是等整段说完。第一个字要等的时间取决于 MoChA 何时触发,理论下界就是一个 chunk 的时长(0.4 秒),实际上几个 chunk 之后就能开始出字。
历史窗口只能向后看,不能向前看。确实存在”需要后面的音频才能确定这个字”的情况(比如多音字、上下文依赖强的词),这种情况下流式就会出错,也是精度损失的主要来源。这是流式 ASR 的基本代价。
3.2 MoChA:逐帧扫描,决定何时触发
分块编码解决了”音频怎么持续喂进来”,接下来的问题是:什么时候可以把当前积累的音频交给 LLM 解码一个字?
这就是 Policy network 里 MoChA(Monotonic Chunkwise Attention,单调分块注意力) 负责的事。MoChA 最早在 ICLR 2018 提出,用于 encoder-decoder 模型的流式解码,这篇把它移植进了 decoder-only 框架。
结合 Figure 2 中间的对齐矩阵来解释。矩阵的每一行对应一个输出 token,每一列对应一个音频帧:
对齐矩阵的含义:行 $i$ 里被”选中”的那个帧(图中的深色圆点)就是 $t_i$——这是 MoChA 认为”第 $i$ 个字的音频到这帧为止就够了”的边界。
MoChA 的扫描过程,以预测第 $i$ 个字为例:
- 从上一个字的边界 $t_{i-1}$ 之后开始,逐帧往后扫
- 对每一帧 $j$,计算选择概率 $p_{i,j}$(用一个轻量 decoder 实现)
- 第一个 $p_{i,j}$ 超过阈值的帧,就是边界 $t_i$
- 触发:把第 $t_{i-1}+1$ 帧到第 $t_i$ 帧这段音频,交给 LLM 解码第 $i$ 个字
“Monotonic(单调)”是指 $t_1 \leq t_2 \leq \cdots \leq t_L$——对齐只能向前走,不能回头。这是现实约束:你不能用”之前已经说完的音频”重新决定更早的字。
对齐跑完整句,得到一个序列 $[t_1, t_2, \ldots, t_L]$,和输出文字 $[y_2, \ldots, y_L]$ 一一对应($y_1$ 是 BOS,不需要对应音频;$t_1 = 0$ 作为起点)。
3.3 Interleaving:改变 LLM 的”看法”
有了 MoChA 的对齐,接下来要解决的问题是:怎么让 LLM 适应流式解码?
非流式 LLM 在训练时学到的是”看到全部音频再出字”——这个能力不能直接迁移到流式。需要重新训练 LLM,让它学会”每次只看对应这个字的那一小段音频就能出字”。
做法是把训练序列的格式从:
[全部 N 帧音频 | BOS | y₂ | y₃ | ... | y_L]
改成音频段和文字交错拼接的格式:
\[H_y = [\underbrace{h_1\cdots h_{t_2}_{y_2的音频},\ \underbrace{y_1}_{BOS},\ \underbrace{h_{t_2+1}\cdots h_{t_3}_{y_3的音频},\ \underbrace{y_2}_{第1字},\ \ldots,\ \underbrace{h_{t_{L-1}+1}\cdots h_{t_L}_{y_L的音频},\ \underbrace{y_{L-1}_{倒数第2字}]\]用一个具体的例子来说明。假设识别目标是”你好啊”,加上 BOS 和 EOS,共 5 个 token:$y_1=\text{BOS}$,$y_2=\text{你}$,$y_3=\text{好}$,$y_4=\text{啊}$,$y_5=\text{EOS}$。
MoChA 跑完之后,假设对齐结果是($t_1=0$ 是起点):
- $t_2=3$:第 1-3 帧是”你”的音频
- $t_3=6$:第 4-6 帧是”好”的音频
- $t_4=9$:第 7-9 帧是”啊”的音频
- $t_5=10$:第 10 帧对应 EOS
那么交错序列 $H_y$ 是:
[h₁, h₂, h₃, BOS, h₄, h₅, h₆, 你, h₇, h₈, h₉, 好, h₁₀, 啊]
↑___________ ↑ ↑___________ ↑ ↑___________ ↑ ↑ ↑
"你"的音频 BOS "好"的音频 你 "啊"的音频 好 EOS前 啊
LLM 拿着这个序列做训练,学到的是:
- 看到
[h₁,h₂,h₃, BOS]→ 预测”你” - 看到
[..., h₄,h₅,h₆, 你]→ 预测”好” - 看到
[..., h₇,h₈,h₉, 好]→ 预测”啊” - 看到
[..., h₁₀, 啊]→ 预测 EOS
这就是 interleaving 的本质:把”用整段音频预测整句话”的训练目标,拆解成”用这段字对应的音频 + 上一个字,预测下一个字”。LLM 学完之后,推理时直接可以流式工作。
3.4 推理时的流式循环
明白了 interleaving,推理就很直白了:
初始状态:BOS token,KV cache 为空
循环:
1. 持续录入音频,编码新 chunk
2. MoChA 从上次边界处开始扫描,直到触发(找到 t_i)
3. 截出这段新音频 h_{t_{i-1}+1 : t_i},拼上上一个预测字
4. 送进 LLM(续接 KV cache,只处理新增的 token)→ 得到下一个字
5. 把这个字回馈给 MoChA,更新 t_{i-1} = t_i,继续等新音频
直到 LLM 输出 EOS
注意第 4 步的 KV cache:每次 LLM 只处理新增的几帧音频 + 上一个字,不需要从头重算,效率没有问题。
和传统非流式 SpeechLLM 的本质区别就在这里:
| 非流式 | 流式(本文) | |
|---|---|---|
| 开始出字的时机 | 整段音频说完之后 | 积累够 MoChA 触发所需的音频后 |
| LLM 每次看到的音频 | 全部 N 帧 | 当前字对应的那几帧 |
| 能否利用未来音频 | 能(整句编码) | 不能(只有历史) |
| 精度 | 高(5.0% CER) | 略低(5.5% CER) |
| 首字延迟 | ≈ 整段录音时长 | ≈ 第一个字说完后的几帧 |
4. 两个训练技巧
4.1 minLT:直接优化延迟
MoChA 学出来的对齐不一定是”最省延迟”的——它只管对齐准不准,不管触发时机早不早。
minLT(Minimal Latency Training) 损失干的是:用一个 HMM 混合 ASR 系统先跑 forced alignment,得到每个字的黄金边界 b_i,然后对 MoChA 的期望对齐位置和 b_i 的偏差做惩罚。损失是可微的(用了 MoChA 的边际对齐概率 α_{i,j}),可以端到端优化:
\[L_{total} = L_{LLM} + L_{MoChA} + \lambda L_{minLT}\]λ=0.1。策略网络的输出只在训练时参与 loss 计算,推理时由 MoChA 实时触发,不需要额外模块。
要说清楚的是:minLT 仍然依赖 HMM forced alignment,这一步没有完全端到端。想显式控制 token 生成延迟,就得知道”黄金时机”在哪,目前绕不过去。
4.2 联合训练:一套参数,两种模式
流式和非流式模型完全共享参数,只在前向路径上有区别(流式走 MoChA + interleaving,非流式走全量 prefix)。训练时每个 batch 随机分配到其中一种模式。
好处很直接:一套参数搞定两个模式,不需要维护两套模型,而且两个任务互相增益——非流式模式让 LLM 充分学语言建模,流式模式强迫对齐能力。
5. 实验结果
评测数据集:AISHELL-1(165 小时普通话)、AISHELL-2(1000 小时)、内部多领域数据集 MD(约 1 小时,金融/教育/影视)。
指标是 CER(Character Error Rate,字错率)——把识别结果改成标准答案需要改动多少个字,除以标准答案总字数,越低越好。中文按字算所以用 CER,英文按词算叫 WER。
5.1 与 baseline 的比较

在 AISHELL-1 上,非流式 CER 4.9%,流式 5.1%。之前最好的流式 decoder-only 方法是 BESTOW,5.3%,被超过了。encoder-decoder 的流式 baseline 是 6.9%——可以看到 LLM-based 方法和传统 encoder-decoder 已经拉开了差距。
AISHELL-2 上类似,流式 5.5% vs BESTOW 的 5.6%,差距不大但稳定。
一个值得注意的地方:流式和非流式的 CER 差距只有 0.5 个点(AISHELL-2 上 5.0% vs 5.5%),说明 MoChA + interleaving 对精度的影响比预想的小得多——传统印象里”流式比非流式差一截”,这里被打了折扣。
5.2 延迟优化效果

延迟以帧为单位,1 帧 = 40 ms。测 First(第一个 token 延迟)、Mid.(中间 token)、Last(最后 token)和平均 Avg.。
baseline 流式系统(encoder-decoder + MoChA)平均延迟 15 帧(600 ms)。本文不加 minLT 时平均 16 帧——MoChA 学出的对齐不够紧,反而比 baseline 略高。
加上 minLT 之后,平均降到 6 帧(240 ms),降幅 62.5%,CER 只从 5.4% 涨到 5.5%——用 0.1 个点的精度换了 2.5 倍的延迟降幅。
Last token 延迟从 9 帧降到 2 帧(80 ms),说话人说完最后一个字之后,系统基本立即输出,不需要等。
5.3 消融实验

三个关键设计的贡献:
- w/o joint-train:流式和非流式单独训,CER 5.1%/5.6%,比联合训练的 5.0%/5.5% 略差,差距不大。联合训练是基本免费的增益。
- w/o LoRA:冻结 LLM 只靠编码器和适配器,CER 5.4%/5.7%,退化明显。LoRA 是必要的。
- w/o Qwen init.:LLM 从随机初始化开始,CER 6.5%/7.2%,退化最显著——预训练 LLM 的知识是这套方案最核心的底气,没有它,decoder-only ASR 的优势基本消失。
6. 几点 take
第一,interleaving 是整篇最值得记住的设计。把音频段和文字标签交错喂给 LLM,这不只是训练技巧,而是从根本上改变了 LLM 的注意力上下文——让它学会”一小段音频对应一个字”,而不是”整段音频对应整个句子”。思路上和 LLM 里的 prefix interleaving、multimodal interleaving 是一脉相承的。
第二,MoChA 从 encoder-decoder 移植到 decoder-only 框架,这件事有个细节值得注意。原来 MoChA 在 encoder-decoder 里,decoder 是单独的 RNN,策略网络和 decoder 融为一体;这里 LLM decoder 太重,不能直接当 policy,所以策略网络是独立的轻量模块,只在训练时贡献损失,推理时只跑 MoChA 的触发机制。解耦本身就是这篇的一个工程贡献。
第三,minLT 还是需要 HMM,没有完全端到端。论文里写得比较轻描淡写,但这是实际的工程成本:需要维护一个 HMM 系统来生成 forced alignment。如果未来能用 CTC 对齐代替 HMM、或者完全用强化学习去优化延迟,实用性会更强。
第四,Qwen 初始化是整套方案的地基。去掉预训练权重,流式 CER 从 5.5% 直接到 7.2%,退化 1.7 个绝对点——幅度远超流式带来的额外损失(0.5 点)。SpeechLLM 的核心优势永远是那个 LLM,不是外面包的那层 wrapper。新加的模块(MoChA、interleaving)是在复用 LLM 的语言建模能力,而不是取代它。
第五,流式精度损失小,背后有个值得挖的原因。中文字符粒度的决策依赖的上下文比英文 word 少,单字在大多数情况下一小段音频就能确定,MoChA 的局部对齐基本够用。如果换成英文长词,或者上下文依赖更强的语言,这个 0.5 点的差距可能会扩大。这篇只测了普通话,英文场景的泛化性还是个问号。
扯一句题外话。这篇把 MoChA 从 encoder-decoder 移植到 decoder-only 框架,本质上解决的是”怎么在预算约束(延迟)下,让模型把已有的表达能力重新分配到新任务上”。和 AutoML 里研究”怎么在参数/算力约束下自动找最优架构配置”是同一类问题的两个侧面——都是效率与能力的 trade-off 优化。
我和合作者把这方面的积累整理成了《动手学 AutoML:从 NAS 到大语言模型优化实战》。书里有专门的章节讲 LoRA 这类参数高效微调方法的机制(rank 怎么选、加在哪些层),以及 LLM 后训练压缩(剪枝/量化)。和本文”给定预训练 LLM,在最小精度损失下加流式能力”的思路是话题相邻的。感兴趣的同学可以搜一下。
