kaldi006 -- 構圖

 

解碼圖構建方法實驗部分

 

這裏我們一步一步地解釋圖的構建方法,以及與之相關的某些數據準備階段。

這種方法的大部分細節不會硬編碼到我們的工具中; 我們正在解釋它目前是如何做的 如果這個部分很混亂,那麼最好的補救辦法可能就是閱讀Mohri等人的“Speech Recognition with Weighted Finite-State Transducers”。 要注意:這張紙是相當長的,閱讀它至少需要幾個小時,對於那些尚不熟悉FST的人。 另一個好的資源是OpenFst網站,它將提供更多相關信息比如符號表

 

準備初始符號表

  我們需要準備OpenFst符號表word.txtphones.txt。 這些將整數id分配給系統中的所有單詞和phone 請注意,OpenFstepsilon的保留符號爲零。 示例WSJ符號表是:

 

## head words.txt

<eps> 0

!SIL 1

<s> 2

</s> 3

<SPOKEN_NOISE> 4

<UNK> 5

<NOISE> 6

!EXCLAMATION-POINT 7

"CLOSE-QUOTE 8

## tail -2 words.txt

}RIGHT-BRACE 123683

#0 123684

## head data/phones.txt

<eps> 0

SIL 1

SPN 2

NSN 3

AA 4

AA_B 5

 

word.txt文件包含單個消歧符號0”(用於G.fst輸入上的epsilon)。 這是最後編號的單詞。 如果您的詞典包含單詞0”,請小心。 phones.txt文件不包含消歧符號,但在創建L.fst之後,我們將創建一個文件phones_disambig.txt,該文件具有消歧符號(這對調試很有用)。

 

準備詞典L

首先,我們創建一個文本格式的詞典,最初沒有消歧符號。我們的C ++工具永遠不會與此交互,它只會被創建詞典FST的腳本使用。 我們的WSJ詞彙的一小部分是:

 

## head data/lexicon.txt

!SIL SIL

<s>

</s>

<SPOKEN_NOISE> SPN

<UNK> SPN

<NOISE> NSN

!EXCLAMATION-POINT EH2_B K S K L AH0 M EY1 SH AH0 N P OY2 N T_E

"CLOSE-QUOTE K_B L OW1 Z K W OW1 T_E

 

phone上的開始,結束和重音標記(例如T_EAH0)是我們的WSJ方法特有的,就我們的工具包而言,它們被視爲不同的phone(然而,對於這個設置我們做特殊處理;樹的構建過程中讀取roots文件)。

 

請注意,我們允許有空語音表示的單詞。 該詞典將用於訓練中創建L.fst(不含消歧符號)。 我們還創建一個消歧符號的詞典,用於解碼圖創建。 此文件的摘錄在這裏:

# [from data/lexicon_disambig.txt]

!SIL    SIL

<s> #1

</s>    #2

<SPOKEN_NOISE>  SPN #3

<UNK>   SPN #4

<NOISE> NSN

...

