Fix issue with output's friendly name after post-processing (#11095)

Scenario:
- Node "Split" with multiple outputs (e.g. 3). All outputs are connected to "Result"s
- Add post-processing step (e.g. convert element type, can be also implicit)

Issue: after post-processing, 3 new results will be created, each will have "Split" friendly name - inconsistency with IRv10 rules
Fix:
- For nodes with multiple outputs, add '.<idx>' suffix to new output's friendly name
- If no post-processing is applied, return immediately, keeping original results as is

Tests:
- Split with 3 outputs where 2 outputs have post-processing.
- Split with 3 outputs, post-processing doesn't create any nodes
This commit is contained in:
Mikhail Nosov authored and GitHub committed 2022-03-21 20:26:24 +03:00
1 parent dacdf67c2c
commit d84d00e2d6
2 files changed
+79 -5

No files matched your search

+18 -4
View File
@@ -359,20 +359,33 @@ void OutputInfo::OutputInfoImpl::build(ov::ResultVector& results) {
node = std::get<0>(action_result);
post_processing_applied = true;
}
node.get_node_shared_ptr()->set_friendly_name(
result->get_input_source_output(0).get_node_shared_ptr()->get_friendly_name());
// Restore tensor names
node.get_tensor().set_names(start_out_node_names);
auto orig_parent = result->get_input_source_output(0).get_node_shared_ptr();
bool reset_orig_friendly_name = false;
if (!post_processing_applied) {
return;
}
if (orig_parent->get_output_size() == 1) {
node.get_node_shared_ptr()->set_friendly_name(orig_parent->get_friendly_name());
reset_orig_friendly_name = true;
} else if (node.get_node_shared_ptr() != orig_parent) {
// Result node is changed - add ".<idx>" suffix
node.get_node_shared_ptr()->set_friendly_name(orig_parent->get_friendly_name() + "." +
std::to_string(result->get_input_source_output(0).get_index()));
}
// Reset friendly name of input node to avoid names collision
// when there is at a new node inserted by post-processing steps
// If no new nodes are inserted by post-processing, then we need to preserve friendly name of input
// as it's required for old API correct work
if (post_processing_applied)
if (reset_orig_friendly_name) {
result->get_input_source_output(0).get_node_shared_ptr()->set_friendly_name("");
}
// Create result
auto new_result = std::make_shared<opset8::Result>(node);
new_result->set_friendly_name(result->get_friendly_name());
node.get_tensor().set_names(start_out_node_names);
// Preserve runtime info of original result
new_result->get_rt_info() = result->get_rt_info();
@@ -462,6 +475,7 @@ void OutputInfo::OutputInfoImpl::dump(std::ostream& str) const {
str << " User's output tensor: ";
dump_tensor(str, node.get_partial_shape(), context.layout(), node.get_element_type());
str << std::endl;
}
} // namespace preprocess
} // namespace ov
+61 -1
View File
@@ -6,6 +6,7 @@
#include "ngraph/ngraph.hpp"
#include "ngraph/ops.hpp"
#include "openvino/core/preprocess/pre_post_process.hpp"
#include "openvino/opsets/opset8.hpp"
#include "openvino/util/common_util.hpp"
#include "util/test_tools.hpp"
@@ -1331,7 +1332,6 @@ TEST(pre_post_process, preprocess_crop_wrong_dims_not_aligned) {
} catch (const ov::Exception& err) {
EXPECT_TRUE(std::string(err.what()).find(exp_dump.str()) != std::string::npos) << err.what();
EXPECT_TRUE(std::string(err.what()).find(act_dump.str()) != std::string::npos) << err.what();
std::cout << err.what();
} catch (...) {
FAIL() << "Expected ov::Exception";
}
@@ -1735,6 +1735,66 @@ TEST(pre_post_process, postprocess_many) {
EXPECT_TRUE(custom_called);
}
TEST(pre_post_process, postprocess_one_node_many_outputs) {
auto data1 = std::make_shared<op::v0::Parameter>(element::i32, Shape{3});
auto c1 = opset8::Constant::create(element::i32, Shape{}, {0});
auto op = std::make_shared<opset8::Split>(data1, c1, 3);
op->set_friendly_name("Split");
ResultVector results;
for (size_t i = 0; i < op->get_num_splits(); i++) {
op->output(i).set_names({"tensor_Split" + std::to_string(i)});
auto res = std::make_shared<op::v0::Result>(op->output(i));
results.emplace_back(res);
}
auto model = std::make_shared<Model>(ResultVector{results}, ParameterVector{data1});
EXPECT_EQ(model->output(0).get_tensor().get_names().count("tensor_Split0"), 1);
EXPECT_EQ(model->output(1).get_tensor().get_names().count("tensor_Split1"), 1);
EXPECT_EQ(model->output(2).get_tensor().get_names().count("tensor_Split2"), 1);
auto p = PrePostProcessor(model);
p.output(0).tensor().set_element_type(element::f32);
p.output(2).tensor().set_element_type(element::f32);
model = p.build();
EXPECT_EQ(model->get_results().size(), 3);
EXPECT_EQ(model->output(0).get_tensor().get_names().count("tensor_Split0"), 1);
EXPECT_EQ(model->output(1).get_tensor().get_names().count("tensor_Split1"), 1);
EXPECT_EQ(model->output(2).get_tensor().get_names().count("tensor_Split2"), 1);
EXPECT_EQ(model->get_results()[0]->input(0).get_source_output().get_node()->get_friendly_name(), "Split.0");
EXPECT_EQ(model->get_results()[1]->input(0).get_source_output().get_node()->get_friendly_name(), "Split");
EXPECT_EQ(model->get_results()[2]->input(0).get_source_output().get_node()->get_friendly_name(), "Split.2");
}
TEST(pre_post_process, postprocess_nothing_applied) {
auto data1 = std::make_shared<op::v0::Parameter>(element::i32, Shape{1, 3, 10, 20});
auto c1 = opset8::Constant::create(element::i32, Shape{}, {1});
auto op = std::make_shared<opset8::Split>(data1, c1, 3);
op->set_friendly_name("Split");
ResultVector results;
for (size_t i = 0; i < op->get_num_splits(); i++) {
op->output(i).set_names({"tensor_Split" + std::to_string(i)});
auto res = std::make_shared<op::v0::Result>(op->output(i));
results.emplace_back(res);
}
auto model = std::make_shared<Model>(ResultVector{results}, ParameterVector{data1});
EXPECT_EQ(model->get_results()[0]->input(0).get_source_output().get_node()->get_friendly_name(), "Split");
EXPECT_EQ(model->get_results()[1]->input(0).get_source_output().get_node()->get_friendly_name(), "Split");
EXPECT_EQ(model->get_results()[2]->input(0).get_source_output().get_node()->get_friendly_name(), "Split");
ov::layout::set_layout(model->output(1), "N???");
auto p = PrePostProcessor(model);
p.output(1).tensor().set_layout("NCHW");
model = p.build();
EXPECT_EQ(model->get_results().size(), 3);
EXPECT_EQ(ov::layout::get_layout(model->output(1)), "NCHW");
EXPECT_EQ(model->output(1).get_shape(), (Shape{1, 1, 10, 20}));
EXPECT_EQ(model->output(0).get_tensor().get_names().count("tensor_Split0"), 1);
EXPECT_EQ(model->output(1).get_tensor().get_names().count("tensor_Split1"), 1);
EXPECT_EQ(model->output(2).get_tensor().get_names().count("tensor_Split2"), 1);
EXPECT_EQ(model->get_results()[0]->input(0).get_source_output().get_node()->get_friendly_name(), "Split");
EXPECT_EQ(model->get_results()[1]->input(0).get_source_output().get_node()->get_friendly_name(), "Split");
EXPECT_EQ(model->get_results()[2]->input(0).get_source_output().get_node()->get_friendly_name(), "Split");
}
TEST(pre_post_process, exception_safety) {
auto f = create_n_inputs<2>(element::f32, Shape{1, 3, 224, 224});
auto name0 = f->input(0).get_node_shared_ptr()->get_friendly_name();