HMM topology and transition modeling

HMM topology and transition modeling

介紹

在這裏我們將介紹在kaldi用如何表示HMM topologies和我們如何讓建模和訓練HMM 轉移概率的。我們將簡要的說下它是如何跟決策樹聯繫的;決策樹你可以在How decision trees are used in KaldiDecision tree internals這些地方看到更詳細的; 對於這個裏面的類和函數,你可以看Classes and functions related to HMM topology and transition modeling

HMM topologies

HmmTopology類是爲用戶指定到HMM音素的拓撲結構。在平常的樣例裏,腳本創建一個HmmTopology 對象的文本格式的文件,然後給命令行使用。爲了說明這個對象包含什麼,接下來的就是一個HmmTopology 對象的文本格式的文件(the 3-state Bakis model):

 <Topology>

<TopologyEntry>

 <ForPhones> 1 2 3 4 5 6 7 8

 </ForPhones>

 <State> 0 <PdfClass> 0

<Transition> 0 0.5

<Transition> 1 0.5

</State> 

 <State> 1 <PdfClass> 1 

<Transition> 1 0.5

<Transition> 2 0.5

 </State>  

 <State> 2 <PdfClass> 2

<Transition> 2 0.5

<Transition> 3 0.5

 </State>   

<State> 3

 </State>   

</TopologyEntry>

 </Topology>

這裏在這個特定的HmmTopology對象裏有一個TopologyEntry,它覆蓋音素18(所以在這個例子裏有8個音素和他們共享相同的topology)。他們有3個發射態;每一個有個自環和一個轉移到下一個狀態的。這裏也有一個第四個非發射態,狀態3 (對於它沒有<PdfClass> entry ) 沒有任何的轉移。這是一個這些topology entries的標準特徵;Kaldi 把第一個狀態(state zero) 看做開始狀態。最後一個狀態,一般都是非發射狀態和沒有任何的轉移,但會有最終的概率的那一個。在一個HMM中,你可以把轉移概率看做最後一個狀態的最終概率。在這個特定的例子裏所有的發射狀態他們有不同的pdf's (因爲PdfClass數目是不同的)。我們可以強制<PdfClass>數目一樣。在對象HmmTopology裏概率是用來初始化訓練的;這個訓練概率對於上下文相關的HMM來說是特定的和存儲在TransitionModel對象。TransitionModel用一個類名來存儲HmmTopology對象,但是要注意,在初始化TransitionModel後,一般不使用在HmmTopology 對象的轉移概率。這裏有個特例,對於那些不是最終的非發射狀態(比如:他們有轉移但是沒有<PdfClass> entry),Kaldi不訓練這些轉移概率,而是用HmmTopology對象裏給的概率。對於不支持非發射狀態的轉移概率的訓練,實際上簡化了我們的訓練機制,和由於非轉移狀態有轉移是不正常的,所以我們就覺得這些沒有很大的損失。

Pdf-classes

pdf-class是一個與HmmTopology對象有關的概念。HmmTopology對象爲每個音素指定了一個 HMM拓撲結構。HMM拓撲結構的每一個狀態數有一個"pdf_class"變量。如果兩個狀態有相同的pdf_class變量,他們將共享相同的概率分佈函數(p.d.f.),如果他們在相同的音素上下文。這是因爲決策樹代碼不能夠直接“看到”HMM狀態,它僅僅可以看到pdf-class。在正常的情況下,pdf-class和HMM狀態的索引(e.g. 0, 1 or 2)一樣,但是pdf classes爲用戶提供一種強制共享的方式。如果你想豐富轉移模型但是想離開聲學模型,這個是非常有用的,要不然就變成一樣的。pdf-class的一個功能就是指定非發射狀態。如果一些HMM狀態的pdf-class 設定常量kNoPdf = -1,然後HMM狀態是非發射的(它與pdf沒有聯繫)。這個可以通過刪除<PdfClass>標籤和相聯繫的數字來簡化對象的文本格式。

