2. 语音识别基础

各位同学好,我是你们这堂课的主讲。今天咱们聊聊语音识别里最基础、也最容易被忽视的一环——声学预处理。

很多人一上来就调模型、改网络结构,结果识别率死活上不去。我见过太多这样的案例了。其实啊,很多时候问题出在入口——你的音频信号还没进模型,就已经“脏”了。

这一章,我会带你走一遍完整的信号预处理流水线。从麦克风采集到的模拟信号,到最终能喂给模型的梅尔频谱特征,每一步我都会讲清楚“为什么这么做”以及“我踩过的坑”。

核心观点: 预处理做得好,模型训练事半功倍;预处理做得糙,再好的网络也白搭。

2.1 语音信号数字化:从连续到离散

麦克风采集到的是连续变化的电压信号,也就是模拟信号。但计算机只认0和1。所以第一步,就是把这个连续信号变成离散的数字序列。

这个过程包含两个关键动作:采样和量化。

  • 采样:每隔固定时间取一个值。时间间隔越短,采样率越高。
  • 量化:把采样得到的电压值,用有限个二进制数表示。位数越多,精度越高。

这里有个经典问题:采样率设多高才够?

答案是遵循奈奎斯特采样定理:采样率必须大于信号最高频率的两倍。人声的主要能量集中在300Hz~3400Hz,所以电话系统用8kHz采样率。但为了保留更多细节,语音识别系统通常用16kHz。

应用场景 采样率 量化位数 说明
电话语音 8 kHz 8 bit 窄带,仅保留语音主要频段
普通语音识别 16 kHz 16 bit 宽带,保留更多细节
高保真音频 44.1 kHz 16/24 bit 音乐级,语音识别很少用
避坑指南: 我曾经接手过一个项目,数据采集时用了8kHz采样率,但模型是在16kHz数据上预训练的。结果识别率直接掉了15个点。后来排查才发现是采样率不匹配。记住:训练和推理的采样率必须一致。

2.2 预加重:补偿高频能量

你有没有发现,人说话时低频能量大,高频能量小?这是声带和口腔的物理特性决定的。但高频部分恰恰包含了很多辅音信息,比如“s”、“sh”、“f”。如果直接处理,高频细节很容易被低频淹没。

预加重就是为了解决这个问题。它本质上是一个高通滤波器,提升高频部分的能量。

公式很简单:

y[n] = x[n] - α * x[n-1]

其中α通常取0.97或0.95。我个人习惯用0.97,效果比较稳定。

为什么是0.97?不是0.9也不是0.99?

α越接近1,高频提升越猛。但太猛了会放大噪声。0.97是个经验值,在大多数场景下表现都不错。你可以根据实际数据微调,但别偏离太远。

小技巧: 预加重这一步,很多人觉得可有可无。但我建议你别省。尤其是在远场语音或噪声环境下,预加重能明显提升辅音的辨识度。我做过对比实验,加上预加重后,词错误率(WER)平均能降1~2个点。

2.3 分帧加窗:把长信号切成短片段

语音信号是非平稳的——它的统计特性随时间变化。但如果我们只取很短的一段(比如20~30毫秒),在这段时间内信号可以近似认为是平稳的。这就是短时平稳假设。

基于这个假设,我们把长信号切成一个个短帧。帧长通常取25ms,帧移取10ms。也就是说,相邻帧之间有15ms的重叠。

为什么要重叠?

因为加窗后,帧边缘的信号会被削弱。重叠可以保证信息不丢失,让帧与帧之间平滑过渡。

切完帧之后,还要对每一帧加窗。常用的窗函数有:

  • 汉明窗(Hamming Window):最常用,旁瓣衰减适中,频率分辨率好。
  • 汉宁窗(Hann Window):旁瓣衰减更快,但主瓣稍宽。
  • 矩形窗:简单粗暴,但频谱泄露严重,基本不用。

我个人几乎只用汉明窗。原因很简单:它在频率分辨率和旁瓣抑制之间取得了很好的平衡。你想想看,如果旁瓣太大,一个频率的能量会“泄露”到其他频率上,造成频谱模糊。这对后续的梅尔频谱提取非常不利。

