戰(zhàn):從序列對(duì)齊到孤立詞識(shí)別)
簡介動(dòng)態(tài)時(shí)間規(guī)整DTW算法是一種經(jīng)典的序列匹配方法專門用于處理長度不同但形態(tài)相似的時(shí)序數(shù)據(jù)它通過構(gòu)造二維代價(jià)矩陣?yán)脛?dòng)態(tài)規(guī)劃搜索最小失真路徑使序列可在時(shí)間軸上伸縮對(duì)齊從而解決了傳統(tǒng)歐氏距離無法應(yīng)對(duì)時(shí)間錯(cuò)位的問題在語音識(shí)別、生物信息學(xué)、手勢(shì)識(shí)別和金融數(shù)據(jù)分析等領(lǐng)域應(yīng)用廣泛。這份基于 MATLAB 的源碼包正面向上述領(lǐng)域的研究者、算法學(xué)習(xí)者及語音識(shí)別開發(fā)人員可在 MATLAB 6.5 環(huán)境下直接運(yùn)行壓縮包共含五個(gè)文件其中四個(gè)為 m 腳本、一個(gè)為 txt 說明整體僅 5KB結(jié)構(gòu)緊湊。核心腳本完整展示了距離矩陣計(jì)算與最優(yōu)路徑回溯另附算法變體、語音活動(dòng)檢測(cè)及測(cè)試模塊便于對(duì)比學(xué)習(xí)、二次開發(fā)和結(jié)果驗(yàn)證通過閱讀這些代碼讀者能深入理解動(dòng)態(tài)規(guī)劃在序列匹配中的應(yīng)用細(xì)節(jié)包括累積距離定義、邊界條件設(shè)置、路徑搜索策略等并可直接遷移到自己的項(xiàng)目中。該資源已有 286 人瀏覽學(xué)習(xí)是一份簡潔、實(shí)用、適合上手的學(xué)習(xí)資料。1. 一個(gè) dtw.zip 把時(shí)間軸壓平DTW 算法在 MATLAB 里搭配 VAD 解決什么解壓開這類壓縮包你大概率會(huì)看到一段規(guī)整的 DTW 主函數(shù)和一個(gè)能跑的 demo但真正讓從業(yè)者翻車的從來不是那三行遞推公式而是喂進(jìn)去的序列里混著靜音和噪聲。動(dòng)態(tài)時(shí)間規(guī)整DTW的作用是讓兩條長度不同、走勢(shì)相似的序列在時(shí)間軸上對(duì)齊它默認(rèn)用戶給的起點(diǎn)和終點(diǎn)都是有效內(nèi)容可錄音天然帶靜音段兩邊各拖一段空白進(jìn)去累積距離會(huì)被靜音稀釋最優(yōu)路徑也被拉偏。因此做語音匹配的人會(huì)在 DTW 前加一級(jí) VAD語音活動(dòng)檢測(cè)先用短時(shí)能量和過零率把有效語音段切干凈再拿切好的片段做模板比對(duì)。這篇筆記面向用 MATLAB 做孤立詞識(shí)別、課程設(shè)計(jì)和傳感器序列對(duì)齊的從業(yè)者把 dtw.zip 背后那條常見技術(shù)鏈講透DTW 怎么手寫、VAD 怎么接、參數(shù)怎么設(shè)、哪些坑會(huì)讓你白跑一夜。2. DTW 遞推公式變成可運(yùn)行的 dtw_custom窗口約束、回溯與距離度量2.1 為什么不能用歐氏距離直接算相似度很多人拿到 dtw.zip 第一反應(yīng)是讀兩個(gè)語音段然后逐點(diǎn)相減結(jié)果發(fā)現(xiàn)數(shù)值完全不可用。原因很直接兩個(gè)序列長度經(jīng)常不一樣同一個(gè)人說同一個(gè)詞快慢可以差出百分之三四十的時(shí)長傳感器采集的序列更是隨工況變長變短。歐氏距離要求兩個(gè)向量等長強(qiáng)行用插值或裁剪拉齊會(huì)把時(shí)間結(jié)構(gòu)壓變形——中間的停頓被拉伸元音段被壓縮關(guān)鍵爆破音可能被切掉能對(duì)上才怪。DTW 的思路是承認(rèn)時(shí)間軸可以伸縮逐幀尋找最合適的對(duì)齊關(guān)系讓形態(tài)相似的幀互相配對(duì)再累計(jì)配對(duì)代價(jià)。通俗地說DTW 回答的是“把 A 這個(gè)彈簧拉一拉、扭一扭變成 B 要花多大力氣”而不是“第 1 幀就必須對(duì)第 1 幀”。這個(gè)直覺立住以后看遞推公式和回溯代碼才不會(huì)被矩陣方向繞暈。2.2 手寫一個(gè)支持窗口約束的 dtw_custom 函數(shù)MATLAB 自帶 Signal Processing Toolbox 里確實(shí)有dtw函數(shù)但它處理的是單變量序列且默認(rèn)做了標(biāo)準(zhǔn)化度量口徑和作業(yè)包里自定義實(shí)現(xiàn)經(jīng)常對(duì)不上。所以建議直接用自定義版本完整代碼如下。function [cost, path_i, path_j] dtw_custom(a, b, w) % 手動(dòng)實(shí)現(xiàn)動(dòng)態(tài)時(shí)間規(guī)整DTW % 輸入 % a, b : 特征序列維度為 D×N每一列是一幀的特征向量 % w : 窗口約束帶寬單位是幀Inf 表示不限制 % 輸出 % cost : 終點(diǎn)累積距離未歸一化 % path_i, path_j : 對(duì)齊路徑下標(biāo) na size(a, 2); nb size(b, 2); % 距離矩陣 D 用來存累積距離inf 表示該格子尚未打開 D inf(na, nb); % 起點(diǎn)和邊界第一行、第一列只能沿著一個(gè)方向累積 D(1,1) norm(a(:,1) - b(:,1)); for i 2:na D(i,1) D(i-1,1) norm(a(:,i) - b(:,1)); end for j 2:nb D(1,j) D(1,j-1) norm(a(:,1) - b(:,j)); end % 主循環(huán)只允許從左邊、上邊、左上角三個(gè)方向轉(zhuǎn)移 for i 2:na j_lo max(2, i - w); j_hi min(nb, i w); if j_lo j_hi continue; end for j j_lo:j_hi d_ij norm(a(:,i) - b(:,j)); D(i,j) d_ij min([D(i-1,j), D(i,j-1), D(i-1,j-1)]); end end cost D(na, nb); if isinf(cost) warning(窗口約束把路徑卡死了請(qǐng)調(diào)大 w); end % 回溯路徑從終點(diǎn)反向找最小值來源 path_i na; path_j nb; while path_i(end) 1 || path_j(end) 1 i path_i(end); j path_j(end); cand [inf, inf, inf]; if i 1 cand(1) D(i-1,j); end if j 1 cand(2) D(i,j-1); end if i 1 j 1 cand(3) D(i-1,j-1); end [~, dir] min(cand); switch dir case 1 path_i(end1) i-1; path_j(end1) j; case 2 path_i(end1) i; path_j(end1) j-1; case 3 path_i(end1) i-1; path_j(end1) j-1; end end path_i fliplr(path_i); path_j fliplr(path_j); end這段代碼把 DTW 拆成三步。第一步初始化距離矩陣第一行第一列只能橫著或豎著累積因?yàn)槁窂讲辉试S回頭邊界的來源方向是唯一的。第二步填累積距離表min([D(i-1,j), D(i,j-1), D(i-1,j-1)])就是核心遞推三種轉(zhuǎn)移分別對(duì)應(yīng)“B 序列停頓”“A 序列停頓”“兩幀同時(shí)前進(jìn)”累積距離代表走到當(dāng)前格子的最小代價(jià)。第三步回溯路徑從終點(diǎn)反推每個(gè)格子來自哪個(gè)方向最后fliplr把路徑恢復(fù)成從起點(diǎn)開始的順序。窗口約束體現(xiàn)在j_lo max(2, i - w)和j_hi min(nb, i w)上它限制了路徑只能在 |i-j|≤w 的帶狀區(qū)域里偏移也就是 Sakoe-Chiba band 的常見實(shí)現(xiàn)。參數(shù)w的單位是幀不是秒對(duì) 25ms 幀長的語音窗口取較短序列長度的 10%~15%大約對(duì)應(yīng) 5~8 幀的時(shí)移余量。設(shè)成 Inf 可以得到無約束結(jié)果但計(jì)算量從 O(n·w) 漲回 O(m·n)序列長時(shí)明顯變慢?;厮萋窂娇梢院途嚯x矩陣一起畫成熱力圖路徑大面積橫走或豎走說明序列兩端混進(jìn)了靜音幀VAD 沒切干凈。2.3 距離度量與特征歸一化歐氏距離不是唯一選擇dtw_custom里用的是norm(a(:,i) - b(:,j))這是標(biāo)準(zhǔn) L2 距離。但有一個(gè)前提特征各維的量綱不能差太遠(yuǎn)。比如 MFCC 的第 1 維是能量相關(guān)系數(shù)數(shù)值可能比高階倒譜系數(shù)大一個(gè)數(shù)量級(jí)直接算歐氏距離等于只比音量不比發(fā)音內(nèi)容。常見做法是先進(jìn) Z-score 標(biāo)準(zhǔn)化再走 L2。% 按特征維度標(biāo)準(zhǔn)化消除量綱差異 feat zscore(feat, 0, 2);這里feat是 D×N 結(jié)構(gòu)zscore(feat, 0, 2)表示沿維度維第 1 維計(jì)算均值和標(biāo)準(zhǔn)差。標(biāo)準(zhǔn)化后再喂給 DTW距離表達(dá)的是“相對(duì)形態(tài)差異”而不是絕對(duì)幅值差異。如果任務(wù)換成說話人確認(rèn)可以用余弦距離替代 L2把d_ij改成1 - dot(a(:,i), b(:,j)) / (norm(a(:,i)) * norm(b(:,j)))。我個(gè)人的經(jīng)驗(yàn)是孤立詞識(shí)別用 L2 更穩(wěn)因?yàn)榘l(fā)音內(nèi)容差異會(huì)體現(xiàn)在距離幅度上說話人確認(rèn)用余弦更穩(wěn)因?yàn)楦P(guān)注特征方向而非能量大小。壓縮包代碼里一般寫死 L2跑通后想換度量只改d_ij這一處即可。3. 把 dtw.zip 跑成最小可用鏈路VAD 切段、幀號(hào)換算與調(diào)用腳本3.1 先理清文件布局主函數(shù)、工具函數(shù)和 demo 腳本網(wǎng)上流傳的 dtw.zip 大多是“半成品作業(yè)包”DTW 核心函數(shù)寫得規(guī)整但 demo 腳本留著上傳者電腦里的絕對(duì)路徑比如addpath(E:\MATLAB\dtw)換一臺(tái)機(jī)器直接運(yùn)行必報(bào)錯(cuò)。解壓后第一步不是點(diǎn)運(yùn)行而是先打開主腳本看它調(diào)用了哪些 m 文件、數(shù)據(jù)路徑寫在哪。常見角色如下表所示實(shí)際包可能少文件或多文件但分工基本是這個(gè)套路。文件角色常見命名作用DTW 核心dtw.m / dtw_custom.m輸入特征序列輸出距離和路徑VAD 切段vad.m / energy_zcr.m輸入采樣點(diǎn)輸出語音段邊界特征提取mfcc.m / mfcc_feature.m輸入音頻輸出逐幀特征向量demo 腳本main.m / test_dtw.m串起全流程的入口檢查完角色后把腳本里的addpath(E:\...)這類絕對(duì)路徑刪掉換成相對(duì)路徑。在解壓目錄執(zhí)行下面的命令就把當(dāng)前目錄加入 MATLAB 搜索路徑了。addpath(pwd);這個(gè)細(xì)節(jié)雖然簡單但能避免一大類“明明文件都在一運(yùn)行就說函數(shù)未定義”的報(bào)錯(cuò)。另外注意看數(shù)據(jù)文件是 wav 還是 mat前者用audioread讀后者直接load。數(shù)據(jù)是 mat 時(shí)還要確認(rèn)里面存的變量名有的包把特征預(yù)先存好了省去自己提特征這一步。3.2 雙門限 VAD用短時(shí)能量和過零率切出有效語音段DTW 對(duì)序列兩端的要求很高路徑必須從 (1,1) 出發(fā)、到 (na,nb) 結(jié)束如果起點(diǎn)或終點(diǎn)落在靜音幀上語音主體會(huì)被擠到路徑中段距離完全失真。所以要先用 VAD 把有效段切出來。下面這個(gè)雙門限 VAD 是最小可用實(shí)現(xiàn)思路是先能量后過零率。function [segs] vad_double_threshold(x, fs) % 雙門限法語音活動(dòng)檢測(cè) % 輸入 % x : 單聲道采樣點(diǎn)列向量 % fs : 采樣率單位 Hz % 輸出 % segs: N×2 矩陣每行 [start_sample, end_sample] % % 幀長 25ms幀移 10ms這是語音識(shí)別里的常見配置 frame_len round(0.025 * fs); frame_shift round(0.010 * fs); num_frames floor((length(x) - frame_len) / frame_shift) 1; en zeros(1, num_frames); zcr zeros(1, num_frames); for f 1:num_frames idx (f-1)*frame_shift (1:frame_len); seg x(idx); en(f) sum(seg .^ 2); % 短時(shí)能量 zcr(f) sum(abs(diff(seg 0))) / frame_len; % 過零率 end % 高門限取最大能量的 10%低門限是高門限的十分之一 en_high max(en) * 0.10; en_low en_high * 0.1; zcr_th mean(zcr) std(zcr); % 過零率自適應(yīng)門限 % 第一遍能量高于高門限的幀標(biāo)記為語音核心 voice zeros(1, num_frames); voice(en en_high) 1; % 第二遍從核心向兩邊擴(kuò)展把清音段拉回來 for f 2:num_frames-1 if voice(f) 0 (voice(f-1) 1 || voice(f1) 1) if en(f) en_low || zcr(f) zcr_th voice(f) 1; end end end % 連續(xù) 1 的幀串成語音段再換算回采樣點(diǎn)范圍 start_i find(diff([0, voice]) 1); stop_i find(diff([voice, 0]) -1); segs [start_i(:), stop_i(:)]; segs(:,1) (segs(:,1)-1) * frame_shift; segs(:,2) (segs(:,2)-1) * frame_shift frame_len; end這段代碼的關(guān)鍵在于“雙門限”三個(gè)字。第一遍用高門限找能量足夠高的核心語音幀相當(dāng)于把元音段先框出來第二遍向兩側(cè)擴(kuò)展低能量但過零率高的幀也會(huì)被收進(jìn)語音段這樣 f、s 這類摩擦音不會(huì)丟。diff(seg 0)統(tǒng)計(jì)相鄰采樣點(diǎn)符號(hào)變化次數(shù)除以frame_len得到歸一化過零率比 sign 判法更直觀也不容易被直流偏移帶偏。注意擴(kuò)展只做了一輪遇到中間夾著較長低能量洞的語音會(huì)補(bǔ)不齊嚴(yán)謹(jǐn)做法是外層套 while 循環(huán)反復(fù)擴(kuò)展直到?jīng)]有新幀加入這里先給一個(gè)能跑的版本。參數(shù)方面幀長 25ms、幀移 10ms 是常見默認(rèn)值壓縮包內(nèi)部即使寫的是 20ms/10ms不換也行但所有函數(shù)必須共用同一個(gè)frame_shift。en_high取最大值 10%、en_low取高門限 10%這兩個(gè)百分比是經(jīng)驗(yàn)值。如果錄音底噪大VAD 會(huì)把尾音拖太長優(yōu)先調(diào)這兩檔而不是去改幀長。過零率門限寫成mean std相當(dāng)于自適應(yīng)門限比固定閾值抗環(huán)境噪聲能力強(qiáng)這也是 zip 包能換設(shè)備跑的關(guān)鍵。3.3 把 VAD 結(jié)果喂給 DTW最小可用調(diào)用腳本假設(shè)你已經(jīng)把 MFCC 特征抽好了完整調(diào)用鏈很短讀 wav、VAD 切段、切出特征片段、兩兩 DTW。腳本里有一處非常容易踩的細(xì)節(jié)我在注釋里標(biāo)了出來。%% 最小調(diào)用鏈在解壓根目錄執(zhí)行本腳本 addpath(pwd); [x, fs] audioread(record_1.wav); x x(:, 1); % 只要單聲道 segs vad_double_threshold(x, fs); % 特征提取每幀 13 維輸出按“特征維 × 幀數(shù)”組織 % 如果裝了 Audio Toolbox可以直接用 mfcc(x, fs)再轉(zhuǎn)置 feat extract_features(x, fs); % 關(guān)鍵一步把采樣點(diǎn)下標(biāo)換算成幀下標(biāo) frame_shift round(0.010 * fs); seg_frame zeros(size(segs)); seg_frame(:,1) floor((segs(:,1) - 1) / frame_shift) 1; seg_frame(:,2) floor((segs(:,2) - 1) / frame_shift) 1; % 取第 1 段做參考模板第 2 段做待匹配樣本 ref feat(:, seg_frame(1,1):seg_frame(1,2)); test feat(:, seg_frame(2,1):seg_frame(2,2)); % 窗口約束按參考段長度的 15% 設(shè)置 w round(size(ref, 2) * 0.15); [cost, pi, pj] dtw_custom(ref, test, w); fprintf(DTW距離: %.3f, 路徑幀數(shù): %d\n, cost, numel(pi));第一處細(xì)節(jié)是換算vad_double_threshold返回的是采樣點(diǎn)下標(biāo)而feat是按幀組織的直接拿采樣點(diǎn)范圍切特征矩陣會(huì)錯(cuò)位所以先用frame_shift把段邊界轉(zhuǎn)成幀號(hào)。第二處是窗口w取參考段長度的 15%兩段長度差大時(shí)以短的那條為基準(zhǔn)更安全這個(gè)參數(shù)只影響路徑偏擺范圍不改變距離量級(jí)。extract_features是自行封裝的特征函數(shù)沒有 Audio Toolbox 時(shí)可以用 filterbank 手工實(shí)現(xiàn) MFCC核心是 mel 濾波器組加 DCT代碼大約二三十行有工具箱就直接mfcc(x, fs)把輸出轉(zhuǎn)置成 D×N 即可。跑完打印cost后順手plot(pj, pi)看一眼路徑路徑貼著對(duì)角線小幅抖動(dòng)是正常狀態(tài)出現(xiàn)長段橫走或豎走就要回去查 VAD。4. DTWVAD 聯(lián)動(dòng)避坑5 個(gè)讓結(jié)果翻車的典型問題4.1 cost 為 Inf 或 NaN窗口約束 w 把路徑切斷了現(xiàn)象dtw_custom返回costInf路徑數(shù)組為空腳本崩在下一行。 原因兩段序列的最優(yōu)路徑偏移量超過w限制DP 表里從起點(diǎn)到終點(diǎn)的每一條合法路線都被 |i-j|≤w 條件攔斷另一種情況是特征矩陣?yán)锍霈F(xiàn) NaN 幀累積計(jì)算一路傳染成 NaN。 解決先把w放大到短序列長度的 30% 跑通再逐步收緊到 10%~15%。調(diào)用后加一句防御斷言assert(~isinf(cost) ~isnan(cost), window too small or feature has NaN)。這樣翻車現(xiàn)場(chǎng)停在有提示的那一行而不是跑到后面某個(gè)莫名其妙的矩陣索引錯(cuò)誤排查時(shí)間能少一半。4.2 特征矩陣傳反norm 報(bào)錯(cuò)說明維度方向錯(cuò)了現(xiàn)象調(diào)用dtw_custom時(shí)size(a)變成 N×D執(zhí)行到norm(a(:,i)-b(:,j))MATLAB 直接報(bào)“Matrix dimensions must agree”或者不報(bào)錯(cuò)但距離數(shù)值大得離譜。 原因特征約定不一致。壓縮包代碼如果按“幀×維”存而你的 MFCC 輸出是“維×幀”函數(shù)第一行就會(huì)踩坑。最陰的情況是兩段長度恰好相等A 的一整列和 B 的一整列在數(shù)值上能相減距離看起來是個(gè)小數(shù)值實(shí)際語義全反。 解決函數(shù)入口加一行維度斷言assert(size(a,1) size(b,1), feature dim mismatch)。特征維必須相等幀數(shù)可以不等這是 DTW 成立的前提。所有工具函數(shù)統(tǒng)一按“維度 × 幀數(shù)”傳參誰打破約定誰報(bào)錯(cuò)不會(huì)靜默帶病運(yùn)行。4.3 VAD 把靜音尾巴切進(jìn)語音段同詞距離比異詞還大現(xiàn)象同一個(gè)詞錄兩遍DTW 距離 120不同詞距離反而 90模板匹配的判決完全反了。 原因雙門限擴(kuò)展把尾部低能量噪聲幀判成語音靜音幀數(shù)量占了整個(gè)序列的三分之一DTW 會(huì)優(yōu)先對(duì)齊在靜音上因?yàn)殪o音幀之間的特征距離幾乎為 0累積距離被稀釋真正發(fā)音部分的差異沒主導(dǎo)cost。 解決切段后對(duì)每段前后各砍兩幀約 20ms再做跨段比較。更穩(wěn)的方式是把 VAD 改成“先高門限找核心段再向兩側(cè)找到能量低于低門限的下降沿截?cái)唷倍皇峭鶅蓚?cè)擴(kuò)展。改完用 4.1 的路徑圖驗(yàn)證靜音尾巴造成的路徑會(huì)在首尾出現(xiàn)明顯水平或豎直段肉眼能識(shí)別。4.4 和 MATLAB 自帶 dtw 函數(shù)結(jié)果對(duì)不上現(xiàn)象裝了 Signal Processing Toolbox 的同事順手用[dist,ix,iy] dtw(x,y)驗(yàn)證發(fā)現(xiàn)dist數(shù)值和自定義函數(shù)差一個(gè)量級(jí)懷疑代碼寫錯(cuò)。 原因自帶dtw接受的是單變量時(shí)間序列默認(rèn)會(huì)對(duì)輸入做標(biāo)準(zhǔn)化輸出的是規(guī)整后的歸一化代價(jià)不是原始特征空間里的累積距離。自定義函數(shù)沒做標(biāo)準(zhǔn)化數(shù)值自然不同這不代表自定義是錯(cuò)的而是度量口徑不同。 解決統(tǒng)一度量。要么全部用自定義函數(shù)并自己做 zscore 預(yù)處理要么全部用自帶dtw且特征同樣預(yù)處理?;煊脙商缀瘮?shù)做橫向比較得到的所有閾值都是無效的。選定一套后就不換這是我在兩個(gè)版本對(duì)不上之后定下的死規(guī)矩。4.5 換了個(gè)音頻文件VAD 直接失效采樣率與幀參數(shù)脫鉤現(xiàn)象第一段錄音切得準(zhǔn)第二段同一套參數(shù)把整段標(biāo)成語音第三段又切不干凈。 原因函數(shù)內(nèi)部寫死了fs16000而audioread返回的實(shí)際采樣率是 8000 或 48000。幀長幀移按0.025*fs算會(huì)跟著變門限卻依賴整段統(tǒng)計(jì)量采樣率一變每幀能量和過零率的數(shù)值范圍全部錯(cuò)位。 解決所有參數(shù)從audioread的第二個(gè)輸出取[x, fs] audioread(xx.wav)然后把fs顯式傳進(jìn) VAD 和特征函數(shù)函數(shù)內(nèi)部不要重新賦值。幀長和幀移的換算統(tǒng)一寫在主腳本里變量名全局統(tǒng)一。這個(gè)習(xí)慣能消滅“換文件就失效”的玄學(xué)問題也讓代碼能直接在 16kHz 和 48kHz 數(shù)據(jù)集上復(fù)用。5. 進(jìn)階驗(yàn)證路徑自檢、距離歸一化與留一法判決5.1 先做路徑自檢再信數(shù)字批量跑之前我習(xí)慣先對(duì)任意兩條序列畫一次路徑圖plot(pj, pi, LineWidth, 1.2)加xlabel(test frame)、ylabel(ref frame)。路徑貼著對(duì)角線小幅抖動(dòng)說明時(shí)間軸伸縮自然如果出現(xiàn)長段水平或豎直邊沿基本能斷定 VAD 切多或切漏。路徑不過關(guān)后面所有距離數(shù)字都當(dāng)作無效這個(gè)檢查耗時(shí)不到一分鐘卻能避免在錯(cuò)誤數(shù)據(jù)上調(diào)一整晚閾值。5.2 距離歸一化除以規(guī)整路徑長度再比較兩個(gè)序列越長累積距離天然越大拿原始cost做判決會(huì)把長句子無腦推成“更不相似”。正確做法是除以路徑涉及的總幀數(shù)得到平均每幀代價(jià)這樣長句和短句才具備可比性。for k 1:num_templates dists(k) dtw_custom(template{k}, test_feat, w); dists(k) dists(k) / (size(template{k}, 2) size(test_feat, 2)); end [~, label] min(dists);除法的分母用兩段幀數(shù)之和而不是回溯路徑的長度原因很簡單路徑長度和兩段幀數(shù)之和接近但計(jì)算成本更低且不受w影響。這段代碼就是最近鄰模板匹配的核心判決。5.3 留一法驗(yàn)證確認(rèn)閾值而不是賭給每類詞錄 5 遍每次取其中 1 遍做測(cè)試其余 4 遍全部作為模板統(tǒng)計(jì)正確率和拒識(shí)率。如果同類距離和異類距離的分布完全重疊別急著調(diào)閾值先回去查 VAD 切段和特征提取如果分布分得開把閾值定在兩類分布交界處系統(tǒng)的拒識(shí)行為才可預(yù)期。鏈路全通之后這套 DTWVAD 組合可以直接往上接孤立詞識(shí)別或輕量級(jí)說話人確認(rèn)。我現(xiàn)在的固定順序是先路徑自檢再距離歸一化最后留一法驗(yàn)證順序不亂翻車概率就低。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取