尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
用Keras搭建MLP+MFCC实现环境噪声分类
这段时间一直在折腾一个跟声音相关的分类Demo需求很简单给定一段音频判断它属于哪一类噪声。比如空调运行的嗡嗡声、雨声、键盘敲击声、脚步声、交通噪声一共五类。第一反应是上卷积网络或者循环网络但仔细想了下需求和计算资源最后用Keras搭了个多层感知机效果相当能打。这篇先把整体思路、数据处理、模型搭建和训练过程完整记录下来算作这个系列的第一篇后续再聊实时识别和工程化落地的内容。1. 为什么用“MLPMFCC”的组合搞定噪声分类1.1 噪声分类到底是什么样的任务先说清楚任务本质。噪声分类属于一维信号分类问题输入是一段时间的音频波形输出是类别标签。它和语音识别有个关键区别语音识别需要建模音素、音节这种强时序依赖而噪声分类更看重声音的“音色特征”。比如空调嗡嗡声是稳定低频、键盘敲击声是短促高频瞬态、雨声是宽频随机噪声不同类别在频谱上的分布差异很大。从数据形态看一段2秒的音频在16kHz采样率下会变成32000个采样点。如果直接把这样的一维数组丢给全连接网络输入维度就是32000第一层参数量会爆炸而且原始波形对相位偏移、幅度缩放、背景叠加都极其敏感泛化能力会很差。所以通常的做法是先做特征提取把音频压缩成一小段有物理意义的特征向量再交给分类模型。这也是“MLPMFCC”组合成立的前提。1.2 特征向量与MLP的天然配合MFCC是Mel频率倒谱系数模拟人耳对不同频率声音的非线性感知特性。它的核心思路是先把音频按帧做傅里叶变换得到频谱再把频谱映射到Mel刻度上最后取对数并做离散余弦变换得到一组低维系数。每一帧音频可以算出一组MFCC整段音频可以取时间维度的统计量比如均值得到一个固定长度的特征向量。MLP的全称是多层感知机本质上就是若干层全连接网络堆叠。它的建模能力在于对输入特征做非线性组合和抽象。MFCC已经把音频从“32000个采样点”压缩成“39个系数”这种低维紧凑表示MLP在这个表示上做分类既不需要像CNN那样构造二维谱图也不需要像LSTM那样考虑时序传播整个链路非常简洁。实测下来干净环境下5类噪声用13维MFCC加delta特征就能达到95%左右的分类准确率直接用原始波形做同样的分类任务MLP很难稳定超过85%而且训练慢得多。1.3 什么时候MLP会不够用不说MLP万能也得说清楚它的边界否则后续工程化的时候容易踩坑。MLP的问题在于它没有显式建模时序结构。MFCC均值这种“全局特征”适合区分音色差异明显的噪声但如果两类噪声有大量重叠频谱或者环境里混响很强、信噪比很低全局统计信息被污染MLP的准确率就会明显下降。这时就要考虑两类升级方案一类是用CNN处理短时频谱图利用局部频率模式另一类是换RNN、Transformer这类序列模型建模帧级别的动态变化。所以选择MLP不是因为它最强而是因为在这个任务阶段它足够简单、足够快、足够稳。2. 数据准备从原始音频到可训练的特征矩阵2.1 音频切割与目录组织数据是一切的前提。我这里每个类别收集了大约200条原始音频样本来源有自己录制的也有网上找的公开环境声音素材。原始录音长短不一有的十几秒有的一分钟。直接整段用不是不行但有两个问题一是样本量太少二是模型会学到“时长”这个无关特征。正确的做法是把长音频切成固定长度的小片段。我的做法是统一切成2秒一段。为什么是2秒太短比如0.5秒可能装不下一次完整的敲击或脚步声信息不足太长比如5秒又会让同一个音频切片内部混入多种状态平均特征被稀释。2秒对大部分环境噪声来说能覆盖一到两个完整发声周期比较平衡。切割用librosa的load加载后按帧索引截取就行。目录组织方面我采用按类别建文件夹的方式data/ air_conditioner/ 001.wav 002.wav rain/ 001.wav keyboard/ 001.wav footsteps/ 001.wav traffic/ 001.wav这样好处是后面写特征提取脚本时只需要遍历根目录、读取子文件夹名作为标签代码非常干净。另外注意统一采样率我统一转成16kHz单声道。很多人忽略这一步不同来源的音频采样率不一样提取出的MFCC维度本身不受影响但频率轴的物理含义就错乱了模型训练时容易学出一些说不清楚的伪规律。2.2 MFCC特征提取的完整流水线特征提取我用librosa完成核心代码不长import os import numpy as np import librosa SAMPLE_RATE 16000 SEGMENT_SEC 2 SEGMENT_LEN SAMPLE_RATE * SEGMENT_SEC def extract_features(file_path, n_mfcc13, n_fft512, hop_length128): audio, sr librosa.load(file_path, srSAMPLE_RATE, monoTrue) # 如果音频长度不足2秒做零填充超过则按2秒截断 if len(audio) SEGMENT_LEN: audio np.pad(audio, (0, SEGMENT_LEN - len(audio))) else: audio audio[:SEGMENT_LEN] mfcc librosa.feature.mfcc( yaudio, srsr, n_mfccn_mfcc, n_fftn_fft, hop_lengthhop_length ) return mfcc上面接口返回的是(n_mfcc, 时间帧数)的二维数组。hop_length128意味着每帧间隔128个采样点2秒音频大概有250帧左右。如果直接把整个二维矩阵拉平特征维度是13 * 250 3250MLP也能处理但没必要而且不同长度的音频帧数不一样没法统一。我的做法是取时间维的均值得到13维的全局特征mfcc.mean(axis1)。但只用静态MFCC丢失了动态信息。语音和噪声分类里一个很常用的改进是加delta和delta-delta也就是计算相邻帧之间的变化率反映特征的时序趋势。用librosa.feature.delta可以很方便地实现。最终我每个样本用39维特征13维静态MFCC加上13维delta再加上13维delta-delta。def build_feature_vector(file_path): mfcc extract_features(file_path) # (13, frames) mfcc_delta librosa.feature.delta(mfcc) mfcc_delta2 librosa.feature.delta(mfcc, order2) feat np.concatenate([ mfcc.mean(axis1), mfcc_delta.mean(axis1), mfcc_delta2.mean(axis1) ]) # (39,) return feat实际对比过只用静态13维时验证集准确率在92%左右加上delta之后提升到95%以上。代价是特征维度从13变成39但对MLP来说增加的计算量微乎其微完全值得。2.3 归一化、数据集划分与一个隐蔽的数据泄漏特征矩阵准备好以后归一化是必须的一步。MFCC系数的绝对值范围差异很大低阶系数和高阶系数的数值尺度可能差一个数量级不归一化会让梯度更新被数值大的维度主导收敛很不稳定。我用StandardScaler把每个特征维度变成均值0、方差1。这里有个新手特别容易踩的坑归一化只能基于训练集拟合参数然后拿着训练集拟合的均值方差去变换验证集和测试集。如果先在整个数据集上fit再切分验证集的统计信息已经混进了归一化参数等于模型在训练时“偷看”了验证集的信息最终评估分数会虚高。这种数据泄漏非常隐蔽因为在准确率上看不出来异常只有部署到真实数据上才会原形毕露。数据切分我用train_test_split并且设置stratifyy保证每个类别的样本比例在训练集和验证集里保持一致。顺序是先做切分再做归一化。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler X np.array(all_features) y np.array(all_labels) X_train, X_val, y_train, y_val train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) scaler StandardScaler() X_train scaler.fit_transform(X_train) X_val scaler.transform(X_val)切分比例我用8:2。如果你的数据量特别少可以考虑7:3或者做K折交叉验证但注意交叉验证里归一化也要在每一折的训练折上重新拟合逻辑跟上面是一样的。标签处理方面我把类别名转成整数索引再用keras.utils.to_categorical转成one-hot向量。这样配合categorical_crossentropy使用是新手最容易理解的方式。如果你不想转one-hot也可以直接用整数标签配合sparse_categorical_crossentropy效果一样。3. 用Keras搭建多层感知机结构设计与逐层解读3.1 网络层数与神经元数量的设计逻辑MLP的结构设计没有绝对标准但有一些实用的经验法则。我这次用的是四个隐藏层256、128、64、32结构上是一个逐渐收窄的“漏斗”。为什么这么设计输入39维特征后第一层256个神经元先把特征映射到更高维空间相当于给模型足够多的组合空间去捕捉特征交叉关系后面层数逐层递减强迫网络把学到的信息压缩成越来越抽象、越来越有判别性的表示最后交到输出层做分类。神经元数量也不是越多越好。曾有测试把第一层加到1024个神经元验证集准确率并没有明显提升训练时间倒是翻了几倍。原因很简单输入特征是39维的强结构化特征不是ImageNet那种百万像素级数据过大的隐层只会带来冗余参数更容易过拟合。现在的规模大概有5万多个参数用CPU训练几十秒就能跑完一个epoch整个训练过程不会超过两分钟。模型定义用Keras的Sequential模型代码非常直观from keras import layers, models model models.Sequential([ layers.Input(shape(39,)), layers.Dense(256, activationrelu), layers.Dropout(0.3), layers.Dense(128, activationrelu), layers.Dropout(0.3), layers.Dense(64, activationrelu), layers.Dropout(0.3), layers.Dense(32, activationrelu), layers.Dense(5, activationsoftmax) ])我多放了一个32维的隐藏层而不是直接从64跳到5。虽然理论上最后一层隐藏层不需要太多神经元也能完成分类但多一层低维表示可以让网络在输出前再做一次非线性整合对区分容易混淆的类别有帮助。3.2 从ReLU到Softmax激活函数选型隐藏层激活函数统一用ReLU输出层用Softmax这个组合是当前MLP的默认方案。ReLU的优势在于计算简单、收敛快而且对正区间梯度恒为1能有效缓解深层网络里的梯度消失问题。可能有人会想用sigmoid或tanh但这两个函数在输入绝对值较大时梯度会趋近于0网络一深就训不动。噪声分类这个任务里没有“需要压缩到-1到1”的特殊理由所以ReLU是第一选择。Softmax做的事情是把最后一层输出变成总和为1的概率分布。比如输出[0.1, 0.3, 0.5, 0.05, 0.05]意思是模型认为这段音频属于第三个类别的概率是50%。分类时取argmax挑最大概率对应的类别就行。配合的损失函数是categorical_crossentropy它衡量模型输出的概率分布与真实one-hot标签之间的差异这也是多分类问题的标准配置。3.3 防止过拟合的招数Dropout、EarlyStopping、学习率调度单靠网络结构还不够训练过程中必须配合正则化手段。我在每个隐藏层后面都加了Dropout(0.3)也就是每次训练迭代随机丢弃30%的神经元。它的作用类似一种廉价的集成学习每次丢弃不同的神经元组合等于让网络在训练过程中经历了多个子网络的联合预测最终学到的特征更加鲁棒不会过度依赖某一个特征通道。编译阶段我指定优化器为Adam初始学习率设为0.001监控指标用准确率model.compile( optimizeradam, losscategorical_crossentropy, metrics[accuracy] )训练时最容易犯的错误是把epoch设个大数然后傻等结束。正确做法是用回调配合EarlyStopping。我设置patience15意思是验证集损失连续15个epoch没有改善就提前停止训练自动返回历史最优模型。另外还加了ReduceLROnPlateau验证集损失停滞时把学习率减半让模型在最优解附近做更精细的搜索。from keras import callbacks early_stop callbacks.EarlyStopping( monitorval_loss, patience15, restore_best_weightsTrue ) reduce_lr callbacks.ReduceLROnPlateau( monitorval_loss, factor0.5, patience5, min_lr1e-6 ) history model.fit( X_train, y_train, validation_data(X_val, y_val), epochs100, batch_size32, callbacks[early_stop, reduce_lr] )restore_best_weightsTrue这个参数值得单独说一下。如果不开启EarlyStopping触发时返回的是最后一个epoch的权重而不是验证集损失最低的那个epoch模型效果可能已经退化。这个坑我一开始也踩过后来检查发现返回的模型比最优状态低了两个百分点亏大了。4. 训练实测曲线判读与常见问题排查4.1 训练过程中的实时监控fit执行时控制台会打印每个epoch的训练损失和验证损失、准确率。我关心的是两个趋势训练集准确率是否持续上升验证集准确率是否也在合理爬升。正常情况是训练集准确率前期上升很快验证集准确率稍微滞后一点验证集损失可能会有小幅度抖动但只要整体趋势向下就是健康的。我这次训练的过程大致是这样前10个epoch训练准确率快速冲到90%以上验证准确率在85%左右到第25个epoch验证准确率站上93%第50个epoch以后曲线变平缓验证准确率在94%到96%之间波动。EarlyStopping在62个epoch触发总共耗时不到两分钟。训练结束后打印验证集准确率在95.3%左右。如果你看到训练准确率一路上涨但验证准确率怎么都不动那基本就是过拟合的信号下一节详细展开。4.2 过拟合的识别与应对第一次搭这个模型的时候我没有加任何Dropout隐藏层设为512和256训练结果非常典型训练集准确率无限逼近100%验证集准确率卡在88%左右。这个现象就是模型“背”下了训练数据中的细节和噪声却无法泛化到新样本。应对过拟合有几板斧按优先级排序加Dropout、减模型容量、加正则化系数、扩充数据。我实际体验下来加Dropout是最立竿见影的。从零Dropout加0.3的Dropout之后验证准确率提升了大概5个百分点。减隐藏层宽度也有帮助但不如Dropout效果明显。还有一个思路是给Dense层加kernel_regularizerl2(0.001)通过限制权重范数防止某些特征权重过大不过我在这个任务里用Dropout已经够了L2正则对最终结果没有额外提升。4.3 类别样本不均衡的坑我收集的音频数量大致均衡每个类别两百多条所以没做特殊处理。但如果你在扩展类别时发现某个类别音频特别少训练时模型的预测会偏向样本多的类别。这种现象在混淆矩阵上表现得很明显少数类别的召回率偏低多数类别的精确率也受影响。处理办法有三种一是尽量多收集少数类样本二是对少数类样本做数据增强比如时间偏移、加白噪声、调整音量、音调变化三是在训练时设置类别权重让少数类的损失贡献更大class_weight { 0: 1.0, 1: 2.0, # 少数类权重更大 2: 1.0, ... }我自己的经验是数据增强通常比调权重更有效因为权重只是改变了梯度比例没有增加新的信息量。但增强要控制尺度加噪声太猛会把原本的类别特性掩盖掉。5. 识别环节从模型文件到单条音频预测5.1 保存与加载模型训练完成后要做的第一件事就是把模型和预处理器都存下来。模型用Keras自带的方式保存model.save(noise_mlp.keras)加载的时候用keras.models.load_model(noise_mlp.keras)就行。这里强调一下新版Keras官方推荐用的是.keras格式而不是老的.h5。.keras格式会把模型结构和权重打包在一起兼容性和后续维护体验都更好。容易忽略的是StandardScaler也得单独存。我一般用pickleimport pickle with open(scaler.pkl, wb) as f: pickle.dump(scaler, f)为什么预处理器跟模型一样重要因为预测阶段的输入必须经过跟训练阶段完全一致的变换。如果你加载模型时忘了scaler直接把原始特征丢给模型数值尺度对不上预测概率会完全乱套。这个问题在模型部署时极常见我建议从一开始就把“模型scaler”绑定成一个整体最好封装到一个类里。5.2 单条音频预测的实现细节预测阶段的核心原则是对新的音频做一遍跟训练时一模一样的处理链路。我把预测封装成下面的函数def predict_single(file_path, model, scaler): feat build_feature_vector(file_path) # (39,) feat scaler.transform(feat.reshape(1, -1)) # reshape成(1, 39) prob model.predict(feat, verbose0) label int(np.argmax(prob, axis1)[0]) return label, prob这里面有两个细节很容易翻车。第一个是reshape(1, -1)模型训练时的输入形状是(样本数, 特征维度)即使只预测一条也要保留batch维度否则会报维度不匹配的错误。第二个细节是预测时不要再对模型做dropoutKeras的推理模式会自动关闭dropout所以不用额外处理。说一下我对模型行为的观察在干净录音上模型对空调声和交通声的判断几乎是100%稳定对雨声和键盘声偶尔会出错。键盘声和脚步声在时域波形上形态不同但在平均MFCC特征上可能靠近。解决思路是把“取平均”替换成“取分位数特征”或者“按帧分段建模”这个等后面做提升时细聊。5.3 混淆矩阵与真实表现分析光看准确率不够我打印了分类报告和混淆矩阵用sklearn.metrics自带的功能from sklearn.metrics import classification_report, confusion_matrix y_pred_val model.predict(X_val, verbose0).argmax(axis1) y_true_val y_val.argmax(axis1) print(classification_report(y_true_val, y_pred_val)) print(confusion_matrix(y_true_val, y_pred_val))从分类报告里能看出不少东西。整体准确率95%其中“空调声”和“交通声”的precision和recall都很高说明这两种噪声的频谱特征非常稳定“雨声”和“脚步声明”偶尔会互相混看混淆矩阵会发现大概有6%的雨声样本被分到了脚步声类。原因并不难理解脚步声在有些地面材质上会产生类似雨声的宽频瞬态成分而我又用的是全局平均特征把时间结构信息丢掉了不少。这个问题想彻底解决要么换成CNN处理帧级特征要么在特征里加入MFCC的方差、频谱质心、过零率等补充信息。我目前是倾向在现有MLP框架里先丰富特征因为改动成本最低。最后说几句这段时间的个人体会。用MLP做噪声分类最大的优点是链路短、排错容易从音频加载到模型预测涉及的环节比CNN方案少很多特别适合验证“这个分类任务到底能不能做”的阶段。但它的上限也确实受限尤其在低信噪比或强混响环境里会明显吃力。这个Demo到目前还只是一个离线分类器我下一步打算把它接到麦克风实时流上做滑动窗口式的连续识别同时把每类噪声的触发时间戳记录下来。到时候面临的问题会更偏向工程侧延迟控制、窗口重叠率、类别平滑策略这些等我做出来再继续整理。
RELATED

