MLBEDSW-2745 Support relus with differing scales
In the event we have a relu op with different input and output scales,
we need to fuse it with a nop avgpool.
Also refactor the existing avgpool nop code to a common function.
Signed-off-by: Michael McGeagh <michael.mcgeagh@arm.com>
Change-Id: Iedf4513e7595ee4ee1777ba0b1eb38a8df8aed5e
diff --git a/ethosu/vela/graph_optimiser.py b/ethosu/vela/graph_optimiser.py
index 4868405..1a6aaf1 100644
--- a/ethosu/vela/graph_optimiser.py
+++ b/ethosu/vela/graph_optimiser.py
@@ -28,6 +28,7 @@
from .errors import UnsupportedFeatureError
from .ethos_u55_regs.ethos_u55_regs import resampling_mode
from .numeric_util import full_shape
+from .operation import create_avgpool_nop
from .operation import NpuBlockType
from .operation import Operation
from .softmax import SoftMax
@@ -563,6 +564,25 @@
return op
+def fixup_relus_with_differing_ifm_ofm_scaling(op, arch):
+ if op.run_on_npu and op.type in relu_ops:
+ ifm = op.inputs[0]
+ ofm = op.outputs[0]
+ # Relu with differing IFM and OFM scaling cannot be fused with another primary op
+ # and requires its own to be inserted
+ if not ifm.is_scaling_equal(ofm):
+ # Override this op with its own primary op (avgpool)
+ relu_fused_op = create_avgpool_nop(op.name + "_avgpool")
+ # And fuse the original activation function to it
+ relu_fused_op.attrs["fused_activation_function"] = op.type
+ # Tidy up and assign the ifm and ofm to the new op
+ ifm.consumer_list.remove(op)
+ relu_fused_op.add_input_tensor(ifm)
+ relu_fused_op.set_output_tensor(ofm)
+ op = relu_fused_op
+ return op
+
+
# Reorder activation op if it's after the memory only operations
def fixup_act_reorder(op, arch):
if op.type in activation_ops:
@@ -929,6 +949,7 @@
fixup_fully_connected_input,
fixup_pack_input,
fixup_conv2d_backprop,
+ fixup_relus_with_differing_ifm_ofm_scaling,
fixup_act_reorder,
mark_npu_block_type,
fixup_elementwise_with_scalars,