Chapter 7: RNN 时代 ASR:从 LSTM/GRU 到 CTC/Attention(并讨论对 MLLM 的启示)

1. 开篇:为什么现在还要学 RNN 时代的 ASR?

在 Transformer、Conformer 和现在的 MLLM(如 GPT-4o Audio, Gemini Live)横行的今天,深入研究 RNN 时代的 ASR 似乎是在“考古”。然而,这种观点是危险的。

架构会过时,但训练目标(Objectives)和对齐思想(Alignment)永存。

现代语音大模型(Speech Foundation Models)虽然将骨干网络换成了 Transformer,但其核心灵魂依然继承自本章讨论的内容:

  1. CTC (Connectionist Temporal Classification):至今仍是强制对齐(Force Alignment)、流式唤醒词、以及辅助 MLLM 生成精准词级时间戳的最佳方案。
  2. Hybrid CTC/Attention:这一架构不仅统治了 2017-2022 年的 ASR 榜单(如 WeNet, ESPnet, Icefall),其思想也被用于解决 MLLM 的幻觉问题。
  3. 序列建模的直觉:理解 RNN 的“长程依赖问题”和“梯度消失”,是你理解为什么 Transformer 需要 Positional Encoding 和 Attention 机制的基石。

本章学习目标:

  • 深度掌握 CTC:理解其动态规划(Forward-Backward)逻辑,明白它为什么能“无中生有”地自动对齐。
  • 掌握 LSTM/BiLSTM 声学建模:学会处理变长序列、填充(Padding)和降采样(Subsampling)。
  • 理解 Hybrid 策略:为什么单用 CTC 或单用 Attention 都不够完美?
  • MLLM 桥接:学习如何将这些“旧”技术模块化,嵌入到最新的 MLLM Pipeline 中解决实际工程痛点。

2. 文字论述

7.1 RNN 声学模型:序列建模的基石

ASR 的本质是计算后验概率 P(Y|X),其中 X 是声学特征序列(如 Mel-spectrogram),Y 是文本序列。难点在于 T >> U(音频帧数远多于字数),且两者没有预先定义的对应关系。

7.1.1 为什么是 LSTM/GRU?

普通的 RNN 存在严重的梯度消失(Vanishing Gradient)问题,导致模型“记不住”长距离的上下文(例如句首的语调影响句尾的语气)。

  • LSTM (Long Short-Term Memory):引入“门控机制”(输入门、遗忘门、输出门)和“细胞状态(Cell State)”,构建了一条梯度的“高速公路”。
  • BiLSTM (Bidirectional LSTM):语音识别不同于实时翻译,很多时候我们需要“听完”整个词才能确定它的含义(例如“为了”和“唯利”在前两个音节完全一样)。BiLSTM 同时维护前向(Forward)和后向(Backward)两个状态。

Rule of Thumb (工程经验): 在同等参数量下,BiLSTM 的性能通常比单向 LSTM 高 10%~15%。但在极低延迟的流式(Streaming)场景中,我们不能等待整个句子结束。这时通常采用 Latency-Controlled BiLSTM(只看未来一小段 chunk)或单向 LSTM。

7.1.2 关键工程:降采样 (Subsampling)

标准的声学特征(Fbank/MFCC)通常是 10ms 一帧

  • 1 秒音频 = 100 帧。
  • 正常人类语速:1 秒约 3-5 个字(中文)或单词(英文)。
  • 问题:如果在 10ms 的粒度上做分类,RNN 的时间步过长(T=1000 对于 10秒音频),导致反向传播计算量大,且显存难以承受。

解决方案:Pyramidal RNN / Subsampling 在底层 RNN 之间插入卷积层或简单的拼接层,将时间分辨率降低。

  • 常见配置:降低 4 倍(1/4 subsampling)。
  • 结果:帧率变为 40ms。此时 1 秒 = 25 帧,与人类说话的音节速率更为匹配。这在现代 Conformer 中演化为 Conv2dSubsampling 层。

7.2 训练目标:ASR 的灵魂

这是章最核心的部分。如何让模型在不知道哪个时间点对应哪个字的情况下学会识别?

7.2.1 CTC (Connectionist Temporal Classification)

