Blame view

egs/csj/s5/local/csj_train_rnnlms.sh 6.42 KB
8dcb6dfcb   Yannick Estève   first commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  #!/bin/bash 
  
  # 2016 Modified by Tomohiro Tanaka at Tokyo Institute of Technology
  # for Japanese speech recognition using CSJ.
  
  # Copyright 2012  Johns Hopkins University (author: Daniel Povey)  Tony Robinson
  #           2015  Guoguo Chen
  
  # This script trains LMs on the WSJ LM-training data.
  # It requires that you have already run wsj_extend_dict.sh,
  # to get the larger-size dictionary including all of CMUdict
  # plus any OOVs and possible acronyms that we could easily 
  # derive pronunciations for.
  
  # This script takes no command-line arguments but takes the --cmd option.
  
  # Begin configuration section.
  rand_seed=0
  cmd=run.pl
  nwords=10000 # This is how many words we're putting in the vocab of the RNNLM. 
  hidden=30
  class=200 # Num-classes... should be somewhat larger than sqrt of nwords.
  direct=1000 # Number of weights that are used for "direct" connections, in millions.
  rnnlm_ver=rnnlm-0.3e # version of RNNLM to use
  threads=1 # for RNNLM-HS
  bptt=6 # length of BPTT unfolding in RNNLM
  bptt_block=10 # length of BPTT unfolding in RNNLM
  dict_suffix=
  # End configuration section.
  
  [ -f ./path.sh ] && . ./path.sh
  . utils/parse_options.sh
  
  if [ $# != 1 ]; then
     echo "Usage: local/wsj_train_rnnlms.sh [options] <dest-dir>"
     echo "For options, see top of script file"
     exit 1;
  fi
  
  dir=$1
  text=data/local/train/text
  lexicon=data/local/dict${dict_suffix}/lexicon.txt
  mkdir -p $dir
  
  rnnlm=$KALDI_ROOT/tools/$rnnlm_ver/rnnlm
  
  
  export PATH=$KALDI_ROOT/tools/$rnnlm_ver:$PATH
  
  ( # First make sure the kaldi_lm toolkit is installed.
   # Note: this didn't work out of the box for me, I had to
   # change the g++ version to just "g++" (no cross-compilation
   # needed for me as I ran on a machine that had been setup
   # as 64 bit by default.
   cd $KALDI_ROOT/tools || exit 1;
   if [ -f $rnnlm_ver/rnnlm ]; then
     echo Not installing the rnnlm toolkit since it is already there.
   else
     if [ $rnnlm_ver == "rnnlm-hs-0.1b" ]; then
         extras/install_rnnlm_hs.sh
     else
         echo Downloading and installing the rnnlm tools
         # http://www.fit.vutbr.cz/~imikolov/rnnlm/$rnnlm_ver.tgz
         if [ ! -f $rnnlm_ver.tgz ]; then
  	   wget http://www.fit.vutbr.cz/~imikolov/rnnlm/$rnnlm_ver.tgz || exit 1;
         fi
         mkdir $rnnlm_ver
         cd $rnnlm_ver
         tar -xvzf ../$rnnlm_ver.tgz || exit 1;
         make CC=g++ || exit 1;
         echo Done making the rnnlm tools
         fi
     fi
  ) || exit 1;
  
  echo "lexicon->wordlist"
  cut -d' ' -f1 $lexicon  > $dir/wordlist.all
  
  # Get training data with OOV words (w.r.t. our current vocab) replaced with <UNK>.                                                                                                   
  echo "Getting training data with OOV words replaced with <UNK>"
  cut -d' ' -f2- $text | awk -v w=$dir/wordlist.all \
    'BEGIN{while((getline<w)>0) v[$1]=1;}                                                                                                                                              
    {for (i=1;i<=NF;i++) if ($i in v) printf $i" ";else printf "<UNK> ";print ""}'|sed 's/ $//g' \
    | gzip -c > $dir/all.gz
  
  echo "Splitting data into train and validation sets."
  heldout_sent=10000
  gunzip -c $dir/all.gz | head -n $heldout_sent > $dir/valid.in # validation data                                                                                                      
  gunzip -c $dir/all.gz | tail -n +$heldout_sent | \
   perl -e ' use List::Util qw(shuffle); @A=<>; print join("", shuffle(@A)); ' \
    > $dir/train.in # training data 
  
      # The rest will consist of a word-class represented by <RNN_UNK>, that
      # maps (with probabilities) to a whole class of words.
  
  # Get unigram counts from our training data, and use this to select word-list
  # for RNNLM training; e.g. 10k most frequent words.  Rest will go in a class
  # that we (manually, at the shell level) assign probabilities for words that
  # are in that class.  Note: this word-list doesn't need to include </s>; this
  # automatically gets added inside the rnnlm program.
  # Note: by concatenating with $dir/wordlist.all, we are doing add-one
  # smoothing of the counts.
  
  echo "unigram-count"
  cat $dir/train.in $dir/wordlist.all | grep -v '</s>' | grep -v '<s>' | \
    awk '{ for(x=1;x<=NF;x++) count[$x]++; } END{for(w in count){print count[w], w;}}' | \
      sort -nr > $dir/unigram.counts
  
  head -$nwords $dir/unigram.counts | awk '{print $2}' > $dir/wordlist.rnn
  tail -n +$nwords $dir/unigram.counts > $dir/unk_class.counts
  
  tot=`awk '{x=x+$1} END{print x}' $dir/unk_class.counts`
  awk -v tot=$tot '{print $2, ($1*1.0/tot);}' <$dir/unk_class.counts  >$dir/unk.probs
  
  for type in train valid; do
    cat $dir/$type.in | awk -v w=$dir/wordlist.rnn \
      'BEGIN{while((getline<w)>0) v[$1]=1;}                                                                                                                                            
      {for (i=1;i<=NF;i++) if ($i in v) printf $i" ";else printf "<RNN_UNK> ";print ""}'|sed 's/ $//g' \
      > $dir/$type
  done
  rm $dir/train.in # no longer needed-- and big.                                                                                                                                       
  
  # Now randomize the order of the training data.                                                                                                                                     
  echo "Randomize training data"
  cat $dir/train | awk -v rand_seed=$rand_seed 'BEGIN{srand(rand_seed);} {printf("%f\t%s
  ", rand(), $0);}' | \
   sort | cut -f 2 > $dir/foo
  mv $dir/foo $dir/train
  
  echo "Finish data preparation!"
  
  # OK we'll train the RNNLM on this data.
  echo "Training RNNLM (note: this uses a lot of memory! Run it on a big machine.)"
  
  $cmd $dir/rnnlm.log \
     $rnnlm -threads $threads -independent -train $dir/train -valid $dir/valid \
     -rnnlm $dir/rnnlm -hidden $hidden -rand-seed 1 -debug 2 -class $class -bptt $bptt -bptt-block $bptt_block \
     -direct-order 4 -direct $direct -binary || exit 1;
  
  
  # make it like a Kaldi table format, with fake utterance-ids.
  cat $dir/valid.in | awk '{ printf("uttid-%d ", NR); print; }' > $dir/valid.with_ids
  
  utils/rnnlm_compute_scores.sh --rnnlm_ver $rnnlm_ver $dir $dir/tmp.valid $dir/valid.with_ids \
    $dir/valid.scores
  nw=`wc -w < $dir/valid.with_ids` # Note: valid.with_ids includes utterance-ids which
    # is one per word, to account for the </s> at the end of each sentence; this is the
    # correct number to normalize buy.
  p=`awk -v nw=$nw '{x=x+$2} END{print exp(x/nw);}' <$dir/valid.scores` 
  echo Perplexity is $p | tee $dir/perplexity.log
  
  rm $dir/train $dir/all.gz