From d45b4f26e35f1a46193404fa6fcc27d1c68fd463 Mon Sep 17 00:00:00 2001 From: Wei Chu Date: Wed, 21 Apr 2021 11:20:43 -0700 Subject: [PATCH 1/8] export from arg and aux --- python/mxnet/onnx/mx2onnx/_export_model.py | 70 +++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/python/mxnet/onnx/mx2onnx/_export_model.py b/python/mxnet/onnx/mx2onnx/_export_model.py index b960282114c4..d00814e72034 100644 --- a/python/mxnet/onnx/mx2onnx/_export_model.py +++ b/python/mxnet/onnx/mx2onnx/_export_model.py @@ -55,7 +55,7 @@ def export_model(sym, params, in_shapes=None, in_types=np.float32, """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 - - https://cwiki.apache.org/confluence/display/MXNET/ONNX+Operator+Coverage + https://github.com/apache/incubator-mxnet/tree/v1.x/python/mxnet/onnx#operator-support-matrix Parameters ---------- @@ -89,7 +89,7 @@ def export_model(sym, params, in_shapes=None, in_types=np.float32, Notes ----- - This method is available when you ``import mxnet.contrib.onnx`` + This method is available when you ``import mxnet.onnx`` """ @@ -147,3 +147,69 @@ def export_model(sym, params, in_shapes=None, in_types=np.float32, logging.info("Exported ONNX file %s saved to disk", onnx_file_path) return onnx_file_path + + +def export_model(sym, arg_params, aux_params, in_shapes=None, in_types=np.float32, + onnx_file_path='model.onnx', verbose=False, dynamic=False, + dynamic_input_shapes=None, run_shape_inference=False, input_type=None, + input_shape=None): + """Exports the MXNet model file, passed as a parameter, into ONNX model. + Accepts symbol, arg_params and aux_params. + Operator support and coverage - + https://github.com/apache/incubator-mxnet/tree/v1.x/python/mxnet/onnx#operator-support-matrix + + Parameters + ---------- + sym : symbol object + Symbol object + arg_params : dict of str to NDArray + Model parameter, dict of name to NDArray of net's weights. + aux_params : dict of str to NDArray + Model parameter, dict of name to NDArray of net's auxiliary states. + in_shapes : List of tuple + Input shape of the model e.g [(1,3,224,224)] + in_types : data type or list of data types + Input data type e.g. np.float32, or [np.float32, np.int32] + onnx_file_path : str + Path where to save the generated onnx file + verbose : Boolean + If True will print logs of the model conversion + dynamic: Boolean + If True will allow for dynamic input shapes to the model + dynamic_input_shapes: list of tuple + Specifies the dynamic input_shapes. If None then all dimensions are set to None + run_shape_inference : Boolean + If True will run shape inference on the model + input_type : data type or list of data types + This is the old name of in_types. We keep this parameter name for backward compatibility + in_shapes : List of tuple + This is the old name of in_shapes. We keep this parameter name for backward compatibility + + Returns + ------- + onnx_file_path : str + Onnx file path + + Notes + ----- + This method is available when you ``import mxnet.onnx`` + + """ + try: + from onnx import helper, mapping, shape_inference + 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 isinstance(sym, symbol.Symbol) and isinstance(params, dict): + # Merging arg and aux parameters + params = {} + params.update(arg_params) + params.update(aux_params) + export_model(sym, params, in_shapes=in_shapes, in_types=in_types, + onnx_file_path=onnx_file_path, verbose=verbose, dynamic=dynamic, + dynamic_input_shapes=dynamic_input_shapes, run_shape_inference=run_shape_inference, input_type=input_type, + input_shape=input_shape): + else: + raise ValueError("Input sym should be symbol object, arg_params and aux_params should be dict objects") \ No newline at end of file From 390762103632a82ba855512bf792eac1bc6260b5 Mon Sep 17 00:00:00 2001 From: Wei Chu Date: Wed, 21 Apr 2021 11:22:31 -0700 Subject: [PATCH 2/8] return --- python/mxnet/onnx/mx2onnx/_export_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/mxnet/onnx/mx2onnx/_export_model.py b/python/mxnet/onnx/mx2onnx/_export_model.py index d00814e72034..c1afac475392 100644 --- a/python/mxnet/onnx/mx2onnx/_export_model.py +++ b/python/mxnet/onnx/mx2onnx/_export_model.py @@ -207,7 +207,7 @@ def export_model(sym, arg_params, aux_params, in_shapes=None, in_types=np.float3 params = {} params.update(arg_params) params.update(aux_params) - export_model(sym, params, in_shapes=in_shapes, in_types=in_types, + return export_model(sym, params, in_shapes=in_shapes, in_types=in_types, onnx_file_path=onnx_file_path, verbose=verbose, dynamic=dynamic, dynamic_input_shapes=dynamic_input_shapes, run_shape_inference=run_shape_inference, input_type=input_type, input_shape=input_shape): From e0b25950bbbf420f8ab7cb64c2af361074fa0248 Mon Sep 17 00:00:00 2001 From: Wei Chu Date: Wed, 21 Apr 2021 11:37:15 -0700 Subject: [PATCH 3/8] add test --- .../python-pytest/onnx/test_onnxruntime_cv.py | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/python-pytest/onnx/test_onnxruntime_cv.py b/tests/python-pytest/onnx/test_onnxruntime_cv.py index e0e24cf2baf5..5770a315ee28 100644 --- a/tests/python-pytest/onnx/test_onnxruntime_cv.py +++ b/tests/python-pytest/onnx/test_onnxruntime_cv.py @@ -61,6 +61,30 @@ def export_onnx_dynamic(self, dynamic_input_shapes): dynamic_input_shapes=dynamic_input_shapes) return onnx_file + def export_onnx_argaux(self): + onnx_file = self.modelpath + ".onnx" + sym_file = self.modelpath + "-symbol.json" + params_file = self.modelpath + "-0000.params" + if not (os.path.isfile(sym_file) and os.path.isfile(params_file)): + raise ValueError("Symbol and params files provided are invalid") + + try: + # reads symbol.json file from given path and + # retrieves model prefix and number of epochs + model_name = sym_file.rsplit('.', 1)[0].rsplit('-', 1)[0] + params_file_list = params_file.rsplit('.', 1)[0].rsplit('-', 1) + # Setting num_epochs to 0 if not present in filename + num_epochs = 0 if len(params_file_list) == 1 else int(params_file_list[1]) + except IndexError: + logging.info("Model and params name should be in format: " + "prefix-symbol.json, prefix-epoch.params") + raise + + sym, arg_params, aux_params = mx.model.load_checkpoint(model_name, num_epochs) + mx.onnx.export_model(sym, arg_params, aux_params, + [self.input_shape], self.input_dtype, onnx_file) + return onnx_file + def predict(self, data): return self.model(data) @@ -176,7 +200,11 @@ def normalize_image(imgfile): try: tmp_path = str(tmp_path) M = GluonModel(model, (1,3,inlen,inlen), 'float32', tmp_path) - onnx_file = M.export_onnx() + if model == 'resnet152_v2': + # testing export for arg/aux + onnx_file = M.export_onnx_argaux() + else: + onnx_file = M.export_onnx() # create onnxruntime session using the generated onnx file ses_opt = onnxruntime.SessionOptions() From 71bba1aa2139957f7a2581cd8f27cb77c454762e Mon Sep 17 00:00:00 2001 From: Wei Chu Date: Wed, 21 Apr 2021 11:54:28 -0700 Subject: [PATCH 4/8] fix typo --- python/mxnet/onnx/mx2onnx/_export_model.py | 2 +- tests/python-pytest/onnx/test_onnxruntime_cv.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/mxnet/onnx/mx2onnx/_export_model.py b/python/mxnet/onnx/mx2onnx/_export_model.py index c1afac475392..3ffc2e2d14d6 100644 --- a/python/mxnet/onnx/mx2onnx/_export_model.py +++ b/python/mxnet/onnx/mx2onnx/_export_model.py @@ -210,6 +210,6 @@ def export_model(sym, arg_params, aux_params, in_shapes=None, in_types=np.float3 return export_model(sym, params, in_shapes=in_shapes, in_types=in_types, onnx_file_path=onnx_file_path, verbose=verbose, dynamic=dynamic, dynamic_input_shapes=dynamic_input_shapes, run_shape_inference=run_shape_inference, input_type=input_type, - input_shape=input_shape): + input_shape=input_shape) else: raise ValueError("Input sym should be symbol object, arg_params and aux_params should be dict objects") \ No newline at end of file diff --git a/tests/python-pytest/onnx/test_onnxruntime_cv.py b/tests/python-pytest/onnx/test_onnxruntime_cv.py index 5770a315ee28..d7e5518ed8f8 100644 --- a/tests/python-pytest/onnx/test_onnxruntime_cv.py +++ b/tests/python-pytest/onnx/test_onnxruntime_cv.py @@ -200,7 +200,7 @@ def normalize_image(imgfile): try: tmp_path = str(tmp_path) M = GluonModel(model, (1,3,inlen,inlen), 'float32', tmp_path) - if model == 'resnet152_v2': + if model == 'resnet50_v2': # testing export for arg/aux onnx_file = M.export_onnx_argaux() else: From 0492d160e430a6211adfbbe91ddba2b551ea0100 Mon Sep 17 00:00:00 2001 From: Wei Chu Date: Wed, 21 Apr 2021 13:09:01 -0700 Subject: [PATCH 5/8] fix spaces --- python/mxnet/onnx/mx2onnx/_export_model.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/python/mxnet/onnx/mx2onnx/_export_model.py b/python/mxnet/onnx/mx2onnx/_export_model.py index 3ffc2e2d14d6..99e75621dca8 100644 --- a/python/mxnet/onnx/mx2onnx/_export_model.py +++ b/python/mxnet/onnx/mx2onnx/_export_model.py @@ -154,7 +154,7 @@ def export_model(sym, arg_params, aux_params, in_shapes=None, in_types=np.float3 dynamic_input_shapes=None, run_shape_inference=False, input_type=None, input_shape=None): """Exports the MXNet model file, passed as a parameter, into ONNX model. - Accepts symbol, arg_params and aux_params. + Accepts symbol, arg_params and aux_params. Operator support and coverage - https://github.com/apache/incubator-mxnet/tree/v1.x/python/mxnet/onnx#operator-support-matrix @@ -195,21 +195,15 @@ def export_model(sym, arg_params, aux_params, in_shapes=None, in_types=np.float3 This method is available when you ``import mxnet.onnx`` """ - try: - from onnx import helper, mapping, shape_inference - 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 isinstance(sym, symbol.Symbol) and isinstance(params, dict): + if isinstance(sym, symbol.Symbol) and isinstance(arg_params, dict) and isinstance(aux_params, dict): # Merging arg and aux parameters params = {} params.update(arg_params) params.update(aux_params) return export_model(sym, params, in_shapes=in_shapes, in_types=in_types, - onnx_file_path=onnx_file_path, verbose=verbose, dynamic=dynamic, - dynamic_input_shapes=dynamic_input_shapes, run_shape_inference=run_shape_inference, input_type=input_type, - input_shape=input_shape) + onnx_file_path=onnx_file_path, verbose=verbose, dynamic=dynamic, + dynamic_input_shapes=dynamic_input_shapes, + run_shape_inference=run_shape_inference, input_type=input_type, + input_shape=input_shape) else: - raise ValueError("Input sym should be symbol object, arg_params and aux_params should be dict objects") \ No newline at end of file + raise ValueError("Input sym should be symbol object, arg_params and aux_params should be dict objects") From f93450d87a419260e1567f50bff7600f8a1bd630 Mon Sep 17 00:00:00 2001 From: Wei Chu Date: Wed, 21 Apr 2021 13:30:27 -0700 Subject: [PATCH 6/8] revert overloding --- python/mxnet/onnx/mx2onnx/_export_model.py | 76 ++++------------------ 1 file changed, 14 insertions(+), 62 deletions(-) diff --git a/python/mxnet/onnx/mx2onnx/_export_model.py b/python/mxnet/onnx/mx2onnx/_export_model.py index 99e75621dca8..093713e3c51d 100644 --- a/python/mxnet/onnx/mx2onnx/_export_model.py +++ b/python/mxnet/onnx/mx2onnx/_export_model.py @@ -61,8 +61,10 @@ def export_model(sym, params, in_shapes=None, in_types=np.float32, ---------- sym : str or symbol object Path to the json file or Symbol object - params : str or symbol object - Path to the params file or params dictionary. (Including both arg_params and aux_params) + params : str or dict or list of dict + str - Path to the params file + dict - params dictionary (Including both arg_params and aux_params) + list - list of length 2 that contains arg_params and aux_params in_shapes : List of tuple Input shape of the model e.g [(1,3,224,224)] in_types : data type or list of data types @@ -126,6 +128,16 @@ def export_model(sym, params, in_shapes=None, in_types=np.float32, in_types_t, verbose=verbose, opset_version=opset_version, dynamic=dynamic, dynamic_input_shapes=dynamic_input_shapes) + elif isinstance(sym, symbol.Symbol) and isinstance(params, list) and len(params) == 2 + and isinstance(params[0], dict) and isinstance(params[1], dict): + # when params contains arg_params and aux_params + p = {} + p.update(params[0]) + p.update(params[1]) + onnx_graph = converter.create_onnx_graph_proto(sym, p, in_shapes, + in_types_t, + verbose=verbose, opset_version=opset_version, + dynamic=dynamic, dynamic_input_shapes=dynamic_input_shapes) else: raise ValueError("Input sym and params should either be files or objects") @@ -147,63 +159,3 @@ def export_model(sym, params, in_shapes=None, in_types=np.float32, logging.info("Exported ONNX file %s saved to disk", onnx_file_path) return onnx_file_path - - -def export_model(sym, arg_params, aux_params, in_shapes=None, in_types=np.float32, - onnx_file_path='model.onnx', verbose=False, dynamic=False, - dynamic_input_shapes=None, run_shape_inference=False, input_type=None, - input_shape=None): - """Exports the MXNet model file, passed as a parameter, into ONNX model. - Accepts symbol, arg_params and aux_params. - Operator support and coverage - - https://github.com/apache/incubator-mxnet/tree/v1.x/python/mxnet/onnx#operator-support-matrix - - Parameters - ---------- - sym : symbol object - Symbol object - arg_params : dict of str to NDArray - Model parameter, dict of name to NDArray of net's weights. - aux_params : dict of str to NDArray - Model parameter, dict of name to NDArray of net's auxiliary states. - in_shapes : List of tuple - Input shape of the model e.g [(1,3,224,224)] - in_types : data type or list of data types - Input data type e.g. np.float32, or [np.float32, np.int32] - onnx_file_path : str - Path where to save the generated onnx file - verbose : Boolean - If True will print logs of the model conversion - dynamic: Boolean - If True will allow for dynamic input shapes to the model - dynamic_input_shapes: list of tuple - Specifies the dynamic input_shapes. If None then all dimensions are set to None - run_shape_inference : Boolean - If True will run shape inference on the model - input_type : data type or list of data types - This is the old name of in_types. We keep this parameter name for backward compatibility - in_shapes : List of tuple - This is the old name of in_shapes. We keep this parameter name for backward compatibility - - Returns - ------- - onnx_file_path : str - Onnx file path - - Notes - ----- - This method is available when you ``import mxnet.onnx`` - - """ - if isinstance(sym, symbol.Symbol) and isinstance(arg_params, dict) and isinstance(aux_params, dict): - # Merging arg and aux parameters - params = {} - params.update(arg_params) - params.update(aux_params) - return export_model(sym, params, in_shapes=in_shapes, in_types=in_types, - onnx_file_path=onnx_file_path, verbose=verbose, dynamic=dynamic, - dynamic_input_shapes=dynamic_input_shapes, - run_shape_inference=run_shape_inference, input_type=input_type, - input_shape=input_shape) - else: - raise ValueError("Input sym should be symbol object, arg_params and aux_params should be dict objects") From d5bfffd9ecb120bca30725ae06131c323b63c3cd Mon Sep 17 00:00:00 2001 From: Wei Chu Date: Wed, 21 Apr 2021 13:37:35 -0700 Subject: [PATCH 7/8] fix sanity --- python/mxnet/onnx/mx2onnx/_export_model.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/mxnet/onnx/mx2onnx/_export_model.py b/python/mxnet/onnx/mx2onnx/_export_model.py index 093713e3c51d..ad33c2aec7c8 100644 --- a/python/mxnet/onnx/mx2onnx/_export_model.py +++ b/python/mxnet/onnx/mx2onnx/_export_model.py @@ -128,8 +128,7 @@ def export_model(sym, params, in_shapes=None, in_types=np.float32, in_types_t, verbose=verbose, opset_version=opset_version, dynamic=dynamic, dynamic_input_shapes=dynamic_input_shapes) - elif isinstance(sym, symbol.Symbol) and isinstance(params, list) and len(params) == 2 - and isinstance(params[0], dict) and isinstance(params[1], dict): + elif isinstance(sym, symbol.Symbol) and isinstance(params, list) and len(params) == 2: # when params contains arg_params and aux_params p = {} p.update(params[0]) From 0e72acfdc037fe57f371de6c20e9d9f759fd1767 Mon Sep 17 00:00:00 2001 From: Wei Chu Date: Wed, 21 Apr 2021 14:51:32 -0700 Subject: [PATCH 8/8] fix export_argaux --- tests/python-pytest/onnx/test_onnxruntime_cv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/python-pytest/onnx/test_onnxruntime_cv.py b/tests/python-pytest/onnx/test_onnxruntime_cv.py index d7e5518ed8f8..f6cf632833c0 100644 --- a/tests/python-pytest/onnx/test_onnxruntime_cv.py +++ b/tests/python-pytest/onnx/test_onnxruntime_cv.py @@ -81,8 +81,8 @@ def export_onnx_argaux(self): raise sym, arg_params, aux_params = mx.model.load_checkpoint(model_name, num_epochs) - mx.onnx.export_model(sym, arg_params, aux_params, - [self.input_shape], self.input_dtype, onnx_file) + params = [arg_params, aux_params] + mx.onnx.export_model(sym, params, [self.input_shape], self.input_dtype, onnx_file) return onnx_file def predict(self, data):