相关推荐

DeepSeek提示词实战手册:从写不准到写得准的7大场景落地法

DeepSeek提示词实战手册:从写不准到写得准的7大场景落地法

简介:这是一份面向AI初学者与实践者的DeepSeek系统化学习指南,专为自媒体创作者、教师、学生、商务人士及科研工作者设计,聚焦自然语言处理、数据分析与图像生成等高频应用场景,助力用户提升效率、激发创意并优化投资决策。资源为…

📅 2026/10/16 21:52:32
2026年多账号运营怎么做?新媒体矩阵工具选型对比

2026年多账号运营怎么做?新媒体矩阵工具选型对比

2026年,多账号运营的画风变了:监管新规落地、平台风控升级,“多开几个号、一键同步分发”的粗放打法已经走不通。不少团队号越开越多,效率反而下降,甚至集体限流。这篇文章结合我们自己的矩阵运营实践,讲清…

📅 2026/10/16 21:52:32
四合院木蜡油避坑指南:老宅翻新最容易踩的8个坑

四合院木蜡油避坑指南:老宅翻新最容易踩的8个坑

四合院翻新,木作是最费心思也最容易翻车的部分。木门窗、廊柱、梁枋、花格这些老木头,既要留住古朴质感,又要扛住风吹日晒。很多业主花了几万块做木蜡油涂装,没过两年就开裂、起皮、长毛——问题往往出在动工前就踩了坑。这篇把民…

