Kaldi(A4)model文件分析

上一节提到了解码需要声学模型(final.mdl)和语言模型(HCLG.fst),这节就来看一看这个model文件里是什么东西。

Ref

Dan's DNN implementation http://kaldi-asr.org/doc/dnn2.html
Kaldi Lecture 4
Decoders used in the Kaldi toolkit http://kaldi-asr.org/doc/decoders.html
kaldi yesno example http://blog.csdn.net/shichaog/article/details/73264152?locationNum=9&fps=1
单音素GMM学习笔记 http://www.itdadao.com/articles/c15a1230377p0.html


model文件是什么?

Kaldi官网上给出的OnlineDecoder Demo用到的是online2-wav-nnet2-latgen-faster这个命令,于是查看了一下它的源码,关于这个Model的部分如下

//nnet2_rxfilename便是.mdl文件
        TransitionModel trans_model;
        nnet2::AmNnet nnet;
        {
            bool binary;
            Input ki(nnet2_rxfilename, &binary);
            //trans_model和nnet声学模型都在final.mdl里面
            trans_model.Read(ki.Stream(), binary);
            nnet.Read(ki.Stream(), binary);
        }

由以上代码便可知道model文件包含了HMM的部分拓扑结构(即TransitionModel,更完整的拓扑结构在HCLG.fst中)以及各个特征对应的概率(即声学模型AM),通过声学模型加上TransitionModel就能知道这个特征属于HMM中的哪个状态,这个完整的HMM结构就是HCLG.fst。

HCLG.fst文件是什么?

音素由状态状态组成,单词由音素组成,句子由单词组成,只要这要一步步还原就能将上面过程得到的状态还原为文字。HCLG.fst便对应这个过程的转化。

  • H: HMM,将状态还原为音素。实际上输入Kaldi这个HCLG.fst的并不是状态,而是一串由TransitionModel产生出来的transition-id,不过理解成状态并无大碍。
  • L: Lexicon,即一个单词是怎么读的,将音素还原为单词。可以理解为一个单词的音标是怎样的。
  • G: Grammar,即单词是如何组成句子的。将单词合理地组成句子。
  • C: Context-dependency,由它来构建音素之间的上下文相关性,即形成triphone。这个过程是实时构建的(原文为on-the-fly,大概意思是这个C是在内存里动态构建的?)

NNet2 Model文件分析

感觉DNN的Model文件比GMM的容易懂一些
Ref1中提到,这个Model输入的是一个窗口内的特征串,输出维数对应决策树的叶节点数量,这样就能得出这个特征串最可能是HMM中的哪个pdf了。先来使用nnet-am-info命令查看一下librispeech经过nnet2训练得到的model里面的声学模型

