Kaldi中的Chain模型


Chain模型的訓練流程

鏈式模型的訓練過程是MMI的無網格的版本,從音素級解碼圖生成HMM,對其使用前向后向算法,獲得分母狀態后驗,通過類似的方式計算分子狀態后驗,但限於對應於轉錄的序列。

對於神經網絡的每個輸出索引(即對於每個pdf-id),我們計算(分子占有概率 - 分母占用概率)的導數,並將它們在網絡中反向傳播。

分母FST

對於計算中的分母部分,我們對HMM進行前向-后向計算。實際上,由於我們把它表示為一個有限狀態接受器,標簽(pdf-id)與弧而不是狀態相關聯,所以在正常的公式中分母FST並不是真正的HMM。不過可以簡單地將它看作是HMM,因為我們使用前向-后向算法來獲得后驗。在代碼和腳本中,我們將其稱為"分母FST"。

分母FST所需的音素語言模型

構建分母FST的第一個階段是創建一個音素語言模型。這種語言模型是從訓練數據的音素對齊中學習得到的。這是一個不平滑的語言模型,這意味着我們不能將它簡化為低階n-gram。然而,在構建時,一些語言模型狀態被完全刪除,所以,連接到這些狀態的轉移,將被連接到低階n-gram的狀態。我們避免進行平滑的原因是為了減少在聲學上下文擴展和編譯后的圖中將出現的弧的數量。

   

我們的設定是估計一個4-gram語言模型,並且永遠不會修剪低於3-gram的語言模型狀態(所以我們始終保持至少兩個音素的歷史記錄)。在無修剪3-gram規則規定的狀態數量之上,我們可以保留指定數量(例如2000)的4-gram語言模型狀態(其余的都以相應的3-gram狀態表示),我們保留的是那些可最大化訓練數據的似然的狀態。估計所有概率,使訓練數據的似然最大。不修剪3-gram的原因是3-gram的任何稀疏度將傾向於最小化編譯后圖的大小。需要注意的是,如果我們的音素LM只是一個簡單的音素回路(即一個unigram),由於語音上下文的影響,它將會擴展到triphones,但它會有所有可能的3-grams中的弧。所以我們從使用未修剪的3-gram模型中獲得的任何稀疏度都是有好處的。從經驗來說,一個不平滑的3-gram LM可能是一個最小FST;並且修剪一些trigrams,同時增加編譯的FST的大小,導致很少或沒有WER的改進(至少在3倍速度擾動擴展的300小時數據上是這樣的;不過在較少的數據上可能有幫助)。

   

Switchboard的配置中,我們嘗試了各種復雜度(57)的音素語言模型;我們選擇的配置(4-gram,除了2000個狀態以外的所有狀態都修剪為3-gram)的音素-LM復雜度大概是6。更低復雜度的音素LM總會導致訓練模型更好的WER;對於常規(基於字的)MMI訓練,語言模型的中等強度似乎最有效。

分母FST的編譯

上一節中描述的音素語言模型被擴展為FST,"pdf-id"作為弧,通常,在Kaldi解碼流程中(除了沒有涉及到詞匯),解碼圖的編譯過程中(參見解碼圖創建流程(測試 )),最后我們將transition-ids轉換為pdf-id。區別在於我們如何最小化圖的大小。 正常的流程包括確定化和最小化。 我們無法通過使用消歧符號、determinizationminimization或它們的變形來減少圖的大小。相反,為了對圖進行最小化,我們重復使用如下步驟3次:pushminimizereverse; pushminimize reverspush"指weight-pushing; 'reverse'是指將弧線的指向反轉,並交換初始狀態和最終狀態。

   

初始、最終概率和'歸一化FST'

上面提到的圖的創建過程自然地給了我們一個初始狀態和每個狀態的最終概率;但這些不是我們在前向-后向中使用的。原因是這些概率適用於話語邊界,但是我們訓練一個固定長度(例如1.5秒)的分割的話語塊。將HMM的初始狀態和最終狀態限制在這些任意選擇的切割點是不合適的。相反,我們使用從"運行HMM"導出的初始概率進行固定次數的迭代,並對概率進行平均;最終概率等於1.0。我們這么做是有理由的,但現在沒有時間解釋。在分母詞圖的前向-后向過程中,我們將這些初始和最終概率應用於初始和最終幀作為計算的一部分。然而,我們也寫出一個具有這些初始和最終概率的分母FST的版本,我們稱之為"歸一化FST"。 (初始概率使用ε弧進行仿真,因為FST不支持初始概率)。這種"歸一化FST"將增加分子FST中的概率,稍后將描述這種增加概率的方式。

   

分子FST