{BRACE  B_B R EY1 S_E #4

{LEFT-BRACE L_B EH1 F T B R EY1 S_E #4

 

 

此文件由腳本創建; 該腳本輸出它不得不添加的消歧符號,並用於創建符號表phones_disambig.txt。 這與phones.txt是一樣的,但它也包含消歧符號#0,#1,#2等的整數id(#0是來自G.fst的特殊消歧符號,但將被passed through”L.fst通過自循環)。 文件phone_disambig.txt中間的一部分是:

 

ZH_E 338

ZH_S 339

#0  340

#1  341

#2  342

#3  343

 

數字是如此之,因爲在這個(WSJ語聊中,我們向phone添加了重音和位置信息。 注意,用於空字(即<s></s>)的消歧符號必須與用於正常詞的那些不同,因此本例中的正常消歧符號從#3開始。

將沒有消歧符號的詞典轉換爲FST的命令是:

scripts/make_lexicon_fst.pl data/lexicon.txt 0.5 SIL | \

  fstcompile --isymbols=data/phones.txt --osymbols=data/words.txt \

  --keep_isymbols=false --keep_osymbols=false | \

   fstarcsort --sort_type=olabel > data/L.fst

 

這裏,腳本make_lexicon_fst.pl創建FST的文本表示。 0.5是靜音概率(即在句子開始處,每個單詞之後,我們以概率0.5輸出靜音;分配給無靜音的概率質量爲1.0-0.5 = 0.5,本示例中其餘的命令涉及 將FST轉換爲編譯格式; fstarcsort是必要的,因爲我們稍後將作compose

 

詞典的結構大致如人們期望的那樣。 有一個狀態(循環狀態)是終止狀態 有一個起始狀態有兩個過渡到循環狀態:一個有靜默和一個沒有。 從循環狀態,對應於每個單詞都有一個轉換,該字是轉換時的輸出符號; 輸入符號是該單詞的第一個phone 對於組合的效率和最小化的有效性,重要的是輸出符號應儘可能早(即開始時不是單詞的結尾)。 在每個單詞的末尾,爲了處理可選的靜音,對應於最後一個phone的轉換有兩種形式:一種到循環狀態,一種轉換爲靜默狀態,它們轉換到循環狀態。 我們不要在靜音中放置可選的靜音,我們將它定義爲只有一個phone是靜音phone的話。

 

用消歧符號創建詞典只是稍微複雜一點。 問題是我們必須將自循環添加到詞典中,以便可以通過詞典傳遞來自G.fst的消歧符號#0。 我們使用程序fstaddselfloopsc.f.添加和刪除消歧義符號)來執行此操作,儘管我們可以在腳本make_lexicon_fst.pl中輕鬆完成手動操作。

 

phone_disambig_symbol=`grep \#0 data/phones_disambig.txt | awk '{print $2}'`

word_disambig_symbol=`grep \#0 data/words.txt | awk '{print $2}'`

 

scripts/make_lexicon_fst.pl data/lexicon_disambig.txt 0.5 SIL  | \

   fstcompile --isymbols=data/phones_disambig.txt --osymbols=data/words.txt \

   --keep_isymbols=false --keep_osymbols=false |   \

   fstaddselfloops  "echo $phone_disambig_symbol |" "echo $word_disambig_symbol |" | \

   fstarcsort --sort_type=olabel > data/L_disambig.fst

 

程序fstaddselfloops不是原始的OpenFst命令行工具之一,它是我們自己的(我們有一些這樣的程序)之一。

 

準備語言模型G

語言模型G大部分是一個接受者(即每個圓弧上的輸入和輸出符號相同),其中以字爲其符號。 除了是消歧符號#0,它只出現在輸入端。 假設輸入是Arpa文件,我們使用Kaldi程序arpa2fst將其轉換爲FST。 程序arpa2fst輸出帶嵌入符號的FST。 在Kaldi,我們通常使用沒有嵌入符號的FST(即我們使用單獨的符號表)。 只需運行arpa2fst,我們必須採取的步驟如下:
  我們必須從FST中刪除嵌入的符號(並依賴於磁盤上的符號表)。
  我們必須確保在語言模型中沒有超出詞彙量的單詞
  我們必須刪除起始和結尾符號的非法序列,例如 <s>後跟</ s>,因爲這些導致L o G不可確定。
  我們必須用特殊的消歧符號#0替換輸入端的epsilons

實際腳本的略簡化版本如下所示:

gunzip -c data_prep/lm.arpa.gz | \

  arpa2fst --disambig-symbol=#0 \

             --read-symbol-table=data/words.txt - data/G.fst

 

最後一個命令(fstisstochastic)是一個診斷步驟(請參閱Preserving stochasticity and testing it)。 在一個典型的例子中,它打印出數字:

9.14233e-05 -0.259833

 

第一個數字很小,所以它證實沒有狀態的弧的概率加上最終狀態顯着小於1。 第二個數字是重要的,這意味着有些狀態具有太多的概率(FST中權重的數值通常可以解釋爲對數概率)。 對於具有回退的語言模型的FST表示,有一些具有太多概率的狀態是正常的。 在後續的構圖步驟中,我們將驗證這一非隨機性並沒有比開始時更糟糕。

所得到的FST G.fst當然只用於測試時間。 在訓練時間內,我們使用從訓練單詞序列生成的線性FST,但這是在Kaldi程序中完成的,而不是在腳本級別。

 

準備LG

 

當用G組合L時,我們概括地遵循一個相當標準的方法,即我們計算mindetL o G))。 命令行如下:

 

fsttablecompose data/L_disambig.fst data/G.fst | \

        fstdeterminizestar --use-log=true | \

        fstminimizeencoded | fstpushspecial | \

         fstarcsort --sort-type=ilabel > somedir/LG.fst

這裏OpenFst的算法有一些小的區別。 我們使用我們的命令行工具“fsttablecompose”實現的更有效的組合算法(參見Composition)。 我們的確定是一種也可以通過命令行程序fstdeterminizestar實現的算法,同時去空邊 選項-use-log = true要求程序首先將FST映射到log半環; 這保留隨機性(在log半環; Preserving stochasticity and testing it

 

我們通過程序“fstminimizeencoded”進行最小化。 這與OpenFst最小化算法的版本大體相同,適用於加權接收器; 唯一相關的變化是避免pushing weights,從而保持隨機性(詳見Minimization)。

 

程序“fstpushspecial”類似於OpenFst“fstpush”程序,但如果權重不等於1,則可以確保所有狀態總計爲相同的值(可能與不同),而不是嘗試推送額外權重到圖表的開頭或結尾。 這具有永遠不會失敗的優點(“Fstpush”可以失敗或循環很長時間,如果FST“總和爲無窮大; 它也快得多。 有關更詳細的文檔,請參閱push-special.cc

 

“fstarcsort”階段對弧進行排序以有助於稍後組合操作快速。

 

準備CLG

 

爲了獲得輸入爲上下文相關phone轉換器,我們需要準備一個叫做CLGFST,它與C o L o G相同,其中LG是詞典和語言模型C代表語音上下文 對於三音系統,C的輸入符號將爲a / b / c(即三元phone),輸出符號將是單個phone(例如abc)。 有關語音上下文窗口的更多信息,請參閱Phonetic context windows,以及我們如何產生爲不同的上下文大小。 首先,我們將描述如何創建上下文FST C,如果我們自己做它並且正常組合(爲了效率和可擴展性的原因,我們的腳本實際上不是這樣工作)。

 

製作上下文轉換器

 

在本節中,我們將解釋如何將C作爲獨立的FST獲得。

 

C的基本結構是它對所有可能的大小爲N-1phone窗口(c.f.語音上下文窗口,三音素N = 3)的狀態。 第一個狀態,意思是開始的話語,只會對應於N-1個子句。 每個狀態都有每個phone的轉換(現在讓我們忘記自循環)。 作爲通用示例,狀態a / b在輸出上具有c的轉換,並且輸入上的a / b / c轉換到狀態b / c。 話語開始和結束時都有特殊情況。

 

在話語開始時,假設狀態爲<eps> / <eps>,輸出符號爲a。 通常,輸入符號將是<eps> / <eps> / a。 但這並不代表phone,因爲(假設P = 1),中心元素是<eps>,不是phone 在這種情況下,我們讓弧的輸入符號爲#-1,這是我們爲此目的引入的特殊符號(我們在這裏不使用epsilon作爲標準方法,因爲當有空字時可能會導致不可確定性)。

 

話語結束有點複雜。上下文FST在右側(其輸出端)具有在語句結尾處出現的特殊符號$。考慮三元phone的情況。在句子結束後,看到所有符號後,我們需要刷出最後一個三元phone(例如a / b / <eps>,其中<eps>表示未定義的上下文)。這樣做的自然方法是在其輸入上具有/ b / <eps>的轉換,其輸出上的<eps>,從狀態a / b到最終狀態(例如b / <eps>a特殊最終狀態)。但是,這對於組合來說是低效的,因爲如果不是話語的結束,我們必須先探索這些轉變,才能將它們修剪掉。相反,我們使用$作爲終止符號,並確保它在LG的每個路徑的末尾出現一次。然後我們用C的輸出$替換<eps>。一般來說,$的重複次數等於N-P-1。爲了避免麻煩,必須確定要添加多少個後續符號到LG,我們只允許它在話語結束時接受任何數量的這種符號。這通過函數AddSubsequentialLoop()和命令行程序fstaddsubsequentialloop來實現。

如果我們自己想要C,我們首先需要一個消歧符號清單; 我們還需要找出我們可以用於後續符號的未使用的符號id,如下所示:

 

 grep '#' data/phones_disambig.txt | awk '{print $2}' > $dir/disambig_phones.list

 subseq_sym=`tail -1 data/phones_disambig.txt | awk '{print $2+1;}'

 

然後,我們可以使用以下命令創建C(但是,關於fstcomposecontext,請參見下文),因爲實踐中不執行此操作,因爲效率低下)。

 

 fstmakecontextfst --read-disambig-syms=$dir/disambig_phones.list \

 --write-disambig-syms=$dir/disambig_ilabels.list data/phones.txt $subseq_sym \

   $dir/ilabels | fstarcsort --sort_type=olabel > $dir/C.fst

 

程序fstmakecontextfst需要phone列表,消歧符號列表和後續符號的標識。 除了C.fst之外,它還會寫出解釋C.fst左邊符號的文件“ilabels”(請參閱The ilabel_info object)。 LG的組合可以做到如下:

 

fstaddsubsequentialloop $subseq_sym $dir/LG.fst | \

 fsttablecompose $dir/C.fst - > $dir/CLG.fst

 

爲了打印出C.fst和使用相同符號索引“ilabels”的任何內容,我們可以使用以下命令創建一個合適的符號表:

 

fstmakecontextsyms data/phones.txt $dir/ilabels > $dir/context_syms.txt

 

This command knows about the "ilabels" format (The ilabel_info object). An example random path through the CLG fst (for Resource Management), printed out with this symbol table, is as follows:

 

該命令知道“ilabels”格式(The ilabel_info object)。 通過CLG fst(用於資源管理),示例隨機路徑與該符號表打印出來,如下所示:

 

## fstrandgen --select=log_prob $dir/CLG.fst | \

   fstprint --isymbols=$dir/context_syms.txt --osymbols=data/words.txt -

0   1   #-1 <eps>

1   2   <eps>/s/ax  SUPPLIES

2   3   s/ax/p  <eps>

3   4   ax/p/l  <eps>

4   5   p/l/ay  <eps>

5   6   l/ay/z  <eps>

6   7   ay/z/sil    <eps>

7   8   z/sil/<eps> <eps>

8

 

動態組合C

 

在正常構圖中,我們使用一個程序fstcomposecontext,它動態地創建所需的狀態和C的弧,而不浪費地創建它。 命令行是:

 

fstcomposecontext  --read-disambig-syms=$dir/disambig_phones.list \

                   --write-disambig-syms=$dir/disambig_ilabels.list \

                   $dir/ilabels < $dir/LG.fst >$dir/CLG.fst

 

If we had different context parameters N and P than the defaults (3 and 1), we would supply extra options to this program. This program writes the file "ilabels" (see ) which interprets the input symbols of CLG.fst. The first few lines of an ilabels file from the  recipe are as follows:

 

相比於默認值31),如果我們有不同的上下文參數NP,我們將爲此程序提供額外的選項。 該程序寫入解釋CLG.fst的輸入符號的文件“ilabels”(請參閱The ilabel_info object)。 Resource Management中的ilabels文件的前幾行如下:

 

65028 [ ]

[ 0 ]

[ -49 ]

[ -50 ]

[ -51 ]

[ 0 1 0 ]

[ 0 1 1 ]

[ 0 1 2 ]

...

 

數字65028是文件中的元素數。 像[-49]這樣的就是消歧符號; [0 1 2]這樣的行表示聲音上下文窗口; 前兩個條目是用於epsilon(從未使用)的[][0]是用於在C開頭代替epsilon使用打印形式#-1的特殊消歧符號,以確保可確定性。

 

減少上下文相關輸入符號的數量

 

在創建CLG.fst之後,有一個可選的構圖階段可以減小其大小。 我們使用從決策樹和HMM拓撲信息中得出的程序make-ilabel-transducer他是上下文相關phone的子集將對應於相同的編譯圖,因此可以合併(我們選擇任何一個子集的元素,並且轉換所有上下文窗口到那個窗口)。 這是一個類似於HTK邏輯到物理映射的概念。 命令是:

make-ilabel-transducer --write-disambig-syms=$dir/disambig_ilabels_remapped.list \

  $dir/ilabels $tree $model $dir/ilabels.remapped > $dir/ilabel_map.fst

 

這個程序需要樹和模型; 它輸出一個名爲“ilabels.remapped”的新的ilabel_info對象; 這與原始“ilabels”文件的格式相同,但行數較少。 FST“ilabel_map.fst”CLG.fst做組合,並重新映射標籤。 做到這一點後,我們確定化和最小化,所以我們可以馬上實現任何尺寸的縮減:

 

fstcompose $dir/ilabel_map.fst $dir/CLG.fst  | \

   fstdeterminizestar --use-log=true | \

   fstminimizeencoded > $dir/CLG2.fst

 

 

對於一般的設置,這個階段實際上並沒有減少圖的大小(通常爲5%到20%的減少),在任何情況下,它只是通過這種機制減少的中間構圖階段的大小。 但是,對於具有更廣泛背景的系統,節省可能會變得重要。

 

製作H轉換器

 

在傳統的FST製作中中,H轉換器是在其輸出上具有上下文相關phone,並且在其輸入上具有表示聲學狀態的符號。在我們的例子中,H(或HCLG)的輸入上的符號不是聲學狀態(在我們的術語中是pdf-id),而是我們稱之爲transition-id的東西(參見Integer identifiers used by TransitionModel)。transition-idpdf-id加上一些包括phone在內的其他信息進行編碼。每個transition-id可以映射到pdf-id。我們創建的H轉換器不對自環進行編碼。這些稍後由單獨的程序添加。 H轉換器具有初始和最終狀態,並且從該狀態到每個條目都有一個轉換,但ilabel_info對象中的第零個(the ilabels file,見上文)。上下文相關phone的轉換轉到相應HMM的結構(缺少自環),然後返回到開始狀態。對於正常拓撲,HMM的這些結構只是三個弧的線性序列。對於每個消歧符號(#-1,#0,#1,#2,#3等),H也在初始狀態上具有自循環。

 

H轉換器的腳本部分(我們稱之爲Ha,因爲它在這一點上缺少自循環),是:

make-h-transducer --disambig-syms-out=$dir/disambig_tstate.list \

   --transition-scale=1.0  $dir/ilabels.remapped \

   $tree $model  > $dir/Ha.fst

 

有一個可選參數來設置轉移因子; 在我們目前的訓練腳本中,這個因子1.0。 這個比例隻影響到與自循環概率無關的轉換部分,而在正常拓撲(Bakis模型)中它完全沒有影響; 請參閱Scaling of transition and acoustic probabilities以獲得更多的解釋。 除了FST之外,該程序還寫入一個消歧符號列表,稍後必須刪除。

 

製作HCLG

 

製作最終圖HCLG的第一步是製作缺少自循環HCLG。 我們當前腳本中的命令如下所示:

  fsttablecompose $dir/Ha.fst $dir/CLG2.fst | \

   fstdeterminizestar --use-log=true | \

   fstrmsymbols $dir/disambig_tstate.list | \

   fstrmepslocal  | fstminimizeencoded > $dir/HCLGa.fst

 

在這裏,CLG2.fstCLG的版本,縮略符號集(邏輯三音素HTK術語)。 我們刪除消歧符號和任何easy-to-remove epsilons(請參閱Removing epsilons),在最小化之前; 我們的最小化算法是避免推送符號和權重(從而保留隨機性),並接受非確定性輸入(見Minimization)。

HCLG添加自環

通過以下命令完成向HCLG添加自環:

 

  add-self-loops --self-loop-scale=0.1 \

    --reorder=true $model < $dir/HCLGa.fst > $dir/HCLG.fst

 

有關如何應用0.1 self-loop-scale的說明,請參見Scaling of transition and acoustic probabilities(注意它也影響非自概率)。有關重新排序選項的說明,請參閱Reordering transitions; “重新排序選項提高了解碼速度,但與kaldi解碼器不兼容。 add-self-loops程序不僅添加自環;它可能還必須複製狀態並添加epsilon轉換,以確保以一致的方式添加自環。在重新排序轉換中,Reordering transitions詳述這個問題。這是唯一不保存隨機性的構圖階段;它不保存它,因爲自環的因子不是1,所以程序fstisstochastic應該給所有的G.fstLG.fstCLG.fstHCLGa.fst相同的輸出,但不是HCLG.fst 。在自環階段之後我們不再確定了;這樣會失敗,因爲我們已經刪除了消歧符號。無論如何,這很慢,我們認爲在這一點上確定和最小化沒有什麼進一步的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章