一個特定的HMM拓撲結構的pdf-classes的設置期望是從0開始和與其鄰近的(e.g. 0, 1, 2)一些值。這些是爲了圖建立代碼的方便和不會導致任何的損失。

Transition models (the TransitionModel object)

TransitionModel對象存儲轉移概率和關於HMM拓撲結構的信息(它包括一個HmmTopology 對象)。圖建立代碼根據TransitionModel對象來獲得topology 和轉移概率(它也需要一個 ContextDependencyInterface對象來獲得與特定音素上下文的pdf-ids)。

How we model transition probabilities in Kaldi

The decision that underlies a lot of the transition-modeling code is as follows: 我們跟據下面的4件事情決定一個上下文相關的HMM狀態的轉移概率(你可以把他們看做4-tuple):

· 音素(HMM裏的)

· 源HMM-state (我們解釋成HmmTopology對象, i.e. normally 0, 1 or 2)

· pdf-id (i.e.與狀態相關的pdf索引)

· HmmTopology對象的轉移的索引

這4項可以看做在HmmTopology對象裏的目標HMM狀態的編碼。根絕這些事情來求轉移概率的原因是,這是最細粒度(the most fine-grained way)的方式,我們在不增加編譯解碼圖的大小來對轉移建模。對於訓練轉移概率也是非常方便的。事實上,用傳統的方法來對轉移儘可能精確的建模可能沒有任何區別,和在單音素層面上的共享轉移的HTK方式可能更加有效。

The reason for transition-ids etc.

TransitionModel對象當被初始化時,就被設置成許多整數的映射,也被其他部分的代碼用作這些映射。除了上面提到的數量,也有轉移標識符(transition-ids), 轉移索引(這個與轉移標識符不是一樣的), 和轉移狀態。之所以要介紹這些標識符和相聯繫的映射,是因爲我們可以用一個完整的基本FST的訓練腳本。最自然的基本FST的設置是輸入標籤是pdf-ids。然而,考慮到我們的樹建立的方法,它不是總可能從pdf-id唯一的映射到一個音素,所以那樣很難從輸入的標籤序列映射到音素序列,那樣就不是很方便了;一般很難僅僅用FST的信息來訓練轉移概率。基於這個原因,我們把轉移標識符transition-ids作爲FST的輸入標籤,和把這些映射到pdf-id,也可以映射到音素,也可以映射到一個prototype HMM (在HmmTopology對象裏)具體的轉移概率。

Integer identifiers used by TransitionModel

以下類型的標識符都用於TransitionModel接口,它們所有的都用int32類型。注意其中的一些量是從1開始索引的,也有的是從0開始索引的。我們儘可能的避免從1開始索引的,因爲與C++數組索引不兼容,但是因爲OpenFst把0看做特定的情況(代表特殊符號epsilon), 我們決定把那些頻繁在FST中用作輸入符號的從1開始索引,我們就允許從1開始。更重要的是,transition-ids 是從1開始的。因爲我們沒有想到作爲FST的標籤,pdf-ids出現很頻繁。因爲我們經常使用他們作爲C++數組索引,我們使他們從0開始,但當pdf-ids作爲FST的輸入標籤,我們就給pdf-ids1當我們閱讀TransitionModel代碼時,我們應該對索引從1開始的量考慮,如果是這種情況我們就減去1,如果不是就不用減去。我們記錄這些聲明的成員變量。在任何情況,這些代碼不是公開接口的,因爲那會引起很多困惑。在TransitionModel中用的許多整數量如下:

· 音素(從1開始): 標識符的類型貫穿整個工具箱;用OpenFst符號表可以把pdf-ids轉換成一個音素。這類的id不一定是鄰近的(工具箱允許跳過音素索引)。

· Hmm狀態(從0開始):這是HmmTopology::TopologyEntry類型的索引,一般取值爲{0, 1, 2}裏的一個。

· Pdf或者pdf-id (從0開始):這是p.d.f.的索引,由決策樹的聚類來初始化(看PDF identifiers)。在一個系統裏通常由幾千個pdf-ids。 

