“lang”目录下的内容
首先是有文件phones.txt和words.txt。这些都是符号表(symbol-table)文件,符合OpenFst的格式定义。其中每一行首先是一个文本项,接着是一个数字项:
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是FST形式的发音字典(L,见 "Speech Recognition with Weighted Finite-State Transducers" by Mohri, Pereira and Riley, in Springer Handbook on SpeechProcessing and Speech Communication, 2008), 其中,输入是音素,输出是词。文件L_disambig.fst也是发音字典,但是还包含了为消歧而引入的符号,诸如#1、 #2 之类,以及为自环(self-loop)而引入的 #0 。 #0 能让消岐符号“通过”(pass through)整个语法(译者注:n元语法,即我们的语言模型。 另外前面这句话我是在不知道该怎么翻译)。更多解释见 \ref graph_disambig 。但是不管 明白与否,你其实不用自己手动去引入这些符号。
文件data/lang/oov.txt仅仅只有一行:
s5# cat data/lang/oov.txt <UNK>
在训练过程中,所有词汇表以外的词都会被映射为这个词(译者注:UNK即unknown)。“”本身并没有特殊的地方,也不一定非要用这个词。重要的是需要保证这个词 的发音只包含一个被指定为“垃圾音素”(garbage phone)的音素。该音素会与各种 口语噪声对齐。在我们的这个特别设置中,该音素被称为<SPN>, 就是“spoken noise”的缩写:
s5# grep -w UNK data/local/dict/lexicon.txt <UNK> SPN
文件oov.int则是SPN的数字形式(从words.txt中提取的),在本设置中是221。你可能已经注意到了,在Resource Management设置中,oov.txt里有一个 静音词,在RM设置中被称为“!SIL”。在这种情况下,我们从词汇表中任意选一个词(放入oov.txt)—— 因为训练集中没有oov词,所以选哪个都不起作用。
文件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>
这个文件指明了我们所用HMM模型的拓扑结构。在这个例子中,一个“真正”的音素内含3个发射状态,呈标准 的三状态从左到右拓扑结构——即“Bakis”模型。发射状态即能“发射”特征矢量的状态,与之对应的就是那些 “假”的仅用于连接其他状态的非发射状态。音素1到20是各种静音和噪声。之所以会有这么多, 是因为低词中的不同位置相同音素进行了区分。这种情况下,实际上这里的静音和噪声音素大多数都用不上。 不考虑在词中的位置的话,应该只有5个代表静音和噪声的音素。所谓静音音素(silence phones)有更复杂的 拓扑结构。每个静音音素都有一个起始发射状态和一个结束发射状态,中间还有另外三个发射状态。 你不用手动创建 data/lang/topo 。
data/lang/phones/ 下有一系列的文件,指明了音素集合的各种信息。这些文件大多数有三个不同版本:一个 “.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状态。这依赖于roots.txt,下面将会介绍到。关于决策树的更深入讨论见 \ref tree_externals 。
文件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则表示一种被视为“单独词”(singleton word)的静音,意指这个音素 只对应一个词——当你的发音字典中有“静音词”且标注中有明确的静音段时会有用。
silence.txt 和nonsilence.txt分别包含静音音素列表和非静音音素列表。 这两个集合是互斥的,且如果合并在一起,应该是音素的总集。在本例中,silence.txt与context_indep.txt的内容完全一致。 我们说“非静音”音素,是指我们将要估计各种线性变换的音素。所谓线性变换是指全局变换,如LDA 和MLLT,以及说话人自适应变换,如fMLLR。 根据之前的实验,我们相信,加入静音对这些变换没有 影响。我们的经验是,把噪声和发生噪声都列为“静音”音素,而其他传统的音素则是“非静音”音素。 在Kaldi中我们还没有通过实验找到一个最佳的方法来这样做。
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包含一个“消岐符号”列表“(见 \ref graph_disambig):
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中。这个原因比较复杂,这里就不讲了。
文件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 包含那些自动产生的问题集之外的一些问题:
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
你可以看到,所谓一个问题就是一组音素。 前4个问题是关于普通音素的词位信息,后面五个则是关于”静音音素“的。 ”静音“音素也可能没有像_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
这和音素中的后缀是相同的信息,但我们并不想给音素的文本形式附加这样的强制限制——记住一件事, Kaldi的可执行程序从不使用音素的文本形式,而是整数形式。)所以我们使用文件word_boundary.txt 来指明各音素与词位间的对应关系。建立这种对应关系的原因是因为我们需要这些信息从音素网格中恢复词的边界(例如,lattice-align-words 需要读取word_boundary.txt的整数版本word_boundary.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“和”split“——这些与我们建立决策树时的具体选项有关(更多信息见 \ref tree_externals)。 像SIL SIL_B SIL_E SIL_I SIL_S 这样,几个音素出现在同一行的意义是, 在决策树中它们都有同一个”共享根“(shared root), 因此状态可在这些音素间共享。 对于带语气语调的系统,通常所有与语气和语调相关的音素变体都会出现在同一行。此外, 一个HMM中的3个状态(对静音来说有5个状态)共享一个根, 且决策树的建立过程需要知道 状态(的共享情况)。 HMM状态间共享决策树根节点,这就是roots文件中”shared“代表的意思。