
簡介一套基于GMM-UBM模型的聲紋識別Python實現(xiàn)源碼及文檔說明面向語音信號處理、機器學習相關課程的高分大作業(yè)場景。資源基于TIMIT數(shù)據(jù)集完整覆蓋靜音消除、FBANK/MFCC/PNCC三種特征提取、UBM訓練、說話人模型訓練與評分測試等關鍵環(huán)節(jié)適合需要快速復現(xiàn)聲紋識別流程的學生和開發(fā)者參考。壓縮包共10個文件包含9個Python腳本和1個Markdown文檔腳本分別負責特征提取、模型訓練、測試集與注冊集生成、打分評估等功能文檔說明則給出清晰的數(shù)據(jù)準備和運行步驟便于對照源碼理解GMM-UBM的完整建模思路。已有224人學習/下載代碼體積雖小但模塊劃分明確可幫助讀者掌握聲紋識別原理并作為課程設計或畢業(yè)設計的可擴展框架也方便針對不同特征或數(shù)據(jù)集進行二次調(diào)參與改進。1. GMM-UBM聲紋識別這份Python課程項目把全鏈路跑通了聲紋識別在課程大作業(yè)里最常見的翻車方式是直接搬一個深度學習分類網(wǎng)絡訓完拿測試集一跑準確率還行就交差。這份基于GMM-UBM的Python實現(xiàn)走的是另一條路它不訓練神經(jīng)網(wǎng)絡而是用高斯混合模型對每個說話人的聲學特征分布建模用通用背景模型UBM做全局兜底再通過最大后驗概率自適應得到每個說話人自己的模型。項目把FBANK、MFCC、PNCC三種特征提取前端全部實現(xiàn)配套了靜音消除、數(shù)據(jù)列表生成、UBM訓練、說話人注冊、打分判決的完整腳本和文檔說明。適合正在做說話人識別課程設計、需要一條能逐步復現(xiàn)并到講臺答辯的完整路徑的同學也適合想把傳統(tǒng)聲紋識別“特征→模型→打分”全鏈路看清楚、不想把模型當黑匣子的技術從業(yè)者。2. 項目結(jié)構與時序十個腳本誰先誰后捋一遍才敢動手這個項目最大的特點是不藏代碼整套流水線被拆成十個左右的Python腳本每個腳本只做一件事順著文檔說明.md能一步步跑完。不像很多深度學習項目一個train.py跑到底中間發(fā)生了什么全靠猜。動手跑之前先把腳本之間的依賴關系捋清楚后面翻車概率能少一半。2.1 工程文件拆解與運行順序先把zip里的腳本挨個看一遍文件清單如下腳本文件職責在流水線中的位置yu_pos.py靜音消除可選數(shù)據(jù)準備最前段train_spk_model.py生成訓練列表訓練說話人模型列表階段建模階段train_test_scp.py生成或調(diào)整訓練/測試列表輔助工具val_enrollment_scp.py生成注冊列表與驗證列表列表階段feature_ex_Fbank.py提取FBANK特征特征階段feature_extract_MFCC.py提取MFCC特征特征階段feature_extract_pncc.py提取PNCC特征特征階段train_UBM.py訓練通用背景模型建模階段eval_score.py打分與判決評估階段按照項目文檔給出的流程完整執(zhí)行順序是這樣的。注意train_spk_model.py會出現(xiàn)兩次第一次負責生成列表第二次負責訓練說話人模型這是這類課程腳本常見的功能分支設計# 0. 可選對數(shù)據(jù)集做靜音消除VAD去掉每段音頻前后的靜音 python yu_pos.py # 1. 第一次運行 train_spk_model.py生成訓練UBM用的 ubm_wav.scp 和測試用的 test.scp python train_spk_model.py # 2. 生成注冊列表 enrollment.scp 與驗證列表 var.scp python val_enrollment_scp.py # 3. 分別提取三種特征這一步的輸出和前面生成的scp一一對應 python feature_ex_Fbank.py python feature_extract_MFCC.py python feature_extract_pncc.py # 4. 訓練UBM運行時用chooes()輸入 fbank / mfcc / pncc 選定特征類型 python train_UBM.py # 5. 第二次運行 train_spk_model.py用chooes()選定同一特征類型生成說話人模型 python train_spk_model.py這個順序本身就是傳統(tǒng)聲紋識別系統(tǒng)的標準流水線先有數(shù)據(jù)列表再提特征再訓UBM再自適應出說話人模型最后才是打分。我第一次拿到這類項目時會先按這個順序空跑一遍即使中途報錯也不急著改先看它停在哪一步再回頭翻文檔說明.md比從讀代碼開始快得多。跑的時候每一步都要回頭確認輸出存在scp文件是文本直接打開看特征文件通常是二進制矩陣看文件大小是否為0。這里有個習慣值得養(yǎng)成——把每個腳本的輸出路徑提前記下來比如fbank特征存在哪個目錄、mfcc特征又存在哪個目錄后面很多報錯都源于“我以為它生成了其實沒有”。2.2 靜音消除yu_pos.py為什么是可選步驟yu_pos.py在流水線最前面但文檔說它是可選的。原因在于TIMIT這類數(shù)據(jù)集本身在安靜錄音室環(huán)境下采集句首句尾的靜音段很短對GMM建模的影響往往可以忽略。但如果未來換自己錄的數(shù)據(jù)或者環(huán)境里有很多無語音片段靜音消除就很必要。常見做法是基于短時能量和過零率的雙門限VAD把音頻切成25毫秒左右的幀計算每幀的短時能量和過零率能量低于閾值且過零率接近靜音水平的幀標記為靜音再用前后幾幀做平滑避免把清音和弱音也切掉。這個項目的yu_pos.py就是這類思路的輕量實現(xiàn)# yu_pos.py 的核心邏輯課程項目的典型VAD實現(xiàn) def vad_frame(frame, energy_thresh, zcr_thresh): # 幀能量對幀內(nèi)采樣點求平方和取對數(shù)數(shù)值越大代表這一幀越“響” energy np.log(np.sum(frame ** 2) 1e-10) # 過零率采樣點符號發(fā)生變化的次數(shù)語音濁音段過零率低清音和靜音段高 zcr np.sum(np.abs(np.diff(np.sign(frame)))) / 2 # 雙門限能量和過零率同時超過設定范圍才判為靜音 if energy energy_thresh and zcr zcr_thresh: return True # 靜音幀 return False這段代碼里energy_thresh和zcr_thresh是經(jīng)驗參數(shù)。對16kHz采樣、16bit的音頻能量閾值一般在0到10之間手動調(diào)過零率閾值在10到20之間。調(diào)參的依據(jù)是看VAD輸出后保留的語音段能否完整覆蓋一句話里所有成音部分。閾值設太高韻母的弱收尾會被切成碎塊設太低靜音段殘留下來后面提取的特征里混入大量低能量幀UBM訓練的收斂速度會變慢。實際跑的時候我一般會先對一段音頻做可視化把能量曲線畫出來再定閾值而不是一上來就套默認值。2.3 scp列表制度訓練、注冊、驗證為什么必須分開scp是Speech Corpus格式的列表文件每一行對應一段音頻典型格式是“音頻路徑 標簽”標簽可以是說話人ID或句子ID。這個項目里train_spk_model.py生成ubm_wav.scp和test.scpval_enrollment_scp.py生成enrollment.scp和var.scp四份列表各有各的用途列表文件用途內(nèi)容來源ubm_wav.scp訓練UBM用覆蓋盡量多說話人的語音訓練集全體說話人test.scp測試/評估用包含身份未知的待識別語音測試集說話人enrollment.scp注冊用每個說話人一段或多段語音用來建模型驗證集說話人var.scp驗證用與注冊說話人一一比對打分與enrollment配對把注冊和驗證分開是實驗紀律里很重要的一條。如果同一個人的同一段錄音既用來注冊又用來驗證打分結(jié)果會虛高等錯誤率好看到不真實這在課程答辯時是最容易被問倒的問題之一。正確做法是enrollment.scp里每人取前幾段語音注冊模型var.scp里每人另取不同句子做驗證確保注冊語音和驗證語音沒有重疊。TIMIT數(shù)據(jù)集本身提供了訓練集與測試集的說話人劃分項目是按這個約定來生成列表的。如果你要換自己的數(shù)據(jù)集至少要自己保證訓練UBM的說話人、注冊的說話人和驗證的說話人互不重疊否則UBM吸收了目標說話人的分布后面MAP自適應就失去意義了。順帶一提t(yī)rain_test_scp.py在文件清單里是輔助工具通常用來做列表的拆分和重排比如把ubm_wav.scp按說話人隨機打亂或者從test.scp里抽一部分做調(diào)試。它不是主流程必需腳本但如果數(shù)據(jù)集很大想先拿一小部分驗證代碼能跑通用它比手動改列表方便。3. 三種特征提取的取舍MFCC、FBANK與PNCC特征提取是把音頻變模型輸入的環(huán)節(jié)。這個項目一次實現(xiàn)了三個前端不是堆功能而是三種特征分別適合不同條件。搞清楚它們各自做了什么、在什么場景下選哪個是復用這份資源時最值得花時間的一步。3.1 MFCC提取流程與參數(shù)含義MFCC是語音識別里最經(jīng)典的聲學特征。feature_extract_MFCC.py做的事情可以拆成四步預加重、分幀加窗、Mel濾波器組、對數(shù)與DCT。預加重用一階高通濾波器把高頻分量抬起來彌補語音發(fā)聲時高頻能量天然衰減的問題分幀加窗用25毫秒的漢明窗把連續(xù)信號切成幀幀間重疊50%保證特征序列在時間軸上是平滑過渡的。# feature_extract_MFCC.py 中MFCC提取的簡化流程 def extract_mfcc(wav, sr16000): # 預加重y[n] x[n] - 0.97 * x[n-1]提升高頻分量 pre_emph np.append(wav[0], wav[1:] - 0.97 * wav[:-1]) # 分幀加窗25ms幀長10ms幀移漢明窗 frame_len int(sr * 0.025) # 400個采樣點 frame_shift int(sr * 0.010) # 160個采樣點 frames framing(pre_emph, frame_len, frame_shift) frames * np.hamming(frame_len) # FFT后過Mel濾波器組取對數(shù) power_spec np.abs(np.fft.rfft(frames, n512)) ** 2 mel_energy np.dot(power_spec, mel_filterbank.T) log_energy np.log(mel_energy 1e-10) # DCT得到倒譜系數(shù)通常取前13維 mfcc scipy.fftpack.dct(log_energy, axis1)[:, :13] return mfcc這里幾個參數(shù)值得留意。預加重系數(shù)0.97是經(jīng)驗值接近1但不等同于1目的是在提升高頻的同時不引入低頻失真。幀長25毫秒、幀移10毫秒是語音特征提取的默認配置因為語音在20到30毫秒內(nèi)可近似看成平穩(wěn)信號。Mel濾波器組的個數(shù)在這個項目里一般設26或40MFCC維度取13維。如果做說話人識別13維通常偏少常見做法是拼接13維靜態(tài)系數(shù)加13維一階差分加13維二階差分湊成39維或者直接用FBANK的40維特征訓練GMM而不做DCT壓縮。這個項目后續(xù)訓練UBM時會讀取mfcc特征文件你要確認維度設置與train_UBM.py里配置的輸入維度一致不然EM迭代第一步就會因為維度對不上而報錯。3.2 FBANK與MFCC的差別少一步DCT多一分相關性FBANK特征其實是MFCC去掉最后一步DCT之后的中間產(chǎn)物。feature_ex_Fbank.py保存的就是經(jīng)過Mel濾波器組和對數(shù)運算之后的濾波器組能量沒有做倒譜變換。少了一步變換FBANK到底好在哪關鍵在DCT的作用是去相關和降維。MFCC通過DCT把高維的濾波器組能量壓縮成低維倒譜系數(shù)丟掉的是濾波器組輸出中彼此相關的部分。對傳統(tǒng)GMM來說特征維數(shù)低、各維相關性低協(xié)方差矩陣更接近對角陣訓練更穩(wěn)定。但FBANK保留了相鄰Mel頻帶之間的能量相關性配合GMM的協(xié)方差建模反而能捕捉到更多頻譜細節(jié)。在語音內(nèi)容識別任務里兩者差不多但在說話人識別里頻譜細節(jié)正是區(qū)分不同人發(fā)聲習慣的關鍵。這個項目同時提供fbank和mfcc兩條路線的特征提取正確用法是訓練UBM和訓練說話人模型時選擇同一個特征類型。很多人第一次跑會踩這個坑訓練UBM時選了mfcc訓練說話人模型時又選了fbank兩者特征維度都不同加載模型時要么報錯要么默默訓練出一個沒用的模型。實際體驗下來FBANK配合GMM-UBM的識別率通常會略高于MFCC特別是在訓練數(shù)據(jù)量充足的情況下。PNCC則主要在噪聲環(huán)境下優(yōu)勢明顯。三者的適用場景差異通常在干凈語音上MFCC和FBANK差距不大在有背景噪聲或信道失真的數(shù)據(jù)上PNCC的魯棒性會明顯體現(xiàn)出來。3.3 PNCC的魯棒性來源從頻譜增強到冪律壓縮PNCC的全稱是Power-Normalized Cepstral Coefficients設計目標就是對抗噪聲和混響。它和MFCC在結(jié)構上相似都是先做頻譜分析再經(jīng)過濾波器組和壓縮最后做DCT。差別在中間的處理環(huán)節(jié)。MFCC的經(jīng)典流程里FFT功率譜直接經(jīng)過Mel濾波器組然后取對數(shù)對數(shù)壓縮是固定底數(shù)的非線性變換。PNCC的做法要復雜幾層先用帶通濾波器和中值濾波估計每個頻帶的噪聲基底從功率譜中減去噪聲成分得到增強后的頻譜然后做冪律非線性壓縮通常取1/15的冪次這個冪律是從人耳聽覺的響度感知模型推出來的比對數(shù)壓縮更接近聽覺神經(jīng)對語音強度的映射再加一個時域掩蔽步驟用鄰近幀的能量做平滑抑制脈沖噪聲。最后再走DCT得到倒譜系數(shù)。# feature_extract_pncc.py 中PNCC的簡化處理鏈 def extract_pncc(wav, sr16000): # 1. 短時傅里葉變換得到功率譜 power_spec stft_power(wav, sr) # 2. 中值濾波估計噪聲基底并從功率譜中扣除 noise_floor median_filter(power_spec, kernel_size5) enhanced np.maximum(power_spec - noise_floor, 0) # 3. 冪律非線性壓縮gamma 1/15 compressed np.power(enhanced, 1.0 / 15.0) # 4. 時域掩蔽對時間軸做指數(shù)平滑抑制脈沖突變 masked temporal_mask(compressed, coeff0.5) # 5. 過Mel濾波器組、取對數(shù)、DCT return dct(np.log(np.dot(masked, mel_filterbank.T)))[:, :13]參數(shù)上最值得關注的是中值濾波的kernel_size和時域掩蔽的平滑系數(shù)。kernel_size取5意味著每個頻帶取相鄰5幀的中值作為噪聲估計窗口太大反應慢會誤把語音段能量當成噪聲估計進去窗口太小又擋不住持續(xù)性的背景噪聲。平滑系數(shù)0.5是保守默認值如果面對的噪聲是短促的開關聲、鍵盤聲可以把系數(shù)調(diào)到0.3以下讓掩蔽更敏感。PNCC在干凈語音上不一定比MFCC強但在有噪聲的測試條件下優(yōu)勢非常明顯。我拿到這個項目后實際體驗是同樣的GMM-UBM管線把特征從MFCC換成PNCC在加了環(huán)境噪聲的測試集上等錯誤率下降明顯。代價是提取速度慢不少中值濾波和時域掩蔽都是逐幀逐頻帶的操作特征提取耗時大約是MFCC的2到3倍。如果你的課程設計沒有噪聲魯棒性要求直接用FBANK性價比最高如果答辯題目里帶“魯棒說話人識別”這類字眼PNCC就是加分項。4. GMM-UBM訓練與打分從EM迭代到似然比判決特征提取完之后進入整個系統(tǒng)的核心訓練UBM、自適應說話人模型、打分判決。這三個環(huán)節(jié)分別對應train_UBM.py、train_spk_model.py和eval_score.py每一步都有值得展開的參數(shù)細節(jié)。4.1 UBM訓練train_UBM.py的EM迭代與分量數(shù)選擇UBM是通用背景模型本質(zhì)上是一個用大量非目標說話人的語音訓練出來的大GMM。它扮演的是“普通人聲音分布”的基準。train_UBM.py讀取ubm_wav.scp對應的特征用期望最大化算法迭代擬合GMM參數(shù)。GMM的數(shù)學形式是多個高斯分量按權重疊加每個說話人的聲學特征分布用M個高斯分量描述每個分量有自己的均值向量、協(xié)方差矩陣和權重。EM迭代的每一步分兩步E步計算每一幀特征落在每個高斯分量上的后驗概率M步用這些后驗概率作為軟標簽重新估計權重、均值和協(xié)方差。迭代到對數(shù)似然的變化量小于某個閾值才停。# train_UBM.py 中GMM-UBM訓練的EM思路 def gmm_em(features, n_components32, max_iter50): # 初始化用KMeans給特征聚類用聚類中心初始化各個高斯分量的均值 gmm initialize_with_kmeans(features, n_components) for i in range(max_iter): # E步計算每個樣本在每個高斯分量上的后驗概率責任度 responsibilities gmm.predict_proba(features) # M步用責任度加權重估權重、均值、協(xié)方差 gmm.update_parameters(features, responsibilities) # 判斷收斂對數(shù)似然變化小于1e-4就停止 if abs(gmm.log_likelihood_change()) 1e-4: break return gmm高斯分量數(shù)n_components是最關鍵參數(shù)常見做法是32、64或128。分量越多對訓練數(shù)據(jù)的擬合越精細但也更容易過擬合。UBM由于是大量說話人數(shù)據(jù)訓練出來的收斂性相對好可以用128個分量到了說話人模型自適應階段如果每個說話人的注冊語音只有幾十秒用64個分量就夠了。一個經(jīng)驗規(guī)則是每個高斯分量至少要有足夠幀數(shù)支撐均值估計注冊語音總幀數(shù)除以分量數(shù)不要小于幾十幀。如果注冊語音只有10秒左右對應特征大約是1000幀分量數(shù)超過64就有點危險。4.2 說話人模型自適應train_spk_model.py的MAP更新UBM訓練好以后下一步為每個說話人生成自己的模型。train_spk_model.py在這一階段做的是最大后驗自適應不是從零訓練一個新GMM而是把UBM的參數(shù)作為先驗用該說話人的少量語音把UBM的均值拉向說話人自己的特征分布。MAP的核心是自適應“力度”的控制。每個高斯分量的均值更新時并不完全等于該說話人語音在這個分量上的均值而是新舊信息的加權平均權重由該說話人語音落在該分量上的幀數(shù)和預先設定的自適應因子共同決定。幀數(shù)越多越信任新數(shù)據(jù)幀數(shù)越少越依賴UBM的先驗。# train_spk_model.py 中MAP自適應說話人模型的示意代碼 def map_adapt(ubm, spk_features, relevance_factor16.0): # 計算說話人特征在UBM各分量上的后驗概率 gamma ubm.predict_proba(spk_features) for c in range(ubm.n_components): n_c gamma[:, c].sum() # 落在分量c上的等效幀數(shù) data_mean (gamma[:, c].dot(spk_features)) / max(n_c, 1e-10) # 權衡系數(shù)alpha n_c / (n_c relevance_factor) alpha n_c / (n_c relevance_factor) ubm.mean_[c] alpha * data_mean (1 - alpha) * ubm.mean_[c] return ubmrelevance_factor是MAP自適應里最重要的參數(shù)常用范圍在8到32之間。它越大說話人模型越接近UBM個性化程度越低它越小說話人模型越依賴注冊語音對注冊語音的質(zhì)量也越敏感。如果注冊語音里有明顯的環(huán)境噪聲或誤切出來的靜音段relevance_factor又設得很小說話人模型會被這些噪聲幀帶偏后面打分時對同一個人其他干凈語音的得分反而很低。課程項目里一般不會主動讓你調(diào)這個參數(shù)但答辯老師很可能會問“自適應因子為什么這樣設”所以至少要能說出relevance_factor對模型偏置和方差的影響。4.3 打分與閾值eval_score.py的似然比判決最后一個關鍵環(huán)節(jié)是打分。eval_score.py加載說話人模型和UBM對每一段待驗證語音計算兩個對數(shù)似然一個是這段語音被該說話人模型生成的概率一個是它被UBM生成的概率。兩個對數(shù)似然的差就是對數(shù)似然比分數(shù)。分數(shù)越高說明這段語音越不像普通人的聲音、越像目標說話人。這個設計是GMM-UBM體系最巧妙的地方。如果不做差、只看說話人模型的絕對似然不同語音時長會讓分數(shù)標準不一致。而UBM作為“普通人基準”可以把說話人模型的分數(shù)歸一化到“相對普通人的偏離程度”上分數(shù)跨語音時長的可比性更強。打分時通常還會對幀得分做平均用平均對數(shù)似然比而不是累加值進一步消除時長影響。訓練完以后判斷“是不是本人”需要一個閾值。低于閾值判為冒認者高于閾值判為目標說話人。項目里通常的做法是把var.scp的驗證分數(shù)畫成分布圖取兩個分布的交叉點或計算等錯誤率閾值。等錯誤率的意思是誤接受率和誤拒絕率相等時的點在課程答辯里是最直觀的評估指標。如果文檔里沒有推薦閾值先用所有驗證分數(shù)的均值附近當作初始閾值再根據(jù)期望偏安全還是偏方便來調(diào)整。到這里整個GMM-UBM系統(tǒng)的工作鏈閉環(huán)特征提取把音頻變成特征矩陣訓練用EM訓UBM、用MAP得到說話人模型評估用似然比打分。5. 避坑與常見問題跑這個項目最常翻車的五個地方5.1 scp文件路徑寫錯或帶空格特征讀取階段直接失敗現(xiàn)象運行特征提取腳本時報文件找不到但文件明明就在scp里列出的路徑下。原因scp文件里寫的是絕對路徑但數(shù)據(jù)集換過目錄或者Windows系統(tǒng)上路徑分隔符是反斜杠腳本里卻用正斜杠去拼接。此外目錄名里有空格的話按行split會把路徑拆成多段。解決先檢查scp文件里每一行的格式確認路徑分隔符和實際環(huán)境一致。我一般會在提取特征前寫一個簡短的Python腳本遍歷scp里的每一條路徑用os.path.exists逐一驗證把不存在的路徑打印出來。路徑換成絕對路徑最省心別依賴腳本里默認的相對路徑拼接。5.2 靜音消除過猛特征矩陣變成空文件現(xiàn)象yu_pos.py跑完后某些音頻的幀數(shù)變成0對應的特征文件也是空的訓練UBM時加載進來直接報維度錯誤。原因雙門限VAD的閾值設得太高把清音段和弱音段全部判成靜音并切掉了。尤其是一些音量偏小的說話人整個句子都可能被誤判為靜音。解決用VAD之前先做一次音量歸一化把每段音頻的峰值幅度統(tǒng)一縮放到一個固定范圍再設定能量閾值。閾值不要卡死留出余量寧可多留一點靜音幀也不要切掉語音。跑完VAD后檢查被切掉的部分占原音頻的比例如果超過10%基本可以斷定閾值偏高。5.3 特征類型不統(tǒng)一訓練出的說話人模型是廢的現(xiàn)象訓練UBM時選fbank訓練說話人模型時也選了fbank但打分階段eval_score.py報特征維度不匹配。原因eval_score.py里加載的是mfcc特征文件而模型是fbank特征訓練的。這類錯誤出現(xiàn)在chooes()這種交互式選擇設計的項目里特別常見每次運行都要手動輸入?yún)?shù)一不留神就選岔了。還有一種更隱蔽的情況三個特征腳本保存的路徑或文件名不同打分腳本默認去讀mfcc的特征目錄導致模型和特征對不上。解決把特征類型當成全局配置管理而不是每次交互輸入。我拿到這類項目后的第一個改動就是把train_UBM.py和train_spk_model.py里的chooes()替換成讀取一個config變量比如feature_type mfcc并統(tǒng)一設置輸出路徑的前綴。訓練完看一眼生成的模型文件名帶的是哪種特征后綴再跑打分能省掉很多莫名其妙的報錯。5.4 訓練集測試集說話人有重疊識別分數(shù)虛高現(xiàn)象模型在var.scp上的分數(shù)高得不正常等錯誤率壓到百分之零點幾答辯時老師一追問就露餡。原因數(shù)據(jù)列表生成時沒有嚴格執(zhí)行說話人水平的劃分同一個人的語音既出現(xiàn)在ubm_wav.scp里用于訓練UBM又出現(xiàn)在enrollment.scp里注冊了模型還在var.scp里當了驗證語音。三重重疊等于開卷考試。解決重新生成列表確保UBM訓練集、注冊說話人集合、驗證說話人集合三者互不重疊。如果換了自己的數(shù)據(jù)集先按說話人ID分組把說話人整體劃分到集合里而不是按音頻文件隨機切。檢查方法很簡單寫一段腳本提取每個列表里的說話人ID用集合的intersection看一下有沒有交集。5.5 打分階段沒有做時長歸一化長音頻天然占便宜現(xiàn)象同一個人的短語音分數(shù)很低長語音分數(shù)高但閾值對長短語音都很公平實際卻一直在誤判。原因eval_score.py如果直接累加每幀對數(shù)似然比而不是用幀數(shù)做平均長音頻的分數(shù)天然更高。GMM的逐幀似然都是負值累加后數(shù)值會隨幀數(shù)線性增大直接把長短語音的分數(shù)拉到不同量級。解決改成累加后除以總幀數(shù)用平均對數(shù)似然比作為分數(shù)。如果不想改打分腳本也可以在測試時把音頻統(tǒng)一截斷到固定長度再打分。這個問題在課程項目里經(jīng)常被忽視因為大部分演示數(shù)據(jù)時長都差不多一旦自己錄幾段長短不一的語音來測試立刻就會暴露。6. 驗證與進階讓識別結(jié)果可解釋的三個習慣把系統(tǒng)跑通以后重要的不是急著調(diào)參而是先建立一套能解釋實驗結(jié)果的驗證習慣。我從這個項目里總結(jié)出三個習慣在答辯和后續(xù)換數(shù)據(jù)集時反復用上了。6.1 用等錯誤率描述性能而不是只報一個識別準確率GMM-UBM的輸出是分數(shù)所謂識別準確率依賴閾值選擇換個閾值就成了另一個數(shù)。而等錯誤率不依賴閾值它關注注冊說話人自身分數(shù)和冒認者分數(shù)兩個分布的交叉點是這套系統(tǒng)最穩(wěn)定的評價指標。每次調(diào)完特征或模型參數(shù)把這個數(shù)記下來比“準確率95%”更有說服力。6.2 給特征做倒譜均值減MFCC和FBANK在特征提取完后把每一維特征在整個句子上減掉均值再歸一化能有效去除信道和麥克風響應帶來的加性偏移。我一般把CMS作為說話人識別的默認預處理它幾乎不增加計算量但對跨設備錄音的識別效果改善明顯。6.3 固定隨機種子讓每次訓練可復現(xiàn)EM初始化和KMeans聚類都帶有隨機性不固定種子的話連續(xù)兩次訓練UBM得到的分量均值可能不同實驗結(jié)果無法復現(xiàn)。在train_UBM.py開頭加一行隨機種子固定是最容易做、也最容易加分的小改動。從那以后我每次跑這類項目都強制自己先做這三步固定隨機種子、確認注冊語音和驗證語音不重疊、用平均對數(shù)似然比打分。希望幫到你。本文還有配套的精品資源點擊獲取