[scripts,egs] fixed issues in multilingual training with --use-ivector option (#1828)
[processor-sdk/kaldi.git] / egs / babel_multilang / s5 / local / nnet3 / run_tdnn_multilingual.sh
1 #!/bin/bash
3 # Copyright 2016 Pegah Ghahremani
5 # This script can be used for training multilingual setup using different
6 # languages (specifically babel languages) with no shared phones.
7 # It will generates separate egs directory for each dataset and combine them
8 # during training.
9 # In the new multilingual training setup, mini-batches of data corresponding to
10 # different languages are randomly combined to generate egs.*.scp files
11 # using steps/nnet3/multilingual/combine_egs.sh and generated egs.*.scp files used
12 # for multilingual training.
13 #
14 # For all languages, we share all except last hidden layer and there is separate final
15 # layer per language.
16 # The bottleneck layer can be added to the network structure using --bnf-dim option
17 #
18 # The script requires baseline PLP features and alignment (e.g. tri5_ali) for all languages.
19 # and it will generate 40dim MFCC + pitch features for all languages.
20 #
21 # The global iVector extractor trained using all languages by specifying
22 # --use-global-ivector-extractor and the iVectors are extracts for all languages.
23 #
24 # local.conf should exists (check README.txt), which contains configs for
25 # multilingual training such as lang_list as array of space-separated languages used
26 # for multilingual training.
27 #
28 echo "$0 $@"  # Print the command line for logging
29 . ./cmd.sh
30 set -e
32 remove_egs=false
33 cmd=queue.pl
34 srand=0
35 stage=0
36 train_stage=-10
37 get_egs_stage=-10
38 decode_stage=-10
39 num_jobs_initial=2
40 num_jobs_final=8
41 speed_perturb=true
42 use_pitch=true  # if true, pitch feature used to train multilingual setup
43 use_pitch_ivector=false # if true, pitch feature used in ivector extraction.
44 use_ivector=true
45 megs_dir=
46 alidir=tri5_ali
47 ivector_extractor=  # If empty, the global iVector extractor trained on pooled data
48                     # from all languages and iVectors are
49                     # extracted using global ivector extractor and ivector_suffix ='_gb'.
50                     # Otherwise this extractor is used to extract iVector and
51                     # ivector_suffix = ''.
53 bnf_dim=           # If non-empty, the bottleneck layer with this dimension is added at two layers before softmax.
54 dir=exp/nnet3/multi_bnf
56 . ./path.sh
57 . ./cmd.sh
58 . ./utils/parse_options.sh
60 [ ! -f local.conf ] && echo 'the file local.conf does not exist! Read README.txt for more details.' && exit 1;
61 . local.conf || exit 1;
63 num_langs=${#lang_list[@]}
64 feat_suffix=_hires      # The feature suffix describing features used in
65                         # multilingual training
66                         # _hires -> 40dim MFCC
67                         # _hires_pitch -> 40dim MFCC + pitch
68                         # _hires_pitch_bnf -> 40dim MFCC +pitch + BNF
70 echo "$0 $@"  # Print the command line for logging
71 if ! cuda-compiled; then
72   cat <<EOF && exit 1
73 This script is intended to be used with GPUs but you have not compiled Kaldi with CUDA
74 If you want to use GPUs (and have them), go to src/, and configure and make on a machine
75 where "nvcc" is installed.
76 EOF
77 fi
79 for lang_index in `seq 0 $[$num_langs-1]`; do
80   for f in data/${lang_list[$lang_index]}/train/{feats.scp,text} exp/${lang_list[$lang_index]}/$alidir/ali.1.gz exp/${lang_list[$lang_index]}/$alidir/tree; do
81     [ ! -f $f ] && echo "$0: no such file $f" && exit 1;
82   done
83 done
85 if [ "$speed_perturb" == "true" ]; then suffix=_sp; fi
86 dir=${dir}${suffix}
88 ivec_feat_suffix=${feat_suffix}
89 if $use_pitch; then feat_suffix=${feat_suffix}_pitch ; fi
90 if $use_pitch_ivector; then nnet3_affix=_pitch; ivec_feat_suffix=${feat_suffix}_pitch ; fi
92 for lang_index in `seq 0 $[$num_langs-1]`; do
93   echo "$0: extract high resolution 40dim MFCC + pitch for speed-perturbed data "
94   echo "and extract alignment."
95   local/nnet3/run_common_langs.sh --stage $stage \
96     --feat-suffix $feat_suffix \
97     --use-pitch $use_pitch \
98     --speed-perturb $speed_perturb ${lang_list[$lang_index]} || exit 1;
99   if $use_pitch && ! $use_pitch_ivector; then
100     echo "$0: select MFCC features for ivector extraction."
101     featdir=data/${lang_list[$lang_index]}/train${suffix}${feat_suffix}
102     ivec_featdir=data/${lang_list[$lang_index]}/train${suffix}${ivec_feat_suffix}
103     mfcc_only_dim=`feat-to-dim scp:$featdir/feats.scp - | awk '{print $1-3}'`
104     if [ ! -f $ivec_featdir/.done ]; then
105       steps/select_feats.sh --cmd "$train_cmd" --nj 70 0-$[$mfcc_only_dim-1] \
106         $featdir ${ivec_featdir} || exit 1;
107       steps/compute_cmvn_stats.sh ${ivec_featdir} || exit 1;
108       touch ${ivec_featdir}/.done || exit 1;
109     fi
110   fi
111 done
113 if $use_ivector; then
114   ivector_suffix=""
115   if [ -z "$ivector_extractor" ]; then
116     mkdir -p data/multi
117     global_extractor=exp/multi/nnet3${nnet3_affix}
118     mkdir -p $global_extractor
119     ivector_extractor=$global_extractor/extractor
120     multi_data_dir_for_ivec=data/multi/train${suffix}${ivec_feat_suffix}
121     ivector_suffix=_gb
122     echo "$0: combine training data using all langs for training global i-vector extractor."
123     if [ ! -f $multi_data_dir_for_ivec/.done ]; then
124       echo ---------------------------------------------------------------------
125       echo "Pooling training data in $multi_data_dir_for_ivec on" `date`
126       echo ---------------------------------------------------------------------
127       mkdir -p $multi_data_dir_for_ivec
128       combine_lang_list=""
129       for lang_index in `seq 0 $[$num_langs-1]`;do
130         combine_lang_list="$combine_lang_list data/${lang_list[$lang_index]}/train${suffix}${ivec_feat_suffix}"
131       done
132       utils/combine_data.sh $multi_data_dir_for_ivec $combine_lang_list
133       utils/validate_data_dir.sh --no-feats $multi_data_dir_for_ivec
134       touch $multi_data_dir_for_ivec/.done
135     fi
136   fi
137   if [ ! -f $global_extractor/extractor/.done ]; then
138     if [ -z $lda_mllt_lang ]; then lda_mllt_lang=${lang_list[0]}; fi
139     echo "$0: Generate global i-vector extractor on pooled data from all "
140     echo "languages in $multi_data_dir_for_ivec, using an LDA+MLLT transform trained "
141     echo "on ${lda_mllt_lang}."
142     local/nnet3/run_shared_ivector_extractor.sh  \
143       --suffix "$suffix" --nnet3-affix "$nnet3_affix" \
144       --feat-suffix "$ivec_feat_suffix" \
145       --stage $stage $lda_mllt_lang \
146       $multi_data_dir_for_ivec $global_extractor || exit 1;
147     touch $global_extractor/extractor/.done
148   fi
149   echo "$0: Extracts ivector for all languages using $global_extractor/extractor."
150   for lang_index in `seq 0 $[$num_langs-1]`; do
151     local/nnet3/extract_ivector_lang.sh --stage $stage \
152       --train-set train${suffix}${ivec_feat_suffix} \
153       --ivector-suffix "$ivector_suffix" \
154       --nnet3-affix "$nnet3_affix" \
155       ${lang_list[$lang_index]} \
156       $ivector_extractor || exit;
157   done
158 fi
161 for lang_index in `seq 0 $[$num_langs-1]`; do
162   multi_data_dirs[$lang_index]=data/${lang_list[$lang_index]}/train${suffix}${feat_suffix}
163   multi_egs_dirs[$lang_index]=exp/${lang_list[$lang_index]}/nnet3${nnet3_affix}/egs${feat_suffix}${ivector_suffix}
164   multi_ali_dirs[$lang_index]=exp/${lang_list[$lang_index]}/${alidir}${suffix}
165   multi_ivector_dirs[$lang_index]=exp/${lang_list[$lang_index]}/nnet3${nnet3_affix}/ivectors_train${suffix}${ivec_feat_suffix}${ivector_suffix}
166 done
169 if $use_ivector; then
170   ivector_dim=$(feat-to-dim scp:${multi_ivector_dirs[0]}/ivector_online.scp -) || exit 1;
171 else
172   echo "$0: Not using iVectors in multilingual training."
173   ivector_dim=0
174 fi
175 feat_dim=`feat-to-dim scp:${multi_data_dirs[0]}/feats.scp -`
177 if [ $stage -le 8 ]; then
178   echo "$0: creating multilingual neural net configs using the xconfig parser";
179   if [ -z $bnf_dim ]; then
180     bnf_dim=1024
181   fi
182   mkdir -p $dir/configs
183   ivector_node_xconfig=""
184   ivector_to_append=""
185   if $use_ivector; then
186     ivector_node_xconfig="input dim=$ivector_dim name=ivector"
187     ivector_to_append=", ReplaceIndex(ivector, t, 0)"
188   fi
189   cat <<EOF > $dir/configs/network.xconfig
190   $ivector_node_xconfig
191   input dim=$feat_dim name=input
193   # please note that it is important to have input layer with the name=input
194   # as the layer immediately preceding the fixed-affine-layer to enable
195   # the use of short notation for the descriptor
196   # the first splicing is moved before the lda layer, so no splicing here
197   relu-renorm-layer name=tdnn1 input=Append(input@-2,input@-1,input,input@1,input@2$ivector_to_append) dim=1024
198   relu-renorm-layer name=tdnn2 dim=1024
199   relu-renorm-layer name=tdnn3 input=Append(-1,2) dim=1024
200   relu-renorm-layer name=tdnn4 input=Append(-3,3) dim=1024
201   relu-renorm-layer name=tdnn5 input=Append(-3,3) dim=1024
202   relu-renorm-layer name=tdnn6 input=Append(-7,2) dim=1024
203   relu-renorm-layer name=tdnn_bn dim=$bnf_dim
204   # adding the layers for diffrent language's output
205 EOF
206   # added separate outptut layer and softmax for all languages.
207   for lang_index in `seq 0 $[$num_langs-1]`;do
208     num_targets=`tree-info ${multi_ali_dirs[$lang_index]}/tree 2>/dev/null | grep num-pdfs | awk '{print $2}'` || exit 1;
210     echo " relu-renorm-layer name=prefinal-affine-lang-${lang_index} input=tdnn_bn dim=1024"
211     echo " output-layer name=output-${lang_index} dim=$num_targets max-change=1.5"
212   done >> $dir/configs/network.xconfig
214   steps/nnet3/xconfig_to_configs.py --xconfig-file $dir/configs/network.xconfig \
215     --config-dir $dir/configs/ \
216     --nnet-edits="rename-node old-name=output-0 new-name=output"
217 fi
219 if [ $stage -le 9 ]; then
220   echo "$0: Generates separate egs dir per language for multilingual training."
221   # sourcing the "vars" below sets
222   #model_left_context=(something)
223   #model_right_context=(something)
224   #num_hidden_layers=(something)
225   . $dir/configs/vars || exit 1;
226   ivec="${multi_ivector_dirs[@]}"
227   if $use_ivector; then
228     ivector_opts=(--online-multi-ivector-dirs "$ivec")
229   fi
230   local/nnet3/prepare_multilingual_egs.sh --cmd "$decode_cmd" \
231     "${ivector_opts[@]}" \
232     --cmvn-opts "--norm-means=false --norm-vars=false" \
233     --left-context $model_left_context --right-context $model_right_context \
234     $num_langs ${multi_data_dirs[@]} ${multi_ali_dirs[@]} ${multi_egs_dirs[@]} || exit 1;
235 fi
237 if [ -z $megs_dir ];then
238   megs_dir=$dir/egs
239 fi
241 if [ $stage -le 10 ] && [ ! -z $megs_dir ]; then
242   echo "$0: Generate multilingual egs dir using "
243   echo "separate egs dirs for multilingual training."
244   if [ ! -z "$lang2weight" ]; then
245       egs_opts="--lang2weight '$lang2weight'"
246   fi
247   common_egs_dir="${multi_egs_dirs[@]} $megs_dir"
248   steps/nnet3/multilingual/combine_egs.sh $egs_opts \
249     --cmd "$decode_cmd" \
250     --samples-per-iter 400000 \
251     $num_langs ${common_egs_dir[@]} || exit 1;
252 fi
254 if [ $stage -le 11 ]; then
255   common_ivec_dir=
256   if $use_ivector;then
257     common_ivec_dir=${multi_ivector_dirs[0]}
258   fi
259   steps/nnet3/train_raw_dnn.py --stage=$train_stage \
260     --cmd="$decode_cmd" \
261     --feat.cmvn-opts="--norm-means=false --norm-vars=false" \
262     --trainer.num-epochs 2 \
263     --trainer.optimization.num-jobs-initial=2 \
264     --trainer.optimization.num-jobs-final=12 \
265     --trainer.optimization.initial-effective-lrate=0.0015 \
266     --trainer.optimization.final-effective-lrate=0.00015 \
267     --trainer.optimization.minibatch-size=256,128 \
268     --trainer.samples-per-iter=400000 \
269     --trainer.max-param-change=2.0 \
270     --trainer.srand=$srand \
271     --feat-dir ${multi_data_dirs[0]} \
272     --feat.online-ivector-dir "$common_ivec_dir" \
273     --egs.dir $megs_dir \
274     --use-dense-targets false \
275     --targets-scp ${multi_ali_dirs[0]} \
276     --cleanup.remove-egs $remove_egs \
277     --cleanup.preserve-model-interval 50 \
278     --use-gpu true \
279     --dir=$dir  || exit 1;
280 fi
282 if [ $stage -le 12 ]; then
283   for lang_index in `seq 0 $[$num_langs-1]`;do
284     lang_dir=$dir/${lang_list[$lang_index]}
285     mkdir -p  $lang_dir
286     echo "$0: rename output name for each lang to 'output' and "
287     echo "add transition model."
288     nnet3-copy --edits="rename-node old-name=output-$lang_index new-name=output" \
289       $dir/final.raw - | \
290       nnet3-am-init ${multi_ali_dirs[$lang_index]}/final.mdl - \
291       $lang_dir/final.mdl || exit 1;
292     cp $dir/cmvn_opts $lang_dir/cmvn_opts || exit 1;
293     echo "$0: compute average posterior and readjust priors for language ${lang_list[$lang_index]}."
294     steps/nnet3/adjust_priors.sh --cmd "$decode_cmd" \
295       --use-gpu true \
296       --iter final --use-raw-nnet false --use-gpu true \
297       $lang_dir ${multi_egs_dirs[$lang_index]} || exit 1;
298   done
299 fi
301 # decoding different languages
302 if [ $stage -le 13 ]; then
303   num_decode_lang=${#decode_lang_list[@]}
304   for lang_index in `seq 0 $[$num_decode_lang-1]`; do
305     if [ ! -f $dir/${decode_lang_list[$lang_index]}/decode_dev10h.pem/.done ]; then
306       echo "Decoding lang ${decode_lang_list[$lang_index]} using multilingual hybrid model $dir"
307       run-4-anydecode-langs.sh --use-ivector $use_ivector \
308         --use-pitch-ivector $use_pitch_ivector \
309         --nnet3-dir $dir --iter final_adj \
310         --nnet3-affix "$nnet3_affix" \
311         ${decode_lang_list[$lang_index]} || exit 1;
312       touch $dir/${decode_lang_list[$lang_index]}/decode_dev10h.pem/.done
313     fi
314   done
315 fi