kaldi006 -- 构图

 

解码图构建方法实验部分

 

这里我们一步一步地解释图的构建方法,以及与之相关的某些数据准备阶段。

这种方法的大部分细节不会硬编码到我们的工具中; 我们正在解释它目前是如何做的 如果这个部分很混乱,那么最好的补救办法可能就是阅读Mohri等人的“Speech Recognition with Weighted Finite-State Transducers”。 要注意:这张纸是相当长的,阅读它至少需要几个小时,对于那些尚不熟悉FST的人。 另一个好的资源是OpenFst网站,它将提供更多相关信息比如符号表

 

准备初始符号表

  我们需要准备OpenFst符号表word.txtphones.txt。 这些将整数id分配给系统中的所有单词和phone 请注意,OpenFstepsilon的保留符号为零。 示例WSJ符号表是:

 

## head words.txt

<eps> 0

!SIL 1

<s> 2

</s> 3

<SPOKEN_NOISE> 4

<UNK> 5

<NOISE> 6

!EXCLAMATION-POINT 7

"CLOSE-QUOTE 8

## tail -2 words.txt

}RIGHT-BRACE 123683

#0 123684

## head data/phones.txt

<eps> 0

SIL 1

SPN 2

NSN 3

AA 4

AA_B 5

 

word.txt文件包含单个消歧符号0”(用于G.fst输入上的epsilon)。 这是最后编号的单词。 如果您的词典包含单词0”,请小心。 phones.txt文件不包含消歧符号,但在创建L.fst之后,我们将创建一个文件phones_disambig.txt,该文件具有消歧符号(这对调试很有用)。

 

准备词典L

首先,我们创建一个文本格式的词典,最初没有消歧符号。我们的C ++工具永远不会与此交互,它只会被创建词典FST的脚本使用。 我们的WSJ词汇的一小部分是:

 

## head data/lexicon.txt

!SIL SIL

<s>

</s>

<SPOKEN_NOISE> SPN

<UNK> SPN

<NOISE> NSN

!EXCLAMATION-POINT EH2_B K S K L AH0 M EY1 SH AH0 N P OY2 N T_E

"CLOSE-QUOTE K_B L OW1 Z K W OW1 T_E

 

phone上的开始,结束和重音标记(例如T_EAH0)是我们的WSJ方法特有的,就我们的工具包而言,它们被视为不同的phone(然而,对于这个设置我们做特殊处理;树的构建过程中读取roots文件)。

 

请注意,我们允许有空语音表示的单词。 该词典将用于训练中创建L.fst(不含消歧符号)。 我们还创建一个消歧符号的词典,用于解码图创建。 此文件的摘录在这里:

# [from data/lexicon_disambig.txt]

!SIL    SIL

<s> #1

</s>    #2

<SPOKEN_NOISE>  SPN #3

<UNK>   SPN #4

<NOISE> NSN

...