作為我們准備訓練過程的一部分,我們為每個話語產生一個叫做"分子FST"的東西。分子FST編碼監督轉錄物,並且還編碼該轉錄物的比對(即,其強制與從基線系統獲得的參考對准相似),但是分子FST允許與轉錄有部分差異,這種差異用"擺動空間"來描述。默認情況下,在lattice對齊中,我們允許一個音素過早或延遲0.05秒。與對齊信息的整合很重要,因為我們並不訓練整句話,而是對分割開的固定長度的一段進行訓練(對於基於GPU的訓練而言這是很重要的),也就是說,需要根據對齊,將話語分成多個部分。

   

作為訓練數據的特定發音,我們不用強制使用訓練數據的特定發音,而是使用lattice格式的對齊(由lattice解碼流程生成,steps/align_fmllr_lat.sh),作為訓練數據發音的替代,使用對應於一段語句的圖作為解碼圖。這會產生beam范圍內最高得分的所有發音的對齊。

固定長度的chunkmini-batches

   

為了使用小批量數據進行訓練,我們將我們所有的語音分解成固定長度的語音塊(在我們當前的腳本中長度為1.5秒)。丟棄短於此長度的語音;長於此長度的語音,被分割成多個塊(塊之間可能有較短的重疊或間隙)。請注意,我們的聲學模型通常需要用到聲學語境的左側或右側的幀,因此,需要在語音塊分割完后添加上下文(left-context, right-context)。

   

