1. 课程导入
各位同学好,我是这门课的主讲。今天咱们聊聊语音识别的前端——说白了,就是麦克风采集到声音之后,到声学模型开始“听懂”之前,中间那一整套信号处理流程。
我刚开始做语音识别那会儿,总觉得前端不就是个“预处理”嘛,随便调调参数就行。后来被现实狠狠教育了一回——同样的后端模型,前端处理得好,识别率能差出十几个点。嗯,从那以后我再也不敢小看前端了。
1.1 语音识别系统架构
一个完整的语音识别系统,通常可以拆成三个大块:
- 前端信号处理:把原始音频波形变成声学特征(比如 MFCC、Fbank)
- 声学模型:把声学特征映射到音素或状态
- 语言模型 + 解码器:把音素序列拼成最合理的文字
你想想看,前端就是整个系统的“耳朵”。耳朵不好使,后面再聪明的脑子也白搭。
核心观点:前端算法的质量,直接决定了声学模型能“看到”什么样的信息。前端做得好,后端事半功倍;前端做得糙,后端再强也救不回来。
下面这张图,是我个人习惯用来给新人讲架构的——简单直接,不绕弯子。
1.2 前端算法的作用与挑战
前端算法到底在干什么?我总结了三件事:
- 降噪——把环境里的空调声、马路声、人声干扰压下去
- 增强——把目标说话人的声音“捞”出来
- 特征提取——把波形变成声学模型能消化的特征
听起来不难?但实际做起来,坑多得很。
我曾经踩过的坑:有一次在车载场景下,我把降噪算法调得太狠,结果把轻声和尾音全切掉了。模型训练时没见过这种“干净”的信号,上线后识别率直接崩了。后来我才明白——前端不是越干净越好,而是要跟后端模型“对齐”。
前端算法面临的挑战,我列了个表,方便大家对比:
| 挑战 | 具体表现 | 影响 |
|---|---|---|
| 噪声多样性 | 空调、风扇、马路、餐厅、地铁…… | 单一降噪方案无法覆盖所有场景 |
| 远场拾音 | 说话人距离麦克风 1-5 米 | 信号衰减严重,混响干扰大 |
| 实时性要求 | 端到端延迟需 < 200ms | 算法复杂度受限,不能上太重的模型 |
| 设备差异 | 不同麦克风频响曲线不同 | 同一算法在不同设备上效果差异大 |
我的建议:做前端调优,一定要先搞清楚你的“敌人”是谁。如果是车载场景,重点处理发动机低频噪声和风噪;如果是智能音箱,重点处理远场混响和房间回声。场景定了,算法选型才有方向。
1.3 主流前端处理流程
现在工业界最常用的前端流程,我画了个流程图,大家跟着走一遍:
每个环节都有讲究。我挑几个重点说说:
1.3.1 预加重
语音信号的高频能量通常比低频弱很多。预加重就是个简单的高通滤波器,把高频抬一抬。公式很简单:
y[n] = x[n] - α * x[n-1]
α 一般取 0.97 左右。我在项目中试过 0.95 到 0.99 之间的值,说实话差别不大,但 0.97 是业界最稳的选择。
1.3.2 分帧与加窗
语音信号是非平稳的,但在 10-30ms 的短时内可以看成平稳。所以我们把它切成帧,每帧 25ms,帧移 10ms。加窗是为了避免帧边界处的频谱泄露。
小技巧:汉明窗是默认选择,但如果你做的是关键词唤醒这类任务,可以试试布莱克曼窗,旁瓣衰减更大,对弱唤醒词更友好。
1.3.3 VAD(语音活动检测)
VAD 的作用很简单——判断当前帧是语音还是静音/噪声。做得好能省掉 30%-50% 的计算量。我见过不少团队在 VAD 上翻车,要么切得太狠把语音开头切掉了,要么漏检导致大量噪声送进模型。
避坑指南:我曾经在一个远场项目里,VAD 阈值设得太高,结果说话人离麦克风稍远一点就被判定为静音。后来我改用自适应阈值——根据前几秒的噪声能量动态调整,效果好了很多。
1.3.4 特征提取
目前最主流的是 Fbank(Filter Bank)特征,40 维或 80 维。MFCC 在传统 GMM-HMM 时代是标配,但到了 DNN 时代,Fbank 因为保留了更多信息,反而更常用。
下面是个简单的 Fbank 提取代码示意:
# Python 伪代码
def extract_fbank(waveform, sample_rate=16000):
# 1. 预加重
pre_emphasized = waveform - 0.97 * np.pad(waveform[:-1], (1,0))
# 2. 分帧
frames = framing(pre_emphasized, frame_len=400, frame_shift=160)
# 3. 加窗
windowed = frames * np.hamming(400)
# 4. FFT
spectrum = np.fft.rfft(windowed)
# 5. Mel 滤波器组
mel_spec = np.dot(np.abs(spectrum)**2, mel_filterbank)
# 6. Log
log_mel = np.log(mel_spec + 1e-6)
return log_mel
嗯,代码看着简单,但每个步骤的参数选择都有讲究。后面章节我会逐一展开细讲。
1.3.5 特征归一化
CMVN(倒谱均值方差归一化)几乎是必做的。原因很简单——不同麦克风、不同信道采集到的特征分布不一样,归一化之后模型才能“一视同仁”。
我习惯在 utterance 级别做 CMVN,也就是对整句话做归一化。流式场景下可以用滑动窗口版本,但效果会略差一点。
总结一下:前端处理不是简单的“预处理”,它是整个语音识别系统的基石。前端做扎实了,后面的模型调优才有意义。接下来的章节,我会带大家逐个环节深入,把每个参数怎么调、每个坑怎么避,都讲清楚。