diff --git a/onnxruntime/core/framework/execution_frame.cc b/onnxruntime/core/framework/execution_frame.cc index 4c349f33ab..c44bb3e049 100644 --- a/onnxruntime/core/framework/execution_frame.cc +++ b/onnxruntime/core/framework/execution_frame.cc @@ -316,14 +316,25 @@ Status ExecutionFrame::AllocateMLValueTensorPreAllocateBuffer(OrtValue& ort_valu OrtValue& ort_value_reuse = GetMutableMLValue(ort_value_index_reuse); auto* reuse_tensor = ort_value_reuse.GetMutable(); + auto buffer_num_elements = reuse_tensor->Shape().Size(); + auto required_num_elements = shape.Size(); // check number of elements matches. shape may not be an exact match (e.g. Reshape op) - if (reuse_tensor->Shape().Size() != shape.Size()) { + if (buffer_num_elements != required_num_elements) { // could be an allocation planner bug (less likely) or the model incorrectly uses something like 'None' - // as a dim_param in multiple places making the planner think those shapes are equal. - return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, - "Shape mismatch attempting to re-use buffer. ", reuse_tensor->Shape(), " != ", shape, - ". Validate usage of dim_param in shapes in the model."); + // as a dim_param, or -1 in dim_value in multiple places making the planner think those shapes are equal. + auto message = onnxruntime::MakeString( + "Shape mismatch attempting to re-use buffer. ", + reuse_tensor->Shape(), " != ", shape, + ". Validate usage of dim_value (values should be > 0) and " + "dim_param (all values with the same string should equate to the same size) in shapes in the model."); + + // be generous and use the buffer if it's large enough. log a warning though as it indicates a bad model + if (buffer_num_elements >= required_num_elements) { + LOGS_DEFAULT(WARNING) << message; + } else { + return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, message); + } } void* reuse_buffer = reuse_tensor->MutableDataRaw(); diff --git a/onnxruntime/core/framework/tensorprotoutils.cc b/onnxruntime/core/framework/tensorprotoutils.cc index 89726f5a4c..634a59232c 100644 --- a/onnxruntime/core/framework/tensorprotoutils.cc +++ b/onnxruntime/core/framework/tensorprotoutils.cc @@ -278,8 +278,8 @@ TensorShape GetTensorShapeFromTensorShapeProto(const ONNX_NAMESPACE::TensorShape const auto& dims = tensor_shape_proto.dim(); std::vector tensor_shape_vec(static_cast(dims.size())); for (int i = 0; i < dims.size(); ++i) { - tensor_shape_vec[i] = dims[i].has_dim_param() ? -1 /* symbolic dimensions are represented as -1 in onnxruntime*/ - : dims[i].dim_value(); + tensor_shape_vec[i] = dims[i].has_dim_value() ? dims[i].dim_value() + : -1; /* symbolic dimensions are represented as -1 in onnxruntime*/ } return TensorShape(std::move(tensor_shape_vec)); } diff --git a/onnxruntime/core/graph/graph.cc b/onnxruntime/core/graph/graph.cc index 217f04fe22..fb34765e59 100644 --- a/onnxruntime/core/graph/graph.cc +++ b/onnxruntime/core/graph/graph.cc @@ -51,13 +51,34 @@ static bool GraphLoadedFromModelFile(const GraphProto* graph_proto) { graph_proto->value_info_size() != 0); } -NodeArg::NodeArg(const std::string& name, - const TypeProto* p_node_arg_type) { +// there are some known invalid usages of dim_param and dim_value. remove them from the TypeProto so that +// they don't affect shape inferencing or the allocation planner +static void RemoveInvalidValues(ONNX_NAMESPACE::TypeProto& type) { + if (type.has_tensor_type() && type.tensor_type().has_shape()) { + auto* shape = type.mutable_tensor_type()->mutable_shape(); + for (int i = 0, end = shape->dim_size(); i < end; ++i) { + auto& dim = *shape->mutable_dim(i); + if (dim.has_dim_param()) { + auto dim_param = dim.dim_param(); + if (dim_param.empty() || dim_param == "None") { + dim.clear_dim_param(); + } + } else if (dim.has_dim_value()) { + if (dim.dim_value() < 0) { + dim.clear_dim_value(); + } + } + } + } +} + +NodeArg::NodeArg(const std::string& name, const TypeProto* p_node_arg_type) { node_arg_info_.set_name(name); // If the name is empty, it means the arg does not exist. exists_ = !(name.empty()); if (nullptr != p_node_arg_type) { (*node_arg_info_.mutable_type()) = *p_node_arg_type; + RemoveInvalidValues(*node_arg_info_.mutable_type()); type_ = DataTypeUtils::ToType(node_arg_info_.type()); } else { type_ = nullptr; diff --git a/onnxruntime/core/providers/cpu/controlflow/loop.cc b/onnxruntime/core/providers/cpu/controlflow/loop.cc index d6b2192f9c..02609cdfee 100644 --- a/onnxruntime/core/providers/cpu/controlflow/loop.cc +++ b/onnxruntime/core/providers/cpu/controlflow/loop.cc @@ -412,19 +412,21 @@ Status LoopImpl::Execute(FeedsFetchesManager* ffm, const FeedsFetchesManager* ca auto& graph_outputs = subgraph_.GetOutputs(); for (int i = num_loop_carried_vars_; i < num_outputs_; ++i) { - std::vector output_dims; - output_dims.push_back(0); // num iterations is first dim - // get shape from subgraph output if possible to attempt to have the correct rank auto* graph_output = graph_outputs.at(i + 1); // + 1 as first subgraph output is condition value auto* graph_output_shape = graph_output->Shape(); - if (graph_output_shape) { - output_dims.reserve(graph_output_shape->dim_size() + 1); + std::vector output_dims; + output_dims.reserve((graph_output_shape ? graph_output_shape->dim_size() : 0) + 1); + output_dims.push_back(0); // num iterations is first dim + if (graph_output_shape) { const auto& tensor_shape = onnxruntime::utils::GetTensorShapeFromTensorShapeProto(*graph_output_shape); const auto& dims = tensor_shape.GetDims(); - std::copy(dims.cbegin(), dims.cend(), std::back_inserter(output_dims)); + + // copy to output dims and use 0 for any symbolic dim + std::for_each(dims.cbegin(), dims.cend(), + [&output_dims](const int64_t dim) { output_dims.push_back(dim < 0 ? 0 : dim); }); } else { // TODO: We could try and call ExecuteGraph to get the output shape from fetches so the rank is correct, // however that could still fail as we would potentially be passing in invalid data. diff --git a/onnxruntime/python/onnxruntime_pybind_state.cc b/onnxruntime/python/onnxruntime_pybind_state.cc index e29c6cf3f8..83b6c87838 100644 --- a/onnxruntime/python/onnxruntime_pybind_state.cc +++ b/onnxruntime/python/onnxruntime_pybind_state.cc @@ -21,7 +21,6 @@ #define BACKEND_OPENMP "" #endif - #if USE_MKLDNN #define BACKEND_MKLDNN "-MKL-DNN" #include "core/providers/mkldnn/mkldnn_execution_provider.h" @@ -48,8 +47,6 @@ #define BACKEND_OPENVINO "" #endif - - #if USE_OPENBLAS #define BACKEND_OPENBLAS "-OPENBLAS" #else @@ -465,8 +462,11 @@ including arg name, arg type (contains both type and shape).)pbdoc") if (shape->dim(i).has_dim_value()) { res << shape->dim(i).dim_value(); } else if (shape->dim(i).has_dim_param()) { + res << "'" << shape->dim(i).dim_param() << "'"; + } else { res << "None"; } + if (i < shape->dim_size() - 1) { res << ", "; } @@ -491,6 +491,8 @@ including arg name, arg type (contains both type and shape).)pbdoc") if (shape->dim(i).has_dim_value()) { arr[i] = py::cast(shape->dim(i).dim_value()); } else if (shape->dim(i).has_dim_param()) { + arr[i] = py::cast(shape->dim(i).dim_param()); + } else { arr[i] = py::none(); } } diff --git a/onnxruntime/test/testdata/invalid_dim_param_value_repetition.onnx b/onnxruntime/test/testdata/invalid_dim_param_value_repetition.onnx index f7d0624635..64db93e30e 100644 Binary files a/onnxruntime/test/testdata/invalid_dim_param_value_repetition.onnx and b/onnxruntime/test/testdata/invalid_dim_param_value_repetition.onnx differ