Kaldi thchs30手札(二)word-graph(line 38-60)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/pelhans/article/details/80002133

欢迎大家关注我的博客 http://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 '<s>' | grep -v '</s>' | sort -u > data/dict/lexicon.txt || exit 1;
  utils/prepare_lang.sh --position_dependent_phones false data/dict "<SPOKEN_NOISE>" 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;
)  

其中Hthchs30/s5thchs30指的是前面设置的语音语料库路径。

头五行为提示开始制作,进入主目录并建立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: 词典源文件夹 <dictsrcdir>

3) "<SPOKENNOISE>":词典里未包含的事例,<oovdictentry>.

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$

  1. lexiconp.txt是带概率的字典文件,概率值为每个词出现的概率,格式为:word pronprob phone1, phone2 phoneN.

  2. silence_phones.txt/nonsilence_phones.txt为静音/非静音音素,每一行代表一组相同的base phone,但可能有不同的中银或者声调的音素,如:a a1 a2 a3

  3. optional_silence.txt:包含一个单独的音素用来作为词典中默认的静音音素。

  4. 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是由头三个音素组成KBAHIZE组成,因此除了第一行是单词’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 '<eps>' | sort -u > data/dict_phone/   lexicon.txt  && \
  echo "<SPOKEN_NOISE> sil " >> data/dict_phone/lexicon.txt  || exit 1;
  utils/prepare_lang.sh --position_dependent_phones false data/dict_phone "<SPOKEN_NOISE>" 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

展开阅读全文

没有更多推荐了,返回首页