Handle names collisions for old IR with new API (#9388)

* Handle names collisions for old IR with new API

* Fixed load model

* Try to fix tests

* Try to fix tests

* Try to fix build

* Try to fix tests

* Fixed tests

* Revert "Fixed tests"

This reverts commit 35da307210.

* Refactoring

* Fixed functional test

* Try to fix CPU tests

Co-authored-by: Ilya Lavrenov <ilya.lavrenov@intel.com>
This commit is contained in:
Ilya Churaev 2022-01-11 09:36:13 +03:00 committed by GitHub
parent e0485c1ad2
commit e095a90cdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 258 additions and 32 deletions

View File

@ -92,7 +92,8 @@ INFERENCE_ENGINE_API_CPP(OutputsDataMap) copyInfo(const OutputsDataMap& networkO
*/ */
INFERENCE_ENGINE_API_CPP(void) INFERENCE_ENGINE_API_CPP(void)
SetExeNetworkInfo(const std::shared_ptr<IExecutableNetworkInternal>& exeNetwork, SetExeNetworkInfo(const std::shared_ptr<IExecutableNetworkInternal>& exeNetwork,
const std::shared_ptr<const ov::Model>& function); const std::shared_ptr<const ov::Model>& function,
bool new_api);
/** /**
* @interface IInferencePlugin * @interface IInferencePlugin

View File

@ -286,18 +286,21 @@ void IInferencePlugin::SetExeNetworkInfo(const std::shared_ptr<IExecutableNetwor
void IInferencePlugin::SetExeNetworkInfo(const std::shared_ptr<IExecutableNetworkInternal>& exeNetwork, void IInferencePlugin::SetExeNetworkInfo(const std::shared_ptr<IExecutableNetworkInternal>& exeNetwork,
const std::shared_ptr<const ov::Model>& function) { const std::shared_ptr<const ov::Model>& function) {
InferenceEngine::SetExeNetworkInfo(exeNetwork, function); bool newAPI = this->GetCore() && this->GetCore()->isNewAPI();
InferenceEngine::SetExeNetworkInfo(exeNetwork, function, newAPI);
exeNetwork->SetPointerToPlugin(shared_from_this()); exeNetwork->SetPointerToPlugin(shared_from_this());
} }
void SetExeNetworkInfo(const std::shared_ptr<IExecutableNetworkInternal>& exeNetwork, void SetExeNetworkInfo(const std::shared_ptr<IExecutableNetworkInternal>& exeNetwork,
const std::shared_ptr<const ov::Model>& function) { const std::shared_ptr<const ov::Model>& function,
bool new_api) {
OPENVINO_ASSERT(exeNetwork != nullptr); OPENVINO_ASSERT(exeNetwork != nullptr);
OPENVINO_ASSERT(function != nullptr); OPENVINO_ASSERT(function != nullptr);
std::vector<std::shared_ptr<const ov::Node>> const_params; std::vector<std::shared_ptr<const ov::Node>> const_params;
std::vector<std::shared_ptr<const ov::Node>> const_results; std::vector<std::shared_ptr<const ov::Node>> const_results;
std::unordered_set<std::string> leaf_names;
bool add_operation_names = false; bool add_operation_names = false;
const auto& rt_info = function->get_rt_info(); const auto& rt_info = function->get_rt_info();
const auto it = rt_info.find("version"); const auto it = rt_info.find("version");
@ -307,6 +310,14 @@ void SetExeNetworkInfo(const std::shared_ptr<IExecutableNetworkInternal>& exeNet
// getInputs / getOutputs. Since these functions are designed to be used in new API only // getInputs / getOutputs. Since these functions are designed to be used in new API only
// always need to add operation names for IR v10 // always need to add operation names for IR v10
add_operation_names = ir_version == 10; add_operation_names = ir_version == 10;
for (const auto& vals : {function->inputs(), function->outputs()}) {
for (const auto& val : vals) {
for (const auto& name : val.get_names()) {
leaf_names.insert(name);
}
}
}
} }
const auto& inputsInfo = exeNetwork->GetInputsInfo(); const auto& inputsInfo = exeNetwork->GetInputsInfo();
@ -315,14 +326,21 @@ void SetExeNetworkInfo(const std::shared_ptr<IExecutableNetworkInternal>& exeNet
OPENVINO_ASSERT(outputsInfo.size() == function->get_output_size()); OPENVINO_ASSERT(outputsInfo.size() == function->get_output_size());
for (const auto& param : function->get_parameters()) { for (const auto& param : function->get_parameters()) {
const auto& param_name = param->get_friendly_name();
auto new_param = ov::as_type_ptr<ov::op::v0::Parameter>(param->copy_with_new_inputs({})); auto new_param = ov::as_type_ptr<ov::op::v0::Parameter>(param->copy_with_new_inputs({}));
new_param->set_friendly_name(param->get_friendly_name()); new_param->set_friendly_name(param_name);
if (add_operation_names) if (add_operation_names) {
new_param->output(0).get_tensor().add_names({new_param->get_friendly_name()}); OPENVINO_ASSERT(!new_api || leaf_names.find(param_name) == leaf_names.end() ||
param->output(0).get_names().find(param_name) != param->output(0).get_names().end(),
"Model operation names have collisions with tensor names.",
" Please use MO to generate new IR version, it should allow to avoid the issue");
leaf_names.insert(param_name);
new_param->output(0).get_tensor().add_names({param_name});
}
// WA: use CNNNetwork's precisions since plugins sometimes override their precisions // WA: use CNNNetwork's precisions since plugins sometimes override their precisions
// after transformation pipeline is run // after transformation pipeline is run
new_param->set_element_type( new_param->set_element_type(
InferenceEngine::details::convertPrecision(inputsInfo.at(new_param->get_friendly_name())->getPrecision())); InferenceEngine::details::convertPrecision(inputsInfo.at(param_name)->getPrecision()));
new_param->set_layout(param->get_layout()); new_param->set_layout(param->get_layout());
new_param->output(0).get_rt_info() = param->output(0).get_rt_info(); new_param->output(0).get_rt_info() = param->output(0).get_rt_info();
new_param->validate_and_infer_types(); new_param->validate_and_infer_types();
@ -331,15 +349,20 @@ void SetExeNetworkInfo(const std::shared_ptr<IExecutableNetworkInternal>& exeNet
for (const auto& result : function->get_results()) { for (const auto& result : function->get_results()) {
auto fake_param = std::make_shared<ov::op::v0::Parameter>(result->get_output_element_type(0), auto fake_param = std::make_shared<ov::op::v0::Parameter>(result->get_output_element_type(0),
result->get_output_partial_shape(0)); result->get_output_partial_shape(0));
const std::string param_name = ngraph::op::util::create_ie_output_name(result->input_value(0)); const std::string res_name = ngraph::op::util::create_ie_output_name(result->input_value(0));
fake_param->set_friendly_name(param_name); fake_param->set_friendly_name(res_name);
fake_param->set_element_type( fake_param->set_element_type(
InferenceEngine::details::convertPrecision(outputsInfo.at(param_name)->getPrecision())); InferenceEngine::details::convertPrecision(outputsInfo.at(res_name)->getPrecision()));
fake_param->validate_and_infer_types(); fake_param->validate_and_infer_types();
auto new_result = result->copy_with_new_inputs({fake_param}); auto new_result = result->copy_with_new_inputs({fake_param});
new_result->set_friendly_name(result->get_friendly_name()); new_result->set_friendly_name(result->get_friendly_name());
if (add_operation_names) { if (add_operation_names) {
new_result->output(0).get_tensor().add_names({fake_param->get_friendly_name()}); OPENVINO_ASSERT(!new_api || leaf_names.find(res_name) == leaf_names.end() ||
result->output(0).get_names().find(res_name) != result->output(0).get_names().end(),
"Model operation names have collisions with tensor names.",
" Please use MO to generate new IR version, it should allow to avoid the issue");
leaf_names.insert(res_name);
new_result->output(0).get_tensor().add_names({res_name});
} }
auto r = std::dynamic_pointer_cast<ov::op::v0::Result>(new_result); auto r = std::dynamic_pointer_cast<ov::op::v0::Result>(new_result);
OPENVINO_ASSERT(r, "Internal error. SetNetworkInfo failure casting output copy to Result"); OPENVINO_ASSERT(r, "Internal error. SetNetworkInfo failure casting output copy to Result");

View File

@ -512,7 +512,7 @@ public:
res = compile_model_impl(network, plugin, parsed._config, context, hash); res = compile_model_impl(network, plugin, parsed._config, context, hash);
} else { } else {
// Temporary workaround until all plugins support caching of original model inputs // Temporary workaround until all plugins support caching of original model inputs
InferenceEngine::SetExeNetworkInfo(res._ptr, network.getFunction()); InferenceEngine::SetExeNetworkInfo(res._ptr, network.getFunction(), isNewAPI());
} }
} else { } else {
res = compile_model_impl(network, plugin, parsed._config, context, {}); res = compile_model_impl(network, plugin, parsed._config, context, {});
@ -589,7 +589,7 @@ public:
res = compile_model_impl(network, plugin, parsed._config, nullptr, hash, {}, forceDisableCache); res = compile_model_impl(network, plugin, parsed._config, nullptr, hash, {}, forceDisableCache);
} else { } else {
// Temporary workaround until all plugins support caching of original model inputs // Temporary workaround until all plugins support caching of original model inputs
InferenceEngine::SetExeNetworkInfo(res._ptr, network.getFunction()); InferenceEngine::SetExeNetworkInfo(res._ptr, network.getFunction(), isNewAPI());
} }
} else { } else {
res = compile_model_impl(network, plugin, parsed._config, nullptr, {}, {}, forceDisableCache); res = compile_model_impl(network, plugin, parsed._config, nullptr, {}, {}, forceDisableCache);

View File

@ -302,11 +302,32 @@ CNNNetwork convert_to_cnnnetwork(std::shared_ptr<ngraph::Function>& function,
const int64_t ir_version = it->second.as<int64_t>(); const int64_t ir_version = it->second.as<int64_t>();
if (ir_version == 10 && newAPI) { if (ir_version == 10 && newAPI) {
std::unordered_set<std::string> leaf_names;
const auto inputs = function->inputs(); const auto inputs = function->inputs();
for (size_t i = 0; i < inputs.size(); ++i) { for (size_t i = 0; i < inputs.size(); ++i) {
const auto ngraph_type = inputs[i].get_element_type(); const auto ngraph_type = inputs[i].get_element_type();
const auto legacy_type = details::toLegacyType(ngraph_type, true); const auto legacy_type = details::toLegacyType(ngraph_type, true);
prepost.input(i).tensor().set_element_type(legacy_type); prepost.input(i).tensor().set_element_type(legacy_type);
for (const auto& name : inputs[i].get_names()) {
OPENVINO_ASSERT(leaf_names.find(name) == leaf_names.end(),
"Model tensor names have collisions.",
" Please use MO to generate new IR version, it should allow to avoid the issue");
leaf_names.insert(name);
}
}
const auto outputs = function->outputs();
for (size_t i = 0; i < outputs.size(); ++i) {
const auto ngraph_type = outputs[i].get_element_type();
const auto legacy_type = details::toLegacyType(ngraph_type, false);
prepost.output(i).tensor().set_element_type(legacy_type);
for (const auto& name : outputs[i].get_names()) {
OPENVINO_ASSERT(leaf_names.find(name) == leaf_names.end(),
"Model tensor names have collisions.",
" Please use MO to generate new IR version, it should allow to avoid the issue");
leaf_names.insert(name);
}
} }
// in order to support the following scenarios for IR v10 cases: // in order to support the following scenarios for IR v10 cases:
@ -317,29 +338,28 @@ CNNNetwork convert_to_cnnnetwork(std::shared_ptr<ngraph::Function>& function,
// f.reshape({ { "input_operation_name", ov::PartialShape{} } }); // f.reshape({ { "input_operation_name", ov::PartialShape{} } });
// we need to add operation names as tensor names for inputs and outputs // we need to add operation names as tensor names for inputs and outputs
{ {
std::vector<std::string> result_names;
std::vector<ov::Output<ov::Node>> prevPorts;
result_names.reserve(function->get_results().size());
prevPorts.reserve(function->get_results().size());
for (const auto& result : function->get_results()) { for (const auto& result : function->get_results()) {
result_names.emplace_back(ngraph::op::util::create_ie_output_name(result->input_value(0))); auto res_name = ngraph::op::util::create_ie_output_name(result->input_value(0));
result->output(0).get_tensor().add_names({result_names.back()}); OPENVINO_ASSERT(
prevPorts.emplace_back(result->input_value(0)); leaf_names.find(res_name) == leaf_names.end() ||
result->output(0).get_names().find(res_name) != result->output(0).get_names().end(),
"Model operation names have collisions with tensor names.",
" Please use MO to generate new IR version, it should allow to avoid the issue");
leaf_names.insert(res_name);
result->output(0).get_tensor().add_names({res_name});
} }
for (const auto& param : function->get_parameters()) { for (const auto& param : function->get_parameters()) {
param->output(0).get_tensor().add_names({param->get_friendly_name()}); auto param_name = param->get_friendly_name();
OPENVINO_ASSERT(
leaf_names.find(param_name) == leaf_names.end() ||
param->output(0).get_names().find(param_name) != param->output(0).get_names().end(),
"Model operation names have collisions with tensor names.",
" Please use MO to generate new IR version, it should allow to avoid the issue");
leaf_names.insert(param_name);
param->output(0).get_tensor().add_names({param_name});
} }
} }
const auto outputs = function->outputs();
for (size_t i = 0; i < outputs.size(); ++i) {
const auto ngraph_type = outputs[i].get_element_type();
const auto legacy_type = details::toLegacyType(ngraph_type, false);
prepost.output(i).tensor().set_element_type(legacy_type);
}
function = prepost.build(); function = prepost.build();
// Set version to 10 // Set version to 10

View File

@ -202,6 +202,96 @@ TEST_F(RTInfoDeserialization, NodeV10) {
} }
} }
TEST_F(RTInfoDeserialization, NamesCollisionV10) {
std::string model = R"V0G0N(
<net name="Network" version="10">
<layers>
<layer name="in1" type="Parameter" id="0" version="opset8">
<data element_type="f16" shape="1,3,22,22"/>
<rt_info>
<attribute name="fused_names" version="0" value="in1"/>
<attribute name="old_api_map_order" version="0" value="0,2,3,1" />
<attribute name="old_api_map_element_type" version="0" value="f16"/>
</rt_info>
<output>
<port id="0" precision="FP16" names="input_tensor">
<rt_info>
<attribute name="layout" version="0" layout="[N,C,H,W]"/>
</rt_info>
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</output>
</layer>
<layer name="input_tensor" id="1" type="Round" version="opset8">
<data mode="half_to_even"/>
<rt_info>
<attribute name="fused_names" version="0" value="Round1,Round2"/>
</rt_info>
<input>
<port id="1" precision="FP16">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</input>
<output>
<port id="2" precision="FP16" names="output_tensor">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</output>
</layer>
<layer name="output" type="Result" id="2" version="opset8">
<input>
<port id="0" precision="FP16">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
<edge from-layer="1" from-port="2" to-layer="2" to-port="0"/>
</edges>
</net>
)V0G0N";
auto f = getWithIRFrontend(model);
ASSERT_NE(nullptr, f);
auto check_version = [](const std::shared_ptr<ov::Model>& f, int version_ref) {
auto& rt_info = f->get_rt_info();
ASSERT_TRUE(rt_info.count("version"));
ASSERT_TRUE(rt_info.at("version").is<int64_t>());
ASSERT_EQ(rt_info.at("version").as<int64_t>(), version_ref);
};
check_version(f, 10);
// read IR v10 with old API
{
InferenceEngine::Core core;
auto f_10 = core.ReadNetwork(model, InferenceEngine::Blob::CPtr());
ASSERT_NE(nullptr, f_10.getFunction());
auto res = compare_functions(f, f_10.getFunction());
EXPECT_TRUE(res.first) << res.second;
}
// read IR v10 with new API and check that CNNNetwork precision conversions are applied
{
ov::runtime::Core core;
EXPECT_THROW(core.read_model(model, ov::runtime::Tensor()), ov::Exception);
}
}
TEST_F(RTInfoDeserialization, InputAndOutputV10) { TEST_F(RTInfoDeserialization, InputAndOutputV10) {
std::string model = R"V0G0N( std::string model = R"V0G0N(
<net name="Network" version="10"> <net name="Network" version="10">

View File

@ -6,6 +6,7 @@
#include "base/behavior_test_utils.hpp" #include "base/behavior_test_utils.hpp"
#include "common_test_utils/ngraph_test_utils.hpp" #include "common_test_utils/ngraph_test_utils.hpp"
#include "common_test_utils/file_utils.hpp" #include "common_test_utils/file_utils.hpp"
#include "openvino/core/model.hpp"
namespace BehaviorTestsDefinitions { namespace BehaviorTestsDefinitions {
class ExecutableNetworkBaseTest : public testing::WithParamInterface<BehaviorTestsUtils::InferRequestParams>, class ExecutableNetworkBaseTest : public testing::WithParamInterface<BehaviorTestsUtils::InferRequestParams>,
@ -316,4 +317,50 @@ TEST_P(ExecNetSetPrecision, canSetOutputPrecisionForNetwork) {
outputs_info.begin()->second->setPrecision(netPrecision); outputs_info.begin()->second->setPrecision(netPrecision);
ASSERT_NO_THROW(ie->LoadNetwork(cnnNet, targetDevice, configuration)); ASSERT_NO_THROW(ie->LoadNetwork(cnnNet, targetDevice, configuration));
} }
} // namespace BehaviorTestsDefinitions TEST_P(ExecutableNetworkBaseTest, loadIncorrectV10Model) {
// Skip test according to plugin specific disabledTestPatterns() (if any)
SKIP_IF_CURRENT_TEST_IS_DISABLED()
ov::runtime::CompiledModel execNet;
// Create simple function
{
auto param1 = std::make_shared<ov::opset8::Parameter>(ov::element::Type_t::f32, ov::Shape({1, 3, 24, 24}));
param1->set_friendly_name("param1");
param1->output(0).get_tensor().set_names({"data1"});
auto relu = std::make_shared<ov::opset8::Relu>(param1);
relu->set_friendly_name("data1");
relu->output(0).get_tensor().set_names({"relu"});
auto result = std::make_shared<ov::opset8::Result>(relu);
result->set_friendly_name("result");
function = std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param1});
function->get_rt_info()["version"] = int64_t(10);
function->set_friendly_name("SimpleReLU");
}
InferenceEngine::CNNNetwork cnnNet(function);
EXPECT_NO_THROW(ie->LoadNetwork(cnnNet, targetDevice, configuration));
}
TEST_P(ExecutableNetworkBaseTest, loadIncorrectV11Model) {
// Skip test according to plugin specific disabledTestPatterns() (if any)
SKIP_IF_CURRENT_TEST_IS_DISABLED()
ov::runtime::CompiledModel execNet;
// Create simple function
{
auto param1 = std::make_shared<ov::opset8::Parameter>(ov::element::Type_t::f32, ov::Shape({1, 3, 24, 24}));
param1->set_friendly_name("param1");
param1->output(0).get_tensor().set_names({"data1"});
auto relu = std::make_shared<ov::opset8::Relu>(param1);
relu->set_friendly_name("data1");
relu->output(0).get_tensor().set_names({"relu"});
auto result = std::make_shared<ov::opset8::Result>(relu);
result->set_friendly_name("result");
function = std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param1});
function->get_rt_info()["version"] = int64_t(11);
function->set_friendly_name("SimpleReLU");
}
InferenceEngine::CNNNetwork cnnNet(function);
EXPECT_NO_THROW(ie->LoadNetwork(cnnNet, targetDevice, configuration));
}
} // namespace BehaviorTestsDefinitions

View File

@ -236,7 +236,7 @@ TEST_P(OVExecGraphImportExportTest, importExportedIENetwork) {
std::shared_ptr<InferenceEngine::Core> ie = ::PluginCache::get().ie(); std::shared_ptr<InferenceEngine::Core> ie = ::PluginCache::get().ie();
InferenceEngine::ExecutableNetwork execNet; InferenceEngine::ExecutableNetwork execNet;
// Create simple function // Create simple function
{ {
auto param1 = std::make_shared<ov::opset8::Parameter>(elementType, ngraph::Shape({1, 3, 24, 24})); auto param1 = std::make_shared<ov::opset8::Parameter>(elementType, ngraph::Shape({1, 3, 24, 24}));
param1->set_friendly_name("param1"); param1->set_friendly_name("param1");

View File

@ -653,6 +653,51 @@ TEST_P(OVExecutableNetworkBaseTest, getCompiledModelFromInferRequest) {
ASSERT_NO_THROW(another_req.infer()); ASSERT_NO_THROW(another_req.infer());
} }
} }
TEST_P(OVExecutableNetworkBaseTest, loadIncorrectV10Model) {
// Skip test according to plugin specific disabledTestPatterns() (if any)
SKIP_IF_CURRENT_TEST_IS_DISABLED()
ov::runtime::CompiledModel execNet;
// Create simple function
{
auto param1 = std::make_shared<ov::opset8::Parameter>(element::Type_t::f32, ngraph::Shape({1, 3, 24, 24}));
param1->set_friendly_name("param1");
param1->output(0).get_tensor().set_names({"data1"});
auto relu = std::make_shared<ov::opset8::Relu>(param1);
relu->set_friendly_name("data1");
relu->output(0).get_tensor().set_names({"relu"});
auto result = std::make_shared<ov::opset8::Result>(relu);
result->set_friendly_name("result");
function = std::make_shared<ngraph::Function>(ngraph::ResultVector{result}, ngraph::ParameterVector{param1});
function->get_rt_info()["version"] = int64_t(10);
function->set_friendly_name("SimpleReLU");
}
EXPECT_THROW(core->compile_model(function, targetDevice, configuration), ov::Exception);
}
TEST_P(OVExecutableNetworkBaseTest, loadIncorrectV11Model) {
// Skip test according to plugin specific disabledTestPatterns() (if any)
SKIP_IF_CURRENT_TEST_IS_DISABLED()
ov::runtime::CompiledModel execNet;
// Create simple function
{
auto param1 = std::make_shared<ov::opset8::Parameter>(element::Type_t::f32, ngraph::Shape({1, 3, 24, 24}));
param1->set_friendly_name("param1");
param1->output(0).get_tensor().set_names({"data1"});
auto relu = std::make_shared<ov::opset8::Relu>(param1);
relu->set_friendly_name("data1");
relu->output(0).get_tensor().set_names({"relu"});
auto result = std::make_shared<ov::opset8::Result>(relu);
result->set_friendly_name("result");
function = std::make_shared<ngraph::Function>(ngraph::ResultVector{result}, ngraph::ParameterVector{param1});
function->get_rt_info()["version"] = int64_t(11);
function->set_friendly_name("SimpleReLU");
}
EXPECT_NO_THROW(core->compile_model(function, targetDevice, configuration));
}
} // namespace behavior } // namespace behavior
} // namespace test } // namespace test
} // namespace ov } // namespace ov