Kaldi脚本分析(3)——单音素建模

1.1单音素模型脚本流程

单音素建模脚本(steps/train_mono.sh):

#monophone

steps/train_mono.sh--boost-silence 1.25 --nj$n --cmd "$train_cmd" data/mfcc/train data/lang exp/mono \

 || exit 1;

输出在exp/mono中,下面按脚本train_mono.sh运行流程逐项说明。

 

1.1.1       特征倒谱归一化(Cepstralnormalization

exp/mono/cmvn.logexp/mono/cmvn.ark:对每个说话人,计算归一化倒谱均值和方差的统计值

#cepstralnormalization

#feature: add"delta" and "accelaration", dim = 39

feats="ark,s,cs:apply-cmvn$cmvn_opts --utt2spk=ark:$sdata/JOB/utt2spkscp:$sdata/JOB/cmvn.scp scp:$sdata/JOB/feats.scp ark:- | add-deltas ark:- ark:- |"

example_feats="`echo$feats| sed s/JOB/1/g`";

 

1.1.2       模型初始化(Modelinitialization

#modelinitialization

#options used toget plausible means and variances

#tree isphonetic-context decision tree-- doesn’t have any splits in monophone case.

$cmd JOB=1$dir/log/init.log \

    gmm-init-mono $shared_phones_opt"--train-feats=$feats subset-feats --n=10 ark:- ark:-|"$lang/topo$feat_dim  $dir/0.mdl $dir/tree || exit 1;

  • 运行log:exp/mono/log/init.log
  • 调用gmm-init-mono命令行工具,即gmm-init-mono.cc中的函数。

gmm-init-mono<topology-in> <dim> <model-out> <tree-out>

  • 双引号中为一些命令行选项,确保求得的均值方差是可行的。
  • 输入:HMM拓扑文件data/lang/topo和GMM特征维度39。
  • 输出:模型文件exp/mono/0.mdl和决策树exp/mono/tree。

 

注1:单音素决策树


--可以通过copy-tree命令行工具查看音素上下文决策树文件。

--决策树文件中存储的是多态类型对象EventMap。

--TE指的是TableEventMap,表示树的各种节点的查询Table。

--CE指的是ConstantEventMap,表示树的叶子节点。

--SE指的是SplitEventMap,表示树的分支,而在单音素模型中不需要做split处理。

--“TE 0 219”表示从第0个音素开始分裂,这里0指向NULL,因为phone-id中0是为epsilon保留的。随后是219个non-NULL event map。

--“TE -1 3 ( CE5 CE 6 CE 7 ) ”这个字符串表示一个音素的分裂从key-1开始,这个key表示拓扑结构中的pdfclass,在这里也就是HMM状态,其中,-1是初始状态,0、1、2是 HMM 中间的转移状态,3 是结束状态。中间的3个转移状态可以有对应的CE,每一个表示决策树的一个叶子节点。

 

注2:HMM拓扑文件

--可以通过cat data/lang/topo查看。

--给定了所有非静音音素的3状态left-to-right型HMM结构,并给出了开始训练前默认的状态转移概率。

--静音音素单独给出了5状态的HMM结构,具有更多的状态转移。

 

注3:模型文件0.mdl

--通过gmm-copy命令行工具查看模型文件gmm-copy --binary=false exp/mono/0.mdl - | less

--模型文件中包含两部分,一部分是TransitionModel(转移模型),其中包含HMM拓扑结构、triples以及转移概率;另一部分是AmGmm(声学模型)。模型文件中的对象都不是Table,所以直接通过Read/Write进行对象读写。写入是 binary or text,取决于命令行选项 --binary。

--Topology信息中,有一个音素(标号1)与其他音素的拓扑结构不同,对比phones.txt可知该音素为sil(代表静音silence)。

--topo 文件的惯例,初始状态和结束状态(概率为 1)为非发射状态。在如下所示的拓扑结构里,-1 是初始状态,0、1、2是 HMM 中间的转移状态,3 是结束状态。

--每一个Triple表示一个三元组(phone,hmm-state,pdf-id),与转移状态一一对应。

--<NUMPDFS>即pdf总数,与Triple总数一致。

 




1.1.3       预编译训练图模型(Compiletraining graphs

#compile training graphs(FSTs)

#one for each train utterance

#encode HMM structure for that training utterance

#we precompile them because otherwise this would dominatetraining time

if [$stage -le -2 ];then

  echo"$0: Compiling training graphs"

  $cmd JOB=1:$nj$dir/log/compile_graphs.JOB.log\

   compile-train-graphs --read-disambig-syms=$lang/phones/disambig.int$dir/tree $dir/0.mdl  $lang/L.fst \

    "ark:sym2int.pl --map-oov$oov_sym -f 2-$lang/words.txt <$sdata/JOB/text|" \

    "ark:|gzip -c >$dir/fsts.JOB.gz" ||exit 1;

fi

 


  • 运行log:exp/mono/log/compile_graphs.log
  • 调用compile-train-graphs命令行工具,编译FSTs,每个训练语句得到一个FST。
  • n  compile-train-graphs[options] <tree-in> <model-in> <lexicon-fst-in><transcriptions-rspecifier> <graphs-wspecifier>
  • 构造训练的fst网络,从源码级别分析,是每个句子构造一个phonelevelfst网络。这里采用预编译的原因是,可以尽量减少占用训练时间。$data/text中包含对每个句子的单词级别(words level)或音素级别(phone level)的标注, L.fst是词典的fst表示,作用是将一串的音素转换成单词。构造monophone解码图就是先将text中的每个句子,生成一个fst(类似于语言模型中的G.fst,只是相对比较简单,只有一个句子),然后和L.fst进行composition 形成训练用的音素级别(phone levelfst网络(类似于LG.fst)。fsts.JOB.gz中使用 key-value 的方式保存每个句子和其对应的fst网络,通过 key(句子索引)就能找到这个句子的fst网络,value中保存的是句子中每两个音素之间互联的边(Arc,例如句子转换成音素后,标注为:"a b c d e f",那么value中保存的其实是 a->b b->c c->d d->e e->f这些连接(kaldi会为每种连接赋予一个唯一的id),后面进行 HMM训练的时候是根据这些连接的id进行计数,进一步得到音素内(intra)状态的转移概率和音素间(inter)状态的转移概率。
  • 输入:决策树文件,模型文件,词典fst文件,训练文件标注transcript_rspecifier(Table格式输入)。
  • 输出:训练图fsts_wspecifier(Table格式输出)。

注1   :训练图模型

--可以通过fstcopy命令将存档格式转到标准输出查看

--存档格式:utt-idgraph utt-id graph...

--其中graph格式:from-state to-state input-symboloutput-symbol cost

--input-symbol,传统意义上是指概率密度函数,GMM中每个混合分量都有一个值。但这样会增加训练转移概率和音素对齐时的难度,所以Kaldi中将input-symbol定义为转移标识符trans-id。

--output-symbol是word.txt中的词。

--cost是转移概率,其中包含发音概率,对于预编译训练图来说,除初始状态外没有转移概率(后面训练时加入)。

注2  :rspecifier/wspecifier

--这两个字符串指明了Table文件是archiv还是script-file,文件在磁盘上的存储位置(普通文件、管道还是标准输入输出)以及其他的选项信息。

 

1.1.4       第一次对齐和求累积统计量(Firstalignment stage

#first alignmentstage

#produces alignments“equally spaced” for each utterance, accumulates 1st iteration stats.

#an alignment isa vector of ints (per utterance)

#Note: we doViterbi training not forward-backward, means we use 1-best path.

if [$stage -le -1 ];then

  echo"$0: Aligning data equally(pass 0)"

  $cmd JOB=1:$nj$dir/log/align.0.JOB.log \

    align-equal-compiled"ark:gunzip -c $dir/fsts.JOB.gz|""$feats" ark,t:-  \| \

    gmm-acc-stats-ali --binary=true$dir/0.mdl "$feats" ark:- \

    $dir/0.JOB.acc ||exit 1;

fi

  • 运行log:exp/mono/log/align.0.log
  • 调用align-equal-compiledgmm-acc-stats-ali命令行工具
align-equal-compiled <graphs-rspecifier> <features-rspecifier> <alignments-wspecifier>
gmm-acc-stats-ali [options] <model-in> <feature-rspecifier> <alignments-rspecifier> <stats-out>
  • 对每个语句采用等空间对齐(alignments“equally spaced”),并累积迭代统计量,开始HMM-GMM训练。
  • 对每个语句来说,对齐相当于一个整数向量vector<int32>,其中存储trans_id的序列,它的长度与需要对齐的语句一样长,即一帧数据提取的一个特征向量对应得到一个trans_id,其中trans_id与(trans_state,trans_index)二元组一一映射,而trans_state与(phone,hmm_state,pdf)三元组一一映射,pdf由GMM计算。对齐(alignments)通常用在训练阶段的Viterbi算法和测试阶段的自适应算法。音素trans_id编码了音素的信息,所以从对齐中可以计算出音素的序列。
  • 训练转移概率时,累积的统计量就是数每个trans_id在训练阶段出现了多少次。
  • 由于采用Viterbi算法训练而不是前向-后向算法,所以只选一个最佳路径。
  • align-equal-compiled输入:训练图fst_rspecifier(Table格式输入),特征feature_rspecifier(Table格式输入)。
  • align-equal-compiled输出:对齐向量alignment_wspecifier(Table格式输出)。
  • gmm-acc-stats-ali输入:初始模型文件0.mdl、特征文件(Table格式输入)、对齐向量(Table格式输出)。
  • gmm-acc-stats-ali输出:初始累积统计量0.acc

 

1.1.5       第一次更新(Firstupdate

# In thefollowing steps, the --min-gaussian-occupancy=3 option is important,

# otherwise wefail to est "rare" phones and later on, they never align properly.

 

#first update

#transitionmodel update

#gmm update

#split gauss

#modelfile(.mdl) contains transition-model object, then GMM object

if [$stage -le 0 ];then

  gmm-est --min-gaussian-occupancy=3  --mix-up=$numgauss --power=$power \

    $dir/0.mdl"gmm-sum-accs- $dir/0.*.acc|"$dir/1.mdl 2>$dir/log/update.0.log||exit 1;

  rm $dir/0.*.acc

fi


  • 运行log:exp/mono/log/update.0.log
  • 调用gmm-estgmm-sum-accs命令行工具

gmm-est [options]<model-in> <stats-in> <model-out>
gmm-sum-accs [options]<stats-out> <stats-in1> <stats-in2> ...

  • 状态转移(Transition-model)更新,GMM更新,状态split
  • 输入:初始模型exp/mono/0.mdl,GMM统计量exp/mono/0.acc
  • 输出:第一次更新后的模型exp/mono/1.mdl

 

1.1.6       建模过程(Modelbuilding schedule

beam=6 # will change to 10 below after 1st pass

# note: usingslightly wider beams for WSJ vs. RM.

 

#model buildingschedule

x=1

while [$x -lt $num_iters ]; do

  echo"$0: Pass$x"

  if [$stage -le $x ]; then

    ifecho$realign_iters | grep -w$x >/dev/null;then

      echo"$0: Aligning data"

      mdl="gmm-boost-silence --boost=$boost_silence `cat$lang/phones/optional_silence.csl`$dir/$x.mdl - |"

    #newalignment

      $cmd JOB=1:$nj$dir/log/align.$x.JOB.log \

        gmm-align-compiled$scale_opts --beam=$beam --retry-beam=$[$beam*4] --careful=$careful"$mdl" \

        "ark:gunzip -c$dir/fsts.JOB.gz|""$feats""ark,t:|gzip -c >$dir/ali.JOB.gz" \

        || exit 1;

    fi

    $cmd JOB=1:$nj$dir/log/acc.$x.JOB.log \

      gmm-acc-stats-ali $dir/$x.mdl"$feats""ark:gunzip -c$dir/ali.JOB.gz|" \

      $dir/$x.JOB.acc ||exit 1;

    #newupdate

    $cmd$dir/log/update.$x.log \

      gmm-est --write-occs=$dir/$[$x+1].occs --mix-up=$numgauss --power=$power$dir/$x.mdl \

      "gmm-sum-accs-$dir/$x.*.acc|"$dir/$[$x+1].mdl || exit 1;

    rm $dir/$x.mdl$dir/$x.*.acc$dir/$x.occs 2>/dev/null

  fi

  if [$x -le $max_iter_inc ]; then

     numgauss=$[$numgauss+$incgauss];

  fi

  beam=10

  x=$[$x+1]

done

  • 多次对齐(newalignment):调用gmm-align-compiledgmm-acc-stats-ali命令行工具,输出对齐向量和累积统计量。

gmm-align-compiled[options] model-in graphs-rspecifier feature-rspecifier alignments-wspecifier[scores-wspecifier]
gmm-acc-stats-ali[options] <model-in> <feature-rspecifier><alignments-rspecifier> <stats-out>

  • 多次更新(newupdate):调用gmm-est命令行工具,输出更新后的模型(更新转移模型参数和GMM参数)。间接调用gmm-sum-accs命令行工具,计算累积统计量总和。

gmm-est [options]<model-in> <stats-in> <model-out>
gmm-sum-accs [options]<stats-out> <stats-in1> <stats-in2> ...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章