Skip to content

Commit 9a51fe3

Browse files
committed
Added scripts for prior computation and model conversion to Kaldi's nnet3 format with a limited functionality.
1 parent 45e498d commit 9a51fe3

10 files changed

+234
-15
lines changed

LICENSE

100644100755
File mode changed.

README.md

100644100755
+7-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Learning models in Keras.
3030

3131
1. Python 3.4+
3232

33-
2. Keras with Theano/Tensorflow backend
33+
2. Keras with Tensorflow/Theano backend
3434

3535
3. Kaldi
3636

@@ -63,6 +63,12 @@ run run_kt_LSTM.sh.
6363

6464
6. align.sh is the alignment script.
6565

66+
7. compute_priors.py computes priors.
67+
68+
8. saveModelNnet3.sh and saveModelNnet3Raw.py convert the trained
69+
feedforward DNNs into Kaldi's nnet3 format. They currently have
70+
limited functionality.
71+
6672
## Training Schedule
6773

6874
The script uses stochastic gradient descent with 0.5 momentum. It

run_kt.sh

-7
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,10 @@ done
4444
## Uncomment to train a Maxout network
4545
#[ -f $exp/dnn.nnet.h5 ] || python3 steps_kt/train_maxout.py ${train}_cv05 ${gmm}_ali_cv05 ${train}_tr95 ${gmm}_ali_tr95 $gmm $exp
4646

47-
## Get priors: Make a Python script to do this.
48-
ali-to-pdf $gmm/final.mdl ark:"gunzip -c ${gmm}_ali_???5/ali.*.gz |" ark,t:- | \
49-
cut -d" " -f2- | tr ' ' '\n' | sed -r '/^\s*$/d' | sort | uniq -c | sort -n -k2 | \
50-
awk '{a[$2]=$1; c+=$1; LI=$2} END{for(i=0;i<LI;i++) printf "%e,",a[i]/c; printf "%e",a[LI]/c}' \
51-
> $exp/dnn.priors.csv
52-
5347
## Make graph
5448
[ -f $gmm/graph/HCLG.fst ] || utils/mkgraph.sh ${lang}_test_bg $gmm $gmm/graph
5549

5650
## Decode
57-
cp $gmm/final.mdl $gmm/tree $exp/
5851
[ -f $exp/decode/wer_11 ] || bash steps_kt/decode.sh --nj $nj \
5952
--add-deltas "true" --norm-vars "true" --splice-opts "--left-context=5 --right-context=5" \
6053
$test $gmm/graph $exp $exp/decode

run_kt_LSTM.sh

-7
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,10 @@ done
4242
## Train
4343
[ -f $exp/dnn.nnet.h5 ] || python3 steps_kt/train_LSTM.py ${train}_cv05 ${gmm}_ali_cv05 ${train}_tr95 ${gmm}_ali_tr95 $gmm $exp
4444

45-
## Get priors: Make a Python script to do this.
46-
ali-to-pdf $gmm/final.mdl ark:"gunzip -c ${gmm}_ali_???5/ali.*.gz |" ark,t:- | \
47-
cut -d" " -f2- | tr ' ' '\n' | sed -r '/^\s*$/d' | sort | uniq -c | sort -n -k2 | \
48-
awk '{a[$2]=$1; c+=$1; LI=$2} END{for(i=0;i<LI;i++) printf "%e,",a[i]/c; printf "%e",a[LI]/c}' \
49-
> $exp/dnn.priors.csv
50-
5145
## Make graph
5246
[ -f $gmm/graph/HCLG.fst ] || utils/mkgraph.sh ${lang}_test_bg $gmm $gmm/graph
5347

5448
## Decode
55-
cp $gmm/final.mdl $gmm/tree $exp/
5649
[ -f $exp/decode/wer_11 ] || bash steps_kt/decode_seq.sh --nj $nj \
5750
--add-deltas "true" --norm-vars "true" --splice-size "11" \
5851
$test $gmm/graph $exp $exp/decode

