语音大模型工程:音频 token、LLM 主干与对齐契约

语音大模型正在把 ASR、语音理解和多模态交互拉进 LLM 生态。Qwen3-ASR、Qwen-Omni、WeNet、CTC-AED 等路线背后都有同一个问题:音频表示如何进入语言模型,并且不破坏位置、mask、长度和训练目标的语义。

这类系统表面上是“接一个 speech encoder 到 LLM”,实际难点在对齐契约。张量 shape 对,不等于语义对。

一条典型链路

语音大模型常见链路可以拆成五段:

  1. audio frontend:采样率、分帧、特征提取或 waveform 编码;
  2. speech encoder:把声学输入变成连续 hidden states 或离散 token;
  3. projector:把音频侧维度映射到 LLM hidden size;
  4. LLM backbone:处理文本 token、音频 token 和 prompt 模板;
  5. objective heads:使用生成 loss、CTC loss、AED loss 或辅助对齐 loss。

这五段每一段都可能改变长度、mask 或位置信息。工程上必须把这些变化显式记录下来,而不是只在 forward 里临时拼接。

对齐契约有哪些

长度契约:输入音频多少帧,经过 subsampling 后剩多少 token,projector 是否再改变长度,最终监督序列如何匹配。这个契约不清楚,训练 loss 下降也可能是错的。

位置契约:文本 token 和音频 token 是否共享 RoPE,是否需要多维位置,音频 token 插入 prompt 的哪个区域,padding 是否影响位置编号。

mask 契约:attention mask、loss mask、padding mask 不是一回事。一个位置可以参与 attention 但不参与 loss,也可以只作为上下文条件存在。把它们混在一起,是语音 LLM 最常见的隐性错误。

目标契约:CTC 更关心单调对齐,AED 更关心序列到序列,LLM loss 更关心生成协议。多目标训练时,要能单独观察每个 loss 的行为。

最小闭环优先于复杂结构

开源模型工程化时,不建议一开始就堆复杂结构。更稳的顺序是:

  1. 固定 LLM,只训练 projector,在极小样本上验证过拟合;
  2. 加入明确的音频 token 长度日志;
  3. 检查 padding 位置不参与 loss;
  4. 单独跑短音频、长音频、静音片段和噪声片段;
  5. 再逐步解冻 encoder 或加入辅助目标。

这条路径能把“系统能跑”和“系统能学”分开。前者是 smoke test,后者才是建模有效性的起点。

Qwen 与 WeNet 的工程启发

Qwen 类模型提示我们,现代 LLM 的位置编码和输入封装并不一定是传统 HuggingFace decoder 的简单结构。不要凭字段名猜测模型内部模块,应该以官方加载入口、配置和源码为准。

WeNet 和 CTC-AED 路线提示我们,语音任务仍然需要单调对齐、时间边界和流式约束。LLM 主干很强,但它不能替代语音侧对齐诊断。

更实际的做法是把两类能力组合起来:语音侧负责稳定编码和对齐,LLM 侧负责语言建模、指令跟随和复杂生成。

流式场景的额外约束

一旦进入流式输入,问题会更复杂:

  • 音频 chunk 之间是否共享缓存;
  • 当前文本生成是否可以被新音频打断;
  • 旧音频 token 是否需要压缩或丢弃;
  • 取消请求时 decoder state 如何清理;
  • partial hypothesis 是否会污染最终输出。

这些都不是模型结构图能解决的问题,而是运行时状态管理问题。

小结

语音大模型工程的核心不是把 encoder 和 LLM 拼起来,而是明确音频 token 如何获得位置、如何参与 attention、如何参与 loss、如何在流式状态中被更新。只要这些契约没有写清楚,模型越大,调试越困难。

输入入口一致性

Speech-LLM debugging should include an input-contract test. The same utterance should be passed through every supported input path, then compare sampling rate, channel handling, feature length, audio-token length, position ids, attention mask, loss mask, and decoded text.

Path-based input often hides loading, mixdown, resampling, and normalization steps. Tensor or precomputed-feature input may bypass them. If these paths are not tested side by side, a model issue can be mistaken for an input-contract issue.