num-components 17
num-updatable-components 5
left-context 7
right-context 7
input-dim 140
output-dim 5816
parameter-dim 10351000
component 0 : SpliceComponent, input-dim=140, output-dim=700, context=-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 , const_component_dim=100
component 1 : FixedAffineComponent, input-dim=700, output-dim=700, linear-params-stddev=0.00203155, bias-params-stddev=0.0310742
component 2 : AffineComponentPreconditionedOnline, input-dim=700, output-dim=3500, linear-params-stddev=0.986785, bias-params-stddev=4.42767, learning-rate=0.001, rank-in=20, rank-out=80, num_samples_history=2000, update_period=4, alpha=4, max-change-per-sample=0.075
component 3 : PnormComponent, input-dim = 3500, output-dim = 350, p = 2
component 4 : NormalizeComponent, input-dim=350, output-dim=350
component 5 : AffineComponentPreconditionedOnline, input-dim=350, output-dim=3500, linear-params-stddev=1.0001, bias-params-stddev=0.983017, learning-rate=0.001, rank-in=20, rank-out=80, num_samples_history=2000, update_period=4, alpha=4, max-change-per-sample=0.075
component 6 : PnormComponent, input-dim = 3500, output-dim = 350, p = 2
component 7 : NormalizeComponent, input-dim=350, output-dim=350
component 8 : AffineComponentPreconditionedOnline, input-dim=350, output-dim=3500, linear-params-stddev=1.00021, bias-params-stddev=0.944995, learning-rate=0.001, rank-in=20, rank-out=80, num_samples_history=2000, update_period=4, alpha=4, max-change-per-sample=0.075
component 9 : PnormComponent, input-dim = 3500, output-dim = 350, p = 2
component 10 : NormalizeComponent, input-dim=350, output-dim=350
component 11 : AffineComponentPreconditionedOnline, input-dim=350, output-dim=3500, linear-params-stddev=1.0002, bias-params-stddev=0.943781, learning-rate=0.001, rank-in=20, rank-out=80, num_samples_history=2000, update_period=4, alpha=4, max-change-per-sample=0.075
component 12 : PnormComponent, input-dim = 3500, output-dim = 350, p = 2
component 13 : NormalizeComponent, input-dim=350, output-dim=350
component 14 : AffineComponentPreconditionedOnline, input-dim=350, output-dim=12000, linear-params-stddev=0.607722, bias-params-stddev=0.874339, learning-rate=0.001, rank-in=20, rank-out=80, num_samples_history=2000, update_period=4, alpha=4, max-change-per-sample=0.075
component 15 : SoftmaxComponent, input-dim=12000, output-dim=12000
component 16 : SumGroupComponent, input-dim=12000, output-dim=5816
prior dimension: 5816, prior sum: 1, prior min: 2.69233e-06
LOG (nnet-am-info[5.3.31~1-4e3c1]:main():nnet-am-info.cc:76) Printed info about final.mdl

可以看到librispeech语料库训练得到的层数为16层,Ref1中提到的RM数据集的mdl层数则是10层,估计大部分时间都用在了这个网络的计算中。影响实时性的元凶找到了,重新训练一个小一点的网络结构吧(说的可真轻松呢)

GMM Model文件分析

Ref4中提到的有些概念可能会让人误解,还有些数据感觉有问题,这里仅以我理解的方式分析一下。
切换到yesno/s5/exp/mono0a目录下,用~/kaldi-bak/src/gmmbin/gmm-copy --binary=false 0.mdl -命令将二进制的mdl文件显示在标准输出中。

<Topology>

这个包含的就不必多说了,就是整个HMM的结构和初始参数(0.mdl和40.mdl这一部分的参数均相同,可见转移概率等应该是储存在HCLG.fst文件中的)了

<Triples>

共有11个,对应11个状态双圆圈,如下图


11个状态

如Kaldi官网HMM部分所说,

Each possible triple of (phone, hmm-state, pdf) maps to a unique transition-state.

<LogProbs>

  [ 0 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -1.386294 -0.2876821 -1.386294 -0.2876821 -1.386294 -0.2876821 -1.386294 -0.2876821 -1.386294 -0.2876821 -1.386294 -0.2876821 -1.386294 -0.2876821 -1.386294 ]

这里共有1(进入HMM的初始概率为1,log后为0)+30个值(不知道Ref4的作者是不是打错了)

30个Transition

也就是说这个LogProbs是和transition-id对应起来的,描述了转移概率。

<DIMENSION> 39 <NUMPDFS> 11 <DiagGMM>

输入的MFCC特征为39维,需要求11个状态的概率密度函数,后续的一些值就是记录这11个状态具体的GMM描述的概率密度函数啦。一个GMM原原本本的参数需要以下参数:分量权重weights_,均值means_,方差vars_,不过为了方便计算,在里面记录了每一分量多维高斯分布里的常量部分取log后的数值gconsts_,方差每一元素求倒数后的inv_vars_、均值乘以inv_vars_后的means_invvars_

RM Model文件分析