steps_kt/compute_priors.py

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/python3
2+
3+
## Copyright (C) 2016 D S Pavan Kumar
4+
## dspavankumar [at] gmail [dot] com
5+
##
6+
## This program is free software: you can redistribute it and/or modify
7+
## it under the terms of the GNU General Public License as published by
8+
## the Free Software Foundation, either version 3 of the License, or
9+
## (at your option) any later version.
10+
##
11+
## This program is distributed in the hope that it will be useful,
12+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
## GNU General Public License for more details.
15+
##
16+
## You should have received a copy of the GNU General Public License
17+
## along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+
20+
import sys
21+
import numpy
22+
from subprocess import Popen, PIPE
23+
24+
## Read output feature dimension
25+
def read_output_feat_dim (exp):
26+
p = Popen (['am-info', exp+'/final.mdl'], stdout=PIPE)
27+
for line in p.stdout:
28+
if b'number of pdfs' in line:
29+
return int(line.split()[-1])
30+
31+
## Compute priors
32+
def compute_priors (exp, ali_tr, ali_cv=None):
33+
dim = read_output_feat_dim (exp)
34+
counts = numpy.zeros(dim)
35+
36+
## Prepare string
37+
ali_str = 'ark:gunzip -c ' + ali_tr+'/ali.*.gz '
38+
if ali_cv:
39+
ali_str += ali_cv+'/ali.*.gz '
40+
ali_str += '|'
41+
42+
p = Popen(['ali-to-pdf', exp+'/final.mdl', ali_str, 'ark,t:-'], stdout=PIPE)
43+
44+
## Compute counts
45+
for line in p.stdout:
46+
line = line.split()
47+
for index in line[1:]:
48+
counts[int(index)] += 1
49+
50+
## Compute priors
51+
priors = counts / numpy.sum(counts)
52+
53+
## Floor zero values
54+
priors[priors==0] = 1e-5
55+
56+
## Write to file
57+
priors.tofile (exp+'/dnn.priors.csv', sep=',', format='%e')
58+
59+
if __name__ == '__main__':
60+
exp = sys.argv[1]
61+
ali_tr = sys.argv[2]
62+
ali_cv = sys.argv[3] if len(sys.argv)==4 else None
63+
64+
compute_priors (exp, ali_tr, ali_cv)

steps_kt/saveModelNnet3.sh

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/bash
2+
3+
## Copyright (C) 2016 D S Pavan Kumar
4+
## dspavankumar [at] gmail [dot] com
5+
##
6+
## This program is free software: you can redistribute it and/or modify
7+
## it under the terms of the GNU General Public License as published by
8+
## the Free Software Foundation, either version 3 of the License, or
9+
## (at your option) any later version.
10+
##
11+
## This program is distributed in the hope that it will be useful,
12+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
## GNU General Public License for more details.
15+
##
16+
## You should have received a copy of the GNU General Public License
17+
## along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+
20+
## NOTE: This script converts feedforward DNNs in HDF5 format to the
21+
## standard Kaldi's nnet3 format. It has limited functionality.
22+
## It uses steps_kt/saveModelNnet3Raw.py, which is also limited
23+
## in functionality. The scripts do the job, but can be better.
24+
25+
exp=$1
26+
27+
. cmd.sh
28+
. path.sh
29+
30+
## Check if argument exists
31+
[ -z $exp ] && echo "Provide DNN directory as an argument" && exit 1
32+
33+
## Check if files required exist in the exp directory
34+
for f in $exp/final.mdl $exp/dnn.nnet.h5 $exp/dnn.priors.csv ; do
35+
[ ! -f $f ] && echo "Expected $f to exist" && exit 1
36+
done
37+
38+
## Copy the raw Nnet3
39+
python3 scripts_kt/saveModelNnet3Raw.py $exp/dnn.nnet.h5 $exp/dnn.nnet3.raw
40+
41+
## Append context and priors to the raw Nnet3
42+
printf "<LeftContext> 0 <RightContext> 0 <Priors> [ " >> $exp/dnn.nnet3.raw
43+
awk '{gsub(","," ",$0); print $0 " ]"}' $exp/dnn.priors.csv >> $exp/dnn.nnet3.raw
44+
45+
## Copy the transition matrix
46+
copy-transition-model --binary=false $exp/final.mdl $exp/dnn.nnet3.trans
47+
48+
mv $exp/final.mdl $exp/final.mdl.bak
49+
50+
## Prepare the final model
51+
cat $exp/dnn.nnet3.trans $exp/dnn.nnet3.raw > $exp/final.mdl.txt
52+
53+
## Convert to binary format
54+
nnet3-am-copy $exp/final.mdl.txt $exp/final.mdl
55+
56+
## Clean up
57+
rm -f $exp/dnn.nnet3.raw $exp/dnn.nnet3.trans $exp/final.mdl.txt
58+
59+
echo "Older final model backed up as: $exp/final.mdl.bak"
60+
echo "Nnet3 model successfully stored as: $exp/final.mdl"

