From d06097ce448932dab725c40515d8797b00ab8eb2 Mon Sep 17 00:00:00 2001 From: George Gekov Date: Wed, 8 Jan 2025 11:51:04 +0000 Subject: [PATCH 1/2] Test the IOQuantization pass does not have Q/DQ nodes Change-Id: Ie3b466af5671f575d2b09e7775ce0b9a11920f2a --- backends/arm/README.md | 12 +++++ .../test/passes/test_ioquantization_pass.py | 50 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 backends/arm/test/passes/test_ioquantization_pass.py diff --git a/backends/arm/README.md b/backends/arm/README.md index 2079e8ddd8a..0c37d18f651 100644 --- a/backends/arm/README.md +++ b/backends/arm/README.md @@ -122,6 +122,18 @@ The you can run the tests with pytest -c /dev/null -v -n auto backends/arm/test --arm_run_corstoneFVP ``` +## Passes + +With the default passes in the Arm Ethos-U backend, assuming the model lowers fully to the +Ethos-U, the exported program is composed of a Quantize node, Ethos-U custom delegate +and a Dequantize node. In some circumstances, you may want to feed quantized input to the Neural +Network straight away, e.g. if you have a camera sensor outputting (u)int8 data and keep all the +arithmetic of the application in the int8 domain. For these cases, you can apply the +`exir/passes/quantize_io_pass.py`. See the unit test in `executorch/backends/arm/ +test/passes/test_ioquantization_pass.py`for an example how to feed quantized inputs and +obtain quantized outputs. + + ### Code coverage To get code coverage: diff --git a/backends/arm/test/passes/test_ioquantization_pass.py b/backends/arm/test/passes/test_ioquantization_pass.py new file mode 100644 index 00000000000..a0a718011a2 --- /dev/null +++ b/backends/arm/test/passes/test_ioquantization_pass.py @@ -0,0 +1,50 @@ +# Copyright 2025 Arm Limited and/or its affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +import unittest + +import torch + +from executorch.backends.arm.test import common + +from executorch.backends.arm.test.tester.arm_tester import ArmTester +from executorch.exir.passes.quantize_io_pass import QuantizeInputs, QuantizeOutputs + + +class SimpleModel(torch.nn.Module): + def forward(self, x, y): + return x + y + + def get_inputs(self): + a = torch.rand(1, 2, 2, 1) + b = torch.rand(1, 2, 2, 1) + return (a, b) + + +class TestIOQuantizationPass(unittest.TestCase): + """ + Test the executorch/exir/passes/quanize_io_pass pass works(meaning we don't get Q/DQ nodes) on a simple model + """ + + def test_ioquantisation_pass(self): + model = SimpleModel() + tester = ( + ArmTester( + model, + example_inputs=model.get_inputs(), + compile_spec=common.get_u55_compile_spec(), + ) + .quantize() + .export() + .to_edge() + .partition() + ) + edge = tester.get_artifact() + edge.transform( + passes=[QuantizeInputs(edge, [0, 1]), QuantizeOutputs(edge, [0])] + ) + tester.check_not(["edge__ops_quantized_decomposed_quantize_per_tensor"]) + tester.check_not(["edge__ops_quantized_decomposed_dequantize_per_tensor"]) From c3d20c4e6d253c0227f4a18eb2e95e22181f56bc Mon Sep 17 00:00:00 2001 From: George Gekov Date: Wed, 8 Jan 2025 11:51:04 +0000 Subject: [PATCH 2/2] Test the IOQuantization pass does not have Q/DQ nodes Change-Id: Ie3b466af5671f575d2b09e7775ce0b9a11920f2a --- backends/arm/README.md | 10 +++++----- .../test/passes/test_ioquantization_pass.py | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/backends/arm/README.md b/backends/arm/README.md index 0c37d18f651..e28559fb90d 100644 --- a/backends/arm/README.md +++ b/backends/arm/README.md @@ -124,13 +124,13 @@ pytest -c /dev/null -v -n auto backends/arm/test --arm_run_corstoneFVP ## Passes -With the default passes in the Arm Ethos-U backend, assuming the model lowers fully to the -Ethos-U, the exported program is composed of a Quantize node, Ethos-U custom delegate +With the default passes in the Arm Ethos-U backend, assuming the model lowers fully to the +Ethos-U, the exported program is composed of a Quantize node, Ethos-U custom delegate and a Dequantize node. In some circumstances, you may want to feed quantized input to the Neural -Network straight away, e.g. if you have a camera sensor outputting (u)int8 data and keep all the -arithmetic of the application in the int8 domain. For these cases, you can apply the +Network straight away, e.g. if you have a camera sensor outputting (u)int8 data and keep all the +arithmetic of the application in the int8 domain. For these cases, you can apply the `exir/passes/quantize_io_pass.py`. See the unit test in `executorch/backends/arm/ -test/passes/test_ioquantization_pass.py`for an example how to feed quantized inputs and +test/passes/test_ioquantization_pass.py`for an example how to feed quantized inputs and obtain quantized outputs. diff --git a/backends/arm/test/passes/test_ioquantization_pass.py b/backends/arm/test/passes/test_ioquantization_pass.py index a0a718011a2..e31007f1ed6 100644 --- a/backends/arm/test/passes/test_ioquantization_pass.py +++ b/backends/arm/test/passes/test_ioquantization_pass.py @@ -40,7 +40,27 @@ def test_ioquantisation_pass(self): .quantize() .export() .to_edge() + .check_count( + { + "executorch_exir_dialects_edge__ops_quantized_decomposed_quantize_per_tensor_default": 3 + } + ) + .check_count( + { + "executorch_exir_dialects_edge__ops_quantized_decomposed_dequantize_per_tensor_default": 3 + } + ) .partition() + .check_count( + { + "executorch_exir_dialects_edge__ops_quantized_decomposed_quantize_per_tensor_default": 2 + } + ) + .check_count( + { + "executorch_exir_dialects_edge__ops_quantized_decomposed_dequantize_per_tensor_default": 1 + } + ) ) edge = tester.get_artifact() edge.transform(