|
21 | 21 | #include "core/providers/openvino/ov_versions/capability.h"
|
22 | 22 | #include "core/providers/openvino/qdq_transformations/qdq_stripping.h"
|
23 | 23 | #include "core/providers/openvino/qdq_transformations/qdq_scales_fix.h"
|
| 24 | +#include "../../framework/tensorprotoutils.h" |
24 | 25 |
|
25 | 26 | namespace onnxruntime {
|
26 | 27 | namespace openvino_ep {
|
@@ -453,6 +454,46 @@ static void DumpOpenVINOEPModel([[maybe_unused]] const std::filesystem::path& on
|
453 | 454 | #endif
|
454 | 455 | }
|
455 | 456 |
|
| 457 | +static void SetExternalDataFields(ONNX_NAMESPACE::TensorProto* proto_init, const void* data_ptr, int64_t data_size) { |
| 458 | + static constexpr const char* ORT_INTERNAL_MEM_INITIALIZER = "*/_ORT_MEM_ADDR_/*"; |
| 459 | + auto* external_data = proto_init->mutable_external_data(); |
| 460 | + bool found_location = false, found_offset = false, found_length = false; |
| 461 | + const int ext_data_size = external_data->size(); |
| 462 | + proto_init->set_data_location(ONNX_NAMESPACE::TensorProto_DataLocation::TensorProto_DataLocation_EXTERNAL); |
| 463 | + |
| 464 | + for (int j = 0; j < ext_data_size; ++j) { |
| 465 | + auto& ext_entry = external_data->at(j); |
| 466 | + auto& key = *ext_entry.mutable_key(); |
| 467 | + if (key == "location") { |
| 468 | + *ext_entry.mutable_value() = ORT_INTERNAL_MEM_INITIALIZER; |
| 469 | + found_location = true; |
| 470 | + } else if (key == "offset") { |
| 471 | + *ext_entry.mutable_value() = std::to_string(reinterpret_cast<uintptr_t>(data_ptr)); |
| 472 | + found_offset = true; |
| 473 | + } else if (key == "length") { |
| 474 | + *ext_entry.mutable_value() = std::to_string(data_size); |
| 475 | + found_length = true; |
| 476 | + } |
| 477 | + } |
| 478 | + |
| 479 | + if (!found_location) { |
| 480 | + auto* new_entry = external_data->Add(); |
| 481 | + *new_entry->mutable_key() = "location"; |
| 482 | + *new_entry->mutable_value() = ORT_INTERNAL_MEM_INITIALIZER; |
| 483 | + } |
| 484 | + if (!found_offset) { |
| 485 | + auto* new_entry = external_data->Add(); |
| 486 | + *new_entry->mutable_key() = "offset"; |
| 487 | + *new_entry->mutable_value() = std::to_string(reinterpret_cast<uintptr_t>(data_ptr)); |
| 488 | + } |
| 489 | + if (!found_length) { |
| 490 | + auto* new_entry = external_data->Add(); |
| 491 | + *new_entry->mutable_key() = "length"; |
| 492 | + *new_entry->mutable_value() = std::to_string(data_size); |
| 493 | + } |
| 494 | +} |
| 495 | + |
| 496 | + |
456 | 497 | std::unique_ptr<ONNX_NAMESPACE::ModelProto>
|
457 | 498 | BackendManager::GetModelProtoFromFusedNode(const onnxruntime::Node& fused_node,
|
458 | 499 | const onnxruntime::GraphViewer& subgraph,
|
@@ -529,12 +570,137 @@ BackendManager::GetModelProtoFromFusedNode(const onnxruntime::Node& fused_node,
|
529 | 570 | return model_proto;
|
530 | 571 | } else {
|
531 | 572 | LOGS_DEFAULT(INFO) << "[OpenVINO-EP] OVEP QDQ optimization pass is disabled";
|
| 573 | + |
| 574 | + static bool load_user_initializer_ = true; |
| 575 | + size_t userWeightsFromRawData = 0; |
| 576 | + size_t userWeightsFromExternalDataInMemory = 0; |
| 577 | + size_t allInitializersCount = 0; |
| 578 | + if (load_user_initializer_) { |
| 579 | + auto allInitializers = subgraph.GetAllInitializedTensors(); |
| 580 | + allInitializersCount = allInitializers.size(); |
| 581 | + |
| 582 | + for (auto& entry : allInitializers) { |
| 583 | + auto* tp = entry.second; |
| 584 | + if (tp->has_raw_data()) { |
| 585 | + userWeightsFromRawData++; |
| 586 | + } else if (utils::HasExternalDataInMemory(*tp)) { |
| 587 | + userWeightsFromExternalDataInMemory++; |
| 588 | + } |
| 589 | + } |
| 590 | + } |
| 591 | + LOGS_DEFAULT(INFO) << "[OpenVINO-EP] Loaded " << allInitializersCount << " initializers from the model. " |
| 592 | + << userWeightsFromRawData << " from raw_data, " |
| 593 | + << userWeightsFromExternalDataInMemory << " from external_data."; |
| 594 | + |
532 | 595 | auto model = subgraph.CreateModel(logger);
|
533 | 596 | auto model_proto = model->ToProto();
|
534 | 597 | model_proto->set_ir_version(ONNX_NAMESPACE::Version::IR_VERSION);
|
535 |
| - subgraph.ToProto(*model_proto->mutable_graph(), true, true); |
| 598 | + subgraph.ToProto(*model_proto->mutable_graph(), /*include_initializers*/true, /*include_outer_scope_args*/true, /*execution order*/0, /*include_initializer_data*/!load_user_initializer_); |
| 599 | + |
536 | 600 | print_model_proto_duration();
|
537 | 601 | DumpOpenVINOEPModel(onnx_model_path_name, model_proto.get(), fused_node);
|
| 602 | + |
| 603 | + // new code: |
| 604 | + if (load_user_initializer_) |
| 605 | + { |
| 606 | + LOGS(logger, INFO) << "Initializer data is not included in the model proto. Updating metadata..."; |
| 607 | + const auto& allInitializers = subgraph.GetAllInitializedTensors(); |
| 608 | + auto* graph_proto = model_proto->mutable_graph(); |
| 609 | + auto* proto_initializers = graph_proto->mutable_initializer(); |
| 610 | + |
| 611 | + // Build a map for quick lookup by name |
| 612 | + std::unordered_map<std::string, ONNX_NAMESPACE::TensorProto*> proto_initializer_map; |
| 613 | + for (int i = 0, n = proto_initializers->size(); i < n; ++i) { |
| 614 | + auto& proto_init = proto_initializers->at(i); |
| 615 | + proto_initializer_map[proto_init.name()] = &proto_init; |
| 616 | + } |
| 617 | + |
| 618 | + for (const auto& init_entry : allInitializers) { |
| 619 | + const std::string& name = init_entry.first; |
| 620 | + const ONNX_NAMESPACE::TensorProto* src_init = init_entry.second; |
| 621 | + |
| 622 | + auto it = proto_initializer_map.find(name); |
| 623 | + if (it == proto_initializer_map.end()) |
| 624 | + continue; |
| 625 | + |
| 626 | + auto* proto_init = it->second; |
| 627 | + |
| 628 | + // If the proto initializer is missing data, fill it in |
| 629 | + if (!proto_init->has_raw_data() && src_init->has_raw_data()) { |
| 630 | + *proto_init->mutable_raw_data() = src_init->raw_data(); |
| 631 | + } |
| 632 | + |
| 633 | + // Only set in-memory external_data fields if the data is in memory |
| 634 | + if (src_init->has_raw_data()) { |
| 635 | + // Debug info for in-memory initializers |
| 636 | + LOGS(logger, VERBOSE) << "In-memory initializer RAW: " |
| 637 | + << src_init->name() |
| 638 | + << ", data_type: " << src_init->data_type() |
| 639 | + << ", raw_data size: " << src_init->raw_data().size(); |
| 640 | + |
| 641 | + SetExternalDataFields(proto_init, src_init->raw_data().data(), src_init->raw_data().size()); |
| 642 | + } |
| 643 | + else if (onnxruntime::utils::HasExternalDataInMemory(*src_init)) { |
| 644 | + |
| 645 | + using mutable_proto_t = ONNX_NAMESPACE::TensorProto*; |
| 646 | + auto& mutable_proto = *const_cast<mutable_proto_t>(src_init); |
| 647 | + auto* entry_protos = mutable_proto.mutable_external_data(); |
| 648 | + std::string location; |
| 649 | + size_t offset = 0; |
| 650 | + size_t length = 0; |
| 651 | + for (int i = 0; i < entry_protos->size(); i++) { |
| 652 | + auto& string_entry_proto{ entry_protos->at(i) }; |
| 653 | + const auto& pb_key{ *(string_entry_proto.mutable_key()) }; |
| 654 | + const auto& pb_value{ *(string_entry_proto.mutable_value()) }; |
| 655 | + if (pb_key == "location") { |
| 656 | + location = pb_value; |
| 657 | + } |
| 658 | + else if (pb_key == "offset") { |
| 659 | + const auto res = std::from_chars(pb_value.data(), pb_value.data() + pb_value.size(), offset); |
| 660 | + if (res.ec != std::errc()) { |
| 661 | + LOGS(logger, ERROR) << "External data in memory has invalid offset field: " |
| 662 | + << src_init->name() << "], location: " << location |
| 663 | + << ", offset: " << pb_value; |
| 664 | + offset = 0; |
| 665 | + } |
| 666 | + } |
| 667 | + else if (pb_key == "length") { |
| 668 | + const auto res = std::from_chars(pb_value.data(), pb_value.data() + pb_value.size(), length); |
| 669 | + if (res.ec != std::errc()) { |
| 670 | + LOGS(logger, ERROR) << "External data in memory has invalid length field: " |
| 671 | + << src_init->name() << "], location: " << location |
| 672 | + << ", length: " << pb_value; |
| 673 | + offset = 0; |
| 674 | + } |
| 675 | + } |
| 676 | + } |
| 677 | + if (offset == 0 || length == 0) { |
| 678 | + LOGS(logger, ERROR) << "External data in memory has invalid external_data fields: " |
| 679 | + << src_init->name() << "], location: " << location |
| 680 | + << ", offset: " << offset |
| 681 | + << ", length: " << length; |
| 682 | + } |
| 683 | + else |
| 684 | + { |
| 685 | + // we have data in it, so populate the proto_init |
| 686 | + LOGS(logger, VERBOSE) << "In-memory initializer EXT: " |
| 687 | + << src_init->name() |
| 688 | + << ", size: " << length; |
| 689 | + |
| 690 | + SetExternalDataFields(proto_init, (const void*)offset, length); |
| 691 | + } |
| 692 | + } |
| 693 | + else { |
| 694 | + // Debug info for file-based initializers |
| 695 | + LOGS(logger, VERBOSE)<< "File-based initializer: " |
| 696 | + << src_init->name() |
| 697 | + << ", data_type: " << src_init->data_type(); |
| 698 | + } |
| 699 | + |
| 700 | + } |
| 701 | + |
| 702 | + } |
| 703 | + |
538 | 704 | return model_proto;
|
539 | 705 | }
|
540 | 706 | }
|
|
0 commit comments