3. 语音信号预处理:预加重、分帧、加窗、STFT 原理

各位同学,欢迎来到第三章。前两章我们聊了唤醒词的整体架构和声学基础。从这章开始,我们要真正动手处理音频了。

你拿到的原始音频,说白了就是一堆随时间变化的电压值。但计算机没法直接理解这个波形。它需要一系列“预处理”步骤,才能变成模型能吃的特征。我当年刚入行时,觉得预处理就是“洗数据”,随便搞搞就行。结果模型怎么训都不收敛,后来才发现是预加重系数设错了。嗯,从那以后我再也不敢小看这一步了。

核心观点:预处理的质量,直接决定了声学模型的上限。数据干净,模型才能学到真东西。

3.1 预加重:把高频信号“抬”起来

先问一个问题:为什么语音信号的高频分量通常很弱?

因为人的发声机制。声带振动产生基频,口腔和鼻腔相当于一个低通滤波器。高频能量天然就比低频少。你想想看,如果直接拿原始信号去训练,模型会过度关注低频,忽略那些对辅音识别至关重要的高频细节。

预加重就是为了解决这个问题。它本质上是一个一阶高通滤波器:

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

α 通常取 0.95 到 0.97。我个人的习惯是取 0.97,对 8kHz 采样率的唤醒词数据效果最好。

这个公式的物理意义很简单:每个采样点减去它前一个点的加权值。低频信号变化慢,相邻点差异小,被减掉的多;高频信号变化快,差异大,被保留甚至增强。

我的经验:预加重系数不要随便改。α 太接近 1,高频噪声会被过度放大;α 太小,预加重等于没做。我曾经在一个远场唤醒词项目里,因为麦克风阵列自带高频噪声,把 α 从 0.97 降到了 0.93,效果立竿见影。

3.2 分帧:把长信号切成短片段

语音信号是非平稳的。但它在极短的时间(10-30ms)内可以看作是平稳的。这个假设是声学模型能工作的基础。

分帧就是把整个音频切成一段段短时帧。帧长通常取 20-30ms,帧移取 10ms。为什么帧移要小于帧长?因为相邻帧要有重叠,才能保证帧与帧之间的连续性。

参数 典型值 说明
帧长 25ms 每帧包含的采样点数。16kHz 采样率下,25ms = 400 点
帧移 10ms 相邻帧起始点之间的间隔。16kHz 下,10ms = 160 点
重叠长度 15ms 帧长 - 帧移 = 240 点。重叠保证了帧间平滑

举个例子:一段 1 秒的 16kHz 音频,总共 16000 个采样点。按 25ms 帧长、10ms 帧移来分,会得到大约 100 帧。每一帧都是一个 400 维的向量。

注意:帧长不能太短,否则帧内采样点太少,FFT 的频率分辨率不够。帧长也不能太长,否则平稳性假设不成立。25ms 是一个经过大量实践验证的折中值。

3.3 加窗:消除频谱泄露

分帧之后,我们得到了一堆截断的片段。直接对这些片段做 FFT,会出现一个问题:频谱泄露。

为什么会这样?因为 FFT 假设输入信号是周期性的。但截断操作破坏了周期性,导致频谱中出现原本不存在的频率分量。你想想看,一个完美的正弦波,被矩形窗截断后,频谱上会多出一堆旁瓣。

加窗就是为了抑制这种泄露。常用的窗函数有汉明窗和海宁窗。

3.3.1 汉明窗

汉明窗的公式:

w[n] = 0.54 - 0.46 * cos(2πn / (N-1)), 0 ≤ n ≤ N-1

它的特点是:主瓣宽度适中,旁瓣衰减较大(约 -43dB)。我个人在大多数唤醒词项目中都用汉明窗,因为它对语音信号的频谱保真度最好。

3.3.2 海宁窗

海宁窗的公式:

w[n] = 0.5 - 0.5 * cos(2πn / (N-1)), 0 ≤ n ≤ N-1

海宁窗的旁瓣衰减更快(约 -32dB),但主瓣比汉明窗稍宽。如果你对频率分辨率要求不高,但对旁瓣抑制要求极高,可以考虑海宁窗。

窗函数 主瓣宽度 旁瓣衰减 适用场景
矩形窗 最窄 -13dB 几乎不用,泄露严重
汉明窗 较窄 -43dB 语音识别首选
海宁窗 稍宽 -32dB 需要快速衰减时
避坑指南:我曾经在一个项目里,为了省计算量,直接用了矩形窗(也就是不加窗)。结果模型在噪声环境下误唤醒率飙升。后来排查发现,频谱泄露导致噪声能量扩散到了原本干净的频带。加窗虽然多了几步乘法,但这点计算量对现代硬件来说根本不是问题。

3.4 短时傅里叶变换(STFT)

好了,现在每一帧都加好了窗。接下来我们要把时域信号转换到频域。这就是 STFT 要做的事。

STFT 的核心思想很简单:对每一帧做 FFT。但要注意,FFT 的点数通常取 512 或 1024,而不是帧长本身。为什么?

因为 FFT 要求输入长度是 2 的幂次。如果帧长是 400 点,我们通常补零到 512 点再做 FFT。补零不会增加频率分辨率,但会让频谱看起来更平滑。

STFT 的数学表达式:

X(m, k) = Σ x[n] * w[n - m] * e^(-j2πkn/N)

其中 m 是帧索引,k 是频率索引,w 是窗函数。输出是一个复数矩阵,行对应时间帧,列对应频率点。

这个矩阵就是声学模型的输入基础。我们通常取它的幅度谱或功率谱,再经过梅尔滤波器组,就得到了 MFCC 或 Fbank 特征。

关键点:STFT 的输出是复数。幅度谱保留了能量信息,相位谱保留了时序信息。在唤醒词任务中,我们通常只使用幅度谱,因为相位信息对噪声敏感,且模型很难学到。

下面这张图展示了整个预处理流程的框架:

语音信号预处理流程框架 原始音频 预加重 分帧 加窗 短时傅里叶变换 频谱特征矩阵 说明:每一步的输出都是下一步的输入。预加重→分帧→加窗→STFT,顺序不能乱。

3.5 实践中的注意事项

最后,我分享几个实际项目中的经验:

  • 采样率一致性:训练数据和推理数据的采样率必须一致。我曾经因为训练用 16kHz,推理用 8kHz,导致模型完全失效。
  • 预加重在分帧之前做:顺序不能错。先预加重再分帧,否则帧边界处的预加重效果会受影响。
  • 窗函数要归一化:加窗后,帧的能量会降低。如果后续要计算能量特征,记得用窗函数的能量做归一化。
  • FFT 点数选择:对于 16kHz 采样率,512 点 FFT 足够。如果频率分辨率不够,可以增加到 1024 点,但计算量会翻倍。
重要提醒:预处理参数(帧长、帧移、窗函数、FFT 点数)一旦确定,在整个项目周期中不要随意更改。参数不一致会导致特征空间偏移,模型需要重新训练。