From 3ec04ac9e38d26193e0081a8e0fa3b8b667bb688 Mon Sep 17 00:00:00 2001 From: Dwight Lidman Date: Thu, 30 Apr 2020 11:54:48 +0200 Subject: MLBEDSW-1498: Add Resize_Bilinear operator support This patch adds support for the ResizeBilinear operator. It is implemented using a 2x2 Nearest Neighbor upscale followed by a 2x2 Average Pool. Depending on the argument align_corners the output is either of shape: - (2 * M, 2 * N) when align_corners == True, or - (2 * M - 1, 2 * N - 1) when align_corners == False where (M, N) is the input shape. The padding mode is SAME when align_corners == True and VALID when align_corners == False. The argument half_pixel_centers is out of scope and is as of now ignored. Note that only upscaling by a factor of 2 is supported. Change-Id: Ia6d6d010c4f1bb13f5f839bc8d16872a626d9a3b Signed-off-by: Dwight Lidman --- ethosu/vela/graph_optimiser.py | 4 ++-- ethosu/vela/pass_packing.py | 2 ++ ethosu/vela/register_command_stream_generator.py | 10 ++++++++-- ethosu/vela/supported_operators.py | 2 ++ ethosu/vela/tflite_reader.py | 16 ++++++++++++++++ 5 files changed, 30 insertions(+), 4 deletions(-) (limited to 'ethosu/vela') diff --git a/ethosu/vela/graph_optimiser.py b/ethosu/vela/graph_optimiser.py index b29a3823..fdd6fc61 100644 --- a/ethosu/vela/graph_optimiser.py +++ b/ethosu/vela/graph_optimiser.py @@ -283,7 +283,7 @@ def add_padding_fields(op, arch): if "Conv" in op.type: kernel_size = op.inputs[1].shape[:2] input_shape = op.inputs[0].shape - elif "Pool" in op.type: + elif "Pool" in op.type or "ResizeBilinear" == op.type: kernel_size = op.attrs["ksize"][1:3] input_shape = op.inputs[0].shape elif op.type == "ExtractImagePatches": @@ -314,7 +314,7 @@ fc_op = set( ) ) depthwise_op = set(("DepthwiseConv2dNative", "DepthwiseConv2dBiasAct",)) -pool_op = set(("AvgPool", "MaxPool", "QuantizedAvgPool", "QuantizedMaxPool", "AvgPoolAct", "MaxPoolAct")) +pool_op = set(("AvgPool", "MaxPool", "QuantizedAvgPool", "QuantizedMaxPool", "AvgPoolAct", "MaxPoolAct", "ResizeBilinear",)) elementwise_op = set(("AddAct", "MulAct", "SubAct", "Maximum", "Minimum", "LeakyRelu", "Abs")) activation_ops = set(("Relu", "Relu6", "ReluN1To1", "Sigmoid", "Tanh")) memory_only_ops = set(("Reshape",)) diff --git a/ethosu/vela/pass_packing.py b/ethosu/vela/pass_packing.py index bae81517..1ad5b4f7 100644 --- a/ethosu/vela/pass_packing.py +++ b/ethosu/vela/pass_packing.py @@ -67,6 +67,8 @@ mac_main_ops = set( "MaxPool", "AvgPoolAct", "MaxPoolAct", + # deconvolution + "ResizeBilinear", ) ) diff --git a/ethosu/vela/register_command_stream_generator.py b/ethosu/vela/register_command_stream_generator.py index 460cf016..7a4faa80 100644 --- a/ethosu/vela/register_command_stream_generator.py +++ b/ethosu/vela/register_command_stream_generator.py @@ -401,6 +401,8 @@ def generate_register_command_stream(nng, sg, arch, verbose=False): use_global_scale = False # Specifies type of rounding to be used. rounding_mode = rounding.TFL + if primary_op.type == 'ResizeBilinear': + rounding_mode = rounding.TRUNCATE fmf = primary_op.attrs.get("fused_memory_function", None) faf = primary_op.attrs.get("fused_activation_function", None) @@ -537,7 +539,11 @@ def generate_register_command_stream(nng, sg, arch, verbose=False): emit.cmd0_with_param(cmd0.NPU_SET_ACC_FORMAT, acc_format_map[shared_buffer.use_accumulator_element]) - emit.cmd0_with_param(cmd0.NPU_SET_IFM_UPSCALE, 0) + if primary_op.type == 'ResizeBilinear': + # perform nearest neighbor upscale + emit.cmd0_with_param(cmd0.NPU_SET_IFM_UPSCALE, 1) + else: + emit.cmd0_with_param(cmd0.NPU_SET_IFM_UPSCALE, 0) if npu_block_type in set( (NpuBlockType.ConvolutionMxN, NpuBlockType.ConvolutionDepthWise, NpuBlockType.Pooling) @@ -579,7 +585,7 @@ def generate_register_command_stream(nng, sg, arch, verbose=False): valid_padding = sum(explicit_padding) == 0 - if primary_op.type in set(("AvgPool", "AvgPoolAct")) and valid_padding: + if primary_op.type in set(("AvgPool", "AvgPoolAct", "ResizeBilinear")) and valid_padding: # For valid padding vela has to output scaling values if faf == "Sigmoid" or faf == "Tanh": rescale = 0x3000 * cmd.ifm_tensor.quantization.scale_f32 diff --git a/ethosu/vela/supported_operators.py b/ethosu/vela/supported_operators.py index 1a25887f..7334fe25 100644 --- a/ethosu/vela/supported_operators.py +++ b/ethosu/vela/supported_operators.py @@ -44,6 +44,8 @@ class SupportedOperators: | self.fc_vector_products # RNN/LSTM/GRU | set(("BlockLSTM")) + # deconvolution + | set(("ResizeBilinear",)) ) self.unary_elem_wise_main_ops = set(("LeakyRelu", "Abs")) self.binary_elem_wise_main_ops = set( diff --git a/ethosu/vela/tflite_reader.py b/ethosu/vela/tflite_reader.py index 4456d5a0..aa0ec4d8 100644 --- a/ethosu/vela/tflite_reader.py +++ b/ethosu/vela/tflite_reader.py @@ -156,6 +156,22 @@ class TFLiteSubgraph: if opt_serializer is not None: op.attrs = opt_serializer.deserialize(op_data.BuiltinOptions(), op_data.CustomOptionsAsNumpy()) + if op_type.startswith("ResizeBilinear"): + upscaled_shape = [op.inputs[0].shape[1] * 2, op.inputs[0].shape[2] * 2] + out_shape = op.outputs[0].shape[1:3] + if not op.attrs['align_corners'] and out_shape == upscaled_shape: + # this means the output is supposed to be a x2 upscale, + # so we need to do SAME padding + op.attrs.update({'padding': b'SAME'}) + elif (op.attrs['align_corners'] + and out_shape == [upscaled_shape[0] - 1, upscaled_shape[1] - 1]): + # here we can just run the avg pool without padding and + # produce a (M * 2 - 1, N * 2 - 1) sized output + op.attrs.update({'padding': b'VALID'}) + else: + assert False, "Only 2x upscaling is supported" + op.attrs.update({'filter_width': 2, 'filter_height': 2, 'stride_w': 1, 'stride_h': 1,}) + if "stride_w" in op.attrs: op.attrs["strides"] = (1, op.attrs["stride_h"], op.attrs["stride_w"], 1) if "filter_width" in op.attrs: -- cgit v1.2.1