静态解码图构建的过程,就是将数据准备和预编译阶段得到的词典FST、语言模型FST、声学建模阶段得到的HMM拓扑FST以及可能的上下文关系FST,通过如下的公式进行组合优化处理,得到最终的HCLG图。Kaldi中对FST的很多操作与参考文献[1]中稍有不同,尤其是对消歧符号和权重推移等的处理上。
其中,G为语言模型FST,输入输出标签相同,都是词,本质为FSA。
L为词典FST,输入标签为音素,输出标签为词(多音词由权重区分,同音词由词尾消歧符号区分);
C为上下文FST,输入标签为上下文音素(初始状态只有右侧上下文,结束状态只有左侧上下文),输出标签为单音素,为上下文音素中的中间音素;
H为HMM定义的声学FST,H’表示不带自环的声学FST,输入标签为trans-id(其中编码了trans-state,pdf-id等信息),输出标签为上下文音素;
o表示FST组合操作;
det表示FST确定化操作;
min表示FST最小化操作;
rds表示去除消歧符号;
asl表示增加自环转移。
上述为标准处理方法,但是,有很多细节需要进一步说明。首先,我们需要保证输出的HCLG是确定化且是最小化的(determinized and minimized),确定化通过插入消歧符号实现。同时,我们还要尽可能保证HCLG是随机的(stochastic,即从每个状态输出的概率之和为1),在传统方法中,随机性通过“权重推移”操作实现,但在Kaldi中,通过保证G的随机性以及构建过程中每个步骤都不丢失随机性来实现,Kaldi的实现方法避免了“权重推移”可能出现失败的问题,确保了组合后的HCLG不会比G本身的随机性更差。
解码图构建主要涉及的脚本文件为mkgraph.sh:
Ø 输入:lang文件夹,模型文件final.mdl,决策树文件tree
Ø 输出:解码图文件../graph/HCLG.fst
utils/mkgraph.sh--mono data/lang_test_bg exp/mono exp/mono/graph #以单音素模型为例
Ø 上述三个参数:$1=data/lang_test_bg, $2=exp/mono,$3=exp/mono/graph,前两个为输入文件夹,后一个为输出文件夹。从输入文件夹$1中提取语言模型、词典、音素列表、词列表等,$2中提取上下文决策树和声学模型。输出文件夹$3保存HCLG.fst。
lang=$1
tree=$2/tree
model=$2/final.mdl
dir=$3
具体实现步骤如下:
Ø 设置转移概率尺度,普通转移和自转移分开设置。
tscale=1.0 #转移概率尺度设置
loopscale=0.1 #自转移概率尺度设置不同
Ø 检测是否所有输入列表中要求的文件都具备。
#输入列表,其中lang文件夹下的文件来自于数据准备阶段,模型和决策树来自于建模阶段
$lang/L.fst #词典fst
$lang/G.fst #语言模型fst
$lang/phones.txt #音素列表,存储音素文本到音素id的映射
$lang/words.txt #词列表,存储词文本到音素id的映射
$lang/phones/silence.csl #静音id列表
$lang/phones/disambig.int #消歧符号id列表
$model #最终模型文件final.mdl
$tree #决策树文件
Ø 检测输出路径中是否已经有HCLG.fst文件,避免覆盖。
Ø 将词典L.fst和语言模型G.fst组合(fsttablecompose),然后进行确定化(fstdeterminizestar)和最小化(fstminimizeencoded),得到LG.fst,并确保结果stochastic,即从每个状态输出的转移概率之和为1。
组合的作用:将每个word扩展到phone级别。(输入标签为)
确定化的作用:在每个语言模型的状态(word)下,构建树结构的词典。
最小化的作用:类似后缀共享(suffix-sharing)。
上述操作的部分C++代码是Kaldi中另写的代码,与OpenFst库中代码稍有不同,包括:
fsttablecompose$lang/L_disambig.fst$lang/G.fst | fstdeterminizestar --use-log=true | \
fstminimizeencoded | fstpushspecial | \
fstarcsort --sort_type=ilabel >$lang/tmp/LG.fst ||exit 1;
fstisstochastic$lang/tmp/LG.fst ||echo"[info]: LG not stochastic."
Ø 将上下文C.fst和LG.fst组合(fstcomposecontext)得到CLG.fst,并确保结果stochastic。
fstcomposecontext --context-size=$N --central-position=$P \
--read-disambig-syms=$lang/phones/disambig.int \
--write-disambig-syms=$lang/tmp/disambig_ilabels_${N}_${P}.int \
$lang/tmp/ilabels_${N}_${P}<$lang/tmp/LG.fst|\
fstarcsort --sort_type=ilabel >$clg
fstisstochastic$clg ||echo"[info]:CLG not stochastic."
Ø 基于HMM拓扑结构、转移概率和决策树,构建不带自转移的声学模型Ha.fst(make-h-transducer),每个转移的输入标签为“trans-id”(编码了“GMM模型的pdf-id和音素信息”)。
make-h-transducer --disambig-syms-out=$dir/disambig_tid.int \
--transition-scale=$tscale$lang/tmp/ilabels_${N}_${P}$tree$model \
>$dir/Ha.fst ||exit 1;
Ø 将不带自转移的声学模型Ha.fst和CLG.fst组合(fsttablecompose),然后进行确定化(fstdeterminizestar),去除消歧符号,去除空转移,然后进行最小化(fstminimizeencoded),得到HCLGa.fst,并确保结果stochastic。
fsttablecompose$dir/Ha.fst"$clg" |fstdeterminizestar --use-log=true \
| fstrmsymbols$dir/disambig_tid.int | fstrmepslocal | \
fstminimizeencoded >$dir/HCLGa.fst ||exit 1;
fstisstochastic$dir/HCLGa.fst ||echo"HCLGa is not stochastic"
Ø 增加每个HMM状态的自转移,从HCLGa.fst得到HCLG.fst。在最后一步加入自转移使得前面几步可以构建较大的解码图。选项reorder将自转移放在其他状态转移之后,加快解码速度。并确保最终结果stochastic。
add-self-loops --self-loop-scale=$loopscale --reorder=true \
$model <$dir/HCLGa.fst >$dir/HCLG.fst ||exit 1;
fstisstochastic$dir/HCLG.fst ||echo"[info]:final HCLG is not stochastic."
[1] Mohri M, Pereira F, Riley M. Speech Recognitionwith Weighted Finite-State Transducers[M]. Springer Berlin Heidelberg, 2008.