現(xiàn)環(huán)境噪聲分類)
這段時(shí)間一直在折騰一個(gè)跟聲音相關(guān)的分類Demo需求很簡單給定一段音頻判斷它屬于哪一類噪聲。比如空調(diào)運(yùn)行的嗡嗡聲、雨聲、鍵盤敲擊聲、腳步聲、交通噪聲一共五類。第一反應(yīng)是上卷積網(wǎng)絡(luò)或者循環(huán)網(wǎng)絡(luò)但仔細(xì)想了下需求和計(jì)算資源最后用Keras搭了個(gè)多層感知機(jī)效果相當(dāng)能打。這篇先把整體思路、數(shù)據(jù)處理、模型搭建和訓(xùn)練過程完整記錄下來算作這個(gè)系列的第一篇后續(xù)再聊實(shí)時(shí)識(shí)別和工程化落地的內(nèi)容。1. 為什么用“MLPMFCC”的組合搞定噪聲分類1.1 噪聲分類到底是什么樣的任務(wù)先說清楚任務(wù)本質(zhì)。噪聲分類屬于一維信號(hào)分類問題輸入是一段時(shí)間的音頻波形輸出是類別標(biāo)簽。它和語音識(shí)別有個(gè)關(guān)鍵區(qū)別語音識(shí)別需要建模音素、音節(jié)這種強(qiáng)時(shí)序依賴而噪聲分類更看重聲音的“音色特征”。比如空調(diào)嗡嗡聲是穩(wěn)定低頻、鍵盤敲擊聲是短促高頻瞬態(tài)、雨聲是寬頻隨機(jī)噪聲不同類別在頻譜上的分布差異很大。從數(shù)據(jù)形態(tài)看一段2秒的音頻在16kHz采樣率下會(huì)變成32000個(gè)采樣點(diǎn)。如果直接把這樣的一維數(shù)組丟給全連接網(wǎng)絡(luò)輸入維度就是32000第一層參數(shù)量會(huì)爆炸而且原始波形對相位偏移、幅度縮放、背景疊加都極其敏感泛化能力會(huì)很差。所以通常的做法是先做特征提取把音頻壓縮成一小段有物理意義的特征向量再交給分類模型。這也是“MLPMFCC”組合成立的前提。1.2 特征向量與MLP的天然配合MFCC是Mel頻率倒譜系數(shù)模擬人耳對不同頻率聲音的非線性感知特性。它的核心思路是先把音頻按幀做傅里葉變換得到頻譜再把頻譜映射到Mel刻度上最后取對數(shù)并做離散余弦變換得到一組低維系數(shù)。每一幀音頻可以算出一組MFCC整段音頻可以取時(shí)間維度的統(tǒng)計(jì)量比如均值得到一個(gè)固定長度的特征向量。MLP的全稱是多層感知機(jī)本質(zhì)上就是若干層全連接網(wǎng)絡(luò)堆疊。它的建模能力在于對輸入特征做非線性組合和抽象。MFCC已經(jīng)把音頻從“32000個(gè)采樣點(diǎn)”壓縮成“39個(gè)系數(shù)”這種低維緊湊表示MLP在這個(gè)表示上做分類既不需要像CNN那樣構(gòu)造二維譜圖也不需要像LSTM那樣考慮時(shí)序傳播整個(gè)鏈路非常簡潔。實(shí)測下來干凈環(huán)境下5類噪聲用13維MFCC加delta特征就能達(dá)到95%左右的分類準(zhǔn)確率直接用原始波形做同樣的分類任務(wù)MLP很難穩(wěn)定超過85%而且訓(xùn)練慢得多。1.3 什么時(shí)候MLP會(huì)不夠用不說MLP萬能也得說清楚它的邊界否則后續(xù)工程化的時(shí)候容易踩坑。MLP的問題在于它沒有顯式建模時(shí)序結(jié)構(gòu)。MFCC均值這種“全局特征”適合區(qū)分音色差異明顯的噪聲但如果兩類噪聲有大量重疊頻譜或者環(huán)境里混響很強(qiáng)、信噪比很低全局統(tǒng)計(jì)信息被污染MLP的準(zhǔn)確率就會(huì)明顯下降。這時(shí)就要考慮兩類升級方案一類是用CNN處理短時(shí)頻譜圖利用局部頻率模式另一類是換RNN、Transformer這類序列模型建模幀級別的動(dòng)態(tài)變化。所以選擇MLP不是因?yàn)樗顝?qiáng)而是因?yàn)樵谶@個(gè)任務(wù)階段它足夠簡單、足夠快、足夠穩(wěn)。2. 數(shù)據(jù)準(zhǔn)備從原始音頻到可訓(xùn)練的特征矩陣2.1 音頻切割與目錄組織數(shù)據(jù)是一切的前提。我這里每個(gè)類別收集了大約200條原始音頻樣本來源有自己錄制的也有網(wǎng)上找的公開環(huán)境聲音素材。原始錄音長短不一有的十幾秒有的一分鐘。直接整段用不是不行但有兩個(gè)問題一是樣本量太少二是模型會(huì)學(xué)到“時(shí)長”這個(gè)無關(guān)特征。正確的做法是把長音頻切成固定長度的小片段。我的做法是統(tǒng)一切成2秒一段。為什么是2秒太短比如0.5秒可能裝不下一次完整的敲擊或腳步聲信息不足太長比如5秒又會(huì)讓同一個(gè)音頻切片內(nèi)部混入多種狀態(tài)平均特征被稀釋。2秒對大部分環(huán)境噪聲來說能覆蓋一到兩個(gè)完整發(fā)聲周期比較平衡。切割用librosa的load加載后按幀索引截取就行。目錄組織方面我采用按類別建文件夾的方式data/ air_conditioner/ 001.wav 002.wav rain/ 001.wav keyboard/ 001.wav footsteps/ 001.wav traffic/ 001.wav這樣好處是后面寫特征提取腳本時(shí)只需要遍歷根目錄、讀取子文件夾名作為標(biāo)簽代碼非常干凈。另外注意統(tǒng)一采樣率我統(tǒng)一轉(zhuǎn)成16kHz單聲道。很多人忽略這一步不同來源的音頻采樣率不一樣提取出的MFCC維度本身不受影響但頻率軸的物理含義就錯(cuò)亂了模型訓(xùn)練時(shí)容易學(xué)出一些說不清楚的偽規(guī)律。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秒截?cái)?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, 時(shí)間幀數(shù))的二維數(shù)組。hop_length128意味著每幀間隔128個(gè)采樣點(diǎn)2秒音頻大概有250幀左右。如果直接把整個(gè)二維矩陣?yán)教卣骶S度是13 * 250 3250MLP也能處理但沒必要而且不同長度的音頻幀數(shù)不一樣沒法統(tǒng)一。我的做法是取時(shí)間維的均值得到13維的全局特征mfcc.mean(axis1)。但只用靜態(tài)MFCC丟失了動(dòng)態(tài)信息。語音和噪聲分類里一個(gè)很常用的改進(jìn)是加delta和delta-delta也就是計(jì)算相鄰幀之間的變化率反映特征的時(shí)序趨勢。用librosa.feature.delta可以很方便地實(shí)現(xiàn)。最終我每個(gè)樣本用39維特征13維靜態(tài)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實(shí)際對比過只用靜態(tài)13維時(shí)驗(yàn)證集準(zhǔn)確率在92%左右加上delta之后提升到95%以上。代價(jià)是特征維度從13變成39但對MLP來說增加的計(jì)算量微乎其微完全值得。2.3 歸一化、數(shù)據(jù)集劃分與一個(gè)隱蔽的數(shù)據(jù)泄漏特征矩陣準(zhǔn)備好以后歸一化是必須的一步。MFCC系數(shù)的絕對值范圍差異很大低階系數(shù)和高階系數(shù)的數(shù)值尺度可能差一個(gè)數(shù)量級不歸一化會(huì)讓梯度更新被數(shù)值大的維度主導(dǎo)收斂很不穩(wěn)定。我用StandardScaler把每個(gè)特征維度變成均值0、方差1。這里有個(gè)新手特別容易踩的坑歸一化只能基于訓(xùn)練集擬合參數(shù)然后拿著訓(xùn)練集擬合的均值方差去變換驗(yàn)證集和測試集。如果先在整個(gè)數(shù)據(jù)集上fit再切分驗(yàn)證集的統(tǒng)計(jì)信息已經(jīng)混進(jìn)了歸一化參數(shù)等于模型在訓(xùn)練時(shí)“偷看”了驗(yàn)證集的信息最終評估分?jǐn)?shù)會(huì)虛高。這種數(shù)據(jù)泄漏非常隱蔽因?yàn)樵跍?zhǔn)確率上看不出來異常只有部署到真實(shí)數(shù)據(jù)上才會(huì)原形畢露。數(shù)據(jù)切分我用train_test_split并且設(shè)置stratifyy保證每個(gè)類別的樣本比例在訓(xùn)練集和驗(yàn)證集里保持一致。順序是先做切分再做歸一化。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。如果你的數(shù)據(jù)量特別少可以考慮7:3或者做K折交叉驗(yàn)證但注意交叉驗(yàn)證里歸一化也要在每一折的訓(xùn)練折上重新擬合邏輯跟上面是一樣的。標(biāo)簽處理方面我把類別名轉(zhuǎn)成整數(shù)索引再用keras.utils.to_categorical轉(zhuǎn)成one-hot向量。這樣配合categorical_crossentropy使用是新手最容易理解的方式。如果你不想轉(zhuǎn)one-hot也可以直接用整數(shù)標(biāo)簽配合sparse_categorical_crossentropy效果一樣。3. 用Keras搭建多層感知機(jī)結(jié)構(gòu)設(shè)計(jì)與逐層解讀3.1 網(wǎng)絡(luò)層數(shù)與神經(jīng)元數(shù)量的設(shè)計(jì)邏輯MLP的結(jié)構(gòu)設(shè)計(jì)沒有絕對標(biāo)準(zhǔn)但有一些實(shí)用的經(jīng)驗(yàn)法則。我這次用的是四個(gè)隱藏層256、128、64、32結(jié)構(gòu)上是一個(gè)逐漸收窄的“漏斗”。為什么這么設(shè)計(jì)輸入39維特征后第一層256個(gè)神經(jīng)元先把特征映射到更高維空間相當(dāng)于給模型足夠多的組合空間去捕捉特征交叉關(guān)系后面層數(shù)逐層遞減強(qiáng)迫網(wǎng)絡(luò)把學(xué)到的信息壓縮成越來越抽象、越來越有判別性的表示最后交到輸出層做分類。神經(jīng)元數(shù)量也不是越多越好。曾有測試把第一層加到1024個(gè)神經(jīng)元驗(yàn)證集準(zhǔn)確率并沒有明顯提升訓(xùn)練時(shí)間倒是翻了幾倍。原因很簡單輸入特征是39維的強(qiáng)結(jié)構(gòu)化特征不是ImageNet那種百萬像素級數(shù)據(jù)過大的隱層只會(huì)帶來冗余參數(shù)更容易過擬合。現(xiàn)在的規(guī)模大概有5萬多個(gè)參數(shù)用CPU訓(xùn)練幾十秒就能跑完一個(gè)epoch整個(gè)訓(xùn)練過程不會(huì)超過兩分鐘。模型定義用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) ])我多放了一個(gè)32維的隱藏層而不是直接從64跳到5。雖然理論上最后一層隱藏層不需要太多神經(jīng)元也能完成分類但多一層低維表示可以讓網(wǎng)絡(luò)在輸出前再做一次非線性整合對區(qū)分容易混淆的類別有幫助。3.2 從ReLU到Softmax激活函數(shù)選型隱藏層激活函數(shù)統(tǒng)一用ReLU輸出層用Softmax這個(gè)組合是當(dāng)前MLP的默認(rèn)方案。ReLU的優(yōu)勢在于計(jì)算簡單、收斂快而且對正區(qū)間梯度恒為1能有效緩解深層網(wǎng)絡(luò)里的梯度消失問題。可能有人會(huì)想用sigmoid或tanh但這兩個(gè)函數(shù)在輸入絕對值較大時(shí)梯度會(huì)趨近于0網(wǎng)絡(luò)一深就訓(xùn)不動(dòng)。噪聲分類這個(gè)任務(wù)里沒有“需要壓縮到-1到1”的特殊理由所以ReLU是第一選擇。Softmax做的事情是把最后一層輸出變成總和為1的概率分布。比如輸出[0.1, 0.3, 0.5, 0.05, 0.05]意思是模型認(rèn)為這段音頻屬于第三個(gè)類別的概率是50%。分類時(shí)取argmax挑最大概率對應(yīng)的類別就行。配合的損失函數(shù)是categorical_crossentropy它衡量模型輸出的概率分布與真實(shí)one-hot標(biāo)簽之間的差異這也是多分類問題的標(biāo)準(zhǔn)配置。3.3 防止過擬合的招數(shù)Dropout、EarlyStopping、學(xué)習(xí)率調(diào)度單靠網(wǎng)絡(luò)結(jié)構(gòu)還不夠訓(xùn)練過程中必須配合正則化手段。我在每個(gè)隱藏層后面都加了Dropout(0.3)也就是每次訓(xùn)練迭代隨機(jī)丟棄30%的神經(jīng)元。它的作用類似一種廉價(jià)的集成學(xué)習(xí)每次丟棄不同的神經(jīng)元組合等于讓網(wǎng)絡(luò)在訓(xùn)練過程中經(jīng)歷了多個(gè)子網(wǎng)絡(luò)的聯(lián)合預(yù)測最終學(xué)到的特征更加魯棒不會(huì)過度依賴某一個(gè)特征通道。編譯階段我指定優(yōu)化器為Adam初始學(xué)習(xí)率設(shè)為0.001監(jiān)控指標(biāo)用準(zhǔn)確率model.compile( optimizeradam, losscategorical_crossentropy, metrics[accuracy] )訓(xùn)練時(shí)最容易犯的錯(cuò)誤是把epoch設(shè)個(gè)大數(shù)然后傻等結(jié)束。正確做法是用回調(diào)配合EarlyStopping。我設(shè)置patience15意思是驗(yàn)證集損失連續(xù)15個(gè)epoch沒有改善就提前停止訓(xùn)練自動(dòng)返回歷史最優(yōu)模型。另外還加了ReduceLROnPlateau驗(yàn)證集損失停滯時(shí)把學(xué)習(xí)率減半讓模型在最優(yōu)解附近做更精細(xì)的搜索。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這個(gè)參數(shù)值得單獨(dú)說一下。如果不開啟EarlyStopping觸發(fā)時(shí)返回的是最后一個(gè)epoch的權(quán)重而不是驗(yàn)證集損失最低的那個(gè)epoch模型效果可能已經(jīng)退化。這個(gè)坑我一開始也踩過后來檢查發(fā)現(xiàn)返回的模型比最優(yōu)狀態(tài)低了兩個(gè)百分點(diǎn)虧大了。4. 訓(xùn)練實(shí)測曲線判讀與常見問題排查4.1 訓(xùn)練過程中的實(shí)時(shí)監(jiān)控fit執(zhí)行時(shí)控制臺(tái)會(huì)打印每個(gè)epoch的訓(xùn)練損失和驗(yàn)證損失、準(zhǔn)確率。我關(guān)心的是兩個(gè)趨勢訓(xùn)練集準(zhǔn)確率是否持續(xù)上升驗(yàn)證集準(zhǔn)確率是否也在合理爬升。正常情況是訓(xùn)練集準(zhǔn)確率前期上升很快驗(yàn)證集準(zhǔn)確率稍微滯后一點(diǎn)驗(yàn)證集損失可能會(huì)有小幅度抖動(dòng)但只要整體趨勢向下就是健康的。我這次訓(xùn)練的過程大致是這樣前10個(gè)epoch訓(xùn)練準(zhǔn)確率快速?zèng)_到90%以上驗(yàn)證準(zhǔn)確率在85%左右到第25個(gè)epoch驗(yàn)證準(zhǔn)確率站上93%第50個(gè)epoch以后曲線變平緩驗(yàn)證準(zhǔn)確率在94%到96%之間波動(dòng)。EarlyStopping在62個(gè)epoch觸發(fā)總共耗時(shí)不到兩分鐘。訓(xùn)練結(jié)束后打印驗(yàn)證集準(zhǔn)確率在95.3%左右。如果你看到訓(xùn)練準(zhǔn)確率一路上漲但驗(yàn)證準(zhǔn)確率怎么都不動(dòng)那基本就是過擬合的信號(hào)下一節(jié)詳細(xì)展開。4.2 過擬合的識(shí)別與應(yīng)對第一次搭這個(gè)模型的時(shí)候我沒有加任何Dropout隱藏層設(shè)為512和256訓(xùn)練結(jié)果非常典型訓(xùn)練集準(zhǔn)確率無限逼近100%驗(yàn)證集準(zhǔn)確率卡在88%左右。這個(gè)現(xiàn)象就是模型“背”下了訓(xùn)練數(shù)據(jù)中的細(xì)節(jié)和噪聲卻無法泛化到新樣本。應(yīng)對過擬合有幾板斧按優(yōu)先級排序加Dropout、減模型容量、加正則化系數(shù)、擴(kuò)充數(shù)據(jù)。我實(shí)際體驗(yàn)下來加Dropout是最立竿見影的。從零Dropout加0.3的Dropout之后驗(yàn)證準(zhǔn)確率提升了大概5個(gè)百分點(diǎn)。減隱藏層寬度也有幫助但不如Dropout效果明顯。還有一個(gè)思路是給Dense層加kernel_regularizerl2(0.001)通過限制權(quán)重范數(shù)防止某些特征權(quán)重過大不過我在這個(gè)任務(wù)里用Dropout已經(jīng)夠了L2正則對最終結(jié)果沒有額外提升。4.3 類別樣本不均衡的坑我收集的音頻數(shù)量大致均衡每個(gè)類別兩百多條所以沒做特殊處理。但如果你在擴(kuò)展類別時(shí)發(fā)現(xiàn)某個(gè)類別音頻特別少訓(xùn)練時(shí)模型的預(yù)測會(huì)偏向樣本多的類別。這種現(xiàn)象在混淆矩陣上表現(xiàn)得很明顯少數(shù)類別的召回率偏低多數(shù)類別的精確率也受影響。處理辦法有三種一是盡量多收集少數(shù)類樣本二是對少數(shù)類樣本做數(shù)據(jù)增強(qiáng)比如時(shí)間偏移、加白噪聲、調(diào)整音量、音調(diào)變化三是在訓(xùn)練時(shí)設(shè)置類別權(quán)重讓少數(shù)類的損失貢獻(xiàn)更大class_weight { 0: 1.0, 1: 2.0, # 少數(shù)類權(quán)重更大 2: 1.0, ... }我自己的經(jīng)驗(yàn)是數(shù)據(jù)增強(qiáng)通常比調(diào)權(quán)重更有效因?yàn)闄?quán)重只是改變了梯度比例沒有增加新的信息量。但增強(qiáng)要控制尺度加噪聲太猛會(huì)把原本的類別特性掩蓋掉。5. 識(shí)別環(huán)節(jié)從模型文件到單條音頻預(yù)測5.1 保存與加載模型訓(xùn)練完成后要做的第一件事就是把模型和預(yù)處理器都存下來。模型用Keras自帶的方式保存model.save(noise_mlp.keras)加載的時(shí)候用keras.models.load_model(noise_mlp.keras)就行。這里強(qiáng)調(diào)一下新版Keras官方推薦用的是.keras格式而不是老的.h5。.keras格式會(huì)把模型結(jié)構(gòu)和權(quán)重打包在一起兼容性和后續(xù)維護(hù)體驗(yàn)都更好。容易忽略的是StandardScaler也得單獨(dú)存。我一般用pickleimport pickle with open(scaler.pkl, wb) as f: pickle.dump(scaler, f)為什么預(yù)處理器跟模型一樣重要因?yàn)轭A(yù)測階段的輸入必須經(jīng)過跟訓(xùn)練階段完全一致的變換。如果你加載模型時(shí)忘了scaler直接把原始特征丟給模型數(shù)值尺度對不上預(yù)測概率會(huì)完全亂套。這個(gè)問題在模型部署時(shí)極常見我建議從一開始就把“模型scaler”綁定成一個(gè)整體最好封裝到一個(gè)類里。5.2 單條音頻預(yù)測的實(shí)現(xiàn)細(xì)節(jié)預(yù)測階段的核心原則是對新的音頻做一遍跟訓(xùn)練時(shí)一模一樣的處理鏈路。我把預(yù)測封裝成下面的函數(shù)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這里面有兩個(gè)細(xì)節(jié)很容易翻車。第一個(gè)是reshape(1, -1)模型訓(xùn)練時(shí)的輸入形狀是(樣本數(shù), 特征維度)即使只預(yù)測一條也要保留batch維度否則會(huì)報(bào)維度不匹配的錯(cuò)誤。第二個(gè)細(xì)節(jié)是預(yù)測時(shí)不要再對模型做dropoutKeras的推理模式會(huì)自動(dòng)關(guān)閉dropout所以不用額外處理。說一下我對模型行為的觀察在干凈錄音上模型對空調(diào)聲和交通聲的判斷幾乎是100%穩(wěn)定對雨聲和鍵盤聲偶爾會(huì)出錯(cuò)。鍵盤聲和腳步聲在時(shí)域波形上形態(tài)不同但在平均MFCC特征上可能靠近。解決思路是把“取平均”替換成“取分位數(shù)特征”或者“按幀分段建?!边@個(gè)等后面做提升時(shí)細(xì)聊。5.3 混淆矩陣與真實(shí)表現(xiàn)分析光看準(zhǔn)確率不夠我打印了分類報(bào)告和混淆矩陣用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))從分類報(bào)告里能看出不少東西。整體準(zhǔn)確率95%其中“空調(diào)聲”和“交通聲”的precision和recall都很高說明這兩種噪聲的頻譜特征非常穩(wěn)定“雨聲”和“腳步聲明”偶爾會(huì)互相混看混淆矩陣會(huì)發(fā)現(xiàn)大概有6%的雨聲樣本被分到了腳步聲類。原因并不難理解腳步聲在有些地面材質(zhì)上會(huì)產(chǎn)生類似雨聲的寬頻瞬態(tài)成分而我又用的是全局平均特征把時(shí)間結(jié)構(gòu)信息丟掉了不少。這個(gè)問題想徹底解決要么換成CNN處理幀級特征要么在特征里加入MFCC的方差、頻譜質(zhì)心、過零率等補(bǔ)充信息。我目前是傾向在現(xiàn)有MLP框架里先豐富特征因?yàn)楦膭?dòng)成本最低。最后說幾句這段時(shí)間的個(gè)人體會(huì)。用MLP做噪聲分類最大的優(yōu)點(diǎn)是鏈路短、排錯(cuò)容易從音頻加載到模型預(yù)測涉及的環(huán)節(jié)比CNN方案少很多特別適合驗(yàn)證“這個(gè)分類任務(wù)到底能不能做”的階段。但它的上限也確實(shí)受限尤其在低信噪比或強(qiáng)混響環(huán)境里會(huì)明顯吃力。這個(gè)Demo到目前還只是一個(gè)離線分類器我下一步打算把它接到麥克風(fēng)實(shí)時(shí)流上做滑動(dòng)窗口式的連續(xù)識(shí)別同時(shí)把每類噪聲的觸發(fā)時(shí)間戳記錄下來。到時(shí)候面臨的問題會(huì)更偏向工程側(cè)延遲控制、窗口重疊率、類別平滑策略這些等我做出來再繼續(xù)整理。