· 轉移狀態,或者trans_state (從0開始):這是由TransitionModel 自己定義的。每個可能的三(phone, hmm-state, pdf) 映射到一個自己的轉移狀態。轉移狀態可以認爲是HMM狀態最細化的分割尺度,轉移概率都是分別評估的。

· 轉移索引或者trans_index (從0開始):這是HmmTopology::HmmState類型中的轉移數組的索引,這個索引記錄了轉移出某個轉移狀態的次數。

· 轉移標識符或者trans_id (從0開始):其中的每一個都對應轉移模型中的唯一的一個轉移概率。用從(transition-state, transition-index)到transition-id的映射,反之亦然。

在轉移模型代碼中也參考如下的概念:

· A triple意思就是a triple (phone, hmm-state, pdf)從轉移狀態中映射。

· A pair意思就是a pair (transition-state, transition-index)從轉移id中映射。

Training the transition model

轉移模型的訓練步驟是非常簡單的,我們創建的FST(訓練和測試中)將transition-ids作爲他們的輸入標籤。在訓練階段,我們做維特比解碼,生成輸入標籤的序列,就是每一個transition-ids的序列(每個特徵向量一個,意思也就是對應每幀輸出有限個transition-id的序列)。訓練轉移概率時我們積累的統計量就是數每個transition-id在訓練階段出現了多少次(代碼本身用浮點數來數,但在正常的訓練時我們僅僅用整數)。函數Transition::Update()對每一個transition-state做最大似然更新。這個明顯是非常有用的。這裏也有一些與概率下限有關的小的問題,和如果一個特定的transition-state不知道,我們怎麼做?更多的細節,請看代碼。

Alignments in Kaldi

這裏我們將介紹對齊的概念。說到對齊,我們意思就是<int32>類型的向量,含有transition-ids的序列,(c.f. Integer identifiers used by TransitionModel)它的長度跟需要對齊的那句話一樣長。transition-ids序列可以通過對輸入標籤序列解碼獲得。對齊通常用在訓練階段的維特比訓練和在測試時間的自適應階段。因爲transition-ids編碼了音素的信息,所以從對齊中計算出音素的序列是可行的 (c.f. SplitToPhones() and ali-to-phones.cc)。

我們經常需要處理以句子id做索引的一系列對齊。爲了方便,我們用表來讀和寫“對齊”,更多的信息可以看 I/O with alignments 。

函數ConvertAlignment() (c.f.命令行convert-ali) 把對齊從一個轉移模型轉換成其他的。這種典型的情況就是你使用一個轉移模型(由特定的決策樹來創建)來做對齊,和希望將其轉換爲不同的樹的其他的轉移模型。最好的就是從最原始的音素映射到新的音素集;這個特徵通常不是一定要的,但是我們使用它來處理一些由減少的音素集的簡化的模型。

在對齊的程序裏通常使用一個後綴"-ali"。

State-level posteriors

狀態級的後驗概率是“對齊”這個概念的擴展,區別於每幀只有一個transition-ids,這裏每幀可以有任意多的transition-ids,每個transition-ids都有權重,存儲在下面的結構體裏:

typedef std::vector<std::vector<std::pair<int32, BaseFloat> > > Posterior;

如果我們有個Posterior類型的對象"post",post.size()就等於一句話的幀數,和post[i]是一串pair (用向量存儲), 每一個pair有一個(transition-id, posterior)構成。

在當前的程序裏,有兩個方式創建後驗概率:

· 用程序ali-to-post來講對齊轉換爲後驗,但是生成的Posterior對象很細小,每幀都有一個單元后驗的transition-id

· 用程序weight-silence-post來修改後驗,通常用於減小靜音的權重。

未來,當加入lattice generation,我們將會從lattice生成後驗。

與"-ali"相比,讀入的後驗程序沒有後綴。這是爲了簡潔;讀取狀態層的後驗要考慮對齊信息的這類程序的默認值。

Gaussian-level posteriors

一個句子的高斯級別的後驗概率用以下的形式存儲:

typedef std::vector<std::vector<std::pair<int32, Vector<BaseFloat> > > > GauPost;

