首页 » 技术分享 » Kaldi 入门详解

Kaldi 入门详解

 

train_mono.sh 是音素训练脚本,下面详细介绍各个功能:

这部分是训练用参数,调用mono.sh时可以通过 –name value的方式改变这些参数.

nj=4   并行个数
cmd=run.pl   处理程序
scale_opts="--transition-scale=1.0 --acoustic-scale=0.1 --self-loop-scale=0.1"
num_iters=40     **  Number of iterations of training  训练循环次数
max_iter_inc=30  **  Last iter to increase  ** Gauss on.  每个循环里增加的高斯数
totgauss=1000  **  Target  ** Gaussians.   总高斯数(可能难理解,但可以这么解释:比如有3状态HMM,每个状态用4组均值方差描述(GMM混合数是4个意思),那总共用了12个高斯,kaldi里不用具体指定GMM个数,而指定整个系统中总共需要多少个单高斯)
careful=false
boost_silence=1.0  **  Factor by which to boost silence likelihoods in alignment
realign_iters="1 2 3 4 5 6 7 8 9 10 12 14 16 18 20 23 26 29 32 35 38";  循环到这个数时做对其
config=  **  name of config file. 
stage=-4  
power=0.25  **  exponent to determine number of gaussians from occurrence counts
norm_vars=false  **  deprecated, prefer --cmvn-opts "--norm-vars=false"
cmvn_opts=   **  can be used to add extra options to cmvn.
 **  End configuration section.

echo "$0 $@"   **  Print the command line for logging

- - name value 参数处理

if [ -f path.sh ]; then . ./path.sh; fi
. parse_options.sh || exit 1;

* - -name 以外的参数必须有3个,否则报错*

if [ $ **  != 3 ]; then
  echo "Usage: steps/train_mono.sh [options] <data-dir> <lang-dir> <exp-dir>"
  echo " e.g.: steps/train_mono.sh data/train.1k data/lang exp/mono"
  echo "main options (for others, see top of script file)"
  echo "  --config <config-file>                            **  config containing options"
  echo "  --nj <nj>                                         **  number of parallel jobs"
  echo "  --cmd (utils/run.pl|utils/queue.pl <queue opts>)  **  how to run jobs."
  exit 1;
fi

data=$1  ** 第一个参数是训练数据所在的目录 ** 
lang=$2 ** 第二个参数是语言模型所在的目录 ** 
dir=$3   ** 第一个参数是日志和最终目标文件输出目录 ** 

oov_sym=`cat $lang/oov.int` || exit 1;

* 训练数据根据–nj 参数分割,用于并行处理 *

mkdir -p $dir/log
echo $nj > $dir/num_jobs
sdata=$data/split$nj;
[[ -d $sdata && $data/feats.scp -ot $sdata ]] || split_data.sh $data $nj || exit 1;


$norm_vars && cmvn_opts="--norm-vars=true $cmvn_opts"
echo $cmvn_opts  > $dir/cmvn_opts  **  keep track of options to CMVN.

**这里可能第一个难理解的地方,但仔细分析也不难
首先这个是一个feats变量定义,这个变量作为后续其他命令的参数,这个主要处理特征数据的。
这里用到了两个kaldi函数,apply-cmvn 和 add-deltas
apply-cmvn 的输入3个文件:
–utt2spk=ark:

sdata/JOB/utt2spkscp:
sdata/JOB/cmvn.scp 说话人相关的均值和方差
scp:$sdata/JOB/feats.scp 训练用特征文件
对feats.scp做归一化处理
输出是 ark:-|,利用管道技术把输出传递给下一个函数作为输入
add-deltas 输入是ark:-,表示把上一个函数的输出作为输入(实际上就是整个训练特征数据),

输出  ark:- | 管道技术同上。训练数据增加差分量,比如13维度mfcc处理后变成39维度
这样的输入输出重定向和管道技术后续使用非常多,所先熟练掌握linux shell的语法。

$feats就变成了 ark,s,cs:特征数据 这个形式**

feats="ark,s,cs:apply-cmvn $cmvn_opts --utt2spk=ark:$sdata/JOB/utt2spk scp:$sdata/JOB/cmvn.scp scp:$sdata/JOB/feats.scp ark:- | add-deltas ark:- ark:- |"
example_feats="`echo $feats | sed s/JOB/1/g`";
echo "$0: Initializing monophone system."

[ ! -f $lang/phones/sets.int ] && exit 1;
shared_phones_opt="--shared-phones=$lang/phones/sets.int"

if [ $stage -le -3 ]; then
   **  Note: JOB=1 just uses the 1st part of the features-- we only need a subset anyway.
  //特征维度获取
  if ! feat_dim=`feat-to-dim "$example_feats" - 2>/dev/null` || [ -z $feat_dim ]; then
    feat-to-dim "$example_feats"  
    echo "error getting feature dimension"
    exit 1;
  fi
