mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-30 20:18:08 +00:00
fix topo sort in quantization tool (#16003)
### Description
<!-- Describe your changes. -->
Should not set up dependent node list for empty('') input
### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->
This commit is contained in:
parent
ea7b2deffd
commit
0fed00c04d
2 changed files with 132 additions and 78 deletions
|
|
@ -414,6 +414,8 @@ class ONNXModel:
|
|||
continue
|
||||
|
||||
for input_name in node.input:
|
||||
if not input_name:
|
||||
continue
|
||||
if input_name not in deps_to_nodes:
|
||||
deps_to_nodes[input_name] = [node_idx]
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -5,14 +5,15 @@
|
|||
# license information.
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
import tempfile
|
||||
import unittest
|
||||
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 # noqa: F401
|
||||
from op_test_utils import check_op_type_order
|
||||
|
||||
import onnxruntime # noqa: F401
|
||||
from onnxruntime.quantization.onnx_model import ONNXModel
|
||||
|
||||
|
||||
|
|
@ -25,97 +26,148 @@ def generate_input_initializer(tensor_shape, tensor_dtype, input_name):
|
|||
return init
|
||||
|
||||
|
||||
def construct_model_for_topo_sort(model_path):
|
||||
# (input)
|
||||
# |
|
||||
# GRU
|
||||
# / \
|
||||
# Conv(1) \
|
||||
# | \
|
||||
# Relu Conv(2)
|
||||
# | |
|
||||
# \ /
|
||||
# Add
|
||||
# |
|
||||
# (output)
|
||||
initializers = []
|
||||
input = helper.make_tensor_value_info("input", TensorProto.FLOAT, [4, 8, 12])
|
||||
output = helper.make_tensor_value_info("output", TensorProto.FLOAT, [4, 2, 8, 8])
|
||||
|
||||
# make GRU
|
||||
initializers.append(generate_input_initializer([2, 24, 12], np.float32, "W_GRU"))
|
||||
initializers.append(generate_input_initializer([2, 24, 8], np.float32, "R_GRU"))
|
||||
initializers.append(generate_input_initializer([2, 8, 8], np.float32, "H_GRU"))
|
||||
gru_node = helper.make_node(
|
||||
"GRU",
|
||||
["input", "W_GRU", "R_GRU", "", "", "H_GRU"],
|
||||
["GRU_O"],
|
||||
hidden_size=8,
|
||||
direction="bidirectional",
|
||||
)
|
||||
|
||||
initializers.append(generate_input_initializer([2, 2, 1, 1], np.float32, "W1"))
|
||||
initializers.append(generate_input_initializer([2, 2, 1, 1], np.float32, "W2"))
|
||||
initializers.append(generate_input_initializer([2], np.float32, "B1"))
|
||||
initializers.append(generate_input_initializer([2], np.float32, "B2"))
|
||||
conv_node_1 = helper.make_node("Conv", ["GRU_O", "W1", "B1"], ["Conv1_O"], name="Conv1")
|
||||
conv_node_2 = helper.make_node("Conv", ["GRU_O", "W2", "B2"], ["Conv2_O"], name="Conv2")
|
||||
relu_node = helper.make_node("Relu", ["Conv1_O"], ["Relu_O"], name="Relu")
|
||||
add_node = helper.make_node("Add", ["Relu_O", "Conv2_O"], ["output"], name="Add")
|
||||
graph = helper.make_graph(
|
||||
[conv_node_1, relu_node, conv_node_2, gru_node, add_node],
|
||||
"onnx_model_test",
|
||||
[input],
|
||||
[output],
|
||||
initializer=initializers,
|
||||
)
|
||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||
onnx.save(model, model_path)
|
||||
|
||||
|
||||
def construct_model_for_topo_sort_constant(model_path):
|
||||
# (input) Constant
|
||||
# \ /
|
||||
# \ /
|
||||
# \ /
|
||||
# \ /
|
||||
# Add
|
||||
# |
|
||||
# (output)
|
||||
|
||||
initializers = []
|
||||
input = helper.make_tensor_value_info("input", TensorProto.FLOAT, [4, 8, 12])
|
||||
output = helper.make_tensor_value_info("output", TensorProto.FLOAT, [4, 8, 12])
|
||||
|
||||
# make nodes
|
||||
constant_node = helper.make_node("Constant", [], ["const_output"], value_float=42.0)
|
||||
add_node = helper.make_node("Add", ["input", "const_output"], ["output"], name="Add")
|
||||
graph = helper.make_graph(
|
||||
[add_node, constant_node],
|
||||
"onnx_model_test",
|
||||
[input],
|
||||
[output],
|
||||
initializer=initializers,
|
||||
)
|
||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||
onnx.save(model, model_path)
|
||||
|
||||
|
||||
def construct_model_for_topo_sort_empty_input_output(model_path):
|
||||
# (input1) (input2)
|
||||
# | |
|
||||
# Op1 Op1
|
||||
# \ /
|
||||
# \ /
|
||||
# \ /
|
||||
# \ /
|
||||
# Op2
|
||||
# |
|
||||
# Op3
|
||||
# |
|
||||
# (output)
|
||||
|
||||
input1 = helper.make_tensor_value_info("input1", TensorProto.FLOAT, [4, 8, 12])
|
||||
input2 = helper.make_tensor_value_info("input2", TensorProto.FLOAT, [4, 8, 12])
|
||||
output = helper.make_tensor_value_info("output", TensorProto.FLOAT, [4, 8, 12])
|
||||
|
||||
# make nodes
|
||||
op1_node_1 = helper.make_node("Op1", ["input1"], ["", "", "Op1_1_output"], name="op1_1", domain="Test")
|
||||
op1_node_2 = helper.make_node("Op1", ["input2"], ["", "", "Op1_2_output"], name="op1_2", domain="Test")
|
||||
op2_node = helper.make_node("Op2", ["Op1_1_output", "Op1_2_output"], ["op2_output"], name="op2", domain="Test")
|
||||
op3_node = helper.make_node("Op3", ["", "op2_output"], ["output"], name="op3", domain="Test")
|
||||
graph = helper.make_graph(
|
||||
[op1_node_1, op1_node_2, op3_node, op2_node],
|
||||
"onnx_model_topo_test",
|
||||
[input1, input2],
|
||||
[output],
|
||||
)
|
||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("Test", 1), helper.make_opsetid("", 13)])
|
||||
onnx.save(model, model_path)
|
||||
|
||||
|
||||
class TestONNXModel(unittest.TestCase):
|
||||
def construct_model(self, model_path):
|
||||
# (input)
|
||||
# |
|
||||
# GRU
|
||||
# / \
|
||||
# Conv(1) \
|
||||
# | \
|
||||
# Relu Conv(2)
|
||||
# | |
|
||||
# \ /
|
||||
# Add
|
||||
# |
|
||||
# (output)
|
||||
initializers = []
|
||||
input = helper.make_tensor_value_info("input", TensorProto.FLOAT, [4, 8, 12])
|
||||
output = helper.make_tensor_value_info("output", TensorProto.FLOAT, [4, 2, 8, 8])
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls._tmp_model_dir = tempfile.TemporaryDirectory(prefix="test_onnx_model.")
|
||||
|
||||
# make GRU
|
||||
initializers.append(generate_input_initializer([2, 24, 12], np.float32, "W_GRU"))
|
||||
initializers.append(generate_input_initializer([2, 24, 8], np.float32, "R_GRU"))
|
||||
initializers.append(generate_input_initializer([2, 8, 8], np.float32, "H_GRU"))
|
||||
gru_node = onnx.helper.make_node(
|
||||
"GRU",
|
||||
["input", "W_GRU", "R_GRU", "", "", "H_GRU"],
|
||||
["GRU_O"],
|
||||
hidden_size=8,
|
||||
direction="bidirectional",
|
||||
)
|
||||
|
||||
initializers.append(generate_input_initializer([2, 2, 1, 1], np.float32, "W1"))
|
||||
initializers.append(generate_input_initializer([2, 2, 1, 1], np.float32, "W2"))
|
||||
initializers.append(generate_input_initializer([2], np.float32, "B1"))
|
||||
initializers.append(generate_input_initializer([2], np.float32, "B2"))
|
||||
conv_node_1 = onnx.helper.make_node("Conv", ["GRU_O", "W1", "B1"], ["Conv1_O"], name="Conv1")
|
||||
conv_node_2 = onnx.helper.make_node("Conv", ["GRU_O", "W2", "B2"], ["Conv2_O"], name="Conv2")
|
||||
relu_node = onnx.helper.make_node("Relu", ["Conv1_O"], ["Relu_O"], name="Relu")
|
||||
add_node = onnx.helper.make_node("Add", ["Relu_O", "Conv2_O"], ["output"], name="Add")
|
||||
graph = helper.make_graph(
|
||||
[conv_node_1, relu_node, conv_node_2, gru_node, add_node],
|
||||
"onnx_model_test",
|
||||
[input],
|
||||
[output],
|
||||
initializer=initializers,
|
||||
)
|
||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||
onnx.save(model, model_path)
|
||||
|
||||
def construct_model_Constant(self, model_path): # noqa: N802
|
||||
# (input) Constant
|
||||
# \ /
|
||||
# \ /
|
||||
# \ /
|
||||
# \ /
|
||||
# Add
|
||||
# |
|
||||
# (output)
|
||||
|
||||
initializers = []
|
||||
input = helper.make_tensor_value_info("input", TensorProto.FLOAT, [4, 8, 12])
|
||||
output = helper.make_tensor_value_info("output", TensorProto.FLOAT, [4, 8, 12])
|
||||
|
||||
# make nodes
|
||||
constant_node = onnx.helper.make_node("Constant", [], ["const_output"], value_float=42.0)
|
||||
add_node = onnx.helper.make_node("Add", ["input", "const_output"], ["output"], name="Add")
|
||||
graph = helper.make_graph(
|
||||
[add_node, constant_node],
|
||||
"onnx_model_test",
|
||||
[input],
|
||||
[output],
|
||||
initializer=initializers,
|
||||
)
|
||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||
onnx.save(model, model_path)
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls._tmp_model_dir.cleanup()
|
||||
|
||||
def test_topo_sort(self):
|
||||
test_model_path = "onnx_model_topo_sort.onnx"
|
||||
self.construct_model(test_model_path)
|
||||
test_model_path = str(Path(self._tmp_model_dir.name) / "onnx_model_topo_sort.onnx")
|
||||
construct_model_for_topo_sort(test_model_path)
|
||||
onnx_model = ONNXModel(onnx.load(test_model_path))
|
||||
check_op_type_order(self, onnx_model.model, ["Conv", "Relu", "Conv", "GRU", "Add"])
|
||||
onnx_model.topological_sort()
|
||||
check_op_type_order(self, onnx_model.model, ["GRU", "Conv", "Conv", "Relu", "Add"])
|
||||
|
||||
def test_topo_sort_constant(self):
|
||||
test_model_path = "onnx_model_topo_sort_constant.onnx"
|
||||
self.construct_model_Constant(test_model_path)
|
||||
test_model_path = str(Path(self._tmp_model_dir.name) / "onnx_model_topo_sort_constant.onnx")
|
||||
construct_model_for_topo_sort_constant(test_model_path)
|
||||
onnx_model = ONNXModel(onnx.load(test_model_path))
|
||||
check_op_type_order(self, onnx_model.model, ["Add", "Constant"])
|
||||
onnx_model.topological_sort()
|
||||
check_op_type_order(self, onnx_model.model, ["Constant", "Add"])
|
||||
|
||||
def test_topo_sort_empty_input_output(self):
|
||||
test_model_path = str(Path(self._tmp_model_dir.name) / "onnx_model_topo_empty_input_output.onnx")
|
||||
construct_model_for_topo_sort_empty_input_output(test_model_path)
|
||||
onnx_model = ONNXModel(onnx.load(test_model_path))
|
||||
check_op_type_order(self, onnx_model.model, ["Op1", "Op1", "Op3", "Op2"])
|
||||
onnx_model.topological_sort()
|
||||
check_op_type_order(self, onnx_model.model, ["Op1", "Op1", "Op2", "Op3"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Reference in a new issue