Blame view

egs/wsj/s5/steps/libs/nnet3/xconfig/parser.py 9.2 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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  # Copyright 2016    Johns Hopkins University (Dan Povey)
  #           2016    Vijayaditya Peddinti
  # Apache 2.0.
  
  """ This module contains the top level xconfig parsing functions.
  """
  
  from __future__ import print_function
  
  import logging
  import sys
  import libs.nnet3.xconfig.layers as xlayers
  import libs.nnet3.xconfig.utils as xutils
  
  import libs.common as common_lib
  
  
  # We have to modify this dictionary when adding new layers
  config_to_layer = {
          'input' : xlayers.XconfigInputLayer,
          'output' : xlayers.XconfigTrivialOutputLayer,
          'output-layer' : xlayers.XconfigOutputLayer,
          'relu-layer' : xlayers.XconfigBasicLayer,
          'relu-renorm-layer' : xlayers.XconfigBasicLayer,
          'relu-batchnorm-dropout-layer' : xlayers.XconfigBasicLayer,
          'relu-dropout-layer': xlayers.XconfigBasicLayer,
          'relu-batchnorm-layer' : xlayers.XconfigBasicLayer,
          'relu-batchnorm-so-layer' : xlayers.XconfigBasicLayer,
          'batchnorm-so-relu-layer' : xlayers.XconfigBasicLayer,
          'batchnorm-layer' : xlayers.XconfigBasicLayer,
          'sigmoid-layer' : xlayers.XconfigBasicLayer,
          'tanh-layer' : xlayers.XconfigBasicLayer,
          'fixed-affine-layer' : xlayers.XconfigFixedAffineLayer,
          'idct-layer' : xlayers.XconfigIdctLayer,
          'affine-layer' : xlayers.XconfigAffineLayer,
          'lstm-layer' : xlayers.XconfigLstmLayer,
          'lstmp-layer' : xlayers.XconfigLstmpLayer,
          'lstmp-batchnorm-layer' : xlayers.XconfigLstmpLayer,
          'fast-lstm-layer' : xlayers.XconfigFastLstmLayer,
          'fast-lstm-batchnorm-layer' : xlayers.XconfigFastLstmLayer,
          'fast-lstmp-layer' : xlayers.XconfigFastLstmpLayer,
          'fast-lstmp-batchnorm-layer' : xlayers.XconfigFastLstmpLayer,
          'lstmb-layer' : xlayers.XconfigLstmbLayer,
          'stats-layer': xlayers.XconfigStatsLayer,
          'relu-conv-layer': xlayers.XconfigConvLayer,
          'conv-layer': xlayers.XconfigConvLayer,
          'conv-relu-layer': xlayers.XconfigConvLayer,
          'conv-renorm-layer': xlayers.XconfigConvLayer,
          'relu-conv-renorm-layer': xlayers.XconfigConvLayer,
          'batchnorm-conv-layer': xlayers.XconfigConvLayer,
          'conv-relu-renorm-layer': xlayers.XconfigConvLayer,
          'batchnorm-conv-relu-layer': xlayers.XconfigConvLayer,
          'relu-batchnorm-conv-layer': xlayers.XconfigConvLayer,
          'relu-batchnorm-noconv-layer': xlayers.XconfigConvLayer,
          'relu-noconv-layer': xlayers.XconfigConvLayer,
          'conv-relu-batchnorm-layer': xlayers.XconfigConvLayer,
          'conv-relu-batchnorm-so-layer': xlayers.XconfigConvLayer,
          'conv-relu-batchnorm-dropout-layer': xlayers.XconfigConvLayer,
          'conv-relu-dropout-layer': xlayers.XconfigConvLayer,
          'res-block': xlayers.XconfigResBlock,
          'res2-block': xlayers.XconfigRes2Block,
          'channel-average-layer': xlayers.ChannelAverageLayer,
          'attention-renorm-layer': xlayers.XconfigAttentionLayer,
          'attention-relu-renorm-layer': xlayers.XconfigAttentionLayer,
          'attention-relu-batchnorm-layer': xlayers.XconfigAttentionLayer,
          'relu-renorm-attention-layer': xlayers.XconfigAttentionLayer,
          'gru-layer' : xlayers.XconfigGruLayer,
          'pgru-layer' : xlayers.XconfigPgruLayer,
          'opgru-layer' : xlayers.XconfigOpgruLayer,
          'norm-pgru-layer' : xlayers.XconfigNormPgruLayer,
          'norm-opgru-layer' : xlayers.XconfigNormOpgruLayer,
          'fast-gru-layer' : xlayers.XconfigFastGruLayer,
          'fast-pgru-layer' : xlayers.XconfigFastPgruLayer,
          'fast-norm-pgru-layer' : xlayers.XconfigFastNormPgruLayer,
          'fast-opgru-layer' : xlayers.XconfigFastOpgruLayer,
          'fast-norm-opgru-layer' : xlayers.XconfigFastNormOpgruLayer,
          'tdnnf-layer': xlayers.XconfigTdnnfLayer,
          'prefinal-layer': xlayers.XconfigPrefinalLayer,
          'spec-augment-layer': xlayers.XconfigSpecAugmentLayer,
          'renorm-component': xlayers.XconfigRenormComponent,
          'batchnorm-component': xlayers.XconfigBatchnormComponent,
          'no-op-component': xlayers.XconfigNoOpComponent,
          'linear-component': xlayers.XconfigLinearComponent,
          'affine-component': xlayers.XconfigAffineComponent,
          'scale-component':  xlayers.XconfigPerElementScaleComponent,
          'dim-range-component': xlayers.XconfigDimRangeComponent,
          'offset-component':  xlayers.XconfigPerElementOffsetComponent,
          'combine-feature-maps-layer': xlayers.XconfigCombineFeatureMapsLayer
  }
  
  # Turn a config line and a list of previous layers into
  # either an object representing that line of the config file; or None
  # if the line was empty after removing comments.
  # 'prev_layers' is a list of objects corresponding to preceding layers of the
  # config file.
  def xconfig_line_to_object(config_line, prev_layers = None):
      try:
          x  = xutils.parse_config_line(config_line)
          if x is None:
              return None
          (first_token, key_to_value) = x
          if not first_token in config_to_layer:
              raise RuntimeError("No such layer type '{0}'".format(first_token))
          return config_to_layer[first_token](first_token, key_to_value, prev_layers)
      except Exception:
          logging.error(
              "***Exception caught while parsing the following xconfig line:
  "
              "*** {0}".format(config_line))
          raise
  
  
  def get_model_component_info(model_filename):
      """
      This function reads existing model (*.raw or *.mdl) and returns array
      of XconfigExistingLayer one per {input,output}-node or component-node
      with same 'name' used in the raw model and 'dim' equal to 'output-dim'
      for component-node and 'dim' for {input,output}-node.
  
      e.g. layer in *.mdl -> corresponding 'XconfigExistingLayer' layer
           'input-node name=ivector dim=100' ->
           'existing name=ivector dim=100'
           'component-node name=tdnn1.affine ... input-dim=1000 '
           'output-dim=500' ->
           'existing name=tdnn1.affine dim=500'
      """
  
      all_layers = []
      try:
          f = open(model_filename, 'r')
      except Exception as e:
          sys.exit("{0}: error reading model file '{1}'".format(sys.argv[0],
                                                                model_filename,
                                                                repr(e)))
  
      # use nnet3-info to get component names in the model.
      out = common_lib.get_command_stdout("""nnet3-info {0} | grep '\-node' """
                                          """ """.format(model_filename))
  
      # out contains all {output, input, component}-nodes used in model_filename
      # It can parse lines in out like:
      # i.e. input-node name=input dim=40
      #   component-node name=tdnn1.affine component=tdnn1.affine input=lda
      #   input-dim=300 output-dim=512
      layer_names = []
      key_to_value = dict()
      for line in out.split("
  "):
          parts = line.split(" ")
          dim = -1
          for  field in parts:
              key_value = field.split("=")
              if len(key_value) == 2:
                  key = key_value[0]
                  value = key_value[1]
                  if key == "name":           # name=**
                      layer_name = value
                  elif key == "dim":          # for input-node
                      dim = int(value)
                  elif key == "output-dim":   # for component-node
                      dim = int(value)
  
          if layer_name is not None and layer_name not in layer_names:
              layer_names.append(layer_name)
              key_to_value['name'] = layer_name
              assert(dim != -1)
              key_to_value['dim'] = dim
              all_layers.append(xlayers.XconfigExistingLayer('existing', key_to_value, all_layers))
      if len(all_layers) == 0:
          raise RuntimeError("{0}: model filename '{1}' is empty.".format(
              sys.argv[0], model_filename))
      f.close()
      return all_layers
  
  
  # This function reads xconfig file and returns it as a list of layers
  # (usually we use the variable name 'all_layers' elsewhere for this).
  # It will die if the xconfig file is empty or if there was
  # some error parsing it.
  # 'existing_layers' contains some layers of type 'existing' (layers which are not really
  # layers but are actual component node names from an existing neural net model
  # and created using get_model_component_info function).
  # 'existing' layers can be used as input to component-nodes in layers of xconfig file.
  def read_xconfig_file(xconfig_filename, existing_layers=None):
      if existing_layers is None:
          existing_layers = []
      try:
          f = open(xconfig_filename, 'r')
      except Exception as e:
          sys.exit("{0}: error reading xconfig file '{1}'; error was {2}".format(
              sys.argv[0], xconfig_filename, repr(e)))
      all_layers = []
      while True:
          line = f.readline()
          if line == '':
              break
          # the next call will raise an easy-to-understand exception if
          # it fails.
          this_layer = xconfig_line_to_object(line, existing_layers)
          if this_layer is None:
              continue  # line was blank after removing comments.
          all_layers.append(this_layer)
          existing_layers.append(this_layer)
      if len(all_layers) == 0:
          raise RuntimeError("{0}: xconfig file '{1}' is empty".format(
              sys.argv[0], xconfig_filename))
      f.close()
      return all_layers