Kaldi 单音素模型训练流程与总结
文章目录脚本原理总体的流程介绍:流程1.初始化单音素模型1.1gmm-init-mono.cc1.2compile-train-graphs.cc2.训练单音素模型2.1align-equal-compiled.cc和gmm-align-compiled2.2gmm-acc-stats-ali2.3gmm-sum-accs2.4gmm-est模型参数输出解释:提醒资料专业名词的解释脚本ais...
文章目录
脚本
aishell/s5/steps/train_mono.sh
原理
SLP 9.7 节 EMBEDDED TRAINING
人工标注的用于连续语音的语音识别所使用的数据集十分昂贵,并且人工在音素级别的标注工作完成的并没有达到预期的精度,所以在语音识别过程中很少使用人工标注的数据集.在实际训练中,我们使用这种名为 EMBEDDED TRAINING 的训练方法,将连续语音的分离与音素的对齐作为训练过程中迭代的过程,由一个平均分布的初始参数(FLAT STRAT)获取初始 GMM 与 HMM 参数,由此获取默认的对齐,然后在接下来的训练过程中与设定的某些轮数中更新对齐信息,再进行迭代.
在由观测序列获取对齐信息的过程中,放弃了考虑所有可能路径的 Baum- Welch 算法,使用维特比算法求解最可能的路径,这样可以减少前向与后向概率的计算,减少计算量.
在Kaldi中,单音素GMM的训练用的是Viterbi training,而不是Baum-Welch training。因此就不是用HMM Baum-Welch那几个公式去更新参数,也就不用计算前向概率、后向概率了。Kaldi中用的是EM算法用于GMM时的那三个参数更新公式,并且稍有改变。
Baum-Welch算法更新参数时,因为要计算前向后向概率,很费时间,因此使用Viterbi Training作为Baum-Welch算法的近似。在Baum-Welch算法中,计算前向后向概率时,要用到所有的状态路径,在Viterbi训练中,用Viterbi路径代替对所有状态路径的累积。
在Viterbi训练中,先根据上一轮的模型参数对语音特征数据进行对齐,得到每一帧的特征所对应的HMM状态(在kaldi中是transition-id),也就是forced alignment。Forced alignment的结果是对应于特征序列的状态序列。
举个例子:
当前的特征序列是o1, o2, o3, o4, o5, o6, o7.(每一帧的特征是39维MFCC)
对应的状态序列是7, 8, 8, 8, 9, 9, 10.(每个数字代表一个HMM state)
知道了特征序列和其对应的状态序列,我们就可以通过简单的数数来更新HMM的参数——转移概率矩阵A。根据对齐结果,统计每一个HMM状态总共出现了多少次(可以从transition-id得到HMM state-id),统计该状态的每一个转移出现了多少次(一般只有两个转移,转移到自身和转移到下一状态),用每一个转移的出现次数除以该状态的出现次数就得到了转移概率(也是转移矩阵8-8的概率是2/3,8-9的概率是1/3)。HMM参数就是这样更新的。
首先应该明白,在单音素GMM训练中,每一个HM M状态有一个对应的GMM概率密度函数(pdf),所以有多少个HMM状态,就有多少个GMM,也就有多少组GMM参数。在知道了特征序列和对齐序列后,找出某一个HMM状态对应的所有观测(比如状态8对应的o2, o3, o4,在kaldi中则是找到某一transition-id对应的所有观测),也就得到了该状态对应的GMM所对应的所有观测。知道了该GMM对应的所有观测、该GMM的当前参数,就可以根据GMM参数更新公式更新GMM参数了,比如知道了状态8对应的观测o2, o3, o4。Kaldi中所用的GMM参数更新公式如下图所示。
然后再解释一下各个名词的含义:
kaldi对每个音素建立一个HMM模型,叫做HMMEntry,由三个state组成,定义在hmm-topology.cc 中。 如上图所示,transition state是一个整数,对应于三个变量:phone的ID号,三个hmm-state(0,1,2),以及pdf-id(P.d.f 为GMM模型的概率密度函数,不同的pdf-id对应了不同的概率密度的参数)。transition-index 对应于一个pair,也就是下一个state的ID和到下一个state的transmission/emitting probability. 而transition-ID 也对应一个pair,为transition state和transition index。简单点来说,transition-index就是那个状态之间指向的箭头。
这样说可能不是很明白。再做一点解释,这些id号都是为了表达抽象的HMM转移模型,如果你需要在数学上表达一个transition,你需要知道在哪个phone上,而每个phone由多个state组成,每个state有多条边(Arc),这些边可能是self-loop,可能transmit 到下一个state,也可能emit 等等。这些边中的第几个就是transition-index,而在哪个phone,phone中哪个state,这个state的emit后的GMM概率密度表达, 这三个共同决定了transition state,因此,为了精确表达一个transition-id,需要transition state和transition-index 共同决定。
总体的流程介绍:
1.初始化单音素模型。调用gmm-init-mono,生成0.mdl、tree。
2.编译训练时的图。调用compile-train-graph生成text中每句抄本对应的fst,存放在fsts.JOB.gz中。
3.第一次对齐数据。调用align-equal-stats-ali生成对齐状态序列,通过管道传递给gmm-acc-stats-ali,得到更新参数时用到的统计量。
4.第一次更新模型参数。调用gmm-est更新模型参数。
5.进入训练模型的主循环:在指定的对齐轮数,使用gmm-align-compiled对齐特征数据,6.得到新的对齐状态序列;每一轮都调用gmm-acc-stats-ali计算更新模型参数所用到的统计量,然后调用gmm-est更新模型参数,并且在每一轮中增加GMM的分量个数。
流程
1.初始化单音素模型
1.1gmm-init-mono.cc
作用:初始化单音素GMM
该程序有两个输入和两个输出,作为输入,需要描述声学模型的 HMM 音素结构的拓扑文件和高斯混合模型中每个分量的维度.输出的话是模型文件和决策树文件
Usage:
gmm-init-mono In Kaldi:
gmm-init-mono data/lang/topo 39 exp/mono/0.mdl exp/mono/tree
1.计算所有特征数据每一维特征的全局均值、方差(均为1,大小的话等于特征的维度)
2.读取topo文件,创建共享音素列表(根据$lang/phones/sets.int),根据共享音素列表创建ctx_dep(决策树),对于单音素,不需要进行KMeans聚类,所以只需要根据set.int文件(该文件描述共享根结点的phone)递归的构造树就好了。
构造决策树的过程:
CE(Constant EventMap):叶子节点,直接保存该节点存放的transition ID;
SE(Split EventMap): 非叶子节点,保存左右子节点yes,no等等
TE(Table EventMap): 非叶子节点,保存节点数以及各个子节点的指针(与SE的区别在于TE的分裂更快,不只是两个分支,可以多个)
现在开始讲解GetStubMap:首先该函数输入phone_sets,所有共享的音素在一个vector中,所有的这些vector组成了phone_sets, 如果phone_sets的大小是1且其中phone都共享,返回CE,否则返回TE;如果phone_sets中每个vector的大小最大是1,并且phone_sets中vetor的数目小于最大phoneId的两倍,也返回TE;如果不是上述两种情况,进行split,phonesets分为两部分分别递归call GetStubMap,并构建SE。
3.每一组共享音素的一个状态对应一个Pdf。对每一个状态,创建只有一个分量的GMM,该GMM的均值初始化为全局均值、方差初始化为全局方差。
4.根据ctx_dep和topo创建转移模型。将转移模型、GMM声学模型写到0.mdl
5.将ctx_dep写到tree.
1.2compile-train-graphs.cc
我们已经得到了决策树和模型.接下来的命令创建训练集对应的 HCLG 图归档文件.该程序为每一个训练语句编译 FST.
Usage:
compile-train-graphs [options]
Example:
compile-train-graphs exp/mono/tree exp/mono/0.mdl
\data/L.fst ark:data/train.tra ark:exp/mono/graphs.fsts
train.tra 是训练集的索引文件,该文件每一行的第一个字段是说话人 ID.
该程序的输出是 graphs.fsts;其为 train.tra 中的每句话建立一个二进制格式的 FST.该 FST 和 HCLG 对应,差异在于转移概率不在里面.这是因为该图将在训练中被多次用到且转移概率也将会发生变化,所以后面才会加上转移概率.但是归档的 FST 中包含了 silence 的概率(编码到 L.fst).
解码图就是 HCLG=H∘C∘L∘G.
1.H 包括了 HMM 定义;输出符号是上下文无关的音素,输入符号是一系列状态转变 ID(是概率 id 和其它信息的编码).
2.C 代表的是上下文依赖关系.其输出代表音素的符号,输入是代表上下文相关的音素符号;
3.L 是字典(Lexicon);输出是单词,输入是一系列音素.
4.G 表示的是 Grammar;是语音模型的有限状态机.
构图过程 G -> L -> C -> H
上面图中,圆圈表示的就是状态,里面的数字是状态的id。弧上面,冒号前面的数字时transition-id,冒号后面的数字是输出的音素;斜杠后面的数字是权重。
其中0-7-8-9-0这条“路径”,表示的就是音素Y; 0-10-11-12-0表示的就是音素N。
2.训练单音素模型
2.1align-equal-compiled.cc和gmm-align-compiled
这两个一起将的原因是功能比较类似,区别是前者在真正的训练前执行一次即可,后者在训练时调用。
他们的输入都是上一步得到的HCLG.fst的图,还有对音频文件进行特征提取(Feature extraction, MFCC, CMVN, etc)。而输出都是alignment,简单来说就是语音每帧对应的HMM state,在kaldi采用transition ID表示,因为之前说过transion ID是可以映射到唯一的HMM state.
对于align-equal-compiled,执行在真正训练之前,因为开始我们输入只有一个图,要得到alignment,需要对图进行viterbi 解码,找到最优路径。但是这里由于我们刚刚初始化,图里面各个转移概率都是初始化的值,没有进行更新,解码没有任何意义,因此我们直接随机产生一条路径,该路径符合以下要求:路径sequence的长度等于语音帧数,为此可能需要一定数目的self-loop作为补充,这样才能使NumOfFrames = non-selfloop’s ilabels + self-loops’s iabels。 该过程的具体代码见函数EqualAlign()。 最后,我们取该路径的所有ilabels作为Alignment输出。
对于gmm-align-compiled, 没有太大差别,但是这里我们使用WFST图中各个转移概率进行解码(采用faster-decoder)得到最佳路径而不是随机生成的路径。这是因为gmm-align-compiled 在训练中执行,而训练中我们不断更新WFST中各个概率参数,使得解码更加准确。 具体函数为AlignUtteranceWrapper()最后,我们取最佳路径的所有ilabels作为Alignment输出。
2.2gmm-acc-stats-ali
输入:模型model,特征(MFCC),对齐序列alignment
输出:用于训练的统计量
首先读取transition模型 和 am-gmm模型信息。
对于每个音频文件和对应的对齐序列,我们进行如下操作:遍历每一帧,因为每一帧都可以从对齐序列里找到所对应的transition-id, 用一个vector记录transitio-id出现的次数,之后用于更新转移概率。同时由transition-id找到所对应的pdf-id,建立一个vector,大小为pdf-id的数目,函数ComponentPosteriors() 计算之前图片中EM算法中E步的响应度,kaldi里调用函数LogLikelihoods()计算数据data也就是每帧语音特征在GMM中每个单高斯出现的概率,存在loglikes里面。
接下来还调用了函数AccumulateFromPosteriors() 这里对M步进行了一个预计算,也就是计算了上述图片中M步三个公式的分子部分,在代码里,三个分子的数据存在occupancy_,mean_accumulator_,covariance_accumulator_三个vector中。
上述所有计算后的统计量都放在AccumAmDiagGmm gmm_accs;中,作为*.acc 输出。
2.3gmm-sum-accs
gmm-acc-stats-ali 生成的累计量分散在 JOB 个文件中,该程序将分散的对应同一 trans-id、pdf-id 的累计量合并在一起,具体用法在 gmm-est 的实现中可见.
2.4gmm-est
这是训练的最后一步,输入是gmm-init-mono初始化的模型或者训练时上一步训练后的模型,和gmm-acc-stat-ali计算的统计量,输出也是模型。
Usage:
gmm-est [options]
这步分为两个部分讲解,一个是EM算法的M步,另一个是Split,因为我们初始化中GMM只有一个单高斯,在训练的时候我们会split出多个,最后使得每个GMM模型中高斯数目等于我们想要的。
M步之前还要更新模型的转移概率,还记得我们在gmm-acc-stat-ali统计好了每个transition-id出现的次数吗,我们就用它来更新,很简单,我们直接用出现的次数除以总数就得到了转移概率,具体代码见函数:
void TransitionModel::MleUpdate(const Vector &stats,const MleTransitionUpdateConfig &cfg,BaseFloat *objf_impr_out,BaseFloat *count_out)
模型参数输出解释:
所有的音素以及对应的HMM的拓扑结构:
这里的2-217这些整数各自代表了一个音素,每一个整数唯一对应一个音素。
并且这些音素的对应拓扑结构如下,是一个有着三个状态的拓扑结构。
他给出了各个状态之间转移的概率。以及每个状态的他们的pdf-id的值。
同理,这边给出了音素1对应的拓扑结构,因为第一个音素有点特殊,他是静音音素,所以他的结构是由五个状态的拓扑结构。
可以看到这个结构体中的元素,phones中存储了我们各个音素代表的整数。相当于一个栈
phones2idx存储了音素以及每一个音素对应的拓扑及结构。相当于数组。
entries代表了每一个音素对应的HMM实体,里面有他的状态,还有pdf-id什么的
第一维是音素的id,第二维是hmm_stat,第三维是pdf。
如果只有三维的话,他的pdf是关联在hmm_stat上,如果是四维的话是关联在他的弧上。
简单点说,两者的区别就是,三维的时候pdf-id是对应在状态上的,而四维的时候,pdf-id是对应在他的transition-index上的,就是每个状态他都有指向自己以及指向下一个状态的两条弧,这两条弧分别对应了一个pdf-id
补充解释一下:为什么第二维的状态一直都是0-4或者0-2,而第三维pdf-id是从0一直往上增长?
首先,我们之前也说过pdf-id具有唯一性,不同的pdf-id的值对应不同的概率密度函数。所以我们的pdf-id的值都是不一样的。
其次我们可以画个图理解一下。因为每个音素对应一个HMM实体,所以每个实体中的状态当然都是从0-2.
这个图上的信息,首先是WIGHT(权重),这个是指高斯混合模型的权重,高斯混模型是由多个单独的高斯模型加权求和而来,这个就是指各个高斯模型的权重
接下来的MEAN_INBBARS,指的是均值,方差的一些数据信息, 可以参考EM算法,EM算法的第E步的相应值,以及M步的求均值方差的分母的值,我们都可以将他们在计算中记录下来,这样就不用一遍一遍的去进行重复的计算,每次需要这个值的时候,都可以直接去查询这个值。
这么做的主要目的就是为了加速计算,提高EM算法的计算速度。
提醒
我们需要重点理解以下的内容:
关于transition-id、多高斯分裂的源代码和保存EM算法M步分母相同部分的原理。
资料
[1]开拓师兄的博客
https://blog.csdn.net/u010731824/article/details/69668765
[2]网上参考的博客
https://blog.csdn.net/fengzhou_/article/details/77996244
https://www.jianshu.com/p/7401399fceda
https://blog.csdn.net/u013677156/article/details/79136418
https://blog.csdn.net/u013677156/article/details/77893661
https://blog.csdn.net/dearwind153/article/details/70053704
专业名词的解释
alignment: 由话语的viterbi(最佳路径)对齐所获得的hmm状态序列的表示。在kaldi中,对齐是转换id序列的同义词。大多数情况下,对齐是通过对齐话语的参考转录本而产生的,在这种情况下,它被称为强制对齐。格也包含对齐信息,作为格中每个字序列的转换id序列。
cost: 在加权fst算法中用作“成本”的任何数量(例如声学成本、语言模型成本;有关详细信息,请参阅kaldi中的格)。一般来说,成本可以解释为一个可能性或概率的负对数,但可能涉及标度因子。
lattice:一个话语的替代性可能转录的表示,以及相关的对齐和成本信息。
likelihood:一个数学概念,指代表连续值分布的函数值。这些可能不止一个。由于多维特征的似然值通常太小或太大,无法满足标准浮点精度,所以通常用对数空间表示(作为对数似然)。利用标准的交叉熵训练神经网络系统,通过将对数概率除以上下文相关状态的先验值,得到“伪似然”。
posterior:“后验”是“后验概率”的缩写,后验概率是一个非常普遍的数学概念,通常指“看到相关数据后某个随机变量的概率”。一般来说,后验的总和是1。在kaldi术语中,如果你遇到术语“后验”,缩写为“post”,没有进一步的解释,它通常意味着每帧后验概率的transition-ids。
phone: 音素,从1开始编号。可以根据phones.txt映射为具体音素
HMM-state: 音素HMM模型的状态,从0开始编号
pdf-id: 决策树和声学模型中用到的pdf的编号,从0开始
transition-state: 一个(虚拟的)状态,通过弧跳转到自己或其他状态。某些情况下,可以跟pdf-id一一对应。
transition-index: HMM状态中转移的索引,即HmmTopology::HmmState::transitions的索引,从0开始编号
transition-id: 所有的HMM状态的弧进行编号。从1开始编号。
更多推荐
所有评论(0)