[ONNX FE] Enable two-steps importing (convert_partially + normalize) (#18440)

This commit is contained in:
Mateusz Bencer authored and GitHub committed 2023-07-18 12:37:02 +00:00
1 parent ec26537b3e
commit 144c1ffd7b
19 files changed
+718 -92

No files matched your search

@@ -20,10 +20,12 @@ public:
using Ptr = std::shared_ptr<FrontEnd>;
std::shared_ptr<ov::Model> convert(const InputModel::Ptr& model) const override;
void convert(const std::shared_ptr<ov::Model>& partially_converted) const override;
std::shared_ptr<ov::Model> convert_partially(const InputModel::Ptr& model) const override;
std::shared_ptr<ov::Model> decode(const InputModel::Ptr& model) const override;
std::string get_name() const override;
bool supported_impl(const std::vector<ov::Any>& variants) const override;
void add_extension(const std::shared_ptr<ov::Extension>& extension) override;
void normalize(const std::shared_ptr<ov::Model>& model) const override;
protected:
InputModel::Ptr load_impl(const std::vector<ov::Any>& params) const override;
+49 -77
View File
@@ -22,36 +22,12 @@
#include "openvino/frontend/onnx/node_context.hpp"
#include "openvino/op/util/op_types.hpp"
#include "openvino/util/log.hpp"
#include "ops_bridge.hpp"
#include "utils/common.hpp"
#include "utils/legacy_conversion_extension.hpp"
namespace ngraph {
namespace onnx_import {
namespace detail {
std::string to_string(const std::map<std::string, std::reference_wrapper<const ONNX_NAMESPACE::NodeProto>>& map) {
std::string result;
for (auto it = std::begin(map); it != std::end(map); ++it) {
result += (it != std::begin(map) ? ", " : "") + it->first;
}
return result;
}
/// \brief Gets the operator represented by provided node unique identificator.
///
/// \param[in] node_proto The node protobuf representation object.
///
/// \note The operator is uniquely identified by the tuple (domain, op_type,
/// since_version). The first two elements are stored in NodeProto object,
/// thus we use only them.
///
/// \return The unique identificator.
///
static std::string get_op_domain_and_name(const ONNX_NAMESPACE::NodeProto& node_proto) {
std::string domain = get_node_domain(node_proto);
return (domain.empty() ? "" : domain + ".") + node_proto.op_type();
}
bool common_node_for_all_outputs(const OutputVector& outputs) {
const auto first_out_node = outputs.at(0).get_node();
bool ret = std::all_of(std::next(std::begin(outputs)),
@@ -157,9 +133,9 @@ Graph::Graph(const std::string& model_dir,
: m_cache{std::move(cache)},
m_extensions{std::move(extensions)},
m_model_dir{model_dir},
m_enable_mmap{enable_mmap} {
const auto ops_bridge = detail::init_ops_bridge(m_extensions.conversions);
m_model = common::make_unique<Model>(model_proto, detail::build_model_opset(*model_proto, ops_bridge));
m_enable_mmap{enable_mmap},
m_ops_bridge{detail::init_ops_bridge(m_extensions.conversions)} {
m_model = common::make_unique<Model>(model_proto, detail::build_model_opset(*model_proto, m_ops_bridge));
transform::expand_onnx_functions(*model_proto);
@@ -197,48 +173,22 @@ Graph::Graph(const std::string& model_dir,
auto ng_node = value_info.get_ng_node(m_parameters, initializers);
m_cache->emplace_node(input.name(), std::move(ng_node));
}
// Verify that ONNX graph contains only nodes of available operator types
std::map<std::string, std::reference_wrapper<const ONNX_NAMESPACE::NodeProto>> unknown_operators;
std::map<std::string, uint64_t> op_statistics;
for (const auto& node_proto : m_model->get_graph().node()) {
if (m_extensions.telemetry) {
op_statistics[node_proto.op_type()]++;
}
if (!m_model->is_operator_available(node_proto)) {
unknown_operators.emplace(detail::get_op_domain_and_name(node_proto), node_proto);
// If a node from an unregistered domain is detected, try registering that domain
m_model->enable_opset_domain(get_node_domain(node_proto), ops_bridge);
}
}
if (m_extensions.telemetry) {
for (const auto& op : op_statistics) {
m_extensions.telemetry->send_event("op_count", "onnx_" + op.first, static_cast<int>(op.second));
}
}
// Reverify wheter we still have any unavailable operators.
auto it = std::begin(unknown_operators);
while (it != std::end(unknown_operators)) {
if (m_model->is_operator_available(it->second)) {
it = unknown_operators.erase(it);
} else {
it++;
}
}
NGRAPH_CHECK(unknown_operators.empty(),
"OpenVINO does not support the following ONNX operations: ",
detail::to_string(unknown_operators));
}
void Graph::convert_to_ngraph_nodes() {
const float total = static_cast<float>(m_model->get_graph().node().size());
unsigned int completed = 0u;
std::map<std::string, uint64_t> op_statistics;
// Process ONNX graph nodes, convert to nGraph nodes
for (const auto& node_proto : m_model->get_graph().node()) {
if (m_extensions.telemetry) {
op_statistics[node_proto.op_type()]++;
}
const Node node{node_proto, this};
if (!m_model->is_operator_available(node.op_type(), node.domain())) {
// If a node from an unregistered domain is detected, try registering that domain
m_model->enable_opset_domain(node.domain(), m_ops_bridge);
}
if (node.has_subgraphs()) {
const auto& subgraphs = node.get_subgraphs();
for (auto& kv : subgraphs) {
@@ -250,6 +200,11 @@ void Graph::convert_to_ngraph_nodes() {
++completed;
m_extensions.progress_reporter->report_progress(completed / total, static_cast<unsigned int>(total), completed);
}
if (m_extensions.telemetry) {
for (const auto& op : op_statistics) {
m_extensions.telemetry->send_event("op_count", "onnx_" + op.first, static_cast<int>(op.second));
}
}
}
void Graph::remove_dangling_parameters() {
@@ -325,8 +280,12 @@ OutputVector Graph::make_framework_nodes(const Node& onnx_node) {
void Graph::decode_to_framework_nodes() {
const float total = static_cast<float>(m_model->get_graph().node().size());
unsigned int completed = 0u;
std::map<std::string, uint64_t> op_statistics;
// Process ONNX graph nodes, convert to nGraph nodes
for (const auto& node_proto : m_model->get_graph().node()) {
if (m_extensions.telemetry) {
op_statistics[node_proto.op_type()]++;
}
const Node node{node_proto, this};
OutputVector ng_nodes{make_framework_nodes(node)};
set_friendly_names(node, ng_nodes);
@@ -339,6 +298,11 @@ void Graph::decode_to_framework_nodes() {
++completed;
m_extensions.progress_reporter->report_progress(completed / total, static_cast<unsigned int>(total), completed);
}
if (m_extensions.telemetry) {
for (const auto& op : op_statistics) {
m_extensions.telemetry->send_event("op_count", "onnx_" + op.first, static_cast<int>(op.second));
}
}
}
std::shared_ptr<Function> Graph::create_function() {
@@ -383,23 +347,31 @@ OutputVector Graph::get_ng_outputs() {
}
OutputVector Graph::make_ng_nodes(const Node& onnx_node) {
const auto ng_node_factory = m_model->get_operator(onnx_node.op_type(), onnx_node.domain());
// contains outputs of nG subgraph implementing a particular ONNX node (possibly a single output of a single node)
OutputVector ng_subgraph_outputs;
try {
ng_subgraph_outputs = ng_node_factory(onnx_node);
} catch (const ::ngraph::onnx_import::error::OnnxNodeValidationFailure&) {
// Do nothing OnnxNodeValidationFailure exception already has ONNX node information.
throw;
} catch (const std::exception& exc) {
std::string msg_prefix = error::detail::get_error_msg_prefix(onnx_node);
OPENVINO_THROW(msg_prefix + ":\n" + std::string(exc.what()));
} catch (...) {
std::string msg_prefix = error::detail::get_error_msg_prefix(onnx_node);
// Since we do not know anything about current exception data type we can only
// notify user in this way.
OPENVINO_ERR << msg_prefix + "Unhandled exception type. \n";
std::rethrow_exception(std::current_exception());
std::string error_message;
if (m_model->is_operator_available(onnx_node.op_type(), onnx_node.domain())) {
const auto ng_node_factory = m_model->get_operator(onnx_node.op_type(), onnx_node.domain());
try {
ng_subgraph_outputs = ng_node_factory(onnx_node);
} catch (const ::ngraph::onnx_import::error::OnnxNodeValidationFailure& e) {
error_message = e.what();
} catch (const std::exception& exc) {
error_message = error::detail::get_error_msg_prefix(onnx_node);
error_message += ":\n" + std::string{exc.what()};
} catch (...) {
error_message = error::detail::get_error_msg_prefix(onnx_node);
// Since we do not know anything about current exception data type we can only
// notify user in this way.
error_message += "Unhandled exception type. \n";
}
}
if (ng_subgraph_outputs.empty()) { // translation not possible (not supported op or exception during processing)
const auto not_supported_node = std::make_shared<frontend::NotSupportedONNXNode>(onnx_node.get_ng_inputs(),
onnx_node.get_outputs_size(),
onnx_node.domain(),
onnx_node.op_type(),
error_message);
ng_subgraph_outputs = not_supported_node->outputs();
}
const size_t outputs_size = std::accumulate(std::begin(ng_subgraph_outputs),
@@ -16,6 +16,7 @@
#include "ngraph/op/parameter.hpp"
#include "onnx_import/core/operator_set.hpp"
#include "openvino/frontend/extension/holder.hpp"
#include "ops_bridge.hpp"
namespace ngraph {
namespace onnx_import {
@@ -83,6 +84,7 @@ private:
std::vector<Node> m_nodes;
std::string m_model_dir;
bool m_enable_mmap;
OperatorsBridge m_ops_bridge;
};
/// \brief Representation of ONNX subgraph. It is used for example by ONNX Loop op.
@@ -52,12 +52,12 @@ const Operator& Model::get_operator(const std::string& name, const std::string&
return op->second;
}
bool Model::is_operator_available(const ONNX_NAMESPACE::NodeProto& node_proto) const {
const auto dm = m_opset.find(get_node_domain(node_proto));
bool Model::is_operator_available(const std::string& name, const std::string& domain) const {
const auto dm = m_opset.find(domain);
if (dm == std::end(m_opset)) {
return false;
}
const auto op = dm->second.find(node_proto.op_type());
const auto op = dm->second.find(name);
return (op != std::end(dm->second));
}
@@ -77,8 +77,10 @@ public:
const Operator& get_operator(const std::string& name, const std::string& domain) const;
/// \brief Check availability of operator base on NodeProto.
/// \param name Type name of the operator.
/// \param domain Domain name of the operator.
/// \return `true` if the operator is available, otherwise it returns `false`.
bool is_operator_available(const ONNX_NAMESPACE::NodeProto& node_proto) const;
bool is_operator_available(const std::string& name, const std::string& domain) const;
/// \brief Enable operators from provided domain to use by this model.
///
+20 -1
View File
@@ -29,6 +29,7 @@
#include "openvino/core/so_extension.hpp"
#include "openvino/frontend/extension/telemetry.hpp"
#include "ops_bridge.hpp"
#include "utils/common.hpp"
using namespace ov;
using namespace ov::frontend::onnx;
@@ -86,7 +87,7 @@ InputModel::Ptr FrontEnd::load_impl(const std::vector<ov::Any>& variants) const
return nullptr;
}
std::shared_ptr<ngraph::Function> FrontEnd::convert(const InputModel::Ptr& model) const {
std::shared_ptr<ngraph::Function> FrontEnd::convert_partially(const InputModel::Ptr& model) const {
auto model_onnx = std::dynamic_pointer_cast<InputModel>(model);
NGRAPH_CHECK(model_onnx != nullptr, "Invalid input model");
@@ -105,6 +106,24 @@ std::shared_ptr<ngraph::Function> FrontEnd::convert(const InputModel::Ptr& model
return model_onnx->convert();
}
void FrontEnd::normalize(const std::shared_ptr<ov::Model>& model) const {
// Here, you can register transformations as a second step of importing process
// In particular, you can operate on not supported ops (it allows to N:N ONNX->OV mapping).
// ov::pass::Manager manager;
// manager.run_passes(model);
}
std::shared_ptr<ngraph::Function> FrontEnd::convert(const InputModel::Ptr& model) const {
const auto partially_converted = convert_partially(model);
const auto error_message = ngraph::onnx_import::common::collect_translation_exceptions(partially_converted);
NGRAPH_CHECK(error_message.empty(), error_message);
normalize(partially_converted);
return partially_converted;
}
void FrontEnd::convert(const std::shared_ptr<ov::Model>& partially_converted) const {
ngraph::onnx_import::detail::convert_decoded_function(partially_converted);
}
+9 -2
View File
@@ -13,6 +13,7 @@
#include "onnx_common/parser.hpp"
#include "onnx_import/onnx_utils.hpp"
#include "ops_bridge.hpp"
#include "utils/common.hpp"
#include "utils/legacy_conversion_extension.hpp"
#include "utils/onnx_internal.hpp"
@@ -28,7 +29,10 @@ std::shared_ptr<Function> import_onnx_model(std::istream& stream,
const auto model_proto = std::make_shared<ONNX_NAMESPACE::ModelProto>(onnx_common::parse_from_istream(stream));
ov::frontend::ExtensionHolder extensions;
extensions.conversions.push_back(legacy_conversion_extension);
return detail::import_onnx_model(model_proto, model_path, enable_mmap, std::move(extensions));
const auto model = detail::import_onnx_model(model_proto, model_path, enable_mmap, std::move(extensions));
const auto error_message = common::collect_translation_exceptions(model);
NGRAPH_CHECK(error_message.empty(), error_message);
return model;
}
std::shared_ptr<Function> import_onnx_model(const std::string& file_path, const bool enable_mmap) {
@@ -39,7 +43,10 @@ std::shared_ptr<Function> import_onnx_model(const std::string& file_path, const
". Could not open the file.");
};
return import_onnx_model(model_stream, file_path, enable_mmap);
const auto model = import_onnx_model(model_stream, file_path, enable_mmap);
const auto error_message = common::collect_translation_exceptions(model);
NGRAPH_CHECK(error_message.empty(), error_message);
return model;
}
std::set<std::string> get_supported_operators(std::int64_t version, const std::string& domain) {
@@ -14,7 +14,7 @@
// limitations under the License.
//*****************************************************************************
#include <onnx_framework_node.hpp>
#include "onnx_framework_node.hpp"
namespace ngraph {
namespace frontend {
@@ -26,5 +26,24 @@ std::shared_ptr<Node> ONNXSubgraphFrameworkNode::clone_with_new_inputs(const Out
return std::make_shared<ONNXSubgraphFrameworkNode>(m_node, m_functions, inputs);
}
std::shared_ptr<Node> NotSupportedONNXNode::clone_with_new_inputs(const OutputVector& inputs) const {
const auto& attrs = get_attrs();
std::string error_message = attrs.at(failed_conversion_key);
return std::make_shared<NotSupportedONNXNode>(inputs,
get_output_size(),
attrs.get_opset_name(),
attrs.get_type_name(),
error_message);
}
bool NotSupportedONNXNode::visit_attributes(AttributeVisitor& visitor) {
const auto& attrs = get_attrs();
auto domain = attrs.get_opset_name();
auto op_type = attrs.get_type_name();
visitor.on_attribute("ONNX_META_domain", domain);
visitor.on_attribute("ONNX_META_type", op_type);
return true;
}
} // namespace frontend
} // namespace ngraph
@@ -36,15 +36,18 @@ class Model;
namespace frontend {
class ONNXFrameworkNode : public ov::op::util::FrameworkNode {
public:
OPENVINO_RTTI("ONNXFrameworkNode", "0");
OPENVINO_OP("ONNXFrameworkNode", "util", ov::op::util::FrameworkNode);
ONNXFrameworkNode(const onnx_import::Node& node)
: ov::op::util::FrameworkNode(node.get_ng_inputs(), node.get_outputs_size()),
m_node(node) {}
ONNXFrameworkNode(const onnx_import::Node& node) : ONNXFrameworkNode(node, node.get_ng_inputs()) {}
ONNXFrameworkNode(const onnx_import::Node& node, const OutputVector& inputs)
: ov::op::util::FrameworkNode(inputs, node.get_outputs_size()),
m_node(node) {}
m_node(node) {
ov::op::util::FrameworkNodeAttrs attrs;
attrs.set_type_name(node.op_type());
attrs.set_opset_name(node.domain());
set_attrs(attrs);
}
OutputVector get_ng_nodes(const std::shared_ptr<onnx_import::Graph>& graph) const {
OutputVector ng_nodes{graph->make_ng_nodes(m_node)};
@@ -71,7 +74,7 @@ protected:
class ONNXSubgraphFrameworkNode : public ONNXFrameworkNode {
public:
OPENVINO_RTTI("ONNXSubgraphFrameworkNode", "0");
OPENVINO_OP("ONNXSubgraphFrameworkNode", "util", ONNXFrameworkNode);
ONNXSubgraphFrameworkNode(const onnx_import::Node& node,
const std::vector<std::shared_ptr<Function>>& functions,
@@ -94,5 +97,35 @@ private:
std::vector<std::shared_ptr<Function>> m_functions;
};
// Be careful with using protobuf references (also onnx_import::Node) inside NotSupportedONNXNode
// which are inserted into ov::Model due to different lifetime and problematic sharing between dynamic libs.
class NotSupportedONNXNode : public ov::op::util::FrameworkNode {
static constexpr const char* failed_conversion_key = "onnx::NotSupportedONNXNode::failed_conversion_key";
public:
OPENVINO_OP("NotSupportedONNXNode", "util", ov::op::util::FrameworkNode);
NotSupportedONNXNode(const OutputVector& inputs,
const size_t output_size,
const std::string& domain,
const std::string& op_type,
const std::string& additional_error_message)
: ov::op::util::FrameworkNode(inputs, output_size) {
ov::op::util::FrameworkNodeAttrs attrs;
attrs.set_opset_name(domain);
attrs.set_type_name(op_type);
attrs[failed_conversion_key] = additional_error_message;
set_attrs(attrs);
}
std::string additional_error_message() const {
auto attrs = get_attrs();
return attrs[failed_conversion_key];
}
virtual std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& inputs) const override;
virtual bool visit_attributes(AttributeVisitor& visitor) override;
};
} // namespace frontend
} // namespace ngraph
@@ -8,6 +8,7 @@
#include "default_opset.hpp"
#include "ngraph/graph_util.hpp"
#include "onnx_framework_node.hpp"
namespace ngraph {
namespace onnx_import {
@@ -141,6 +142,44 @@ bool is_optimized_out(const Output<ov::Node>& node_output) {
return rt_info.find(OPTIMIZED_OUT_NODE) != rt_info.end();
}
std::string collect_translation_exceptions(const std::shared_ptr<ov::Model>& partially_converted) {
std::string fully_unsupported_ops = "OpenVINO does not support the following ONNX operations: ";
std::string additional_error_message = "Errors during ONNX translation: \n";
const std::string sep = ", ";
bool unsupported_found = false;
bool additional_error_found = false;
for (const auto& op : partially_converted->get_ops()) {
if (const auto unsupported = std::dynamic_pointer_cast<frontend::NotSupportedONNXNode>(op)) {
if (unsupported->additional_error_message().empty()) {
fully_unsupported_ops += (unsupported->get_attrs().get_opset_name().empty()
? ""
: unsupported->get_attrs().get_opset_name() + ".") +
unsupported->get_attrs().get_type_name() + sep;
unsupported_found = true;
} else {
additional_error_message += unsupported->additional_error_message();
additional_error_found = true;
}
}
}
fully_unsupported_ops = fully_unsupported_ops.substr(0, fully_unsupported_ops.size() - sep.size());
// remove redundant new line
additional_error_message =
(additional_error_message.empty() || additional_error_message[additional_error_message.length() - 1] != '\n')
? additional_error_message
: additional_error_message.erase(additional_error_message.length() - 1);
if (unsupported_found && additional_error_found) {
return fully_unsupported_ops + "\n" + additional_error_message;
} else if (unsupported_found) {
return fully_unsupported_ops;
} else if (additional_error_found) {
return additional_error_message;
} else {
return "";
}
}
} // namespace common
} // namespace onnx_import
} // namespace ngraph
@@ -150,6 +150,9 @@ void mark_as_optimized_out(Output<ov::Node>& node_output);
/// \brief Checks if a given output was marked as optimized out byt the function above.
bool is_optimized_out(const Output<ov::Node>& node_output);
/// \brief Collect unsupported operators after convert_partially and all exceptions from translation process.
std::string collect_translation_exceptions(const std::shared_ptr<ov::Model>& partially_converted);
} // namespace common
} // namespace onnx_import
} // namespace ngraph
+2
View File
@@ -73,6 +73,8 @@ set(MULTI_TEST_SRC
onnx_import_with_editor.in.cpp)
set(SRC
conversion.cpp
convert_tests.cpp
convert_partially_tests.cpp
library_extension.cpp
load_from.cpp
onnx_editor.cpp
@@ -0,0 +1,109 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include <string>
#include "common_test_utils/file_utils.hpp"
#include "common_test_utils/ngraph_test_utils.hpp"
#include "openvino/frontend/manager.hpp"
#include "openvino/op/util/framework_node.hpp"
#include "openvino/opsets/opset11.hpp"
namespace {
std::shared_ptr<ov::Model> convert_partially(const std::string& path) {
ov::frontend::FrontEndManager manager;
const auto frontend = manager.load_by_framework("onnx");
const auto in_model = frontend->load(path);
return frontend->convert_partially(in_model);
}
std::shared_ptr<ov::op::util::FrameworkNode> get_framework_node_with_out_name(const std::shared_ptr<ov::Model>& model,
const std::string& out_name) {
for (const auto& op : model->get_ops()) {
if (auto framework_node = std::dynamic_pointer_cast<ov::op::util::FrameworkNode>(op)) {
for (const auto& out : op->outputs()) {
if (out.get_any_name() == out_name) {
return framework_node;
}
}
}
}
return nullptr;
}
} // namespace
TEST(ONNXFeConvertPartially, insert_framework_node_if_unsupported) {
const auto path = CommonTestUtils::getModelFromTestModelZoo(std::string(ONNX_TEST_MODELS) +
"unsupported_ops/add_unsupported.onnx");
std::shared_ptr<ov::Model> model;
ASSERT_NO_THROW(model = convert_partially(path));
ASSERT_TRUE(model);
EXPECT_EQ(count_ops_of_type<ov::opset11::Add>(model), 1);
const auto unsupported_add = get_framework_node_with_out_name(model, "Y");
ASSERT_TRUE(unsupported_add);
EXPECT_EQ(unsupported_add->get_attrs().get_type_name(), "UnsupportedAdd");
EXPECT_EQ(unsupported_add->get_attrs().get_opset_name(), "test_domain");
}
TEST(ONNXFeConvertPartially, insert_more_framework_nodes_if_unsupported) {
const auto path = CommonTestUtils::getModelFromTestModelZoo(std::string(ONNX_TEST_MODELS) +
"unsupported_ops/two_unsupported_nodes.onnx");
std::shared_ptr<ov::Model> model;
ASSERT_NO_THROW(model = convert_partially(path));
ASSERT_TRUE(model);
EXPECT_EQ(count_ops_of_type<ov::opset11::Add>(model), 1);
const auto unsupported_add = get_framework_node_with_out_name(model, "X");
ASSERT_TRUE(unsupported_add);
EXPECT_EQ(unsupported_add->get_attrs().get_type_name(), "UnsupportedAdd");
const auto unsupported_abs = get_framework_node_with_out_name(model, "Y_out");
ASSERT_TRUE(unsupported_abs);
EXPECT_EQ(unsupported_abs->get_attrs().get_type_name(), "UnsupportedAbs");
}
// validation error - onnx/instance_norm_bad_scale_type.onnx
TEST(ONNXFeConvertPartially, insert_framework_node_if_onnx_validation_exception) {
const auto path =
CommonTestUtils::getModelFromTestModelZoo(std::string(ONNX_TEST_MODELS) + "instance_norm_bad_scale_type.onnx");
std::shared_ptr<ov::Model> model;
ASSERT_NO_THROW(model = convert_partially(path));
ASSERT_TRUE(model);
const auto incorrect_instance_norm = get_framework_node_with_out_name(model, "y");
ASSERT_TRUE(incorrect_instance_norm);
EXPECT_EQ(incorrect_instance_norm->get_attrs().get_type_name(), "InstanceNormalization");
}
TEST(ONNXFeConvertPartially, insert_framework_node_if_other_translation_exception) {
const auto path =
CommonTestUtils::getModelFromTestModelZoo(std::string(ONNX_TEST_MODELS) + "depth_to_space_bad_mode.onnx");
std::shared_ptr<ov::Model> model;
ASSERT_NO_THROW(model = convert_partially(path));
ASSERT_TRUE(model);
const auto incorrect_dts = get_framework_node_with_out_name(model, "B");
ASSERT_TRUE(incorrect_dts);
EXPECT_EQ(incorrect_dts->get_attrs().get_type_name(), "DepthToSpace");
}
TEST(ONNXFeConvertPartially, insert_framework_nodes_if_both_unsupported_and_other_translation_exception) {
const auto path = CommonTestUtils::getModelFromTestModelZoo(
std::string(ONNX_TEST_MODELS) + "unsupported_ops/unsupported_add_and_incorrect_dts.onnx");
std::shared_ptr<ov::Model> model;
ASSERT_NO_THROW(model = convert_partially(path));
ASSERT_TRUE(model);
EXPECT_EQ(count_ops_of_type<ov::opset11::Abs>(model), 1);
const auto incorrect_dts = get_framework_node_with_out_name(model, "B");
ASSERT_TRUE(incorrect_dts);
EXPECT_EQ(incorrect_dts->get_attrs().get_type_name(), "DepthToSpace");
const auto unsupported_add = get_framework_node_with_out_name(model, "Y");
ASSERT_TRUE(unsupported_add);
EXPECT_EQ(unsupported_add->get_attrs().get_type_name(), "UnsupportedAdd");
}
@@ -0,0 +1,80 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include <string>
#include "common_test_utils/file_utils.hpp"
#include "common_test_utils/test_assertions.hpp"
#include "openvino/frontend/manager.hpp"
namespace {
void convert(const std::string& path) {
ov::frontend::FrontEndManager manager;
const auto frontend = manager.load_by_framework("onnx");
const auto in_model = frontend->load(path);
frontend->convert(in_model);
}
} // namespace
TEST(ONNXFeConvertException, exception_if_node_unsupported) {
const auto path = CommonTestUtils::getModelFromTestModelZoo(std::string(ONNX_TEST_MODELS) +
"unsupported_ops/add_unsupported.onnx");
OV_EXPECT_THROW(
convert(path),
ov::AssertFailure,
testing::EndsWith("OpenVINO does not support the following ONNX operations: test_domain.UnsupportedAdd\n"));
}
TEST(ONNXFeConvertException, exception_if_more_nodes_unsupported) {
const auto path = CommonTestUtils::getModelFromTestModelZoo(std::string(ONNX_TEST_MODELS) +
"unsupported_ops/two_unsupported_nodes.onnx");
OV_EXPECT_THROW(
convert(path),
ov::AssertFailure,
testing::EndsWith("OpenVINO does not support the following ONNX operations: UnsupportedAbs, UnsupportedAdd\n"));
}
TEST(ONNXFeConvertException, exception_if_onnx_validation_exception) {
const auto path =
CommonTestUtils::getModelFromTestModelZoo(std::string(ONNX_TEST_MODELS) + "instance_norm_bad_scale_type.onnx");
OV_EXPECT_THROW(convert(path),
ov::AssertFailure,
testing::EndsWith("Element types for data and scale input do not match (data element type: f32, "
"scale element type: u16).\n"));
}
TEST(ONNXFeConvertException, exception_if_other_translation_exception) {
const auto path =
CommonTestUtils::getModelFromTestModelZoo(std::string(ONNX_TEST_MODELS) + "depth_to_space_bad_mode.onnx");
OV_EXPECT_THROW(convert(path), ov::AssertFailure, testing::EndsWith("only 'DCR' and 'CRD' modes are supported\n"));
}
TEST(ONNXFeConvertException, exception_if_both_unsupported_and_other_translation_exception) {
const auto path = CommonTestUtils::getModelFromTestModelZoo(
std::string(ONNX_TEST_MODELS) + "unsupported_ops/unsupported_add_and_incorrect_dts.onnx");
OV_EXPECT_THROW(
convert(path),
ov::AssertFailure,
testing::HasSubstr("OpenVINO does not support the following ONNX operations: test_domain.UnsupportedAdd"));
OV_EXPECT_THROW(convert(path), ov::AssertFailure, testing::EndsWith("only 'DCR' and 'CRD' modes are supported\n"));
}
TEST(ONNXFeConvertException, exception_if_both_unsupported_onnx_validation_exception_and_other_exception) {
const auto path = CommonTestUtils::getModelFromTestModelZoo(
std::string(ONNX_TEST_MODELS) + "unsupported_ops/unsupported_add_incorrect_dts_and_inst_norm_bad_scale.onnx");
OV_EXPECT_THROW(
convert(path),
ov::AssertFailure,
testing::HasSubstr("OpenVINO does not support the following ONNX operations: test_domain.UnsupportedAdd"));
OV_EXPECT_THROW(convert(path), ov::AssertFailure, testing::HasSubstr("'stop' input is not a scalar"));
OV_EXPECT_THROW(convert(path), ov::AssertFailure, testing::EndsWith("only 'DCR' and 'CRD' modes are supported\n"));
}
@@ -0,0 +1,76 @@
ir_version: 3
producer_name: "OV ONNX FE"
graph {
node {
input: "A"
input: "B"
output: "X"
name: "add_node1"
op_type: "Add"
}
node {
input: "X"
input: "C"
output: "Y"
op_type: "UnsupportedAdd"
domain: "test_domain"
}
name: "test_graph"
input {
name: "A"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
}
}
}
}
input {
name: "B"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
}
}
}
}
input {
name: "C"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
}
}
}
}
output {
name: "Y"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
}
}
}
}
}
opset_import {
domain: "test_domain"
version: 4
}
@@ -0,0 +1,77 @@
ir_version: 3
producer_name: "OV ONNX FE"
graph {
node {
input: "A"
input: "B"
output: "X"
op_type: "UnsupportedAdd"
}
node {
input: "X"
input: "C"
output: "Y"
op_type: "Add"
}
node {
input: "Y"
output: "Y_out"
op_type: "UnsupportedAbs"
}
name: "test_graph"
input {
name: "A"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
}
}
}
}
input {
name: "B"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
}
}
}
}
input {
name: "C"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
}
}
}
}
output {
name: "Y_out"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
}
}
}
}
}
opset_import {
version: 4
}
@@ -0,0 +1,79 @@
ir_version: 3
producer_name: "nGraph ONNX Importer"
graph {
node {
input: "A"
output: "B"
op_type: "DepthToSpace"
attribute {
name: "blocksize"
i: 2
type: INT
}
attribute {
name: "mode"
s: "blocks_first"
type: STRING
}
}
node {
input: "B"
output: "D"
op_type: "Abs"
}
node {
input: "B"
input: "D"
output: "Y"
op_type: "UnsupportedAdd"
domain: "test_domain"
}
name: "compute_graph"
input {
name: "A"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
dim {
dim_value: 8
}
dim {
dim_value: 2
}
dim {
dim_value: 2
}
}
}
}
}
output {
name: "Y"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
dim {
dim_value: 2
}
dim {
dim_value: 4
}
dim {
dim_value: 4
}
}
}
}
}
}
opset_import {
version: 11
}
@@ -0,0 +1,105 @@
ir_version: 3
producer_name: "nGraph ONNX Importer"
graph {
node {
input: "A"
output: "B"
op_type: "DepthToSpace"
attribute {
name: "blocksize"
i: 2
type: INT
}
attribute {
name: "mode"
s: "blocks_first"
type: STRING
}
}
node {
input: "B"
output: "D"
op_type: "Abs"
}
node {
input: "B"
input: "D"
output: "Y"
op_type: "UnsupportedAdd"
domain: "test_domain"
}
node {
input: "Y"
input: "scale"
input: "bias"
output: "Z"
op_type: "InstanceNormalization"
attribute {
name: "epsilon"
f: 0.01
type: FLOAT
}
}
name: "compute_graph"
input {
name: "A"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 1
}
dim {
dim_value: 8
}
dim {
dim_value: 2
}
dim {
dim_value: 2
}
}
}
}
}
input {
name: "scale"
type {
tensor_type {
elem_type: 4
shape {
dim {
dim_value: 2
}
}
}
}
}
input {
name: "bias"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 2
}
}
}
}
}
output {
name: "Z"
type {
tensor_type {
elem_type: 1
}
}
}
}
opset_import {
version: 11
}
+1 -1
View File
@@ -281,7 +281,7 @@ NGRAPH_TEST(${BACKEND_NAME}, onnx_model_custom_op_register_unregister) {
FAIL() << "Expected ngraph::ngraph_error";
} catch (ngraph::ngraph_error const& err) {
std::string what{err.what()};
EXPECT_NE(what.find("Check 'unknown_operators.empty()' failed"), std::string::npos);
EXPECT_NE(what.find("OpenVINO does not support the following ONNX operations:"), std::string::npos);
} catch (...) {
FAIL() << "Expected ngraph::ngraph_error";
}