This is an automated email from the ASF dual-hosted git repository.

lukhut pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new e51ba294d9 [ACL] Prevent offloading of per-channel quantized operators 
(#14484)
e51ba294d9 is described below

commit e51ba294d99c93ccbed064ed1fe35b90b1195140
Author: Nicola Lancellotti <[email protected]>
AuthorDate: Wed Apr 5 15:32:46 2023 +0100

    [ACL] Prevent offloading of per-channel quantized operators (#14484)
    
    Currently, only per-layer quantization is supported in the
    Arm Compute Library runtime. However, there is no check that
    prevents the offloading of per-channel quantized operators,
    as a consequence, tvm fails during inference if such operators
    are found.
---
 python/tvm/relay/op/contrib/arm_compute_lib.py     | 24 ++++++++++-
 .../contrib/test_arm_compute_lib/test_add.py       | 31 +++++++++++++-
 .../contrib/test_arm_compute_lib/test_conv2d.py    | 50 ++++++++++++++++++++++
 .../contrib/test_arm_compute_lib/test_dense.py     | 43 +++++++++++++++++++
 4 files changed, 146 insertions(+), 2 deletions(-)

diff --git a/python/tvm/relay/op/contrib/arm_compute_lib.py 
b/python/tvm/relay/op/contrib/arm_compute_lib.py
index 1b9abb0948..6b8d000c66 100644
--- a/python/tvm/relay/op/contrib/arm_compute_lib.py
+++ b/python/tvm/relay/op/contrib/arm_compute_lib.py
@@ -359,6 +359,10 @@ def qnn_conv2d(expr):
     kernel_typ = args[1].checked_type
     if len(kernel_typ.shape) != 4 or kernel_typ.dtype not in qnn_dtypes:
         return False
+    if is_per_channel_quantization(
+        zero_point=args[2], scale=args[4]
+    ) or is_per_channel_quantization(zero_point=args[3], scale=args[5]):
+        return False
     is_depthwise = is_depthwise_conv2d(
         data_typ.shape,
         attrs["data_layout"],
@@ -422,6 +426,10 @@ def qnn_dense(expr):
         return False
     if attrs.out_dtype != "int32":
         return False
+    if is_per_channel_quantization(
+        zero_point=args[2], scale=args[4]
+    ) or is_per_channel_quantization(zero_point=args[3], scale=args[5]):
+        return False
     return True
 
 
@@ -514,10 +522,24 @@ def qnn_add(expr):
     for typ in [args[0].checked_type, args[1].checked_type]:
         if typ.dtype not in ["int8", "uint8"]:
             return False
-
+    if (
+        is_per_channel_quantization(zero_point=args[3], scale=args[2])
+        or is_per_channel_quantization(zero_point=args[5], scale=args[4])
+        or is_per_channel_quantization(zero_point=args[7], scale=args[6])
+    ):
+        return False
     return True
 
 
+def is_per_channel_quantization(zero_point, scale):
+    """Check if the quantization is per-channel"""
+    for value in [zero_point, scale]:
+        shape = value.checked_type.shape
+        if len(shape) != 0 and shape[0] != 1:
+            return True
+    return False
+
+
 class OpAttrContext(object):
     """Temporarily changes the attr of an op."""
 
diff --git a/tests/python/contrib/test_arm_compute_lib/test_add.py 
b/tests/python/contrib/test_arm_compute_lib/test_add.py
index ee6fcf603c..319105bb5f 100644
--- a/tests/python/contrib/test_arm_compute_lib/test_add.py
+++ b/tests/python/contrib/test_arm_compute_lib/test_add.py
@@ -17,6 +17,7 @@
 """Arm Compute Library integration reshape tests."""
 
 import numpy as np
+import pytest
 
 import tvm
 import tvm.testing
@@ -134,6 +135,34 @@ def test_codegen_add():
             verify_codegen(func, exp_codegen, 1)
 
 
[email protected](
+    "param, param_type",
+    [
+        ("lhs_scale", "float32"),
+        ("lhs_zero_point", "int32"),
+        ("rhs_scale", "float32"),
+        ("rhs_zero_point", "int32"),
+    ],
+)
+def test_codegen_add_per_channel_quantization(param, param_type):
+    if skip_codegen_test():
+        return
+
+    qnn_params = _qnn_params
+    qnn_params[param] = relay.const([1, 2], param_type)
+
+    dtype = "int8"
+    op_name = "qnn.add"
+    op = relay.qnn.op.add
+    inputs = {"a", "b"}
+
+    for shape in [(1, 3, 3, 2)]:
+        func = _get_model(shape, dtype, iter(inputs), op, qnn_params)
+        exp_codegen = _get_expected_codegen(shape, dtype, op_name, qnn_params)
+        verify_codegen(func, exp_codegen, num_acl_modules=0, tvm_ops=1)
+
+
 if __name__ == "__main__":
-    test_codegen_add()
     test_runtime_add()
+    test_codegen_add()
+    test_codegen_add_per_channel_quantization()
diff --git a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py 
b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py
index df708020bf..b4fa49ffa2 100644
--- a/tests/python/contrib/test_arm_compute_lib/test_conv2d.py
+++ b/tests/python/contrib/test_arm_compute_lib/test_conv2d.py
@@ -615,8 +615,58 @@ def test_codegen_qnn_conv2d(trial, dtype):
     verify_codegen(func, exp_codegen, 1)
 
 
[email protected](
+    "param",
+    ["kernel_sc", "kernel_zp"],
+)
+def test_codegen_qnn_conv2d_per_channel_quantization(param):
+    if skip_codegen_test():
+        return
+
+    dtype = "int8"
+    kernel_h = 2
+    kernel_w = 2
+    pad = (1, 1)
+    stride = (1, 1)
+    dilation = (1, 1)
+    out_channels = 4
+    shape = (1, 10, 10, 14)
+    composite = (False, False, False)
+    groups = 1
+    inputs = {"a"}
+
+    qnn_params = {
+        "input_zp": 1,
+        "input_sc": 1,
+        "kernel_zp": 1,
+        "kernel_sc": 1,
+        "output_zp": 1,
+        "output_sc": 1,
+    }
+    qnn_params[param] = [1, 1, 1, 1]
+
+    args = (shape, kernel_h, kernel_w, pad, stride, dilation, groups, dtype, 
out_channels)
+
+    func, params = _get_qnn_model(
+        *args,
+        input_zp=qnn_params["input_zp"],
+        input_sc=qnn_params["input_sc"],
+        kernel_zp=qnn_params["kernel_zp"],
+        kernel_sc=qnn_params["kernel_sc"],
+        output_zp=qnn_params["output_zp"],
+        output_sc=qnn_params["output_sc"],
+        var_names=iter(inputs),
+        has_pad=composite[0],
+        has_bias=composite[1],
+        has_activation=composite[2],
+    )
+    exp_codegen = _get_expected_codegen(*args, has_bias=composite[1], 
has_activation=composite[2])
+    verify_codegen(func, exp_codegen, num_acl_modules=0, tvm_ops=2)
+
+
 if __name__ == "__main__":
     test_conv2d()
     test_qnn_conv2d()
     test_codegen_conv2d()
     test_codegen_qnn_conv2d()
+    test_codegen_qnn_conv2d_per_channel_quantization()
diff --git a/tests/python/contrib/test_arm_compute_lib/test_dense.py 
b/tests/python/contrib/test_arm_compute_lib/test_dense.py
index bbcfc4abe6..411f790f34 100644
--- a/tests/python/contrib/test_arm_compute_lib/test_dense.py
+++ b/tests/python/contrib/test_arm_compute_lib/test_dense.py
@@ -380,8 +380,51 @@ def test_codegen_qnn_dense(dtype):
         verify_codegen(func, exp_codegen)
 
 
[email protected](
+    "param",
+    ["kernel_sc", "kernel_zp"],
+)
+def test_codegen_qnn_dense_per_channel_quantization(param):
+    if skip_codegen_test():
+        return
+
+    np.random.seed(0)
+    dtype = "int8"
+    shape = (1, 2)
+    weight_shape = (2, 2)
+    units = 2
+    composite = True
+    inputs = {"a"}
+    args = (shape, weight_shape, units, dtype)
+
+    qnn_params = {
+        "input_zp": 1,
+        "input_sc": 1,
+        "kernel_zp": 1,
+        "kernel_sc": 1,
+        "output_zp": 1,
+        "output_sc": 1,
+    }
+    qnn_params[param] = [1, 1]
+
+    func, _ = _get_qnn_model(
+        *args,
+        var_names=iter(inputs),
+        input_zp=qnn_params["input_zp"],
+        input_sc=qnn_params["input_sc"],
+        kernel_zp=qnn_params["kernel_zp"],
+        kernel_sc=qnn_params["kernel_sc"],
+        output_zp=qnn_params["output_zp"],
+        output_sc=qnn_params["output_sc"],
+        has_bias=composite,
+    )
+    exp_codegen = _get_expected_codegen(*args, has_bias=composite)
+    verify_codegen(func, exp_codegen, num_acl_modules=0, tvm_ops=3)
+
+
 if __name__ == "__main__":
     test_dense()
     test_qnn_dense()
     test_codegen_dense()
     test_codegen_qnn_dense()
+    test_codegen_qnn_dense_per_channel_quantization()

Reply via email to