這個和狀態級的後驗概率結構體非常相似,除了原來是一個浮點數,現在是一個浮點數向量(代表狀態的後驗),每個值就是狀態裏的每個高斯分量。向量的大小跟pdf裏的高斯分量的數目一樣,pdf對應於轉移標識符transition-id,也就是每個pair的第一個元素。

程序post-to-gpost把後驗概率的結構體轉換爲高斯後驗的結構體,再用模型和特徵來計算高斯級別的後驗概率。當我們需要用不同的模型或特徵計算高斯級別的後驗時,這個就有用了。讀取高斯後驗程序有一個後綴"-gpost"。

Functions for converting HMMs to FSTs

把HMMs轉換爲FSTs的整個函數和類的列表可以在here這裏找到。

GetHTransducer()

最重要的函數就是GetHTranducer(), 聲明如下:

fst::VectorFst<fst::StdArc>*

GetHTransducer(const std::vector<std::vector<int32> > &ilabel_info,

constContextDependencyInterface &ctx_dep,

constTransitionModel &trans_model,

constHTransducerConfig &config,

std::vector<int32> *disambig_syms_left);

如果沒有介紹ilabel_info對象,ContextDependencyInterface接口,和fst在語音識別裏的一些應用基礎,這個函數的很多方面大家會很難理解。這個函數返回一個FST,輸入標籤是 transition-ids ,輸出標籤是代表上下文相關的音素(他們有對象ilabel_info的索引)。FST返回一個既有初始狀態又有最終狀態,所有的轉移都有輸出標籤(CLG合理利用)。每一個轉移都是一個3狀態的HMM結構,和循環返回最初狀態。FST返回GetHTransducer()僅僅對錶示ilabel_info的上下文相關音素有效,我們稱爲特定。這是非常有用的,因爲對於寬的上下文系統,有很多數量的上下文,大多是沒有被使用。ilabel_info對象可以從ContextFst (代表C)對象獲得,在合併它和一些東西,和它含有已經使用過的上下文。我們提供相同的ilabel_info對象給GetHTransducer()來獲得覆蓋我們需要的一個H轉換器。

注意GetHTransducer()函數不包括自環。這必須通過函數AddSelfLoops()來添加;當所有的圖解碼優化後,僅僅添加自環是很方便的。

The HTransducerConfig configuration class

The HTransducerConfig configuration class控制了GetHTransducer行爲。

· 變量trans_prob_scale是一個轉移概率尺度。當轉移概率包含在圖裏,他們就包含了這個尺度。命令行就是–transition-scale。對尺度的合理使用和討論,可以看Scaling of transition and acoustic probabilities

· 一個變量reverse和二個其他變量是可以修改的,如果"reverse"選項是true。"reverse"選項是爲後向解碼創建一個time-reversed FSTs。爲了使這個有用,我們需要添加功能到Arpa語言模型裏,我們將在以後去做。

The function GetHmmAsFst()

函數GetHmmAsFst() 需要一個音素上下文窗和返回一個用transition-ids作爲符號的對應的有限狀態接受器。這個在GetHTransducer()使用。函數GetHmmAsFstSimple() 有很少的選擇被提供,是爲了表示在原理上是怎麼工作的。

AddSelfLoops()

AddSelfLoops() 函數把自環添加到一個沒有自環的圖中。一個典型的就是創建一個沒有自環的H轉換器,和CLG一起,做確定性和最小化,和然後添加自環。這樣會使確定性和最小化更有效。AddSelfLoops() 函數喲選項來重新對轉移排序,更多的細節可以看Reordering transitions。它也有一個轉移概率尺度,"self_loop_scale",這個跟轉移概率尺度不是一樣的,可以看Scaling of transition and acoustic probabilities

Adding transition probabilities to FSTs

