语音信号预处理:预加重、分帧、加窗、端点检测
各位同学,欢迎来到第二章。上一章我们聊了语音信号的基础概念,今天咱们直接上手干活——预处理。
预处理是语音算法的第一道工序。说白了,就是把原始录音里那些乱七八糟的东西清理干净,让后续的特征提取和模型训练能正常工作。我做了这么多年语音,见过太多因为预处理没做好导致整个系统崩掉的案例。嗯,咱们今天就把这块彻底讲透。
核心观点:预处理做得好,后续工作事半功倍;预处理偷懒,后面全是坑。
2.1 预加重:把高频信号拉起来
先问一个问题:为什么语音信号的高频部分总是比低频弱?
原因其实很简单——声带发出的声音,经过口腔、鼻腔这些腔体时,高频能量天然就被衰减了。再加上麦克风采集时也会损失一部分高频。结果就是,你拿到的信号频谱里,低频能量远大于高频。
这有什么问题?问题大了。语音中的很多关键信息,比如辅音(/s/、/ʃ/、/t/)都集中在中高频段。如果高频被压得太低,后续的声学模型就很难区分这些音素。
预加重就是干这个的——把高频部分抬起来,让频谱变得平坦。
数学上很简单,就是一个一阶高通滤波器:
y[n] = x[n] - α * x[n-1]
其中 α 通常取 0.95 到 0.97。我个人习惯用 0.97,效果比较稳。
为什么是这个范围?你想想看,α 越接近 1,高频提升越猛。但太猛了会把噪声也放大。0.97 是个折中值,我在多个项目里试下来,这个值对大多数场景都适用。
小技巧:如果你做的是儿童语音识别,建议把 α 调低到 0.95 左右。儿童语音的基频本来就高,预加重太强反而会失真。
2.2 分帧:把长信号切成小段
语音信号是非平稳的——它的统计特性随时间变化。但如果我们只取很短的一段(比如 20-30 毫秒),这段信号可以近似认为是平稳的。
这就是分帧的核心思想:把长信号切成一个个短帧,每帧单独处理。
帧长一般取 20-30 毫秒。太短了,频率分辨率不够;太长了,平稳假设不成立。
帧移呢?通常取帧长的一半或三分之一。比如帧长 25 毫秒,帧移 10 毫秒。这样相邻帧之间有重叠,不会丢失帧边界的信息。
我举个例子:
采样率: 16000 Hz
帧长: 25 ms → 400 个采样点
帧移: 10 ms → 160 个采样点
总帧数: (总采样点数 - 帧长) / 帧移 + 1
这里有个坑——帧长和帧移的单位是毫秒,但实际计算要用采样点数。我曾经有个同事直接拿毫秒数去算索引,结果帧与帧之间全错位了。嗯,这种低级错误咱们就别犯了。
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 帧长 | 20-30 ms | 常用 25 ms |
| 帧移 | 10-15 ms | 常用 10 ms |
| 重叠率 | 50%-75% | 帧移越小,重叠越多 |
2.3 加窗:解决频谱泄漏问题
分帧之后,直接做傅里叶变换行不行?理论上可以,但效果很差。
为什么?因为分帧相当于对信号做了一个矩形截断。矩形窗在频域有很高的旁瓣,会导致频谱泄漏——本来集中在某个频率的能量,会扩散到其他频率上。
加窗就是为了抑制这种泄漏。常见的窗函数有汉明窗和汉宁窗。
汉明窗:
w[n] = 0.54 - 0.46 * cos(2πn / (N-1))
汉宁窗:
w[n] = 0.5 * (1 - cos(2πn / (N-1)))
这两个窗长得像,但效果有区别。汉明窗的旁瓣更低,主瓣稍宽;汉宁窗的主瓣更窄,但旁瓣衰减慢一些。
我个人习惯用汉明窗。为什么?因为它在大多数语音任务里表现稳定。我记得有一次做说话人识别,试了五六种窗函数,最后还是汉明窗效果最好。
注意:加窗是在分帧之后做的。先分帧,再对每一帧乘以窗函数。顺序不能搞反。
加窗后的信号:
y_frame[n] = x_frame[n] * w[n]
这样每一帧的两端都被平滑地衰减到零,再做傅里叶变换,频谱泄漏就大大减少了。
2.4 端点检测(VAD):找到语音的起止点
VAD 的全称是 Voice Activity Detection。它的任务很简单——判断一段信号里,哪些部分是语音,哪些部分是静音或噪声。
为什么需要 VAD?
- 节省计算资源:只处理语音段,跳过静音段
- 提高识别率:避免把噪声当成语音去识别
- 降低误触发:唤醒词检测必须靠 VAD 先定位语音起点
最经典的 VAD 方法是基于能量和过零率的双门限法。
短时能量:
E = Σ x[n]²
语音段的能量通常比静音段高。但噪声能量也可能很高,所以单靠能量不够。
短时过零率:
ZCR = (1/2) * Σ |sign(x[n]) - sign(x[n-1])|
过零率反映信号穿越零轴的频率。清音(如 /s/、/f/)的过零率很高,浊音(如 /a/、/i/)的过零率较低。噪声的过零率通常介于两者之间。
双门限法的流程:
- 计算每一帧的能量和过零率
- 设定两个门限:高门限和低门限
- 能量超过高门限 → 标记为语音
- 能量在高低门限之间,且过零率符合语音特征 → 标记为语音
- 其余帧标记为静音
这里有个经验:高门限通常设为最大能量的 10%-20%,低门限设为 5%-10%。具体值需要根据实际噪声环境调整。
避坑指南:我曾经在一个嘈杂的工厂环境里做语音识别,双门限法完全失效。后来改用基于信噪比的自适应门限,才勉强能用。如果你的应用场景噪声很大,建议上深度学习 VAD。
2.5 知识体系总览
下面这张图把本章的核心流程串起来了。你可以把它当作预处理的标准流水线:
2.6 各步骤参数速查表
| 步骤 | 关键参数 | 推荐值 | 注意事项 |
|---|---|---|---|
| 预加重 | α | 0.95-0.97 | 儿童语音用 0.95 |
| 分帧 | 帧长 / 帧移 | 25 ms / 10 ms | 采样率不同时需换算为点数 |
| 加窗 | 窗类型 | 汉明窗 | 汉宁窗也可,效果接近 |
| VAD | 能量门限 / 过零率门限 | 高门限 15%,低门限 8% | 噪声大时改用自适应门限 |
2.7 避坑总结
最后,我把这些年踩过的坑总结一下:
- 预加重 α 别乱调:0.97 是通用值,除非你有明确理由,否则别改。
- 帧长帧移的单位:记得转成采样点数,别直接用毫秒算索引。
- 加窗顺序:先分帧,再加窗。先加窗再分帧,窗函数会被截断,效果全废。
- VAD 门限:静态门限只适合安静环境。噪声变化大的场景,必须上自适应门限或深度学习 VAD。
- 采样率一致性:整个预处理流程中,采样率必须统一。16 kHz 就是 16 kHz,别混用。