diff --git a/onnxruntime/core/graph/graph.cc b/onnxruntime/core/graph/graph.cc index ce1e1a743a..bdba029fdb 100644 --- a/onnxruntime/core/graph/graph.cc +++ b/onnxruntime/core/graph/graph.cc @@ -4097,7 +4097,7 @@ ONNX_NAMESPACE::GraphProto Graph::ToGraphProto() const { // This is used for constructing full path for external data // if it exists - auto add_initializer = [](TensorList& output_initializers, const TensorProto& initializer) -> Status { + auto add_initializer = [](TensorList& output_initializers, const TensorProto& initializer) -> void { TensorProto& output = *output_initializers.Add(); output = initializer; @@ -4108,7 +4108,7 @@ ONNX_NAMESPACE::GraphProto Graph::ToGraphProto() const { onnxruntime::FileOffsetType file_offset; SafeInt tensor_byte_size; - ORT_RETURN_IF_ERROR(utils::GetExternalDataInfo(initializer, ignored, location, file_offset, tensor_byte_size)); + ORT_THROW_IF_ERROR(utils::GetExternalDataInfo(initializer, ignored, location, file_offset, tensor_byte_size)); if (location == onnxruntime::utils::kTensorProtoMemoryAddressTag) { // file_offset is address @@ -4119,8 +4119,6 @@ ONNX_NAMESPACE::GraphProto Graph::ToGraphProto() const { output.set_raw_data(data, tensor_byte_size); } } - - return Status::OK(); }; auto* mutable_initializers = result.mutable_initializer(); @@ -4129,27 +4127,24 @@ ONNX_NAMESPACE::GraphProto Graph::ToGraphProto() const { const auto& model_path = ModelPath(); // We want to make sure that sparse initializers do not appear // as dense duplicates within the initializers list. - if (!sparse_tensor_names_.empty()) { - const auto sparse_end = sparse_tensor_names_.end(); - for (const auto& initializer : graph_proto_->initializer()) { - if (sparse_end == sparse_tensor_names_.find(initializer.name())) { - add_initializer(*mutable_initializers, initializer); - } else { - auto& sparse_initializer = *result.add_sparse_initializer(); - auto status = utils::DenseTensorToSparseTensorProto(initializer, model_path, sparse_initializer); - ORT_ENFORCE(status.IsOK(), "Failed to convert dense initializer to sparse"); - } - } - } else -#else - { - for (const auto& initializer : graph_proto_->initializer()) { + const bool has_sparse_initializers = !sparse_tensor_names_.empty(); + const auto sparse_end = sparse_tensor_names_.end(); + for (const auto& initializer : graph_proto_->initializer()) { + if (!has_sparse_initializers || sparse_end == sparse_tensor_names_.find(initializer.name())) { add_initializer(*mutable_initializers, initializer); + } else { + auto& sparse_initializer = *result.add_sparse_initializer(); + auto status = utils::DenseTensorToSparseTensorProto(initializer, model_path, sparse_initializer); + ORT_ENFORCE(status.IsOK(), "Failed to convert dense initializer to sparse"); } } +#else + for (const auto& initializer : graph_proto_->initializer()) { + add_initializer(*mutable_initializers, initializer); + } #endif - return result; + return result; } Status Graph::AddExternalInitializersToGraphProtoImpl( diff --git a/onnxruntime/test/shared_lib/test_model_builder_api.cc b/onnxruntime/test/shared_lib/test_model_builder_api.cc index 073280c4b1..f3bdd4f60b 100644 --- a/onnxruntime/test/shared_lib/test_model_builder_api.cc +++ b/onnxruntime/test/shared_lib/test_model_builder_api.cc @@ -33,7 +33,7 @@ Ort::Session CreateSession(Ort::Env& env, : default_session_options; // Set this to save the model if you want to debug. - // session_options.SetOptimizedModelFilePath(ORT_TSTR("model_builder_output.onnx")); + session_options.SetOptimizedModelFilePath(ORT_TSTR("model_builder_output.onnx")); Ort::Session session(env, graph_api_model, session_options); @@ -141,14 +141,14 @@ TEST(ModelBuilderAPITest, Basic_CApi) { Ort::ThrowOnError(model_builder_api.CreateGraph(&graph)); // - // Create OrtModel with a Gemm. X input is 3x2, Y input is 2x3, Z output is 3x3. + // Create OrtModel with a Gemm. X input is 3x2, Y input is 2x8, Z output is 3x8. // X is model input. Y is initializer. // Set the alpha attribute of the Gemm node to 2.0 to test attribute handling. // // model input OrtTensorTypeAndShapeInfo* tensor_type_info = nullptr; - std::vector input_dims = {3, 2}; + std::vector input_dims = {3, 4}; // can use api.SetSymbolicDimensions to set symbolic dimensions. // the input array should have the same rank as the call to SetDimensions. // e.g. call SetDimensions with {-1, 3, 2} and SetSymbolicDimensions with {"N", nullptr, nullptr} to create @@ -170,7 +170,7 @@ TEST(ModelBuilderAPITest, Basic_CApi) { // model outputs OrtTypeInfo* output_type_info = nullptr; - std::vector output_dims = {3, 3}; + std::vector output_dims = {3, 8}; Ort::ThrowOnError(api.CreateTensorTypeAndShapeInfo(&tensor_type_info)); Ort::ThrowOnError(api.SetTensorElementType(tensor_type_info, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT)); @@ -209,11 +209,13 @@ TEST(ModelBuilderAPITest, Basic_CApi) { node = nullptr; // graph now owns node // Y input - std::vector y_dims = {2, 3}; - deleter.weights.emplace_back( - std::make_unique>(std::initializer_list{1.0f, 2.0f, 3.0f, - 4.0f, 5.0f, 6.0f})); + // As it's 128 bytes it could either be allocated using CreateTensorAsOrtValue or use existing memory. + // Under 128 bytes must use CreateTensorAsOrtValue. + std::vector y_dims = {4, 8}; + + deleter.weights.emplace_back(std::make_unique>(32)); auto& y_values = *deleter.weights.back(); + std::iota(y_values.begin(), y_values.end(), 1.0f); // create an initializer for the Y input. add to `weights` so the memory remains valid. OrtValue* y_tensor = nullptr; @@ -228,18 +230,24 @@ TEST(ModelBuilderAPITest, Basic_CApi) { y_tensor = nullptr; // graph now owns if (use_constant_node) { - // Test that a Constant node is converted to an intializer + // Test that a Constant node is converted to an initializer - // create Constant node that is used as the Max in a Clip to limit the output - OrtOpAttr* value_attr = nullptr; - float max = 60.0f; - Ort::ThrowOnError(api.CreateOpAttr("value", &max, sizeof(max), ORT_OP_ATTR_FLOAT, &value_attr)); - - node = CreateNode(model_builder_api, "Constant", "clip_max", {}, {"max"}, {value_attr}); + // create Constant nodes for min/max to limit output range + OrtOpAttr* min_attr = nullptr; + float min = 400.0f; + Ort::ThrowOnError(api.CreateOpAttr("value", &min, sizeof(min), ORT_OP_ATTR_FLOAT, &min_attr)); + node = CreateNode(model_builder_api, "Constant", "clip_min", {}, {"min"}, {min_attr}); Ort::ThrowOnError(model_builder_api.AddNodeToGraph(graph, node)); node = nullptr; // graph now owns node - node = CreateNode(model_builder_api, "Clip", "Clip1", {gemm_output_name.c_str(), "", "max"}, {"Z"}); + OrtOpAttr* max_attr = nullptr; + float max = 900.0f; + Ort::ThrowOnError(api.CreateOpAttr("value", &max, sizeof(max), ORT_OP_ATTR_FLOAT, &max_attr)); + node = CreateNode(model_builder_api, "Constant", "clip_max", {}, {"max"}, {max_attr}); + Ort::ThrowOnError(model_builder_api.AddNodeToGraph(graph, node)); + node = nullptr; // graph now owns node + + node = CreateNode(model_builder_api, "Clip", "Clip1", {gemm_output_name.c_str(), "min", "max"}, {"Z"}); Ort::ThrowOnError(model_builder_api.AddNodeToGraph(graph, node)); node = nullptr; // graph now owns node } @@ -261,22 +269,25 @@ TEST(ModelBuilderAPITest, Basic_CApi) { std::vector> inputs(1); auto& input = inputs[0]; input.name = "X"; - input.dims = {3, 2}; - input.values = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; + input.dims = {3, 4}; + input.values = {1.0f, 2.0f, 3.0f, 4.0f, + 8.0f, 7.0f, 6.0f, 5.0f, + 9.0f, 3.0f, 5.0f, 7.0f}; - std::vector expected_dims = {3, 3}; + std::vector expected_dims = {3, 8}; ModelBuilderAPI::Model cxx_model(model); auto session = CreateSession(*ort_env, cxx_model); std::vector expected_output; if (use_constant_node) { - expected_output = {18.0f, 24.0f, 30.0f, - 38.0f, 52.0f, 60.0f, // clipped - 58.0f, 60.0f, 60.0f}; // clipped + // clipped with max 160 + expected_output = {400.0f, 400.0f, 400.0f, 400.0f, 420.0f, 440.0f, 460.0f, 480.0f, + 596.0f, 648.0f, 700.0f, 752.0f, 804.0f, 856.0f, 900.0f, 900.0f, + 592.0f, 640.0f, 688.0f, 736.0f, 784.0f, 832.0f, 880.0f, 900.0f}; } else { - expected_output = {18.0f, 24.0f, 30.0f, - 38.0f, 52.0f, 66.0f, - 58.0f, 80.0f, 102.0f}; + expected_output = {340.0f, 360.0f, 380.0f, 400.0f, 420.0f, 440.0f, 460.0f, 480.0f, + 596.0f, 648.0f, 700.0f, 752.0f, 804.0f, 856.0f, 908.0f, 960.0f, + 592.0f, 640.0f, 688.0f, 736.0f, 784.0f, 832.0f, 880.0f, 928.0f}; } TestInference(session, inputs, "Z", expected_dims, expected_output); @@ -306,7 +317,7 @@ TEST(ModelBuilderAPITest, Basic_CxxApi) { std::vector graph_outputs; // model input. it's {3, 2} but use a symbolic dim to test that works. - std::vector input_dims({-1, 2}); + std::vector input_dims({-1, 4}); std::vector input_symbolic_dims({"multiple_of_3", ""}); TensorTypeAndShapeInfo input_tensor_info(ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, input_dims, @@ -315,7 +326,7 @@ TEST(ModelBuilderAPITest, Basic_CxxApi) { graph_inputs.emplace_back("X", input_type_info.GetConst()); // model outputs - std::vector output_dims = {-1, 3}; + std::vector output_dims = {-1, 8}; std::vector output_symbolic_dims({"multiple_of_3", ""}); TensorTypeAndShapeInfo output_tensor_info(ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, output_dims, @@ -340,10 +351,14 @@ TEST(ModelBuilderAPITest, Basic_CxxApi) { // create an initializer for the Y input. // add to `weights` so it remains valid for the lifetime of the session and we can avoid copying the data. - std::vector y_dims = {2, 3}; - weights.emplace_back(std::make_unique>(std::initializer_list{1.0f, 2.0f, 3.0f, - 4.0f, 5.0f, 6.0f})); + // As it's 128 bytes it could either be allocated using CreateTensorAsOrtValue or use existing memory. + // Under 128 bytes must use CreateTensorAsOrtValue. + std::vector y_dims = {4, 8}; + + weights.emplace_back(std::make_unique>(32)); auto& y_values = *weights.back(); + std::iota(y_values.begin(), y_values.end(), 1.0f); + auto info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeDefault); // if you use this API the initializer data MUST remain valid for the lifetime of the InferenceSession @@ -357,16 +372,18 @@ TEST(ModelBuilderAPITest, Basic_CxxApi) { std::vector> inputs(1); auto& input = inputs[0]; input.name = "X"; - input.dims = {3, 2}; - input.values = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; + input.dims = {3, 4}; + input.values = {1.0f, 2.0f, 3.0f, 4.0f, + 8.0f, 7.0f, 6.0f, 5.0f, + 9.0f, 3.0f, 5.0f, 7.0f}; - std::vector expected_dims = {3, 3}; + std::vector expected_dims = {3, 8}; auto session = CreateSession(*ort_env, model); TestInference(session, inputs, "Z", expected_dims, - {18.0f, 24.0f, 30.0f, - 38.0f, 52.0f, 66.0f, - 58.0f, 80.0f, 102.0f}); + {340.0f, 360.0f, 380.0f, 400.0f, 420.0f, 440.0f, 460.0f, 480.0f, + 596.0f, 648.0f, 700.0f, 752.0f, 804.0f, 856.0f, 908.0f, 960.0f, + 592.0f, 640.0f, 688.0f, 736.0f, 784.0f, 832.0f, 880.0f, 928.0f}); } TEST(ModelBuilderAPITest, BasicModelEdit_CxxApi) {