steps_kt/saveModelNnet3Raw.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/python3
2+
3+
## Copyright (C) 2016 D S Pavan Kumar
4+
## dspavankumar [at] gmail [dot] com
5+
##
6+
## This program is free software: you can redistribute it and/or modify
7+
## it under the terms of the GNU General Public License as published by
8+
## the Free Software Foundation, either version 3 of the License, or
9+
## (at your option) any later version.
10+
##
11+
## This program is distributed in the hope that it will be useful,
12+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
## GNU General Public License for more details.
15+
##
16+
## You should have received a copy of the GNU General Public License
17+
## along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+
20+
## NOTE: This script has limited functionality. It currently converts
21+
## feedforward networks with relu and softmax layers in HDF5 format
22+
## to the standard Kaldi's nnet3 "raw" format. Call this script from
23+
## steps_kt/saveModelNnet3.sh to get a complete model.
24+
25+
import keras
26+
import numpy
27+
import sys
28+
29+
def saveModel (model, fileName):
30+
with open (fileName, 'w') as f:
31+
f.write ('<Nnet3> \n')
32+
33+
## Write the component descriptions
34+
f.write ('input-node name=input dim=%d\n' % m.input_shape[-1])
35+
prevLayerName = 'input'
36+
num_components = 0
37+
for layer in model.layers:
38+
if layer.name.startswith ('dense'):
39+
f.write ('component-node name=%s.affine component=%s.affine input=%s\n' % (layer.name, layer.name, prevLayerName))
40+
num_components += 1
41+
activation_text = layer.get_config()['activation']
42+
if activation_text != 'linear':
43+
f.write ('component-node name=%s.%s component=%s.%s input=%s.affine\n' % (layer.name, activation_text, layer.name, activation_text, layer.name))
44+
num_components += 1
45+
prevLayerName = layer.name + '.' + activation_text
46+
f.write('output-node name=output input=%s objective=linear\n' % prevLayerName)
47+
48+
f.write('\n<NumComponents> %d\n' % num_components)
49+
50+
## Write the layer values
51+
for layer in model.layers:
52+
if not layer.name.startswith ('dense'):
53+
raise TypeError ('Unknown layer type: ' + layer.name)
54+
55+
f.write ('<ComponentName> %s.affine <NaturalGradientAffineComponent> <MaxChange> 2.0 <LearningRate> 0.001 <LinearParams> [ \n ' % (layer.name))
56+
for row in layer.get_weights()[0].T:
57+
row.tofile (f, format="%e", sep=' ')
58+
f.write (' \n ')
59+
f.write ('] \n <BiasParams> [ ')
60+
layer.get_weights()[1].tofile (f, format="%e", sep=' ')
61+
f.write (' ] \n')
62+
f.write ('<RankIn> 20 <RankOut> 80 <UpdatePeriod> 4 <NumSamplesHistory> 2000 <Alpha> 4 <IsGradient> F </NaturalGradientAffineComponent>\n')
63+
64+
## Deal with the activation
65+
activation_text = layer.get_config()['activation']
66+
if activation_text == 'relu':
67+
f.write ('<ComponentName> %s.relu <RectifiedLinearComponent> <Dim> %d <ValueAvg> [ ] <DerivAvg> [ ] <Count> 0 <NumDimsSelfRepaired> 0 <NumDimsProcessed> 0 </RectifiedLinearComponent>\n' % (layer.name, layer.output_shape[-1]))
68+
elif activation_text == 'softmax':
69+
f.write ('<ComponentName> %s.softmax <LogSoftmaxComponent> <Dim> %d <ValueAvg> [ ] <DerivAvg> [ ] <Count> 0 <NumDimsSelfRepaired> 0 <NumDimsProcessed> 0 </LogSoftmaxComponent>\n' % (layer.name, layer.output_shape[-1]))
70+
else:
71+
raise TypeError ('Unknown/unhandled activation: ' + activation_text)
72+
f.write ('</Nnet3> \n')
73+
74+
## Save h5 model in nnet3 format
75+
if __name__ == '__main__':
76+
h5model = sys.argv[1]
77+
nnet3 = sys.argv[2]
78+
m = keras.models.load_model (h5model)
79+
saveModel(m, nnet3)

steps_kt/train.py

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import keras.backend as K
2222
from keras.optimizers import SGD
2323
from dataGenerator import dataGenerator
24+
from compute_priors import compute_priors
25+
from shutil import copy
2426
import numpy
2527
import sys
2628
import os
@@ -46,7 +48,13 @@
4648
'lrScaleCount' : 18,
4749
'minValError' : 0.002}
4850

51+
## Copy final model and tree from GMM directory
4952
os.makedirs (exp, exist_ok=True)
53+
copy (gmm + '/final.mdl', exp)
54+
copy (gmm + '/tree', exp)
55+
56+
## Compute priors
57+
compute_priors (exp, ali_tr, ali_cv)
5058

5159
trGen = dataGenerator (data_tr, ali_tr, gmm, learning['batchSize'])
5260
cvGen = dataGenerator (data_cv, ali_cv, gmm, learning['batchSize'])

steps_kt/train_LSTM.py

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import keras.backend as K
2222
from keras.optimizers import SGD
2323
from dataGenSequences import dataGenSequences
24+
from compute_priors import compute_priors
25+
from shutil import copy
2426
import numpy
2527
import sys
2628
import os
@@ -47,7 +49,13 @@
4749
'lrScaleCount' : 18,
4850
'minValError' : 0.002}
4951

52+
## Copy final model and tree from GMM directory
5053
os.makedirs (exp, exist_ok=True)
54+
copy (gmm + '/final.mdl', exp)
55+
copy (gmm + '/tree', exp)
56+
57+
## Compute priors
58+
compute_priors (exp, ali_tr, ali_cv)
5159

5260
trGen = dataGenSequences (data_tr, ali_tr, gmm, learning['batchSize'], learning['spliceSize'])
5361
cvGen = dataGenSequences (data_cv, ali_cv, gmm, learning['batchSize'], learning['spliceSize'])

steps_kt/train_maxout.py

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import keras.backend as K
2222
from keras.optimizers import SGD
2323
from dataGenerator import dataGenerator
24+
from compute_priors import compute_priors
25+
from shutil import copy
2426
import numpy
2527
import sys
2628
import os
@@ -46,7 +48,13 @@
4648
'lrScaleCount' : 18,
4749
'minValError' : 0.002}
4850

51+
## Copy final model and tree from GMM directory
4952
os.makedirs (exp, exist_ok=True)
53+
copy (gmm + '/final.mdl', exp)
54+
copy (gmm + '/tree', exp)
55+
56+
## Compute priors
57+
compute_priors (exp, ali_tr, ali_cv)
5058

5159
trGen = dataGenerator (data_tr, ali_tr, gmm, learning['batchSize'])
5260
cvGen = dataGenerator (data_cv, ali_cv, gmm, learning['batchSize'])

0 commit comments

Comments
 (0)