从官网上下载各个阶段训练出的model文件,http://kaldi-asr.org/downloads/build/4/trunk/egs/rm/s5/exp/,并转为文本格式,~/kaldi-bak/src/gmmbin/gmm-copy --binary=false 0.mdl 0_text.mdl,然后就可以像上面那样分析一下声学模型是如何构成的,这里只比较一下Monophone单音素Triphone三音素之间的差异。

Monophone阶段

  • 1~5为SIL,含5个状态
  • 6~193为发音音素,含3个状态
  • 共有5*5+(194-6)*3=589个transition state
  • 得益于决策树的聚类, PDF数量减少了一些,为146

Triphone 1阶段

  • 1~5为SIL,含5个状态
  • 6~193为发音音素,含3个状态


    Ref:Tree-Based State Tying for High Acoustic Accuracy Modelling
  • 单音素内部三个状态是排列好的,所以之前的计算是5*5+(194-6)*3=589个Transition;
    现在三音素的三个状态可以从相关的别的音素那里去组合了,所以transition state增加到了5662
  • PDF 1435

关于实时性

观察一下解码等命令所用的时间,

各部分的用时

IO时间
可以注意到将final.mdl这个接近40MB的文件给读入内存还是需要一定时间的,而且在Decoder's latticeAlign lattice里面都会用到它,因此考虑在程序启动的时候就将它读入内存,后续解码的时候直接使用。因此假如想移植到安卓,通过JNI操作的话就需要全局变量了。

解码时间
也就是第二个蓝框里的时间,这部分耗时是最长的,目前的想法就是分析这个解码运算集中在哪里,进行优化来提高实时性了。这也是为什么要分析model文件的原因。
分析一下Nnet2方式和GMM方式计算速度到底谁更快以及怎么优化: Nnet2输入特征找到最可能对应的pdf-id,在这个过程中计算了分别属于所有pdf-id的概率; GMM则可计算指定个数的pdf-id,找一个概率最大的出来。但是GMM是只找了该特征属于部分pdf-id的概率,还是找了所有的呢?Ref3中提到的

The function LogLikelihood() returns the log-likelihood for this frame and index; the index would normally be the (one-based) transition-id, see Integer identifiers used by TransitionModel. The frame is a zero-based quantity. The most normal DecodableInterface object will just look up the appropriate feature vector (using the index "frame"), work out the pdf-id corresponding to that transition-id, and return the corresponding acoustic log-likelihood.

似乎说明是找部分pdf-id去进行计算的,但是实际运行过程中发现GMM和NNET2解码时间相差并不大。

总结

model文件包含声学模型和transition model用于找出特征对应的状态的位置,再将状态扔到HCLG.fst中构建出所有状态组合成的句子。除开GMM和NNET2之外,还有NNET3模型没有分析,留到后面再说了。另外HCLG.fst是如何构建的?下一篇文章见。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 数据准备 local/timit_data_prep.sh 生成的内容放在data/local/data中。对于t...
    kaituoxu阅读 8,896评论 0 10
  • 承接前面的《浅谈机器学习基础》、《浅谈深度学习基础》和《浅谈自然语言处理基础》,主要参考了《解析深度学习:语音识别...
    我偏笑_NSNirvana阅读 23,291评论 6 67
  • 上篇语音识别原理通俗地介绍了ASR,这一篇将会简单介绍一下前一篇提到的FST。 Ref Kaldi HMM htt...
    Seeker_zz阅读 7,796评论 0 5
  • 这一篇文章其实是参考了很多篇文章之后写出的一篇对于语言模型的一篇科普文,目的是希望大家可以对于语言模型有着更好地理...
    云时之间阅读 4,389评论 2 8
  • 喜欢灯 莫名的喜欢 那一次我坐在出租车里,已经是晚上七点多了,路灯已经亮起,无意中的一张照片让我喜欢上了灯,我并没...
    时光不矜持啊阅读 177评论 0 0