音声認識メモ(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に該当)が合成対象となる。
結果を先に書くと、各モデル、各コンポーネントに対して、以下のようなスケールが得られた。
各モデル、コンポーネントごとのスケール
スケールを求める過程を追ってみる。
更新における評価の尺度は、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, ®ularizer_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);