from .infrastructure import Device
-def _get_model(shape, dtype, typef, sizes, strides, padding,
- ceil_mode, var_names):
+def _calculate_output_shape(shape, sizes, padding, strides):
+ """Calculate pooling output shape."""
+ output_height = ((shape[1] - sizes[0] + padding[0] + padding[2]) / strides[0]) + 1
+ output_width = ((shape[2] - sizes[1] + padding[1] + padding[3]) / strides[1]) + 1
+ return 1, int(output_height), int(output_width), shape[3]
+
+
+def _get_pooling_model(shape, dtype, typef, sizes, strides, padding,
+ ceil_mode, count_include_pad, var_names):
+ """Return a model and any parameters it may have."""
+ if len(padding) == 2:
+ padding = (padding[0], padding[1], padding[0], padding[1])
+ out = relay.var(next(var_names), shape=shape, dtype=dtype)
+
+ if typef == "nn.max_pool2d":
+ out = relay.nn.max_pool2d(out, pool_size=sizes, strides=strides, padding=padding,
+ ceil_mode=ceil_mode, layout="NHWC")
+ elif typef == "nn.avg_pool2d":
+ if dtype == "uint8":
+ out = relay.cast(out, 'int32')
+ out = relay.nn.avg_pool2d(out, pool_size=sizes, strides=strides, padding=padding,
+ ceil_mode=ceil_mode, count_include_pad=count_include_pad,
+ layout="NHWC")
+ if dtype == "uint8":
+ out = relay.cast(out, 'uint8')
+ elif typef == "nn.l2_pool2d":
+ out = relay.power(out, relay.const(2.0))
+ out = relay.nn.avg_pool2d(out, pool_size=sizes, strides=strides, padding=padding,
+ ceil_mode=ceil_mode, count_include_pad=count_include_pad,
+ layout="NHWC")
+ out = relay.sqrt(out)
+ else:
+ raise ValueError("Function not supported")
+
+ return out
+
+
+def _get_global_pooling_model(shape, dtype, typef, var_names):
"""Return a model and any parameters it may have."""
- var = relay.var(next(var_names), shape=shape, dtype=dtype)
- pool = typef(var, pool_size=sizes, strides=strides, padding=padding,
- ceil_mode=ceil_mode, layout="NHWC")
- return pool
+ out = relay.var(next(var_names), shape=shape, dtype=dtype)
+
+ if typef == "nn.global_max_pool2d":
+ out = relay.nn.global_max_pool2d(out, layout="NHWC")
+ elif typef == "nn.global_avg_pool2d":
+ if dtype == "uint8":
+ out = relay.cast(out, 'int32')
+ out = relay.nn.global_avg_pool2d(out, layout="NHWC")
+ if dtype == "uint8":
+ out = relay.cast(out, 'uint8')
+ else:
+ raise ValueError("Function not supported")
+
+ return out
-def _get_expected_codegen(shape, dtype, typef, sizes, strides,
- padding, ceil_mode):
+def _get_expected_pooling_codegen(shape, dtype, typef, sizes, strides,
+ padding, ceil_mode, count_include_pad):
if len(padding) == 2:
- padding = (padding[1], padding[1], padding[0], padding[0])
- output_height = ((shape[1] - sizes[0] + padding[0] + padding[2]) / strides[0]) + 1
- output_width = ((shape[2] - sizes[1] + padding[1] + padding[3]) / strides[1]) + 1
- output_shape = (1, int(output_height), int(output_width), shape[3])
+ padding = (padding[0], padding[1], padding[0], padding[1])
+ output_shape = _calculate_output_shape(shape, sizes, padding, strides)
node = {
"op": "kernel",
- "name": "nn.max_pool2d",
+ "name": typef,
"inputs": [[0, 0, 0]],
"attrs": {
"num_inputs": "1",
},
}
+ if typef == "nn.avg_pool2d" or typef == "nn.l2_pool2d":
+ node["attrs"]["count_include_pad"] = [["1" if count_include_pad else "0"]]
+
+ input = {
+ "op": "input",
+ "name": "",
+ "attrs": {"shape": [[list(shape)]], "dtype": [[dtype]]}}
+ return [input, node]
+
+
+def _get_expected_global_pooling_codegen(shape, dtype, typef):
+ node = {
+ "op": "kernel",
+ "name": typef,
+ "inputs": [[0, 0, 0]],
+ "attrs": {
+ "num_inputs": "1",
+ "num_outputs": "1",
+ "layout": [["NHWC"]],
+ "shape": [[[1, 1, 1, shape[3]]]],
+ "dtype": [[dtype]]
+ }
+ }
+
input = {
"op": "input",
"name": "",
device = Device()
np.random.seed(0)
- for dtype, low, high, atol, rtol in [("float32", -127, 128, 0.001, 0.001), ("uint8", 0, 255, 0, 0)]:
- for size in [(2, 2), (3, 3)]:
- for stride in [(2, 2)]:
- shape = (1, size[0] + stride[0] * 5,
- size[1] + stride[1] * 5, 16)
- pad = (0, 0)
-
- inputs = {
- "a": tvm.nd.array(np.random.uniform(low, high, shape).astype(dtype)),
- }
-
- outputs = []
- func = _get_model(shape, dtype, relay.nn.max_pool2d, size,
- stride, pad, True, iter(inputs))
- for acl in [False, True]:
- outputs.append(build_and_run(func, inputs, 1, None, device,
- enable_acl=acl)[0])
-
- params = {
- "size": size,
- "stride": stride,
- "shape": shape,
- "pooling type": "max",
- "dtype": dtype,
- "padding": pad
- }
- verify(outputs, atol=atol, rtol=rtol, params=params, verify_saturation=True)
+ fp32_dtype = ("float32", -127, 128, 0.001, 0.001)
+ uint8_dtype = ("uint8", 0, 255, 1, 0)
+
+ trials = [["nn.max_pool2d", fp32_dtype, (2, 2), (2, 2), (0, 0), False, True, (16, 16, 16)],
+ ["nn.max_pool2d", fp32_dtype, (3, 3), (2, 2), (1, 1), True, True, (15, 15, 16)],
+ ["nn.max_pool2d", fp32_dtype, (2, 2), (2, 2), (0, 1), False, False, (16, 16, 16)],
+ ["nn.max_pool2d", uint8_dtype, (3, 3), (2, 2), (0, 1), False, False, (16, 16, 16)],
+ ["nn.max_pool2d", uint8_dtype, (2, 2), (2, 2), (1, 1), True, True, (15, 15, 16)],
+ ["nn.avg_pool2d", fp32_dtype, (2, 2), (2, 2), (1, 1), False, False, (16, 16, 16)],
+ ["nn.avg_pool2d", fp32_dtype, (2, 2), (2, 2), (0, 0), False, True, (16, 16, 16)],
+ ["nn.avg_pool2d", fp32_dtype, (3, 3), (2, 2), (0, 1), True, False, (15, 15, 16)],
+ ["nn.avg_pool2d", uint8_dtype, (2, 2), (2, 2), (1, 1), False, True, (16, 16, 16)],
+ ["nn.avg_pool2d", uint8_dtype, (3, 3), (2, 2), (0, 1), False, False, (16, 16, 16)],
+ ["nn.l2_pool2d", fp32_dtype, (2, 2), (2, 2), (0, 1), True, False, (16, 16, 16)],
+ ["nn.l2_pool2d", fp32_dtype, (3, 3), (2, 2), (0, 0), False, False, (16, 16, 16)],
+ ["nn.l2_pool2d", fp32_dtype, (2, 2), (2, 2), (1, 1), False, True, (15, 15, 16)]]
+
+ for typef, (dtype, low, high, atol, rtol), size, stride, pad, ceil_mode, count_include_pad, \
+ input_shape in trials:
+ shape = (1, *input_shape)
+ outputs = []
+ inputs = {
+ "a": tvm.nd.array(np.random.uniform(low, high, shape).astype(dtype)),
+ }
+
+ func = _get_pooling_model(shape, dtype, typef, size,
+ stride, pad, ceil_mode, count_include_pad, iter(inputs))
+
+ config = {
+ "size": size,
+ "stride": stride,
+ "shape": shape,
+ "pooling type": typef,
+ "dtype": dtype,
+ "padding": pad,
+ "ceil_mode": ceil_mode,
+ "count_include_pad": count_include_pad
+ }
+ verify_saturation = True if dtype == "uint8" else False
+
+ for acl in [False, True]:
+ outputs.append(build_and_run(func, inputs, 1, None, device,
+ enable_acl=acl, config=config)[0])
+
+ verify(outputs, atol=atol, rtol=rtol, config=config, verify_saturation=verify_saturation)
+
+
+def test_global_pooling():
+ Device.load("test_config.json")
+
+ if skip_runtime_test():
+ return
+
+ device = Device()
+ np.random.seed(0)
+
+ fp32_dtype = ("float32", -127, 128, 0.001, 0.001)
+ uint8_dtype = ("uint8", 0, 255, 1, 0)
+
+ trials = [["nn.global_max_pool2d", fp32_dtype, (8, 8, 16)],
+ ["nn.global_max_pool2d", fp32_dtype, (9, 9, 16)],
+ ["nn.global_max_pool2d", fp32_dtype, (8, 8, 16)],
+ ["nn.global_max_pool2d", uint8_dtype, (8, 8, 16)],
+ ["nn.global_max_pool2d", uint8_dtype, (9, 9, 16)],
+ ["nn.global_avg_pool2d", fp32_dtype, (8, 8, 16)],
+ ["nn.global_avg_pool2d", fp32_dtype, (8, 8, 16)],
+ ["nn.global_avg_pool2d", fp32_dtype, (9, 9, 16)],
+ ["nn.global_avg_pool2d", uint8_dtype, (8, 8, 16)],
+ ["nn.global_avg_pool2d", uint8_dtype, (8, 8, 16)]]
+
+ for typef, (dtype, low, high, atol, rtol), input_shape in trials:
+ shape = (1, *input_shape)
+ outputs = []
+ inputs = {
+ "a": tvm.nd.array(np.random.uniform(low, high, shape).astype(dtype)),
+ }
+
+ func = _get_global_pooling_model(shape, dtype, typef, iter(inputs))
+
+ config = {
+ "shape": shape,
+ "pooling type": typef,
+ "dtype": dtype,
+ }
+ verify_saturation = True if dtype == "uint8" else False
+
+ for acl in [False, True]:
+ outputs.append(build_and_run(func, inputs, 1, None, device,
+ enable_acl=acl, config=config)[0])
+
+ verify(outputs, atol=atol, rtol=rtol, config=config, verify_saturation=verify_saturation)
def test_codegen_pooling():
if skip_codegen_test():
return
- inputs = {"a"}
+ fp32_dtype = ("float32", -127, 128)
+ uint8_dtype = ("uint8", 0, 255)
+
+ trials = [["nn.max_pool2d", fp32_dtype, (2, 2), (2, 2), (0, 0), False, True, (16, 16, 16)],
+ ["nn.max_pool2d", fp32_dtype, (3, 3), (2, 2), (1, 1), True, True, (15, 15, 16)],
+ ["nn.max_pool2d", fp32_dtype, (2, 2), (2, 2), (0, 1), False, False, (16, 16, 16)],
+ ["nn.max_pool2d", uint8_dtype, (3, 3), (2, 2), (0, 1), False, False, (16, 16, 16)],
+ ["nn.max_pool2d", uint8_dtype, (2, 2), (2, 2), (1, 1), True, True, (15, 15, 16)],
+ ["nn.avg_pool2d", fp32_dtype, (2, 2), (2, 2), (1, 1), False, False, (16, 16, 16)],
+ ["nn.avg_pool2d", fp32_dtype, (2, 2), (2, 2), (0, 0), False, True, (16, 16, 16)],
+ ["nn.avg_pool2d", fp32_dtype, (3, 3), (2, 2), (0, 1), True, False, (15, 15, 16)],
+ ["nn.avg_pool2d", uint8_dtype, (2, 2), (2, 2), (1, 1), False, True, (16, 16, 16)],
+ ["nn.avg_pool2d", uint8_dtype, (3, 3), (2, 2), (0, 1), False, False, (16, 16, 16)],
+ ["nn.l2_pool2d", fp32_dtype, (2, 2), (2, 2), (0, 1), True, False, (15, 15, 16)],
+ ["nn.l2_pool2d", fp32_dtype, (3, 3), (2, 2), (0, 0), False, False, (16, 16, 16)],
+ ["nn.l2_pool2d", fp32_dtype, (2, 2), (2, 2), (1, 1), False, True, (15, 15, 16)]]
+
+ for typef, (dtype, low, high), size, stride, pad, ceil_mode, count_include_pad, \
+ input_shape in trials:
+ shape = (1, *input_shape)
+ inputs = {"a"}
+ args = (shape, dtype, typef, size,
+ stride, pad, False, False)
+ func = _get_pooling_model(*args, iter(inputs))
+ exp_codegen = _get_expected_pooling_codegen(*args)
+ verify_codegen(func, exp_codegen, 1)
+
+
+def test_codegen_global_pooling():
+ if skip_codegen_test():
+ return
+
+ fp32_dtype = ("float32", -127, 128)
+ uint8_dtype = ("uint8", 0, 255)
+
+ trials = [["nn.global_max_pool2d", fp32_dtype, (8, 8, 16)],
+ ["nn.global_max_pool2d", fp32_dtype, (9, 9, 16)],
+ ["nn.global_max_pool2d", fp32_dtype, (8, 8, 16)],
+ ["nn.global_max_pool2d", uint8_dtype, (8, 8, 16)],
+ ["nn.global_max_pool2d", uint8_dtype, (9, 9, 16)],
+ ["nn.global_avg_pool2d", fp32_dtype, (8, 8, 16)],
+ ["nn.global_avg_pool2d", fp32_dtype, (8, 8, 16)],
+ ["nn.global_avg_pool2d", fp32_dtype, (9, 9, 16)],
+ ["nn.global_avg_pool2d", uint8_dtype, (8, 8, 16)],
+ ["nn.global_avg_pool2d", uint8_dtype, (8, 8, 16)]]
- for dtype in ["float32", "uint8"]:
- for size in [(2, 2), (3, 3)]:
- for stride in [(2, 2)]:
- shape = (1, size[0] + stride[0] * 5,
- size[1] + stride[1] * 5, 16)
- args = (shape, dtype, relay.nn.max_pool2d, size,
- stride, (0, 0), True)
- func = _get_model(*args, iter(inputs))
- exp_codegen = _get_expected_codegen(*args)
- verify_codegen(func, exp_codegen, 1)
+ for typef, (dtype, low, high), input_shape in trials:
+ shape = (1, *input_shape)
+ inputs = {"a"}
+ args = (shape, dtype, typef)
+ func = _get_global_pooling_model(*args, iter(inputs))
+ exp_codegen = _get_expected_global_pooling_codegen(*args)
+ verify_codegen(func, exp_codegen, 1)
if __name__ == "__main__":
test_pooling()
+ test_global_pooling()
test_codegen_pooling()
+ test_codegen_global_pooling()