歡迎大家關注我的博客 pelhans.com/ ,所有文章都會第一時間發布在那裡哦~


本部分是對Kaldi thchs30 中run.sh的代碼的line 38-60行研究和知識總結,重點是word-graph的建立。

概覽

先把代碼放在這裡:

prepare language stuff
build a large lexicon that invovles words in both the training and decoding.
(
echo "make word graph ..."
cd $H; mkdir -p data/{dict,lang,graph} &&
cp $thchs/resource/dict/{extra_questions.txt,nonsilence_phones.txt,optional_silence.txt, silence_phones.txt} data/dict &&
cat $thchs/resource/dict/lexicon.txt $thchs/data_thchs30/lm_word/lexicon.txt |
grep -v | grep -v | sort -u > data/dict/lexicon.txt || exit 1;
utils/prepare_lang.sh --position_dependent_phones false data/dict "" data/local/ lang data/lang || exit 1;
gzip -c $thchs/data_thchs30/lm_word/word.3gram.lm > data/graph/word.3gram.lm.gz || exit 1;
utils/format_lm.sh data/lang data/graph/word.3gram.lm.gz $thchs/data_thchs30/lm_word/lexicon.txt data/graph/lang || exit 1;
)

其中$H是指的是thchs30/s5這個路徑,$thchs30指的是前面設置的語音語料庫路徑。

頭五行為提示開始製作,進入主目錄並建立dict,lang,graph三個文件夾,然後講語音資料庫中dict下的文件考到data/dict目錄下。用cat命令顯示字典文件並用grep查找不包含 <s></s> 字元的行並輸入到lexicon.txt文件中。

utils/prepare_lang.sh 構建字典L.fst文件,該文件不包含消岐符。

gzip -c這行是解壓語言模型。

utils/format_lm.sh 該行是為了生成G.fst文件和並檢查它是否包含空環。

可以看出重點就是兩個utils/*的腳本,下面我們詳細介紹它倆。

prepare_lang.sh

該程序的參數在文件開頭有詳細的說明,這裡挑選使用到的參數翻譯一下:

1) --position_dependent_phones false: 是否將phone拆成更詳細的部分,若選擇true,則將在phone後面根據音素所處的位置加上 _B, _I, _E, _S

2) data/dict: 詞典源文件夾 <dict-src-dir>

3) "<SPOKEN_NOISE>" :詞典裏未包含的事例, <oov-dict-entry> .

4) data/local/lang : 臨時文件夾

5) data/lang: 目標文件夾。

6) share_silence_phones:在構造出的roots.txt文件中,其中的所有sil共享同一個高斯pdf。需要注意的是若選擇共享也只是共享pdf,轉移概率還是不一樣的。

這裡對roots.txt文件詳細解釋一下,在同一行出現的所有音素對應的HMM會共享它們的pdf-class根節點,如果不共享的話就放在不同行。

roots.txt每行開頭都包含(not)shared和(not)split的修飾,share代表對於每個HMM(例如有三個狀態),是否所有的狀態共享一個根節點(如三個狀態共享一個根節點),not-shared就是每個狀態都有不同的根節點。(not)split則表示對於上述的每個根節點,是否有機會根據問題進行決策樹分裂,如果為split,則允許進行分裂,分裂後同一行中的不同音素對應的HMM狀態可能會有不同的pdf-class。如果是not-split,則不允許分裂,同一行中的所有音素對應的HMM將固定共享它們的pdf-class而不發生分裂。在thchs30中該選項是false同時不進行共享。

到這裡用到的選項我們就解釋完了,下面從頭到尾把用到的部分代碼解釋一下。

line 85-129是各種輸入文件的檢查和整理。其中:

1. lexicon.txt文件是字典文件,格式為 word~phone~phone2~phoneN .

2. lexiconp.txt是帶概率的字典文件,概率值為每個詞出現的概率,格式為: word~pron-prob~phone1,~phone2~phoneN .

3. silence_phones.txt/nonsilence_phones.txt為靜音/非靜音音素,每一行代表一組相同的base phone,但可能有不同的中銀或者聲調的音素,如: a~a1~a2~a3

4. optional_silence.txt:包含一個單獨的音素用來作為詞典中默認的靜音音素。

5. extra_questions.txt:用來構建決策樹的問題集,可以是空的,包含多組相同的音素,每一組音素包含相同的重音或者聲調;也有可能是一致表示非語音的靜音/噪音音素。這可以用於增加自動生成問題的數量。

line 131-203:當你選擇position_dependent_phones是true時,根據lexiconp.txt文件在每個詞的音素後面添加位置標記.

line 209-230: 執行上面建立roots.txt文件的過程。若share_silence_phones為true則共享sil,若為false則直接將shared 和split加到roots.txt文件頭並且將需要共享的音素放到一行即可。

line 232-237: 使用utils/apply_map.pl將源文件夾下的音素映射到phones/下的對應文件。(map程序說自己applies a map to things in a file...這個things..等我有時間好好看看這貨幹嘛的,不過我對比了下,倆文件除了多個空格沒有其他區別。。。)

line 243-253: 根據音素的位置來添加extra questions.

line 255-284: 字典中的詞可能會出現同個發音的情況,這裡在同音詞的發音標註後面加入消岐符(disambig sysmbols,長這樣#1)來進行區分,字典中有多少同音詞就會有多少個消岐符。

line 286-296: 為每個音素創建描述詞邊界信息的文件。

line 298-345: 創建辭彙-符號表。這個表中的內容就是所有辭彙和符號與數字的對應; <eps> 0 這樣。

line 347-362: 創建align_lexicon.{txt,int}文件,大體操作為從lexiconp.txt中移除概率部分。

line 364-386: 創建不包含消岐符的的L.fst文件用於訓練使用。使用的是utils/make_lexicon_fst.pl,它將詞典中的單詞和音素轉換成fst輸入格式的文件。內部存儲格式為:

line 389-412: 主要是格式轉換,將各個文本轉換成前面映射得到的數字表示。

line 415-433: 創建完整的L.fst文件。

format_lm.sh

該程序的主要目標就是根據語言模型生成G.fst文件。方便與之前的L.fst結合,發揮fst的優勢。腳本最後會檢測G.fst中是否存沒有單詞的空環,如果存在就會報錯,這回這會導致後續HLG的determinization出現錯誤。其程序核心就是arpa2fst

L.fst 與 G.fst

可以看出,上面兩個程序的主要目標就是創建L.fst和G.fst,那麼這兩個文件是什麼呢?它們的作用又是什麼呢?

fst文件根據它的顯示圖我猜測應該是有限狀態轉換機(Finite State Transducer)的縮寫,FST允許聲學信息(HMM集合)、單詞發音、語言模型和識別語法的單獨的統一的表示。由於該統一表示,導致在遍歷搜索網路期間,可以執行計算花費不大的計算,而其他計算花費大的優化能夠被卸下到預編譯階段進行。

知道它是個有限狀態轉換機,剩餘的就好辦的多了,以L.fst來說,它在轉換之前是一個帶有概率和消岐符的字典文件,長的是下面這個樣子:

它的第一行 !EXCLAMATION=POINT 表示一個詞word,後面的1.0表示概率,再後面的EH_B等就表示音素以及音素的相對位置(當然若未選擇位置依賴選項為true的話就沒有_B等後綴)。若是帶有消岐符的詞典的話,則在重音的單詞後的音素結尾處會帶有消岐符,如#1這種。字典中有多少個同音詞就會有多少個消岐符(disambig sysmbol).

而後我們使用腳本utils/make_lexicon_fst.pl將詞典中的單詞和音素轉換成fst輸入文件的格式,其格式為:

頭兩行與後兩行相對應,從第一行的0開始,到下一個第一行出現0為止構成一個循環,如上圖中第一行數字為0 1,代表從狀態0指向狀態1,對應的弧為K_B:CAUSE,而後第二行是1 2,對應弧為AH_I:.第三行為2 0 ,對應弧為Z_E:。這樣又指回了0即完成了一個循環,對應於下圖中的最上面的環.而後接下來繼續從0開始下一個循環,只不過接著的狀態不是1而是上一個循環0之前的狀態數+1。

看完數字再看後兩行,即第三行和第四行,其中第三行是音素,第四行是單詞,<eps>代表單詞間的空格,如CAUSE是由頭三個音素組成K_B AH_I Z_E組成,因此除了第一行是單詞CAUSE外,後面兩個接著的都是<eps>。至此我們知道一個從0開始到0結束的閉環代表一個單詞的音素狀態轉移。我們可以由一連串的音素閉環輸入得到一個對應的單詞輸出,即詞典的功能。

我們還可以使用fstcompile將text描述性質的fst轉換成二進位形式:

fstcompile --isymbols=phone.txt --osymbols=word.txt text_format.fst binary.fst

生成的二進位文件可以使用fstdraw可視化,結果就是上面的圖:

fstdraw --isymbols=phone.txt --osymbols=word.txt binary.fst | dot -Tjpg > fst.jpg

上面對L.fst進行了詳細的介紹,可以看到,轉換到fst的文件,其功能並未發生改變,只是其本身結構發生了變換-變成了狀態轉換圖的形式了,因此對於G.fst也可以用同樣的眼光看待,它只不過是轉換為fst格式的語言模型而已,畫出來圖形仍然是與L.fst類似的形式。

建立phone-graph

與建立word-graph的代碼極為相似,不同的就是把lexicon.txt和lexiconp.txt裏的單詞換成音素標記,中間一行依舊是概率,最後一行是該音素的發音(因此對於大部分音素第一行與第三行是相同的),代碼如下:

(
echo "make phone graph ..."
cd $H; mkdir -p data/{dict_phone,graph_phone,lang_phone} &&
cp $thchs/resource/dict/{extra_questions.txt,nonsilence_phones.txt,optional_silence.txt, silence_phones.txt} data/dict_phone &&
cat $thchs/data_thchs30/lm_phone/lexicon.txt | grep -v | sort -u > data/dict_phone/ lexicon.txt &&
echo " sil " >> data/dict_phone/lexicon.txt || exit 1;
utils/prepare_lang.sh --position_dependent_phones false data/dict_phone "" data/local/lang_phone data/lang_phone || exit 1;
gzip -c $thchs/data_thchs30/lm_phone/phone.3gram.lm > data/graph_phone/phone.3gram.lm.gz || exit 1;
utils/format_lm.sh data/lang_phone data/graph_phone/phone.3gram.lm.gz $thchs/data_thchs30/ lm_phone/lexicon.txt
data/graph_phone/lang || exit 1;
)

因為其使用的也是utils/prepare_lang.sh 和 format_lm.sh,因此它的目的還是建立L.fst和G.fst文件。只不過L.FST的輸入是音素,輸出也是音素了。二G.fst文件則對應著音素的3-gram語言模型的fst文件。

參考

Kaldi學習筆記 -- 構建字典FST腳本 -- prepare_lang.sh 關鍵內容解析

kaldi學習筆記 -- 構造語言模型相關腳本 -- ami_train_lms.sh,utils/format_lm.sh


推薦閱讀:
相關文章