📅 2026/10/16 21:52:32
MORE NEWS

更多资讯

📰

实测虎贲等考AI:论文全流程写作工具的优势与雷区

1. 写在实测之前:为什么我盯上"全流程"这个关键词先说结论:市面上的AI写作工具,我一共摸过十几个,从免费小工具到按字数收费的"高端货"都试了一遍。绝大多数产品有个通病——它们只解决"憋不出字"的…

📰

遗留系统初始化模块迁移与排错实践指南

简介:面向 ADI Blackfin BF533 处理器开发者,这份初始化 SDRAM 工程包对应 Project Option 中 initialization 流程所需的 Init_Sdram.dxe,重点解决 LDR 烧录时因 SDRAM 参数与硬件配置不一致而无法正常启动的问题。使用前应核对自身板卡的实…

📰

jre-11.0.10.zip实战:Java运行时安装部署与避坑指南

简介:JRE 11 是从 JDK 11 中提取的精简运行环境,面向服务器资源紧张、只需运行 Java 11 应用的运维与开发人员。相比完整 JDK 的 279MB 占用,压缩包仅 56.27MB,解压后约 158MB,能有效降低磁盘与内存消耗,特…

📰

SAP BSP 报表开发实战:从 SE80 建应用到事件处理全流程

简介:这份资源是面向SAP ABAP开发者的BSP报表开发实例,适合已掌握ABAP基础、希望入门SAP Web应用与动态报表的初中级开发者。内容围绕SE80中创建BSP应用、页面与CSS引用,重点演示ABAP脚本与HTML混合编写,包括表单提交、事件处理及…

📰

弱监督音素预训练:低资源语言语音识别的破局之道

语音识别做了几年,最深的体会是:大语种拼的是算力和资源,小语种拼的是“耐心和方法”。少数民族语言,或者说任何低资源语种,最尴尬的不是模型不够强,而是连“拿来训练的数据”都凑不齐。这几年把普通话、英…

📰

贪心算法四讲:区间选点、最大不相交、区间分组与区间覆盖全解析

刷算法题这几年,最怕看到两类题:一类是动态规划,状态转移方程搞死人;另一类就是贪心,思路看着挺对,一提交就WA,看完题解才发现自己漏了个反例。不过贪心里面有个很固定的套路,就是“…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