Save Parameters order during IR deserialization (#9286)

* Save Parameters order during IR deserialization

* Add test to check inputs/outputs in IR
This commit is contained in:
Vitaliy Urusovskij
2021-12-21 06:45:14 +03:00
committed by GitHub
parent b5238e55e1
commit cf15cbd301
2 changed files with 91 additions and 9 deletions

View File

@@ -423,6 +423,9 @@ std::shared_ptr<ngraph::Function> XmlDeserializer::parse_function(
std::vector<size_t /*layer-id*/> outputs;
std::unordered_set<std::string> opName;
std::vector<size_t> order;
std::set<size_t> dfs_used_nodes;
std::map<size_t /*to-layer-id*/, std::vector<edge>> edges;
// Read all layers and store their parameters in params map
FOREACH_CHILD (node, root.child("layers"), "layer") {
auto node_param = parseGenericParams(node);
@@ -433,11 +436,15 @@ std::shared_ptr<ngraph::Function> XmlDeserializer::parse_function(
if (node_param.type == "Result" || node_param.type == "Assign") {
outputs.push_back(node_param.layerId);
}
if (node_param.type == "Parameter") {
// Save Parameters order according to order in XML.
// To do so, handle nodes manually and ignore during DFS
dfs_used_nodes.insert(node_param.layerId);
order.push_back(node_param.layerId);
edges[node_param.layerId] = {};
}
}
std::map<size_t /*to-layer-id*/, std::vector<edge>> edges;
std::map<size_t, std::shared_ptr<ngraph::Node>> id_to_node;
// Read all edges and store them for further usage
FOREACH_CHILD (_ec, root.child("edges"), "edge") {
size_t fromLayer = XMLParseUtils::GetUIntAttr(_ec, "from-layer");
@@ -448,12 +455,10 @@ std::shared_ptr<ngraph::Function> XmlDeserializer::parse_function(
}
// Run DFS starting from outputs to get nodes topological order
std::set<size_t> used;
std::vector<size_t> order;
std::function<void(size_t)> dfs = [&edges, &order, &used, &dfs](const size_t id) {
if (used.count(id))
std::function<void(size_t)> dfs = [&edges, &order, &dfs_used_nodes, &dfs](const size_t id) {
if (dfs_used_nodes.count(id))
return;
used.insert(id);
dfs_used_nodes.insert(id);
for (auto& edge : edges[id]) {
dfs(edge.fromLayerId);
}
@@ -464,7 +469,7 @@ std::shared_ptr<ngraph::Function> XmlDeserializer::parse_function(
// OV_ITT_SCOPE_NEXT(FIRST_INFERENCE, taskChain, "ConstructNgraphNodes");
FunctionNodes func_nodes;
std::map<size_t, std::shared_ptr<ngraph::Node>> id_to_node;
std::map<std::string, std::shared_ptr<ngraph::Node>> variable_id_to_read_value;
// Following topological order create nGraph operations

View File

@@ -9,6 +9,12 @@
#include "gtest/gtest.h"
#include "ie_core.hpp"
#include <openvino/runtime/runtime.hpp>
#include <ngraph/function.hpp>
#include <ngraph/opsets/opset5.hpp>
using namespace ngraph;
#ifndef IR_SERIALIZATION_MODELS_PATH // should be already defined by cmake
# error "IR_SERIALIZATION_MODELS_PATH is not defined"
#endif
@@ -92,3 +98,74 @@ TEST_F(SerializationDeterministicityTest, SerializeToBlob) {
ASSERT_TRUE(expected.layerCount() == result.layerCount());
ASSERT_TRUE(expected.getInputShapes() == result.getInputShapes());
}
class DeterministicityTest : public ::testing::Test {
protected:
std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name();
std::string m_out_xml_path = test_name + ".xml";
std::string m_out_bin_path = test_name + ".bin";
void TearDown() override {
std::remove(m_out_xml_path.c_str());
std::remove(m_out_bin_path.c_str());
}
};
TEST_F(DeterministicityTest, IRInputsOrder) {
const std::vector<std::string> friend_names = {"A", "B", "C"};
auto a = std::make_shared<opset5::Parameter>(element::f32, Shape{2, 2});
auto b = std::make_shared<opset5::Parameter>(element::f32, Shape{2, 2});
auto add = std::make_shared<opset5::Add>(a, b);
auto c = std::make_shared<opset5::Parameter>(element::f32, Shape{2, 2});
auto relu = std::make_shared<opset5::Relu>(c);
auto add2 = std::make_shared<opset5::Add>(add, relu);
auto res = std::make_shared<opset5::Result>(add2);
a->set_friendly_name(friend_names[0]);
b->set_friendly_name(friend_names[1]);
c->set_friendly_name(friend_names[2]);
auto model = std::make_shared<ov::Model>(NodeVector{res}, ParameterVector{a, b, c});
ov::runtime::Core core;
auto serialize = ov::pass::Serialize(m_out_xml_path, m_out_bin_path);
serialize.run_on_function(model);
auto serialized_func = core.read_model(m_out_xml_path);
for (size_t i = 0; i < friend_names.size(); i++) {
auto param = serialized_func->get_parameters()[i];
ASSERT_EQ(i, serialized_func->get_parameter_index(param));
ASSERT_STREQ(friend_names[i].c_str(), param->get_friendly_name().c_str());
}
}
TEST_F(DeterministicityTest, IROutputsOrder) {
const std::vector<std::string> friend_names = {"D", "E", "F"};
auto a = std::make_shared<opset5::Parameter>(element::f32, Shape{4, 4});
auto axis_1 = ngraph::opset5::Constant::create(element::i64, Shape{}, {1});
auto split1 = std::make_shared<opset5::Split>(a, axis_1, 2);
auto res1 = std::make_shared<opset5::Result>(split1->output(0));
auto relu = std::make_shared<opset5::Relu>(split1->output(1));
auto split2 = std::make_shared<opset5::Split>(relu, axis_1, 2);
auto res2 = std::make_shared<opset5::Result>(split2->output(0));
auto res3 = std::make_shared<opset5::Result>(split2->output(1));
res1->set_friendly_name(friend_names[0]);
res2->set_friendly_name(friend_names[1]);
res3->set_friendly_name(friend_names[2]);
auto model = std::make_shared<ov::Model>(NodeVector{res1, res2, res3}, ParameterVector{a});
ov::runtime::Core core;
auto serialize = ov::pass::Serialize(m_out_xml_path, m_out_bin_path);
serialize.run_on_function(model);
auto serialized_func = core.read_model(m_out_xml_path);
for (size_t i = 0; i < friend_names.size(); i++) {
auto out = serialized_func->get_results()[i];
ASSERT_EQ(i, serialized_func->get_result_index(out));
ASSERT_STREQ(friend_names[i].c_str(), out->get_friendly_name().c_str());
}
}