音声認識メモ(Kaldi)その23(トレーニング時のモデル更新 Dan's DNN(nnet2))

前回の続き。

mixup後に生成した12個のモデル(14.mdl〜25.mdl)から「final.mdl」を生成する過程を追ってみる。
nnet2では、結果が良かったモデルを1つだけ選ぶのではなく、スケールを掛けた上で足し合わせている。

生成は、「nnet2bin/nnet-combine-fast」コマンドを使ってモデルをcombine(合成)する。

nnet-combine-fast \
    --minibatch-size=129 \ 
    exp/nnet4c/14.mdl \
    exp/nnet4c/15.mdl \
    <snip> \
    exp/nnet4c/25.mdl \
    ark:exp/nnet4c/egs/combine.egs \    #  <valid-examples-in>
    exp/nnet4c/final.mdl

今回のケースでは、モデルのComponent数が「9」、Updatable-Component数が「3」

infoコマンド(nnet2bin/nnet-am-info exp/nnet4c/14.mdl )
num-components 9
num-updatable-components 3
input-dim 40
output-dim 192
component 0 : SpliceComponent, input-dim=40, output-dim=360, ... 
component 1 : FixedAffineComponent, input-dim=360, output-dim=360, ...
component 2 : AffineComponentPreconditionedOnline, input-dim=360, output-dim=375, ...
component 3 : TanhComponent, input-dim=375, output-dim=375
component 4 : AffineComponentPreconditionedOnline, input-dim=375, output-dim=375, ...
component 5 : TanhComponent, input-dim=375, output-dim=375
component 6 : AffineComponentPreconditionedOnline, input-dim=375, output-dim=453, ...
component 7 : SoftmaxComponent, input-dim=453, output-dim=453
component 8 : SumGroupComponent, input-dim=453, output-dim=192

indexが2、4、6の「AffineComponentPreconditionedOnline」コンポーネント(Updatable-Componentに該当)が合成対象となる。

結果を先に書くと、各モデル、各コンポーネントに対して、以下のようなスケールが得られた。

各モデル、コンポーネントごとのスケール

f:id:ichou1:20180812105103p:plain

スケールを求める過程を追ってみる。


更新における評価の尺度は、Propagateの値(Log値)となる(値が小さいものほど良)
同一のデータ(exp/nnet4c/egs/combine.egs)に対する各モデルの値は以下のとおりであった。

14.mdl : -0.0753596
15.mdl : -0.0679429
16.mdl : -0.0373454
17.mdl : -0.0325585
18.mdl : -0.0286209
19.mdl : -0.0237487
20.mdl : -0.0209156
21.mdl : -0.00755152
22.mdl : -0.0101945
23.mdl : -0.00779286
24.mdl : -0.00615664
25.mdl : -0.00456561

今回のケースでは、「25.mdl」の「-0.00456561」がもっとも良い値なので、この値を基準に、さらに良好な値を得られるようなスケールを求める。

スケールの最適解は準ニュートン法(L-BFGS)を使って求める。

ソースの抜粋(nnet2/combine-nnet-fast.cc)
OptimizeLbfgs<double> lbfgs(params_,
                            lbfgs_options);

// Loop 10 times
for (int32 i = 0; i < config_.num_lbfgs_iters; i++) {

    // スケールをセット
    params_.CopyFromVec(lbfgs.GetProposedValue());

    // Propagateの値とgradientを求める
    objf = ComputeObjfAndGradient(&gradient, &regularizer_objf);

    if (i == 0) {
        initial_objf = objf;
        initial_regularizer_objf = regularizer_objf;
    }
    // 判定とスケールの更新
    lbfgs.DoStep(objf, gradient);

} // end of for(i)

各ループ段階におけるPropagateの値と判定は以下のとおりであった。

(i=0) -0.00456561                  
(i=1) -0.310829      action = decrease 
(i=2) -0.0201834     action = decrease 
(i=3) -0.00512826    action = decrease 
(i=4) -0.00410415    action = accept   
(i=5) -0.00352887    action = accept
(i=6) -0.00315388    action = accept
(i=7) -0.00228854    action = accept
(i=8) -0.000916785   action = accept
(i=9) -0.000539656   action = accept

ループカウンタが「9」の時の値「-0.000539656」がもっとも良いので、この時のスケールが最適解となる。

params_ = lbfgs.GetValue(&objf);