This is an automated email from the ASF dual-hosted git repository. skm pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git
The following commit(s) were added to refs/heads/master by this push: new 77510d7 ONNX export: Instance normalization, Shape (#12920) 77510d7 is described below commit 77510d7b37a5da80bd43b3f1c21a39a52163dae8 Author: Vandana Kannan <vandan...@users.noreply.github.com> AuthorDate: Fri Nov 30 23:05:51 2018 -0800 ONNX export: Instance normalization, Shape (#12920) * ONNX import/export: Make backend_rep common * ONNX export: Instance Normalization * ONNX export: Shape operator --- .../mxnet/contrib/onnx/mx2onnx/_op_translations.py | 26 ++++++ .../python-pytest/onnx/{export => }/backend_rep.py | 32 +++---- tests/python-pytest/onnx/export/backend.py | 4 + .../python-pytest/onnx/export/onnx_backend_test.py | 4 +- tests/python-pytest/onnx/import/mxnet_backend.py | 6 +- .../python-pytest/onnx/import/mxnet_backend_rep.py | 98 ---------------------- 6 files changed, 54 insertions(+), 116 deletions(-) diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py index e2aab6b..facdcfe 100644 --- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py +++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py @@ -623,6 +623,23 @@ def convert_identity(node, **kwargs): """ return create_basic_op_node('Identity', node, kwargs) +@mx_op.register("InstanceNorm") +def convert_instancenorm(node, **kwargs): + """Map MXNet's InstanceNorm operator attributes to onnx's InstanceNormalization operator + based on the input node's attributes and return the created node. + """ + name, input_nodes, attrs = get_inputs(node, kwargs) + + eps = float(attrs.get("eps", 0.001)) + + node = onnx.helper.make_node( + 'InstanceNormalization', + inputs=input_nodes, + outputs=[name], + name=name, + epsilon=eps) + + return [node] @mx_op.register("LeakyReLU") def convert_leakyrelu(node, **kwargs): @@ -1546,6 +1563,15 @@ def convert_sum(node, **kwargs): ) return [node] + +@mx_op.register("shape_array") +def convert_shape(node, **kwargs): + """Map MXNet's shape_array operator attributes to onnx's Shape operator + and return the created node. + """ + return create_basic_op_node('Shape', node, kwargs) + + @mx_op.register("hard_sigmoid") def convert_hardsigmoid(node, **kwargs): """Map MXNet's hard_sigmoid operator attributes to onnx's HardSigmoid operator diff --git a/tests/python-pytest/onnx/export/backend_rep.py b/tests/python-pytest/onnx/backend_rep.py similarity index 78% rename from tests/python-pytest/onnx/export/backend_rep.py rename to tests/python-pytest/onnx/backend_rep.py index 8729eaf..63836ac 100644 --- a/tests/python-pytest/onnx/export/backend_rep.py +++ b/tests/python-pytest/onnx/backend_rep.py @@ -16,16 +16,17 @@ # under the License. # coding: utf-8 -"""backend rep for onnx test infrastructure""" +"""MXNet backend rep for onnx test infrastructure""" try: from onnx.backend.base import BackendRep except ImportError: - raise ImportError("Onnx and protobuf need to be installed") + raise ImportError("Onnx and protobuf need to be installed. Instructions to" + + " install - https://github.com/onnx/onnx#installation") import mxnet as mx # Using these functions for onnx test infrastructure. # Implemented by following onnx docs guide: -# https://github.com/onnx/onnx/blob/master/docs/Implementing%20an%20ONNX%20backend.md +# https://github.com/onnx/onnx/blob/master/docs/ImplementingAnOnnxBackend.md # MXNetBackendRep object will be returned by MXNetBackend's prepare method which is used to # execute a model repeatedly. # Inputs will be passed to the run method of MXNetBackendRep class, it will perform computation and @@ -54,9 +55,6 @@ class MXNetBackendRep(BackendRep): params : numpy array result obtained after running the inference on mxnet """ - data_forward = [] - for val in inputs: - data_forward.append(mx.nd.array(val)) # create module, passing cpu context if self.device == 'CPU': ctx = mx.cpu() @@ -68,17 +66,19 @@ class MXNetBackendRep(BackendRep): data_names = [graph_input for graph_input in self.symbol.list_inputs() if graph_input not in self.arg_params and graph_input not in self.aux_params] - data_shapes = [] + data_forward = [] for idx, input_name in enumerate(data_names): - data_shapes.append((input_name, inputs[idx].shape)) + val = inputs[idx] + data_forward.append(mx.nd.array(val)) - mod = mx.mod.Module(symbol=self.symbol, data_names=data_names, context=ctx, - label_names=None) - mod.bind(for_training=False, data_shapes=data_shapes, - label_shapes=None) - mod.set_params(arg_params=self.arg_params, aux_params=self.aux_params) + if self.arg_params: + for idx, input_name in enumerate(self.arg_params): + val = self.arg_params[input_name] + data_names.append(input_name) + data_forward.append(mx.nd.array(val)) - # run inference - mod.forward(mx.io.DataBatch(data_forward)) - result = mod.get_outputs()[0].asnumpy() + args = dict(zip(data_names, data_forward)) + exe = self.symbol.bind(ctx, args=args, aux_states=self.aux_params) + exe.forward(is_train=False) + result = exe.outputs[0].asnumpy() return [result] diff --git a/tests/python-pytest/onnx/export/backend.py b/tests/python-pytest/onnx/export/backend.py index e23cc01..3ea1daf 100644 --- a/tests/python-pytest/onnx/export/backend.py +++ b/tests/python-pytest/onnx/export/backend.py @@ -17,6 +17,8 @@ # coding: utf-8 """backend wrapper for onnx test infrastructure""" +import os +import sys import numpy as np from mxnet.contrib.onnx.onnx2mx.import_onnx import GraphProto from mxnet.contrib.onnx.mx2onnx.export_onnx import MXNetGraph @@ -25,6 +27,8 @@ try: from onnx.backend.base import Backend except ImportError: raise ImportError("Onnx and protobuf need to be installed") +CURR_PATH = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) +sys.path.insert(0, os.path.join(CURR_PATH, '../')) from backend_rep import MXNetBackendRep # Using these functions for onnx test infrastructure. diff --git a/tests/python-pytest/onnx/export/onnx_backend_test.py b/tests/python-pytest/onnx/export/onnx_backend_test.py index ec9ddf2..be9273e 100644 --- a/tests/python-pytest/onnx/export/onnx_backend_test.py +++ b/tests/python-pytest/onnx/export/onnx_backend_test.py @@ -95,7 +95,9 @@ IMPLEMENTED_OPERATORS_TEST = [ 'test_clip' 'test_cast', 'test_depthtospace', - 'test_hardsigmoid' + 'test_hardsigmoid', + 'test_instancenorm', + 'test_shape' ] BASIC_MODEL_TESTS = [ diff --git a/tests/python-pytest/onnx/import/mxnet_backend.py b/tests/python-pytest/onnx/import/mxnet_backend.py index 10f89ec..bd4910b 100644 --- a/tests/python-pytest/onnx/import/mxnet_backend.py +++ b/tests/python-pytest/onnx/import/mxnet_backend.py @@ -17,6 +17,8 @@ # coding: utf-8 """MXNet backend wrapper for onnx test infrastructure""" +import os +import sys from mxnet.contrib.onnx.onnx2mx.import_onnx import GraphProto try: from onnx import helper, TensorProto @@ -24,7 +26,9 @@ try: except ImportError: raise ImportError("Onnx and protobuf need to be installed. Instructions to" + " install - https://github.com/onnx/onnx#installation") -from mxnet_backend_rep import MXNetBackendRep +CURR_PATH = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) +sys.path.insert(0, os.path.join(CURR_PATH, '../')) +from backend_rep import MXNetBackendRep # MXNetBackend class will take an ONNX model with inputs, perform a computation, # and then return the output. diff --git a/tests/python-pytest/onnx/import/mxnet_backend_rep.py b/tests/python-pytest/onnx/import/mxnet_backend_rep.py deleted file mode 100644 index 938f25d..0000000 --- a/tests/python-pytest/onnx/import/mxnet_backend_rep.py +++ /dev/null @@ -1,98 +0,0 @@ -# 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. - -# coding: utf-8 -"""MXNet backend rep for onnx test infrastructure""" -try: - from onnx.backend.base import BackendRep -except ImportError: - raise ImportError("Onnx and protobuf need to be installed. Instructions to" - + " install - https://github.com/onnx/onnx#installation") -import mxnet as mx - -# Using these functions for onnx test infrastructure. -# Implemented by following onnx docs guide: -# https://github.com/onnx/onnx/blob/master/docs/ImplementingAnOnnxBackend.md -# MXNetBackendRep object will be returned by MXNetBackend's prepare method which is used to -# execute a model repeatedly. -# Inputs will be passed to the run method of MXNetBackendRep class, it will perform computation and -# retrieve the corresponding results for comparison to the onnx backend. -# https://github.com/onnx/onnx/blob/master/onnx/backend/test/runner/__init__.py. - -class MXNetBackendRep(BackendRep): - """Running model inference on mxnet engine and return the result - to onnx test infrastructure for comparison.""" - def __init__(self, symbol, arg_params, aux_params, device): - self.symbol = symbol - self.arg_params = arg_params - self.aux_params = aux_params - self.device = device - - def run(self, inputs, **kwargs): - """Run model inference and return the result - - Parameters - ---------- - inputs : numpy array - input to run a layer on - - Returns - ------- - params : numpy array - result obtained after running the inference on mxnet - """ - data_forward = [] - for val in inputs: - data_forward.append(mx.nd.array(val)) - # create module, passing cpu context - if self.device == 'CPU': - ctx = mx.cpu() - else: - raise NotImplementedError("ONNX tests are run only for CPU context.") - - # To fetch the data names of the input to the model we list the inputs of the symbol graph - # and exclude the argument and auxiliary parameters from the list - data_names = [graph_input for graph_input in self.symbol.list_inputs() - if graph_input not in self.arg_params and graph_input not in self.aux_params] - - data_shapes = [] - for idx, input_name in enumerate(data_names): - data_shapes.append((input_name, inputs[idx].shape)) - - # module bind method requires all data to have same batch size, - # using module if all data have same batch size - if len(set([data_shape[1][0] for data_shape in data_shapes])) == 1: - mod = mx.mod.Module(symbol=self.symbol, data_names=data_names, context=ctx, - label_names=None) - mod.bind(for_training=False, data_shapes=data_shapes, - label_shapes=None) - mod.set_params(arg_params=self.arg_params, aux_params=self.aux_params) - - # run inference - mod.forward(mx.io.DataBatch(data_forward)) - result = mod.get_outputs()[0].asnumpy() - # split operator inference returns 1 less dimension - if self.symbol.name.startswith('split'): - return [i.asnumpy() for i in mod.get_outputs()] - return [result] - # using symbol bind method if data have different batch size - else: - exec1 = self.symbol.bind(ctx, args=dict(zip(data_names, data_forward))) - exec1.forward(is_train=False) - result = exec1.outputs[0].asnumpy() - return [result] -