minibatch通常設定為2的冪,並且受到GPU內存的限制。我們的許多示例腳本講minibatch設定為128 GPU內存的最大單次消耗是前向后向計算中的alpha概率。例如,chunk1.5秒,經過3次降采樣之后,我們有50time steps。在Switchboard配置中,分母FST30000個狀態。我們使用單精度浮點來表示alpha,使用的內存為(128 * 50 * 30000 * 4/ 10 ^ 9 = 0.768G

   

這並不會耗盡所有的GPU內存,但還有其他的地方需要消耗內存,例如我們在內存中保留nnet輸出的兩個副本,這將消耗大量的內存,如果設定分母FST的狀態為10000個而非30000,它將為nnet輸出的副本留出足夠的內存。

使用幀偏移的數據進行訓練

在神經網絡訓練中,我們已經可以通過對數據進行擾動的方法來人為地增加我們訓練的數據量。標准的nnet3訓練示例腳本中使用0.9,1.01.0的因子對原始音頻進行時間扭曲,以得到3倍的數據。這是chain模型的模型配置。如果基線做了速度擾動,chain模型也需要這么做,反之亦然。然而,有一種額外的方法可以讓我們對數據進行擴充,即對幀進行偏移。這些模型的輸出幀速率是常規幀速率的三分之一(可配置),這意味着我們只計算時間t3的倍數的輸出,因此我們可以通過對訓練樣本偏移0幀,1幀和2幀來生成不同版本的訓練數據。這可以在訓練腳本中自動完成,並且在我們從磁盤讀取訓練樣本時就已經完成,程序nnet3-chain-copy-egs具有由腳本設置的-frame-shift選項。這會影響epoch的數量。如果用戶要求,例如4epochs,那么我們實際上訓練了12epochs;我們只是在3種不同版本的數據中進行。選項-frame-shift = t選項實際上是將輸入幀進行t個移位並將輸出的幀進行偏移,偏移位數為接近t3的倍數(n%3==0, n~t)。 (這個倍數可能不是3,而是一個名為-frame-subsampling-factor的配置變量)。

訓練時GPU的問題

chain的計算包括分子FST和分母HMM上的前向后向計算。分子FST的計算是非常快的。而分母前向后向計算需要花費相當多的時間,因為分母FST中可能會有很多的弧(例如默認配置的Switchboard中有200,000個弧和30,000個狀態)。所花費的時間幾乎與計算神經網絡所需的時間一樣多。

   

為了進一步加速前向后向算法的計算,需要對前向后退計算進行簡化(像pruned Viterbi一樣,但只計算后驗概率)。為了進行加速,我們必須將很大一部分的狀態修剪掉,因為我們需要彌補修剪會帶來的內存損失。在我們目前的實現中,我們非常小心,確保一組GPU線程都處理相同的HMM狀態和時間,只是從不同的塊(我們在代碼中稱為這些不同的"序列");並且我們確保與這些不同序列相對應的存儲器位置在存儲器中彼此相鄰,因此GPU可以進行"整合存儲器訪問"。通過狀態級修剪,由於不同序列的內存訪問將不再"同步",所以我們將失去這一優勢。盡管如此,仍然可以獲得修剪版本的前向后退算法。

   

為了速度,我們不會在分母圖的alpha-beta計算中使用對數值。為了將所有的數值保持在一個合適的范圍內,我們將每一幀的所有的聲學概率(即指數形式的nnet輸出)進行累積,選擇一個"任意值",以確保我們的α分數保持在一個好的范圍內。我們將其稱為"任意值",因為算法的設計使得我們可以在此處選擇任何值,並且在數學上仍然是正確的。我們將一個HMM狀態指定為"特殊狀態",該"任意常數"選取為特殊狀態前一幀的α的倒數。這使得特殊狀態的alpha值接近於1。作為"特殊狀態",我們選擇在HMM的有限分布中具有高概率的狀態,並且可以訪問HMM的大部分狀態。

   

Chain模型的解碼

chain模型的解碼過程與常規的基於nnet3的模型完全相同,實際上,它們使用相同的腳本(steps/nnet3/decode.sh)。不過,還是有一些配置差異:

首先,構建chain的圖時,使用更簡單的拓撲結構;但這不需要用戶的特定操作,因為圖的構建腳本所需的拓撲結構是通過'final.mdl'中提取的,該'final.mdl'chain訓練腳本生成的,模型中包含有正確的拓撲。

默認情況下,編譯圖時,我們將"self-loop-scale"設定為0.1。這影響編譯程序對自環上的轉換概率的處理方式(通常會更好)。然而,對於chain模型來說,由於它的訓練方式,我們需要使用與訓練時完全相同的概率縮放系數,為簡單起見,我們設置為1.0。因此在utils/mkgraph.sh腳本中將選項-self-loop-scale設定為1.0

在這些模型中沒有必要"以先驗進行分割"。所以在.mdl文件中,我們根本不設置先驗向量。當未設定先驗向量時,解碼器會忽略這個步驟。

通常在解碼中使用的默認聲學權重(0.1)是不恰當的 - 對於chain模型,最佳聲學權重接近於1。因此,我們腳本steps/nnet3/decode.sh中將選項-acwt默認設定為1.0

評分腳本對語言模型進行打分(搜索)時,語言模型權重只能設定為整數,在一些實例腳本的設置中,最佳的語言模型權重介於1015之間,當權重接近於1時,效果不好。(注意:可以將語言模型權重視為聲學模型權重的倒數)。為了解決"語言模型權重只能為整數"這個問題而不改變評分腳本(這評分腳本是特定於數據庫的),我們向腳本steps/nnet3/decode.sh中加入了一個新的選項-post-decode-acwt 10.0,在生成lattice之前,它將聲學概率縮小10倍。此后,最佳語言模型權重大約為10

   

只要不修改聲學模型權重,默認的decode beamlattice beam對於'chain'模型來說是合適的。然而,一旦使用-acwt 1.0選項,可能不會明顯加速解碼,您可以通過使用稍微更小的decode beam來獲得更快的解碼。通過在Switchboard中縮小光束,我們可以將解碼時間從實時的大約1.5倍降低到實時的大約0.5倍,精度只降低0.2%左右(這是神經網絡在CPU上的結果;在GPU上應該會更快)。Dan指出:以上我的實驗得出的最好的結果,實際上的解碼速度可能會超過了上述結果。請記住,這是在大功率的最新的服務器機器(單線程)上運行的結果。

   

您可能會注意到,在當前的示例腳本中,我們使用了iVectors。我們這樣做只是因為他們通常會對性能有一點幫助,並且,因為我們在基線中也使用了iVectors,為了對chain與基線進行對比(控制變量),我們在chain模型中也使用了iVectorsiVectorschain模型沒有固有的聯系,使用chain模型也不一定需要使用iVectors。其實我們想擺脫iVectors的使用(見下文)。

Chain模型的下一步工作(未來與展望)

(注意:此列表生成於20151213日,但可能已過期)。我們需要做的事情(我們想幫助的)是:

在各種語料庫中提供示例腳本(並調整它們),看看示例腳本中定義的模型的性能是否與語料(語料的特性:語種、方言、場景、語料量等)有關。

創建並調整訓練腳本的LSTMBLSTM版本。(這可能涉及到一些學習率schedules和配置)。

找出如何加快前向-后向的計算。(例如進行狀態層面的修剪,或優化當前的Kaldi內核或數據結構)。

   

Dan應該去做的,一個更長遠的計划是,為chain模型提供在線解碼。實際上chain的在線解碼與nnet3的在線解碼沒有什么不同,因為chain模型類似與普通的nnet3模型。有一點必須要完成的,就是決定是否繼續支持iVector,棄用iVector可以大大地簡化訓練流程,並使得模型更加魯棒。我們希望LSTMs能夠代替iVector,因為LSTMs可以對聲學語境進行良好的學習,所以iVector自適應將不再有用,可能會被棄用。我們還有一些其他想法,將模型自適應加入到神經網絡中,而不再使用iVectors進行自適應。這將需要一些實驗。

   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM