Skip to content
This repository was archived by the owner on Oct 13, 2021. It is now read-only.

Commit 49e00fe

Browse files
authored
improve the converter debugging (#466)
1 parent 167bcba commit 49e00fe

File tree

4 files changed

+64
-29
lines changed

4 files changed

+64
-29
lines changed

keras2onnx/_parser_1x.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def on_parsing_keras_layer(graph, node_list, layer, kenode, model, varset, prefi
8383
kenode_input_shapes = kenode.input_shapes if isinstance(kenode.input_shapes, list) else [kenode.input_shapes]
8484
for n_, i_ in enumerate(inputs):
8585
iname = prefix + i_.name
86-
k2o_logger().debug('input : ' + iname)
86+
k2o_logger().debug('\tinput : ' + iname)
8787
var_type = adjust_input_batch_size(infer_variable_type(i_, varset.target_opset, kenode_input_shapes[n_]))
8888
i0 = varset.get_local_variable_or_declare_one(iname, var_type)
8989
operator.add_input(i0)
@@ -92,22 +92,22 @@ def on_parsing_keras_layer(graph, node_list, layer, kenode, model, varset, prefi
9292
in_mask = layer.input_mask if isinstance(layer.input_mask, (list, tuple)) else [layer.input_mask]
9393
for im_ in [m_ for m_ in in_mask if m_ is not None]:
9494
mts_name = im_.name # input mask in a shared model is not supported yet, why is it needed?
95-
k2o_logger().debug('input mask: ' + mts_name)
95+
k2o_logger().debug('\tinput mask: ' + mts_name)
9696
mts_var = varset.get_local_variable_or_declare_one(mts_name, infer_variable_type(im_, varset.target_opset))
9797
operator.add_input_mask(mts_var)
9898

9999
kenode_output_shapes = kenode.output_shapes if isinstance(kenode.output_shapes, list) else [kenode.output_shapes]
100100
for n_, o_ in enumerate(outputs):
101101
oname = prefix + o_.name
102-
k2o_logger().debug('output: ' + oname)
102+
k2o_logger().debug('\toutput: ' + oname)
103103
o1 = varset.get_local_variable_or_declare_one(oname, infer_variable_type(o_, varset.target_opset, kenode_output_shapes[n_]))
104104
operator.add_output(o1)
105105

106106
if hasattr(layer, 'output_mask') and layer.output_mask is not None:
107107
out_mask = layer.output_mask if isinstance(layer.output_mask, (list, tuple)) else [layer.output_mask]
108108
for om_ in [m_ for m_ in out_mask if m_ is not None]:
109109
mts_name = prefix + om_.name
110-
k2o_logger().debug('output mask: ' + mts_name)
110+
k2o_logger().debug('\toutput mask: ' + mts_name)
111111
mts_var = varset.get_local_variable_or_declare_one(mts_name, infer_variable_type(om_, varset.target_opset))
112112
operator.add_output_mask(mts_var)
113113

keras2onnx/_parser_tf.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,27 +311,27 @@ def on_parsing_keras_layer_v2(graph, layer_info, varset, prefix=None):
311311
for o_ in layer_info.outputs:
312312
if o_ not in output_masks: # the layer converter will handle output_mask by itself.
313313
oname = prefix + o_.name
314-
k2o_logger().debug('output: ' + oname)
314+
k2o_logger().debug('\toutput: ' + oname)
315315
o1 = varset.get_local_variable_or_declare_one(oname, infer_variable_type(o_, varset.target_opset))
316316
operator.add_output(o1)
317317

318318
for i_ in layer_info.inputs:
319319
if i_ not in input_masks: # the layer converter will handle input_mask by itself.
320320
iname = prefix + i_.name
321-
k2o_logger().debug('input : ' + iname)
321+
k2o_logger().debug('\tinput : ' + iname)
322322
var_type = adjust_input_batch_size(infer_variable_type(i_, varset.target_opset))
323323
i0 = varset.get_local_variable_or_declare_one(iname, var_type)
324324
operator.add_input(i0)
325325

326326
for om_ in [m_ for m_ in output_masks if m_ is not None]:
327327
mts_name = prefix + om_.name
328-
k2o_logger().debug('output mask: ' + mts_name)
328+
k2o_logger().debug('\toutput mask: ' + mts_name)
329329
mts_var = varset.get_local_variable_or_declare_one(mts_name, infer_variable_type(om_, varset.target_opset))
330330
operator.add_output_mask(mts_var)
331331

332332
for im_ in [m_ for m_ in input_masks if m_ is not None]:
333333
mts_name = im_.name # input mask in a shared model is not supported yet, why is it needed?
334-
k2o_logger().debug('input mask: ' + mts_name)
334+
k2o_logger().debug('\tinput mask: ' + mts_name)
335335
mts_var = varset.get_local_variable_or_declare_one(mts_name, infer_variable_type(im_, varset.target_opset))
336336
operator.add_input_mask(mts_var)
337337

keras2onnx/parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,12 +313,12 @@ def _on_parsing_tf_nodes(graph, nodelist, varset, debug_mode):
313313

314314
for o_ in node_.outputs:
315315
oname = o_.name
316-
k2o_logger().debug('output: ' + oname)
316+
k2o_logger().debug('\toutput: ' + oname)
317317
out0 = varset.get_local_variable_or_declare_one(oname, infer_variable_type(o_, varset.target_opset))
318318
operator.add_output(out0)
319319

320320
for i_ in node_.inputs:
321-
k2o_logger().debug('input : ' + i_.name)
321+
k2o_logger().debug('\tinput : ' + i_.name)
322322
var_type = infer_variable_type(i_, varset.target_opset)
323323
i0 = varset.get_local_variable_or_declare_one(i_.name, var_type)
324324
operator.add_input(i0)

keras2onnx/topology.py

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,53 @@ def _remove_unused_initializers(nodes, initializers):
203203
return adjusted_initializers
204204

205205

206+
def _remove_unused_nodes(nodes, inputs, outputs):
207+
nodes_input_set = set()
208+
for n_ in nodes:
209+
for input_name_ in n_.input:
210+
nodes_input_set.add(input_name_)
211+
212+
input_dict = set([in_.name for in_ in inputs])
213+
output_dict = {}
214+
for nd_ in nodes:
215+
output_dict.update({o_: nd_ for o_ in nd_.output})
216+
217+
nodes_to_keep = set()
218+
node_inputs = [output_dict[ts_.name] for ts_ in outputs]
219+
while node_inputs:
220+
nd_ = node_inputs[0]
221+
del node_inputs[0]
222+
if id(nd_) in nodes_to_keep:
223+
continue
224+
225+
nodes_to_keep.add(id(nd_))
226+
for in_ in nd_.input:
227+
if in_ in output_dict:
228+
node_inputs.append(output_dict[in_])
229+
else:
230+
assert in_ == '' or in_ in input_dict
231+
232+
return [nd_ for nd_ in nodes if id(nd_) in nodes_to_keep]
233+
234+
235+
def _build_extra_inputs(container):
236+
# When calling ModelComponentContainer's add_initializer(...), nothing is added into the input list.
237+
# However, In ONNX, for target opset < 9, initializers should also be model's (GraphProto) inputs.
238+
# Thus, we create ValueInfoProto objects from initializers (type: TensorProto) directly and then add them into model's input list.
239+
extra_inputs = [] # ValueInfoProto list of the initializers
240+
for tensor in container.initializers:
241+
# Sometimes (especially when creating optional input values such as RNN's initial hidden state), an initializer
242+
# is also one of the original model's input, so it has been added into the container's input list. If this is
243+
# the case, we need to skip one iteration to avoid duplicated inputs.
244+
if tensor.name in [value_info.name for value_info in container.inputs]:
245+
continue
246+
247+
# Initializers are always tensors so we can just call make_tensor_value_info(...)
248+
value_info = helper.make_tensor_value_info(tensor.name, tensor.data_type, tensor.dims)
249+
extra_inputs.append(value_info)
250+
return extra_inputs
251+
252+
206253
def convert_topology(topology, model_name, doc_string, target_opset, channel_first_inputs=None):
207254
"""
208255
This function is used to convert our Topology object defined in _parser.py into a ONNX model (type: ModelProto).
@@ -271,28 +318,15 @@ def convert_topology(topology, model_name, doc_string, target_opset, channel_fir
271318
raise RuntimeError("Unexpected error on find the converter for op {}".format(operator.type))
272319
cvt(scope, operator, container)
273320

274-
# When calling ModelComponentContainer's add_initializer(...), nothing is added into the input list.
275-
# However, In ONNX, for target opset < 9, initializers should also be model's (GraphProto) inputs.
276-
# Thus, we create ValueInfoProto objects from initializers (type: TensorProto) directly and then add them into model's input list.
277-
extra_inputs = [] # ValueInfoProto list of the initializers
278-
for tensor in container.initializers:
279-
# Sometimes (especially when creating optional input values such as RNN's initial hidden state), an initializer
280-
# is also one of the original model's input, so it has been added into the container's input list. If this is
281-
# the case, we need to skip one iteration to avoid duplicated inputs.
282-
if tensor.name in [value_info.name for value_info in container.inputs]:
283-
continue
284-
285-
# Initializers are always tensors so we can just call make_tensor_value_info(...)
286-
value_info = helper.make_tensor_value_info(tensor.name, tensor.data_type, tensor.dims)
287-
extra_inputs.append(value_info)
288-
289321
# enable the ONNX optimizations
290322
graph = None
291-
nodes = container.nodes
323+
extra_inputs = _build_extra_inputs(container)
324+
nodes = _remove_unused_nodes(container.nodes, container.inputs + extra_inputs, container.outputs)
325+
292326
if not topology.debug_mode:
293327
try:
294328
import onnxconverter_common
295-
origin_node_number = len(container.nodes)
329+
origin_node_number = len(nodes)
296330
if target_opset < 9:
297331
nodes = onnxconverter_common.optimizer.optimize_onnx(nodes, nchw_inputs=nchw_inputs,
298332
inputs=container.inputs + extra_inputs,
@@ -307,7 +341,8 @@ def convert_topology(topology, model_name, doc_string, target_opset, channel_fir
307341
model_name=model_name,
308342
target_opset=container.target_opset)
309343
node_number = len(graph.node)
310-
k2o_logger().info("The node number after optimization: {} -> {}".format(origin_node_number, node_number))
344+
k2o_logger().info(
345+
"The ONNX operator number change on the optimization: {} -> {}".format(origin_node_number, node_number))
311346
except ImportError:
312347
onnx_not_imported = 'onnxconverter_common is not imported,'
313348
if nchw_inputs:
@@ -326,8 +361,8 @@ def convert_topology(topology, model_name, doc_string, target_opset, channel_fir
326361
if graph is None:
327362
# Create a graph from its main components
328363
adjusted_initializers = _remove_unused_initializers(nodes, container.initializers)
329-
adjusted_extra_inputs = _remove_unused_initializers(nodes, extra_inputs)
330364
if target_opset < 9:
365+
adjusted_extra_inputs = _remove_unused_initializers(nodes, extra_inputs)
331366
graph = helper.make_graph(nodes, model_name, container.inputs + adjusted_extra_inputs,
332367
container.outputs, adjusted_initializers)
333368
else:

0 commit comments

Comments
 (0)