在解读解码过程中,发现在令牌如何在Network上传递还有些模糊。从而发现自己对Network的结构还没到无比熟悉的程度,所以这篇博客开始,重新、重头梳理HMMSet、Vocabulary、Lattice,到Network的过程。力争使得对这一过程了如指掌。
HVite工具要完成对MFCC参数文件的识别需要经历的过程。
第一步:MakeHMMSet。
输入的参数主要是hmmListFn,它的内容是该系统包含的所有hmm的名称列表,每行一个。
k |
ao |
l |
sp |
d |
ey |
v |
ay |
ax |
t |
它会调用CreateHMM函数,在该函数内实现HMM对象构建,并由hset->mtab中的指针指向它。
需要注意的是,这个HMM set并没有具体的参数,只有hmmList文件列举的hmm名称的空壳。
第二步,LoadHMMSet函数完成具体模型参数的加载。
这时需要hmm参数文件,通过命令行的-H指定的文件,即为HMM macro文件(包括hmm和宏)。
~o
<STREAMINFO> 1 39
<VECSIZE> 39<NULLD><MFCC_D_A_0><DIAGC>
~s "silst"
<MEAN> 39
-1.397300e+00 -1.077311e+00 1.101036e+00 -9.778857e-01 1.415934e+00 2.013623e+00 3.620641e+00 -3.266380e+00 6.048667e-01 8.009976e-01 -2.406307e+00 -7.590867e-01 5.095136e+01 -1.892487e-01 -1.241990e-01 -1.226040e-01 1.292183e-01 1.395147e-01 1.665978e-01 1.042514e-01 6.866585e-02 1.503324e-01 9.821929e-02 1.121857e-01 6.939935e-02 -1.075907e-01 1.078276e-02 9.979360e-03 2.644900e-03 -6.263836e-02 -2.683258e-02 -4.371678e-02 -1.381797e-02 7.796610e-03 -2.530313e-02 -2.031450e-02 -1.974577e-03 4.176129e-03 4.330555e-02
<VARIANCE> 39
2.207184e+01 1.799583e+01 2.160415e+01 4.417380e+01 2.185633e+01 3.125096e+01 2.334721e+01 2.834366e+01 2.488723e+01 2.535289e+01 2.548509e+01 2.510006e+01 4.209428e+01 9.554118e-01 8.272743e-01 1.105140e+00 2.111607e+00 1.420813e+00 1.774607e+00 1.728339e+00 1.989705e+00 1.871716e+00 1.781830e+00 1.594687e+00 1.616295e+00 6.955460e-01 1.316881e-01 1.271856e-01 1.668149e-01 2.984289e-01 2.577819e-01 2.981737e-01 3.295846e-01 3.710345e-01 3.435419e-01 3.295808e-01 2.892002e-01 2.939855e-01 8.663701e-02
<GCONST> 9.990668e+01
~h "d"
<BEGINHMM>
<NUMSTATES> 5
<STATE> 2
<MEAN> 39
-2.657632e+00 -1.152784e+00 2.760070e-01 -4.684222e-01 3.427158e-01 2.619073e+00 6.201020e+00 -2.094508e+00 4.289240e+00 4.655592e+00 4.445346e-01 2.340469e+00 4.794483e+01 -1.353383e-01 -1.037934e-01 -1.126405e-01 -3.832025e-02 1.023052e-01 8.720895e-02 1.490425e-01 1.655028e-01 -7.635970e-02 -4.075745e-02 1.163056e-01 5.512707e-02 -1.060684e-01 4.220375e-02 5.909489e-02 1.100840e-01 -3.321219e-02 6.739228e-02 -7.282927e-02 -2.221439e-01 -1.074107e-01 -1.842521e-01 -1.416463e-01 -1.434225e-01 -1.020464e-01 1.473876e-01
<VARIANCE> 39
7.434759e+00 3.740149e+00 5.216793e+00 7.948927e+00 8.632475e+00 1.443532e+01 1.405637e+01 2.008308e+01 1.719524e+01 1.751916e+01 1.429284e+01 1.384994e+01 5.507482e+00 4.803076e-01 3.867665e-01 6.138433e-01 6.832920e-01 9.793965e-01 1.078818e+00 1.546346e+00 1.629715e+00 1.516798e+00 1.465263e+00 1.322730e+00 1.169087e+00 3.255346e-01 9.978916e-02 7.954152e-02 1.139906e-01 1.701871e-01 2.173880e-01 2.038061e-01 3.291334e-01 3.237424e-01 2.773003e-01 2.746472e-01 2.897568e-01 2.432019e-01 1.045628e-01
<GCONST> 7.881728e+01
<STATE> 3
<MEAN> 39
-4.666384e+00 -1.865000e-03 2.731846e+00 1.163274e+00 2.422674e+00 1.803326e+00 4.538071e+00 -5.774121e+00 -9.428361e-01 6.558975e-01 -3.752897e+00 -4.514457e-01 4.672131e+01 -3.875452e-02 7.664852e-03 1.912313e-02 -7.832502e-03 5.145986e-02 9.910762e-05 -6.542074e-02 -8.398993e-04 -3.676043e-02 -2.331419e-02 -1.164499e-02 2.606855e-03 2.513995e-02 -1.676337e-02 -2.356523e-02 -4.046491e-03 -2.852478e-02 2.518643e-03 -2.338183e-02 -3.230591e-02 2.933652e-02 1.942882e-02 4.378057e-03 2.297758e-02 1.427195e-02 4.526214e-02
<VARIANCE> 39
3.198217e+00 4.858822e+00 4.877367e+00 8.165546e+00 9.870108e+00 1.152250e+01 1.228697e+01 1.689025e+01 1.576818e+01 1.581842e+01 1.805326e+01 1.239329e+01 2.391316e+00 2.413918e-01 3.934696e-01 5.207388e-01 7.039284e-01 1.014561e+00 1.172389e+00 1.217836e+00 1.645708e+00 1.790495e+00 1.700976e+00 1.502479e+00 1.399769e+00 1.232742e-01 5.053905e-02 8.360466e-02 1.072032e-01 1.470615e-01 2.064964e-01 2.612020e-01 2.613055e-01 3.574451e-01 4.078086e-01 3.780035e-01 3.228870e-01 2.992494e-01 5.231208e-02
<GCONST> 7.518591e+01
<STATE> 4
<MEAN> 39
-5.854234e+00 -2.966419e+00 2.874869e+00 -2.412796e+00 2.705436e+00 2.335080e-01 1.440433e+00 1.588776e+00 1.944512e+00 1.413263e+00 4.924903e-01 2.075649e+00 5.246408e+01 -1.045735e-01 -3.083847e-01 2.040161e-01 -1.173415e-01 1.423181e-01 -5.104095e-01 -3.108896e-01 4.452672e-01 -6.381394e-02 -2.685330e-01 7.785648e-02 1.393704e-01 4.931580e-01 1.244351e-01 4.143445e-02 -5.802656e-02 1.044002e-01 -1.151898e-01 3.954919e-02 2.090154e-01 -1.809378e-01 3.612456e-02 7.854719e-02 -4.775418e-02 -1.125454e-02 -1.144335e-01
<VARIANCE> 39
2.338185e+01 2.070014e+01 1.732226e+01 2.838920e+01 2.848204e+01 4.363123e+01 3.378460e+01 3.451517e+01 2.433481e+01 2.328713e+01 2.327781e+01 1.624626e+01 3.832143e+01 2.790690e+00 2.316788e+00 1.729795e+00 2.841474e+00 2.690685e+00 2.925945e+00 4.090334e+00 3.534522e+00 1.595624e+00 2.214930e+00 2.174373e+00 1.531668e+00 3.563339e+00 4.865716e-01 5.027289e-01 3.758193e-01 5.042238e-01 5.615985e-01 5.821174e-01 7.841897e-01 6.462195e-01 3.306068e-01 4.144576e-01 4.281815e-01 3.122002e-01 6.657145e-01
<GCONST> 1.168186e+02
<TRANSP> 5
0.000000e+00 1.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
0.000000e+00 7.093335e-01 2.906664e-01 0.000000e+00 0.000000e+00
0.000000e+00 0.000000e+00 9.222462e-01 7.775384e-02 0.000000e+00
0.000000e+00 0.000000e+00 0.000000e+00 7.572219e-01 2.427782e-01
0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
<ENDHMM>
它是包含各个HMM的描述文件,HMM的名称、状态数、均值和协方差矩阵的参数等等。
第三步,初始化PSetInfo和VRecInfo,在识别的时候用于存放Token信息和中间结果。
(这一步与最后的识别息息相关,但是目前还不是特别清楚它中间涉及的数据的意义)
第四步,构建发音词典,vocab,依赖dictFn。
CALL k ao l sp
DAVE d ey v sp
DIAL d ay ax l sp
EIGHT ey t sp
FIVE f ay v sp
FOUR f ao sp
FOUR f ao r sp
JULIAN jh uw l ia n sp
JULIAN jh uw l y ax n sp
LAW l ao sp
LEE l iy sp
NINE n ay n sp
OH ow sp
ONE w ah n sp
PHIL f ih l sp
PHONE f ow n sp
SENT-END [] sil
SENT-START [] sil
大体是这样的。它会构建一个wtab,存放了一系列指针,指向每行的第一个字段表示的词。它对应词典的DictEntry,后面是它的发音音子序列。每个音字一般就是一个hmm模型,该模型参数存储在hset的mtab中,是由hmm的名称做哈希来索引的。
第五步,构建词法网络Lattice。
到目前为止,有了发音字典vocab、有了HMM模型hset,还缺词格网络Lattice。它是高层级的识别网络。函数FeadLattice来完成,它依赖词典对象。
这个Lattice,包含的信息有,多少个节点、多少条边,系统的发音字典(扩展节点时,需要发音信息)。
第六步,扩展词网络,构建phone的Network。
这是开始识别每一帧数据前的最后一步。Network集成了词网络、发音信息、HMM模型等,识别过程,就是在这个网络上传递令牌,并收集信息、剪枝,优化等。
这个扩展过程最简单就是在Lattice基础上扩展,比如把word扩展为多个phone,然后拼接到一起。复杂的有三音子绑定,扩词绑定等等,是由ExpandWordNet完成的。要理解chain模型,就要把这个函数的每个主要步骤搞清楚才可以。
1)首先为Lattice中的每个节点构建发音对象,如果该词是多音字,则建多个WordPron,每个WordPron包含了音子序列,输入符号、包含多少个音子。然后挂到节点的sublat中。
2)上一步只完成了Word内部的扩展,把该单词的发音信息,构建一个WordPron挂Word->sublat下。但是Word之间的连接信息还没有表示。
3)分别由CreateWIModels(pInst,p,q,net,hci);和CreateIEModels(thisWord,pInst,p,q,net,hci);完成计算。
有了扩展后的phone网络,就开始处理语音参数文件。
第七步,识别。
Boolean ProcessFile(char *fn, Network *net, int utterNum, LogDouble currGenBeam, Boolean restartable);包含了整个识别过程,唯一的依赖就是我们在第六步构建的Network。
1)调用openBuffer,返回输入信道的buffer,包括channel类型,总行数,main Block,是否进行静音检测、检测窗口大小等等。
它包含了输入信道的数据信息。
2)StartRecognition函数,初始识别器。输入参数包括net和一些语言模型补偿系数、词惩罚系数和一个可见的识别信息(VRecInfo)。
初始化net中chain上节点的inst都为NULL;
每个模型上的状态节点id为-1,每个状态的高斯id为-1;
3)读取observation,然后处理ProcessObservation。比较重要的是StepInst函数。
首先在net上进行传递时,构建一个VRecInfo,里面包含PRecInfo,剪枝的设置信息,当前帧的序号,激活态的模型,最优的Token;
而在PRecInfo里包含识别的状态信息。
4)在SetpInst1中完成token向前传递,并计算结果保存在模型的inst中。
下图是net网络的大体结构,从net->initial开始,连接指向pInst->starts,然后逐次指向下一个NetNode。但是这里的节点类别type不同,代表了不同含义。
// n_hmm = 2: HMM 模型节点 #define node_hmm(node) ((node)->type & n_hmm) // n_word = 4: 词节点 #define node_word(node) ((node)->type == n_word) // n_tr0 = 4: 一类特殊的点,貌似可以从start直接调到fin状态 #define node_tr0(node) ((node)->type & n_tr0) // n_wd0 = 1: 最后一个发音节点,如果是5状态的话,就是3节点 #define node_wd0(node) ((node)->type & n_wd0) |
不同的节点类型在识别时的行为将会不同。