语音识别(ASR)要解决的核心难题,是一个严重的对齐错配:输入是每秒上万采样点的连续波形,输出是几个离散的字或词,而且你事先不知道哪段音频对应哪个字。一句话说快说慢、有停顿、有口音,长度都不一样。整个 ASR 技术栈,本质上就是在和这个"变长输入、变长输出、对齐未知"的问题搏斗。这篇文章顺着经典管线"特征 → 声学模型 → 解码"讲清楚每一环在干什么,以及现代端到端方案怎么把它们打通。

第一步:声学特征——为什么不直接喂波形

理论上可以把原始波形直接喂神经网络,但传统上更高效的做法是先提特征,理由是波形里绝大部分信息对识别是冗余的(相位、绝对幅度等)。

经典特征是 MFCC 或 log-Mel 谱,提取流程值得理解,因为它编码了语音的物理先验:

1
2
3
4
5
6
7
波形
-> 分帧(25ms 一帧,帧移 10ms,帧间重叠) # 语音在短时内近似平稳
-> 加窗(汉明窗,减少帧边界泄漏)
-> 短时傅里叶变换 STFT -> 功率谱 # 转到频域
-> Mel 滤波器组(低频密、高频疏) # 模拟人耳对频率的非线性感知
-> 取对数 # 模拟人耳对响度的对数感知
-> (MFCC 再做 DCT 去相关,log-Mel 则不做)

几个关键设计动机:分帧是因为语音只在 20-30ms 的短时窗内近似平稳,能做傅里叶分析;帧移小于帧长保证帧间重叠、特征连续;Mel 刻度取对数都是在模仿人耳——人耳对低频更敏感、对响度是对数感知。最终一句话变成一个 T×FT \times F 的特征序列(TT 帧、每帧 FF 维),这就是声学模型的输入。

第二步:声学模型——把帧映射到音素/字

声学模型要把每一帧(或一段帧)映射到语言单元(音素、字或子词)的概率。难点回到开头那个对齐问题:我有 TT 帧、想输出 LL 个字,TLT \gg L,但没有人告诉我第几帧到第几帧对应哪个字。标注这种逐帧对齐成本极高且本身有歧义。

CTC:绕过对齐的关键发明

CTC(Connectionist Temporal Classification)是解决这个问题最优雅的方案之一。它的核心 trick 是引入一个特殊的空白符 ϵ\epsilon,并允许字符重复,然后定义一个多对一的折叠规则:先合并连续重复,再删掉所有 ϵ\epsilon

例如帧级输出 h h ε e ε l l l ε o 折叠后得到 hello。一个目标文本可以由很多条帧级路径折叠而来。CTC 的损失就是把所有能折叠到正确文本的路径概率全部加起来取负对数:

P(yx)=πB1(y)t=1TP(πtx),LCTC=logP(yx)P(y \mid x) = \sum_{\pi \in \mathcal{B}^{-1}(y)} \prod_{t=1}^{T} P(\pi_t \mid x), \qquad \mathcal{L}_{CTC} = -\log P(y \mid x)

其中 B\mathcal{B} 是折叠函数,B1(y)\mathcal{B}^{-1}(y) 是所有能折叠成 yy 的路径集合。直接枚举路径是指数级的,但因为路径有重叠子结构,可以用前向-后向动态规划O(TL)O(T \cdot L) 内算完——这和 HMM 的前向算法同源。CTC 的意义在于:模型自己在训练中学会了对齐,不需要逐帧标注

它的代价是一个著名假设——条件独立:CTC 假定各帧的输出在给定声学特征下彼此独立,没有显式建模"前一个字是什么会影响后一个字"。这正是 CTC 必须配语言模型的根本原因。

注意力 / Transducer:另一条路

除了 CTC,还有两条主流:

  • 注意力编码-解码(AED):解码器自回归地生成字符,每步用注意力软对齐到编码后的声学帧,类似机器翻译。它建模了输出间的依赖,但纯注意力对流式(边说边出字)不友好。
  • RNN-T / Transducer:在 CTC 基础上增加一个预测网络建模输出依赖,天然支持流式,是很多实时语音助手的选择。

第三步:解码——声学 + 语言模型的联合搜索

声学模型给出"听起来像什么",但同音、近音词靠声学分不开(“在座"和"再做”)。这就需要语言模型提供"哪种说法更合理"的先验。经典做法是贝叶斯框架下的联合搜索:

y^=argmaxy logPAM(xy)+λlogPLM(y)\hat{y} = \arg\max_{y}\ \log P_{\text{AM}}(x \mid y) + \lambda \log P_{\text{LM}}(y)

PAMP_{\text{AM}} 是声学得分,PLMP_{\text{LM}} 是语言模型得分,λ\lambda 是权衡二者的权重。搜索这个最优序列用束搜索:维护若干候选路径,逐帧扩展并按综合得分剪枝。传统系统会把发音词典、语言模型、声学映射编译成一个加权有限状态转换器(WFST)统一搜索;现代则常用 shallow fusion——解码时把神经语言模型的分数直接加进束搜索打分。

1
2
3
4
5
6
7
8
9
beam = [空假设]
for t in 1..T:
new_beam = []
for hyp in beam:
for c in 候选字符:
score = hyp.am_score + AM(c | x_t) + λ * LM(c | hyp.text)
new_beam.add(扩展后的 hyp, score)
beam = top_k(new_beam, k) # 剪枝
return beam[0].text

端到端时代与工程权衡

如今主流是端到端模型:一个网络(常是 Conformer——卷积捕捉局部细节、注意力捕捉长程依赖的混合结构)直接从特征到文本,CTC 损失常和注意力损失联合训练。但即便端到端,上面的"特征—对齐—语言先验"三层逻辑并没消失,只是被吸收进了网络和解码里。几个反复出现的取舍:

  1. 流式 vs. 精度。语音助手要边说边出字(低延迟),意味着模型只能看到有限的未来上下文(受限注意力/chunk 机制),精度通常低于能看完整句的离线模型。这是延迟与准确率的硬权衡。
  2. 声学条件独立的代价。纯 CTC 不建模输出依赖,没有语言模型兜底时容易出现读音对但用词怪的结果。
  3. 领域与口音偏移。训练数据没覆盖的专业术语、人名、强口音是错误高发区,常靠领域语言模型或热词增强来救。
  4. 端点检测与噪声。什么时候算"说完了"(VAD)、远场回声和背景噪声,这些前端问题对最终体验的影响往往不亚于声学模型本身。

小结

ASR 的整条管线,都是围绕"变长波形对齐到变长文本"这个核心难题展开的:特征提取用人耳先验把波形压成紧凑表示,CTC/Transducer用空白符和动态规划让模型自学对齐、绕开逐帧标注,解码则把声学证据和语言先验在束搜索里联合起来。端到端模型把这三层揉进了一个网络,但理解了每一层在解决什么问题,你才看得懂流式延迟、同音纠错、口音失效这些工程现象背后的成因。