From bd57a7e7f45794b8f4d43e94a3c89075ef00ab5d Mon Sep 17 00:00:00 2001 From: HongHongHongL Date: Mon, 9 Sep 2024 15:02:46 +0800 Subject: [PATCH 1/4] fix params name bug --- python/tvm/relax/frontend/onnx/onnx_frontend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/tvm/relax/frontend/onnx/onnx_frontend.py b/python/tvm/relax/frontend/onnx/onnx_frontend.py index c3116f9988ce..462d1cf92c01 100644 --- a/python/tvm/relax/frontend/onnx/onnx_frontend.py +++ b/python/tvm/relax/frontend/onnx/onnx_frontend.py @@ -91,7 +91,7 @@ def get_constant( # Convert if possible if isinstance(var, relax.Var) and var.name_hint in params: # When converting a parameter to a constant, update references to it as well. - _, value = params.pop(var.name_hint) + _, value = params[var.name_hint] const_value = relax.const(value) graph_nodes[var.name_hint] = const_value return const_value @@ -2152,7 +2152,7 @@ def _parse_graph_initializers(self, graph: onnx.onnx_ml_pb2.GraphProto): init_var = self._new_var(var_name, shape=array.shape, dtype=array.dtype) self._nodes[init_tensor.name] = init_var # We need to keep track of both the real value and variable for this variable. - self._params[init_tensor.name] = (init_var, array) + self._params[var_name] = (init_var, array) # Otherwise we can use the weight as a constant. else: self._nodes[init_tensor.name] = relax.const(array) From 177fed91e012e574fdfb04c446fa26a3887e6ffb Mon Sep 17 00:00:00 2001 From: HongHongHongL Date: Mon, 30 Sep 2024 15:41:54 +0800 Subject: [PATCH 2/4] add support for onnx pad_v2 --- .../tvm/relax/frontend/onnx/onnx_frontend.py | 31 +++++++++++ tests/python/relax/test_frontend_onnx.py | 55 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/python/tvm/relax/frontend/onnx/onnx_frontend.py b/python/tvm/relax/frontend/onnx/onnx_frontend.py index 462d1cf92c01..7e569bc62a4a 100644 --- a/python/tvm/relax/frontend/onnx/onnx_frontend.py +++ b/python/tvm/relax/frontend/onnx/onnx_frontend.py @@ -1074,6 +1074,37 @@ def _impl_v13(cls, bb, inputs, attr, params): class Pad(OnnxOpConverter): """Converts an onnx Pad node into an equivalent Relax expression.""" + @classmethod + def _impl_v2(cls, bb, inputs, attr, params): + pads = attr.get("pads") + pads = relax.const(_np.array(pads), inputs[0].struct_info.shape[0].dtype) + constant_value = attr.get("value") + if constant_value is not None: + constant_value = constant_value + else: + constant_value = 0.0 + + if isinstance(pads, relax.Constant): + pad_before, pad_after = _np.split(pads.data.numpy(), 2) + pad_before = _np.ndarray.tolist(pad_before) + pad_after = _np.ndarray.tolist(pad_after) + else: + raise ValueError("Dynamic pads are not supported yet.") + + pad_mode = attr.get("mode", b"constant").decode("utf-8") + if not pad_mode in ["constant", "edge", "reflect"]: + raise tvm.error.OpAttributeInvalid( + "Value " + pad_mode + ' in attribute "mode" is invalid for operator Pad.' + ) + + if pad_mode == "constant": + return bb.emit_te(topi.nn.pad, inputs[0], pad_before, pad_after, constant_value) + elif pad_mode == "reflect": + return bb.emit_te(topi.nn.mirror_pad, inputs[0], pad_before, pad_after, "REFLECT") + else: + # TODO(gigiblender) Support edge mode. + raise NotImplementedError("Pad mode {} not implemented".format(pad_mode)) + @classmethod def _impl_v11(cls, bb, inputs, attr, params): pads = get_constant(inputs[1], params) diff --git a/tests/python/relax/test_frontend_onnx.py b/tests/python/relax/test_frontend_onnx.py index 3ea987973578..1d8a5989d591 100644 --- a/tests/python/relax/test_frontend_onnx.py +++ b/tests/python/relax/test_frontend_onnx.py @@ -1399,6 +1399,61 @@ def verify_pad(input_shape, pads, mode="constant", value=0.0): verify_pad((1, 3, 4, 5), [0, 1, 1, 1, 0, 0, 1, 1], "reflect") +@pytest.mark.parametrize("dynamic", [True, False]) +def test_pad_v2(dynamic): + + if dynamic: + pytest.skip("Dynamic pad not supported") + + def verify_pad(input_shape, pads, mode="constant", value=0.0): + indata = np.random.normal(size=input_shape).astype(np.float32) + # numpy expect result + len_dim = len(pads) // 2 + np_pads = [(pads[i], pads[i + len_dim]) for i in range(len_dim)] + pads = np.array(pads) + # onnx graph + if mode in ["edge", "reflect"]: + outdata = np.pad(indata, pad_width=np_pads, mode=mode) + node = helper.make_node("Pad", inputs=["input"], outputs=["output"], mode=mode, pads=pads) + graph = helper.make_graph( + [node], + "pad_test", + inputs=[ + helper.make_tensor_value_info("input", TensorProto.FLOAT, list(indata.shape)) + ], + outputs=[ + helper.make_tensor_value_info("output", TensorProto.FLOAT, list(outdata.shape)) + ], + ) + else: + outdata = np.pad(indata, pad_width=np_pads, mode="constant", constant_values=value) + node = helper.make_node( + "Pad", + inputs=["input"], + outputs=["output"], + mode="constant", + pads=pads, + value=value, + ) + graph = helper.make_graph( + [node], + "pad_test", + inputs=[ + helper.make_tensor_value_info("input", TensorProto.FLOAT, list(indata.shape)) + ], + outputs=[ + helper.make_tensor_value_info("output", TensorProto.FLOAT, list(outdata.shape)) + ], + ) + model = helper.make_model(graph, producer_name="pad_test") + check_correctness(model=model, opset=10) + + verify_pad((2, 2), [0, 1, 0, 0], "constant", 0.0) + verify_pad((2, 3), [1, 0, 0, 1], "constant", 0.0) + verify_pad((3, 2), [0, 0, 1, 0], "constant", 5.0) + verify_pad((1, 3, 4, 5), [0, 1, 1, 1, 0, 0, 1, 1], "reflect") + + @pytest.mark.parametrize("fp_arith", [np.float16, np.float32]) @pytest.mark.parametrize("dynamic", [True, False]) def test_split(fp_arith, dynamic): From 7ede64d2c60cfe773c5f0c0f5ec7b059e46781d2 Mon Sep 17 00:00:00 2001 From: Honglin Zhu Date: Mon, 30 Sep 2024 16:26:37 +0800 Subject: [PATCH 3/4] Update test_frontend_onnx.py --- tests/python/relax/test_frontend_onnx.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/python/relax/test_frontend_onnx.py b/tests/python/relax/test_frontend_onnx.py index 1d8a5989d591..84e0399bcc3f 100644 --- a/tests/python/relax/test_frontend_onnx.py +++ b/tests/python/relax/test_frontend_onnx.py @@ -1414,7 +1414,9 @@ def verify_pad(input_shape, pads, mode="constant", value=0.0): # onnx graph if mode in ["edge", "reflect"]: outdata = np.pad(indata, pad_width=np_pads, mode=mode) - node = helper.make_node("Pad", inputs=["input"], outputs=["output"], mode=mode, pads=pads) + node = helper.make_node( + "Pad", inputs=["input"], outputs=["output"], mode=mode, pads=pads + ) graph = helper.make_graph( [node], "pad_test", From 2e81eabe2413619ec5f75f5fbd328fdb4739dc56 Mon Sep 17 00:00:00 2001 From: Honglin Zhu Date: Mon, 30 Sep 2024 16:43:26 +0800 Subject: [PATCH 4/4] Update onnx_frontend.py --- python/tvm/relax/frontend/onnx/onnx_frontend.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python/tvm/relax/frontend/onnx/onnx_frontend.py b/python/tvm/relax/frontend/onnx/onnx_frontend.py index 7e569bc62a4a..c26585fc749b 100644 --- a/python/tvm/relax/frontend/onnx/onnx_frontend.py +++ b/python/tvm/relax/frontend/onnx/onnx_frontend.py @@ -1079,9 +1079,7 @@ def _impl_v2(cls, bb, inputs, attr, params): pads = attr.get("pads") pads = relax.const(_np.array(pads), inputs[0].struct_info.shape[0].dtype) constant_value = attr.get("value") - if constant_value is not None: - constant_value = constant_value - else: + if constant_value is None: constant_value = 0.0 if isinstance(pads, relax.Constant):