4. 频域特征
MFCC,全称 Mel 频率倒谱系数。这玩意儿在语音领域,基本就是「标配」级别的特征。我入行那会儿,第一个上手的项目就是说话人识别,用的就是 MFCC。说实话,当时照着论文抄代码,跑通了也不知道为啥要这么折腾。后来踩的坑多了,才慢慢理解每一步设计的精妙之处。
今天咱们就把 MFCC 的提取流程拆开揉碎。从 Mel 滤波器组到 DCT 变换,再到完整的流水线,一个环节都不放过。
4.1 为什么非要用 Mel 刻度?
人耳对频率的感知不是线性的。你想想看,100Hz 和 200Hz 的差距,我们听着差别很大;但 1000Hz 和 1100Hz,感觉就没那么明显了。Mel 刻度就是模拟这种听觉特性——低频分得细,高频分得粗。
Mel 频率与线性频率的转换公式:
Mel(f) = 2595 * log10(1 + f / 700)
反过来:
f = 700 * (10^(Mel/2595) - 1)
我个人习惯在代码里直接用 librosa 的 mel_frequencies 函数,省得自己手算。但理解公式背后的物理意义,对调参很有帮助。
4.2 Mel 滤波器组的设计
Mel 滤波器组,说白了就是一组三角滤波器。它们在 Mel 刻度上是等间距的,但在线性频率上,低频的滤波器窄,高频的滤波器宽。
设计步骤大致如下:
- 确定参数:滤波器个数 M(通常 20-40),FFT 点数 N,采样率 sr。
- 计算 Mel 刻度上的等间距点:从 0 Mel 到最大 Mel 值,均匀取 M+2 个点。
- 映射回线性频率:把 Mel 值转回 Hz,再对应到 FFT 的 bin 索引。
- 构造三角滤波器:每个滤波器在中心频率处值为 1,两端降为 0。
我在项目中遇到过一个问题:采样率 16kHz,滤波器个数设成 40,结果高频段的滤波器宽得离谱,覆盖了太多 bin,导致特征模糊。后来我改成 26 个滤波器,效果反而更好。嗯,这里要注意——滤波器个数不是越多越好。
| 参数 | 典型值 | 我的建议 |
|---|---|---|
| 滤波器个数 M | 20 - 40 | 语音识别用 26,音乐分析用 40 |
| FFT 点数 N | 512 / 1024 / 2048 | 16kHz 采样率用 512 或 1024 |
| 最低频率 | 0 Hz | 有时设为 80 Hz 滤除工频干扰 |
| 最高频率 | sr / 2 | 一般就是奈奎斯特频率 |
4.3 从频谱到 Mel 频谱
拿到 Mel 滤波器组之后,怎么用?
假设我们有一段音频,经过预加重、分帧、加窗、FFT 之后,得到了每一帧的幅度谱(或功率谱)。然后,把幅度谱和 Mel 滤波器组做矩阵乘法——说白了就是加权求和。
数学上很简单:
Mel_spectrum[m] = sum(spectrum[k] * H_m[k]) for k = 0..N/2
其中 H_m[k] 是第 m 个滤波器在第 k 个 bin 上的权重。
结果就是每一帧得到一个 M 维的向量,这就是 Mel 频谱。注意,这时候还没做对数运算。
4.4 对数运算:压缩动态范围
取对数,目的有两个:
- 模拟人耳听觉:声音响度与物理能量是对数关系。
- 让特征更稳定:对数能把乘性噪声变成加性噪声,方便后续处理。
实际操作就是:
log_Mel_spectrum = log(Mel_spectrum + epsilon)
加一个很小的 epsilon(比如 1e-10),防止 log(0) 出现。这个细节我当年就吃过亏——某次跑训练,loss 突然变成 NaN,查了半天发现是频谱里有全零帧。
4.5 DCT 变换:去相关与降维
到了这一步,我们有了对数 Mel 频谱。但有个问题:Mel 滤波器之间有重叠,所以相邻的滤波器输出是相关的。直接拿去做 GMM 或 HMM 建模,效果不好。
DCT(离散余弦变换)就是来解决这个问题的。它能把相关的 Mel 频谱系数转换成一组不相关的倒谱系数。
DCT 公式(Type-II,最常用):
C[n] = sum( log_Mel[m] * cos( n * (m + 0.5) * pi / M ) ) for m = 0..M-1
其中 n = 0, 1, ..., L-1,L 是保留的倒谱系数个数,通常取 12 或 13。
为什么只保留前几个系数?因为 DCT 有能量集中特性——大部分信息都集中在低阶系数里。高阶系数往往对应的是频谱的精细结构,对语音识别来说反而是噪声。
4.6 完整的 MFCC 提取流程
把上面所有步骤串起来,就是完整的 MFCC 提取流水线:
- 预加重:提升高频,平衡频谱。
- 分帧:帧长 20-40ms,帧移 10-20ms。
- 加窗:汉明窗或汉宁窗,减少频谱泄漏。
- FFT:计算每一帧的幅度谱或功率谱。
- Mel 滤波器组:加权求和,得到 Mel 频谱。
- 取对数:压缩动态范围。
- DCT:去相关,得到 MFCC。
- 动态特征:通常还会加一阶差分(Δ)和二阶差分(ΔΔ)。
下面这张图把整个流程串起来了,你可以对照着看:
4.7 动态特征:让时序信息说话
标准的 MFCC 只描述当前帧的频谱包络,不包含帧与帧之间的变化信息。但语音是动态的,音素之间的过渡信息非常关键。
所以,我们通常会在 MFCC 基础上计算一阶差分和二阶差分:
- 一阶差分(Δ):反映 MFCC 的变化速度。
- 二阶差分(ΔΔ):反映 MFCC 的加速度。
计算公式:
Δ[t] = (C[t+1] - C[t-1]) / 2
更平滑的版本会用多帧窗口:
Δ[t] = sum( k * C[t+k] ) / sum( k^2 ) for k = -W..W
窗口宽度 W 通常取 2 或 3。
最终的特征向量维度 = (MFCC 个数) × (1 + 1 + 1) = 13 × 3 = 39。这是最经典的配置。
4.8 常见陷阱与调参经验
最后,分享几个我踩过的坑:
- 滤波器个数与 FFT 点数不匹配:如果 FFT 点数太小,高频滤波器可能只覆盖一两个 bin,计算出来的能量不稳定。我建议 FFT 点数至少是滤波器个数的 4 倍。
- 忽略预加重:有些新手直接拿原始音频做 MFCC,结果高频信息被压制,识别率上不去。预加重系数 α 取 0.97 或 0.95 就行。
- DCT 保留太多系数:保留全部 M 个系数,等于没做去相关。我一般保留 12-13 个,最多不超过 20。
- 能量维度的处理:有些实现会把帧能量作为 MFCC 的第 0 个系数。注意,能量和倒谱系数量纲不同,需要归一化或加权。
MFCC 这套流程,从 80 年代用到现在,依然活跃在语音领域的前线。虽然深度学习时代有了 FilterBank 特征、Wav2Vec 等端到端方案,但 MFCC 的物理可解释性和低计算量,让它依然是很多工业级系统的首选。理解它,就是理解语音特征提取的底层逻辑。