CTC 是不需要对齐数据的端到端训练的鼻祖。它的核心思想是引入 Blank Token (_) 并对所有可能的对齐路径进行积分。

  1. 映射逻辑 CTC 定义了一个多对一的映射 B(·),规则如下:

  2. 合并连续的相同符号(Collapse repeats)。

  3. 移除 Blank 符号(Remove blanks)。

图解:路径折叠 假设输出词表为 V,_ 为 blank。目标单词是 "ab"。 以下所有路径(Path)在经过 B 变换后都是合法的 "ab"

  • Path 1: a, b, _, _ ab
  • Path 2: _, a, _, b ab
  • Path 3: a, a, _, b ab
  • Path 4: a, _, b, b ab
  • 陷阱a, a, b, b ab (注意:aa合并成a)
  • 非法b, a, _, _ ba (错)
  1. 损失函数 CTC 的目标是最大化所有能映射到真实文本 y 的路径概率之和: L = -log P(y|x) = -log ∑_{π ∈ B^{-1}(y)} P(π|x) 由于路径数量随 T 指数增长,直接求和不可行。CTC 使用 前向-后向算法(Forward-Backward Algorithm) 进行动态规划计算,将复杂度降为 O(TU)。

  2. CTC 的尖峰行为 (Spike Behavior) 训练成熟的 CTC 模型非常有意思:它倾向于在字符发音的中间或结束时刻给出一个极高的概率尖峰,而在其余时间全部预测为 Blank。

  • 这种特性使得 CTC 非常适合做关键词检索强制对齐

Gotcha (常见误区): CTC 的输出并不是“字符持续了多久”,而是一个“触发信号”。你不能简单地数连续的 a 的数量来判断 a 读了多久。

7.2.2 Attention-based (LAS: Listen, Attend, Spell)

LAS 采用了 Seq2Seq (Encoder-Decoder) 结构。

  • Listen (Encoder): 提取高层特征 H。
  • Attend (Attention): 在解码第 i 个字时,计算 context vector c_i。它是 H 的加权平均,权重取决于 Decoder 当前状态 s_{i-1} 与每个 h_t 的相似度。
  • Spell (Decoder): 基于 c_i 和上一个字 y_{i-1} 预测当前字 y_i。

LAS vs CTC:

  • CTC 假设每一帧输出是条件独立的(P(π|X)=∏_t P(π_t|X))。这导致 CTC 很难学会复杂的语言模型知识(如“虽然”后面大概率接“但是”)。
  • LAS 是自回归的(Auto-regressive),它天然自带语言模型能力,通常识别率更高。
  • LAS 的致命弱点对齐不单调。在长静音或噪声段,Attention 可能会“注意力涣散”,导致重复解码(repeating)或漏词(deletion)。

7.2.3 Hybrid CTC/Attention (工业界的主流)

为了结合两者的优点,WeNet/ESPnet 提出了混合架构:

  • 训练时:CTC Loss 作为一个正则化项,强制 Encoder 学习到的特征具有良好的时间对齐性,辅助 Attention 收敛。
  • 解码时
  • Rescoring(重打分):先用 CTC 快速生成 N 个候选(N-best),再用 Attention Decoder 对这 N 个候选计算分数,选最好的。
  • 这样做既保留了 CTC 的鲁棒性(不乱跳),又用了 Attention 的高精度。

7.3 解码策略与 LM Fusion

模型训练好后,如何从概率分布中得到最终文本?

  • Greedy: 每一步选概率最大的。快,但短视。
  • Beam Search: 维护一个宽度为 B 的候选池(Beam)。每一步保留全局分数最高的 B 条路径。 *

  • Beam Size: 通常设为 10。过大则慢,收益递减;过小则精度差。

7.3.2 LM Fusion (Shallow Fusion)

RNN 声学模型虽然强,但受限于训练数据的文本覆盖度。我们需要外挂一个在大规模纯文本上训练的 Language Model (LM)。

  • λ (LM weight): 一个超参数,需要在验证集上调优。
  • 作用:纠正同音错别字。例如 ASR 听到 "ping guo",声学模型分不清“平果”和“苹果”,但 LM 知道“苹果”概率大得多。

7.4 对 MLLM 的借鉴意义(The Bridge)

