kaldi学习笔记(三)生成L.fst

kaldi项目新路径:kaldi/egs/xuexi/s5

L.fst是lexicon的WFST格式,L_disambig.fst引入了消歧符号。L.fst的输入音素序列,输出词序列。在这假设大家已经知道WFST,下面我们用一个小lexicon.txt来演示怎么生成L.fst等,词典放在data/local/dict下。

1
2
3
4
5
6
7
8
9
10
11
(base) [lsy@small dict]$ cat lexicon.txt 
SIL sil
<SPOKEN_NOISE> sil
你 n i3
的 d e5
原谅 vv van2 l iang4
也许 ii ie3 x v3
现在 x ian4 z ai4
如果 r u2 g uo3
认识 r en4 sh ix5
会 h ui4

依靠这个字典我们准备以下文件:
nonsilence_phones.txt:语言直接相关的真实音素,同一行的音素是某一个音素的不同变体(重音、音调方面),故可共享决策树根,用的aishell的脚本;

1
2
3
4
(base) [lsy@small dict]$ cat lexicon.txt |awk '{ for(n=2;n<=NF;n++){ phones[$n] = 1; }} END{for (p in phones) print p;}'| perl -e 'while(<>){ chomp($_); $phone = $_; next if ($phone eq "sil");
m:^([^\d]+)(\d*)$: || die "Bad phone $_"; $q{$1} .= "$phone "; }
foreach $l (values %q) {print "$l\n";}
'| sort -k1 > nonsilence_phones.txt

silence_phones.txt:静音类音素
optional_silence.txt:备用的静音类音素

1
2
(base) [lsy@small dict]$ echo sil > silence_phones.txt
(base) [lsy@small dict]$ echo sil > optional_silence.txt

extra_questions.txt:同一行的音素有着相同的重音或音调,与GMM训练中自动生成的“questions”一同用于决策树的生成。

image (16)

1
2
3
(base) [lsy@small dict]$ cat silence_phones.txt > extra_questions.txt 
(base) [lsy@small dict]$ cat nonsilence_phones.txt | perl -e 'while(<>){ foreach $p (split(" ", $_)) {
$p =~ m:^([^\d]+)(\d*)$: || die "Bad phone $_"; $q{$2} .= "$p "; } } foreach $l (values %q) {print "$l\n";}' >> extra_questions.txt

这时候我们dict就准备好了,返回s5目录,下一步我们使用perpare_lang.sh来生成L.fst

首先连接wsj的steps和utils

1
2
(base) [lsy@small s5]$ ln -s ~/kaldi/egs/wsj/s5/steps/ ./
(base) [lsy@small s5]$ ln -s ~/kaldi/egs/wsj/s5/utils/ ./

然后使用perpare_lang.sh

1
(base) [lsy@small s5]$ utils/prepare_lang.sh data/local/dict "<SPOKEN_NOISE>" data/local/lang data/lang

这时候我们来看以下data/lang文件夹下生成的东西

image (17)

我们可以通过fstprint和fstdraw进行可视化L.fst

1
2
(base) [lsy@small lang]$ ~/kaldi/tools/openfst/bin/fstprint L.fst > L_print.txt
(base) [lsy@small lang]$ ~/kaldi/tools/openfst/bin/fstprint --isymbols=phones.txt --osymbols=words.txt L.fst > L_print1.txt

一般我们用第二行命令进行可视化

image (18)

这个图的意思:

第16行只有一列,说明状态1是终止状态,并且没有权重。其余的都是5列,第一列是起点状态id,第二列是终点状态id,第三列是输入符号,第四列是输出符号,第五列是weight。因此第一行表示的边为:从状态0到1的边,输入是<eps>,输出是<eps>,权重为0.69314。这个WFST的初始状态是什么呢?OpenFst约定第一行的起点就是初始状态。

因此我们可以依靠此表将WFST画出来

现在再我们使用下面两行命令用fstdraw来进行可视化(因为这个词典太小可以可视化出来,一般这个命令没法用)

1
2
(base) [lsy@small lang]$ ~/kaldi/tools/openfst/bin/fstdraw --isymbols=phones.txt --osymbols=words.txt L.fst > L.dot
(base) [lsy@small lang]$ dot -Tjpg L.dot > L.jpg

L (2) (1)

我们现在来看一下lang文件夹下面的其他文件。

1.phones.txt,将所有音素一一映射为自然数,即音素 ID,引入“”(epsilon)、消歧(Disambiguation)符号“#n”(n 为自然数), 便于 FST 处理。

第一列为音素,第二列为映射的自然素,可以看到总共122个音素,代表空,因为加了位置相关,B为音素在开头的意思,E为结尾,I为中间,S为单独一个,例如”SIL sil“就是单独的,”也许 ii ie3 x v3“ii就是开头的B, ie3和x就是中间I,v3就是结尾的E,后面都是要统计的。

image (19)

image (20)

2.words.txt,将词一一映射为自然数,即词ID,引入“”(epsilon)、消歧符号 “#0”、“”(句子起始处)、“”(句子结尾处),便于 FST 处理;

image (21)

3.oov.txt,oov.int,集外词的替代者(此处为)及其在words.txt 中的ID;

image (22)

oov.txt就是如果出现界外词,就用oov.txt中的词代替。

4.topo,各个音素HMM模型的拓扑图,通过将一个音素(或三音素)表示成一个HMM,此文件确定了每个音素使用的HMM状态数以及转移概率,用于初始化单音素GMM-HMM,可根据需要自行进行修改(并用utils/validate_lang.pl校验),实验中静音音素用了5个状态,其他音素用了3个状态;

image (23)

6-117是非静音音素,1-5为静音音素

5.phones/,是dict/ 的拓展,内部文件均可以文本形式打开查看,后缀为 txt/int/csl 的同名文件之间是相互转换的,其中 context_indep.txt 标明了上下文无关建模的音素,通常为静音音素, wdisambig.txt/wdisambig_phones.int/wdisambig_words.int 分别标明了words.txt 引入的消歧符号(#0)及其在phones.txt 和words.txt 中的ID, roots.txt 定义了同一行音素的各个状态是否共享决策树的根以及是否拆分,对应的音素集则存放于sets.txt。

消歧是为了确保发音词典能够得到一个确定性的(Deterministic) WFST。 如果有些词对应的音素串是另一些词音素串的前缀,比如 good 的音素串是 goodness 的前半段音素串,需要在前者对应的音素串后面加入消歧音素,破坏这种前缀关系,这样, WFST 中一个词的路径就不会包含于另一个词的路径中。

未完待续。。。