1. 课程导入

各位同学好,我是这门课的主讲。今天咱们聊聊语音识别的前端——说白了,就是麦克风采集到声音之后,到声学模型开始“听懂”之前,中间那一整套信号处理流程。

我刚开始做语音识别那会儿,总觉得前端不就是个“预处理”嘛,随便调调参数就行。后来被现实狠狠教育了一回——同样的后端模型,前端处理得好,识别率能差出十几个点。嗯,从那以后我再也不敢小看前端了。

1.1 语音识别系统架构

一个完整的语音识别系统,通常可以拆成三个大块:

  • 前端信号处理:把原始音频波形变成声学特征(比如 MFCC、Fbank)
  • 声学模型:把声学特征映射到音素或状态
  • 语言模型 + 解码器:把音素序列拼成最合理的文字

你想想看,前端就是整个系统的“耳朵”。耳朵不好使,后面再聪明的脑子也白搭。

核心观点:前端算法的质量,直接决定了声学模型能“看到”什么样的信息。前端做得好,后端事半功倍;前端做得糙,后端再强也救不回来。

下面这张图,是我个人习惯用来给新人讲架构的——简单直接,不绕弯子。

语音识别系统架构 前端信号处理 音频 → 特征 声学模型 特征 → 音素/状态 语言模型 + 解码器 音素 → 文字 💡 说明: • 前端输出的是固定维度的声学特征(如 40 维 Fbank) • 声学模型输出的是音素/状态的后验概率 • 解码器结合语言模型,搜索最优文字序列

1.2 前端算法的作用与挑战

前端算法到底在干什么?我总结了三件事:

  1. 降噪——把环境里的空调声、马路声、人声干扰压下去
  2. 增强——把目标说话人的声音“捞”出来
  3. 特征提取——把波形变成声学模型能消化的特征

听起来不难?但实际做起来,坑多得很。

我曾经踩过的坑:有一次在车载场景下,我把降噪算法调得太狠,结果把轻声和尾音全切掉了。模型训练时没见过这种“干净”的信号,上线后识别率直接崩了。后来我才明白——前端不是越干净越好,而是要跟后端模型“对齐”。

前端算法面临的挑战,我列了个表,方便大家对比:

挑战 具体表现 影响
噪声多样性 空调、风扇、马路、餐厅、地铁…… 单一降噪方案无法覆盖所有场景
远场拾音 说话人距离麦克风 1-5 米 信号衰减严重,混响干扰大
实时性要求 端到端延迟需 < 200ms 算法复杂度受限,不能上太重的模型
设备差异 不同麦克风频响曲线不同 同一算法在不同设备上效果差异大

我的建议:做前端调优,一定要先搞清楚你的“敌人”是谁。如果是车载场景,重点处理发动机低频噪声和风噪;如果是智能音箱,重点处理远场混响和房间回声。场景定了,算法选型才有方向。

1.3 主流前端处理流程

现在工业界最常用的前端流程,我画了个流程图,大家跟着走一遍:

主流前端处理流程 音频采集 预加重 分帧 + 加窗 VAD 检测 降噪 / 增强 特征提取 特征归一化 输出特征 📌 各环节要点: • 预加重:提升高频分量,补偿口唇辐射损失 • 分帧加窗:帧长 25ms,帧移 10ms,加汉明窗 • VAD:去掉静音段,减少无效计算 • 降噪:谱减法、维纳滤波、或基于 DNN 的降噪 • 特征归一化:CMVN(倒谱均值方差归一化)

每个环节都有讲究。我挑几个重点说说:

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,也就是对整句话做归一化。流式场景下可以用滑动窗口版本,但效果会略差一点。

总结一下:前端处理不是简单的“预处理”,它是整个语音识别系统的基石。前端做扎实了,后面的模型调优才有意义。接下来的章节,我会带大家逐个环节深入,把每个参数怎么调、每个坑怎么避,都讲清楚。