mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-30 20:18:08 +00:00
do not quantize Relu/Clip if their inputs are not quantized (#12565)
This commit is contained in:
parent
67f6b7ce29
commit
95df5dac51
3 changed files with 82 additions and 11 deletions
|
|
@ -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]
|
||||
):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue