Kaldi學習之數據準備詳細解釋說明

文章對Kaldi數據準備做更詳細的解釋,如有錯誤,還請指正。
數據基本源自Kaldi官網:http://www.kaldi-asr.org/doc/data_prep.html

數據準備詳細介紹
在run.sh中有數據準備各個階段的腳本。
例子中的local/文件夾下是數據準備專用的一些東西。
比如RM下的腳本run.sh部分內容:

local/rm_data_prep.sh /export/corpora5/LDC/LDC93S3A/rm_comp || exit 1;

utils/prepare_lang.sh data/local/dict '!SIL' data/local/lang data/lang || exit 1;

local/rm_prepare_grammar.sh || exit 1;

又比如WSJ下的腳本run.sh的部分內容:

wsj0=/export/corpora5/LDC/LDC93S6B
wsj1=/export/corpora5/LDC/LDC94S13B

local/wsj_data_prep.sh $wsj0/??-{?,??}.? $wsj1/??-{?,??}.?  || exit 1;

local/wsj_prepare_dict.sh || exit 1;

utils/prepare_lang.sh data/local/dict "<SPOKEN_NOISE>" data/local/lang_tmp data/lang || exit 1;

local/wsj_format_data.sh || exit 1;

WSJ涉及到準備本地語言模型腳本的腳本之後還有更多的命令,但是之前三條是更加重要的命令。
數據準備階段的輸出包括了兩樣東西集合。一個是“數據”(像/data/train這種目錄),一個是“語言”(像/data/lang目錄)。數據部分就是相關的語音數據,語言部分包含更多的是語言本身,比如字典,音素集合和一些其他的信息比如Kaldi需要的音素集。如果想準備數據,用當前系統現有的解碼器和現存的語言模型,那麼只需要接觸“數據”部分。

數據準備之“數據”部分
看一個Switchboard的train部分例子(train和test本質上結構是一樣的,只是用途不一樣)。
例子目錄egs/swbd/s5。裏面擁有的文件如下:

s5# ls data/train
cmvn.scp  feats.scp  reco2file_and_channel  segments  spk2utt  text  utt2spk  wav.scp

不是所有的文件都是同等重要。簡單數據集是沒有分割的信息的。自己必須創建的文件是
Utt2spk text wav.scp ,有時候需要創建 segments 和 reco2file_and_channel ,剩下的可由標準腳本創建。

需要自己創建的文件
text 文件樣例:

s5# head -3 data/train/text
sw02001-A_000098-001156 HI UM YEAH I'D LIKE TO TALK ABOUT HOW YOU DRESS FOR WORK AND
sw02001-A_001980-002131 UM-HUM
sw02001-A_002736-002893 AND IS

格式:


語音-id 標註

語音-id可以是任意字符串,如果有說話人信息,則說話人-id是語音-id的前綴。無需保證文件內的所有詞都在詞彙表中,詞的輸出會映射到專門的一個詞文件中,data/lang/oov.txt。

要注意utt2spk和spk2utt這兩個文件的排序順序的一致性。比如,從utt2spk(語音-id所對應的說話人-id)文件提取的說話人-id列表和字符串的排序順序一樣。最簡單的做法就是說話人-id作爲語音-id的前綴,一般用”-“分隔。如果說話人-id長度不一致,在某些情況下,用標準的C字符串排序時,說話人-id和其對應的語音-id可能以不同的順序進行排序/分類,這可能導致崩潰的情況。

另外一個重要文件就是 wav.scp
swbd例子:

s5# head -3 data/train/wav.scp
sw02001-A /home/dpovey/kaldi-trunk/tools/sph2pipe_v2.5/sph2pipe -f wav -p -c 1 /export/corpora3/LDC/LDC97S62/swb1/sw02001.sph |
sw02001-B /home/dpovey/kaldi-trunk/tools/sph2pipe_v2.5/sph2pipe -f wav -p -c 2 /export/corpora3/LDC/LDC97S62/swb1/sw02001.sph |

文件格式是:

<recording-id> <extended-filename>

extended-filename 可能是確切的文件名,或者是提取wav格式文件的命令。extended-filename 最後的管道符號意味着它將被解讀成管道。如果分割文件不存在,那麼wav.scp每行的第一個字符就是語音-id。wav.scp必須是單聲道的,如果底層語音文件有多個聲道,就必須有一個短命令在wav.scp中要用到來提取一個特殊的通道。

比如在swbd中有“segments”文件,如下:

s5# head -3 data/train/segments
sw02001-A_000098-001156 sw02001-A 0.98 11.56
sw02001-A_001980-002131 sw02001-A 19.8 21.31
sw02001-A_002736-002893 sw02001-A 27.36 28.93

