From c4a6ccea5a4cdf85baee5b0a988a5305149d9b57 Mon Sep 17 00:00:00 2001 From: Hendrik Borras <hendrikborras@web.de> Date: Wed, 13 Oct 2021 12:09:20 +0100 Subject: [PATCH] Moved RemoveIdentityOps transformation from FINN to finn-base. --- docker/Dockerfile.finn | 2 +- .../transformation/streamline/__init__.py | 2 +- src/finn/transformation/streamline/remove.py | 92 ------------------ tests/end2end/test_end2end_mobilenet_v1.py | 2 +- .../streamline/test_remove_identity_ops.py | 95 ------------------- 5 files changed, 3 insertions(+), 190 deletions(-) delete mode 100644 src/finn/transformation/streamline/remove.py delete mode 100644 tests/transformation/streamline/test_remove_identity_ops.py diff --git a/docker/Dockerfile.finn b/docker/Dockerfile.finn index 5a7a90ec4..1276a94a8 100644 --- a/docker/Dockerfile.finn +++ b/docker/Dockerfile.finn @@ -86,7 +86,7 @@ RUN pip install -e git+https://github.com/fbcotter/dataset_loading.git@0.0.4#egg # git-based Python repo dependencies # these are installed in editable mode for easier co-development -ARG FINN_BASE_COMMIT="17687a6a3371936147ceb4ed0d49f985a7ea9fde" +ARG FINN_BASE_COMMIT="7f13a67ba27c4f2c979bcbca26f49a5409b4b433" ARG QONNX_COMMIT="02b15f56d199576cefe9aff672d5e009349e402a" ARG FINN_EXP_COMMIT="f82c0d9868bb88ea045dfadb28508d327d287221" ARG BREVITAS_COMMIT="462f86cdc60f9915baf13afd1676fb21da44c2ee" diff --git a/src/finn/transformation/streamline/__init__.py b/src/finn/transformation/streamline/__init__.py index ea5475716..d0ec26a4d 100644 --- a/src/finn/transformation/streamline/__init__.py +++ b/src/finn/transformation/streamline/__init__.py @@ -39,6 +39,7 @@ from finn.transformation.general import ( GiveUniqueNodeNames, ) from finn.transformation.infer_datatypes import InferDataTypes +from finn.transformation.remove import RemoveIdentityOps from finn.transformation.streamline.absorb import ( Absorb1BitMulIntoConv, Absorb1BitMulIntoMatMul, @@ -51,7 +52,6 @@ from finn.transformation.streamline.collapse_repeated import ( CollapseRepeatedAdd, CollapseRepeatedMul, ) -from finn.transformation.streamline.remove import RemoveIdentityOps from finn.transformation.streamline.reorder import ( MoveAddPastConv, MoveAddPastMul, diff --git a/src/finn/transformation/streamline/remove.py b/src/finn/transformation/streamline/remove.py deleted file mode 100644 index 27e420a79..000000000 --- a/src/finn/transformation/streamline/remove.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) 2020, Xilinx -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# * Neither the name of FINN nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -import numpy as np - -from finn.transformation.base import Transformation -from finn.transformation.infer_shapes import InferShapes - - -def _remove_node_and_rewire(model, node): - producer = model.find_producer(node.input[0]) - if producer is not None: - # wire output tensor to - # output of producer node - producer.output[0] = node.output[0] - else: - # node is first in graph - consumer = model.find_consumer(node.output[0]) - assert consumer is not None, "Whole graph is identity" - assert consumer.input[0] == node.output[0] - # rewire consumer's input directly to graph input - consumer.input[0] = node.input[0] - # remove node - model.graph.node.remove(node) - - -class RemoveIdentityOps(Transformation): - """Remove identity ops like Add/Sub with zero or Mul/Div with one. A tolerance - value (defaults to 1e-05) can be specified during init for the comparison - to zero/one.""" - - def __init__(self, atol=1e-05): - super().__init__() - self.atol = atol - - def apply(self, model): - graph = model.graph - node_ind = 0 - graph_modified = False - for n in graph.node: - node_ind += 1 - if ( - n.op_type in ["Add", "Sub"] - and not model.is_fork_node(n) - and not model.is_join_node(n) - ): - A = model.get_initializer(n.input[1]) - if ( - A is not None - and np.isclose(A, np.zeros_like(A), atol=self.atol).all() - ): - _remove_node_and_rewire(model, n) - - elif ( - n.op_type in ["Mul", "Div"] - and not model.is_fork_node(n) - and not model.is_join_node(n) - ): - A = model.get_initializer(n.input[1]) - if ( - A is not None - and np.isclose(A, np.ones_like(A), atol=self.atol).all() - ): - _remove_node_and_rewire(model, n) - model = model.transform(InferShapes()) - return (model, graph_modified) diff --git a/tests/end2end/test_end2end_mobilenet_v1.py b/tests/end2end/test_end2end_mobilenet_v1.py index 760c77ea4..6af5019d7 100644 --- a/tests/end2end/test_end2end_mobilenet_v1.py +++ b/tests/end2end/test_end2end_mobilenet_v1.py @@ -62,9 +62,9 @@ from finn.transformation.infer_shapes import InferShapes from finn.transformation.insert_topk import InsertTopK from finn.transformation.lower_convs_to_matmul import LowerConvsToMatMul from finn.transformation.merge_onnx_models import MergeONNXModels +from finn.transformation.remove import RemoveIdentityOps from finn.transformation.streamline import Streamline from finn.transformation.streamline.collapse_repeated import CollapseRepeatedMul -from finn.transformation.streamline.remove import RemoveIdentityOps from finn.transformation.streamline.round_thresholds import RoundAndClipThresholds from finn.util.basic import alveo_default_platform, alveo_part_map from finn.util.pytorch import NormalizePreProc diff --git a/tests/transformation/streamline/test_remove_identity_ops.py b/tests/transformation/streamline/test_remove_identity_ops.py deleted file mode 100644 index ad7c20fb5..000000000 --- a/tests/transformation/streamline/test_remove_identity_ops.py +++ /dev/null @@ -1,95 +0,0 @@ -import pytest - -import numpy as np -from onnx import TensorProto, helper - -import finn.core.onnx_exec as oxe -from finn.core.datatype import DataType -from finn.core.modelwrapper import ModelWrapper -from finn.transformation.infer_datatypes import InferDataTypes -from finn.transformation.infer_shapes import InferShapes -from finn.transformation.streamline.remove import RemoveIdentityOps -from finn.util.basic import gen_finn_dt_tensor - - -def insert_identity_op(model, op, as_first_node, approx): - if approx: - zero_val = 0.000001 - one_val = 0.999999 - else: - zero_val = 0.0 - one_val = 1.0 - if op in ["Add", "Sub"]: - val = np.asarray([zero_val], dtype=np.float32) - elif op in ["Mul", "Div"]: - val = np.asarray([one_val], dtype=np.float32) - else: - return - - graph = model.graph - if as_first_node: - identity_node = helper.make_node(op, ["inp", "value"], ["ident_out"]) - graph.node.insert(0, identity_node) - graph.node[1].input[0] = "ident_out" - else: - identity_node = helper.make_node(op, ["div_out", "value"], ["ident_out"]) - graph.node.insert(3, identity_node) - graph.node[-1].input[0] = "ident_out" - model.set_initializer("value", val) - - return model - - -# identity operations to be inserted -@pytest.mark.parametrize("op", ["Add", "Sub", "Mul", "Div"]) -@pytest.mark.parametrize("approx", [False, True]) -@pytest.mark.parametrize("as_first_node", [False, True]) -def test_remove_identity_ops(op, as_first_node, approx): - - # set up onnx model - inp = helper.make_tensor_value_info("inp", TensorProto.FLOAT, [1, 4, 1, 1]) - mul = helper.make_tensor_value_info("mul", TensorProto.FLOAT, []) - shape = helper.make_tensor_value_info("shape", TensorProto.FLOAT, [2]) - div = helper.make_tensor_value_info("div", TensorProto.FLOAT, []) - matmul = helper.make_tensor_value_info("matmul", TensorProto.FLOAT, [4, 2]) - outp = helper.make_tensor_value_info("outp", TensorProto.FLOAT, [1, 2]) - - mul_node = helper.make_node("Mul", ["inp", "mul"], ["mul_out"]) - reshape_node = helper.make_node("Reshape", ["mul_out", "shape"], ["reshape_out"]) - div_node = helper.make_node("Div", ["reshape_out", "div"], ["div_out"]) - matmul_node = helper.make_node("MatMul", ["div_out", "matmul"], ["outp"]) - - graph = helper.make_graph( - nodes=[mul_node, reshape_node, div_node, matmul_node], - name="identity-graph", - inputs=[inp], - outputs=[outp], - value_info=[mul, shape, div, matmul], - ) - - model = helper.make_model(graph, producer_name="mulpastconv-model") - model = ModelWrapper(model) - inp_values = gen_finn_dt_tensor(DataType.INT2, [1, 4, 1, 1]) - mul_values = np.random.uniform(low=0.1, high=0.99, size=(1)).astype(np.float32) - shape_values = np.asarray([1, -1], dtype=np.int64) - div_values = np.random.uniform(low=0.1, high=0.99, size=(1)).astype(np.float32) - matmul_values = gen_finn_dt_tensor(DataType.INT2, [4, 2]) - model.set_initializer("mul", mul_values) - model.set_initializer("shape", shape_values) - model.set_initializer("div", div_values) - model.set_initializer("matmul", matmul_values) - insert_identity_op(model, op, as_first_node, approx) - model = model.transform(InferShapes()) - model = model.transform(InferDataTypes()) - idict = {"inp": inp_values} - odict = oxe.execute_onnx(model, idict) - out_before = odict["outp"] - num_of_nodes_before = len(model.graph.node) - - model = model.transform(RemoveIdentityOps()) - num_of_nodes_after = len(model.graph.node) - assert num_of_nodes_before - 1 == num_of_nodes_after - - odict = oxe.execute_onnx(model, idict) - out_after = odict["outp"] - assert np.isclose(out_before, out_after, atol=1e-3).all() -- GitLab