From 51715d7b2604762a3fbc56e583042f106a6a03e2 Mon Sep 17 00:00:00 2001 From: "Liu, Hao" Date: Mon, 10 Aug 2020 08:15:22 +0800 Subject: [PATCH 01/35] fix pooling_convention warning when convert model to onnx (#18529) * fix pooling_convention warning * fix pooling_convention warning * fix lint Co-authored-by: JackieWu --- .../contrib/onnx/mx2onnx/_op_translations.py | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index b1ab40e1bf02..161d77579231 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -648,12 +648,13 @@ def convert_pooling(node, **kwargs): p_value = attrs.get('p_value', 'None') pooling_convention = attrs.get('pooling_convention', 'valid') - + ceil_mode = False if pooling_convention == 'full': - pooling_warning = "Pooling: ONNX currently doesn't support pooling_convention. " \ - "This might lead to shape or accuracy issues. " \ - "https://github.com/onnx/onnx/issues/549" - + if onnx.__version__ < "1.5.0": + pooling_warning = "Pooling: ONNX lower than 1.5.0 doesn't support pooling_convention. " \ + "This might lead to shape or accuracy issues. " \ + "https://github.com/onnx/onnx/issues/549" + ceil_mode = True logging.warning(pooling_warning) pad_dims = list(parse_helper(attrs, "pad", [0, 0])) @@ -694,15 +695,27 @@ def convert_pooling(node, **kwargs): name=name ) else: - node = onnx.helper.make_node( - pool_types[pool_type], - input_nodes, # input - [name], - kernel_shape=kernel, - pads=pad_dims, - strides=stride, - name=name - ) + if onnx.__version__ >= "1.5.0": + node = onnx.helper.make_node( + pool_types[pool_type], + input_nodes, # input + [name], + kernel_shape=kernel, + pads=pad_dims, + strides=stride, + name=name, + ceil_mode=ceil_mode + ) + else: + node = onnx.helper.make_node( + pool_types[pool_type], + input_nodes, # input + [name], + kernel_shape=kernel, + pads=pad_dims, + strides=stride, + name=name + ) return [node] From 7b7141b8ba08a208e0b095e5cc582ee258a9f625 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Tue, 18 Aug 2020 16:19:07 -0700 Subject: [PATCH 02/35] Prevent uninitialized variable error. --- python/mxnet/contrib/onnx/mx2onnx/_op_translations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index 161d77579231..322925c5371a 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -654,8 +654,8 @@ def convert_pooling(node, **kwargs): pooling_warning = "Pooling: ONNX lower than 1.5.0 doesn't support pooling_convention. " \ "This might lead to shape or accuracy issues. " \ "https://github.com/onnx/onnx/issues/549" + logging.warning(pooling_warning) ceil_mode = True - logging.warning(pooling_warning) pad_dims = list(parse_helper(attrs, "pad", [0, 0])) pad_dims = pad_dims + pad_dims From aa1515b47326c9d8b77a90f2b21a4c5992d1e001 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 26 Aug 2020 03:29:45 +0000 Subject: [PATCH 03/35] Initial work to get Dropout to work with onnx 1.7 --- .../contrib/onnx/mx2onnx/_op_translations.py | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index 322925c5371a..cdf0b3867fd4 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -961,13 +961,31 @@ def convert_dropout(node, **kwargs): probability = float(attrs.get("p", 0.5)) - dropout_node = onnx.helper.make_node( - "Dropout", - input_nodes, - [name], - ratio=probability, - name=name - ) + if onnx.defs.onnx_opset_version() >= 12: + # opset >= 12 requires the ratio to be an input + initializer = kwargs["initializer"] + ratio_input_name = name + "_ratio" + initializer.append( + onnx.helper.make_tensor(ratio_input_name, onnx.TensorProto.FLOAT, + (), [probability]) + ) + #onnx.helper.make_tensor_value_info(ratio_input_name, + # onnx.TensorProto.FLOAT, ()) + dropout_node = onnx.helper.make_node( + "Dropout", + [input_nodes[0], ratio_input_name], + [name], + name=name + ) + else: + dropout_node = onnx.helper.make_node( + "Dropout", + input_nodes, + [name], + ratio=probability, + name=name + ) + return [dropout_node] From 77fb75f464c5e0bb3b17fbd53a571a2c1b7a9bf9 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 26 Aug 2020 16:36:42 +0000 Subject: [PATCH 04/35] Remove trailing whitespace for pylint. --- python/mxnet/contrib/onnx/mx2onnx/_op_translations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index cdf0b3867fd4..a5a78206f01e 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -966,11 +966,11 @@ def convert_dropout(node, **kwargs): initializer = kwargs["initializer"] ratio_input_name = name + "_ratio" initializer.append( - onnx.helper.make_tensor(ratio_input_name, onnx.TensorProto.FLOAT, + onnx.helper.make_tensor(ratio_input_name, onnx.TensorProto.FLOAT, (), [probability]) ) #onnx.helper.make_tensor_value_info(ratio_input_name, - # onnx.TensorProto.FLOAT, ()) + # onnx.TensorProto.FLOAT, ()) dropout_node = onnx.helper.make_node( "Dropout", [input_nodes[0], ratio_input_name], From ae1e74dcf829080d924cba05eb9c75812c2bea66 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Thu, 27 Aug 2020 19:15:12 +0000 Subject: [PATCH 05/35] Fix tensor initialization for Dropout operator input. --- .../mxnet/contrib/onnx/mx2onnx/_op_translations.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index a5a78206f01e..643d3b1ef8cd 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -965,18 +965,18 @@ def convert_dropout(node, **kwargs): # opset >= 12 requires the ratio to be an input initializer = kwargs["initializer"] ratio_input_name = name + "_ratio" - initializer.append( - onnx.helper.make_tensor(ratio_input_name, onnx.TensorProto.FLOAT, + value_node = onnx.helper.make_tensor_value_info(ratio_input_name, + onnx.TensorProto.FLOAT, ()) + tensor_node = onnx.helper.make_tensor(ratio_input_name, onnx.TensorProto.FLOAT, (), [probability]) - ) - #onnx.helper.make_tensor_value_info(ratio_input_name, - # onnx.TensorProto.FLOAT, ()) + initializer.append(tensor_node) dropout_node = onnx.helper.make_node( "Dropout", [input_nodes[0], ratio_input_name], [name], name=name ) + return [value_node, dropout_node] else: dropout_node = onnx.helper.make_node( "Dropout", @@ -985,8 +985,7 @@ def convert_dropout(node, **kwargs): ratio=probability, name=name ) - - return [dropout_node] + return [dropout_node] @mx_op.register("Flatten") From 0faeeef40beb842768bf8457ffd6da891658b159 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Thu, 27 Aug 2020 19:35:55 +0000 Subject: [PATCH 06/35] Update Clip operator to support latest ONNX opset versions by moving min/max attributes to inputs. --- .../contrib/onnx/mx2onnx/_op_translations.py | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index 643d3b1ef8cd..423773e615d3 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -1002,18 +1002,44 @@ def convert_clip(node, **kwargs): """ name, input_nodes, attrs = get_inputs(node, kwargs) - a_min = np.float(attrs.get('a_min', -np.inf)) - a_max = np.float(attrs.get('a_max', np.inf)) + a_min = float(attrs.get('a_min', -np.inf)) + a_max = float(attrs.get('a_max', np.inf)) - clip_node = onnx.helper.make_node( - "Clip", - input_nodes, - [name], - name=name, - min=a_min, - max=a_max - ) - return [clip_node] + if onnx.defs.onnx_opset_version() >= 11: + # opset >= 11 requires min/max to be inputs + initializer = kwargs["initializer"] + min_input_name = name + "_min" + max_input_name = name + "_max" + min_value_node = onnx.helper.make_tensor_value_info(min_input_name, + onnx.TensorProto.FLOAT, ()) + max_value_node = onnx.helper.make_tensor_value_info(max_input_name, + onnx.TensorProto.FLOAT, ()) + min_tensor_node = onnx.helper.make_tensor(min_input_name, onnx.TensorProto.FLOAT, + (), [a_min]) + max_tensor_node = onnx.helper.make_tensor(max_input_name, onnx.TensorProto.FLOAT, + (), [a_max]) + initializer.append(min_tensor_node) + initializer.append(max_tensor_node) + input_nodes.append(min_input_name) + input_nodes.append(max_input_name) + clip_node = onnx.helper.make_node( + "Clip", + input_nodes, + [name], + name=name + ) + return [min_value_node, max_value_node, clip_node] + + else: + clip_node = onnx.helper.make_node( + "Clip", + input_nodes, + [name], + name=name, + min=a_min, + max=a_max + ) + return [clip_node] def scalar_op_helper(node, op_name, **kwargs): From e9453c53182c14d9a4c844faecc5f14675b73db0 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Thu, 27 Aug 2020 20:24:06 +0000 Subject: [PATCH 07/35] Fix whitespace. --- python/mxnet/contrib/onnx/mx2onnx/_op_translations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index 423773e615d3..a97ab76ecce1 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -966,9 +966,9 @@ def convert_dropout(node, **kwargs): initializer = kwargs["initializer"] ratio_input_name = name + "_ratio" value_node = onnx.helper.make_tensor_value_info(ratio_input_name, - onnx.TensorProto.FLOAT, ()) + onnx.TensorProto.FLOAT, ()) tensor_node = onnx.helper.make_tensor(ratio_input_name, onnx.TensorProto.FLOAT, - (), [probability]) + (), [probability]) initializer.append(tensor_node) dropout_node = onnx.helper.make_node( "Dropout", From 1d5b664606224e8234fbbe3d9a8f35115ad3102f Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Fri, 28 Aug 2020 01:07:10 +0000 Subject: [PATCH 08/35] Add support for importing Dropout operator in ONNX opset version >= 12. --- .../contrib/onnx/onnx2mx/_op_translations.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py b/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py index 311fd86ef623..329f7670e31c 100644 --- a/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py +++ b/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py @@ -446,12 +446,25 @@ def local_response_norm(attrs, inputs, proto_obj): def dropout(attrs, inputs, proto_obj): """Dropout Regularization.""" mode = 'training' + try: + from onnx.defs import onnx_opset_version + except ImportError: + raise ImportError("Onnx and protobuf need to be installed. " + "Instructions to install - https://github.com/onnx/onnx") + if 'is_test' in attrs and attrs['is_test'] == 0: mode = 'always' - new_attrs = translation_utils._fix_attribute_names(attrs, - {'ratio': 'p'}) - new_attrs = translation_utils._remove_attributes(new_attrs, ['is_test']) + new_attrs = translation_utils._remove_attributes(attrs, ['is_test']) new_attrs = translation_utils._add_extra_attributes(new_attrs, {'mode': mode}) + if onnx_opset_version() >= 12: + new_attrs = translation_utils._remove_attributes(new_attrs, ['seed']) + if len(inputs) == 2: + ratio_float = proto_obj._params[inputs[1].name].asnumpy()[0] + new_attrs = translation_utils._remove_attributes(new_attrs, ['p']) + new_attrs = translation_utils._add_extra_attributes(new_attrs, {'p': ratio_float}) + return 'Dropout', new_attrs, inputs[0] + else: + new_attrs = translation_utils._fix_attribute_names(new_attrs, {'ratio': 'p'}) return 'Dropout', new_attrs, inputs # Changing shape and type. From 9c5c0340c7b510e91999169f29dc2cafcdec6c2b Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Fri, 28 Aug 2020 03:44:45 +0000 Subject: [PATCH 09/35] Add support for import ONNX opsets >= 11 to clip operator. --- .../contrib/onnx/onnx2mx/_op_translations.py | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py b/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py index 329f7670e31c..51df2a027f76 100644 --- a/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py +++ b/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py @@ -562,13 +562,32 @@ def flatten(attrs, inputs, proto_obj): def clip(attrs, inputs, proto_obj): """Clips (limits) the values in an array.""" - new_attrs = translation_utils._fix_attribute_names(attrs, {'min' : 'a_min', - 'max' : 'a_max'}) - if 'a_max' not in new_attrs: - new_attrs = translation_utils._add_extra_attributes(new_attrs, {'a_max' : np.inf}) - if 'a_min' not in new_attrs: - new_attrs = translation_utils._add_extra_attributes(new_attrs, {'a_min' : -np.inf}) - return 'clip', new_attrs, inputs + try: + from onnx.defs import onnx_opset_version + except ImportError: + raise ImportError("Onnx and protobuf need to be installed. " + "Instructions to install - https://github.com/onnx/onnx") + if onnx_opset_version() >= 11: + if len(inputs) == 1: + new_attrs = translation_utils._add_extra_attributes(new_attrs, {'a_max' : np.inf, + 'a_min' : -np.inf}) + elif len(inputs) == 2: + min_float = proto_obj._params[inputs[1].name].asnumpy() + new_attrs = translation_utils._add_extra_attributes(attrs, {'a_min': min_float[0], + 'a_max': np.inf}) + elif len(inputs) == 3: + min_float = proto_obj._params[inputs[1].name].asnumpy() + max_float = proto_obj._params[inputs[2].name].asnumpy() + new_attrs = translation_utils._add_extra_attributes(attrs, {'a_min': min_float[0], + 'a_max': max_float[0]}) + else: + new_attrs = translation_utils._fix_attribute_names(attrs, {'min' : 'a_min', + 'max' : 'a_max'}) + if 'a_max' not in new_attrs: + new_attrs = translation_utils._add_extra_attributes(new_attrs, {'a_max' : np.inf}) + if 'a_min' not in new_attrs: + new_attrs = translation_utils._add_extra_attributes(new_attrs, {'a_min' : -np.inf}) + return 'clip', new_attrs, inputs[0] def gather(attrs, inputs, proto_obj): """Gather elements from an input array along the given axis.""" From aabcdd5f4db35c9a3d5c7db7ecb87accd57193fd Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Fri, 28 Aug 2020 21:27:00 +0000 Subject: [PATCH 10/35] Add optional opset_version parameter that defaults to latest opset version supported by onnx. Pass this parameter to each graph layer when exporting. --- python/mxnet/contrib/onnx/mx2onnx/export_onnx.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/export_onnx.py b/python/mxnet/contrib/onnx/mx2onnx/export_onnx.py index 14aa52b29c6a..30965adcbbd5 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/export_onnx.py +++ b/python/mxnet/contrib/onnx/mx2onnx/export_onnx.py @@ -157,7 +157,7 @@ def convert_weights_to_numpy(weights_dict): return dict([(k.replace("arg:", "").replace("aux:", ""), v.asnumpy()) for k, v in weights_dict.items()]) - def create_onnx_graph_proto(self, sym, params, in_shape, in_type, verbose=False): + def create_onnx_graph_proto(self, sym, params, in_shape, in_type, verbose=False, opset_version=None): """Convert MXNet graph to ONNX graph Parameters @@ -181,10 +181,14 @@ def create_onnx_graph_proto(self, sym, params, in_shape, in_type, verbose=False) try: from onnx import (checker, helper, NodeProto, ValueInfoProto, TensorProto) from onnx.helper import make_tensor_value_info + from onnx.defs import onnx_opset_version except ImportError: raise ImportError("Onnx and protobuf need to be installed. " + "Instructions to install - https://github.com/onnx/onnx") + if opset_version is None: + opset_version = onnx_opset_version() + # When MXNet model is saved to json file , MXNet adds a node for label. # The name of this node is, name of the last node + "_label" ( i.e if last node # name is "Softmax", this node will have a name "Softmax_label". Also, the new node @@ -246,7 +250,8 @@ def create_onnx_graph_proto(self, sym, params, in_shape, in_type, verbose=False) proc_nodes=all_processed_nodes, initializer=initializer, index_lookup=index_lookup, - idx=idx + idx=idx, + opset_version=opset_version ) if isinstance(converted, list): From edd6f532c9361984422ff83f0066ffc654bbfee4 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Fri, 28 Aug 2020 21:28:39 +0000 Subject: [PATCH 11/35] Add optional parameter to create_model() that allows user to specify which onnx opset version they want to use when exporting, defaults to latest version supported by onnx. --- python/mxnet/contrib/onnx/mx2onnx/export_model.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/export_model.py b/python/mxnet/contrib/onnx/mx2onnx/export_model.py index 51a62ed46e59..2fc77604b9b6 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/export_model.py +++ b/python/mxnet/contrib/onnx/mx2onnx/export_model.py @@ -29,7 +29,7 @@ def export_model(sym, params, input_shape, input_type=np.float32, - onnx_file_path='model.onnx', verbose=False): + onnx_file_path='model.onnx', verbose=False, opset_version=None): """Exports the MXNet model file, passed as a parameter, into ONNX model. Accepts both symbol,parameter objects as well as json and params filepaths as input. Operator support and coverage - @@ -63,11 +63,15 @@ def export_model(sym, params, input_shape, input_type=np.float32, try: from onnx import helper, mapping + from onnx.defs import onnx_opset_version except ImportError: raise ImportError("Onnx and protobuf need to be installed. " + "Instructions to install - https://github.com/onnx/onnx") converter = MXNetGraph() + if opset_version is None: + # default is to use latest opset version the onnx package supports + opset_version = onnx_opset_version() data_format = np.dtype(input_type) # if input parameters are strings(file paths), load files and create symbol parameter objects @@ -76,11 +80,11 @@ def export_model(sym, params, input_shape, input_type=np.float32, sym_obj, params_obj = load_module(sym, params) onnx_graph = converter.create_onnx_graph_proto(sym_obj, params_obj, input_shape, mapping.NP_TYPE_TO_TENSOR_TYPE[data_format], - verbose=verbose) + verbose=verbose, opset_version=opset_version) elif isinstance(sym, symbol.Symbol) and isinstance(params, dict): onnx_graph = converter.create_onnx_graph_proto(sym, params, input_shape, mapping.NP_TYPE_TO_TENSOR_TYPE[data_format], - verbose=verbose) + verbose=verbose, opset_version=opset_version) else: raise ValueError("Input sym and params should either be files or objects") From 2dfa22f56a3eb3a1e382296c32ecdb6cb05e1bf9 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Fri, 28 Aug 2020 21:31:53 +0000 Subject: [PATCH 12/35] Use opset_version argument to determine operator format. --- python/mxnet/contrib/onnx/mx2onnx/_op_translations.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index a97ab76ecce1..91a05e9cc839 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -958,10 +958,11 @@ def convert_dropout(node, **kwargs): and return the created node. """ name, input_nodes, attrs = get_inputs(node, kwargs) + opset_version = kwargs["opset_version"] probability = float(attrs.get("p", 0.5)) - if onnx.defs.onnx_opset_version() >= 12: + if opset_version >= 12: # opset >= 12 requires the ratio to be an input initializer = kwargs["initializer"] ratio_input_name = name + "_ratio" @@ -1001,11 +1002,12 @@ def convert_clip(node, **kwargs): and return the created node. """ name, input_nodes, attrs = get_inputs(node, kwargs) + opset_version = kwargs["opset_version"] a_min = float(attrs.get('a_min', -np.inf)) a_max = float(attrs.get('a_max', np.inf)) - if onnx.defs.onnx_opset_version() >= 11: + if opset_version >= 11: # opset >= 11 requires min/max to be inputs initializer = kwargs["initializer"] min_input_name = name + "_min" From 6c4e5556acd5a916629ea477629922537b0dd0e7 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Fri, 28 Aug 2020 21:57:25 +0000 Subject: [PATCH 13/35] Add a opset_version parameter to from_onnx() so at operator conversion time, we know what opset version to use. --- python/mxnet/contrib/onnx/onnx2mx/import_model.py | 3 ++- python/mxnet/contrib/onnx/onnx2mx/import_onnx.py | 8 +++++--- python/mxnet/contrib/onnx/onnx2mx/import_to_gluon.py | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/python/mxnet/contrib/onnx/onnx2mx/import_model.py b/python/mxnet/contrib/onnx/onnx2mx/import_model.py index 1c195435729b..d060b082cc5c 100644 --- a/python/mxnet/contrib/onnx/onnx2mx/import_model.py +++ b/python/mxnet/contrib/onnx/onnx2mx/import_model.py @@ -56,7 +56,8 @@ def import_model(model_file): + "Instructions to install - https://github.com/onnx/onnx") # loads model file and returns ONNX protobuf object model_proto = onnx.load_model(model_file) - sym, arg_params, aux_params = graph.from_onnx(model_proto.graph) + model_opset_version = max([x.version for x in model_proto.opset_import]) + sym, arg_params, aux_params = graph.from_onnx(model_proto.graph, opset_version=model_opset_version) return sym, arg_params, aux_params def get_model_metadata(model_file): diff --git a/python/mxnet/contrib/onnx/onnx2mx/import_onnx.py b/python/mxnet/contrib/onnx/onnx2mx/import_onnx.py index 72913ddaec4e..c2be83d8f12e 100644 --- a/python/mxnet/contrib/onnx/onnx2mx/import_onnx.py +++ b/python/mxnet/contrib/onnx/onnx2mx/import_onnx.py @@ -36,6 +36,7 @@ def __init__(self): self.aux_dict = {} self.arg_dict = {} self.model_metadata = {} + self.opset_version = 0 def _convert_operator(self, node_name, op_name, attrs, inputs): """Convert from onnx operator to mxnet operator. @@ -72,7 +73,7 @@ def _convert_operator(self, node_name, op_name, attrs, inputs): return mxnet_sym return op_name - def from_onnx(self, graph): + def from_onnx(self, graph, opset_version): """Construct symbol from onnx graph. Parameters @@ -87,6 +88,7 @@ def from_onnx(self, graph): params : dict A dict of name: nd.array pairs, used as pretrained weights """ + self.opset_version = opset_version # get input, output shapes self.model_metadata = self.get_graph_metadata(graph) # parse network inputs, aka parameters @@ -156,7 +158,7 @@ def get_graph_metadata(self, graph): } return metadata - def graph_to_gluon(self, graph, ctx): + def graph_to_gluon(self, graph, ctx, opset_version): """Construct SymbolBlock from onnx graph. Parameters @@ -171,7 +173,7 @@ def graph_to_gluon(self, graph, ctx): sym_block :gluon.nn.SymbolBlock The returned gluon SymbolBlock """ - sym, arg_params, aux_params = self.from_onnx(graph) + sym, arg_params, aux_params = self.from_onnx(graph, opset_version) metadata = self.get_graph_metadata(graph) data_names = [input_tensor[0] for input_tensor in metadata['input_tensor_data']] data_inputs = [symbol.var(data_name) for data_name in data_names] diff --git a/python/mxnet/contrib/onnx/onnx2mx/import_to_gluon.py b/python/mxnet/contrib/onnx/onnx2mx/import_to_gluon.py index 13ad5b9f8fa1..f6e10365d5d1 100644 --- a/python/mxnet/contrib/onnx/onnx2mx/import_to_gluon.py +++ b/python/mxnet/contrib/onnx/onnx2mx/import_to_gluon.py @@ -49,5 +49,6 @@ def import_to_gluon(model_file, ctx): raise ImportError("Onnx and protobuf need to be installed. Instructions to" + " install - https://github.com/onnx/onnx#installation") model_proto = onnx.load_model(model_file) - net = graph.graph_to_gluon(model_proto.graph, ctx) + model_opset_version = max([x.version for x in model_proto.opset_import]) + net = graph.graph_to_gluon(model_proto.graph, ctx, model_opset_version) return net From 7305b9db7bb4ca21c30882cebb873cb7681020a7 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Fri, 28 Aug 2020 21:59:54 +0000 Subject: [PATCH 14/35] For Clip and Dropout operators, use opset version from passed proto_obj, which reflects what opset version the onnx model uses. --- .../contrib/onnx/onnx2mx/_op_translations.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py b/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py index 51df2a027f76..abbcbbb2a496 100644 --- a/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py +++ b/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py @@ -446,22 +446,19 @@ def local_response_norm(attrs, inputs, proto_obj): def dropout(attrs, inputs, proto_obj): """Dropout Regularization.""" mode = 'training' - try: - from onnx.defs import onnx_opset_version - except ImportError: - raise ImportError("Onnx and protobuf need to be installed. " - "Instructions to install - https://github.com/onnx/onnx") - + opset_version = proto_obj.opset_version if 'is_test' in attrs and attrs['is_test'] == 0: mode = 'always' new_attrs = translation_utils._remove_attributes(attrs, ['is_test']) new_attrs = translation_utils._add_extra_attributes(new_attrs, {'mode': mode}) - if onnx_opset_version() >= 12: + if opset_version >= 12: new_attrs = translation_utils._remove_attributes(new_attrs, ['seed']) if len(inputs) == 2: ratio_float = proto_obj._params[inputs[1].name].asnumpy()[0] new_attrs = translation_utils._remove_attributes(new_attrs, ['p']) new_attrs = translation_utils._add_extra_attributes(new_attrs, {'p': ratio_float}) + elif len(inputs) == 1: + new_attrs = translation_utils._fix_attribute_names(new_attrs, {'ratio': 'p'}) return 'Dropout', new_attrs, inputs[0] else: new_attrs = translation_utils._fix_attribute_names(new_attrs, {'ratio': 'p'}) @@ -562,12 +559,8 @@ def flatten(attrs, inputs, proto_obj): def clip(attrs, inputs, proto_obj): """Clips (limits) the values in an array.""" - try: - from onnx.defs import onnx_opset_version - except ImportError: - raise ImportError("Onnx and protobuf need to be installed. " - "Instructions to install - https://github.com/onnx/onnx") - if onnx_opset_version() >= 11: + opset_version = proto_obj.opset_version + if opset_version >= 11: if len(inputs) == 1: new_attrs = translation_utils._add_extra_attributes(new_attrs, {'a_max' : np.inf, 'a_min' : -np.inf}) From 39da0fc32b1447e9eddcfac7045916b121a0f1e9 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Mon, 31 Aug 2020 23:25:49 +0000 Subject: [PATCH 15/35] Use same tolerances that are in master. --- tests/python-pytest/onnx/mxnet_export_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python-pytest/onnx/mxnet_export_test.py b/tests/python-pytest/onnx/mxnet_export_test.py index 90e92cccee06..947fa2f6bf97 100644 --- a/tests/python-pytest/onnx/mxnet_export_test.py +++ b/tests/python-pytest/onnx/mxnet_export_test.py @@ -74,7 +74,7 @@ def _check_onnx_export(net, group_outputs=False, shape_type=tuple, extra_params= # Confirm network outputs are the same imported_net_output = _force_list(imported_net(data)) for out, imp_out in zip(output, imported_net_output): - mx.test_utils.assert_almost_equal(out, imp_out) + mx.test_utils.assert_almost_equal(out, imp_out, atol=1e-5, rtol=1e-5) class TestExport(unittest.TestCase): From e36c2008982e2a3068e54cb1a2872e9665bff51d Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Tue, 1 Sep 2020 15:26:54 +0000 Subject: [PATCH 16/35] Change Pad operator to use inputs instead of attributes for newer opset versions. Check opset version instead of ONNX version for Pooling operator. --- .../contrib/onnx/mx2onnx/_op_translations.py | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index 91a05e9cc839..cb73540cd80c 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -175,7 +175,7 @@ def convert_weights_and_inputs(node, **kwargs): data_type=data_type, dims=dims, vals=np_arr.flatten().tolist(), - raw=False, + raw=False ) ) @@ -462,6 +462,7 @@ def convert_pad(node, **kwargs): """Map MXNet's pad operator attributes to onnx's Pad operator and return the created node. """ + opset_version = kwargs["opset_version"] name, input_nodes, attrs = get_inputs(node, kwargs) mxnet_pad_width = convert_string_to_list(attrs.get("pad_width")) @@ -470,17 +471,42 @@ def convert_pad(node, **kwargs): pad_mode = attrs.get("mode") if pad_mode == "constant": - pad_value = float(attrs.get("constant_value")) \ - if "constant_value" in attrs else 0.0 - node = onnx.helper.make_node( - 'Pad', - inputs=input_nodes, - outputs=[name], - mode='constant', - value=pad_value, - pads=onnx_pad_width, - name=name - ) + pad_value = float(attrs.get("constant_value", 0.0)) + if opset_version >= 11: + # starting with opset 11, pads and constant_value are inputs instead of attributes + from onnx.helper import make_tensor, make_tensor_value_info + initializer = kwargs["initializer"] + pads_input_name = name + "_pads" + pads_input_type = onnx.TensorProto.INT64 + pads_input_shape = np.shape(np.array(onnx_pad_width)) + pads_value_node = make_tensor_value_info(pads_input_name, pads_input_type, pads_input_shape) + pads_tensor_node = make_tensor(pads_input_name, pads_input_type, pads_input_shape, onnx_pad_width) + initializer.append(pads_tensor_node) + input_nodes.append(pads_input_name) + + const_input_name = name + "_constant" + const_input_type = onnx.mapping.NP_TYPE_TO_TENSOR_TYPE[pad_value.dtype] + const_value_node = make_tensor_value_info(const_input_name, const_input_type, ()) + const_tensor_node = make_tensor(const_input_name, const_input_type, (), [pad_value]) + initializer.append(const_tensor_node) + input_nodes.append(const_input_name) + pad_node = onnx.helper.make_node( + "Pad", + input_nodes, + [name], + name=name + ) + return [pads_value_node, const_value_node, pad_node] + else: + node = onnx.helper.make_node( + 'Pad', + inputs=input_nodes, + outputs=[name], + mode='constant', + value=pad_value, + pads=onnx_pad_width, + name=name + ) else: node = onnx.helper.make_node( 'Pad', @@ -639,6 +665,7 @@ def convert_pooling(node, **kwargs): MaxPool/AveragePool/GlobalMaxPool/GlobalAveragePool operators based on the input node's attributes and return the created node. """ + opset_version = kwargs["opset_version"] name, input_nodes, attrs = get_inputs(node, kwargs) kernel = eval(attrs["kernel"]) @@ -650,7 +677,7 @@ def convert_pooling(node, **kwargs): pooling_convention = attrs.get('pooling_convention', 'valid') ceil_mode = False if pooling_convention == 'full': - if onnx.__version__ < "1.5.0": + if opset_version < 10: pooling_warning = "Pooling: ONNX lower than 1.5.0 doesn't support pooling_convention. " \ "This might lead to shape or accuracy issues. " \ "https://github.com/onnx/onnx/issues/549" @@ -695,7 +722,7 @@ def convert_pooling(node, **kwargs): name=name ) else: - if onnx.__version__ >= "1.5.0": + if opset_version >= 10: node = onnx.helper.make_node( pool_types[pool_type], input_nodes, # input From e4a9318aab43fdb10bd0a4e3d35d798efe515e79 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Tue, 1 Sep 2020 16:12:51 +0000 Subject: [PATCH 17/35] Add documentation opset_version parameter. --- python/mxnet/contrib/onnx/mx2onnx/export_onnx.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/mxnet/contrib/onnx/mx2onnx/export_onnx.py b/python/mxnet/contrib/onnx/mx2onnx/export_onnx.py index 30965adcbbd5..bd2533646df3 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/export_onnx.py +++ b/python/mxnet/contrib/onnx/mx2onnx/export_onnx.py @@ -172,6 +172,8 @@ def create_onnx_graph_proto(self, sym, params, in_shape, in_type, verbose=False, Input data type e.g. np.float32 verbose : Boolean If true will print logs of the model conversion + opset_version : Int + ONNX opset version to use for export, defaults to latest supported by onnx package Returns ------- From 85a0ea6ed71de899b6729dbbc2829bccc2f22afe Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Tue, 1 Sep 2020 16:13:24 +0000 Subject: [PATCH 18/35] Add opset_version parameters to unit tests. --- tests/python-pytest/onnx/backend.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/python-pytest/onnx/backend.py b/tests/python-pytest/onnx/backend.py index 2f9e2470d225..eb803f790332 100644 --- a/tests/python-pytest/onnx/backend.py +++ b/tests/python-pytest/onnx/backend.py @@ -26,6 +26,7 @@ try: from onnx import helper, TensorProto, mapping from onnx.backend.base import Backend + from onnx.defs import onnx_opset_version except ImportError: raise ImportError("Onnx and protobuf need to be installed. Instructions to" + " install - https://github.com/onnx/onnx#installation") @@ -57,13 +58,16 @@ def perform_import_export(sym, arg_params, aux_params, input_shape): params = {} params.update(arg_params) params.update(aux_params) + # use the latest opset version supported by the onnx library + opset_version = onnx_opset_version() # exporting to onnx graph proto format converter = MXNetGraph() graph_proto = converter.create_onnx_graph_proto(sym, params, in_shape=input_shape, - in_type=mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype('float32')]) + in_type=mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype('float32')], + opset_version=opset_version) # importing back to MXNET for verifying result. - sym, arg_params, aux_params = graph.from_onnx(graph_proto) + sym, arg_params, aux_params = graph.from_onnx(graph_proto, opset_version) return sym, arg_params, aux_params @@ -95,8 +99,11 @@ def prepare(cls, model, device='CPU', **kwargs): else: raise NotImplementedError("ONNX tests are run only for CPU context.") + # determine opset version model uses + model_opset_version = max([x.version for x in model.opset_import]) + if backend == 'mxnet': - sym, arg_params, aux_params = graph.from_onnx(model.graph) + sym, arg_params, aux_params = graph.from_onnx(model.graph, model_opset_version) if operation == 'export': metadata = graph.get_graph_metadata(model.graph) input_data = metadata['input_tensor_data'] @@ -107,7 +114,7 @@ def prepare(cls, model, device='CPU', **kwargs): return MXNetBackendRep(sym, arg_params, aux_params, device) elif backend == 'gluon': if operation == 'import': - net = graph.graph_to_gluon(model.graph, ctx) + net = graph.graph_to_gluon(model.graph, ctx, model_opset_version) return GluonBackendRep(net, device) elif operation == 'export': raise NotImplementedError("Gluon->ONNX export not implemented.") From 0738620a5560d333f2364b1e10984c634d65314d Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Tue, 1 Sep 2020 23:58:02 +0000 Subject: [PATCH 19/35] Add test script for testing inference with onnxruntime on CV models from gluon model zoo. --- tests/python/unittest/test_contrib_onnx.py | 116 +++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 tests/python/unittest/test_contrib_onnx.py diff --git a/tests/python/unittest/test_contrib_onnx.py b/tests/python/unittest/test_contrib_onnx.py new file mode 100644 index 000000000000..d19b824d312b --- /dev/null +++ b/tests/python/unittest/test_contrib_onnx.py @@ -0,0 +1,116 @@ +import tempfile +import os +import mxnet as mx +import numpy as np +import shutil +import onnxruntime +import cv2 +import gluoncv +import json + +from PIL import Image + +def test_cv_model_inference_onnxruntime(): + def get_gluon_cv_model(model_name, tmp): + tmpfile = os.path.join(tmp, model_name) + ctx = mx.cpu(0) + net_fp32 = mx.gluon.model_zoo.vision.get_model(model_name, pretrained=True, ctx=ctx, root=tmp) + net_fp32.hybridize() + data = mx.nd.zeros((1,3,224,224), dtype='float32', ctx=ctx) + net_fp32.forward(data) + net_fp32.export(tmpfile, 0) + sym_file = tmpfile + '-symbol.json' + params_file = tmpfile + '-0000.params' + return sym_file, params_file + + def export_model_to_onnx(sym_file, params_file): + input_shape = (1,3,224,224) + onnx_file = os.path.join(os.path.dirname(sym_file), "model.onnx") + converted_model_path = mx.contrib.onnx.export_model(sym_file, params_file, [input_shape], + np.float32, onnx_file) + return onnx_file + + def normalize_image(imgfile): + image = Image.open(imgfile) + image_data = np.array(image).transpose(2, 0, 1) + img_data = image_data.astype('float32') + mean_vec = np.array([0.485, 0.456, 0.406]) + stddev_vec = np.array([0.229, 0.224, 0.225]) + norm_img_data = np.zeros(img_data.shape).astype('float32') + for i in range(img_data.shape[0]): + norm_img_data[i,:,:] = (img_data[i,:,:]/255 - mean_vec[i]) / stddev_vec[i] + return norm_img_data.reshape(1, 3, 224, 224).astype('float32') + + def get_prediction(model, image): + pass + + def softmax(x): + x = x.reshape(-1) + e_x = np.exp(x - np.max(x)) + return e_x / e_x.sum(axis=0) + + def load_imgnet_labels(): + mx.test_utils.download('https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/doc/tutorials/onnx/image_net_labels.json') + return np.array(json.load(open('image_net_labels.json', 'r'))) + + def download_test_images(): + test_images = [ + ['dog.jpg',['boxer']], + ['apron.jpg', ['apron', 'maillot']], + ['dolphin.jpg', ['great white shark','grey whale']], + ['hammerheadshark.jpg', ['tiger shark']], + ['lotus.jpg', ['pinwheel','pot']] + ] + for f,_ in test_images: + mx.test_utils.download('https://github.com/dmlc/web-data/blob/master/mxnet/doc/tutorials/onnx/images/'+f+'?raw=true', + fname=f) + return test_images + + + test_models = [ + 'mobilenet1.0', 'mobilenet0.75', 'mobilenet0.5', 'mobilenet0.25', + 'mobilenetv2_1.0', 'mobilenetv2_0.75', 'mobilenetv2_0.5', 'mobilenetv2_0.25', + 'resnet18_v1', 'resnet18_v2', 'resnet34_v1', 'resnet34_v2', 'resnet50_v1', 'resnet50_v2', + 'resnet101_v1', 'resnet101_v2', 'resnet152_v1', 'resnet152_v2', + 'squeezenet1.0', 'squeezenet1.1', + 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', 'vgg19', 'vgg19_bn' + ] + labels = load_imgnet_labels() + test_images = download_test_images() + + for model in test_models: + tmpdir = tempfile.mkdtemp() + sym_file, params_file = get_gluon_cv_model(model, tmpdir) + onnx_file = export_model_to_onnx(sym_file, params_file) + #print("exported onnx file: ",onnx_file) + + # create onnxruntime session using the generated onnx file + ses_opt = onnxruntime.SessionOptions() + ses_opt.log_severity_level = 3 + session = onnxruntime.InferenceSession(onnx_file, ses_opt) + input_name = session.get_inputs()[0].name + + for img,classes in test_images: + img_data = normalize_image(img) + raw_result = session.run([], {input_name: img_data}) + res = softmax(np.array(raw_result)).tolist() + class_idx = np.argmax(res) + #print("Image top classification:",labels[class_idx]) + sort_idx = np.flip(np.squeeze(np.argsort(res))) + #print("\tTop labels: " + ",".join(labels[sort_idx[:5]])) + correct_classification = False + for label in labels[sort_idx[:5]]: + for c in classes: + if c in label: + correct_classification = True + assert correct_classification == True + + # cleanup + shutil.rmtree(tmpdir) + + + + +if __name__ == "__main__": + test_cv_model_inference_onnxruntime() + From 885862d4d0ec3ac7826839681015ea936fdb5783 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 2 Sep 2020 00:20:47 +0000 Subject: [PATCH 20/35] Add license and clean up imports. --- tests/python/unittest/test_contrib_onnx.py | 26 +++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/tests/python/unittest/test_contrib_onnx.py b/tests/python/unittest/test_contrib_onnx.py index d19b824d312b..7c3e761a2ed5 100644 --- a/tests/python/unittest/test_contrib_onnx.py +++ b/tests/python/unittest/test_contrib_onnx.py @@ -1,12 +1,28 @@ -import tempfile -import os +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + import mxnet as mx import numpy as np -import shutil import onnxruntime -import cv2 -import gluoncv + import json +import os +import shutil +import tempfile from PIL import Image From 9bb2b477076976d379e98ba45f69a841d68d7f7e Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 2 Sep 2020 02:17:28 +0000 Subject: [PATCH 21/35] Install onnxruntime in docker container for unit tests. --- ci/docker/install/ubuntu_onnx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/docker/install/ubuntu_onnx.sh b/ci/docker/install/ubuntu_onnx.sh index 44d6b9ed52dc..f04404d9bbb8 100755 --- a/ci/docker/install/ubuntu_onnx.sh +++ b/ci/docker/install/ubuntu_onnx.sh @@ -31,4 +31,4 @@ apt-get update || true apt-get install -y libprotobuf-dev protobuf-compiler echo "Installing pytest, pytest-cov, protobuf, Pillow, ONNX and tabulate ..." -pip3 install pytest==3.6.3 pytest-cov==2.5.1 protobuf==3.5.2 onnx==1.3.0 Pillow==5.0.0 tabulate==0.7.5 +pip3 install pytest==3.6.3 pytest-cov==2.5.1 protobuf==3.5.2 onnx==1.3.0 Pillow==5.0.0 tabulate==0.7.5 onnxruntime==1.4.0 From 50d929c19faa23332883a432ad016de0be0d950f Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 2 Sep 2020 03:39:29 +0000 Subject: [PATCH 22/35] Add onnxruntime to test dependencies. --- tests/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/requirements.txt b/tests/requirements.txt index dde62c52fdf3..24764fb1ae84 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -6,3 +6,4 @@ nose-timer ipython numpy>1.16.0,<1.19.0 # Restrict numpy version to < 1.19.0 due to https://github.com/apache/incubator-mxnet/issues/18600 scipy +onnxruntime From a6e696703c00cfe56fea3ed20136710f659b9ac5 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 2 Sep 2020 16:32:18 +0000 Subject: [PATCH 23/35] Install onnxruntime into CentOS docker image. --- ci/docker/install/centos7_python.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/docker/install/centos7_python.sh b/ci/docker/install/centos7_python.sh index 1bbcf5c8b024..a0f4402a5ccc 100755 --- a/ci/docker/install/centos7_python.sh +++ b/ci/docker/install/centos7_python.sh @@ -30,4 +30,4 @@ yum -y install python36u curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" python3.6 get-pip.py # Restrict numpy version to < 1.19.0 due to https://github.com/apache/incubator-mxnet/issues/18600 -pip3 install nose pylint 'numpy>1.16.0,<1.19.0' nose-timer requests h5py scipy==1.2.3 +pip3 install nose pylint 'numpy>1.16.0,<1.19.0' nose-timer requests h5py scipy==1.2.3 onnxruntime=1.4.0 From 0bfec8e3881c2936efe818bf1e279a4957b7130c Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 2 Sep 2020 17:20:44 +0000 Subject: [PATCH 24/35] Disable testing squeezenet models for now. --- tests/python/unittest/test_contrib_onnx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/unittest/test_contrib_onnx.py b/tests/python/unittest/test_contrib_onnx.py index 7c3e761a2ed5..15dda725a7f8 100644 --- a/tests/python/unittest/test_contrib_onnx.py +++ b/tests/python/unittest/test_contrib_onnx.py @@ -88,7 +88,7 @@ def download_test_images(): 'mobilenetv2_1.0', 'mobilenetv2_0.75', 'mobilenetv2_0.5', 'mobilenetv2_0.25', 'resnet18_v1', 'resnet18_v2', 'resnet34_v1', 'resnet34_v2', 'resnet50_v1', 'resnet50_v2', 'resnet101_v1', 'resnet101_v2', 'resnet152_v1', 'resnet152_v2', - 'squeezenet1.0', 'squeezenet1.1', + #'squeezenet1.0', 'squeezenet1.1', 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', 'vgg19', 'vgg19_bn' ] labels = load_imgnet_labels() From 26708e3e5785e34776464b43b12bfdb7fb10bc8a Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 2 Sep 2020 18:27:28 +0000 Subject: [PATCH 25/35] Update onnx version. --- ci/docker/install/ubuntu_onnx.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/docker/install/ubuntu_onnx.sh b/ci/docker/install/ubuntu_onnx.sh index f04404d9bbb8..31eb5e8c9380 100755 --- a/ci/docker/install/ubuntu_onnx.sh +++ b/ci/docker/install/ubuntu_onnx.sh @@ -30,5 +30,5 @@ echo "Installing libprotobuf-dev and protobuf-compiler ..." apt-get update || true apt-get install -y libprotobuf-dev protobuf-compiler -echo "Installing pytest, pytest-cov, protobuf, Pillow, ONNX and tabulate ..." -pip3 install pytest==3.6.3 pytest-cov==2.5.1 protobuf==3.5.2 onnx==1.3.0 Pillow==5.0.0 tabulate==0.7.5 onnxruntime==1.4.0 +echo "Installing pytest, pytest-cov, protobuf, Pillow, ONNX, tabulate and onnxruntime..." +pip3 install pytest==3.6.3 pytest-cov==2.5.1 protobuf==3.5.2 onnx==1.7.0 Pillow==5.0.0 tabulate==0.7.5 onnxruntime==1.4.0 From d6205480b2e33398f630acac1290914da714e0e8 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 2 Sep 2020 18:29:05 +0000 Subject: [PATCH 26/35] Fix typo. --- ci/docker/install/centos7_python.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/docker/install/centos7_python.sh b/ci/docker/install/centos7_python.sh index a0f4402a5ccc..9dbfac7f1905 100755 --- a/ci/docker/install/centos7_python.sh +++ b/ci/docker/install/centos7_python.sh @@ -30,4 +30,4 @@ yum -y install python36u curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" python3.6 get-pip.py # Restrict numpy version to < 1.19.0 due to https://github.com/apache/incubator-mxnet/issues/18600 -pip3 install nose pylint 'numpy>1.16.0,<1.19.0' nose-timer requests h5py scipy==1.2.3 onnxruntime=1.4.0 +pip3 install nose pylint 'numpy>1.16.0,<1.19.0' nose-timer requests h5py scipy==1.2.3 onnxruntime==1.4.0 From c7b55c1243308146c86c6af6aa17ca55000218e0 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 2 Sep 2020 21:08:06 +0000 Subject: [PATCH 27/35] Use mx.image.imread instead of PIL module. --- tests/python/unittest/test_contrib_onnx.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/python/unittest/test_contrib_onnx.py b/tests/python/unittest/test_contrib_onnx.py index 15dda725a7f8..d3a963b09b61 100644 --- a/tests/python/unittest/test_contrib_onnx.py +++ b/tests/python/unittest/test_contrib_onnx.py @@ -24,7 +24,6 @@ import shutil import tempfile -from PIL import Image def test_cv_model_inference_onnxruntime(): def get_gluon_cv_model(model_name, tmp): @@ -47,7 +46,7 @@ def export_model_to_onnx(sym_file, params_file): return onnx_file def normalize_image(imgfile): - image = Image.open(imgfile) + image = mx.image.imread(imgfile).asnumpy() image_data = np.array(image).transpose(2, 0, 1) img_data = image_data.astype('float32') mean_vec = np.array([0.485, 0.456, 0.406]) From f49e47ab19ed98f09c9f133d59475c94938d6fa2 Mon Sep 17 00:00:00 2001 From: Serge Panev Date: Fri, 24 Jul 2020 14:22:42 -0700 Subject: [PATCH 28/35] ONNX import: use Conv pad attribute for symmetrical padding (#18675) Signed-off-by: Serge Panev --- .../contrib/onnx/onnx2mx/_op_translations.py | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py b/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py index abbcbbb2a496..c71e702b1359 100644 --- a/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py +++ b/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py @@ -334,14 +334,25 @@ def conv(attrs, inputs, proto_obj): no_bias = new_attrs['no_bias'] if 'no_bias' in new_attrs else 0 bias = None if no_bias is True else inputs[2] - # Unlike ONNX, MXNet's convolution operator does not support asymmetric padding, so we first - # use 'Pad' operator, which supports asymmetric padding. Then use the convolution operator. - pad_width = (0, 0, 0, 0) + translation_utils._pad_sequence_fix(padding, kernel_dim=len(kernel)) - pad_op = symbol.pad(inputs[0], mode='constant', pad_width=pad_width) - - conv_op = symbol.Convolution(pad_op, inputs[1], bias, - kernel=kernel, stride=stride, dilate=dilations, - num_filter=num_filter, num_group=num_group, no_bias=no_bias) + mxnet_pad = translation_utils._pad_sequence_fix(padding, kernel_dim=len(kernel)) + + left_pads = mxnet_pad[0::2] + right_pads = mxnet_pad[1::2] + is_pad_sym = left_pads == right_pads + + if not is_pad_sym: + # Unlike ONNX, MXNet's convolution operator does not support asymmetric padding, so we first + # use 'Pad' operator, which supports asymmetric padding. Then use the convolution operator. + pad_width = (0, 0, 0, 0) + mxnet_pad + pad_op = symbol.pad(inputs[0], mode='constant', pad_width=pad_width) + conv_op = symbol.Convolution(pad_op, inputs[1], bias, + kernel=kernel, stride=stride, dilate=dilations, + num_filter=num_filter, num_group=num_group, no_bias=no_bias) + else: + pad_width = left_pads + conv_op = symbol.Convolution(inputs[0], inputs[1], bias, + kernel=kernel, stride=stride, dilate=dilations, pad=pad_width, + num_filter=num_filter, num_group=num_group, no_bias=no_bias) return conv_op, new_attrs, inputs From 36d92ca2a84d4a6d6190db076bee123753d28518 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Wed, 2 Sep 2020 22:32:51 +0000 Subject: [PATCH 29/35] Install onnx in CentOS containers when installing python. --- ci/docker/install/centos7_python.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/docker/install/centos7_python.sh b/ci/docker/install/centos7_python.sh index 9dbfac7f1905..6cadb8d1f154 100755 --- a/ci/docker/install/centos7_python.sh +++ b/ci/docker/install/centos7_python.sh @@ -30,4 +30,4 @@ yum -y install python36u curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" python3.6 get-pip.py # Restrict numpy version to < 1.19.0 due to https://github.com/apache/incubator-mxnet/issues/18600 -pip3 install nose pylint 'numpy>1.16.0,<1.19.0' nose-timer requests h5py scipy==1.2.3 onnxruntime==1.4.0 +pip3 install nose pylint 'numpy>1.16.0,<1.19.0' nose-timer requests h5py scipy==1.2.3 onnx==1.7.0 onnxruntime==1.4.0 From b102f78a2a75ced5dbd62f2548cd0b43862b6fef Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Thu, 3 Sep 2020 00:29:33 +0000 Subject: [PATCH 30/35] Update import and export of some ONNX ops to support newer opset versions - this gets all ONNX unit tests to pass with onnx 1.7. --- .../contrib/onnx/mx2onnx/_op_translations.py | 78 ++++++++++++++----- .../contrib/onnx/onnx2mx/_op_translations.py | 66 ++++++++++++---- .../onnx/test_onnxruntime.py} | 0 3 files changed, 109 insertions(+), 35 deletions(-) rename tests/{python/unittest/test_contrib_onnx.py => python-pytest/onnx/test_onnxruntime.py} (100%) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index cb73540cd80c..0e546c8c16e7 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -471,7 +471,7 @@ def convert_pad(node, **kwargs): pad_mode = attrs.get("mode") if pad_mode == "constant": - pad_value = float(attrs.get("constant_value", 0.0)) + pad_value = np.float32(attrs.get("constant_value", 0.0)) if opset_version >= 11: # starting with opset 11, pads and constant_value are inputs instead of attributes from onnx.helper import make_tensor, make_tensor_value_info @@ -494,6 +494,7 @@ def convert_pad(node, **kwargs): "Pad", input_nodes, [name], + mode='constant', name=name ) return [pads_value_node, const_value_node, pad_node] @@ -508,16 +509,35 @@ def convert_pad(node, **kwargs): name=name ) else: - node = onnx.helper.make_node( - 'Pad', - inputs=input_nodes, - outputs=[name], - mode=pad_mode, - pads=onnx_pad_width, - name=name - ) - - return [node] + if opset_version >= 11: + # starting with opset 11, pads and constant_value are inputs instead of attributes + from onnx.helper import make_tensor, make_tensor_value_info + initializer = kwargs["initializer"] + pads_input_name = name + "_pads" + pads_input_type = onnx.TensorProto.INT64 + pads_input_shape = np.shape(np.array(onnx_pad_width)) + pads_value_node = make_tensor_value_info(pads_input_name, pads_input_type, pads_input_shape) + pads_tensor_node = make_tensor(pads_input_name, pads_input_type, pads_input_shape, onnx_pad_width) + initializer.append(pads_tensor_node) + input_nodes.append(pads_input_name) + pad_node = onnx.helper.make_node( + "Pad", + input_nodes, + [name], + mode=pad_mode, + name=name + ) + return [pads_value_node, pad_node] + else: + node = onnx.helper.make_node( + 'Pad', + inputs=input_nodes, + outputs=[name], + mode=pad_mode, + pads=onnx_pad_width, + name=name + ) + return [node] def create_helper_trans_node(op_name, input_node, node_name): @@ -2155,14 +2175,34 @@ def convert_topk(node, **kwargs): else: raise NotImplementedError("ONNX expects both value and indices as output") - topk_node = onnx.helper.make_node( - "TopK", - input_nodes, - outputs, - axis=axis, - k=k, - name=name - ) + opset_version = kwargs['opset_version'] + if opset_version >= 10: + from onnx.helper import make_tensor, make_tensor_value_info + initializer = kwargs["initializer"] + k_input_name = name + "_k" + k_input_type = onnx.TensorProto.INT64 + k_value_node = make_tensor_value_info(k_input_name, k_input_type, ()) + k_tensor_node = make_tensor(k_input_name, k_input_type, (), k) + initializer.append(k_tensor_node) + input_nodes.append(k_input_name) + + topk_node = onnx.helper.make_node( + "TopK", + input_nodes, + outputs, + axis=axis, + name=name + ) + return [k_value_node, topk_node] + else: + topk_node = onnx.helper.make_node( + "TopK", + input_nodes, + outputs, + axis=axis, + k=k, + name=name + ) return [topk_node] diff --git a/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py b/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py index c71e702b1359..51fe41895ec0 100644 --- a/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py +++ b/python/mxnet/contrib/onnx/onnx2mx/_op_translations.py @@ -240,11 +240,24 @@ def relu(attrs, inputs, proto_obj): def pad(attrs, inputs, proto_obj): """ Add padding to input tensor""" - new_attrs = translation_utils._fix_attribute_names(attrs, {'pads' : 'pad_width', - 'value' : 'constant_value' - }) - new_attrs['pad_width'] = translation_utils._pad_sequence_fix(new_attrs.get('pad_width')) - return 'pad', new_attrs, inputs + opset_version = proto_obj.opset_version + if 'mode' not in attrs.keys(): + attrs['mode'] = 'constant' + if opset_version >= 11: + pads = list(proto_obj._params[inputs[1].name].asnumpy()) + pads = tuple([int(i) for i in pads]) + new_attrs = translation_utils._add_extra_attributes(attrs, {'pad_width': pads}) + if len(inputs) == 3: + const = proto_obj._params[inputs[2].name].asnumpy()[0] + new_attrs = translation_utils._add_extra_attributes(new_attrs, {'constant_value': const}) + new_attrs['pad_width'] = translation_utils._pad_sequence_fix(new_attrs.get('pad_width')) + return 'pad', new_attrs, inputs[0] + else: + new_attrs = translation_utils._fix_attribute_names(attrs, {'pads' : 'pad_width', + 'value' : 'constant_value' + }) + new_attrs['pad_width'] = translation_utils._pad_sequence_fix(new_attrs.get('pad_width')) + return 'pad', new_attrs, inputs def matrix_multiplication(attrs, inputs, proto_obj): """Performs general matrix multiplication""" @@ -367,7 +380,7 @@ def deconv(attrs, inputs, proto_obj): new_attrs = translation_utils._fix_bias('Deconvolution', new_attrs, len(inputs)) new_attrs = translation_utils._fix_channels('Deconvolution', new_attrs, inputs, proto_obj) - kernel = new_attrs['kernel'] + kernel = new_attrs['kernel'] if 'kernel' in new_attrs else [] stride = new_attrs['stride'] if 'stride' in new_attrs else [] padding = new_attrs['pad'] if 'pad' in new_attrs else [] dilations = new_attrs['dilate'] if 'dilate' in new_attrs else [] @@ -522,15 +535,30 @@ def _slice(attrs, inputs, proto_obj): """Returns a slice of the input tensor along multiple axes.""" input_tensor_data = proto_obj.model_metadata.get('input_tensor_data')[0] input_shape = input_tensor_data[1] - new_attrs = translation_utils._fix_attribute_names(attrs, - {'axes' : 'axis', - 'ends' : 'end', - 'starts' : 'begin'}) - # onnx slice provides slicing on multiple axis. Adding multiple slice_axis operator - # for multiple axes from mxnet - begin = new_attrs.get('begin') - end = list(new_attrs.get('end')) - axes = new_attrs.get('axis', tuple(range(len(begin)))) + + if proto_obj.opset_version >= 10: + begin = proto_obj._params[inputs[1].name].asnumpy() + end = proto_obj._params[inputs[2].name].asnumpy() + if len(inputs) >= 4: + axes = list(proto_obj._params[inputs[3].name].asnumpy()) + axes = tuple([int(i) for i in axes]) + else: + axes = tuple(range(len(begin))) + new_attrs = translation_utils._add_extra_attributes(attrs, {'axes' : axes, + 'begin' : begin, + 'end' : end + }) + else: + new_attrs = translation_utils._fix_attribute_names(attrs, + {'axes' : 'axis', + 'ends' : 'end', + 'starts' : 'begin'}) + # onnx slice provides slicing on multiple axis. Adding multiple slice_axis operator + # for multiple axes from mxnet + begin = new_attrs.get('begin') + end = list(new_attrs.get('end')) + axes = new_attrs.get('axis', tuple(range(len(begin)))) + for i, axis in enumerate(axes): end[i] = None if end[i] >= input_shape[axis] else end[i] slice_op = symbol.slice_axis(inputs[0], axis=axes[0], begin=begin[0], end=end[0]) @@ -826,4 +854,10 @@ def topk(attrs, inputs, proto_obj): new_attrs = translation_utils._add_extra_attributes(attrs, {'ret_typ': 'both', 'dtype': 'int64'}) - return 'topk', new_attrs, inputs + opset_version = proto_obj.opset_version + if opset_version >= 10: + k_vals = proto_obj._params[inputs[1].name].asnumpy() + new_attrs = translation_utils._add_extra_attributes(new_attrs, {'k': k_vals}) + return 'topk', new_attrs, inputs[0] + else: + return 'topk', new_attrs, inputs diff --git a/tests/python/unittest/test_contrib_onnx.py b/tests/python-pytest/onnx/test_onnxruntime.py similarity index 100% rename from tests/python/unittest/test_contrib_onnx.py rename to tests/python-pytest/onnx/test_onnxruntime.py From 8bd6a649383972a985ce1e8fd7c7cc998c0884a3 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Thu, 3 Sep 2020 00:41:32 +0000 Subject: [PATCH 31/35] Re-enable squeezenet model testings in onnxruntime. --- tests/python-pytest/onnx/test_onnxruntime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python-pytest/onnx/test_onnxruntime.py b/tests/python-pytest/onnx/test_onnxruntime.py index d3a963b09b61..052b24185735 100644 --- a/tests/python-pytest/onnx/test_onnxruntime.py +++ b/tests/python-pytest/onnx/test_onnxruntime.py @@ -87,7 +87,7 @@ def download_test_images(): 'mobilenetv2_1.0', 'mobilenetv2_0.75', 'mobilenetv2_0.5', 'mobilenetv2_0.25', 'resnet18_v1', 'resnet18_v2', 'resnet34_v1', 'resnet34_v2', 'resnet50_v1', 'resnet50_v2', 'resnet101_v1', 'resnet101_v2', 'resnet152_v1', 'resnet152_v2', - #'squeezenet1.0', 'squeezenet1.1', + 'squeezenet1.0', 'squeezenet1.1', 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', 'vgg19', 'vgg19_bn' ] labels = load_imgnet_labels() From a3ea8515cf2dd539ef09f93fd9d48a5c86f111b5 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Thu, 3 Sep 2020 00:42:11 +0000 Subject: [PATCH 32/35] Run the onnxruntime inference tests in the ONNX pipeline instead of normal unittests pipelines. --- ci/docker/runtime_functions.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci/docker/runtime_functions.sh b/ci/docker/runtime_functions.sh index b5cbb9a35122..f8f2b570a32d 100755 --- a/ci/docker/runtime_functions.sh +++ b/ci/docker/runtime_functions.sh @@ -1228,11 +1228,13 @@ unittest_centos7_gpu() { integrationtest_ubuntu_cpu_onnx() { set -ex export PYTHONPATH=./python/ - export DMLC_LOG_STACK_TRACE_DEPTH=10 + export MXNET_SUBGRAPH_VERBOSE=0 + export DMLC_LOG_STACK_TRACE_DEPTH=10 tests/python-pytest/onnx/backend_test.py pytest tests/python-pytest/onnx/mxnet_export_test.py pytest tests/python-pytest/onnx/test_models.py pytest tests/python-pytest/onnx/test_node.py + pytest tests/python-pytest/onnx/test_onnxruntime.py } integrationtest_ubuntu_gpu_python() { From a5246fe394d318a989e1faeb6a69edc044bfb28b Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Thu, 3 Sep 2020 01:58:44 +0000 Subject: [PATCH 33/35] Add missed return value. --- python/mxnet/contrib/onnx/mx2onnx/_op_translations.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index 0e546c8c16e7..db8538068d5d 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -508,6 +508,7 @@ def convert_pad(node, **kwargs): pads=onnx_pad_width, name=name ) + return [node] else: if opset_version >= 11: # starting with opset 11, pads and constant_value are inputs instead of attributes From 29dcdf37793013ca7254440dcfe2e9e0e119c461 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Tue, 8 Sep 2020 21:18:41 +0000 Subject: [PATCH 34/35] Refactor code based on review comment. --- .../contrib/onnx/mx2onnx/_op_translations.py | 60 ++++++++----------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index db8538068d5d..f03fabb3e0c6 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -469,21 +469,21 @@ def convert_pad(node, **kwargs): onnx_pad_width = transform_padding(mxnet_pad_width) pad_mode = attrs.get("mode") + pad_value = np.float32(attrs.get("constant_value", 0.0)) - if pad_mode == "constant": - pad_value = np.float32(attrs.get("constant_value", 0.0)) - if opset_version >= 11: - # starting with opset 11, pads and constant_value are inputs instead of attributes - from onnx.helper import make_tensor, make_tensor_value_info - initializer = kwargs["initializer"] - pads_input_name = name + "_pads" - pads_input_type = onnx.TensorProto.INT64 - pads_input_shape = np.shape(np.array(onnx_pad_width)) - pads_value_node = make_tensor_value_info(pads_input_name, pads_input_type, pads_input_shape) - pads_tensor_node = make_tensor(pads_input_name, pads_input_type, pads_input_shape, onnx_pad_width) - initializer.append(pads_tensor_node) - input_nodes.append(pads_input_name) - + if opset_version >= 11: + # starting with opset 11, pads and constant_value are inputs instead of attributes + from onnx.helper import make_tensor, make_tensor_value_info + initializer = kwargs["initializer"] + pads_input_name = name + "_pads" + pads_input_type = onnx.TensorProto.INT64 + pads_input_shape = np.shape(np.array(onnx_pad_width)) + pads_value_node = make_tensor_value_info(pads_input_name, pads_input_type, pads_input_shape) + pads_tensor_node = make_tensor(pads_input_name, pads_input_type, pads_input_shape, onnx_pad_width) + initializer.append(pads_tensor_node) + input_nodes.append(pads_input_name) + + if pad_mode == "constant": const_input_name = name + "_constant" const_input_type = onnx.mapping.NP_TYPE_TO_TENSOR_TYPE[pad_value.dtype] const_value_node = make_tensor_value_info(const_input_name, const_input_type, ()) @@ -494,11 +494,21 @@ def convert_pad(node, **kwargs): "Pad", input_nodes, [name], - mode='constant', + mode=pad_mode, name=name ) return [pads_value_node, const_value_node, pad_node] else: + pad_node = onnx.helper.make_node( + "Pad", + input_nodes, + [name], + mode=pad_mode, + name=name + ) + return [pads_value_node, pad_node] + else: + if pad_mode == "constant": node = onnx.helper.make_node( 'Pad', inputs=input_nodes, @@ -509,26 +519,6 @@ def convert_pad(node, **kwargs): name=name ) return [node] - else: - if opset_version >= 11: - # starting with opset 11, pads and constant_value are inputs instead of attributes - from onnx.helper import make_tensor, make_tensor_value_info - initializer = kwargs["initializer"] - pads_input_name = name + "_pads" - pads_input_type = onnx.TensorProto.INT64 - pads_input_shape = np.shape(np.array(onnx_pad_width)) - pads_value_node = make_tensor_value_info(pads_input_name, pads_input_type, pads_input_shape) - pads_tensor_node = make_tensor(pads_input_name, pads_input_type, pads_input_shape, onnx_pad_width) - initializer.append(pads_tensor_node) - input_nodes.append(pads_input_name) - pad_node = onnx.helper.make_node( - "Pad", - input_nodes, - [name], - mode=pad_mode, - name=name - ) - return [pads_value_node, pad_node] else: node = onnx.helper.make_node( 'Pad', From d597b5af9be656545d0f40ac79d382c0224d58c0 Mon Sep 17 00:00:00 2001 From: Joe Evans Date: Tue, 8 Sep 2020 21:38:37 +0000 Subject: [PATCH 35/35] Since the onnx tests are only run on ubuntu_cpu images, we don't need to install onnx and onnxruntime in the CentOS containers. --- ci/docker/install/centos7_python.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/docker/install/centos7_python.sh b/ci/docker/install/centos7_python.sh index 6cadb8d1f154..1bbcf5c8b024 100755 --- a/ci/docker/install/centos7_python.sh +++ b/ci/docker/install/centos7_python.sh @@ -30,4 +30,4 @@ yum -y install python36u curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" python3.6 get-pip.py # Restrict numpy version to < 1.19.0 due to https://github.com/apache/incubator-mxnet/issues/18600 -pip3 install nose pylint 'numpy>1.16.0,<1.19.0' nose-timer requests h5py scipy==1.2.3 onnx==1.7.0 onnxruntime==1.4.0 +pip3 install nose pylint 'numpy>1.16.0,<1.19.0' nose-timer requests h5py scipy==1.2.3