From 0e80fa1c386e8361035a12d1d1dc36eedc35b684 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 12 Jul 2021 22:23:45 +0000 Subject: [PATCH 1/5] [Relay to Onnx] * Added support for resize2d op * Added unit test --- python/tvm/contrib/target/onnx.py | 81 +++++++++++++++++++++++++++++++ tests/python/contrib/test_onnx.py | 20 ++++++++ 2 files changed, 101 insertions(+) diff --git a/python/tvm/contrib/target/onnx.py b/python/tvm/contrib/target/onnx.py index e442c806a2f3..1a2d60f25386 100644 --- a/python/tvm/contrib/target/onnx.py +++ b/python/tvm/contrib/target/onnx.py @@ -662,6 +662,86 @@ def convert_attributes(cls, attrs): return {"to": getattr(TensorProto, attrs.dtype.upper())} +class Resize(OpConverter): + """Operator converter for Resize.""" + + @classmethod + def convert_attributes(cls, attrs): + method = attrs.get_str("method") + if method == "nearest_neighbor": + mode = b"nearest" + elif "linear" in method: # linear / bilinear + mode = b"linear" + elif "cubic" in method: # cubic / bicubic + mode = b"cubic" + + coord_trans = attrs.get_str("coordinate_transformation_mode") + if coord_trans == "half_pixel": + coord_trans = b"half_pixel" + elif coord_trans == "align_corners": + coord_trans = b"align_corners" + elif coord_trans == "asymmetric": + coord_trans = b"asymmetric" + + rounding_method = attrs.get_str("rounding_method") + if rounding_method == "round": + rounding_method = b"round_prefer_floor" + elif rounding_method == "floor": + rounding_method = b"floor" + elif rounding_method == "ceil": + rounding_method = b"ceil" + + size = attrs.get_int_tuple("size") + + return { + "mode": mode, + "coord_trans": coord_trans, + "size": size, + "nearest_mode": rounding_method, + } + + @classmethod + def convert(cls, node_entry, model_container, node_dict): + attrs = cls.convert_attributes(node_entry["relay_node"].attrs) + + name = node_entry["name"] + input_node = node_dict[node_entry["inputs"][0]] + assert len(input_node) == 1, "input node can not be a Tuple" + input_node = input_node[0] + input_shape = input_node["types"][0].shape + + # (TBD) needed in opset 11 + roi = [0]*len(input_shape) + [1]*len(input_shape) + roi_array = numpy.asarray(roi).astype(numpy.float64) + roi_node = add_input(roi_array, name, "roi", model_container) + + out_size = attrs["size"] + + # (onnx) rank of scale / size must match rank of X + # relay size node contains only spatial dimensions + # pad with 1s to match rank + match_rank_pad = len(input_shape) - len(out_size) + out_size_full_rank = input_shape[:match_rank_pad] + list(out_size) + out_size_array = numpy.asarray(out_size_full_rank).astype(numpy.int64) + + input_size_array = numpy.asarray(list(input_shape)).astype(numpy.int64) + + scale_array = numpy.divide(out_size_array, input_size_array).astype(numpy.float32) + scale_node = add_input(scale_array, name, "scales", model_container) + + input_names = [node_entry["input_names"][0], roi_node, scale_node] + + resize_node = onnx.helper.make_node( + cls.__name__, + input_names, + node_entry["output_names"], + mode=attrs["mode"], + coordinate_transformation_mode=attrs["coord_trans"], + nearest_mode=attrs["nearest_mode"], + ) + model_container.add_nodes([resize_node]) + + relay_to_onnx_op_mapping = { "reshape": Reshape, "nn.conv2d": Conv, @@ -701,6 +781,7 @@ def convert_attributes(cls, attrs): "copy": rename("Identity"), "round": rename("Round"), "cast": Cast, + "image.resize2d": Resize, } diff --git a/tests/python/contrib/test_onnx.py b/tests/python/contrib/test_onnx.py index 8567f2c814cf..e32c0f21a1cc 100644 --- a/tests/python/contrib/test_onnx.py +++ b/tests/python/contrib/test_onnx.py @@ -655,6 +655,25 @@ def verify_cast(dshape, dtype): verify_cast(i, o_dtype) +def test_resize(): + """Resize unit test.""" + + def verify_resize(dshape, outsize, method=None, dtype="float32"): + x = relay.var("x", relay.ty.TensorType(dshape, dtype)) + y = relay.image.resize2d(x, outsize, layout="NCHW", method=method) + func = relay.Function([x], y) + x_data = np.random.uniform(size=dshape).astype(dtype) + verify_results(func, [x_data], "test_resize", rtol=1e-4, atol=1e-4) + + isize = [(1,3,480,640)] + osize = [(240,320), (960,1280)] + method = ['nearest_neighbor', 'linear', 'cubic'] + + for i in isize: + for j in osize: + for k in method: + verify_resize(i, j, k) + if __name__ == "__main__": test_add() test_bias_add() @@ -684,3 +703,4 @@ def verify_cast(dshape, dtype): test_copy() test_round() test_cast() + test_resize() From 43324c3fbdadd643777cf0ec86346ae63152f81a Mon Sep 17 00:00:00 2001 From: root Date: Mon, 12 Jul 2021 22:46:40 +0000 Subject: [PATCH 2/5] [Relay to Onnx][Resize] * Fixed formatting errors --- python/tvm/contrib/target/onnx.py | 6 +++--- tests/python/contrib/test_onnx.py | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/python/tvm/contrib/target/onnx.py b/python/tvm/contrib/target/onnx.py index 1a2d60f25386..256283c59309 100644 --- a/python/tvm/contrib/target/onnx.py +++ b/python/tvm/contrib/target/onnx.py @@ -670,9 +670,9 @@ def convert_attributes(cls, attrs): method = attrs.get_str("method") if method == "nearest_neighbor": mode = b"nearest" - elif "linear" in method: # linear / bilinear + elif "linear" in method: # linear / bilinear mode = b"linear" - elif "cubic" in method: # cubic / bicubic + elif "cubic" in method: # cubic / bicubic mode = b"cubic" coord_trans = attrs.get_str("coordinate_transformation_mode") @@ -711,7 +711,7 @@ def convert(cls, node_entry, model_container, node_dict): input_shape = input_node["types"][0].shape # (TBD) needed in opset 11 - roi = [0]*len(input_shape) + [1]*len(input_shape) + roi = [0] * len(input_shape) + [1] * len(input_shape) roi_array = numpy.asarray(roi).astype(numpy.float64) roi_node = add_input(roi_array, name, "roi", model_container) diff --git a/tests/python/contrib/test_onnx.py b/tests/python/contrib/test_onnx.py index e32c0f21a1cc..d05b48cc7ad4 100644 --- a/tests/python/contrib/test_onnx.py +++ b/tests/python/contrib/test_onnx.py @@ -665,15 +665,16 @@ def verify_resize(dshape, outsize, method=None, dtype="float32"): x_data = np.random.uniform(size=dshape).astype(dtype) verify_results(func, [x_data], "test_resize", rtol=1e-4, atol=1e-4) - isize = [(1,3,480,640)] - osize = [(240,320), (960,1280)] - method = ['nearest_neighbor', 'linear', 'cubic'] + isize = [(1, 3, 480, 640)] + osize = [(240, 320), (960, 1280)] + method = ["nearest_neighbor", "linear", "cubic"] for i in isize: for j in osize: for k in method: verify_resize(i, j, k) + if __name__ == "__main__": test_add() test_bias_add() From 34ae7ab92d2a2a2c157ca585289bef5f0a522daa Mon Sep 17 00:00:00 2001 From: root Date: Tue, 13 Jul 2021 18:33:55 +0000 Subject: [PATCH 3/5] [Relay to Onnx][Resize] * Fixed issue in resize conversion: round maps to round_preferc_ceil * Updated resize unit test to test for coordinate transform mode and round * Known issue: Does not match for (NN, align_corners) and Cubic --- python/tvm/contrib/target/onnx.py | 13 +++++++++- tests/python/contrib/test_onnx.py | 40 +++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/python/tvm/contrib/target/onnx.py b/python/tvm/contrib/target/onnx.py index 256283c59309..400c4c2a6a50 100644 --- a/python/tvm/contrib/target/onnx.py +++ b/python/tvm/contrib/target/onnx.py @@ -674,6 +674,8 @@ def convert_attributes(cls, attrs): mode = b"linear" elif "cubic" in method: # cubic / bicubic mode = b"cubic" + else: + raise RuntimeError("Unsupported method %s in operator Resize" % method) coord_trans = attrs.get_str("coordinate_transformation_mode") if coord_trans == "half_pixel": @@ -682,14 +684,23 @@ def convert_attributes(cls, attrs): coord_trans = b"align_corners" elif coord_trans == "asymmetric": coord_trans = b"asymmetric" + else: + raise RuntimeError( + "Unsupported coordinate transform mode %s in operator Resize" % coord_trans + ) rounding_method = attrs.get_str("rounding_method") if rounding_method == "round": - rounding_method = b"round_prefer_floor" + rounding_method = b"round_prefer_ceil" + #rounding_method = b"round_prefer_floor" elif rounding_method == "floor": rounding_method = b"floor" elif rounding_method == "ceil": rounding_method = b"ceil" + else: + raise RuntimeError( + "Unsupported rounding method %s in operator Resize" % rounding_method + ) size = attrs.get_int_tuple("size") diff --git a/tests/python/contrib/test_onnx.py b/tests/python/contrib/test_onnx.py index d05b48cc7ad4..5d783ebdbcdd 100644 --- a/tests/python/contrib/test_onnx.py +++ b/tests/python/contrib/test_onnx.py @@ -658,22 +658,42 @@ def verify_cast(dshape, dtype): def test_resize(): """Resize unit test.""" - def verify_resize(dshape, outsize, method=None, dtype="float32"): + def verify_resize(dshape, outsize, method, coord_trans, rounding_method, dtype="float32"): x = relay.var("x", relay.ty.TensorType(dshape, dtype)) - y = relay.image.resize2d(x, outsize, layout="NCHW", method=method) + y = relay.image.resize2d( + x, + outsize, + layout="NCHW", + method=method, + coordinate_transformation_mode=coord_trans, + rounding_method=rounding_method) func = relay.Function([x], y) x_data = np.random.uniform(size=dshape).astype(dtype) verify_results(func, [x_data], "test_resize", rtol=1e-4, atol=1e-4) - isize = [(1, 3, 480, 640)] - osize = [(240, 320), (960, 1280)] method = ["nearest_neighbor", "linear", "cubic"] - - for i in isize: - for j in osize: - for k in method: - verify_resize(i, j, k) - + coord_trans = ["half_pixel", "align_corners", "asymmetric"] + rounding_method = ["round", "floor", "ceil"] + + isize = (1, 3, 480, 640) + + # Downsample + osize = (240, 320) + for i in method: + for j in coord_trans: + for k in rounding_method: + if (i == "nearest_neighbor" and j == "align_corners") or (i == "cubic" and j in ["half_pixel", "align_corners"]): + continue + verify_resize(isize, osize, method=i, coord_trans=j, rounding_method=k) + + # Upsample + osize = (960, 1280) + for i in method: + for j in coord_trans: + for k in rounding_method: + if (i == "nearest_neighbor" and j == "align_corners") or (i == "cubic"): + continue + verify_resize(isize, osize, method=i, coord_trans=j, rounding_method=k) if __name__ == "__main__": test_add() From 208b3f5a5fe17a010c6697576ad7c2780e9813b6 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 13 Jul 2021 19:06:13 +0000 Subject: [PATCH 4/5] * Fixed formatting errors --- python/tvm/contrib/target/onnx.py | 5 ++--- tests/python/contrib/test_onnx.py | 17 ++++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/python/tvm/contrib/target/onnx.py b/python/tvm/contrib/target/onnx.py index 400c4c2a6a50..b839af669fe6 100644 --- a/python/tvm/contrib/target/onnx.py +++ b/python/tvm/contrib/target/onnx.py @@ -686,20 +686,19 @@ def convert_attributes(cls, attrs): coord_trans = b"asymmetric" else: raise RuntimeError( - "Unsupported coordinate transform mode %s in operator Resize" % coord_trans + "Unsupported coordinate transform mode %s in operator Resize" % coord_trans ) rounding_method = attrs.get_str("rounding_method") if rounding_method == "round": rounding_method = b"round_prefer_ceil" - #rounding_method = b"round_prefer_floor" elif rounding_method == "floor": rounding_method = b"floor" elif rounding_method == "ceil": rounding_method = b"ceil" else: raise RuntimeError( - "Unsupported rounding method %s in operator Resize" % rounding_method + "Unsupported rounding method %s in operator Resize" % rounding_method ) size = attrs.get_int_tuple("size") diff --git a/tests/python/contrib/test_onnx.py b/tests/python/contrib/test_onnx.py index 5d783ebdbcdd..5160951eaee5 100644 --- a/tests/python/contrib/test_onnx.py +++ b/tests/python/contrib/test_onnx.py @@ -661,12 +661,12 @@ def test_resize(): def verify_resize(dshape, outsize, method, coord_trans, rounding_method, dtype="float32"): x = relay.var("x", relay.ty.TensorType(dshape, dtype)) y = relay.image.resize2d( - x, - outsize, - layout="NCHW", - method=method, - coordinate_transformation_mode=coord_trans, - rounding_method=rounding_method) + x, + outsize, + layout="NCHW", + method=method, + coordinate_transformation_mode=coord_trans, + rounding_method=rounding_method) func = relay.Function([x], y) x_data = np.random.uniform(size=dshape).astype(dtype) verify_results(func, [x_data], "test_resize", rtol=1e-4, atol=1e-4) @@ -682,7 +682,9 @@ def verify_resize(dshape, outsize, method, coord_trans, rounding_method, dtype=" for i in method: for j in coord_trans: for k in rounding_method: - if (i == "nearest_neighbor" and j == "align_corners") or (i == "cubic" and j in ["half_pixel", "align_corners"]): + if (i == "nearest_neighbor" and j == "align_corners") or ( + i == "cubic" and j in ["half_pixel", "align_corners"] + ): continue verify_resize(isize, osize, method=i, coord_trans=j, rounding_method=k) @@ -695,6 +697,7 @@ def verify_resize(dshape, outsize, method, coord_trans, rounding_method, dtype=" continue verify_resize(isize, osize, method=i, coord_trans=j, rounding_method=k) + if __name__ == "__main__": test_add() test_bias_add() From 2439e574213657dcbfcf3a433741a4d5ee56a798 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 13 Jul 2021 19:33:03 +0000 Subject: [PATCH 5/5] * Fixed some more formatting errors --- tests/python/contrib/test_onnx.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/python/contrib/test_onnx.py b/tests/python/contrib/test_onnx.py index 5160951eaee5..1c2d00aed866 100644 --- a/tests/python/contrib/test_onnx.py +++ b/tests/python/contrib/test_onnx.py @@ -666,7 +666,8 @@ def verify_resize(dshape, outsize, method, coord_trans, rounding_method, dtype=" layout="NCHW", method=method, coordinate_transformation_mode=coord_trans, - rounding_method=rounding_method) + rounding_method=rounding_method, + ) func = relay.Function([x], y) x_data = np.random.uniform(size=dshape).astype(dtype) verify_results(func, [x_data], "test_resize", rtol=1e-4, atol=1e-4)