AddTransitionProbs() 函數添加轉移概率到FST。這樣做時有用的,原因是我們創造了一個沒有轉移概率的圖(i.e. without the component of the weights that arises from the HMM transitions), 和他們將在以後被添加;這樣就使得訓練模型的不同的迭代使用相同的圖成爲可能,和保持圖中的轉移概率更新。創造一個沒有轉移概率的圖是使用trans_prob_scale (command-line option: –transition-scale)爲0來實現的。在訓練的時候,我們的腳本開始存儲沒有轉移概率的圖,然後每次我們重新調整時,我們就增加當前有效的轉移概率。

Reordering transitions

AddSelfLoops()函數有一個布爾選項"reorder" ,這個選項表明重新對轉移概率排序,the self-loop comes after the transition out of the state。當應用變成一個布爾命令行,比如,你可以用 –reorder=true 在創建圖時重新排序。這個選項使得解碼更加簡單,更快和更有效(看Decoders used in the Kaldi toolkit),儘管它與kaldi的解碼不兼容。

重排序的想法是,我們切換自環弧與所有其他狀態出來的弧,所以自環維語每一個互相連接的弧的目標狀態(The idea of reordering is that we switch the order of the self-loop arcs with all the other arcs that come out of a state, so the self-loop is located at the destination state of each of the other arcs)。爲了這個,我們必須保證FST有某些特性,即一個特定狀態的所有弧必須引導相同的自環(也,一個有自環的狀態不能夠有一個靜音的輸入弧,或者是開始的狀態)。AddSelfLoops() 函數修改圖,以確保他們有這個特性。一個相同的特性也是需要的,即使"reorder"選項設置爲false。創建一個有"reorder"選項的圖準確的說就相對於你解碼一個句子得到的聲學和轉移模型概率而言,是一個非重新排序的圖 。得到的對齊的transition-ids 是一個不同的順序,但是這個不影響你使用這些對齊。

Scaling of transition and acoustic probabilities

這裏有三種在kaldi中使用的尺度類型:

Name in code

Name in command-line arguments

Example value (train)

Example value (test)

acoustic_scale

–acoustic-scale=?

0.1

0.08333

self_loop_scale

–self-loop-scale=?

0.1

0.1

transition_scale

–transition-scale=?

1.0

1.0







你也許注意到這裏沒有用語言模型的尺度;相對於語言模型來說,任何事情都是尺度。我們不支持插入一個詞的懲罰,一般來說(景觀kaldi的解碼不支持這個)。語言模型表示的真正的概率,一切相對於他們的尺度都是有意義的人。在訓練階段的尺度是我們在通過Viterbi 對齊解碼得到的。一般而言,我們用0.1,當一個參數不是很關鍵和期望它是小的。當測試是很重要的和這個任務是調整的,聲學尺度將被使用。現在我們來解釋這三個尺度:

· 聲學尺度是應用到聲學上的尺度(比如:給定一個聲學狀態的一幀的log似然比).

· 轉移尺度是轉移概率上的尺度,但是這個僅僅適合多個轉換的HMM狀態,它應用到這些轉移中的相對的權重。它對典型的沒有影響。

· 自環尺度是那些應用到自環的尺度。特別的是,當我們添加自環,讓給定自環的概率爲p,而剩下的爲(1-p)。我們添加一個自環,對數概率是self_loop_scale * log(p), 和對所有其他不在這個狀態的對數轉移概率添加(self_loop_scale * log(1-p))。在典型的topologies,自環尺度僅僅是那個有關係的尺度。

我們覺得對自環應用一個不同的概率尺度,而不是正常的轉移尺度有意義的原因是我們認爲他們可以決定不同時間上事件的概率。稍微更加微妙的說法是,所有的轉移概率可以被看成真正的概率(相對於LM 概率), 因爲聲學概率的相關性的問題與轉移沒有關係。然而,一個問題出現了,因爲我們在測試時使用Viterbi 算法(在我們的例子裏,訓練也是)。轉移概率僅僅表示當累加起來的真正的概率,在前向後向算法裏。我們期望這個是自環的問題,而不是由於HMM的完全不同的路徑的概率。因爲後面的情況裏,聲學分佈通常不是連接的,在前向後向算法和 Viterbi 算法裏的差別很小。


發佈了76 篇原創文章 · 獲贊 57 · 訪問量 76萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章