From e07019e9508684c9f1ade2a38ecf35c309133d58 Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Thu, 15 Apr 2021 11:30:33 -0700 Subject: [PATCH 01/17] add support for expr as inputs to pad --- include/tvm/relay/attrs/nn.h | 3 --- python/tvm/relay/op/nn/nn.py | 14 +++++--------- src/relay/op/make_op.h | 2 +- src/relay/op/nn/pad.cc | 16 +++++++--------- src/relay/qnn/op/convolution.cc | 3 +-- src/relay/transforms/dynamic_to_static.cc | 4 +++- src/relay/transforms/fold_explicit_padding.cc | 7 ++++++- src/relay/transforms/pattern_utils.h | 2 +- 8 files changed, 24 insertions(+), 27 deletions(-) diff --git a/include/tvm/relay/attrs/nn.h b/include/tvm/relay/attrs/nn.h index 42adefa31429..43704e39953c 100644 --- a/include/tvm/relay/attrs/nn.h +++ b/include/tvm/relay/attrs/nn.h @@ -1044,13 +1044,10 @@ struct UpSampling3DAttrs : public tvm::AttrsNode { /*! \brief Attributes used for the padding operator */ struct PadAttrs : public tvm::AttrsNode { - double pad_value; Array> pad_width; std::string pad_mode; TVM_DECLARE_ATTRS(PadAttrs, "relay.attrs.PadAttrs") { - TVM_ATTR_FIELD(pad_value).set_default(0.0).describe( - "The value used for padding when mode is 'constant'."); TVM_ATTR_FIELD(pad_width).describe( "Number of values padded to the edges of each axis, " "in the format of ((before_1, after_1), ..., (before_N, after_N))"); diff --git a/python/tvm/relay/op/nn/nn.py b/python/tvm/relay/op/nn/nn.py index 5acdafef0ed9..e5ca7e4a4717 100644 --- a/python/tvm/relay/op/nn/nn.py +++ b/python/tvm/relay/op/nn/nn.py @@ -18,10 +18,10 @@ """Neural network operations.""" from tvm.relay import expr -from . import _make +from ...expr import Constant, Expr, const from ..dyn.nn import _make as _dyn_make +from . import _make from .utils import get_pad_tuple1d, get_pad_tuple2d, get_pad_tuple3d -from ...expr import const, Expr, Constant def conv1d( @@ -1606,15 +1606,11 @@ def pad(data, pad_width, pad_value=0, pad_mode="constant"): result : tvm.relay.Expr The computed result. """ - if isinstance(pad_value, Constant): - pad_value = pad_value.data.asnumpy().item() if isinstance(pad_width, Constant): pad_width = [list(i) for i in pad_width.data.asnumpy()] - if isinstance(pad_width, Expr) or (isinstance(pad_value, Expr)): - if not isinstance(pad_width, Expr): - pad_width = const(list(pad_width)) - if not isinstance(pad_value, Expr): - pad_value = const(pad_value) + if not isinstance(pad_value, Expr): + pad_value = const(pad_value) + if isinstance(pad_width, Expr): return _dyn_make.pad(data, pad_width, pad_value, pad_mode) return _make.pad(data, pad_width, pad_value, pad_mode) diff --git a/src/relay/op/make_op.h b/src/relay/op/make_op.h index e5a20abd7624..a3a182515b6d 100644 --- a/src/relay/op/make_op.h +++ b/src/relay/op/make_op.h @@ -58,7 +58,7 @@ Expr MakeAutoSchedulerLayoutTransform(Expr data, String src_layout, String dst_l Expr MakeOnes(Array shape, DataType dtype); -Expr MakePad(Expr data, Array> pad_width, double pad_value, String pad_mode); +Expr MakePad(Expr data, Array> pad_width, Expr pad_value, String pad_mode); Expr MakeReduce(Expr data, Array axis, bool keepdims, bool exclude, String op_name); diff --git a/src/relay/op/nn/pad.cc b/src/relay/op/nn/pad.cc index c6b987eb42aa..6f6eff355d2b 100644 --- a/src/relay/op/nn/pad.cc +++ b/src/relay/op/nn/pad.cc @@ -114,7 +114,7 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Array& types, int num_inputs, const Attrs& attrs, const TypeReporter& reporter) { - ICHECK_EQ(types.size(), 2); + ICHECK_EQ(types.size(), 3); const auto* data = types[0].as(); if (data == nullptr) return false; @@ -151,7 +151,7 @@ bool PadRel(const Array& types, int num_inputs, const Attrs& attrs, } } - reporter->Assign(types[1], TensorType(Array(oshape), data->dtype)); + reporter->Assign(types[2], TensorType(Array(oshape), data->dtype)); return true; } @@ -170,20 +170,18 @@ Array PadCompute(const Attrs& attrs, const Array& inputs for (size_t i = 0; i < pad_width.size(); ++i) { pad_after.push_back(pad_width[i][1]); } - const auto* out_ttype = out_type.as(); - return Array{topi::pad(inputs[0], pad_before, pad_after, - tvm::tir::make_const(out_ttype->dtype, param->pad_value), - "T_pad", topi::kElementWise, param->pad_mode)}; + const PrimExpr& pad_value = inputs[1](Array()); + return Array{topi::pad(inputs[0], pad_before, pad_after, pad_value, "T_pad", + topi::kElementWise, param->pad_mode)}; } // Handler to create a call to the padding op used by front-end FFI -Expr MakePad(Expr data, Array> pad_width, double pad_value, String pad_mode) { +Expr MakePad(Expr data, Array> pad_width, Expr pad_value, String pad_mode) { auto attrs = make_object(); - attrs->pad_value = pad_value; attrs->pad_width = std::move(pad_width); attrs->pad_mode = std::move(pad_mode); static const Op& op = Op::Get("nn.pad"); - return Call(op, {data}, Attrs(attrs), {}); + return Call(op, {data, pad_value}, Attrs(attrs), {}); } TVM_REGISTER_GLOBAL("relay.op.nn._make.pad").set_body_typed(MakePad); diff --git a/src/relay/qnn/op/convolution.cc b/src/relay/qnn/op/convolution.cc index 21335ec2fb34..1a81d10c2583 100644 --- a/src/relay/qnn/op/convolution.cc +++ b/src/relay/qnn/op/convolution.cc @@ -234,8 +234,7 @@ Expr Conv2DPadInput(const Expr& data, const Expr& input_zero_point, const Conv2D } else { LOG(FATAL) << "qnn.conv2d does not support " << param->data_layout << " layout"; } - auto pad_value = GetScalarFromConstant(input_zero_point); - padded_data = Pad(data, pad_width, pad_value, "constant"); + padded_data = Pad(data, pad_width, input_zero_point, "constant"); } return padded_data; } diff --git a/src/relay/transforms/dynamic_to_static.cc b/src/relay/transforms/dynamic_to_static.cc index 0590b41550ce..8bfe1d83bd9e 100644 --- a/src/relay/transforms/dynamic_to_static.cc +++ b/src/relay/transforms/dynamic_to_static.cc @@ -179,7 +179,9 @@ class DynamicToStaticMutator : public MixedModeMutator { const PadAttrs* param = call_node->attrs.as(); ICHECK(param); - return MakePad(call_node->args[0], ToMatrix(pad_width->data), ToScalar(pad_fill->data), + + Expr pad_value = args[2]; + return MakePad(call_node->args[0], ToMatrix(pad_width->data), pad_value, param->pad_mode); } return Expr(nullptr); diff --git a/src/relay/transforms/fold_explicit_padding.cc b/src/relay/transforms/fold_explicit_padding.cc index d959e5b75e40..ada795f9f176 100644 --- a/src/relay/transforms/fold_explicit_padding.cc +++ b/src/relay/transforms/fold_explicit_padding.cc @@ -119,7 +119,12 @@ class SimplifyConvPad { ICHECK(pad_node); const PadAttrs* param = pad_node->attrs.as(); ICHECK(param); - if (param->pad_mode == "constant" && param->pad_value == 0.0) { + Array args = pad_node->args; + + // Possibly perform more optimizations if the pad_value is 0 + Expr pad_value = args[1]; + Constant zero_scalar = MakeConstantScalar(DataType::Float(64), 0); + if (param->pad_mode == "constant" && IsEqualScalar(pad_value, zero_scalar)) { Attrs attrs; if (node_map.count(conv1d_)) { attrs = GetAttrs(param, call_node->attrs.as()); diff --git a/src/relay/transforms/pattern_utils.h b/src/relay/transforms/pattern_utils.h index 8d9f723dffea..975c07e563be 100644 --- a/src/relay/transforms/pattern_utils.h +++ b/src/relay/transforms/pattern_utils.h @@ -676,7 +676,7 @@ static inline Expr AvgPool2D(Expr data, Array pool_size, Array> pad_width, double pad_value, +static inline Expr Pad(Expr data, Array> pad_width, Expr pad_value, std::string pad_mode) { Array> pad_width_int; for (size_t i = 0; i < pad_width.size(); ++i) { From c1f058a9e668608bda69c7c6f3a2e0fe3d14dc04 Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Thu, 15 Apr 2021 12:26:17 -0700 Subject: [PATCH 02/17] fix improper amount of args --- src/relay/op/nn/pad.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/relay/op/nn/pad.cc b/src/relay/op/nn/pad.cc index 6f6eff355d2b..355d4c3943c6 100644 --- a/src/relay/op/nn/pad.cc +++ b/src/relay/op/nn/pad.cc @@ -191,8 +191,9 @@ RELAY_REGISTER_OP("nn.pad") )code" TVM_ADD_FILELINE) .set_attrs_type() - .set_num_inputs(1) + .set_num_inputs(2) .add_argument("data", "Tensor", "The input tensor.") + .add_argument("pad_val", "Tensor", "The value to fill the padded area with") .set_support_level(2) .add_type_rel("Pad", PadRel) .set_attr("FInferCorrectLayout", PadInferCorrectLayout) From 0281d57bb8665695c34c832e6918dccce3ff275b Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Thu, 15 Apr 2021 13:26:29 -0700 Subject: [PATCH 03/17] add dynamic padding test --- tests/python/relay/test_op_level2.py | 74 ++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/tests/python/relay/test_op_level2.py b/tests/python/relay/test_op_level2.py index c5843758c3d2..801e064e0e8c 100644 --- a/tests/python/relay/test_op_level2.py +++ b/tests/python/relay/test_op_level2.py @@ -18,15 +18,13 @@ """ import numpy as np import tvm -from tvm import te -from tvm import autotvm -from tvm import relay +import tvm.testing +import tvm.topi.testing +from tvm import autotvm, relay, te +from tvm.contrib import utils from tvm.relay import transform from tvm.relay.testing import run_infer_type -from tvm.contrib import utils -import tvm.topi.testing from tvm.topi.cuda.conv3d_winograd import _infer_tile_size -import tvm.testing @tvm.testing.uses_gpu @@ -1197,6 +1195,30 @@ def test_pad_infer_type(): yy = run_infer_type(y) assert yy.checked_type == relay.TensorType((n + (-2), c + (-4), h + (-2), w + 8), "float32") + # dealing with dynamic vals + n, c, h, w = te.size_var("n"), 2, 3, te.size_var("w") + t = relay.var("t", relay.TensorType((n, c, h, w), "float32")) + y = relay.nn.pad( + t, ((1, 1), (2, 2), (3, 3), (4, 4)), pad_value=relay.const(2.2) * relay.const(3.14) + ) + yy = run_infer_type(y) + assert yy.checked_type == relay.TensorType((n + 2, 6, 9, w + 8), "float32") + + +def _get_numpy_pad(dshape, data, pad, pad_value=0): + mod_pad = [] + for axis, (pad_x, pad_y) in enumerate(pad): + indices = range(dshape[axis]) + if pad_x < 0: + indices = indices[abs(pad_x) :] + pad_x = 0 + if pad_y < 0: + indices = indices[:pad_y] + pad_y = 0 + data = np.take(data, indices, axis) + mod_pad.append((pad_x, pad_y)) + return np.pad(data, tuple(mod_pad), "constant", constant_values=pad_value) + @tvm.testing.uses_gpu def test_pad_run(): @@ -1209,20 +1231,7 @@ def _test_run(dtype): y = relay.nn.pad(x, pad) func = relay.Function([x], y) data = np.random.uniform(size=dshape).astype(dtype) - mod_pad = [] - mod_data = data - for axis, (pad_x, pad_y) in enumerate(pad): - indices = range(dshape[axis]) - if pad_x < 0: - indices = indices[abs(pad_x) :] - pad_x = 0 - if pad_y < 0: - indices = indices[:pad_y] - pad_y = 0 - mod_data = np.take(mod_data, indices, axis) - mod_pad.append((pad_x, pad_y)) - - ref_res = np.pad(mod_data, tuple(mod_pad), "constant") + ref_res = _get_numpy_pad(dshape, data, pad) for target, dev in tvm.testing.enabled_targets(): intrp1 = relay.create_executor("graph", device=dev, target=target) op_res1 = intrp1.evaluate(func)(data) @@ -1232,6 +1241,30 @@ def _test_run(dtype): _test_run("int32") +@tvm.testing.uses_gpu +def test_pad_run_dynamic_pad_value(): + def _test_run(dtype): + dshape = (4, 6, 3, 5) + pad = ((-1, -1), (2, -2), (0, -2), (4, 4)) + + data = relay.var("data", shape=dshape, dtype=dtype) + pad_value = relay.var("pad_value", dtype) + pad_data = relay.nn.pad(data, pad, pad_value=pad_value) + f = relay.Function([data, pad_value], pad_data) + + data_arr = np.random.uniform(-10, 10, size=dshape).astype(dtype) + pad_value_arr = 2.0 + ref_res = _get_numpy_pad(dshape, data_arr, pad, pad_value=pad_value_arr) + + for target, dev in tvm.testing.enabled_targets(): + intrp = relay.create_executor(kind="graph", device=dev, target=target) + result = intrp.evaluate(f)(data_arr, pad_value_arr) + tvm.testing.assert_allclose(result.asnumpy(), ref_res, rtol=1e-5, atol=1e-5) + + _test_run("float32") + _test_run("int32") + + @tvm.testing.uses_gpu def test_lrn(): n, c, h, w = te.size_var("n"), te.size_var("c"), te.size_var("h"), te.size_var("w") @@ -1766,6 +1799,7 @@ def _test_correlation( test_flatten_infer_type() test_pad_infer_type() test_pad_run() + test_pad_run_dynamic_pad_value() test_conv3d_transpose_infer_type() test_conv3d_transpose_ncdhw_run() test_conv2d_transpose_infer_type() From 77256fa8bb1f43d6c53d07a7f82f8f393989b325 Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Thu, 15 Apr 2021 13:31:46 -0700 Subject: [PATCH 04/17] infer type better test --- tests/python/relay/test_op_level2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/relay/test_op_level2.py b/tests/python/relay/test_op_level2.py index 801e064e0e8c..964086845837 100644 --- a/tests/python/relay/test_op_level2.py +++ b/tests/python/relay/test_op_level2.py @@ -1199,7 +1199,7 @@ def test_pad_infer_type(): n, c, h, w = te.size_var("n"), 2, 3, te.size_var("w") t = relay.var("t", relay.TensorType((n, c, h, w), "float32")) y = relay.nn.pad( - t, ((1, 1), (2, 2), (3, 3), (4, 4)), pad_value=relay.const(2.2) * relay.const(3.14) + t, ((1, 1), (2, 2), (3, 3), (4, 4)), pad_value=relay.var("pad_value", "float32") ) yy = run_infer_type(y) assert yy.checked_type == relay.TensorType((n + 2, 6, 9, w + 8), "float32") From fe37664165298cba9bc1c9ec3f15345f93fbaf2d Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Thu, 15 Apr 2021 14:35:28 -0700 Subject: [PATCH 05/17] add comments to type relations --- src/relay/op/nn/pad.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/relay/op/nn/pad.cc b/src/relay/op/nn/pad.cc index 355d4c3943c6..cc1071710416 100644 --- a/src/relay/op/nn/pad.cc +++ b/src/relay/op/nn/pad.cc @@ -114,6 +114,7 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Array& types, int num_inputs, const Attrs& attrs, const TypeReporter& reporter) { + // types = [pad_data_type, pad_value_type, ret_type] ICHECK_EQ(types.size(), 3); const auto* data = types[0].as(); if (data == nullptr) return false; From 5288dfa52be4732bedb5e1e75b4284c1e855b109 Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Fri, 16 Apr 2021 11:04:11 -0700 Subject: [PATCH 06/17] fix infer type layouts --- src/relay/op/nn/pad.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/relay/op/nn/pad.cc b/src/relay/op/nn/pad.cc index cc1071710416..bfa6029fecb8 100644 --- a/src/relay/op/nn/pad.cc +++ b/src/relay/op/nn/pad.cc @@ -55,8 +55,8 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Array> axis_pad_width; int index_counter = 0; - ICHECK_EQ(new_in_layouts.size(), 1); - ICHECK_EQ(old_in_layouts.size(), 1); + ICHECK_EQ(new_in_layouts.size(), 2); + ICHECK_EQ(old_in_layouts.size(), 2); for (auto iter_var : old_in_layouts[0]->axes) { const auto& old_layout_axis = LayoutAxis::Get(iter_var); axis_pad_width.emplace(old_layout_axis.name(), params->pad_width[index_counter]); @@ -102,7 +102,7 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Array Date: Fri, 16 Apr 2021 11:21:29 -0700 Subject: [PATCH 07/17] proper return shape --- src/relay/op/nn/pad.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/relay/op/nn/pad.cc b/src/relay/op/nn/pad.cc index bfa6029fecb8..7de478f7bc33 100644 --- a/src/relay/op/nn/pad.cc +++ b/src/relay/op/nn/pad.cc @@ -109,7 +109,7 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Array>{{ret}, {ret}}; + return Array>{{ret, new_in_layouts[0]}, {ret, new_in_layouts[0]}}; } bool PadRel(const Array& types, int num_inputs, const Attrs& attrs, From 391fdbef0560c123d25b8021eef94f5c8b1eeea0 Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Fri, 16 Apr 2021 12:31:13 -0700 Subject: [PATCH 08/17] proper shape infer type --- src/relay/op/nn/pad.cc | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/relay/op/nn/pad.cc b/src/relay/op/nn/pad.cc index 7de478f7bc33..c4a0fa492cc3 100644 --- a/src/relay/op/nn/pad.cc +++ b/src/relay/op/nn/pad.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,7 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Array(attrs.as()); - Layout ret; + Layout ret_data, ret_pad_value; // If new_in_layouts are defined, this code tries to modify the layout. bool is_layout_modified = new_in_layouts.defined(); if (new_in_layouts.defined()) { @@ -95,7 +96,8 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Arraypad_width = new_pad_width; } } @@ -103,13 +105,14 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Array>{{ret, new_in_layouts[0]}, {ret, new_in_layouts[0]}}; + return Array>{{ret_data, ret_pad_value}, {ret_data, ret_pad_value}}; } bool PadRel(const Array& types, int num_inputs, const Attrs& attrs, @@ -171,7 +174,8 @@ Array PadCompute(const Attrs& attrs, const Array& inputs for (size_t i = 0; i < pad_width.size(); ++i) { pad_after.push_back(pad_width[i][1]); } - const PrimExpr& pad_value = inputs[1](Array()); + te::Tensor cast_pad_value = topi::cast(inputs[1], inputs[0]->dtype); + const PrimExpr& pad_value = cast_pad_value(Array()); return Array{topi::pad(inputs[0], pad_before, pad_after, pad_value, "T_pad", topi::kElementWise, param->pad_mode)}; } From 3df33ef2aff87369e52e0db6457c8eecba050e69 Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Fri, 16 Apr 2021 17:15:17 -0700 Subject: [PATCH 09/17] make the tests pass by setting the conditions --- .../contrib/test_arm_compute_lib/test_conv2d.py | 11 +++++------ tests/python/contrib/test_ethosn/test_conv2d.py | 8 ++++++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py index cc5bbfec7c69..bdf31775cde5 100644 --- a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py +++ b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py @@ -17,18 +17,17 @@ """Arm Compute Library integration conv2d tests.""" import numpy as np - import tvm from tvm import relay from test_arm_compute_lib.infrastructure import ( - skip_runtime_test, - skip_codegen_test, + Device, build_and_run, + skip_codegen_test, + skip_runtime_test, verify, verify_codegen, ) -from test_arm_compute_lib.infrastructure import Device def _get_model( @@ -426,7 +425,7 @@ def test_codegen_conv2d(): exp_codegen = _get_expected_codegen( *args, has_bias=composite[1], has_activation=composite[2] ) - verify_codegen(func, exp_codegen, 1) + verify_codegen(func, exp_codegen, 1, tvm_ops=1) def test_qnn_conv2d(): @@ -600,7 +599,7 @@ def test_codegen_qnn_conv2d(): exp_codegen = _get_expected_codegen( *args, has_bias=composite[1], has_activation=composite[2] ) - verify_codegen(func, exp_codegen, 1) + verify_codegen(func, exp_codegen, 1, tvm_ops=1) if __name__ == "__main__": diff --git a/tests/python/contrib/test_ethosn/test_conv2d.py b/tests/python/contrib/test_ethosn/test_conv2d.py index ca551603d13f..7db3c18d66ea 100644 --- a/tests/python/contrib/test_ethosn/test_conv2d.py +++ b/tests/python/contrib/test_ethosn/test_conv2d.py @@ -17,11 +17,13 @@ """Ethos-N integration conv2d tests""" -import numpy as np import math + +import numpy as np import tvm from tvm import relay from tvm.relay.op.contrib.ethosn import ethosn_available + from . import infrastructure as tei @@ -176,7 +178,9 @@ def test_conv2d(): ) for npu in [False, True]: mod = tei.make_module(model, params) - outputs.append(tei.build_and_run(mod, inputs, 1, params, npu=npu)) + outputs.append( + tei.build_and_run(mod, inputs, 1, params, npu=npu, expected_host_ops=1) + ) tei.verify(outputs, 1) From 4f7064c17df684f08353298577d182b71c75d1fd Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Sat, 17 Apr 2021 02:29:18 -0700 Subject: [PATCH 10/17] make codegen reflect reality --- .../test_arm_compute_lib/test_conv2d.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py index bdf31775cde5..34d3ecce9449 100644 --- a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py +++ b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py @@ -193,11 +193,23 @@ def _get_expected_codegen( groups, dtype, channels, + has_pad=False, has_bias=False, has_activation=False, ): if len(padding) == 2: padding = (padding[0], padding[1], padding[0], padding[1]) + + # If an explicit relay pad op occurs before conv, don't have the pad in the conv + if has_pad: + shape = ( + shape[0], + shape[1] + padding[0] + padding[1], + shape[2] + padding[2] + padding[3], + shape[3], + ) + padding = (0, 0, 0, 0) + output_height = ((shape[1] - kernel_h + padding[0] + padding[2]) / strides[0]) + 1 output_width = ((shape[2] - kernel_w + padding[1] + padding[3]) / strides[1]) + 1 output_shape = (1, int(output_height), int(output_width), channels) @@ -423,9 +435,9 @@ def test_codegen_conv2d(): has_activation=composite[2], ) exp_codegen = _get_expected_codegen( - *args, has_bias=composite[1], has_activation=composite[2] + *args, has_pad=composite[0], has_bias=composite[1], has_activation=composite[2] ) - verify_codegen(func, exp_codegen, 1, tvm_ops=1) + verify_codegen(func, exp_codegen, 1, tvm_ops=int(composite[0])) def test_qnn_conv2d(): @@ -597,9 +609,9 @@ def test_codegen_qnn_conv2d(): has_activation=composite[2], ) exp_codegen = _get_expected_codegen( - *args, has_bias=composite[1], has_activation=composite[2] + *args, has_pad=composite[0], has_bias=composite[1], has_activation=composite[2] ) - verify_codegen(func, exp_codegen, 1, tvm_ops=1) + verify_codegen(func, exp_codegen, 1, tvm_ops=int(composite[0])) if __name__ == "__main__": From 6561af6723d33892844605c7977b39b405b377ad Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Sat, 17 Apr 2021 02:32:34 -0700 Subject: [PATCH 11/17] make ternary operations more pythonic --- .../contrib/test_arm_compute_lib/test_conv2d.py | 4 ++-- tests/python/contrib/test_ethosn/test_conv2d.py | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py index 34d3ecce9449..a416cad911bb 100644 --- a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py +++ b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py @@ -437,7 +437,7 @@ def test_codegen_conv2d(): exp_codegen = _get_expected_codegen( *args, has_pad=composite[0], has_bias=composite[1], has_activation=composite[2] ) - verify_codegen(func, exp_codegen, 1, tvm_ops=int(composite[0])) + verify_codegen(func, exp_codegen, 1, tvm_ops=1 if composite[0] else 0) def test_qnn_conv2d(): @@ -611,7 +611,7 @@ def test_codegen_qnn_conv2d(): exp_codegen = _get_expected_codegen( *args, has_pad=composite[0], has_bias=composite[1], has_activation=composite[2] ) - verify_codegen(func, exp_codegen, 1, tvm_ops=int(composite[0])) + verify_codegen(func, exp_codegen, 1, tvm_ops=1 if composite[0] else 0) if __name__ == "__main__": diff --git a/tests/python/contrib/test_ethosn/test_conv2d.py b/tests/python/contrib/test_ethosn/test_conv2d.py index 7db3c18d66ea..4e2768f3f291 100644 --- a/tests/python/contrib/test_ethosn/test_conv2d.py +++ b/tests/python/contrib/test_ethosn/test_conv2d.py @@ -176,10 +176,20 @@ def test_conv2d(): out_channels, weight_format, ) + + # Whether an explicit call to nn.pad was called before the conv + use_explicit_pad_op = pad == "op" or pad == "both" for npu in [False, True]: mod = tei.make_module(model, params) outputs.append( - tei.build_and_run(mod, inputs, 1, params, npu=npu, expected_host_ops=1) + tei.build_and_run( + mod, + inputs, + 1, + params, + npu=npu, + expected_host_ops=1 if use_explicit_pad_op else 0, + ) ) tei.verify(outputs, 1) From 4baef297aafa4bdc82b2081eda91e50f90356a0e Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Sat, 17 Apr 2021 12:46:46 -0700 Subject: [PATCH 12/17] proper infer layout --- src/relay/op/nn/pad.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/relay/op/nn/pad.cc b/src/relay/op/nn/pad.cc index c4a0fa492cc3..42a1ea186dbf 100644 --- a/src/relay/op/nn/pad.cc +++ b/src/relay/op/nn/pad.cc @@ -45,7 +45,7 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Array(attrs.as()); - Layout ret_data, ret_pad_value; + Layout ret_data; // If new_in_layouts are defined, this code tries to modify the layout. bool is_layout_modified = new_in_layouts.defined(); if (new_in_layouts.defined()) { @@ -97,7 +97,6 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Arraypad_width = new_pad_width; } } @@ -106,13 +105,14 @@ Array> PadInferCorrectLayout(const Attrs& attrs, const Array>{{ret_data, ret_pad_value}, {ret_data, ret_pad_value}}; + + // The pad value is always a scalar + Layout ret_pad_value = Layout("1"); + return Array>{{ret_data, ret_pad_value}, {ret_data}}; } bool PadRel(const Array& types, int num_inputs, const Attrs& attrs, From a16d026b97c81114ca7efd3c6c522afdfe9a9d55 Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Mon, 19 Apr 2021 11:43:19 -0700 Subject: [PATCH 13/17] fold explicit padding --- src/relay/transforms/fold_explicit_padding.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/relay/transforms/fold_explicit_padding.cc b/src/relay/transforms/fold_explicit_padding.cc index ada795f9f176..b600953e0765 100644 --- a/src/relay/transforms/fold_explicit_padding.cc +++ b/src/relay/transforms/fold_explicit_padding.cc @@ -45,7 +45,7 @@ class SimplifyConvPad { SimplifyConvPad() { x_ = IsWildcard(); w_ = IsWildcard(); - pad_ = IsOp("nn.pad")({x_}); + pad_ = IsOp("nn.pad")({x_, IsWildcard()}); conv1d_ = IsOp("nn.conv1d"); conv2d_ = IsOp("nn.conv2d"); conv3d_ = IsOp("nn.conv3d"); @@ -122,9 +122,8 @@ class SimplifyConvPad { Array args = pad_node->args; // Possibly perform more optimizations if the pad_value is 0 - Expr pad_value = args[1]; - Constant zero_scalar = MakeConstantScalar(DataType::Float(64), 0); - if (param->pad_mode == "constant" && IsEqualScalar(pad_value, zero_scalar)) { + const ConstantNode* pad_value = args[1].as(); + if (param->pad_mode == "constant" && pad_value && ToScalar(pad_value->data) == 0.0) { Attrs attrs; if (node_map.count(conv1d_)) { attrs = GetAttrs(param, call_node->attrs.as()); From 9e501f60f979df9e8c2b1cd506d27a4679006abd Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Mon, 19 Apr 2021 11:56:02 -0700 Subject: [PATCH 14/17] fix pattern matching in contrib --- python/tvm/relay/op/contrib/arm_compute_lib.py | 11 +++++------ python/tvm/relay/op/contrib/ethosn.py | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/python/tvm/relay/op/contrib/arm_compute_lib.py b/python/tvm/relay/op/contrib/arm_compute_lib.py index 6234c944d4e4..9152b50e7686 100644 --- a/python/tvm/relay/op/contrib/arm_compute_lib.py +++ b/python/tvm/relay/op/contrib/arm_compute_lib.py @@ -17,16 +17,15 @@ # pylint: disable=invalid-name, unused-argument """Arm Compute Library supported operators.""" import tvm - from tvm import relay from tvm._ffi import register_func -from tvm.relay.expr import const from tvm.relay import transform from tvm.relay.build_module import bind_params_by_name +from tvm.relay.expr import const -from ...dataflow_pattern import wildcard, is_op, is_constant, is_expr -from .register import register_pattern_table +from ...dataflow_pattern import is_constant, is_expr, is_op, wildcard from ..strategy.generic import is_depthwise_conv2d +from .register import register_pattern_table def is_arm_compute_runtime_enabled(): @@ -140,7 +139,7 @@ def conv_pattern(): pattern : dataflow_pattern.AltPattern Denotes the convolution pattern. """ - pattern = is_op("nn.pad")(wildcard()) | wildcard() + pattern = is_op("nn.pad")(wildcard(), wildcard()) | wildcard() pattern = is_op("nn.conv2d")(pattern, is_constant()) pattern = pattern.optional(lambda x: is_op("nn.bias_add")(x, is_constant())) pattern = pattern.optional(is_op("nn.relu")) @@ -154,7 +153,7 @@ def qnn_conv_pattern(): pattern : dataflow_pattern.AltPattern Denotes the convolution pattern. """ - pattern = is_op("nn.pad")(wildcard()) | wildcard() + pattern = is_op("nn.pad")(wildcard(), wildcard()) | wildcard() pattern = is_op("qnn.conv2d")( pattern, is_constant(), is_constant(), is_constant(), is_constant(), is_constant() ) diff --git a/python/tvm/relay/op/contrib/ethosn.py b/python/tvm/relay/op/contrib/ethosn.py index 2c63d63a36ef..2cd787a74dc1 100644 --- a/python/tvm/relay/op/contrib/ethosn.py +++ b/python/tvm/relay/op/contrib/ethosn.py @@ -22,10 +22,10 @@ from tvm.relay import transform from tvm.relay.build_module import bind_params_by_name -from ...dataflow_pattern import wildcard, is_op, is_constant from ... import qnn as _qnn -from .register import register_pattern_table +from ...dataflow_pattern import is_constant, is_op, wildcard from . import _ethosn as support +from .register import register_pattern_table class Available(Enum): @@ -82,7 +82,7 @@ def pattern_table(): """Get the Ethos-N compiler pattern table.""" def qnn_conv_pattern(): - pattern = is_op("nn.pad")(wildcard()) | wildcard() + pattern = is_op("nn.pad")(wildcard(), wildcard()) | wildcard() pattern = is_op("qnn.conv2d")( pattern, is_constant(), is_constant(), is_constant(), is_constant(), is_constant() ) From b4e34c9fcbcb076923e062b02640e2b735aac9f4 Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Mon, 19 Apr 2021 12:07:00 -0700 Subject: [PATCH 15/17] revert tests for contrib now that pattern matching works --- .../test_arm_compute_lib/test_conv2d.py | 20 ++++--------------- .../python/contrib/test_ethosn/test_conv2d.py | 14 +------------ 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py index a416cad911bb..908399ad558d 100644 --- a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py +++ b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py @@ -193,23 +193,11 @@ def _get_expected_codegen( groups, dtype, channels, - has_pad=False, has_bias=False, has_activation=False, ): if len(padding) == 2: padding = (padding[0], padding[1], padding[0], padding[1]) - - # If an explicit relay pad op occurs before conv, don't have the pad in the conv - if has_pad: - shape = ( - shape[0], - shape[1] + padding[0] + padding[1], - shape[2] + padding[2] + padding[3], - shape[3], - ) - padding = (0, 0, 0, 0) - output_height = ((shape[1] - kernel_h + padding[0] + padding[2]) / strides[0]) + 1 output_width = ((shape[2] - kernel_w + padding[1] + padding[3]) / strides[1]) + 1 output_shape = (1, int(output_height), int(output_width), channels) @@ -435,9 +423,9 @@ def test_codegen_conv2d(): has_activation=composite[2], ) exp_codegen = _get_expected_codegen( - *args, has_pad=composite[0], has_bias=composite[1], has_activation=composite[2] + *args, has_bias=composite[1], has_activation=composite[2] ) - verify_codegen(func, exp_codegen, 1, tvm_ops=1 if composite[0] else 0) + verify_codegen(func, exp_codegen, 1) def test_qnn_conv2d(): @@ -609,9 +597,9 @@ def test_codegen_qnn_conv2d(): has_activation=composite[2], ) exp_codegen = _get_expected_codegen( - *args, has_pad=composite[0], has_bias=composite[1], has_activation=composite[2] + *args, has_bias=composite[1], has_activation=composite[2] ) - verify_codegen(func, exp_codegen, 1, tvm_ops=1 if composite[0] else 0) + verify_codegen(func, exp_codegen, 1) if __name__ == "__main__": diff --git a/tests/python/contrib/test_ethosn/test_conv2d.py b/tests/python/contrib/test_ethosn/test_conv2d.py index 4e2768f3f291..b9bcc6ee5867 100644 --- a/tests/python/contrib/test_ethosn/test_conv2d.py +++ b/tests/python/contrib/test_ethosn/test_conv2d.py @@ -176,21 +176,9 @@ def test_conv2d(): out_channels, weight_format, ) - - # Whether an explicit call to nn.pad was called before the conv - use_explicit_pad_op = pad == "op" or pad == "both" for npu in [False, True]: mod = tei.make_module(model, params) - outputs.append( - tei.build_and_run( - mod, - inputs, - 1, - params, - npu=npu, - expected_host_ops=1 if use_explicit_pad_op else 0, - ) - ) + outputs.append(tei.build_and_run(mod, inputs, 1, params, npu=npu)) tei.verify(outputs, 1) From 2c7f8ac39f5374b45009cc96786f572e92e87e95 Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Mon, 19 Apr 2021 12:09:11 -0700 Subject: [PATCH 16/17] revert import changes --- tests/python/contrib/test_arm_compute_lib/test_conv2d.py | 9 +++++---- tests/python/contrib/test_ethosn/test_conv2d.py | 6 ++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py index 908399ad558d..b54af9c3ff81 100644 --- a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py +++ b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py @@ -17,17 +17,18 @@ """Arm Compute Library integration conv2d tests.""" import numpy as np + import tvm from tvm import relay from test_arm_compute_lib.infrastructure import ( - Device, - build_and_run, - skip_codegen_test, skip_runtime_test, + skip_codegen_test, + build_and_run, verify, verify_codegen, ) +from test_arm_compute_lib.infrastructure import Device def _get_model( @@ -606,4 +607,4 @@ def test_codegen_qnn_conv2d(): test_conv2d() test_qnn_conv2d() test_codegen_conv2d() - test_codegen_qnn_conv2d() + test_codegen_qnn_conv2d() \ No newline at end of file diff --git a/tests/python/contrib/test_ethosn/test_conv2d.py b/tests/python/contrib/test_ethosn/test_conv2d.py index b9bcc6ee5867..004f32ab06b7 100644 --- a/tests/python/contrib/test_ethosn/test_conv2d.py +++ b/tests/python/contrib/test_ethosn/test_conv2d.py @@ -17,13 +17,11 @@ """Ethos-N integration conv2d tests""" -import math - import numpy as np +import math import tvm from tvm import relay from tvm.relay.op.contrib.ethosn import ethosn_available - from . import infrastructure as tei @@ -352,4 +350,4 @@ def test_conv2d_failure(): ) model = tei.make_ethosn_composite(model, "ethos-n.qnn_conv2d") mod = tei.make_ethosn_partition(model) - tei.test_error(mod, {}, err_msg) + tei.test_error(mod, {}, err_msg) \ No newline at end of file From 5d951c6fed66e746fa693f6b95a383226c598039 Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Mon, 19 Apr 2021 12:10:16 -0700 Subject: [PATCH 17/17] add newline --- tests/python/contrib/test_arm_compute_lib/test_conv2d.py | 2 +- tests/python/contrib/test_ethosn/test_conv2d.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py index b54af9c3ff81..cc5bbfec7c69 100644 --- a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py +++ b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py @@ -607,4 +607,4 @@ def test_codegen_qnn_conv2d(): test_conv2d() test_qnn_conv2d() test_codegen_conv2d() - test_codegen_qnn_conv2d() \ No newline at end of file + test_codegen_qnn_conv2d() diff --git a/tests/python/contrib/test_ethosn/test_conv2d.py b/tests/python/contrib/test_ethosn/test_conv2d.py index 004f32ab06b7..ca551603d13f 100644 --- a/tests/python/contrib/test_ethosn/test_conv2d.py +++ b/tests/python/contrib/test_ethosn/test_conv2d.py @@ -350,4 +350,4 @@ def test_conv2d_failure(): ) model = tei.make_ethosn_composite(model, "ethos-n.qnn_conv2d") mod = tei.make_ethosn_partition(model) - tei.test_error(mod, {}, err_msg) \ No newline at end of file + tei.test_error(mod, {}, err_msg)