kaldi三音素GMM学习笔记

kaldi三音素GMM学习笔记
2021年09月08日 16:07 语音之家SpeechHome

三音素GMM与单音素GMM的主要差别在于决策树状态绑定,与GMM参数更新相关的原理、程序和类两者都是一样的。

在这个笔记中,我会首先介绍表示HMM的类HmmTopology和TransitionModel,然后介绍三音素GMM训练脚本train_deltas.sh用到的几个程序,这几个程序与单音素GMM的不同或者只在三音素GMM训练中出现。与GMM相关的其余部分请参考单音素GMM学习笔记。

HmmTipology

为什么要介绍HmmTopology(后简称HT)和TransitionModel(后简称TM)?前面我们几乎一直在讲GMM和决策树,那么HMM用什么表示?在Kaldi中用TM表示HMM,TM中包含一个HT对象,用来表示HMM拓扑结构。

在Kaldi数据准备阶段,Kaldi会在data/lang目录下自动生成表示HMM拓扑结构的文件topo,HT对象就保存topo中的信息。一是topo中都有哪些音素,保存在HT的数据成员phone_中;二是每个音素的HMM结构是什么,由HT的数据成员phone2idx_和entries_共同决定。我们用下面一个图来解构HmmTopology的数据成员。

TransitionModel

在单音素GMM初始化程序gmm-init-mono和三音素GMM初始化程序gmm-init-model中都会调用TM构造函数TransitionModel(const ContextDependencyInterface &ctx_dep,const HmmTopology &hmm_topo)来初始化TM。我们也就以此构造函数为切入口,来学习TransitionModel中各数据成员是怎么构造出来的。

我们先来看看TM都有哪些数据成员以及各自的作用:

HmmTopology topo_;

构造函数的调用过程如下两图所示:

下面我们对train_deltas.sh中与三音素GMM相关的几个程序逐个进行说明。

gmm-init-model

Ø示例:gmm-init-model tree treeacc topo 1.mdl

Ø作用:使用决策树tree和决策树统计量treeacc初始化GMM。

Ø流程:

1.读取tree, treeacc, topo。

2.用tree和topo初始化TransitionModel trans_model,trans_model中保存着每个音素和其每个状态对应的pdf-id的Tuple(实际为Triple).

3.调用InitAmGmm()初始化am_gmm;若提供old_tree_filename和old_model_filename,调用InitAmGmmFromOld()初始化am_gmm。在InitAmGmm()中,将stats划分到决策树的每个叶子上(对应一个pdf),用该pdf对应的stats的count_、x、x^2初始化该pdf对应的DiagGmm的参数weight_、means_invvars_、inv_vars_和gconsts_。

4.若指定参数--write-occs=1.occs,调用GetOccs()得到每个pdf对应的state occupancies(也就是该pdf对应的观测的数量,或者说该pdf对应的帧数),将state occupancies写到1.occs

5.将trans_model和am_gmm写到1.mdl,得到初始GMM模型。

gmm-mixup

Ø示例:gmm-mixup --mix-up=4000 1.mdl 1.occs 2.mdl

gmm-mixup --merge=2000 1.mdl 1.occs 2.mdl

Ø作用:用来增加GMM混合分量的个数,或合并GMM混合分量。

Ø流程:

1.从1.mdl里读取trans_model, am_gmm,从1.occs里读取occs。

2.若mixdown!=0,对am_gmm调用MergeByCount();若mixup!=0,对am_gmm调用SplitByCount()。

3.将trans_model和改变后的am_gmm写到1.mdl。

AmDiagGmm::SplitByCount()

根据occs,调用AmDiagGmm::GetSplitTargets()得到am_gmm中每个DiagGmm i应该增加到的混个分量个数targets[i]。GetSplitTargets()对观测数最多的pdf优先增加混合分量个数(使用优先队列实现)。对每个DiagGmm i,根据targets[i],调用DiagGmm::Split()增加该DiagGmm i的混合分量。Split()对混合分量中weights_最大的分量优先进行分割,将其权值对半分,一半留给自己一半分给新的分量,被分割分量的均值、方差相关参数直接复制给新分量,复制完后对新分量的均值、方差相关参数加一个随机的扰动。

convert-ali

由单音素GMM我们得到训练数据的对齐文件,但是单音素GMM中的TransitionModeltm1和三音素GMM中的TransitionModeltm2不同,两者的每个数据成员都不一样,所以要把用tm1的tid(transition-id)表示的对齐转换成tm2的tid表示的对齐。这就是convert-ali的作用。

要看懂convert-ali,首先要对TransitionModel理解得比较清楚。建议先搞明白TM再去看该程序的代码。

我个人觉得这里的核心在于:根据tid能知道当前是哪个音素的哪个HMM状态(知道tid和对应的TM,由id2state_知道t-state,由state2id和tid只能t-idx,由t-state索引tuple_知道tuple,tuple保存音素、HMM state-id,也就知道了这两者),而无论该特征向量所对应的tid编号怎么变化,该特征向量对应的音素和HMM状态都是不变的。

从旧的tid转换成新的tid的流程大致如下:

明白了上述流程之后,convert-ali的代码就容易看懂了,这里我不再讲述细节,只把自己当时学习的手写笔记草稿放在这里,希望能有一定启发。

由convert-ali得到基于新的TransitionModel的对齐后,后面的GMM参数更新就和单音素GMM一样,GMM参数更新就是对每一个GMM(也就是每一个pdf)更新相应的参数。由对齐序列(tid序列),我们就能找到属于每一个pdf的特征向量,用这些特征向量更新该pdf的每个分量的均值、方差和权值即可。

财经自媒体联盟更多自媒体作者

新浪首页 语音播报 相关新闻 返回顶部