//gmm-init-mono 构造第一个模型文件和决策树
//"--train-feats=$feats subset-feats --n=10 ark:- ark:-|"  这个的意思是特征数据中取10个特征用于构造原始模型 
//输出是    $dir/0.mdl $dir/tree   这两个文件 
//可以利用 gmm-info $dir/0.mdl 查看这个模型概要信息 
//$cmd 后面的具体命令 gmm-init-mono通过$cmd环境运行的意思,log输出在$dir/log/init.log这里
  $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;
fi

//默认高斯数获取,从模型0.mdl里读取的
numgauss=`gmm-info --print-args=false $dir/0.mdl | grep gaussians | awk '{print $NF}'`
//根据参数总高斯计算每个循环增减的具体高斯数
incgauss=$[($totgauss-$numgauss)/$max_iter_inc]  **  per-iter increment for  ** Gauss

**这部分主要调用compile-train-graphs,根据决策树,模型,lexsionFST,text转ID这些文件,构造训练用FST,输出文本形式后:
S00001
0 25 2 0 0.693359
0 26 3 0 0.693359
0 3 245 419 0.693359
1 27 5 0
1 28 6 0
2 29 7 0
2 30 9 0
3 4 247 0
3 3 244 0
4 5 249 0
4 4 246 0
5 6 251 0
训练用每句话转换成一个FST结构,输入是音素,输出是整个句子**

if [ $stage -le -2 ]; then
  echo "$0: Compiling training graphs"
  //主要调用compile-train-graphs,输出 fsts.JOB.gz,JOB是具体分割是数据,比如分割16分(16个并行)
  //fsts.1.gz  fsts.2.gz  ... fsts.16.gz 这些文件
    $cmd JOB=1:$nj $dir/log/compile_graphs.JOB.log \
      compile-train-graphs $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

align-equal-compiled:对每一个训练特征做解码对其,输出文本形式为:
S00001 3 1 9 8 11 10 245 244 247 246 249 248 251 250 45 44 47 46 49 48 51 50 157 156 159 158 161 160 163 162 149 148 151 150 153 152 155 154 2 1 5 4 9 8 11 10

S00002 3 1 7 8 5 4 9 8 11 10 29 28 31 30 33 32 35 34 117 116 119 118 121 120 123 122 69 68 71 70 73 72 75 74 77 76 79 78 81 80 83 82 21 20 23 22 25 24 27 26 37 39 41 43 2 6 11
编号是对应: transition-ids