不要以为 RNN 已经过气了。在构建 GPT-4o 级别的语音交互模型时,RNN 时代的智慧无处不在。

7.4.1 MLLM 的“时间戳锚点”

MLLM(如 Whisper, Qwen-Audio)本质上是一个巨大的 Decoder。它们擅长生成语义通顺的文本,但极不擅长精准的时间对齐

  • 痛点:用户问“这句话第几秒提到了价格?”,MLLM 经常产生幻觉。
  • CTC 的回归:现在的趋势是,在 MLLM 的 Audio Encoder 之上挂一个轻量级的 CTC Head
  • MLLM 负责生成内容(Content)。
  • CTC Head 负责预测每一帧的概率尖峰,从而反推出精准的字级时间戳(Word-level Timestamps)
  • 案例:OpenAI Whisper 的 alignment logic 其实就借鉴了类似动态规划的思想。

7.4.2 WFST 与 "Logits Bias"

RNN 时代,我们用 WFST(加权有限状态转换器)将词表编译成图,限制解码路径。

  • MLLM 映射:在 RAG(检索增强生成)场景中,我们需要模型只输出“现有的产品名”。
  • 这可以通过 Trie-based Logits Constraint 来实现。这本质上就是实时构建了一个简单的 WFST,强制将 MLLM 的输出概率分布(Logits)中不符合前缀树(Trie)的 token 设为负无穷。

7.4.3 解决“长静音幻觉”

LAS 时代的教训是:Attention 在静音段会“瞎看”。

  • MLLM 也有这个问题:如果不加控制,它会对一段背景噪音生成莫名其妙的句子(如 "Thanks for watching")。
  • Legacy Strategy:利用传统 VAD (Voice Activity Detection) 或 CTC 的 blank probability 来切断 MLLM 的输入,或者在解码时检测到长时间 blank 就强制停止生成。

3. 本章小结

  1. BiLSTM + Subsampling 是 RNN 时代的标准声学编码器,降采样(40ms/帧)是平衡算力与精度的关键。
  2. CTC 利用 Blank 标签和动态规划,解决了不定长序列的自动对齐问题,是 ASR 的核心算法。
  3. Hybrid CTC/Attention 架构通过多任务学习,结合了 CTC 的对齐约束(鲁棒性)和 Attention 的语言能力(高精度),是工业界的主流范式。
  4. LM Fusion 通过外挂语言模型,弥补了声学数据文本多样性的不足。
  5. MLLM 启示:CTC 并没有死,它正在作为 MLLM 的“对齐插件”和“防幻觉卫士”重新焕发生机。

4. 练习题

提示:答案默认折叠,建议先自行思考。

基础题(熟悉概念)

Q1: CTC 路径积分

假设词表是 {a, b}, _ 是 blank。 输入音频有 T=3 帧。模型输出概率矩阵如下(行是时间,列是 token _, a, b):

  • t1: [0.8, 0.2, 0.0]
  • t2: [0.6, 0.4, 0.0]
  • t3: [0.0, 1.0, 0.0]

请计算目标文本 "a" 的总概率。

显示答案与解析

答案: 目标序列 "a" 在 T=3 时可能的合法 CTC 路径有:

  1. a, _, _
  2. _, a, _
  3. _, _, a
  4. a, a, _ (合并为 a)
  5. _, a, a (合并为 a)
  6. a, a, a (合并为 a)
  7. a, _, a (注意:这是非法的!因为中间隔了 blank这会解码成 "aa" 而不是 "a")

计算各路径概率:

  1. a, _, _: 0.2 * 0.6 * 0.0 = 0 (因为 t3 的 blank 概率是 0)
  2. _, a, _: 0.8 * 0.4 * 0.0 = 0
  3. _, _, a: 0.8 * 0.6 * 1.0 = 0.48
  4. a, a, _: 0.2 * 0.4 * 0.0 = 0
  5. _, a, a: 0.8 * 0.4 * 1.0 = 0.32
  6. a, a, a: 0.2 * 0.4 * 1.0 = 0.08

总概率 = 0.88

Q2: Subsampling 的维度变化

输入音频特征维度为 (Batch=1, Time=1000, Dim=80)。 经过一个 Conv2dSubsampling 层(两层卷积,每层 stride=2),输出的 Time 维度大约是多少?为什么这对 CTC Loss 很重要?

