音频特征提取这个方向,我前后折腾了快六年,从最早做语音唤醒的小项目,到后来处理环境声音分类、乐器识别,绕不开的一个东西就是MFCC。很多刚入门的朋友第一次看到"Mel频率倒谱系数"这七个字,脑子是懵的——倒谱是什么?Mel又是什么?为什么语音任务里它几乎成了默认特征?我见过太多人直接调库librosa.feature.mfcc一把梭,参数全默认,结果模型效果上不去,回头排查半天发现是特征这一层就没吃透。这篇就把MFCC从头到尾拆开讲,生成步骤一步步走,每一步为什么这么设计、参数怎么定、坑在哪里,都摊开说。适合刚接触音频信号处理的学生、做语音相关工程的开发者,也适合那些用了很久库但没深究过原理的人补一补底层逻辑。
1. 先搞清楚MFCC到底在解决什么问题
1.1 从人耳听觉特性说起
声音本质是空气压强随时间的变化,麦克风录下来就是一串采样点。原始波形直接喂给模型行不行?理论上行,实践中很难。原因在于波形里混杂了太多信息——说话人的音色、内容、环境噪声、录音设备特性全搅在一起,而且同一句话不同人说,波形差异巨大,模型很难从时域采样点里直接学到"说了什么"这种抽象语义。
人耳处理声音的方式给了我们启发。人对频率的感知不是线性的,低频区域一点点频率变化人耳很敏感,高频区域变化很大反而感知不明显。举个例子,100Hz和200Hz的差别,人一听就能分辨;但8000Hz和8100Hz的差别,绝大多数人根本听不出来。这种非线性感知特性,用Mel刻度来建模。Mel刻度是一种基于人耳感知的频率标度,它把物理频率映射到"人觉得有多高"的心理感知尺度上。
MFCC做的事情,简单说就是:把一段音频的频谱,按照人耳的感知方式重新组织,再提取出一组紧凑的系数来代表这段声音的音色特征。它最初是为语音识别设计的,但后来在音乐信息检索、环境声音分析、甚至机械故障诊断里都能见到身影。理解它的关键在于理解"为什么要做这一系列变换",而不是死记步骤。
1.2 倒谱这个名字的由来与意义
"倒谱"(cepstrum)这个词是把spectrum(频谱)的前四个字母倒过来拼的,挺有意思的命名。它的数学定义是信号功率谱取对数后再做一次傅里叶逆变换。为什么要这么绕?
想象一下,一段语音信号可以粗略看成两部分卷积在一起:声带振动产生的激励信号,和声道形状决定的滤波特性。卷积在时域里混在一起分不开,但到了频域,卷积变成乘积,取对数之后乘积又变成加法。这时候激励的对数谱和声道的对数谱是相加关系,再做一次逆变换(相当于回到某种"时域"),两者就被分到了不同的位置——声道特性集中在低倒谱区域,激励的周期性结构跑到高倒谱区域。我们取前十几个系数,就拿到了声道特性,也就是音色包络,把音高、语调这些变化因素压下去了。
这就是MFCC对说话人变化、音高变化相对鲁棒的原因。它提取的是"声道在那一刻的形状",而声道形状主要由说了什么音素决定,跟谁说的、说得多大声关系没那么大。当然这个鲁棒性是相对的,不是绝对的,后面讲特性的时候会细说。
1.3 MFCC在整个音频处理链路里的位置
一个典型的音频任务链路是这样的:原始音频 → 预处理(分帧、加窗)→ 特征提取(MFCC或别的)→ 模型(传统分类器或深度网络)→ 输出。MFCC处在特征提取这一环,它的输出是一个二维矩阵,行是帧数,列是系数维度(常见13维、20维、40维)。
这个矩阵就是模型看到的"声音"。所以MFCC的质量直接决定了模型能学到什么。我见过有人采样率设错、帧长设错,导致MFCC矩阵形状都不对,模型训练半天loss不降,最后发现是特征层的问题。这一层值得花时间吃透。
2. 生成步骤逐环节拆解
2.1 预加重:为什么要给高频"加把劲"
第一步是预加重,公式很简单:y[n] = x[n] - α·x[n-1],α通常取0.97。这一步的作用是提升高频分量。为什么?语音信号的能量主要集中在低频,高频部分能量小,而且人发声时声门激励的频谱大致按每倍频程下降6dB的规律衰减。高频被"压"得太狠,后面做频谱分析时高频细节容易被淹没。预加重相当于一个高通滤波器,把高频抬起来,让整个频谱更平坦,各频段的信息都能被公平对待。
α取0.97是经验值,对应大概在几十Hz到几百Hz处开始明显提升高频。取0.9到0.99之间都有人用,0.97最常见。这个参数不用太纠结,但要知道它的存在。实操中如果你发现高频噪声特别重,预加重会放大噪声,这时候可以考虑跳过或者减小α。
2.2 分帧与加窗:把长信号切成短片段
音频是长时信号,但声音的统计特性在短时间内才近似平稳。一般取20到40毫秒作为一帧,25毫秒是最常见的默认值。为什么是25毫秒?因为在这个时间尺度内,人的发音器官形状基本不变,可以认为信号是短时平稳的。帧移通常取10毫秒,也就是相邻帧重叠15毫秒。重叠是为了避免帧边界处的信息丢失,让帧与帧之间平滑过渡。
分帧之后要加窗。直接切出来的帧,两端是突变的,做傅里叶变换会产生频谱泄漏——本来集中在某个频率的能量"漏"到旁边频率去了。加窗就是让帧两端平滑衰减到零,减少泄漏。最常用的是汉明窗(Hamming)和汉宁窗(Hanning)。汉明窗两端不完全到零,第一旁瓣衰减更好;汉宁窗两端到零,主瓣更宽。语音任务里汉明窗用得多。
这里有个细节:窗长和帧长要一致,加窗是逐点相乘。加窗之后帧的能量会变化,所以有些实现会在加窗后做能量归一化,但标准MFCC流程里通常不做,因为后面的对数操作会处理动态范围。
2.3 傅里叶变换:从时域到频域
对每一帧做N点FFT,N通常取256或512,要大于等于帧长,不足补零。25毫秒在16kHz采样率下是400个点,取512点FFT,补112个零。补零不增加信息,但让频率分辨率更细,频谱更平滑。
FFT之后得到复数频谱,取模平方得到功率谱。这一步把时域波形变成了"每个频率上有多少能量"的描述。到这里还是线性频率刻度,接下来要往Mel刻度上转。
2.4 Mel滤波器组:按人耳的方式重新分频
这是MFCC区别于普通倒谱特征的核心一步。设计一组三角形滤波器,在Mel刻度上均匀分布,在物理频率上则低频密、高频疏。具体做法是:确定最低频率(通常0Hz或几十Hz)和最高频率(通常取采样率的一半,即奈奎斯特频率),把这段频率范围转换到Mel刻度,在Mel刻度上等间距取若干个点,再转回物理频率,得到每个三角滤波器的中心频率。
Mel刻度与物理频率的换算公式常用的是:mel = 2595 · log10(1 + f/700)。反过来f = 700 · (10^(mel/2595) - 1)。这个公式里700这个常数是拟合人耳数据得到的,不同文献有细微差异,但主流实现都用这个。
滤波器个数一般取26到40个,26是早期语音识别的经典值,40在现代实现里更常见,因为能保留更多高频细节。滤波器组的作用是把线性频谱"压缩"成Mel频谱,每个滤波器输出是该频带内能量的加权和。这一步既降维又贴合人耳特性。
滤波器的设计有几个要点:每个三角滤波器在Mel刻度上跨度相同,相邻滤波器在中心频率处有50%重叠,保证频谱不被割裂。最低和最高滤波器的边界要处理好,别让边缘频率的能量丢失。
2.5 取对数:把乘性关系变加性
对每个滤波器的输出取自然对数。前面讲倒谱时说过,取对数是为了把卷积关系变成加法关系,方便后续分离。另外对数也压缩了动态范围,人耳对声音强度的感知本身接近对数关系,这一步符合听觉特性。取对数后得到的是对数Mel频谱,维度等于滤波器个数。
2.6 离散余弦变换:去相关并压缩
对对数Mel频谱做DCT,取前若干维系数,就是最终的MFCC。DCT的作用有两个:一是去相关,Mel滤波器组的输出之间是高度相关的(相邻滤波器有重叠),DCT能把能量集中到少数几个系数上;二是压缩,大部分信息集中在前十几维,后面的系数接近零,可以丢掉。
通常取前12或13维,加上第0维(代表帧能量)构成13维。有些实现会丢掉第0维,因为它对增益变化敏感,然后单独算一个对数能量特征替代。DCT用类型II最常见,和MFCC的标准定义对应。
到这里,一帧音频就变成了一个13维向量。整段音频所有帧的向量堆起来,就是模型要的输入矩阵。
3. 关键参数怎么定:一份可抄的配置表
参数选择没有绝对标准,但有一些经过大量实践验证的默认值。下面这张表是我在多个项目里总结的常用配置,按任务类型区分。
| 参数 | 语音识别常用值 | 音乐/环境声常用值 | 说明 |
|---|---|---|---|
| 采样率 | 16000 Hz | 22050 或 44100 Hz | 语音16k足够,音乐要保留高频 |
| 帧长 | 25 ms | 25-50 ms | 音乐可用更长帧捕捉和声 |
| 帧移 | 10 ms | 10-25 ms | 帧移越小时间分辨率越高 |
| 窗函数 | 汉明窗 | 汉宁窗 | 语音偏汉明,音乐偏汉宁 |
| FFT点数 | 512 | 1024或2048 | 要≥帧长对应采样点数 |
| Mel滤波器数 | 26-40 | 40-64 | 音乐需要更细的频率划分 |
| 倒谱系数维度 | 13 | 20-40 | 语音13够用,音乐信息多要更多维 |
| 预加重系数 | 0.97 | 0.97或不加重 | 音乐有时不加预加重 |
这张表可以直接拿去改。但我要强调,参数不是拍脑袋定的,要结合你的任务。比如做关键词识别,13维加差分通常够;做乐器分类,高频细节重要,滤波器数和维度都要往上加。我做过一个环境声音分类的项目,一开始用语音的默认配置,准确率卡在七十几上不去,后来把滤波器数从26提到40、维度从13提到20,涨了将近八个点。特征维度不够,模型再强也巧妇难为无米之炊。
3.1 差分特征:给静态特征加上动态信息
MFCC本身描述的是每一帧的静态频谱包络,但声音是动态的,音素之间的过渡信息很重要。所以通常会加一阶差分(delta)和二阶差分(delta-delta)。一阶差分反映MFCC随时间的变化速度,二阶反映变化加速度。13维MFCC加13维delta加13维delta-delta,共39维,这是语音识别里非常经典的配置。
差分计算一般用回归公式,对当前帧前后各取N帧(N常取2)做加权差分,而不是简单相邻帧相减。这样更平滑,抗噪更好。加差分之后特征维度翻三倍,计算量增加但通常值得。
3.2 归一化:别忽略的一步
不同录音的音量、设备增益不同,MFCC的绝对值会有偏移。常见做法是做倒谱均值归一化(CMN),即对整段音频的每一维MFCC减去该维的均值,让特征零均值化。这能消除通道效应。更严格的还有均值方差归一化(MVN)。这一步在跨设备、跨场景的任务里特别重要,我踩过坑:训练集和测试集录音设备不同,不做归一化,模型直接崩,做了之后恢复 normal。
4. MFCC的特性:它强在哪,弱在哪
4.1 优势:紧凑、鲁棒、成熟
MFCC最大的优势是紧凑。一段1秒16kHz的音频有16000个采样点,经过MFCC变成100帧×13维=1300个数,压缩了十几倍,而且保留了大部分与内容相关的信息。这对传统机器学习模型特别友好,特征维度低,样本需求少,训练快。
鲁棒性体现在对增益变化和音高变化的相对不敏感。前面讲过,倒谱操作把声道特性和激励分离,取低维系数主要保留声道特性,所以不同人说同一个字,MFCC的差异比原始波形小得多。音量变化主要影响第0维能量,丢掉第0维或用CMN就能缓解。
成熟度也是优势。几十年的研究积累,各种工具库支持完善,参数经验丰富,遇到问题容易找到参考。在很多任务上,MFCC仍然是强基线,深度学习方法想超过它也得费点劲。
4.2 局限:丢掉了相位和细粒度时频结构
MFCC只用了频谱的幅度信息,相位完全丢弃。对于语音识别这类任务,相位信息相对不重要,但对某些音频任务,比如声音合成、空间音频处理,相位是关键。这是MFCC的天然局限。
另一个局限是它做了较强的信息压缩。Mel滤波器组把频率轴压缩了,DCT又把维度压缩了,细粒度的时频结构被抹平。对于需要精细时频分析的任务,比如鸟鸣识别、某些音乐流派分类,MFCC可能不如梅尔频谱图(Mel-spectrogram)直接喂给卷积网络效果好。现在深度学习流行之后,很多任务直接用对数梅尔频谱图,让网络自己学特征,效果往往更好。MFCC更适合数据量小、需要手工特征的传统流程。
还有一点,MFCC的帧长固定,对持续时间很短的瞬态声音(比如打击乐)捕捉不够好。这类任务要考虑更短的帧或者别的特征。
4.3 和其他特征的对比
| 特征 | 维度 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| MFCC | 13-40 | 紧凑、鲁棒、成熟 | 丢相位、压缩强 | 语音识别、说话人识别 |
| 对数梅尔频谱 | 40-128 | 保留更多时频细节 | 维度高、相关性强 | 深度学习音频分类 |
| 线性预测系数 | 10-16 | 建模声道物理特性 | 对噪声敏感 | 语音编码、传统识别 |
| 过零率/能量 | 1-2 | 极简、快 | 信息量少 | 端点检测、预处理 |
这张表不是要分高下,而是说不同特征适合不同场景。我现在的习惯是:传统流程或小数据用MFCC,深度学习大数据用对数梅尔频谱图,两者都跑一遍做对比,看哪个在验证集上稳。
5. 典型应用场景与实操建议
5.1 语音识别与关键词检测
这是MFCC的老本行。传统GMM-HMM声学模型时代,MFCC加差分是标配。现在虽然端到端模型流行,很多前端仍然用MFCC或梅尔频谱。做关键词检测(比如唤醒词)时,数据量通常不大,MFCC的紧凑性优势明显。我的建议是13维MFCC加delta和delta-delta共39维,帧长25ms帧移10ms,配合CMN,这套配置在多数关键词任务上能打。
实操中要注意端点检测。静音段的MFCC没有意义,还会干扰模型。先做VAD把有效语音段切出来,再提特征。VAD可以用短时能量加过零率做,简单有效。
5.2 说话人识别
MFCC在这里要小心一个矛盾:它设计上是为了弱化说话人差异、突出内容,但说话人识别恰恰要区分说话人。所以直接用MFCC效果一般,通常要用更长的统计量,比如对整段音频的MFCC算均值和方差,或者用i-vector、x-vector这类方法。MFCC在这里更多是底层特征,上层还要做统计建模。滤波器数和维度可以适当加大,保留更多个人特征。
5.3 环境声音与音乐分类
这类任务里MFCC能用但不是最优。环境声音种类杂,很多声音的区分靠的是细粒度时频纹理,MFCC压缩太狠会丢信息。我一般会同时提MFCC和对数梅尔频谱,做特征融合或者分别训练再集成。音乐任务里,节奏、和声信息重要,MFCC对这类信息的表达有限,通常要配合节奏特征、色度特征一起用。
5.4 非音频领域的意外应用
有意思的是,MFCC的思路被搬到了别的领域。比如机械振动信号诊断,把振动信号当"音频"处理,用类似的滤波器组和倒谱分析提取故障特征。还有把MFCC用在心电图、脑电信号分析上的尝试。核心逻辑是一样的:信号里有卷积混合的成分,用倒谱分离。这类跨领域应用要注意,Mel刻度的参数是按人耳调的,搬到别的信号上未必合适,需要重新设计滤波器组。
6. 常见问题与排查实录
做MFCC这些年,踩过的坑不少,整理成速查表,遇到问题先对一遍。
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
| 特征矩阵形状不对 | 帧长/帧移/采样率不匹配 | 检查帧长对应采样点数,FFT点数≥帧长 |
| 模型loss不降 | 特征未归一化、维度不够 | 加CMN,适当增加滤波器数和维度 |
| 高频信息丢失严重 | 滤波器数太少、采样率太低 | 提高滤波器数,确认采样率覆盖目标频段 |
| 跨设备效果崩 | 通道效应未消除 | 做CMN或MVN,训练时加数据增强 |
| 静音段干扰大 | 未做端点检测 | 先VAD切有效段再提特征 |
| 计算太慢 | FFT点数过大、帧移过小 | 合理设置FFT点数,帧移别小于5ms |
几个重点说说。第一个形状问题,最常见的是采样率改了但帧长没跟着改。25ms在16kHz是400点,在8kHz是200点,FFT点数也要相应调整,不然频率分辨率对不上。第二个归一化问题,我见过太多人跳过这步,单设备实验看不出问题,一换设备就露馅。第三个高频丢失,做音乐任务时特别容易犯,用语音的26个滤波器处理音乐,高频细节全糊了,一定要按任务调。
还有一个隐蔽的坑:不同库的MFCC实现细节有差异。比如第0维是否包含、DCT类型、滤波器边界处理,不同库可能不一样。跨库对比特征时要注意对齐,别拿A库的13维和B库的13维直接比,可能第0维定义就不同。我一般固定用一个库,或者自己实现一遍确保可控。
提示:调试MFCC时,把中间结果可视化出来看。原始波形、加窗后的帧、功率谱、Mel滤波器组响应、对数Mel频谱、最终MFCC,每一步都画出来,哪里不对一目了然。这比盯着数字猜快得多。
7. 自己实现一遍:理解的最快路径
调库用久了容易黑盒化,我强烈建议自己用numpy实现一遍MFCC。不需要多优雅,能跑通、能和库的结果对上就行。实现顺序就是前面讲的步骤:预加重、分帧、加窗、FFT、功率谱、Mel滤波器组、取对数、DCT。每一步单独写函数,中间结果存下来可视化。
自己实现的过程中,你会被迫想清楚每个参数的物理意义,比如滤波器组的中心频率怎么算、边界怎么处理、DCT的归一化方式。这些细节在调库时是隐藏的,但恰恰是理解MFCC的关键。我当年就是自己写了一遍之后,才真正明白为什么取前13维、为什么第0维特殊、为什么差分要用回归公式。
实现完和库对比,如果结果有差异,逐个环节排查。常见差异点在滤波器组的频率边界、DCT的归一化、第0维的处理。对上之后,你对MFCC的理解就到位了,再遇到特征相关的问题,排查起来心里有底。
最后分享一个我个人的习惯:每接一个新音频任务,先不提复杂特征,就用MFCC跑一个基线,看看数据本身的可分性。如果MFCC基线就很差,往往说明数据有问题(标注错、采样率错、静音太多),先解决数据再谈模型。这个习惯帮我省了很多时间,避免在烂数据上堆模型。特征工程这层,MFCC是个很好的探针,简单、快、信息量够,值得每个做音频的人吃透。