mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-06-04 23:59:56 +00:00
Two fixes involving minimal builds (#17000)
### Description <!-- Describe your changes. --> - allocation planner was breaking if graph had no nodes - in this particular model a branch of an If node returned an outer scope value directly. - if model used non-tensor types and sparse tensors are disabled the call to IsSpareTensor causes an exception when prematurely terminates the code. - it's perfectly fine to check if a value is a sparse tensor when support for them is disabled. we just can't do anything with that OrtValue which is what the current ifdef's after the call to IsSparseTensor handle. ### 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. --> Fix model execution failure for partner with model that uses sequences in a minimal build with sparse tensors disabled.
This commit is contained in:
parent
d21a2f064b
commit
b3cb775cf9
8 changed files with 146 additions and 26 deletions
|
|
@ -68,11 +68,7 @@ struct OrtValue {
|
|||
}
|
||||
|
||||
bool IsSparseTensor() const {
|
||||
#if !defined(DISABLE_SPARSE_TENSORS)
|
||||
return (type_ != nullptr && type_->IsSparseTensorType());
|
||||
#else
|
||||
ORT_THROW("Sparse tensor is not supported in this build.");
|
||||
#endif
|
||||
}
|
||||
|
||||
onnxruntime::MLDataType Type() const {
|
||||
|
|
|
|||
|
|
@ -1715,31 +1715,39 @@ class PlannerImpl {
|
|||
void PartitionIntoStreams(const logging::Logger& /*logger*/,
|
||||
const ExecutionProviders& /*execution_providers*/,
|
||||
const PathString& /*partition_config_file*/) {
|
||||
stream_nodes_.push_back({});
|
||||
node_stream_map_.resize(SafeInt<size_t>(graph_viewer_.MaxNodeIndex()) + 1);
|
||||
for (auto node_index : graph_viewer_.GetNodesInTopologicalOrder()) {
|
||||
stream_nodes_[0].push_back(node_index);
|
||||
node_stream_map_[node_index] = 0;
|
||||
if (graph_viewer_.NumberOfNodes() > 0) {
|
||||
stream_nodes_.push_back({});
|
||||
node_stream_map_.resize(SafeInt<size_t>(graph_viewer_.MaxNodeIndex()) + 1);
|
||||
for (auto node_index : graph_viewer_.GetNodesInTopologicalOrder()) {
|
||||
stream_nodes_[0].push_back(node_index);
|
||||
node_stream_map_[node_index] = 0;
|
||||
}
|
||||
num_logic_streams_ = 1;
|
||||
}
|
||||
num_logic_streams_ = 1;
|
||||
}
|
||||
|
||||
Status BuildExecutionPlan(const ExecutionProviders& execution_providers) {
|
||||
// 1. create logic stream instance
|
||||
auto& execution_plan = plan_.execution_plan;
|
||||
ORT_ENFORCE(num_logic_streams_ == 1 && !stream_nodes_[0].empty());
|
||||
execution_plan.reserve(1);
|
||||
auto first_node_index = stream_nodes_[0][0];
|
||||
auto* node = graph_viewer_.GetNode(first_node_index);
|
||||
onnxruntime::ProviderType exec_provider_name = node->GetExecutionProviderType();
|
||||
const IExecutionProvider* ep = execution_providers.Get(exec_provider_name);
|
||||
ORT_ENFORCE(ep);
|
||||
auto node_device_mem_location = ep->GetOrtDeviceByMemType(OrtMemType::OrtMemTypeDefault);
|
||||
execution_plan.emplace_back(std::make_unique<SequentialExecutionPlan::LogicStream>(node_device_mem_location));
|
||||
// 2. add steps to the execution plan
|
||||
for (auto node_index : stream_nodes_[0]) {
|
||||
execution_plan[0]->steps_.emplace_back(std::make_unique<LaunchKernelStep>(node_index));
|
||||
|
||||
if (graph_viewer_.NumberOfNodes() > 0) {
|
||||
ORT_ENFORCE(num_logic_streams_ == 1 && !stream_nodes_[0].empty());
|
||||
execution_plan.reserve(1);
|
||||
auto first_node_index = stream_nodes_[0][0];
|
||||
auto* node = graph_viewer_.GetNode(first_node_index);
|
||||
onnxruntime::ProviderType exec_provider_name = node->GetExecutionProviderType();
|
||||
const IExecutionProvider* ep = execution_providers.Get(exec_provider_name);
|
||||
ORT_ENFORCE(ep);
|
||||
auto node_device_mem_location = ep->GetOrtDeviceByMemType(OrtMemType::OrtMemTypeDefault);
|
||||
execution_plan.emplace_back(std::make_unique<SequentialExecutionPlan::LogicStream>(node_device_mem_location));
|
||||
// 2. add steps to the execution plan
|
||||
for (auto node_index : stream_nodes_[0]) {
|
||||
execution_plan[0]->steps_.emplace_back(std::make_unique<LaunchKernelStep>(node_index));
|
||||
}
|
||||
} else {
|
||||
// graph with no nodes. e.g. subgraph of If might return the input as-is or a constant value from an initializer
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "core/flatbuffers/schema/ort.fbs.h"
|
||||
#include "core/framework/data_types.h"
|
||||
#include "core/framework/tensorprotoutils.h"
|
||||
#include "core/framework/TensorSeq.h"
|
||||
#include "core/graph/model.h"
|
||||
#include "core/graph/onnx_protobuf.h"
|
||||
#include "core/session/onnxruntime_cxx_api.h"
|
||||
|
|
@ -556,6 +557,41 @@ TEST(OrtModelOnlyTests, LoadOrtFormatModelFromBufferNoCopyInitializersUseBuffer)
|
|||
RunOrtModel(test_info);
|
||||
}
|
||||
|
||||
// regression test for 2 issues covered by PR #17000 (internally reported issue).
|
||||
// 1) allocation planner broke in minimal build when subgraph had no nodes.
|
||||
// 2) usage of a sequence data type caused an exception due to IsSparseTensor() throwing
|
||||
// instead of allowing the calling code to have #ifdef'd code to handle when IsSparseTensor
|
||||
// returned true and sparse tensors were disabled.
|
||||
TEST(OrtModelOnlyTests, GithubIssue17000) {
|
||||
// need to run the model to
|
||||
auto model_uri = ORT_TSTR("testdata/ort_github_issue_17000.ort");
|
||||
|
||||
auto allocator = TestCPUExecutionProvider()->CreatePreferredAllocators()[0];
|
||||
|
||||
OrtValue item0, item1;
|
||||
CreateMLValue<float>(allocator, {1}, {1.f}, &item0);
|
||||
CreateMLValue<float>(allocator, {2}, {2.f, 3.f}, &item1);
|
||||
|
||||
auto elem_type = DataTypeImpl::GetType<float>();
|
||||
auto tensor_seq = std::make_unique<TensorSeq>(elem_type);
|
||||
tensor_seq->SetElements({item0, item1});
|
||||
|
||||
auto mltype = DataTypeImpl::GetType<TensorSeq>();
|
||||
OrtValue value(tensor_seq.release(), mltype, mltype->GetDeleteFunc());
|
||||
|
||||
OrtModelTestInfo test_info;
|
||||
test_info.model_filename = model_uri;
|
||||
test_info.inputs.insert(std::make_pair("seq_in", value));
|
||||
test_info.output_names = {"still_has_elements"};
|
||||
test_info.output_verifier = [](const std::vector<OrtValue>& fetches) {
|
||||
const auto& output = fetches[0].Get<Tensor>();
|
||||
ASSERT_EQ(output.Shape().Size(), 1);
|
||||
ASSERT_EQ(output.Data<bool>()[0], true); // removed one item from seq so should still have elements
|
||||
};
|
||||
|
||||
RunOrtModel(test_info);
|
||||
}
|
||||
|
||||
#if !defined(DISABLE_ML_OPS)
|
||||
// test that we can deserialize and run a previously saved ORT format model
|
||||
// for a model with sequence and map outputs
|
||||
|
|
|
|||
BIN
onnxruntime/test/testdata/ort_github_issue_17000.onnx
vendored
Normal file
BIN
onnxruntime/test/testdata/ort_github_issue_17000.onnx
vendored
Normal file
Binary file not shown.
BIN
onnxruntime/test/testdata/ort_github_issue_17000.ort
vendored
Normal file
BIN
onnxruntime/test/testdata/ort_github_issue_17000.ort
vendored
Normal file
Binary file not shown.
77
onnxruntime/test/testdata/ort_github_issue_17000.py
vendored
Normal file
77
onnxruntime/test/testdata/ort_github_issue_17000.py
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import numpy as np
|
||||
import onnx
|
||||
from onnx import TensorProto, helper, numpy_helper
|
||||
|
||||
|
||||
def order_repeated_field(repeated_proto, key_name, order):
|
||||
order = list(order)
|
||||
repeated_proto.sort(key=lambda x: order.index(getattr(x, key_name)))
|
||||
|
||||
|
||||
def make_node(op_type, inputs, outputs, name=None, doc_string=None, domain=None, **kwargs):
|
||||
node = helper.make_node(op_type, inputs, outputs, name, doc_string, domain, **kwargs)
|
||||
if doc_string == "":
|
||||
node.doc_string = ""
|
||||
order_repeated_field(node.attribute, "name", kwargs.keys())
|
||||
return node
|
||||
|
||||
|
||||
def make_graph(*args, doc_string=None, **kwargs):
|
||||
graph = helper.make_graph(*args, doc_string=doc_string, **kwargs)
|
||||
if doc_string == "":
|
||||
graph.doc_string = ""
|
||||
return graph
|
||||
|
||||
|
||||
test_graph = make_graph(
|
||||
name="test_graph",
|
||||
# model input of a sequence type to test IsSparseTensor issue
|
||||
inputs=[
|
||||
helper.make_tensor_sequence_value_info("seq_in", TensorProto.FLOAT, shape=None),
|
||||
],
|
||||
outputs=[
|
||||
helper.make_tensor_value_info("still_has_elements", TensorProto.BOOL, shape=[]),
|
||||
],
|
||||
initializer=[
|
||||
numpy_helper.from_array(np.array(0, dtype="int64"), name="i0"),
|
||||
],
|
||||
nodes=[
|
||||
make_node("SequenceLength", inputs=["seq_in"], outputs=["seq_len"], name="get_seq_len"),
|
||||
make_node("Greater", inputs=["seq_len", "i0"], outputs=["has_elements"], name="get_has_elements"),
|
||||
# If node with one branch that has no nodes to test the allocation planner issue
|
||||
# if sequence has elements:
|
||||
# remove one
|
||||
# output bool of whether it still has elements
|
||||
# else:
|
||||
# output false (gives us branch with no nodes)
|
||||
make_node(
|
||||
"If",
|
||||
name="test_if",
|
||||
inputs=["has_elements"],
|
||||
outputs=["still_has_elements"],
|
||||
then_branch=make_graph(
|
||||
name="then",
|
||||
inputs=[],
|
||||
outputs=[helper.make_tensor_value_info("then_bool_out", TensorProto.BOOL, shape=[])],
|
||||
nodes=[
|
||||
make_node("SequenceErase", inputs=["seq_in", "i0"], outputs=["seq_less_one"]),
|
||||
make_node("SequenceLength", inputs=["seq_less_one"], outputs=["new_seq_len"]),
|
||||
make_node("Greater", inputs=["new_seq_len", "i0"], outputs=["then_bool_out"]),
|
||||
],
|
||||
),
|
||||
else_branch=make_graph(
|
||||
name="else",
|
||||
initializer=[numpy_helper.from_array(np.array(False, dtype="bool"), name="else_bool_out")],
|
||||
inputs=[],
|
||||
outputs=[helper.make_tensor_value_info("else_bool_out", TensorProto.BOOL, shape=[])],
|
||||
nodes=[],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# Graph with Sequence operations and an If node that has a subgraph with no nodes
|
||||
model = helper.make_model(opset_imports=[helper.make_operatorsetid("ai.onnx", 14)], ir_version=7, graph=test_graph)
|
||||
|
||||
onnx.shape_inference.infer_shapes(model, strict_mode=True)
|
||||
onnx.save(model, "ort_github_issue_17000.onnx")
|
||||
|
|
@ -3,9 +3,9 @@ ai.onnx;7;Abs,Add,And,BatchNormalization,Concat,Conv,Dropout,Flatten,Foo,Gather,
|
|||
ai.onnx;8;Add,Conv,Flatten,Gemm,MatMul,MaxPool,Mul,Relu,Reshape
|
||||
ai.onnx;9;Abs,Add,BatchNormalization,Cast,Clip,Concat,Constant,ConstantOfShape,Conv,Div,Equal,Gather,Gemm,Identity,If,LayerNormalization,LeakyRelu,Loop,MatMul,Mul,Pow,ReduceMean,Relu,Reshape,Scan,Shape,Sigmoid,Slice,Softmax,Softsign,Sqrt,Squeeze,Sub,Tanh,Transpose,Unsqueeze
|
||||
ai.onnx;10;Add,Cast,Concat,ConstantOfShape,Div,Dropout,Erf,Expand,Gather,Greater,Identity,If,LayerNormalization,Loop,MatMul,Mul,Neg,NonZero,Pow,ReduceMean,ReduceSum,Shape,Sqrt,Squeeze,Sub,Tanh,Transpose,Unsqueeze
|
||||
ai.onnx;11;Abs,Add,ArgMax,BatchNormalization,Cast,Clip,Concat,Constant,ConstantOfShape,Conv,Div,Equal,Exp,Expand,Flatten,Gather,Gemm,Identity,If,LayerNormalization,Log,Loop,MatMul,MatMulInteger,Max,Min,Mul,Neg,Pow,RandomUniform,Range,ReduceMean,ReduceSum,ReduceSumSquare,Relu,Reshape,Scan,SequenceConstruct,SequenceInsert,SequenceLength,Shape,Sigmoid,Slice,Softmax,Split,Sqrt,Squeeze,Sub,Sum,Tanh,Transpose,Unsqueeze,Where
|
||||
ai.onnx;11;Abs,Add,ArgMax,BatchNormalization,Cast,Clip,Concat,Constant,ConstantOfShape,Conv,Div,Equal,Exp,Expand,Flatten,Gather,Gemm,Identity,If,LayerNormalization,Log,Loop,MatMul,MatMulInteger,Max,Min,Mul,Neg,Pow,RandomUniform,Range,ReduceMean,ReduceSum,ReduceSumSquare,Relu,Reshape,Scan,SequenceConstruct,SequenceErase,SequenceInsert,SequenceLength,Shape,Sigmoid,Slice,Softmax,Split,Sqrt,Squeeze,Sub,Sum,Tanh,Transpose,Unsqueeze,Where
|
||||
ai.onnx;12;Add,And,Cast,Concat,Constant,ConstantOfShape,Conv,CumSum,Div,Dropout,DynamicQuantizeLinear,Equal,Erf,Expand,Flatten,Gather,GatherND,Gemm,GlobalAveragePool,Greater,Identity,If,IsInf,LayerNormalization,Less,Loop,MatMul,MatMulInteger,Min,Mul,Not,Pad,Pow,RandomNormalLike,RandomUniform,ReduceMean,ReduceSum,Relu,Reshape,Shape,Slice,Softmax,SoftmaxCrossEntropyLoss,SparseSoftmaxCrossEntropy,Split,Sqrt,Squeeze,Sub,Tanh,Transpose,Unsqueeze,Where
|
||||
ai.onnx;13;Abs,Add,Cast,Concat,ConstantOfShape,Conv,DequantizeLinear,DynamicQuantizeLinear,Equal,Expand,FooBar,FooBar_Attr,Gather,Identity,LayerNormalization,MatMul,MatMulInteger,Mul,Pad,Pow,QuantizeLinear,Range,ReduceSum,Reshape,Shape,Tanh,Transpose,Unsqueeze,Where
|
||||
ai.onnx;13;Abs,Add,Cast,Concat,ConstantOfShape,Conv,DequantizeLinear,DynamicQuantizeLinear,Equal,Expand,FooBar,FooBar_Attr,Gather,Greater,Identity,If,LayerNormalization,MatMul,MatMulInteger,Mul,Pad,Pow,QuantizeLinear,Range,ReduceSum,Reshape,Shape,Tanh,Transpose,Unsqueeze,Where
|
||||
ai.onnx;14;Add,ArgMax,Cast,Conv,Identity,Relu,Sigmoid,Sub
|
||||
ai.onnx;314159;Add
|
||||
ai.onnx.contrib;1;StringLower
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
# required ops and types for ORT format models in testdata
|
||||
ai.onnx;1;Conv{"inputs": {"0": ["float"]}},Foo,Identity
|
||||
ai.onnx;1;Conv{"inputs": {"0": ["float"]}}
|
||||
ai.onnx;5;Reshape
|
||||
ai.onnx;6;Relu{"inputs": {"0": ["float"]}}
|
||||
ai.onnx;7;Add{"inputs": {"0": ["float"]}},Gemm{"inputs": {"0": ["float"]}},Mul{"inputs": {"0": ["float"]}}
|
||||
ai.onnx;8;MaxPool{"inputs": {"0": ["float"]}},Sum{"inputs": {"0": ["float"]}}
|
||||
ai.onnx;9;Cast{"inputs": {"0": ["float"]}, "outputs": {"0": ["bool"]}}
|
||||
ai.onnx;11;ArgMax{"inputs": {"0": ["float"]}},If,Loop
|
||||
ai.onnx;10;QLinearConv{"inputs": {"0": ["uint8_t"]}}
|
||||
ai.onnx;11;ArgMax{"inputs": {"0": ["float"]}},Clip{"inputs": {"0": ["float"]}},Conv{"inputs": {"0": ["float"]}},If,Loop,SequenceErase,SequenceLength
|
||||
ai.onnx;13;DequantizeLinear{"inputs": {"0": ["int32_t", "uint8_t"]}},Greater{"inputs": {"0": ["int64_t"]}},If,QuantizeLinear{"outputs": {"0": ["uint8_t"]}}
|
||||
ai.onnx.ml;1;ArrayFeatureExtractor,LinearClassifier,Normalizer,ZipMap
|
||||
test;1;Foo
|
||||
|
|
|
|||
Loading…
Reference in a new issue