参数总结:
  • 帧长:25ms(对应16kHz采样率就是400个采样点)
  • 帧移:10ms(160个采样点)
  • 窗函数:汉明窗

2.4 短时傅里叶变换(STFT):看穿时间的频谱

分帧之后,我们得到了很多短时平稳的信号片段。接下来要做的,就是分析每个片段的频率成分。这就是短时傅里叶变换(STFT)的用武之地。

STFT说白了就是:对每一帧做一次快速傅里叶变换(FFT),得到该帧的频谱。把所有帧的频谱按时间排列,就得到了一个二维的时频谱图(spectrogram)。

横轴是时间,纵轴是频率,颜色深浅代表能量大小。这张图,就是语音信号的“指纹”。

这里有个参数需要你注意:FFT点数。通常取512或1024。点数越多,频率分辨率越高,但计算量也越大。对于16kHz的语音,512点FFT已经够用了。

# Python示例:STFT计算
import numpy as np
from scipy import signal

# 参数设置
fs = 16000          # 采样率
frame_len = 0.025   # 帧长25ms
hop_len = 0.010     # 帧移10ms
nfft = 512          # FFT点数

# 计算STFT
f, t, Zxx = signal.stft(
    audio, 
    fs=fs, 
    nperseg=int(frame_len * fs),
    noverlap=int((frame_len - hop_len) * fs),
    nfft=nfft
)

# Zxx的shape: (频率点数, 时间帧数)
# 取幅度谱
magnitude = np.abs(Zxx)
注意: STFT得到的频谱是复数,包含幅度和相位信息。在语音识别中,我们通常只取幅度谱,丢掉相位。为什么?因为人耳对相位不敏感,而且相位信息在噪声环境下很不稳定。丢掉相位能提升模型的鲁棒性。

2.5 梅尔频谱:模拟人耳的听觉特性

STFT得到的线性频谱,频率轴是均匀分布的。但人耳不是这样工作的。人耳对低频更敏感,对高频的分辨能力则逐渐下降。换句话说,人耳对频率的感知是非线性的。

梅尔刻度(Mel Scale)就是用来模拟这种非线性感知的。它把物理频率映射到人耳感知频率上。转换公式如下:

Mel(f) = 2595 * log10(1 + f / 700)

从公式可以看出:低频段,梅尔频率随物理频率线性增长;高频段,增长变缓。这正好符合人耳的听觉特性。

具体做法是:在梅尔刻度上均匀放置一组三角滤波器(通常40个),然后用这些滤波器去加权求和STFT的幅度谱。每个滤波器输出一个能量值,最终得到40维的梅尔频谱特征。

为什么是40维?

这是经验值。太少会丢失信息,太多会增加计算量。40维在大多数语音识别任务中表现都不错。如果你做的是细粒度任务(比如说话人识别),可以试试64维或80维。

我的经验: 梅尔滤波器的数量不是越多越好。我曾经试过128个滤波器,结果模型反而过拟合了。因为滤波器越多,每个滤波器的带宽越窄,对频率的细微变化越敏感。但语音信号本身就有一定的频率波动,过度敏感反而会引入噪声。40~80是个安全范围。

2.6 完整流程总结

好了,我们把整个预处理流水线串起来,看看它长什么样:

语音信号预处理完整流程 模拟信号 数字化 预加重 分帧加窗 STFT 梅尔滤波器组 梅尔频谱特征 采样 + 量化 16kHz / 16bit 是语音识别标配 FFT点数通常取512或1024 只保留幅度谱,丢弃相位

整个流程走下来,你从一段原始的音频文件,得到了一个40维的梅尔频谱特征序列。这个序列就是后续声学模型的输入。

每一步都有它的道理:数字化让信号能被计算机处理,预加重补偿了高频损失,分帧加窗满足了短时平稳假设,STFT揭示了时频结构,梅尔滤波器则模拟了人耳的听觉特性。

嗯,这里要注意:预处理不是一成不变的。如果你做的是远场语音识别,可能需要额外的去混响处理;如果是噪声环境,可能还要加VAD(语音活动检测)。但今天讲的这五步,是绝大多数语音识别系统的“标配”。