[ONNX FE] Enable two-steps importing (convert_partially + normalize) (#18440)
This commit is contained in:
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;
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
+79
@@ -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
|
||||
}
|
||||
+105
@@ -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
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user