do not quantize Relu/Clip if their inputs are not quantized (#12565)

This commit is contained in:
Yufeng Li 2022-08-11 16:16:10 -07:00 committed by GitHub
parent 67f6b7ce29
commit 95df5dac51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 11 deletions

View file

@ -103,6 +103,10 @@ class QDQRemovableActivation(QDQOperatorBase):
def quantize(self):
node = self.node
# If input to this node is not quantized then keep this node
if not self.quantizer.is_tensor_quantized(node.input[0]):
return
if not self.quantizer.is_activation_symmetric and self.quantizer.try_replacing_upstream_output(
node.input[0], node.output[0]
):

View file

@ -1,3 +1,4 @@
import uuid
from pathlib import Path
import numpy as np
@ -118,3 +119,14 @@ def check_qtype_by_node_type(testcase, model_to_check, check_list):
else: # if (tensor_name in initializers):
init = initializers[tensor_name]
testcase.assertTrue(init.data_type == check_item[2])
def create_clip_node(input_name, output_name, node_name, initializers, min_value=-1.0, max_value=1.0):
clip_min_name = str(uuid.uuid4())
clip_max_name = str(uuid.uuid4())
clip_inputs = [input_name, clip_min_name, clip_max_name]
clip_outputs = [output_name]
clip_name = node_name
initializers.append(onnx.numpy_helper.from_array(np.array(min_value, dtype=np.float32), name=clip_min_name))
initializers.append(onnx.numpy_helper.from_array(np.array(max_value, dtype=np.float32), name=clip_max_name))
return onnx.helper.make_node("Clip", clip_inputs, clip_outputs, name=clip_name)

View file

@ -13,7 +13,13 @@ from pathlib import Path
import numpy as np
import onnx
from onnx import TensorProto, helper, numpy_helper
from op_test_utils import TestDataFeeds, check_model_correctness, check_op_type_count, check_op_type_order
from op_test_utils import (
TestDataFeeds,
check_model_correctness,
check_op_type_count,
check_op_type_order,
create_clip_node,
)
from onnxruntime.quantization import QDQQuantizer, QuantFormat, QuantizationMode, QuantType, quantize_static
@ -348,14 +354,7 @@ class TestQDQFormatConvClip(TestQDQFormat):
conv_node = onnx.helper.make_node("Conv", conv_inputs, conv_outputs, name=conv_name)
# make Clip node
clip_min_name = "clip_min"
clip_max_name = "clip_max"
clip_inputs = [conv_outputs[0], clip_min_name, clip_max_name]
clip_outputs = ["clip_output"]
clip_name = "clip_node"
initializers.append(onnx.numpy_helper.from_array(np.array(-1.0, dtype=np.float32), name=clip_min_name))
initializers.append(onnx.numpy_helper.from_array(np.array(1.0, dtype=np.float32), name=clip_max_name))
clip_node = onnx.helper.make_node("Clip", clip_inputs, clip_outputs, name=clip_name)
clip_node = create_clip_node(conv_outputs[0], "clip_output", "clip_node", initializers)
# make Identity node
reshape_name = "reshape_node"
@ -530,13 +529,13 @@ class TestQDQFormatConvRelu(TestQDQFormat):
initializers.append(onnx.numpy_helper.from_array(conv_weight_data, name=weight_name))
conv_node = onnx.helper.make_node("Conv", conv_inputs, conv_outputs, name=conv_name)
# make Clip node
# make Relu node
relu_node = onnx.helper.make_node("Relu", conv_outputs, [output_name], name="Relu")
# make graph
input_tensor = helper.make_tensor_value_info(input_name, TensorProto.FLOAT, input_shape)
output_tensor = helper.make_tensor_value_info(output_name, TensorProto.FLOAT, output_shape)
graph_name = "QDQ_Test_Conv_clip"
graph_name = "QDQ_Test_Conv_Relu"
graph = helper.make_graph(
[conv_node, relu_node],
graph_name,
@ -623,5 +622,61 @@ class TestQDQFormatConvRelu(TestQDQFormat):
)
class TestQDQRemovableActivation(TestQDQFormat):
@classmethod
def setUpClass(cls):
cls._tmp_model_dir = tempfile.TemporaryDirectory(prefix="ort.quant.activation")
@classmethod
def tearDownClass(cls):
cls._tmp_model_dir.cleanup()
def construct_model_clip_relu(self, output_model_path, input_shape, output_shape):
# (input)
# |
# Clip
# |
# Relu
# |
# (output)
input_name = "input"
output_name = "output"
initializers = []
# make Clip node
clip_output_name = "clip_output"
clip_node = create_clip_node(input_name, clip_output_name, "clip_node", initializers)
# make Relu node
relu_node = onnx.helper.make_node("Relu", [clip_output_name], [output_name], name="Relu")
# make graph
input_tensor = helper.make_tensor_value_info(input_name, TensorProto.FLOAT, input_shape)
output_tensor = helper.make_tensor_value_info(output_name, TensorProto.FLOAT, output_shape)
graph_name = "QDQ_Test_Clip_Relu"
graph = helper.make_graph(
[clip_node, relu_node],
graph_name,
[input_tensor],
[output_tensor],
initializer=initializers,
)
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
model.ir_version = 7 # use stable onnx ir version
onnx.save(model, output_model_path)
def test_activation_only(self):
float_model_path = str(Path(self._tmp_model_dir.name) / "float_relu_convs_model.onnx")
self.construct_model_clip_relu(float_model_path, [1, 3, 1, 3], [1, 3, 1, 3])
data_reader = self.input_feeds(2, {"input": [1, 3, 1, 3]})
qdq_model_path = str(Path(self._tmp_model_dir.name) / "qdq_relu_convs_model.onnx")
quantize_static(float_model_path, qdq_model_path, data_reader)
qop_nodes = {"Clip": 1, "Relu": 1, "QuantizeLinear": 0, "DequantizeLinear": 0}
check_op_type_count(self, qdq_model_path, **qop_nodes)
if __name__ == "__main__":
unittest.main()