文章目录

  • 脚本
  • 原理
  • 总体的流程介绍:
  • 流程
    • 1.初始化单音素模型
      • 1.1gmm-init-mono.cc
    • 1.2compile-train-graphs.cc
    • 2.训练单音素模型
      • 2.1align-equal-compiled.cc和gmm-align-compiled
      • 2.2gmm-acc-stats-ali
      • 2.3gmm-sum-accs
      • 2.4gmm-est
  • 模型参数输出解释:
  • 提醒
  • 资料
  • 专业名词的解释

脚本

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开始编号。

Kaldi 单音素模型训练流程与总结相关推荐

  1. kaldi单音素模型训练 - train_mono.sh脚本解读

    提示:本文适合kaldi的初学者,但最好有过运行kaldi的经验,并且大概了解EM算法.本文比较细致地对train_mono.sh脚本进行了解读,包括其源码,输入输出,以及对输出文件的内容都有详细的解 ...

  2. kaldi基于GMM的单音素模型 训练部分

    目录 1. gmm-init-mono 模型初始化 2. compile-train-graghs 训练图初始化 3. align-equal-compiled 特征文件均匀分割 4. gmm-acc ...

  3. (八)kaldi thchs30 单音素模型训练(line 62-68)

    概览 先把代码放在这里: #monophone steps/train_mono.sh --boost-silence 1.25 --nj $n --cmd "$train_cmd" ...

  4. 《kaldi语音识别实战》阅读笔记:三音素模型训练—train_deltas.sh解析

    一.使用说明 1.1 描述 训练三音素模型.与单音素模型训练相比,因为建模单元变为三音素,因此多了决策树状态绑定. steps/deltas.sh Usage: steps/train_deltas. ...

  5. Kaldi单音素GMM学习笔记

    建议在csdn资源页中免费下载该学习笔记的PDF版进行阅读:)点击进入下载页面 Kaldi单音素GMM学习笔记 目录 Kaldi单音素GMM学习笔记 原理 脚本 程序 gmm-init-mono co ...

  6. yolov5模型训练流程

    yolov5简介 YOLOv5(You Only Look Once)是由 UitralyticsLLC公司发布的一种单阶段目标检测算 法,YOLOv5 相比YOLOv4 而言,在检测平均精度降低不多 ...

  7. YOLOv5全面解析教程⑥:模型训练流程详解

    作者 | Fengwen.BBuf 欢迎Star.试用One-YOLOv5: https://github.com/Oneflow-Inc/one-yolov5 1 结构项目预览 2 安装 git c ...

  8. kaldi 学习笔记-单音素训练

    本人初入语音识别一个月, 最近开始学习kaldi源码.本文介绍kaldi语音识别对单音素训练的大致流程.欢迎指正纠错,谢谢. 0. 预备知识 单音素的训练在一个名为train-mono.sh的shel ...

  9. kaldi中文语音识别thchs30模型训练

    1.准备thchs30中文数据集 今天开始做语言模型啦,数据集采用的是清华大学的中文数据集thchs30,下载地址在http://www.openslr.org/18/ ,由于是在服务器做训练,以下步 ...

最新文章

  1. Quartz 2D Programming Guide笔记
  2. Linux下Mysql的基本操作
  3. Java文本框只有一行数据,Java只允许输入数目字的文本框
  4. docker oracle 11g
  5. 二、Merge sort
  6. 海量数据下的存储技术,哪些模式靠得住?
  7. jquery获取元素索引
  8. overflow 属性
  9. 黎活明给程序员的忠告(转)
  10. c语言写的电脑开关机代码,只需要几行代码制作电脑开关机控制软件
  11. 【游戏运营】【推荐阅读】
  12. Java项目:jsp+servlet图书管理系统
  13. 蘑菇管理定律,虽可恨,但合理
  14. 2.1 SSD算法理论
  15. java tcp门禁_门禁控制器的TCP/IP协议功能
  16. python股票相关性分析_python处理时间序列数据股票数据,并进行相关性分析
  17. 干货 | 使用云监控实现触发一个url调用
  18. 电动晾衣架的优点都有哪些?是否适合家庭选择?
  19. 工作室多wifi软路由指南
  20. 2022高教社杯全国大学生数学建模竞赛C题详细解析

热门文章

  1. 有多层棱镜和凸透镜组成的激光聚焦光学系统项目
  2. SDET Unicorns - 为什么如此难以雇用SDET?
  3. 使用EventLog Analyzer监控、管理及分析日志
  4. Python离线包安装方法
  5. 360浏览器 与 IE的兼容性模式笔记
  6. GIS开发二:批量下载和拼接地图瓦片
  7. Java实现多人聊天室
  8. 基于SSM技术的汽车销售系统
  9. Datawhale 图神经网络 Task05 超大图上的节点表征学习
  10. 玄星幻月003-PDF文件转图片并将图片拆分