格式是:

<utterance-id> <recording-id> <segment-begin> <segment-end>

即 語音-id 記錄-id 分割開始時間 分割結束時間
分割的開始和結束單位是 秒。
記錄-id是wav.scp中使用的相同的標識,也是可以自己選擇的任意的標識。文件”reco2file_and_channel”只在打分(測量錯誤率)的時候用到,用NIST的”sclite”工具。

s5# head -3 data/train/reco2file_and_channel
sw02001-A sw02001 A
sw02001-B sw02001 B
sw02005-A sw02005 A

格式爲

<recording-id> <filename> <recording-side (A or B)>

filename是.sph文件的名字,沒有後綴,但是一般來說它是stm文件中的任何標識符。recording-side是一個電話對話中兩個聲道的概念,如果不是的話,用A更安全。如果沒有stm文件或者不知道幹啥的,record2file_and_channel文件就沒必要創建。

最後一個需要自己創建的文件是”utt2spk”文件。

s5# head -3 data/train/utt2spk
sw02001-A_000098-001156 2001-A
sw02001-A_001980-002131 2001-A
sw02001-A_002736-002893 2001-A

格式:

<utterance-id> <speaker-id>

說話人-id不需要精確地對應每一個說話人,有個大概的猜測就好。可能會出現這樣的情況:打電話的一方說了幾句話之後可能會拿電話給另外一個人。如果沒有說話人標識的相關信息,可以使說話人-id和語音-id相同。因爲有人創建了”global”說話人-id(所有語音只對應一個說話人)。這樣不好的原因:它使得cmn在訓練的時候無效(因爲被全局應用,一般CMN都是在句子內或者說一條語音內減去特徵的均值),而且在使用utils/split_data_dir.sh分割數據的時候會引起問題。

在一些數據集中還存在其它的文件,偶爾出現。比如性別相關的:

s5# head -3 ../../rm/s5/data/train/spk2gender
adg0 f
ahh0 m
ajp0 m

所有的文件都要排序。未排序會有錯誤。排序的最終原因就是使一些不支持fseek()函數的數據流也能夠達到隨機訪問的效果。許多Kaldi程序從其它Kaldi命令中讀取很多管道,讀不同類型的對象,並且和合並排序的不同的輸入做一些大致的比較。

export LC_ALL=C

如果做了這個命令,就和C++排序不一樣,Kaldi會崩潰。

如果數據有NIST來的測試集組成,有”stm”和”glm”文件,使用local/score.sh腳本文件可以測試WER。swbd集有用到這樣的打分文件,比如
egs/swbd/s5/local/score_sclite.sh就被egs/swbd/s5/local/score.sh調用。

無需自己創建的文件
除了上述的其它文件都可由kaldi提供的命令生成。

比如utt2spk轉換成spk2utt

utils/utt2spk_to_spk2utt.pl data/train/utt2spk > data/train/spk2utt

因爲utt2spk和spk2utt具有相同的信息
utt2spk 格式 :

<utterance-id><speaker-id>

spk2utt 格式 :

<spaker-id><utterance-id>

feats.scp文件格式

s5# head -3 data/train/feats.scp
sw02001-A_000098-001156 /home/dpovey/kaldi-trunk/egs/swbd/s5/mfcc/raw_mfcc_train.1.ark:24
sw02001-A_001980-002131 /home/dpovey/kaldi-trunk/egs/swbd/s5/mfcc/raw_mfcc_train.1.ark:54975
sw02001-A_002736-002893 /home/dpovey/kaldi-trunk/egs/swbd/s5/mfcc/raw_mfcc_train.1.ark:62762

這個文件指向提取的特徵——swbd例子中是MFCC特徵,因爲用了make_mfcc.sh腳本。

feats.scp格式如下:

<utterance-id> <extended-filename-of-features>

在Kaldi中每個特徵文件都包含一個矩陣。swbd例中矩陣的維度將會是13維(文件以10s作爲間隔的長度)。
擴展文件名/home/dpovey/kaldi-trunk/egs/swbd/s5/mfcc/raw_mfcc_train.1.ark:24 意思是,打開ark文件,通過fseek()文件找到第24個位置,從這開始讀數據。

feat.scp通過以下命令創建:

steps/make_mfcc.sh --nj 20 --cmd "$train_cmd" data/train exp/make_mfcc/train $mfccdir

這個腳本被頂層的run.sh腳本調用。Shell變量的定義可查看腳本說明。$mfccdir是用戶指定的目錄,.ark文件會寫入到這個目錄中。

dnn/train目錄的最後一個文件是”cmvn.scp”文件。這個文件包含倒譜均值和方差規整的數據,通過說話人-id進行索引。每組統計量都是一個矩陣,swbd例中是維數2*14?
swbd例中,看cmvn.scp中部分內容:

s5# head -3 data/train/cmvn.scp
2001-A /home/dpovey/kaldi-trunk/egs/swbd/s5/mfcc/cmvn_train.ark:7
2001-B /home/dpovey/kaldi-trunk/egs/swbd/s5/mfcc/cmvn_train.ark:253
2005-A /home/dpovey/kaldi-trunk/egs/swbd/s5/mfcc/cmvn_train.ark:499

和feats.scp不同的是,這個文件是通過說話人-id進行索引的,本文件通過以下命令創建:

steps/compute_cmvn_stats.sh data/train exp/make_mfcc/train $mfccdir

本例來源:egs/swbd/s5/run.sh

爲了防止數據準備(不符合條件)引發後續的問題,有工具檢查腳本是否符合格式規範:

utils/validate_data_dir.sh data/train

尤其是以下命令,可以修正排序錯誤,還會移除一些所需數據(比如特徵數據或者腳本)丟失時候的語音。

utils/fix_data_dir.sh data/train

語言模型準備部分
首先需要準備dict字典,包括
lexicon.txt:包括語料中涉及的詞彙與發音,與單字及其發音
lexicon_nosil.txt:同上,去掉靜音部分
phones.txt:音素髮音標識

然後需要使用srilm的ngram-count命令生成詞彙的arpa格式文件

ngram-count -order 1 -text words.txt -lm word.arpa

ngram-count部分參數解釋如下:
-order 指定n-gram的n是多少(n代表n元語言模型),默認是3
-text 提供輸入的語料文件,統計該語料中的n-gram
-lm 指定輸出的lm文件
-vocab 用來指定對哪些詞進行n-gram統計
-wbdiscount1 表示1gram Witten-Bell discounting
注意:參數順序無所謂

arpa文件的使用:
使用arpa2fst命令進行轉換,轉換成符合FST規範的文件。;
如若不然,prepare_lang.sh會使用一些自帶的fst開頭的程序生成.fst文件。

語言模型準備部分詳述
轉到”lang”目錄,看目錄下面的文件

s5# ls data/lang
L.fst  L_disambig.fst  oov.int  oov.txt  phones  phones.txt  topo  words.txt

還有很多相似的格式,比如在data/lang_test就包含了相同的內容,還多了一個G.fst(Grammer)語言模型的有限狀態轉換器格式。

s5# ls data/lang_test
G.fst  L.fst  L_disambig.fst  oov.int  oov.txt  phones  phones.txt  topo  words.txt

lang_test是通過複製lang/下面的內容,並且增加G.fst生成的。這些目錄包含幾個文件之外還包含幾個文件夾。比如phones就是一個文件夾:

s5# ls data/lang/phones
context_indep.csl  disambig.txt         nonsilence.txt        roots.txt    silence.txt
context_indep.int  extra_questions.int  optional_silence.csl  sets.int     word_boundary.int
context_indep.txt  extra_questions.txt  optional_silence.int  sets.txt     word_boundary.txt
disambig.csl       nonsilence.csl       optional_silence.txt  silence.csl

音素目錄包含很多音素集合的不同信息;有三個不同文件版本,擴展名包括.csl,.int和.txt,三種格式包含了相同信息,不需要全部創建它。因爲通過一個簡單的輸入,腳本utils/prepare_lang.sh就會輸出這些文件。接下來解釋lang目錄。不想了解Kaldi到底怎麼工作的原理可以直接跳到Creating the “lang” directory部分


lang目錄的內容
首先是phones.txtwords.txt的內容,這兩個都是字符表文件,用OpenFst格式,每行都是文本形式 int形式組合。

s5# head -3 data/lang/phones.txt
<eps> 0
SIL 1
SIL_B 2
s5# head -3 data/lang/words.txt
<eps> 0
!SIL 1
-'S 2

這些文件被Kaldi用來在這些符號的整數和文本形式之間來回映射。 它們大多隻能通過腳本utils / int2sym.pl和utils / sym2int.pl以及OpenFst程序fstcompile和fstprint訪問。

文件L.fst是字典的有限狀態轉換器形式(L,參見Mohri,Pereira和Riley的論文:
“Speech Recognition with Weighted Finite-State Transducers”
Springer Handbook on SpeechProcessing and Speech Communication,2008)。 輸入是音素符號和輸出是字詞符號。 文件L_disambig.fst是詞典,但是和上面說的詞典比多了消歧符號#1,#2等,以及帶有#0的自循環,以從語法中“消除”歧義符號。 參閱Disambiguation symbols獲得消歧符號更多的解釋。這個文件不需要自己處理。

文件data/lang/oov.txt只包含一行內容

s5# cat data/lang/oov.txt
<UNK>

在訓練的時候會映射所有詞彙中的詞到這個詞中。<UNK>在這裏沒有什麼特別,它不是這個文件的特定詞;重要的是這個詞要有一個發音,這個發音只包含一個我們指定爲“垃圾音素”的音素;這個音素將與各種說話的噪音對齊。 在Kaldi設置中,這個音素被稱爲“SPN”(“spoken noise”)),比如在lexicon.txt文件中:

s5# grep -w UNK data/local/dict/lexicon.txt
<UNK> SPN

文件oov.int包括上述內容的int形式(從words.txt中提取),在此設置中恰好爲221。 可能會注意到,在資源管理設置中,oov.txt包含靜音詞,該設置恰好稱爲“!SIL”。 在這種情況下(SIL),Kaldi只是從詞彙表中選擇一個任意的單詞——訓練集中沒有這樣的詞,所以Kaldi選擇什麼詞都沒有影響。

文件data/lang/topo中包含以下數據:

s5# cat data/lang/topo
<Topology>
<TopologyEntry>
<ForPhones>
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
</ForPhones>
<State> 0 <PdfClass> 0 <Transition> 0 0.75 <Transition> 1 0.25 </State>
<State> 1 <PdfClass> 1 <Transition> 1 0.75 <Transition> 2 0.25 </State>
<State> 2 <PdfClass> 2 <Transition> 2 0.75 <Transition> 3 0.25 </State>
<State> 3 </State>
</TopologyEntry>
<TopologyEntry>
<ForPhones>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
</ForPhones>
<State> 0 <PdfClass> 0 <Transition> 0 0.25 <Transition> 1 0.25 <Transition> 2 0.25 <Transition> 3 0.25 </State>
<State> 1 <PdfClass> 1 <Transition> 1 0.25 <Transition> 2 0.25 <Transition> 3 0.25 <Transition> 4 0.25 </State>
<State> 2 <PdfClass> 2 <Transition> 1 0.25 <Transition> 2 0.25 <Transition> 3 0.25 <Transition> 4 0.25 </State>
<State> 3 <PdfClass> 3 <Transition> 1 0.25 <Transition> 2 0.25 <Transition> 3 0.25 <Transition> 4 0.25 </State>
<State> 4 <PdfClass> 4 <Transition> 4 0.75 <Transition> 5 0.25 </State>
<State> 5 </State>
</TopologyEntry>
</Topology>

這是Kaldi使用的HMM拓撲。在這種情況下,“真實”音素包含三種標準的從左到右3狀態的拓撲結構的發射狀態——叫“Bakis模型” (發射狀態是“發射”特徵向量的狀態,與僅用於將其他狀態連接在一起的“假的”非發射狀態不同)。 音素1到20是各種靜音和噪音; 有很多靜音和噪音是因爲字位依賴,事實上大多數的靜音和噪聲永遠都用不上; 不包括字位依賴的狀態數是5個左右。 “靜音音素”具有更復雜的拓撲結構,具有初始發射狀態和結束髮射狀態,但在中間是三個發射狀態。 不需要手動創建此文件。

data/lang/phones文件夾中有幾個存放關於音素集的不同信息的文件。這些文件大多數都有三個不同的版本,分別是.txt .int 和 .csl版本,內容分別如下
.txt

s5# head -3 data/lang/phones/context_indep.txt
SIL
SIL_B
SIL_E

.int

s5# head -3 data/lang/phones/context_indep.int
1
2
3

.csl,是一個冒號分隔的列表

s5# cat data/lang/phones/context_indep.csl
1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17:18:19:20

這些文件總是包含相同的信息,所以集中看可讀性最強的.txt格式內容。文件”context_indep.txt”包含我們要建立上下文模型的音素列表:對於這些音素,不需要建立一個決策樹來提出音素左右上下文的問題。實際上,關於中心音素和HMM狀態的問題Kaldi建立了一個小得多的樹來實現它;這取決於下面要說的root.txt文件。關於樹的問題,想要更深入瞭解的可以查看 How decision trees are used in Kaldi

context_indep.txt包含了非“真實音素”的所有音素,比如靜音(SIL),說話人噪聲(SPN),非說話人噪聲(NSN),和笑聲(LAU):