gmm-acc-stats-ali:根据对其信息,计算每个高斯分布的均值和方差,文本输出形式是:
[ 0 1251 780 747 1127 763 789 772 1097 738 1100 1527 455 344 441 344 434 344 417 344 861 900 834 900 798 900 767 900 367 336 350 3 36 337 336 332 336 238 322 225 322 213 322 201 322 310 323 301 323 290 323 277 323 46 39 44 39 42 39 41 39 160 190 156 190 152 190 144 190 133 134 128 134 123 134 118 134 389 387 380 387 370 387 358 387 232 225 230 225 222 225 218 225 115 173 100 173 92 173 83 1 73 162 160 154 160 149 160 146 160 30 21 26 21 26 21 25 21 57 58 56 58 55 58 53 58 141 159 134 159 131 159 127 159 155 154 151 154 149 154 142 154 718 658 698 658 682 658 657 658 154 198 139 198 131 198 120 198 173 181 167 181 159 181 155 181 43 116 40 116 36 11 6 36 116 14 16 14 16 12 16 12 16 50 94 46 94 39 94 34 94 95 146 87 146 82 146 78 146 68 93 65 93 59 93 52 93 197 173 191 173 185 17 3 175 173 18 20 17 20 17 20 13 20 104 89 100 89 97 89 95 89 22 27 20 27 19 27 19 27 71 79 68 79 65 79 62 79 180 184 175 184 168 184 166 184 50 55 46 55 40 55 39 55 81 89 78 89 74 89 72 89 32 27 31 27 29 27 29 27 6 9 6 9 6 9 5 9 130 113 126 113 123 113 116 113 52 56 51 56 51 56 50 56 152 120 148 120 147 120 145 120 45 28 45 28 44 28 44 28 27 22 26 22 26 22 26 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 31 26 29 26 28 26 28 26 160 119 158 119 153 119 152 119 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
96 1 15 [ 2778 ]
[
131.1602 -122.5539 34.99217 -58.83603 -41.43925 13.44948 463.3047 -57.9593 -18.75538 75.6356 -19.41969 104.4369 -124.7882 10.0328 6 192.8238 17.50561 30.52075 -186.307 -103.0168 -182.8055 -93.31313 274.8601 -134.9286 -20.74373 292.326 -139.4371 -76.76163 125.10 22 176.4698 -73.94184 671.5715 320.7724 187.5074 20.53596 -39.48004 0.9577144 -17.36876 98.11237 -74.94476 -6.995347 -24.19745 12.9 314 -11.86463 24.64146 -4.998218 16.60446 54.21263 7.680242 80.09453 -1.158172 12.43884 -21.7555 8.72164 -82.95818 30.4046 77.66879 -147.196 37.55052 -9.052818 169.3757 25.53467 -29.35383 -9.141276 -163.5246 -0.5642087 -4.418148 7.150371 1.227273 1.874714 -5.212 431 -35.17178 3.145967 -9.014412 5.978422 4.867862 24.94123 4.603298 9.424832 4.24349 11.20849 -4.842735 4.014105 -4.744929 5.26339 6 2.670479 2.020237 2.857623 -11.47297 6.608553 3.21471 -13.41074 -4.644363 0.2190919 -2.052493 -27.65508 8.728714 ]
[
302.8793 96.91061 50.14678 32.40083 62.61554 127.9802 427.8968 59.14513 97.06206 45.82985 51.53176 124.2855 36.80134 57.89008 169 .3976 79.1737 140.4388 50.01487 68.45477 115.8913 41.76318 151.5989 36.88505 87.20601 204.4366 56.90768 203.9601 228.5459 130.8333 52.88366 559.3757 200.2088 59.71447 11.39293 9.831112 5.036578 8.143344 25.86716 35.48496 8.004593 9.955203 7.049322 7.714571 22.01 161 4.823307 8.744013 22.8913 12.14625 22.91733 6.248809 7.172078 16.7681 4.72665 21.95807 6.282173 16.24944 35.32213 9.839767 18.3 4587 44.31969 12.50674 5.586533 45.20058 31.49718 7.654399 2.389589 1.18734 1.046883 1.593502 3.394817 5.20261 1.703343 1.964516 1. 464402 1.537351 4.471569 0.8961765 1.717167 3.762137 2.311954 4.111099 1.202704 1.794535 3.404304 1.064913 2.071092 1.182136 2.2995 83 2.488608 1.465976 4.587708 5.893738 2.384184 0.8646237 6.272319 1.73957 ]

这个信息最后保存在 0.JOB.acc 里。JOB同上。

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

 **  In the following steps, the --min-gaussian-occupancy=3 option is important, otherwise
 **  we fail to est "rare" phones and later on, they never align properly.

gmm-est:模型重建,根据原始模型0.mdl 所有0.*.acc文件合并输出 1.mdl
gmm-sum-accs:完成多个文件的统计合并

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

下面代码根据循环次数做模型训练
主要用下面三个函数:
$x变量就是目前的循环次数

gmm-align-compiled:和上面讲的“align-equal-compiled”类似,做特征数据的对其,对其信息输出到ali.JOB.gz里。对其不是每次都做,realign_iters=”1 2 3 4 5 6 7 8 9 10 12 14 16 18 20 23 26 29 32 35 38”;

x
realign_iters 是才做对其。

gmm-acc-stats-ali:根据对其信息,计算每个高斯分布的均值和方差(看有介绍),输出$x.JOB.acc文件

gmm-est:根据上一次的模型,构建新模型

[
<script id="MathJax-Element-145" type="math/tex">[</script>x+1].mdl

beam=6  **  will change to 10 below after 1st pass
 **  note: using slightly wider beams for WSJ vs. RM.
x=1
while [ $x -lt $num_iters ]; do
  echo "$0: Pass $x"
  if [ $stage -le $x ]; then
    if echo $realign_iters | grep -w $x >/dev/null; then
      echo "$0: Aligning data"
      //sil模型调整
      mdl="gmm-boost-silence --boost=$boost_silence `cat $lang/phones/optional_silence.csl` $dir/$x.mdl - |"
      $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;

    $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] 循环变量+1
done

最后模型就是final.mdl文件,他是对最后一个训练模型的link

( cd $dir; rm final.{mdl,occs} 2>/dev/null; ln -s $x.mdl final.mdl; ln -s $x.occs final.occs )

utils/summarize_warnings.pl $dir/log

echo Done

 **  example of showing the alignments:
 **  show-alignments data/lang/phones.txt $dir/30.mdl "ark:gunzip -c $dir/ali.0.gz|" | head -4

以上就是对train_mono.sh的详细解释。

理解train_mono.sh对后续三音素, LDM MLLT, MMI ,MPE,DNN等都有很大帮助。

跟多细节自行阅读http://kaldi.sourceforge.net/

**kaldi主要运行思路是:
1.准备数据
2.训练单因素模型
3.训练三音素模型 train_deltas.sh
3. 调整三音素模型(LDM MLLT, MMI ,MPE)等
4. 训练DNN
5. GMM模型和DNN模型绑定**

转载自原文链接, 如需删除请联系管理员。

原文链接:Kaldi 入门详解,转载请注明来源!

0