{BRACE  B_B R EY1 S_E #4

{LEFT-BRACE L_B EH1 F T B R EY1 S_E #4

 

 

此文件由脚本创建; 该脚本输出它不得不添加的消歧符号,并用于创建符号表phones_disambig.txt。 这与phones.txt是一样的,但它也包含消歧符号#0,#1,#2等的整数id(#0是来自G.fst的特殊消歧符号,但将被passed through”L.fst通过自循环)。 文件phone_disambig.txt中间的一部分是:

 

ZH_E 338

ZH_S 339

#0  340

#1  341

#2  342

#3  343

 

数字是如此之,因为在这个(WSJ语聊中,我们向phone添加了重音和位置信息。 注意,用于空字(即<s></s>)的消歧符号必须与用于正常词的那些不同,因此本例中的正常消歧符号从#3开始。

将没有消歧符号的词典转换为FST的命令是:

scripts/make_lexicon_fst.pl data/lexicon.txt 0.5 SIL | \

  fstcompile --isymbols=data/phones.txt --osymbols=data/words.txt \

  --keep_isymbols=false --keep_osymbols=false | \

   fstarcsort --sort_type=olabel > data/L.fst

 

这里,脚本make_lexicon_fst.pl创建FST的文本表示。 0.5是静音概率(即在句子开始处,每个单词之后,我们以概率0.5输出静音;分配给无静音的概率质量为1.0-0.5 = 0.5,本示例中其余的命令涉及 将FST转换为编译格式; fstarcsort是必要的,因为我们稍后将作compose

 

词典的结构大致如人们期望的那样。 有一个状态(循环状态)是终止状态 有一个起始状态有两个过渡到循环状态:一个有静默和一个没有。 从循环状态,对应于每个单词都有一个转换,该字是转换时的输出符号; 输入符号是该单词的第一个phone 对于组合的效率和最小化的有效性,重要的是输出符号应尽可能早(即开始时不是单词的结尾)。 在每个单词的末尾,为了处理可选的静音,对应于最后一个phone的转换有两种形式:一种到循环状态,一种转换为静默状态,它们转换到循环状态。 我们不要在静音中放置可选的静音,我们将它定义为只有一个phone是静音phone的话。

 

用消歧符号创建词典只是稍微复杂一点。 问题是我们必须将自循环添加到词典中,以便可以通过词典传递来自G.fst的消歧符号#0。 我们使用程序fstaddselfloopsc.f.添加和删除消歧义符号)来执行此操作,尽管我们可以在脚本make_lexicon_fst.pl中轻松完成手动操作。

 

phone_disambig_symbol=`grep \#0 data/phones_disambig.txt | awk '{print $2}'`

word_disambig_symbol=`grep \#0 data/words.txt | awk '{print $2}'`

 

scripts/make_lexicon_fst.pl data/lexicon_disambig.txt 0.5 SIL  | \

   fstcompile --isymbols=data/phones_disambig.txt --osymbols=data/words.txt \

   --keep_isymbols=false --keep_osymbols=false |   \

   fstaddselfloops  "echo $phone_disambig_symbol |" "echo $word_disambig_symbol |" | \

   fstarcsort --sort_type=olabel > data/L_disambig.fst

 

程序fstaddselfloops不是原始的OpenFst命令行工具之一,它是我们自己的(我们有一些这样的程序)之一。

 

准备语言模型G

语言模型G大部分是一个接受者(即每个圆弧上的输入和输出符号相同),其中以字为其符号。 除了是消歧符号#0,它只出现在输入端。 假设输入是Arpa文件,我们使用Kaldi程序arpa2fst将其转换为FST。 程序arpa2fst输出带嵌入符号的FST。 在Kaldi,我们通常使用没有嵌入符号的FST(即我们使用单独的符号表)。 只需运行arpa2fst,我们必须采取的步骤如下:
  我们必须从FST中删除嵌入的符号(并依赖于磁盘上的符号表)。
  我们必须确保在语言模型中没有超出词汇量的单词
  我们必须删除起始和结尾符号的非法序列,例如 <s>后跟</ s>,因为这些导致L o G不可确定。
  我们必须用特殊的消歧符号#0替换输入端的epsilons

实际脚本的略简化版本如下所示:

gunzip -c data_prep/lm.arpa.gz | \

  arpa2fst --disambig-symbol=#0 \

             --read-symbol-table=data/words.txt - data/G.fst

 

最后一个命令(fstisstochastic)是一个诊断步骤(请参阅Preserving stochasticity and testing it)。 在一个典型的例子中,它打印出数字:

9.14233e-05 -0.259833

 

第一个数字很小,所以它证实没有状态的弧的概率加上最终状态显着小于1。 第二个数字是重要的,这意味着有些状态具有太多的概率(FST中权重的数值通常可以解释为对数概率)。 对于具有回退的语言模型的FST表示,有一些具有太多概率的状态是正常的。 在后续的构图步骤中,我们将验证这一非随机性并没有比开始时更糟糕。

所得到的FST G.fst当然只用于测试时间。 在训练时间内,我们使用从训练单词序列生成的线性FST,但这是在Kaldi程序中完成的,而不是在脚本级别。

 

准备LG

 

当用G组合L时,我们概括地遵循一个相当标准的方法,即我们计算mindetL o G))。 命令行如下:

 

fsttablecompose data/L_disambig.fst data/G.fst | \

        fstdeterminizestar --use-log=true | \

        fstminimizeencoded | fstpushspecial | \

         fstarcsort --sort-type=ilabel > somedir/LG.fst

这里OpenFst的算法有一些小的区别。 我们使用我们的命令行工具“fsttablecompose”实现的更有效的组合算法(参见Composition)。 我们的确定是一种也可以通过命令行程序fstdeterminizestar实现的算法,同时去空边 选项-use-log = true要求程序首先将FST映射到log半环; 这保留随机性(在log半环; Preserving stochasticity and testing it

 

我们通过程序“fstminimizeencoded”进行最小化。 这与OpenFst最小化算法的版本大体相同,适用于加权接收器; 唯一相关的变化是避免pushing weights,从而保持随机性(详见Minimization)。

 

程序“fstpushspecial”类似于OpenFst“fstpush”程序,但如果权重不等于1,则可以确保所有状态总计为相同的值(可能与不同),而不是尝试推送额外权重到图表的开头或结尾。 这具有永远不会失败的优点(“Fstpush”可以失败或循环很长时间,如果FST“总和为无穷大; 它也快得多。 有关更详细的文档,请参阅push-special.cc

 

“fstarcsort”阶段对弧进行排序以有助于稍后组合操作快速。

 

准备CLG

 

为了获得输入为上下文相关phone转换器,我们需要准备一个叫做CLGFST,它与C o L o G相同,其中LG是词典和语言模型C代表语音上下文 对于三音系统,C的输入符号将为a / b / c(即三元phone),输出符号将是单个phone(例如abc)。 有关语音上下文窗口的更多信息,请参阅Phonetic context windows,以及我们如何产生为不同的上下文大小。 首先,我们将描述如何创建上下文FST C,如果我们自己做它并且正常组合(为了效率和可扩展性的原因,我们的脚本实际上不是这样工作)。

 

制作上下文转换器

 

在本节中,我们将解释如何将C作为独立的FST获得。

 

C的基本结构是它对所有可能的大小为N-1phone窗口(c.f.语音上下文窗口,三音素N = 3)的状态。 第一个状态,意思是开始的话语,只会对应于N-1个子句。 每个状态都有每个phone的转换(现在让我们忘记自循环)。 作为通用示例,状态a / b在输出上具有c的转换,并且输入上的a / b / c转换到状态b / c。 话语开始和结束时都有特殊情况。

 

在话语开始时,假设状态为<eps> / <eps>,输出符号为a。 通常,输入符号将是<eps> / <eps> / a。 但这并不代表phone,因为(假设P = 1),中心元素是<eps>,不是phone 在这种情况下,我们让弧的输入符号为#-1,这是我们为此目的引入的特殊符号(我们在这里不使用epsilon作为标准方法,因为当有空字时可能会导致不可确定性)。

 

话语结束有点复杂。上下文FST在右侧(其输出端)具有在语句结尾处出现的特殊符号$。考虑三元phone的情况。在句子结束后,看到所有符号后,我们需要刷出最后一个三元phone(例如a / b / <eps>,其中<eps>表示未定义的上下文)。这样做的自然方法是在其输入上具有/ b / <eps>的转换,其输出上的<eps>,从状态a / b到最终状态(例如b / <eps>a特殊最终状态)。但是,这对于组合来说是低效的,因为如果不是话语的结束,我们必须先探索这些转变,才能将它们修剪掉。相反,我们使用$作为终止符号,并确保它在LG的每个路径的末尾出现一次。然后我们用C的输出$替换<eps>。一般来说,$的重复次数等于N-P-1。为了避免麻烦,必须确定要添加多少个后续符号到LG,我们只允许它在话语结束时接受任何数量的这种符号。这通过函数AddSubsequentialLoop()和命令行程序fstaddsubsequentialloop来实现。

如果我们自己想要C,我们首先需要一个消歧符号清单; 我们还需要找出我们可以用于后续符号的未使用的符号id,如下所示:

 

 grep '#' data/phones_disambig.txt | awk '{print $2}' > $dir/disambig_phones.list

 subseq_sym=`tail -1 data/phones_disambig.txt | awk '{print $2+1;}'

 

然后,我们可以使用以下命令创建C(但是,关于fstcomposecontext,请参见下文),因为实践中不执行此操作,因为效率低下)。

 

 fstmakecontextfst --read-disambig-syms=$dir/disambig_phones.list \

 --write-disambig-syms=$dir/disambig_ilabels.list data/phones.txt $subseq_sym \

   $dir/ilabels | fstarcsort --sort_type=olabel > $dir/C.fst

 

程序fstmakecontextfst需要phone列表,消歧符号列表和后续符号的标识。 除了C.fst之外,它还会写出解释C.fst左边符号的文件“ilabels”(请参阅The ilabel_info object)。 LG的组合可以做到如下:

 

fstaddsubsequentialloop $subseq_sym $dir/LG.fst | \

 fsttablecompose $dir/C.fst - > $dir/CLG.fst

 

为了打印出C.fst和使用相同符号索引“ilabels”的任何内容,我们可以使用以下命令创建一个合适的符号表:

 

fstmakecontextsyms data/phones.txt $dir/ilabels > $dir/context_syms.txt

 

This command knows about the "ilabels" format (The ilabel_info object). An example random path through the CLG fst (for Resource Management), printed out with this symbol table, is as follows:

 

该命令知道“ilabels”格式(The ilabel_info object)。 通过CLG fst(用于资源管理),示例随机路径与该符号表打印出来,如下所示:

 

## fstrandgen --select=log_prob $dir/CLG.fst | \

   fstprint --isymbols=$dir/context_syms.txt --osymbols=data/words.txt -

0   1   #-1 <eps>

1   2   <eps>/s/ax  SUPPLIES

2   3   s/ax/p  <eps>

3   4   ax/p/l  <eps>

4   5   p/l/ay  <eps>

5   6   l/ay/z  <eps>

6   7   ay/z/sil    <eps>

7   8   z/sil/<eps> <eps>

8

 

动态组合C

 

在正常构图中,我们使用一个程序fstcomposecontext,它动态地创建所需的状态和C的弧,而不浪费地创建它。 命令行是:

 

fstcomposecontext  --read-disambig-syms=$dir/disambig_phones.list \

                   --write-disambig-syms=$dir/disambig_ilabels.list \

                   $dir/ilabels < $dir/LG.fst >$dir/CLG.fst

 

If we had different context parameters N and P than the defaults (3 and 1), we would supply extra options to this program. This program writes the file "ilabels" (see ) which interprets the input symbols of CLG.fst. The first few lines of an ilabels file from the  recipe are as follows:

 

相比于默认值31),如果我们有不同的上下文参数NP,我们将为此程序提供额外的选项。 该程序写入解释CLG.fst的输入符号的文件“ilabels”(请参阅The ilabel_info object)。 Resource Management中的ilabels文件的前几行如下:

 

65028 [ ]

[ 0 ]

[ -49 ]

[ -50 ]

[ -51 ]

[ 0 1 0 ]

[ 0 1 1 ]

[ 0 1 2 ]

...

 

数字65028是文件中的元素数。 像[-49]这样的就是消歧符号; [0 1 2]这样的行表示声音上下文窗口; 前两个条目是用于epsilon(从未使用)的[][0]是用于在C开头代替epsilon使用打印形式#-1的特殊消歧符号,以确保可确定性。

 

减少上下文相关输入符号的数量

 

在创建CLG.fst之后,有一个可选的构图阶段可以减小其大小。 我们使用从决策树和HMM拓扑信息中得出的程序make-ilabel-transducer他是上下文相关phone的子集将对应于相同的编译图,因此可以合并(我们选择任何一个子集的元素,并且转换所有上下文窗口到那个窗口)。 这是一个类似于HTK逻辑到物理映射的概念。 命令是:

make-ilabel-transducer --write-disambig-syms=$dir/disambig_ilabels_remapped.list \

  $dir/ilabels $tree $model $dir/ilabels.remapped > $dir/ilabel_map.fst

 

这个程序需要树和模型; 它输出一个名为“ilabels.remapped”的新的ilabel_info对象; 这与原始“ilabels”文件的格式相同,但行数较少。 FST“ilabel_map.fst”CLG.fst做组合,并重新映射标签。 做到这一点后,我们确定化和最小化,所以我们可以马上实现任何尺寸的缩减:

 

fstcompose $dir/ilabel_map.fst $dir/CLG.fst  | \

   fstdeterminizestar --use-log=true | \

   fstminimizeencoded > $dir/CLG2.fst

 

 

对于一般的设置,这个阶段实际上并没有减少图的大小(通常为5%到20%的减少),在任何情况下,它只是通过这种机制减少的中间构图阶段的大小。 但是,对于具有更广泛背景的系统,节省可能会变得重要。

 

制作H转换器

 

在传统的FST制作中中,H转换器是在其输出上具有上下文相关phone,并且在其输入上具有表示声学状态的符号。在我们的例子中,H(或HCLG)的输入上的符号不是声学状态(在我们的术语中是pdf-id),而是我们称之为transition-id的东西(参见Integer identifiers used by TransitionModel)。transition-idpdf-id加上一些包括phone在内的其他信息进行编码。每个transition-id可以映射到pdf-id。我们创建的H转换器不对自环进行编码。这些稍后由单独的程序添加。 H转换器具有初始和最终状态,并且从该状态到每个条目都有一个转换,但ilabel_info对象中的第零个(the ilabels file,见上文)。上下文相关phone的转换转到相应HMM的结构(缺少自环),然后返回到开始状态。对于正常拓扑,HMM的这些结构只是三个弧的线性序列。对于每个消歧符号(#-1,#0,#1,#2,#3等),H也在初始状态上具有自循环。

 

H转换器的脚本部分(我们称之为Ha,因为它在这一点上缺少自循环),是:

make-h-transducer --disambig-syms-out=$dir/disambig_tstate.list \

   --transition-scale=1.0  $dir/ilabels.remapped \

   $tree $model  > $dir/Ha.fst

 

有一个可选参数来设置转移因子; 在我们目前的训练脚本中,这个因子1.0。 这个比例只影响到与自循环概率无关的转换部分,而在正常拓扑(Bakis模型)中它完全没有影响; 请参阅Scaling of transition and acoustic probabilities以获得更多的解释。 除了FST之外,该程序还写入一个消歧符号列表,稍后必须删除。

 

制作HCLG

 

制作最终图HCLG的第一步是制作缺少自循环HCLG。 我们当前脚本中的命令如下所示:

  fsttablecompose $dir/Ha.fst $dir/CLG2.fst | \

   fstdeterminizestar --use-log=true | \

   fstrmsymbols $dir/disambig_tstate.list | \

   fstrmepslocal  | fstminimizeencoded > $dir/HCLGa.fst

 

在这里,CLG2.fstCLG的版本,缩略符号集(逻辑三音素HTK术语)。 我们删除消歧符号和任何easy-to-remove epsilons(请参阅Removing epsilons),在最小化之前; 我们的最小化算法是避免推送符号和权重(从而保留随机性),并接受非确定性输入(见Minimization)。

HCLG添加自环

通过以下命令完成向HCLG添加自环:

 

  add-self-loops --self-loop-scale=0.1 \

    --reorder=true $model < $dir/HCLGa.fst > $dir/HCLG.fst

 

有关如何应用0.1 self-loop-scale的说明,请参见Scaling of transition and acoustic probabilities(注意它也影响非自概率)。有关重新排序选项的说明,请参阅Reordering transitions; “重新排序选项提高了解码速度,但与kaldi解码器不兼容。 add-self-loops程序不仅添加自环;它可能还必须复制状态并添加epsilon转换,以确保以一致的方式添加自环。在重新排序转换中,Reordering transitions详述这个问题。这是唯一不保存随机性的构图阶段;它不保存它,因为自环的因子不是1,所以程序fstisstochastic应该给所有的G.fstLG.fstCLG.fstHCLGa.fst相同的输出,但不是HCLG.fst 。在自环阶段之后我们不再确定了;这样会失败,因为我们已经删除了消歧符号。无论如何,这很慢,我们认为在这一点上确定和最小化没有什么进一步的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章