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 函数,省得自己手算。但理解公式背后的物理意义,对调参很有帮助。

小提示: Mel 刻度不是唯一的听觉刻度。Bark 刻度、ERB 刻度也常见。但 MFCC 之所以流行,是因为它结合了 Mel 刻度和倒谱分析,效果稳定,计算量也适中。

4.2 Mel 滤波器组的设计

Mel 滤波器组,说白了就是一组三角滤波器。它们在 Mel 刻度上是等间距的,但在线性频率上,低频的滤波器窄,高频的滤波器宽。

设计步骤大致如下:

  1. 确定参数:滤波器个数 M(通常 20-40),FFT 点数 N,采样率 sr。
  2. 计算 Mel 刻度上的等间距点:从 0 Mel 到最大 Mel 值,均匀取 M+2 个点。
  3. 映射回线性频率:把 Mel 值转回 Hz,再对应到 FFT 的 bin 索引。
  4. 构造三角滤波器:每个滤波器在中心频率处值为 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 频谱。注意,这时候还没做对数运算。

避坑指南: 我曾经在功率谱上直接应用 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 有能量集中特性——大部分信息都集中在低阶系数里。高阶系数往往对应的是频谱的精细结构,对语音识别来说反而是噪声。

核心要点: DCT 在这里的作用不是压缩,而是去相关。去掉相关性之后,用对角协方差矩阵的 GMM 就能很好地建模,计算量大大降低。

4.6 完整的 MFCC 提取流程

把上面所有步骤串起来,就是完整的 MFCC 提取流水线:

  1. 预加重:提升高频,平衡频谱。
  2. 分帧:帧长 20-40ms,帧移 10-20ms。
  3. 加窗:汉明窗或汉宁窗,减少频谱泄漏。
  4. FFT:计算每一帧的幅度谱或功率谱。
  5. Mel 滤波器组:加权求和,得到 Mel 频谱。
  6. 取对数:压缩动态范围。
  7. DCT:去相关,得到 MFCC。
  8. 动态特征:通常还会加一阶差分(Δ)和二阶差分(ΔΔ)。

下面这张图把整个流程串起来了,你可以对照着看:

MFCC 提取流程 原始音频 预加重 分帧 + 加窗 FFT Mel 滤波器组 取对数 DCT 变换 MFCC 系数 Δ + ΔΔ 特征 关键参数速查 • 帧长:25ms(常用),帧移:10ms • Mel 滤波器个数:26(语音),40(音乐) • MFCC 系数个数:12-13(不含能量) • 动态特征:Δ(一阶差分)+ ΔΔ(二阶差分),最终维度通常为 39

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。这是最经典的配置。

实战建议: 如果你用深度学习模型(比如 CNN、Transformer),可以只保留静态 MFCC,让模型自己学习时序依赖。但传统 GMM-HMM 系统,动态特征几乎是必须的。

4.8 常见陷阱与调参经验

最后,分享几个我踩过的坑:

  • 滤波器个数与 FFT 点数不匹配:如果 FFT 点数太小,高频滤波器可能只覆盖一两个 bin,计算出来的能量不稳定。我建议 FFT 点数至少是滤波器个数的 4 倍。
  • 忽略预加重:有些新手直接拿原始音频做 MFCC,结果高频信息被压制,识别率上不去。预加重系数 α 取 0.97 或 0.95 就行。
  • DCT 保留太多系数:保留全部 M 个系数,等于没做去相关。我一般保留 12-13 个,最多不超过 20。
  • 能量维度的处理:有些实现会把帧能量作为 MFCC 的第 0 个系数。注意,能量和倒谱系数量纲不同,需要归一化或加权。

MFCC 这套流程,从 80 年代用到现在,依然活跃在语音领域的前线。虽然深度学习时代有了 FilterBank 特征、Wav2Vec 等端到端方案,但 MFCC 的物理可解释性和低计算量,让它依然是很多工业级系统的首选。理解它,就是理解语音特征提取的底层逻辑。