显示答案

答案:

  • 第一层卷积 stride=2,长度变为 500。
  • 第二层卷积 stride=2,长度变为 250。
  • 输出 Time 约为 250。
  • 重要性:CTC Loss 要求 Input_Length >= Target_Length。如果目标文本有 300 个字,而降采样后只剩 250 帧,CTC 就无法放置所有的字符(哪怕一个格子放一个也不够),会导致 Loss NaN 或报错。因此降采样倍率不能无限大。

挑战题(深入思考)

Q3: 为什么 CTC 很难分 "a pair of" 和 "a pear of"?

结合 CTC 的条件独立性假设(Conditional Independence Assumption)来解释。

显示答案

答案:

  • CTC 的假设是 P(π|X)=∏_t P(π_t|X)。也就是说,模型在预测 t 时刻的字符时,只看声学特征,而不看之前预测了什么字符。
  • 对于 "pair" 和 "pear",它们的声学特征(发音)几乎一模一样。
  • 如果是一个自回归模型(如 LAS 或 Transformer),在预测 "pair" 之前,如果看到前文是 "I bought a...",它可能会倾向于 "pair of shoes";如果前文是 "Just ate a...",倾向于 "pear"。
  • 但 CTC 看不到前文的解码结果。它只能根据声音瞎猜。如果数据里 "pair" 出现得多,它就永远猜 "pair"。
  • 补救:这就是为什么 CTC 必须要配合 Language Model 使用的原因。

Q4: MLLM 时代,我们还需要训练一个独立的 BiLSTM-CTC 模型吗?

在资源受限(端侧设备)场景下,对 "微型 Transformer" 和 "BiLSTM" 的优劣。

显示答案

答案: 这取决于硬件对流式推理的支持。

  1. BiLSTM: * 劣势:必须按顺序计算(无法并行),推理慢;无法利用 GPU 的并行优势。 * 优势:在纯 CPU 的低端设备(如嵌入式芯片)上,LSTM 的状态缓存机制非常省内存,且模型文件极小(几 MB)。

  2. Transformer/Conformer: * 劣势:KV Cache 占用显存/内存大;Attention 计算复杂度是 (虽然流式版可以优化)。 * 优势:精度远高;训练快。 结论:在极端低功耗(IoT、穿戴设备)场景做关键词唤醒(KWS)或简单指令词,BiLSTM-CTC 依然是王者。但在手机/服务器端,Conformer 是首选。


5. 常见陷阱与错误 (Gotchas)

5.1 "NaN" Loss 的噩梦

  • 现象:训练几个 step 后,CTC Loss 突然变成 NaN,梯度爆炸。
  • 原因 1:长度不匹配。如 Q2 所述,文本长度 > 降采样后的音频帧数。
  • Fix: 数据清洗时,过滤掉 audio_len / 4 < text_len 的样本。

  • 原因 2:脏数据。标注里有空字符串,或者音频全是静音但标注了文字。

  • Fix: 增加 min_text_len > 0min_audio_len 的检查。

5.2 词表中的 0 号陷阱

  • 问题:PyTorch 的 nn.CTCLoss 默认 blank=0
  • 场景:如果你的 Tokenizer(比如 SentencePiece)把 ID 0 分配给了 <unk> 或者某个常用字(如“的”),而你没有修改 CTC 的配置。
  • 后果:模型会拼命把“的”字当成 blank 忽略掉,导致那个字的召回率极低,且模型难以收敛。
  • Rule of Thumb永远显式指定 blank ID。通常建议将 blank 放在词表的最末尾(例如 vocab size),以避免与 padding (0) 或其他特殊符号冲突。

5.3 Sortagrad (按长度排序训练)

  • 问题:RNN 对长序列极其敏感,早期训练如果直接扔进去一个 30 秒的长句子,梯度可能会炸,或者因为 padding 太多导致浪费算力。
  • 技巧:在第一个 Epoch,将数据集按音频长度从小到大排序。
  • 先让模型在短句上学会基本的“声学-字符”对齐关系。
  • 从第二个 Epoch 开始,再 Shuffle(打乱)数据以保证泛化性。
  • 这在 Kaldi 和 ESPnet 中是标准操作。