You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by zh...@apache.org on 2020/12/16 06:14:13 UTC
[incubator-mxnet] branch v1.x updated: [v1.x] Make ONNX export
operators work properly with the node input shape (#19676)
This is an automated email from the ASF dual-hosted git repository.
zha0q1 pushed a commit to branch v1.x
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git
The following commit(s) were added to refs/heads/v1.x by this push:
new 806d91f [v1.x] Make ONNX export operators work properly with the node input shape (#19676)
806d91f is described below
commit 806d91f20ea0df4948d0a54bd67f0330e22e7841
Author: Joe Evans <jo...@gmail.com>
AuthorDate: Tue Dec 15 22:12:45 2020 -0800
[v1.x] Make ONNX export operators work properly with the node input shape (#19676)
* Expand test for arange_like to test more inputs and parametrize axis.
* Fix zeros_like and contrib_arange_like onnx export functions to work properly.
* Update variable names.
* Fix lint.
* Parametrize start and step paramaters for arange_like onnx tests.
* Fix arange_like for non-default start and step values, assign name to output nodes.
Co-authored-by: Joe Evans <jo...@amazon.com>
---
.../mxnet/contrib/onnx/mx2onnx/_op_translations.py | 70 +++++++++++++---------
tests/python-pytest/onnx/test_operators.py | 10 +++-
2 files changed, 48 insertions(+), 32 deletions(-)
diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py
index 4b7c3a5..4225028 100644
--- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py
+++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py
@@ -64,7 +64,7 @@ except ImportError:
def parse_helper(attrs, attrs_name, alt_value=None):
"""Helper function to parse operator attributes in required format."""
- tuple_re = re.compile('\([0-9L|,| ]+\)')
+ tuple_re = re.compile(r'\([0-9L|,| ]+\)')
if not attrs:
return alt_value
attrs_str = None if attrs.get(attrs_name) is None else str(attrs.get(attrs_name))
@@ -187,7 +187,7 @@ def create_tensor(shape_list, shape_name, initializer, dtype='int64'):
data_type=data_type,
dims=dims,
vals=shape_list,
- raw=False,
+ raw=False
)
)
@@ -2543,13 +2543,13 @@ def convert_zeros_like(node, **kwargs):
"""Map MXNet's zeros_like operator attributes to onnx's ConstantOfShape operator.
"""
from onnx.helper import make_node, make_tensor
- name, _, _ = get_inputs(node, kwargs)
+ name, input_nodes, _ = get_inputs(node, kwargs)
# create tensor with shape of input
- create_const_node(name+"_shape", np.array(kwargs['in_shape'][0], dtype='int64'), kwargs)
tensor_value = make_tensor(name+"_zero", kwargs['in_type'], [1], [0])
nodes = [
- make_node("ConstantOfShape", [name+"_shape"], [name], value=tensor_value)
+ make_node("Shape", [input_nodes[0]], [name+"_shape"]),
+ make_node("ConstantOfShape", [name+"_shape"], [name], name=name, value=tensor_value)
]
return nodes
@@ -2559,7 +2559,7 @@ def convert_arange_like(node, **kwargs):
"""Map MXNet's arange_like operator attributes to onnx's Range and Reshape operators.
"""
from onnx.helper import make_node
- name, _, attrs = get_inputs(node, kwargs)
+ name, input_nodes, attrs = get_inputs(node, kwargs)
opset_version = kwargs['opset_version']
if opset_version < 11:
@@ -2567,32 +2567,44 @@ def convert_arange_like(node, **kwargs):
input_type = kwargs['in_type']
dtype = onnx.mapping.TENSOR_TYPE_TO_NP_TYPE[input_type]
- in_shape = kwargs['in_shape']
- axis = attrs.get('axis')
+ axis = attrs.get('axis', 'None')
+ start = attrs.get('start', 0.)
+ step = attrs.get('step', 1.)
+ repeat = int(attrs.get('repeat', 1))
+ if repeat != 1:
+ raise NotImplementedError("arange_like operator with repeat != 1 not yet implemented.")
- if axis is None:
+ create_const_scalar_node(name+"_start", np.array([start], dtype=dtype), kwargs)
+ create_const_scalar_node(name+"_step", np.array([step], dtype=dtype), kwargs)
+ create_const_scalar_node(name+"_half_step", np.array([float(step)*0.5], dtype=dtype), kwargs)
+ create_tensor([], name+'_void', kwargs["initializer"])
+ if axis == 'None':
# output will be same shape as input
- output_shape = in_shape[0]
+ nodes = [
+ make_node('Shape', [input_nodes[0]], [name+"_shape0_out"]),
+ make_node("ReduceProd", [name+"_shape0_out"], [name+"_redprod0_out"]),
+ make_node('Reshape', [name+'_redprod0_out', name+'_void'], [name+'_reshape0_out']),
+ make_node("Cast", [name+"_reshape0_out"], [name+"_cast0_out"], to=input_type),
+ make_node("Mul", [name+"_cast0_out", name+"_step"], [name+"_mul0_out"]),
+ make_node("Add", [name+"_mul0_out", name+"_start"], [name+"_add1_out"]),
+ make_node("Sub", [name+"_add1_out", name+"_half_step"], [name+"_sub0_out"]),
+ make_node("Range", [name+"_start", name+"_sub0_out", name+"_step"], [name+"_range0_out"]),
+ make_node("Reshape", [name+"_range0_out", name+"_shape0_out"], [name], name=name)
+ ]
else:
# determine shape of axis
- output_shape = [in_shape[0][int(axis)]]
-
- start = np.array([attrs.get('start', 0.)], dtype=dtype)
- step = np.array([attrs.get('step', 1.)], dtype=dtype)
- repeat = np.array([attrs.get('repeat', 1)], dtype=dtype)
- if repeat != 1:
- raise NotImplementedError("arange_like operator with repeat != 1 not yet implemented.")
-
- tot_elements = np.prod(output_shape)
- limit = np.array([start + (tot_elements * step)], dtype=dtype)
+ create_tensor([int(axis)], name+"_axis_start", kwargs["initializer"], dtype='int64')
+ create_tensor([int(axis)+1], name+"_axis_end", kwargs["initializer"], dtype='int64')
+ nodes = [
+ make_node('Shape', [input_nodes[0]], [name+"_shape0_out"]),
+ make_node('Slice', [name+"_shape0_out", name+"_axis_start", name+"_axis_end"], [name+"_slice0_out"]),
+ make_node("ReduceProd", [name+"_slice0_out"], [name+"_reprod0_out"]),
+ make_node('Reshape', [name+'_reprod0_out', name+'_void'], [name+'_reshape0_out']),
+ make_node("Cast", [name+"_reshape0_out"], [name+"_cast0_out"], to=input_type),
+ make_node("Mul", [name+"_cast0_out", name+"_step"], [name+"_mul0_out"]),
+ make_node("Add", [name+"_mul0_out", name+"_start"], [name+"_add1_out"]),
+ make_node("Sub", [name+"_add1_out", name+"_half_step"], [name+"_sub0_out"]),
+ make_node("Range", [name+"_start", name+"_sub0_out", name+"_step"], [name], name=name)
+ ]
- # create constant inputs
- nodes = [
- create_const_scalar_node(name+"_start", start, kwargs),
- create_const_scalar_node(name+"_limit", limit, kwargs),
- create_const_scalar_node(name+"_step", step, kwargs),
- create_const_node(name+"_shape", np.array(output_shape, dtype='int64'), kwargs),
- make_node("Range", [name+"_start", name+"_limit", name+"_step"], [name+"_range0_out"]),
- make_node("Reshape", [name+"_range0_out", name+"_shape"], [name])
- ]
return nodes
diff --git a/tests/python-pytest/onnx/test_operators.py b/tests/python-pytest/onnx/test_operators.py
index 169fc2e..c537ee3 100644
--- a/tests/python-pytest/onnx/test_operators.py
+++ b/tests/python-pytest/onnx/test_operators.py
@@ -91,9 +91,13 @@ def test_onnx_export_zeros_like(tmp_path):
@pytest.mark.parametrize("dtype", ["float32", "float64"])
-def test_onnx_export_arange_like(tmp_path, dtype):
- M = def_model('contrib.arange_like')
- x = mx.nd.array([[-2,-1,0],[0,50,99],[4,5,6],[7,8,9]], dtype=dtype)
+@pytest.mark.parametrize("axis", [None,0,1])
+@pytest.mark.parametrize("start", [0, 0.5, 1])
+@pytest.mark.parametrize("step", [0.01, 0.1, 0.5, 1])
+@pytest.mark.parametrize("test_data", [ mx.random.uniform(0, 1, (10,20)), [[0,1,2,3,4,5],[4,5,6,7,8,9],[8,9,10,11,12,13]]])
+def test_onnx_export_arange_like(tmp_path, dtype, axis, start, step, test_data):
+ M = def_model('contrib.arange_like', axis=axis, start=start, step=step)
+ x = mx.nd.array(test_data, dtype=dtype)
op_export_test('arange_like', M, [x], tmp_path)