-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Add partial data propagation to enhance shape inference #26269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 39 commits
d345c26
cb52305
fb5a424
bd14235
e2c532f
f79f65b
dbe36a8
d310a26
3fa22a0
92c71be
f652eec
78ed993
035a66e
e3e26a0
6d478cb
9dc51aa
1ec4bf9
1391a15
21d5e33
e59216b
eb8be39
2616c6f
73e38ad
8bbebf4
de2ad68
24ed992
40c8d09
8b444c4
49f7063
4649a73
2043c3a
f6b3f63
89faeab
037c5cb
6bf5e97
114a0fb
2e263ae
dad69a5
a2d12dd
9abfc68
7754921
745e095
b959403
29ff80f
d08da8c
5f9f09a
9b35eee
2c59c09
58c1711
9af5e28
280ebf9
b431a1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
|
|
||
| // Licensed under the MIT License. | ||
|
|
||
| #include "add_op_data_propagation.h" | ||
|
Check warning on line 4 in onnxruntime/core/graph/data_propagation/add_op_data_propagation.cc
|
||
| #include "core/common/common.h" | ||
| #include "core/graph/node_arg.h" | ||
| #include "core/graph/onnx_protobuf.h" | ||
| #include "core/providers/common.h" | ||
|
|
||
| namespace onnxruntime { | ||
|
|
||
| Status AddOpDataPropagation::infer() { | ||
| // Get "A" input | ||
| const auto* input_0 = node_.InputDefs()[0]; | ||
| // Get "B" input | ||
| const auto* input_1 = node_.InputDefs()[1]; | ||
|
|
||
| if (input_0->GetInferredShapeScalarValue().has_value() && input_1->GetInferredShapeScalarValue().has_value()) { | ||
| output_def_.SetInferredShapeScalarValue(input_0->GetInferredShapeScalarValue().value() + input_1->GetInferredShapeScalarValue().value()); | ||
|
yuslepukhin marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| return Status::OK(); | ||
| } | ||
|
|
||
| } // namespace onnxruntime | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
|
|
||
| // Licensed under the MIT License. | ||
|
|
||
| #include "data_propagation.h" | ||
|
Check warning on line 4 in onnxruntime/core/graph/data_propagation/add_op_data_propagation.h
|
||
| #include "core/graph/graph.h" | ||
| namespace onnxruntime { | ||
|
|
||
| class AddOpDataPropagation : public CustomDataPropagation { | ||
|
yuslepukhin marked this conversation as resolved.
Outdated
|
||
| public: | ||
| AddOpDataPropagation(const Node& node, | ||
| NodeArg& output_def, | ||
| std::function<Status(const std::string&, TensorShapeVector&)> func, | ||
|
Check warning on line 12 in onnxruntime/core/graph/data_propagation/add_op_data_propagation.h
|
||
| const ONNX_NAMESPACE::TypeProto& output_from_onnx_op_data_propagation, | ||
| const logging::Logger& logger) noexcept | ||
| : CustomDataPropagation(node, output_def, func, output_from_onnx_op_data_propagation, logger) {} | ||
|
|
||
| Status infer() override; | ||
| }; | ||
|
|
||
| } // namespace onnxruntime | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
|
|
||
| // Licensed under the MIT License. | ||
|
|
||
| #pragma once | ||
| #include "core/common/common.h" | ||
| #include "core/graph/graph.h" | ||
| #include "core/common/logging/logging.h" | ||
| #include <onnx/onnx-ml.pb.h> | ||
|
Check warning on line 8 in onnxruntime/core/graph/data_propagation/data_propagation.h
|
||
|
|
||
| namespace onnxruntime { | ||
|
|
||
| /** | ||
| * @class CustomDataPropagation | ||
| * Custom data propagation for the operator to help enhance shape inference. | ||
| */ | ||
| class CustomDataPropagation { | ||
|
yuslepukhin marked this conversation as resolved.
Outdated
|
||
| public: | ||
| virtual ~CustomDataPropagation() = default; | ||
| virtual Status infer() = 0; | ||
|
|
||
| protected: | ||
| CustomDataPropagation(const Node& node, | ||
| NodeArg& output_def, | ||
| std::function<Status(const std::string&, TensorShapeVector&)> func, | ||
| const ONNX_NAMESPACE::TypeProto& output_from_onnx_op_data_propagation, | ||
| const logging::Logger& logger) noexcept | ||
| : node_(node), | ||
| output_def_(output_def), | ||
| get_initialized_input_values_func_(func), | ||
|
yuslepukhin marked this conversation as resolved.
Outdated
|
||
| output_from_onnx_op_data_propagation_(output_from_onnx_op_data_propagation), | ||
| logger_(logger) {} | ||
|
|
||
| const Node& node_; | ||
| NodeArg& output_def_; | ||
| std::function<Status(const std::string&, TensorShapeVector&)> get_initialized_input_values_func_; | ||
|
Check warning on line 35 in onnxruntime/core/graph/data_propagation/data_propagation.h
|
||
| const ONNX_NAMESPACE::TypeProto& output_from_onnx_op_data_propagation_; | ||
| const logging::Logger& logger_; | ||
| }; | ||
|
|
||
| } // namespace onnxruntime | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #include "data_propagation_factory.h" | ||
|
Check warning on line 4 in onnxruntime/core/graph/data_propagation/data_propagation_factory.cc
|
||
| #include "core/common/logging/logging.h" | ||
| #include "size_op_data_propagation.h" | ||
|
Check warning on line 6 in onnxruntime/core/graph/data_propagation/data_propagation_factory.cc
|
||
| #include "squeeze_op_data_propagation.h" | ||
|
Check warning on line 7 in onnxruntime/core/graph/data_propagation/data_propagation_factory.cc
|
||
| #include "unsqueeze_op_data_propagation.h" | ||
|
Check warning on line 8 in onnxruntime/core/graph/data_propagation/data_propagation_factory.cc
|
||
| #include "gather_op_data_propagation.h" | ||
| #include "add_op_data_propagation.h" | ||
| #include "sub_op_data_propagation.h" | ||
| #include "mul_op_data_propagation.h" | ||
| #include "div_op_data_propagation.h" | ||
| #include <onnx/onnx-ml.pb.h> | ||
|
|
||
| namespace onnxruntime { | ||
| /** | ||
| * @brief Create custom data propagation for the operator. | ||
| * | ||
| * For certain operators (e.g., Size, Squeeze, Unsqueeze), ONNX's | ||
| * PartialDataPropagationFunction() does not always produce complete or accurate | ||
| * inferred shape values. | ||
| * | ||
| * In particular: | ||
| * - Scalar inputs and outputs are not handled correctly. | ||
| * - Some operators require additional logic that is not covered by the default function. | ||
| * | ||
| * Therefore, for these cases, we perform custom data propagation to ensure | ||
| * correct and complete inference. | ||
| * | ||
| * @param node The ORT's node | ||
| * @param output_def The node's output NodeArg to save the inferred shape values if needed | ||
| * @param func Helper function to get the input value if it's a initializer | ||
| * @param output_from_onnx_op_data_propagation The result from executing ONNX operator's data propagation | ||
| * @param logger The reference to a logger | ||
| * @return std::unique_ptr<CustomDataPropagation> Returns a CustomDataPropagation object if available | ||
| */ | ||
| std::unique_ptr<CustomDataPropagation> CreateCustomDataPropagation(const Node& node, | ||
| NodeArg& output_def, | ||
| std::function<Status(const std::string&, TensorShapeVector&)> func, | ||
|
yuslepukhin marked this conversation as resolved.
Outdated
|
||
| const ONNX_NAMESPACE::TypeProto& output_from_onnx_op_data_propagation, | ||
| const logging::Logger& logger) { | ||
| int dim_size = 0; | ||
| if (output_from_onnx_op_data_propagation.has_tensor_type() && | ||
| output_from_onnx_op_data_propagation.tensor_type().has_shape()) { | ||
| dim_size = output_from_onnx_op_data_propagation.tensor_type().shape().dim_size(); | ||
| } | ||
|
|
||
| if (node.OpType() == "Size") { | ||
| return std::make_unique<SizeOpDataPropagation>(node, output_def, func, output_from_onnx_op_data_propagation, logger); | ||
| } else if (node.OpType() == "Squeeze") { | ||
| return std::make_unique<SqueezeOpDataPropagation>(node, output_def, func, output_from_onnx_op_data_propagation, logger); | ||
| } else if (node.OpType() == "Unsqueeze") { | ||
| return std::make_unique<UnsqueezeOpDataPropagation>(node, output_def, func, output_from_onnx_op_data_propagation, logger); | ||
| } else if (dim_size == 0) { | ||
| if (node.OpType() == "Gather") { | ||
| return std::make_unique<GatherOpDataPropagation>(node, output_def, func, output_from_onnx_op_data_propagation, logger); | ||
| } else if (node.OpType() == "Add") { | ||
| return std::make_unique<AddOpDataPropagation>(node, output_def, func, output_from_onnx_op_data_propagation, logger); | ||
| } else if (node.OpType() == "Sub") { | ||
| return std::make_unique<SubOpDataPropagation>(node, output_def, func, output_from_onnx_op_data_propagation, logger); | ||
| } else if (node.OpType() == "Mul") { | ||
| return std::make_unique<MulOpDataPropagation>(node, output_def, func, output_from_onnx_op_data_propagation, logger); | ||
| } else if (node.OpType() == "Div") { | ||
| return std::make_unique<DivOpDataPropagation>(node, output_def, func, output_from_onnx_op_data_propagation, logger); | ||
| } | ||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
| } // namespace onnxruntime | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
|
|
||
| // Licensed under the MIT License. | ||
|
|
||
| #pragma once | ||
| #include <memory> | ||
| #include "core/graph/graph.h" | ||
| #include "data_propagation.h" | ||
|
|
||
| namespace onnxruntime { | ||
| /** | ||
| * @brief Create custom data propagation for the operator. | ||
| * | ||
| * For certain operators (e.g., Size, Squeeze, Unsqueeze), ONNX's | ||
| * PartialDataPropagationFunction() does not always produce complete or accurate | ||
| * inferred shape values. | ||
| * | ||
| * In particular: | ||
| * - Scalar inputs and outputs are not handled correctly. | ||
| * - Some operators require additional logic that is not covered by the default function. | ||
| * | ||
| * Therefore, for these cases, we perform custom data propagation to ensure | ||
| * correct and complete inference. | ||
| * | ||
| * @param node The ORT's node | ||
| * @param output_def The node's output NodeArg to save the inferred shape values if needed | ||
| * @param func Helper function to get the input value if it's a initializer | ||
| * @param output_from_onnx_op_data_propagation The result from executing ONNX operator's data propagation | ||
| * @param logger The reference to a logger | ||
| * @return std::unique_ptr<CustomDataPropagation> Returns a CustomDataPropagation object if available | ||
| */ | ||
| std::unique_ptr<CustomDataPropagation> CreateCustomDataPropagation(const Node& node, | ||
| NodeArg& output_def, | ||
| std::function<Status(const std::string&, TensorShapeVector&)> funcs, | ||
| const ONNX_NAMESPACE::TypeProto& output_from_onnx_op_data_propagation, | ||
| const logging::Logger& logger); | ||
|
|
||
| } // namespace onnxruntime | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
|
|
||
| // Licensed under the MIT License. | ||
|
|
||
| #include "div_op_data_propagation.h" | ||
| #include "core/common/common.h" | ||
| #include "core/graph/node_arg.h" | ||
| #include "core/graph/onnx_protobuf.h" | ||
| #include "core/providers/common.h" | ||
|
|
||
| namespace onnxruntime { | ||
|
|
||
| Status DivOpDataPropagation::infer() { | ||
| // Get "A" input | ||
| const auto* input_0 = node_.InputDefs()[0]; | ||
| // Get "B" input | ||
| const auto* input_1 = node_.InputDefs()[1]; | ||
|
|
||
| if (input_0->GetInferredShapeScalarValue().has_value() && input_1->GetInferredShapeScalarValue().has_value()) { | ||
| output_def_.SetInferredShapeScalarValue(input_0->GetInferredShapeScalarValue().value() / input_1->GetInferredShapeScalarValue().value()); | ||
|
yuslepukhin marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| return Status::OK(); | ||
| } | ||
|
|
||
| } // namespace onnxruntime | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
|
|
||
| // Licensed under the MIT License. | ||
|
|
||
| #include "data_propagation.h" | ||
| #include "core/graph/graph.h" | ||
| namespace onnxruntime { | ||
|
|
||
| class DivOpDataPropagation : public CustomDataPropagation { | ||
|
yuslepukhin marked this conversation as resolved.
Outdated
|
||
| public: | ||
| DivOpDataPropagation(const Node& node, | ||
| NodeArg& output_def, | ||
| std::function<Status(const std::string&, TensorShapeVector&)> func, | ||
| const ONNX_NAMESPACE::TypeProto& output_from_onnx_op_data_propagation, | ||
| const logging::Logger& logger) noexcept | ||
| : CustomDataPropagation(node, output_def, func, output_from_onnx_op_data_propagation, logger) {} | ||
|
|
||
| Status infer() override; | ||
| }; | ||
|
|
||
| } // namespace onnxruntime | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
|
|
||
| // Licensed under the MIT License. | ||
|
|
||
| #include "gather_op_data_propagation.h" | ||
| #include "core/common/common.h" | ||
| #include "core/graph/node_arg.h" | ||
| #include "core/graph/onnx_protobuf.h" | ||
| #include "core/providers/common.h" | ||
|
|
||
| namespace onnxruntime { | ||
|
|
||
| Status GatherOpDataPropagation::infer() { | ||
| int dim_size = 0; | ||
| if (output_from_onnx_op_data_propagation_.has_tensor_type() && | ||
| output_from_onnx_op_data_propagation_.tensor_type().has_shape()) { | ||
| dim_size = output_from_onnx_op_data_propagation_.tensor_type().shape().dim_size(); | ||
| } | ||
| ORT_ENFORCE(dim_size == 0); | ||
|
yuslepukhin marked this conversation as resolved.
Outdated
|
||
|
|
||
| // Following code extracts an element from a 1D array if all conditions are met. | ||
| // e.g. | ||
| // shape data is [1, 3, 64, 64] -> gets 64 if the index is 2. | ||
| // shape data is [1, 3, 64, 64] -> gets 3 if the index is 1. | ||
|
|
||
| // Get "data" input | ||
| // Note: The "data" input should be a one dimension array in this case. | ||
| const auto* input_0 = node_.InputDefs()[0]; | ||
|
yuslepukhin marked this conversation as resolved.
|
||
|
|
||
| // Get "indices" input | ||
| // Note: The "indices" input could be one of the three cases: | ||
| // 1. A tensor with rank > 0 and all tensor values are known. | ||
| // 2. A tensor with rank > 0 but not all tensor values are know. | ||
| // 3. A scalar. | ||
| // | ||
| // If it's case #1, ONNX operator's PartialDataPropagationFunction() | ||
| // should have inferred the output shape value. | ||
|
yuslepukhin marked this conversation as resolved.
|
||
| // This Gather's custom data propagation handles case #3. | ||
| const auto* input_1 = node_.InputDefs()[1]; | ||
|
|
||
| TensorShapeVector indices; | ||
| ORT_RETURN_IF_ERROR(get_initialized_input_values_func_(input_1->Name(), indices)); | ||
|
|
||
| // Save the dimension value in the NodeArg. | ||
| // Index value is expected to be within bounds [-s, s-1] along axis of size s | ||
| if (input_0->GetInferredShapeValues().has_value()) { | ||
| const auto& tensor_shape_proto = input_0->GetInferredShapeValues().value(); | ||
|
|
||
| // If "indices" input is a scalar, then the size of indices is 1. | ||
| if (indices.size() == 1) { | ||
| ORT_TRY { | ||
| auto& dim = tensor_shape_proto.dim(static_cast<int32_t>(HandleNegativeAxis(indices[0], tensor_shape_proto.dim_size()))); | ||
| if (dim.has_dim_value()) { | ||
| output_def_.SetInferredShapeScalarValue(dim.dim_value()); | ||
|
yuslepukhin marked this conversation as resolved.
|
||
| } | ||
| } | ||
| ORT_CATCH(const std::exception& ex) { | ||
|
chilo-ms marked this conversation as resolved.
Outdated
|
||
| ORT_HANDLE_EXCEPTION([&]() { | ||
| LOGS(logger_, ERROR) << ex.what(); | ||
| LOGS(logger_, INFO) << "Skip Gather op custom data propagation."; | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return Status::OK(); | ||
| } | ||
|
|
||
| } // namespace onnxruntime | ||
Uh oh!
There was an error while loading. Please reload this page.