# cat data/lang/phones/context_indep.txt
SIL
SIL_B
SIL_E
SIL_I
SIL_S
SPN
SPN_B
SPN_E
SPN_I
SPN_S
NSN
NSN_B
NSN_E
NSN_I
NSN_S
LAU
LAU_B
LAU_E
LAU_I
LAU_S

由於字位依賴(字所在的位置有多種組合),這些音素有很多變體;不是所有的這些變種都不會被使用。在這裏,SIL被選擇性地插入字典(不是單詞的一部分),SIL_B是一個單詞開頭的靜音音素(不應該存在),SIL_I字內的靜音(不太可能存在) ),SIL_E是結尾的靜音(永遠不會存在),SIL_S將作爲一個“單個”的靜音,即只有一個字的音素—— 如果詞典中有“靜音詞”,則可能會在轉錄文件中出現明顯的靜音。

文件silence.txtnonsilence.txt分別包含靜音/噪聲音素和非靜音/噪聲音素的列表。這些應該互斥,應該包含所有的音素。在這個特定的設置中,silence.txt與context_indep.txt相同。 “nonsilence”音素的意思是,Kaldi打算預測這些音素上的各種線性變換:即LDA和MLLT等全局變換,以及諸如fMLLR之類的說話人自適應變換。Kaldi基於以往經驗的信念是,在預測這種變換中不包括任何silence音素。Kaldi的做法是將所有靜音,噪音和發聲的噪聲音素指定爲“silence”音素,所有代表傳統音素的音素都是“nonsilence”音素。

s5# head -3 data/lang/phones/silence.txt
SIL
SIL_B
SIL_E
s5# head -3 data/lang/phones/nonsilence.txt
IY_B
IY_E
IY_I

文件disambig.txt包括“消歧符號”(看Disambiguation symbols

s5# head -3 data/lang/phones/disambig.txt
#0
#1
#2

如果這些符號是音素,它們會在phones.txt出現

文件optional_silence.txt 包括能夠有選擇性地在詞之間出現的單音素

s5# cat data/lang/phones/optional_silence.txt
SIL

在詞之間可以選擇性出現的機制是,在字典的FST中,它可以任意地在每個單詞結尾(語音的開始)中出現。必須在phones/指定,而不是僅出現在L.fst中的原因Kaldi也未說明,不在這裏細說。

文件sets.txt包括組合的音素集合(考慮是相同的音素),同時對音素進行聚類來創造上下文相關性問題(Kaldi中在構造決策樹時使用自動生成的問題,而不是語言的有意義的問題)。這種設定下,sets.txt對每個音素的所有字位依賴版本做組合:

s5# head -3 data/lang/phones/sets.txt
SIL SIL_B SIL_E SIL_I SIL_S
SPN SPN_B SPN_E SPN_I SPN_S
NSN NSN_B NSN_E NSN_I NSN_S

文件extra_questions.txt包括一些Kaldi需要包括的其它(除了自動生成問題之外)的問題

s5# cat data/lang/phones/extra_questions.txt
IY_B B_B D_B F_B G_B K_B SH_B L_B M_B N_B OW_B AA_B TH_B P_B OY_B R_B UH_B AE_B S_B T_B AH_B V_B W_B Y_B Z_B CH_B AO_B DH_B UW_B ZH_B EH_B AW_B AX_B EL_B AY_B EN_B HH_B ER_B IH_B JH_B EY_B NG_B
IY_E B_E D_E F_E G_E K_E SH_E L_E M_E N_E OW_E AA_E TH_E P_E OY_E R_E UH_E AE_E S_E T_E AH_E V_E W_E Y_E Z_E CH_E AO_E DH_E UW_E ZH_E EH_E AW_E AX_E EL_E AY_E EN_E HH_E ER_E IH_E JH_E EY_E NG_E
IY_I B_I D_I F_I G_I K_I SH_I L_I M_I N_I OW_I AA_I TH_I P_I OY_I R_I UH_I AE_I S_I T_I AH_I V_I W_I Y_I Z_I CH_I AO_I DH_I UW_I ZH_I EH_I AW_I AX_I EL_I AY_I EN_I HH_I ER_I IH_I JH_I EY_I NG_I
IY_S B_S D_S F_S G_S K_S SH_S L_S M_S N_S OW_S AA_S TH_S P_S OY_S R_S UH_S AE_S S_S T_S AH_S V_S W_S Y_S Z_S CH_S AO_S DH_S UW_S ZH_S EH_S AW_S AX_S EL_S AY_S EN_S HH_S ER_S IH_S JH_S EY_S NG_S
SIL SPN NSN LAU
SIL_B SPN_B NSN_B LAU_B
SIL_E SPN_E NSN_E LAU_E
SIL_I SPN_I NSN_I LAU_I
SIL_S SPN_S NSN_S LAU_S

可以看到這個問題就是一個簡單的音素集合。前四個問題是詢問常規音素的字位; 最後五個對於“靜音”操作是一樣的。 “靜音”音素也會出現一個不帶_B字樣的後綴,例如SIL。 這些可能在詞典中表現爲可選的靜音,比如,不在實際的單詞內。 在使用諸如音調依賴或重音標記的設置中,extra_questions.txt可能包含與這些功能相關的問題。

文件word_boundary.txt解釋了音素如何和字位進行關聯:

s5# head  data/lang/phones/word_boundary.txt
SIL nonword
SIL_B begin
SIL_E end
SIL_I internal
SIL_S singleton
SPN nonword
SPN_B begin

這是與音素後綴相同的信息(_B等等),但是我們不喜歡在音素的文本形式中對這個進行硬編碼,首先,Kaldi的可執行文件永遠都不會看到音素的文本形式,只能看到整數形式。 所由文件word_boundary.txt指定。 需要這個信息的主要原因是爲了恢復詞圖內的單詞邊界(例如,程序lattice-align-words讀取此文件的整數形式,word_boundaray.int)。 尋找單詞邊界非常有用,原因包括NIST sclite評分,這需要時間標記的單詞和其他下游處理。

文件roots.txt包括關於如何建立音素上下文決策樹的信息:

head data/lang/phones/roots.txt
shared split SIL SIL_B SIL_E SIL_I SIL_S
shared split SPN SPN_B SPN_E SPN_I SPN_S
shared split NSN NSN_B NSN_E NSN_I NSN_S
shared split LAU LAU_B LAU_E LAU_I LAU_S
...
shared split B_B B_E B_I B_S

現在可以忽略關鍵字”shared” and “split”——這些跟如何建立決策樹的選項相關(更多信息看How decision trees are used in Kaldi )。每行有幾個音素的意義,比如SIL SIL_B SIL_E SIL_I SIL_S,是因爲這些音素在決策樹中都有相同的“共享根節點”,所以這些音素的狀態可能也是共享的。對於重音和音調相關係統,通常重音或音調相關的所有版本都會出現在同一行。另外,HMM的所有3個狀態(對於靜音來說是5個)都共享根,決策樹的構造過程中可以詢問狀態。HMM狀態之間決策樹的根共享就是在roots文件中”shared”的意思。

創建 lang 文件夾
data/lang/ 目錄包含一些不同的文件,所以提供了腳本來創建這些文件,可以用來從一些簡單的輸入入手:

utils/prepare_lang.sh data/local/dict "<UNK>" data/local/lang data/lang

輸入是目錄 data/local/dict/,標籤 <UNK> 是字典裏的單詞,映射OOV單詞到出現在副本中的單詞中(data/lang/oov.txt)。目錄data/local/lang/是腳本會用到的臨時目錄;data/lang/是輸出目錄。

作爲數據準備者需要創建的是目錄data/local/dict/。此目錄包含以下內容:

s5# ls data/local/dict
extra_questions.txt  lexicon.txt nonsilence_phones.txt  optional_silence.txt  silence_phones.txt

(實際上還有幾個沒有列出來的文件,但是它們只是創建目錄放進去的臨時文件,可以忽略它們)。下面的命令對這些文件內容有些瞭解:

s5# head -3 data/local/dict/nonsilence_phones.txt
IY
B
D
s5# cat data/local/dict/silence_phones.txt
SIL
SPN
NSN
LAU
s5# cat data/local/dict/extra_questions.txt
s5# head -5 data/local/dict/lexicon.txt
!SIL SIL
-'S S
-'S Z
-'T K UH D EN T
-1K W AH N K EY

可以看到,目錄的內容在這個例子(Switchboard)的設置中很簡單。只有分開的“真實”音素和“靜音”音素的列表,還有個空文件extra_questions.txt,另外還有個叫lexicon.txt 的字典文件,文件有以下形式:

<word> <phone1> <phone2> ...

注意:lexicon.txt中同一個詞如果有不同發音的話,可能會包含多個重複的條目,分佈在不同的行。如果想使用發音概率,創建lexiconp.txt(第二個字段是概率)代替原來的lexicon.txt。注意一般都要對發音概率做規範化,而不是對一個詞求和,每個單詞最有可能的發音都是一個。這樣會得到更好的結果。對於以發音概率跑實驗的頂層腳本,在egs/wsj/s5/run.sh中找關鍵字”pp”。

請注意,在該輸入中,沒有字位置依賴性的概念,即沒有後綴如_B和_E。 這是因爲添加這些後綴是腳本prepare_lang.sh才做的。

可以從空文件extra_questions.txt中看到,這裏還有一些沒有被充分挖掘的東西。 這涉及重音標記或音調標記的一些東西。 用戶可能會需要具有不同重音或音調的特定音素的不同版本。 爲了演示這樣的內容,查看與上述相同的文件,但在egs / wsj / s5 / setup中。 結果如下:

s5# cat data/local/dict/silence_phones.txt
SIL
SPN
NSN
s5# head data/local/dict/nonsilence_phones.txt
S
UW UW0 UW1 UW2
T
N
K
Y
Z
AO AO0 AO1 AO2
AY AY0 AY1 AY2
SH
s5# head -6 data/local/dict/lexicon.txt
!SIL SIL
<SPOKEN_NOISE> SPN
<UNK> SPN
<NOISE> NSN
!EXCLAMATION-POINT  EH2 K S K L AH0 M EY1 SH AH0 N P OY2 N T
"CLOSE-QUOTE  K L OW1 Z K W OW1 T
s5# cat data/local/dict/extra_questions.txt
SIL SPN NSN
S UW T N K Y Z AO AY SH W NG EY B CH OY JH D ZH G UH F V ER AA IH M DH L AH P OW AW HH AE R TH IY EH
UW1 AO1 AY1 EY1 OY1 UH1 ER1 AA1 IH1 AH1 OW1 AW1 AE1 IY1 EH1
UW0 AO0 AY0 EY0 OY0 UH0 ER0 AA0 IH0 AH0 OW0 AW0 AE0 IY0 EH0
UW2 AO2 AY2 EY2 OY2 UH2 ER2 AA2 IH2 AH2 OW2 AW2 AE2 IY2 EH2

可以看到nonsilence_phones.txt裏面在單行中包括了多個音素的行有很多。這些是元音的不同重音相關版本。注意,CMU字典中每個音素都出現了四個不同版本:例如,UW UW0 UW1 UW2;由於某些原因,其中一個版本沒有數字後綴。音素在一行的順序並不重要,但分組到不同行的時候就很重要了;一般來說,Kaldi建議用戶“真實音素”分組的不同形式在不同行。Kaldi使用CMU字典中存在的重音標記。文件extra_questions.txt包括一個包含所有“silence”音素的問題(實際上這是不必要的,因爲腳本prepare_lang.sh會添加這樣的問題),還有一個與每個不同的重音標記相對應的問題。爲了從重音標記得到更好的結果,這些問題是必要的,因爲實際上每個音素的不同重音相關版本都統一放在nonsilence_phones.txt文件行中,確保他們都在文件data/lang/phones/roots.txt 和data/lang/phones/sets.txt中,這反過來又確保它們共享相同的樹根結點,並且永遠不能被一個問題所區分。因此,我們必須提供一個特殊的問題,使決策樹建立過程能夠區分音素。注意:我們將音素放在sets.txt和roots.txt中的原因是,一些重音相關版本的音素可能沒有足夠的數據可靠地估計單獨的決策樹或生成問題時用到的音素聚類信息。通過將它們分組在一起,我們確保在沒有足夠的數據的情況下分別預測它們,這些不同版本的音素在整個決策樹構建過程中都“保持在一起”。

還有一個要提出的點事是腳本utils/prepare_lang.sh支持幾個選項。以下是腳本的使用信息,可以看看這些選項到底是什麼:

usage: utils/prepare_lang.sh <dict-src-dir> <oov-dict-entry> <tmp-dir> <lang-dir>
e.g.: utils/prepare_lang.sh data/local/dict <SPOKEN_NOISE> data/local/lang data/lang
options:
     --num-sil-states <number of states>             # default: 5, #states in silence models.
     --num-nonsil-states <number of states>          # default: 3, #states in non-silence models.
     --position-dependent-phones (true|false)        # default: true; if true, use _B, _E, _S & _I
                                                     # markers on phones to indicate word-internal positions.
     --share-silence-phones (true|false)             # default: false; if true, share pdfs of
                                                     # all non-silence phones.
     --sil-prob <probability of silence>             # default: 0.5 [must have 0 < silprob < 1]

一個很重要的選項是–share-silence-phones選項。默認是false。如果選項是true,所有“silence”音素(比如靜音,發聲噪聲和笑聲)的高斯混合模型的概率密度函數都是共享的,只有這些模型之間的轉移概率不同。不清楚爲什麼有幫助,但是發現這對IARPA的BABEL項目的粵語數據確實是非常有幫助的。這些數據非常混亂,並且有很長一部分沒有轉錄,Kaldi試圖爲此目的與指定的特殊音素對齊。Dan懷疑訓練數據不知道什麼原因無法正確對齊,但是將此選項設置爲true可以改變這種情況。

另外一個很重要的選項是”–sil-prob“選項。總的來說,Dan沒有對這些選項進行實驗,所以不能給出非常詳細的建議。

創建語法的語言模型
上面說的如何創建lang/目錄沒有說如何創建G.fst文件,這個文件是需要解碼的語言模型或者語法的有限狀態轉移機形式。實際上,在一些設置中可能會針對不同的測試目的有多個不同的”lang”目錄,那裏面有不同的語言模型和字典文件。華爾街日報(WSJ)是一個例子:

s5# echo data/lang*
data/lang data/lang_test_bd_fg data/lang_test_bd_tg data/lang_test_bd_tgpr data/lang_test_bg \
 data/lang_test_bg_5k data/lang_test_tg data/lang_test_tg_5k data/lang_test_tgpr data/lang_test_tgpr_5k

創建G.fst的過程根據是否用統計量語言模型或用什麼種類的語法是不同的。在RM設置中有一個bigram語法,它只允許某些單詞對。 在每個語法狀態下,通過分配概率1在這些輸出弧上,把這些單詞對總計到一個?。

local/ rm_data_prep.sh中有一條語句:

local/make_rm_lm.pl $RMROOT/rm1_audio1/rm1/doc/wp_gram.txt  > $tmpdir/G.txt || exit 1;

腳本local/make_rm_lm.pl創建了一個FST格式(文本格式)的語法。它包含了以下形式的行

s5# head data/local/tmp/G.txt
0    1    ADD    ADD    5.19849703126583
0    2    AJAX+S    AJAX+S    5.19849703126583
0    3    APALACHICOLA+S    APALACHICOLA+S    5.19849703126583

OpenFst的更多信息查看www.openfst.org

腳本local/rm_prepare_grammar.sh會把這個文件的文本形式轉換成二進制形式G.fst。命令如下:

fstcompile --isymbols=data/lang/words.txt --osymbols=data/lang/words.txt --keep_isymbols=false \
    --keep_osymbols=false $tmpdir/G.txt > data/lang/G.fst

如果想創建自己的語法,或者會想做一些類似的事情。要注意:這個程序只適用一個固定類的語法:不允許編譯完全上下文自由的語法,因爲這不能以OpenFst形式表示。可以通過WFST框架做到這件事(可以看看Mike Riley 下推轉移機的近期工作),但是Kaldi中還沒有實現這些想法。

在關於語言模型或製作語法FST的列表上有任何問題,請閱讀Joshua Goodman的“A Bit of Progress in Language Modeling” 並訪問www.openfst.org並執行FST教程,以便了解有限狀態轉移機的基礎知識。 (請注意,語言模型將被表示爲有限狀態接收器或FSA,可被視爲有限狀態轉換機的特殊情況)。

腳本utils/format_lm.sh解決把ARPA格式的語言模型轉換成OpenFST格式類型。腳本用法如下:

Usage: utils/format_lm.sh <lang_dir> <arpa-LM> <lexicon> <out_dir>
E.g.: utils/format_lm.sh data/lang data/local/lm/foo.kn.gz data/local/dict/lexicon.txt data/lang_test
Convert ARPA-format language models to FSTs.

這個腳本的一些關鍵命令如下:

gunzip -c $lm \
  | arpa2fst --disambig-symbol=#0 \
             --read-symbol-table=$out_dir/words.txt - $out_dir/G.fst

Kaldi程序arpa2fst將ARPA格式的語言模型轉換成一個加權有限狀態轉移機(實際上是接收機)。

構建語言模型的常用工具時SRILM。不同的語言模型工具都在Kaldi的樣例腳本中用到了。SRILM有最好的文檔和最全面的功能,通常推薦它(唯一的缺點是它沒有最免費的許可證)。 以下是utils / format_lm_sri.sh的使用信息:

Usage: utils/format_lm_sri.sh [options] <lang-dir> <arpa-LM> <out-dir>
E.g.: utils/format_lm_sri.sh data/lang data/local/lm/foo.kn.gz data/lang_test
Converts ARPA-format language models to FSTs. Change the LM vocabulary using SRILM.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章