Kaldi三音素GMM学习笔记

建议在csdn资源页中免费下载该学习笔记的PDF版进行阅读:)点击进入下载页面

Kaldi三音素GMM学习笔记

  三音素GMM与单音素GMM的主要差别在于决策树状态绑定,与GMM参数更新相关的原理、程序和类两者都是一样的。
  在这个笔记中,我会首先介绍表示HMM的类HmmTopology和TransitionModel,然后介绍三音素GMM训练脚本train_deltas.sh用到的几个程序,这几个程序与单音素GMM的不同或者只在三音素GMM训练中出现。与GMM相关的其余部分请参考单音素GMM学习笔记。

  目录

HmmTopology

  为什么要介绍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_;
  /// 由transition state – 1进行索引;
  std::vector<Tuple> tuples_;
  /// Gives the first transition_id of each transition-state; indexed by
  /// the transition-state.  Array indexed 1..num-transition-states+1 (the last one
  /// is needed so we can know the num-transitions of the last transition-state.
  std::vector<int32> state2id_;
  /// For each transition-id, the corresponding transition
  /// state (indexed by transition-id).
  std::vector<int32> id2state_;
  std::vector<int32> id2pdf_id_;
  /// For each transition-id, the corresponding log-prob.  Indexed by transition-id.
  Vector<BaseFloat> log_probs_;
  /// For each transition-state, the log of (1 - self-loop-prob).  Indexed by
  /// transition-state.
  Vector<BaseFloat> non_self_loop_log_probs_;
  /// This is actually one plus the highest-numbered pdf we ever got back from the
  /// tree (but the tree numbers pdfs contiguously from zero so this is the number
  /// of pdfs).
  int32 num_pdfs_;

  构造函数的调用过程如下两图所示:
这里写图片描述

这里写图片描述

下面我们对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中的TransitionModel tm1和三音素GMM中的TransitionModel tm2不同,两者的每个数据成员都不一样,所以要把用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的每个分量的均值、方差和权值即可。


作者:许开拓
日期:写于2017-04-13
联系方式:540262601@qq.com

发布了22 篇原创文章 · 获赞 22 · 访问量 3万+
展开阅读全文

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

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览