[PT FE] Print better exception message (#18015)
* [PT FE] Print better exception message * Sort ops and fix code style * Apply feedback * Remove obsolete variable * Rearrange newlines * Apply feedback, add messages in transformations * Fix style * Add exceptions in cat and getitem replacers * Add test * Fix test regex
This commit is contained in:
1 parent
7fd394e87e
commit
caedb6a6b4
23 files changed
+226
-64
No files matched your search
@@ -65,8 +65,9 @@ def get_value_from_getattr(getattr_node, self_module):
|
||||
module = self_module
|
||||
while len(stack) > 0:
|
||||
node = stack.pop()
|
||||
assert (hasattr(module, node.s("name")))
|
||||
module = getattr(module, node.s("name"))
|
||||
attr_name = node.s("name")
|
||||
assert hasattr(module, attr_name), f"No attribute with name \"{attr_name}\" found in module."
|
||||
module = getattr(module, attr_name)
|
||||
return module
|
||||
|
||||
|
||||
|
||||
@@ -8,33 +8,33 @@ void ov::frontend::GeneralFailure::create(const CheckLocInfo& check_loc_info,
|
||||
const std::string& context_info,
|
||||
const std::string& explanation) {
|
||||
throw ov::frontend::GeneralFailure(
|
||||
make_what(check_loc_info, "FrontEnd API failed with GeneralFailure: " + context_info, explanation));
|
||||
make_what(check_loc_info, "FrontEnd API failed with GeneralFailure" + context_info, explanation));
|
||||
}
|
||||
|
||||
void ov::frontend::InitializationFailure::create(const CheckLocInfo& check_loc_info,
|
||||
const std::string& context_info,
|
||||
const std::string& explanation) {
|
||||
throw ov::frontend::InitializationFailure(
|
||||
make_what(check_loc_info, "FrontEnd API failed with InitializationFailure: " + context_info, explanation));
|
||||
make_what(check_loc_info, "FrontEnd API failed with InitializationFailure" + context_info, explanation));
|
||||
}
|
||||
|
||||
void ov::frontend::OpValidationFailure::create(const CheckLocInfo& check_loc_info,
|
||||
const std::string& context_info,
|
||||
const std::string& explanation) {
|
||||
throw ov::frontend::OpValidationFailure(
|
||||
make_what(check_loc_info, "FrontEnd API failed with OpValidationFailure: " + context_info, explanation));
|
||||
make_what(check_loc_info, "FrontEnd API failed with OpValidationFailure" + context_info, explanation));
|
||||
}
|
||||
|
||||
void ov::frontend::OpConversionFailure::create(const CheckLocInfo& check_loc_info,
|
||||
const std::string& context_info,
|
||||
const std::string& explanation) {
|
||||
throw ov::frontend::OpConversionFailure(
|
||||
make_what(check_loc_info, "FrontEnd API failed with OpConversionFailure: " + context_info, explanation));
|
||||
make_what(check_loc_info, "FrontEnd API failed with OpConversionFailure" + context_info, explanation));
|
||||
}
|
||||
|
||||
void ov::frontend::NotImplementedFailure::create(const CheckLocInfo& check_loc_info,
|
||||
const std::string& context_info,
|
||||
const std::string& explanation) {
|
||||
throw ov::frontend::NotImplementedFailure(
|
||||
make_what(check_loc_info, "FrontEnd API failed with NotImplementedFailure: " + context_info, explanation));
|
||||
make_what(check_loc_info, "FrontEnd API failed with NotImplementedFailure" + context_info, explanation));
|
||||
}
|
||||
@@ -40,41 +40,88 @@ namespace frontend {
|
||||
namespace pytorch {
|
||||
|
||||
namespace {
|
||||
std::set<std::string> get_unconverted_types_from_model(const std::shared_ptr<Model>& model) {
|
||||
std::set<std::string> unconverted_ops_types;
|
||||
std::map<std::string, std::string> get_unconverted_types_from_model(const std::shared_ptr<Model>& model) {
|
||||
std::map<std::string, std::string> unconverted_ops_types;
|
||||
for (const auto& node : model->get_ordered_ops()) {
|
||||
if (const auto& fw_node = ov::as_type_ptr<PtFrameworkNode>(node)) {
|
||||
auto attrs = fw_node->get_attrs();
|
||||
FRONT_END_GENERAL_CHECK(attrs.find("PtTypeName") != attrs.end(),
|
||||
const auto& attrs = fw_node->get_attrs();
|
||||
FRONT_END_GENERAL_CHECK(attrs.find(PtFrameworkNode::op_type_key) != attrs.end(),
|
||||
"FrameworkNode attributes do not contain operation type.");
|
||||
unconverted_ops_types.insert(attrs.at("PtTypeName"));
|
||||
std::string exception_msg;
|
||||
if (attrs.find(PtFrameworkNode::failed_conversion_key) != attrs.end()) {
|
||||
exception_msg = attrs.at(PtFrameworkNode::failed_conversion_key);
|
||||
}
|
||||
if (!unconverted_ops_types.count(attrs.at(PtFrameworkNode::op_type_key))) {
|
||||
unconverted_ops_types[attrs.at(PtFrameworkNode::op_type_key)] = exception_msg;
|
||||
}
|
||||
}
|
||||
if (const auto& fw_node = ov::as_type_ptr<ov::op::util::MultiSubGraphOp>(node)) {
|
||||
for (size_t i = 0; i < fw_node->get_internal_subgraphs_size(); i++) {
|
||||
auto internal_types = get_unconverted_types_from_model(fw_node->get_function(i));
|
||||
const auto& internal_types = get_unconverted_types_from_model(fw_node->get_function(i));
|
||||
unconverted_ops_types.insert(internal_types.begin(), internal_types.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
return unconverted_ops_types;
|
||||
}
|
||||
|
||||
std::string pack_detailed_failure_report(const std::map<std::string, std::string>& unconverted_ops,
|
||||
const std::string& additional_error = "") {
|
||||
std::stringstream error_msg;
|
||||
std::stringstream unconverted_ops_msg;
|
||||
std::stringstream failed_ops_msg;
|
||||
std::stringstream failed_ops_short;
|
||||
error_msg << "Model wasn't fully converted.";
|
||||
unconverted_ops_msg << "-- No conversion rule found for operations: ";
|
||||
failed_ops_msg << " Failed operations detailed log:";
|
||||
failed_ops_short << "-- Conversion is failed for: ";
|
||||
bool at_least_one = false;
|
||||
bool at_least_one_except = false;
|
||||
for (auto&& op : unconverted_ops) {
|
||||
if (op.second.empty()) {
|
||||
if (at_least_one)
|
||||
unconverted_ops_msg << ", ";
|
||||
unconverted_ops_msg << op.first;
|
||||
at_least_one = true;
|
||||
} else {
|
||||
if (at_least_one_except)
|
||||
failed_ops_short << ", ";
|
||||
failed_ops_short << op.first;
|
||||
failed_ops_msg << "\n-- " << op.first << " with a message:\n" << op.second;
|
||||
at_least_one_except = true;
|
||||
}
|
||||
}
|
||||
if (at_least_one_except)
|
||||
error_msg << failed_ops_msg.str();
|
||||
error_msg << "\nSummary:" << additional_error;
|
||||
if (at_least_one)
|
||||
error_msg << '\n' << unconverted_ops_msg.str();
|
||||
if (at_least_one_except)
|
||||
error_msg << '\n' << failed_ops_short.str();
|
||||
return error_msg.str();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FrontEnd::FrontEnd() : m_op_translators(get_supported_ops()) {}
|
||||
|
||||
std::shared_ptr<Model> FrontEnd::convert(const InputModel::Ptr& model) const {
|
||||
auto converted_model = convert_partially(model);
|
||||
normalize(converted_model);
|
||||
std::set<std::string> unconverted_ops_types = get_unconverted_types_from_model(converted_model);
|
||||
std::stringstream ops_str;
|
||||
for (auto&& op_type : unconverted_ops_types) {
|
||||
if (m_telemetry) {
|
||||
m_telemetry->send_event("error_cause", "pytorch_" + op_type);
|
||||
}
|
||||
ops_str << op_type << '\n';
|
||||
|
||||
std::string norm_err;
|
||||
try {
|
||||
normalize(converted_model);
|
||||
} catch (const std::exception& e) {
|
||||
norm_err = "\n-- normalize step failed with: " + std::string(e.what());
|
||||
}
|
||||
FRONT_END_OP_CONVERSION_CHECK(unconverted_ops_types.size() == 0,
|
||||
"Model wasn't fully converted. Unconverted operation types:\n" + ops_str.str());
|
||||
|
||||
const auto& unconverted_ops = get_unconverted_types_from_model(converted_model);
|
||||
for (auto&& op : unconverted_ops) {
|
||||
if (m_telemetry) {
|
||||
m_telemetry->send_event("error_cause", "pytorch_" + op.first);
|
||||
}
|
||||
}
|
||||
bool is_conversion_successful = unconverted_ops.size() == 0 && norm_err.empty();
|
||||
FRONT_END_GENERAL_CHECK(is_conversion_successful, pack_detailed_failure_report(unconverted_ops, norm_err));
|
||||
return converted_model;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ OutputVector NodeContext::as_constant() const {
|
||||
auto fw_node = std::make_shared<PtFrameworkNode>(m_decoder, OutputVector{});
|
||||
auto attrs = fw_node->get_attrs();
|
||||
attrs["string_value"] = str;
|
||||
attrs[PtFrameworkNode::failed_conversion_key] =
|
||||
"String constant cannot be converted to OpenVINO opset and should be removed by consuming operation.";
|
||||
fw_node->set_attrs(attrs);
|
||||
return {fw_node};
|
||||
} else if (dtype.is<type::PyNone>()) {
|
||||
@@ -33,6 +35,8 @@ OutputVector NodeContext::as_constant() const {
|
||||
auto fw_node = std::make_shared<PtFrameworkNode>(m_decoder, OutputVector{});
|
||||
auto attrs = fw_node->get_attrs();
|
||||
attrs["none_value"] = "";
|
||||
attrs[PtFrameworkNode::failed_conversion_key] =
|
||||
"None constant cannot be converted to OpenVINO opset and should be removed by consuming operation.";
|
||||
fw_node->set_attrs(attrs);
|
||||
return {fw_node};
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "openvino/frontend/pytorch/node_context.hpp"
|
||||
#include "openvino/op/concat.hpp"
|
||||
#include "openvino/op/parameter.hpp"
|
||||
#include "pt_framework_node.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
@@ -12,6 +13,8 @@ namespace frontend {
|
||||
namespace pytorch {
|
||||
namespace op {
|
||||
|
||||
using namespace ov::op;
|
||||
|
||||
OutputVector translate_cat(const NodeContext& context) {
|
||||
// This translator is only needed to get axis as constant from external scope
|
||||
num_inputs_check(context, 2, 2);
|
||||
@@ -24,9 +27,14 @@ OutputVector translate_cat(const NodeContext& context) {
|
||||
// If this fails it means axis is dynamic and aten::cat will be converted to fw node in regular pipeline
|
||||
attrs["axis"] = std::to_string(axis);
|
||||
fw_node->set_attrs(attrs);
|
||||
return {context.mark_node(std::dynamic_pointer_cast<Node>(fw_node))};
|
||||
return {context.mark_node(fw_node)};
|
||||
} else {
|
||||
auto first_elem = list_elems.front().get_node_shared_ptr();
|
||||
FRONT_END_OP_CONVERSION_CHECK(list_elems.size() > 1 || !ov::as_type_ptr<v0::Parameter>(first_elem),
|
||||
"aten::cat is located inside body while inputs are located outside of the body. "
|
||||
"This case is not supported.");
|
||||
}
|
||||
auto concat = std::make_shared<ov::op::v0::Concat>(OutputVector(list_elems.begin(), list_elems.end()), axis);
|
||||
auto concat = std::make_shared<v0::Concat>(OutputVector(list_elems.begin(), list_elems.end()), axis);
|
||||
return {context.mark_node(concat)};
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace frontend {
|
||||
namespace pytorch {
|
||||
namespace op {
|
||||
|
||||
using namespace ov::op;
|
||||
|
||||
OutputVector translate_getitem(const NodeContext& context) {
|
||||
num_inputs_check(context, 2, 2);
|
||||
auto input = context.get_input(0);
|
||||
@@ -34,8 +36,8 @@ OutputVector translate_getitem(const NodeContext& context) {
|
||||
return {list_elems.at(getitem_idx)};
|
||||
}
|
||||
auto getitem_idx = context.get_input(1);
|
||||
auto zero = context.mark_node(ov::op::v0::Constant::create(element::i32, Shape{}, {0}));
|
||||
return {context.mark_node(std::make_shared<ov::op::v8::Gather>(input, getitem_idx, zero))};
|
||||
auto zero = context.mark_node(v0::Constant::create(element::i32, Shape{}, {0}));
|
||||
return {context.mark_node(std::make_shared<v8::Gather>(input, getitem_idx, zero))};
|
||||
};
|
||||
|
||||
} // namespace op
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace pytorch {
|
||||
class PtFrameworkNode : public ov::op::util::FrameworkNode {
|
||||
public:
|
||||
OPENVINO_OP("PtFrameworkNode", "util", ::ov::op::util::FrameworkNode);
|
||||
static constexpr const char* op_type_key = "PtTypeName";
|
||||
static constexpr const char* schema_key = "PtSchema";
|
||||
static constexpr const char* failed_conversion_key = "PtException";
|
||||
|
||||
PtFrameworkNode(const std::shared_ptr<TorchDecoder>& decoder,
|
||||
const OutputVector& inputs,
|
||||
@@ -23,11 +26,11 @@ public:
|
||||
ov::op::util::FrameworkNodeAttrs attrs;
|
||||
attrs.set_type_name("PTFrameworkNode");
|
||||
if (is_backprop) {
|
||||
attrs["PtTypeName"] = m_decoder->get_op_type() + "_backprop";
|
||||
attrs["PtSchema"] = "None";
|
||||
attrs[op_type_key] = m_decoder->get_op_type() + "_backprop";
|
||||
attrs[schema_key] = "None";
|
||||
} else {
|
||||
attrs["PtTypeName"] = m_decoder->get_op_type();
|
||||
attrs["PtSchema"] = m_decoder->get_schema();
|
||||
attrs[op_type_key] = m_decoder->get_op_type();
|
||||
attrs[schema_key] = m_decoder->get_schema();
|
||||
}
|
||||
set_attrs(attrs);
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
#include <tuple>
|
||||
|
||||
#include "openvino/frontend/pytorch/decoder.hpp"
|
||||
#include "openvino/op/util/framework_node.hpp"
|
||||
#include "openvino/opsets/opset10.hpp"
|
||||
#include "openvino/pass/graph_rewrite.hpp"
|
||||
#include "openvino/pass/pattern/op/wrap_type.hpp"
|
||||
#include "pt_framework_node.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace ov {
|
||||
@@ -69,7 +69,7 @@ std::shared_ptr<FrameworkNode> cast_internal_node(std::shared_ptr<Node> node, co
|
||||
if (!fw_node) {
|
||||
return nullptr;
|
||||
}
|
||||
if (fw_node->get_attrs().find("PtTypeName") != fw_node->get_attrs().end()) {
|
||||
if (fw_node->get_attrs().find(PtFrameworkNode::op_type_key) != fw_node->get_attrs().end()) {
|
||||
// This is FW node, not PT FW internal node, don't mix them
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ AppendListUnpackReplacer::AppendListUnpackReplacer() {
|
||||
auto getitem_index_const = std::dynamic_pointer_cast<ov::op::v0::Constant>(getitem_index_ptr);
|
||||
auto index_val = getitem_index_const->cast_vector<int64_t>();
|
||||
if (index_val.size() != 1) {
|
||||
add_exception_to_fw_node(list_unpack, "prim::ListUnpack: index of aten::__getitem__ is not scalar.");
|
||||
return false;
|
||||
}
|
||||
auto index = index_val[0];
|
||||
|
||||
@@ -42,16 +42,22 @@ AtenCatToConcat::AtenCatToConcat() {
|
||||
if (cat->get_input_size() > 1) {
|
||||
auto axis_node = cat->get_input_node_shared_ptr(1);
|
||||
auto axis_const = std::dynamic_pointer_cast<ov::op::v0::Constant>(axis_node);
|
||||
if (!axis_const)
|
||||
if (!axis_const) {
|
||||
add_exception_to_fw_node(cat, "aten::cat unsupported case: axis is not a constant.");
|
||||
return false;
|
||||
}
|
||||
auto _axis = axis_const->cast_vector<int64_t>();
|
||||
if (_axis.size() != 1)
|
||||
if (_axis.size() != 1) {
|
||||
add_exception_to_fw_node(cat, "aten::cat unsupported case: axis is not a scalar.");
|
||||
return false;
|
||||
}
|
||||
axis = _axis[0];
|
||||
} else {
|
||||
const auto& attrs = cat->get_attrs();
|
||||
if (attrs.find("axis") == attrs.end())
|
||||
if (attrs.find("axis") == attrs.end()) {
|
||||
add_exception_to_fw_node(cat, "aten::cat unsupported case: axis not found in attributes.");
|
||||
return false;
|
||||
}
|
||||
axis = std::stoll(attrs.at("axis"));
|
||||
}
|
||||
|
||||
@@ -70,11 +76,19 @@ AtenCatToConcat::AtenCatToConcat() {
|
||||
FRONT_END_GENERAL_CHECK(body_result_index >= 0, "Couldn't find descriptor for output.");
|
||||
auto body_result = body->get_results()[body_result_index];
|
||||
auto append = cast_fw_node(body_result->get_input_node_shared_ptr(0), "aten::append");
|
||||
if (!append)
|
||||
if (!append) {
|
||||
add_exception_to_fw_node(
|
||||
cat,
|
||||
"aten::cat unsupported case: aten::append wasn't found inside prim::Loop body.");
|
||||
return false;
|
||||
}
|
||||
auto param = std::dynamic_pointer_cast<ov::op::v0::Parameter>(append->get_input_node_shared_ptr(0));
|
||||
if (!param)
|
||||
if (!param) {
|
||||
add_exception_to_fw_node(cat,
|
||||
"aten::cat unsupported case: input of aten::append inside prim::Loop "
|
||||
"body is not a body input.");
|
||||
return false;
|
||||
}
|
||||
auto body_param_index = body->get_parameter_index(param);
|
||||
FRONT_END_GENERAL_CHECK(body_param_index >= 0, "Couldn't find parameter in body parameters.");
|
||||
int64_t input_index = -1;
|
||||
@@ -86,8 +100,12 @@ AtenCatToConcat::AtenCatToConcat() {
|
||||
}
|
||||
FRONT_END_GENERAL_CHECK(input_index >= 0, "Couldn't find descriptor for input.");
|
||||
auto list_construct = cast_fw_node(loop->get_input_node_shared_ptr(input_index), "prim::ListConstruct");
|
||||
if (!list_construct || list_construct->get_input_size() > 0)
|
||||
if (!list_construct || list_construct->get_input_size() > 0) {
|
||||
add_exception_to_fw_node(cat,
|
||||
"aten::cat unsupported case: aten::append input outside of prim::Loop "
|
||||
"body is not a prim::ListConstruct.");
|
||||
return false;
|
||||
}
|
||||
auto new_result = std::make_shared<ov::op::v0::Result>(append->input_value(1));
|
||||
body->add_results({new_result});
|
||||
auto new_output = loop->get_concatenated_slices(new_result, 0, 1, 1, -1, axis);
|
||||
|
||||
@@ -45,6 +45,7 @@ AtenGetItemReplacer::AtenGetItemReplacer() {
|
||||
if (auto torch_split = cast_fw_node(input_node, "aten::split")) {
|
||||
auto rank = torch_split->input(1).get_partial_shape().rank();
|
||||
if (rank.is_dynamic()) {
|
||||
add_exception_to_fw_node(getitem, "aten::__getitem__: dynamic aten::split rank is not supported.");
|
||||
return false;
|
||||
}
|
||||
if (rank.get_length() == 0) {
|
||||
@@ -99,6 +100,7 @@ AtenGetItemReplacer::AtenGetItemReplacer() {
|
||||
torch_split->get_input_source_output(1));
|
||||
auto index_val = getitem_index_const->cast_vector<int64_t>();
|
||||
if (index_val.size() != 1) {
|
||||
add_exception_to_fw_node(getitem, "aten::__getitem__ index is not scalar.");
|
||||
return false;
|
||||
}
|
||||
auto index = index_val[0];
|
||||
|
||||
@@ -59,6 +59,7 @@ AtenIndexPutReplacer::AtenIndexPutReplacer() {
|
||||
auto acc_const =
|
||||
std::dynamic_pointer_cast<ov::op::v0::Constant>(index_op->input_value(3).get_node_shared_ptr());
|
||||
if (!acc_const) {
|
||||
add_exception_to_fw_node(index_op, "aten::index_put_: non constant accumulate input is not supported.");
|
||||
return false;
|
||||
}
|
||||
bool accumulate = acc_const->cast_vector<bool>()[0];
|
||||
@@ -73,11 +74,14 @@ AtenIndexPutReplacer::AtenIndexPutReplacer() {
|
||||
auto indices_partial_shape = indices.get_partial_shape();
|
||||
if (!indices_partial_shape.rank().is_static()) {
|
||||
// "We support only indices with static rank."
|
||||
add_exception_to_fw_node(index_op, "aten::index_put_: dynamic rank for indices is not supported.");
|
||||
return false;
|
||||
}
|
||||
auto indices_first_dim = indices_partial_shape[0];
|
||||
if (!indices_first_dim.is_static()) {
|
||||
// We support only lists of tensors with static number of elements.
|
||||
add_exception_to_fw_node(index_op,
|
||||
"aten::index_put_: dynamic dynamic number of indices is not supported.");
|
||||
return false;
|
||||
}
|
||||
indices_list_len = indices_first_dim.get_length();
|
||||
|
||||
@@ -150,6 +150,7 @@ AtenIndexToSelect::AtenIndexToSelect() {
|
||||
auto rank = input_node.get_partial_shape().rank();
|
||||
// index transformation supports only tensors with static rank
|
||||
if (rank.is_dynamic()) {
|
||||
add_exception_to_fw_node(index_op, "aten::index: dynamic rank for aten::index input is not supported.");
|
||||
return false;
|
||||
}
|
||||
auto input_shape = std::make_shared<v3::ShapeOf>(input_node, element::i32);
|
||||
@@ -263,6 +264,7 @@ AtenIndexToSelect::AtenIndexToSelect() {
|
||||
gather->set_friendly_name(index_op->get_friendly_name());
|
||||
return true;
|
||||
}
|
||||
add_exception_to_fw_node(index_op, "Unsupported case of aten::index.");
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ AtenStackListConstructReplacer::AtenStackListConstructReplacer() {
|
||||
replace_node(stack, concat);
|
||||
return true;
|
||||
}
|
||||
add_exception_to_fw_node(stack, "Unsupported case of aten::stack.");
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ bool DictResolver::run_on_model(const std::shared_ptr<Model>& model) {
|
||||
const auto inputs = dict_construct_node->input_values();
|
||||
if (inputs.size() % 2) {
|
||||
// inputs must be divisible by 2
|
||||
add_exception_to_fw_node(dict_construct_node,
|
||||
"prim::DictConstruct: inputs number is not divisible by 2.");
|
||||
return false;
|
||||
}
|
||||
ResultVector new_outputs;
|
||||
@@ -34,12 +36,15 @@ bool DictResolver::run_on_model(const std::shared_ptr<Model>& model) {
|
||||
const auto name_node = inputs.at(i);
|
||||
auto fw_node = std::dynamic_pointer_cast<ov::op::util::FrameworkNode>(name_node.get_node_shared_ptr());
|
||||
if (!fw_node) {
|
||||
// odd inputs must contain constant strings encoded as fw nodes
|
||||
add_exception_to_fw_node(
|
||||
dict_construct_node,
|
||||
"prim::DictConstruct: odd inputs must contain constant strings encoded as fw nodes.");
|
||||
return false;
|
||||
}
|
||||
const auto attrs = fw_node->get_attrs();
|
||||
if (attrs.find("string_value") == attrs.end()) {
|
||||
// fw node must contain string value
|
||||
add_exception_to_fw_node(dict_construct_node, "prim::DictConstruct: unexpected dict key format.");
|
||||
return false;
|
||||
}
|
||||
auto name = attrs.at("string_value");
|
||||
|
||||
@@ -31,13 +31,13 @@ AtenEinsumListConstructReplacer::AtenEinsumListConstructReplacer() {
|
||||
auto equation_input = einsum_op->input_value(0).get_node_shared_ptr();
|
||||
auto tensor_list = einsum_op->input_value(1).get_node_shared_ptr();
|
||||
std::string equation;
|
||||
// equation should be string constant
|
||||
if (const auto& fw_node_mode = cast_fw_node(equation_input, "prim::Constant")) {
|
||||
const auto& attrs = fw_node_mode->get_attrs();
|
||||
if (attrs.find("string_value") != attrs.end()) {
|
||||
equation = attrs.at("string_value");
|
||||
}
|
||||
} else {
|
||||
add_exception_to_fw_node(einsum_op, "aten::einsum: equation should be string constant.");
|
||||
return false;
|
||||
}
|
||||
// Check if ListConstruct is an input
|
||||
@@ -54,6 +54,7 @@ AtenEinsumListConstructReplacer::AtenEinsumListConstructReplacer() {
|
||||
replace_node(einsum_op, einsum);
|
||||
return true;
|
||||
}
|
||||
add_exception_to_fw_node(einsum_op, "aten::einsum: unsupported case.");
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ ListConstructReplacer::ListConstructReplacer() {
|
||||
auto rank = input.get_partial_shape().rank();
|
||||
if (rank.is_static() && rank.get_length() > 1) {
|
||||
// if list elements of rank higher then 1D we cannot resolve it
|
||||
add_exception_to_fw_node(list, "unsupported list: all inputs must be 1D.");
|
||||
return false;
|
||||
}
|
||||
// reshape all elements to 1D
|
||||
|
||||
@@ -99,10 +99,10 @@ PrimListConstructPadReplacer::PrimListConstructPadReplacer() {
|
||||
}
|
||||
pad_value = std::make_shared<v1::ConvertLike>(pad_value, input_node);
|
||||
}
|
||||
FRONT_END_OP_CONVERSION_CHECK(PAD_MODES.find(mode) != PAD_MODES.end(),
|
||||
"Unsupported mode: ",
|
||||
mode,
|
||||
"for aten::pad");
|
||||
if (PAD_MODES.find(mode) == PAD_MODES.end()) {
|
||||
add_exception_to_fw_node(pad_op, "Unsupported mode: " + mode + "for aten::pad");
|
||||
return false;
|
||||
}
|
||||
auto pad_mode = PAD_MODES.at(mode);
|
||||
auto pad = std::make_shared<v1::Pad>(input_node, pad_begins_full, pad_ends_full, pad_value, pad_mode);
|
||||
replace_node(pad_op, pad);
|
||||
|
||||
@@ -31,6 +31,7 @@ PrimListUnpackReplacer::PrimListUnpackReplacer() {
|
||||
if (auto torch_split = cast_fw_node(input_node, "aten::split")) {
|
||||
auto rank = torch_split->input(1).get_partial_shape().rank();
|
||||
if (rank.is_dynamic()) {
|
||||
add_exception_to_fw_node(torch_split, "aten::split: dynamic rank is not supported.");
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<Node> split;
|
||||
@@ -171,6 +172,7 @@ PrimListUnpackReplacer::PrimListUnpackReplacer() {
|
||||
auto meshgrid_input_node =
|
||||
cast_fw_node(meshgrid->input_value(0).get_node_shared_ptr(), "prim::ListConstruct");
|
||||
if (!meshgrid_input_node) {
|
||||
add_exception_to_fw_node(input_node, "aten::meshgrid: only prim::ListConstruct supported as input.");
|
||||
return false;
|
||||
}
|
||||
NodeVector rt_copy_from{list_unpack, input_node, meshgrid_input_node};
|
||||
@@ -182,11 +184,13 @@ PrimListUnpackReplacer::PrimListUnpackReplacer() {
|
||||
auto meshgrid_attrs = meshgrid->get_attrs();
|
||||
if (meshgrid_attrs.find("indexing") == meshgrid_attrs.end()) {
|
||||
// Check if "indexing" key is available in meshgrid attributes set in translation.
|
||||
add_exception_to_fw_node(input_node, "aten::meshgrid: couldn't find indexing attribute.");
|
||||
return false;
|
||||
}
|
||||
std::string indexing = meshgrid_attrs.at("indexing");
|
||||
if (indexing != "ij" && indexing != "xy") {
|
||||
// Check if indexing attribute has correct values.
|
||||
add_exception_to_fw_node(input_node, "aten::meshgrid: unsupported indexing mode.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -266,6 +270,9 @@ PrimListUnpackReplacer::PrimListUnpackReplacer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::stringstream msg;
|
||||
msg << "prim::ListUnpack: unsupported input node: " << input_node;
|
||||
add_exception_to_fw_node(list_unpack, msg.str());
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -244,20 +244,26 @@ std::shared_ptr<Model> TranslateSession::convert_pytorch_model(
|
||||
}
|
||||
|
||||
OutputVector TranslateSession::convert_node(const NodeContext& context) {
|
||||
std::string exception;
|
||||
try {
|
||||
auto it = m_translator_map.find(context.get_op_type());
|
||||
if (it != m_translator_map.end()) {
|
||||
return it->second(context);
|
||||
}
|
||||
|
||||
} catch (std::exception& e) {
|
||||
OPENVINO_DEBUG << "Exception happened during conversion of op: " << context.get_op_type()
|
||||
<< " with schema: " << context.get_schema() << ": " << e.what();
|
||||
exception = e.what();
|
||||
} catch (...) {
|
||||
OPENVINO_DEBUG << "Some exception happened during conversion of node of type: " << context.get_op_type();
|
||||
exception = "Unknown exception type.";
|
||||
}
|
||||
// Create PtFrameworkNode for everything that wasn't able to be converted normally
|
||||
return make_framework_node(context);
|
||||
try {
|
||||
// Create PtFrameworkNode for everything that wasn't able to be converted normally
|
||||
return make_framework_node(context, exception);
|
||||
} catch (std::exception& e) {
|
||||
exception += " Exception happened while creating FrameworkNode with subgraphs: " + std::string(e.what());
|
||||
} catch (...) {
|
||||
exception += " Unknown exception happened while creating FrameworkNode with subgraphs";
|
||||
}
|
||||
return make_framework_node_ignore_bodies(context, exception);
|
||||
}
|
||||
|
||||
void TranslateSession::encode_tensor_name(Output<Node> output,
|
||||
|
||||
@@ -188,21 +188,43 @@ Output<Node> concat_list_construct(const Output<Node>& input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
OutputVector make_framework_node(const NodeContext& context) {
|
||||
namespace {
|
||||
std::shared_ptr<PtFrameworkNode> create_fw_node_with_exception(const NodeContext& context,
|
||||
const ov::OutputVector& inputs,
|
||||
size_t num_outputs,
|
||||
const std::string& exception_message) {
|
||||
auto fw_node = std::make_shared<PtFrameworkNode>(context.get_decoder(), inputs, num_outputs);
|
||||
context.mark_node(fw_node);
|
||||
auto attrs = fw_node->get_attrs();
|
||||
std::string message(exception_message);
|
||||
if (!message.empty()) {
|
||||
message = "Exception happened during conversion of operation " + fw_node->get_friendly_name() +
|
||||
" with schema " + context.get_schema() + '\n' + message;
|
||||
}
|
||||
attrs[PtFrameworkNode::failed_conversion_key] = message;
|
||||
fw_node->set_attrs(attrs);
|
||||
return fw_node;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
OutputVector make_framework_node_ignore_bodies(const NodeContext& context, const std::string& exception) {
|
||||
auto fw_node = create_fw_node_with_exception(context, context.inputs(), context.get_output_size() + 1, exception);
|
||||
return fw_node->outputs();
|
||||
}
|
||||
|
||||
OutputVector make_framework_node(const NodeContext& context, const std::string& exception) {
|
||||
auto schema = context.get_schema();
|
||||
// TODO: properly process schema to get the actual position of mutable input
|
||||
// Hack. Can indicate mutable inputs, but can it be reliable?
|
||||
if (schema.find('!') != std::string::npos) {
|
||||
// We create additional output for such nodes. It contains new tensor that represents input that was changed.
|
||||
auto fw_node =
|
||||
std::make_shared<PtFrameworkNode>(context.get_decoder(), context.inputs(), context.get_output_size() + 1);
|
||||
fw_node->set_friendly_name(context.get_op_type());
|
||||
create_fw_node_with_exception(context, context.inputs(), context.get_output_size() + 1, exception);
|
||||
auto outputs = fw_node->outputs();
|
||||
// Usually mutated input index is 0, because it is usually "self" input, so we need to replace this tensor with
|
||||
// output we created.
|
||||
context.mutate_input(0, outputs.back());
|
||||
OPENVINO_DEBUG << "Created node with mutated 0 input. Schema: " << schema << '\n';
|
||||
context.mark_node(fw_node);
|
||||
// For simplification we do not expect such operations to have extra bodies
|
||||
FRONT_END_OP_CONVERSION_CHECK(context.get_decoder()->get_subgraph_size() == 0,
|
||||
"Mutable operation has subgraphs.");
|
||||
@@ -251,9 +273,10 @@ OutputVector make_framework_node(const NodeContext& context) {
|
||||
num_body_outs > context.get_output_size() ? num_body_outs - context.get_output_size() : 0;
|
||||
|
||||
// We need to reduce number of outputs, because some outputs are outputs from body
|
||||
auto fw_node = std::make_shared<PtFrameworkNode>(context.get_decoder(),
|
||||
context.inputs(),
|
||||
context.get_output_size() - num_body_outs + num_skip_body_outputs);
|
||||
auto fw_node = create_fw_node_with_exception(context,
|
||||
context.inputs(),
|
||||
context.get_output_size() - num_body_outs + num_skip_body_outputs,
|
||||
exception);
|
||||
fw_node->set_friendly_name(context.get_op_type());
|
||||
for (size_t i = 0; i < bodies.size(); ++i) {
|
||||
fw_node->set_function(static_cast<int>(i), bodies[i]);
|
||||
@@ -301,7 +324,7 @@ std::shared_ptr<ov::op::util::FrameworkNode> cast_fw_node(std::shared_ptr<Node>
|
||||
return nullptr;
|
||||
}
|
||||
const auto& attrs = fw_node->get_attrs();
|
||||
if (attrs.find("PtTypeName") == attrs.end() || attrs.at("PtTypeName") != type) {
|
||||
if (attrs.find(PtFrameworkNode::op_type_key) == attrs.end() || attrs.at(PtFrameworkNode::op_type_key) != type) {
|
||||
return nullptr;
|
||||
}
|
||||
return fw_node;
|
||||
@@ -441,12 +464,12 @@ std::deque<Output<Node>> get_list_as_outputs(const Output<Node>& start) {
|
||||
while (const auto& input_fw_node =
|
||||
std::dynamic_pointer_cast<ov::op::util::FrameworkNode>(current_output.get_node_shared_ptr())) {
|
||||
const auto& attrs = input_fw_node->get_attrs();
|
||||
if (attrs.find("PtTypeName") == attrs.end()) {
|
||||
if (attrs.find(PtFrameworkNode::op_type_key) == attrs.end()) {
|
||||
break;
|
||||
}
|
||||
if (attrs.at("PtTypeName") == "aten::append") {
|
||||
if (attrs.at(PtFrameworkNode::op_type_key) == "aten::append") {
|
||||
res.push_front(input_fw_node->input(1).get_source_output());
|
||||
} else if (attrs.at("PtTypeName") == "aten::add") {
|
||||
} else if (attrs.at(PtFrameworkNode::op_type_key) == "aten::add") {
|
||||
const auto&& lhs_list = get_list_as_outputs(input_fw_node->input(1).get_source_output());
|
||||
res.insert(res.end(), lhs_list.begin(), lhs_list.end());
|
||||
} else {
|
||||
@@ -466,6 +489,14 @@ std::deque<Output<Node>> get_list_as_outputs(const Output<Node>& start) {
|
||||
return res;
|
||||
}
|
||||
|
||||
void add_exception_to_fw_node(std::shared_ptr<Node> node, const std::string& msg) {
|
||||
if (auto fw_node = ov::as_type_ptr<PtFrameworkNode>(node)) {
|
||||
auto attrs = fw_node->get_attrs();
|
||||
attrs[PtFrameworkNode::failed_conversion_key] = msg;
|
||||
fw_node->set_attrs(attrs);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pytorch
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
@@ -53,13 +53,16 @@ op::PadType convert_pad(const std::string& pt_pad);
|
||||
|
||||
Output<Node> concat_list_construct(const Output<Node>& input);
|
||||
|
||||
OutputVector make_framework_node(const NodeContext& context);
|
||||
OutputVector make_framework_node_ignore_bodies(const NodeContext& context, const std::string& exception);
|
||||
OutputVector make_framework_node(const NodeContext& context, const std::string& exception);
|
||||
|
||||
std::shared_ptr<op::util::FrameworkNode> cast_fw_node(std::shared_ptr<Node> node, const std::string& type);
|
||||
|
||||
// TODO: Eliminate the need of this function by implementing more accurate custom data type handling
|
||||
Any simplified_type_interpret(Any type);
|
||||
|
||||
void add_exception_to_fw_node(std::shared_ptr<Node> node, const std::string& msg);
|
||||
|
||||
void align_eltwise_input_types(const NodeContext& context,
|
||||
Output<Node>& lhs,
|
||||
Output<Node>& rhs,
|
||||
|
||||
@@ -664,7 +664,7 @@ def create_pt_model_with_custom_op():
|
||||
return MyModel()
|
||||
|
||||
|
||||
class ConvertRaisesExampleInputs(unittest.TestCase):
|
||||
class ConvertRaises(unittest.TestCase):
|
||||
def test_example_inputs(self):
|
||||
from openvino.tools.mo import convert_model
|
||||
pytorch_model = create_pt_model_with_custom_op()
|
||||
@@ -672,3 +672,18 @@ class ConvertRaisesExampleInputs(unittest.TestCase):
|
||||
# Check that mo raises error message of wrong argument.
|
||||
with self.assertRaisesRegex(AssertionError, ".*argument is not recognized.*"):
|
||||
convert_model(pytorch_model, example_inputs=(torch.tensor(1),))
|
||||
|
||||
def test_failed_extension(self):
|
||||
from openvino.tools.mo import convert_model
|
||||
from openvino.frontend.pytorch import ConversionExtension
|
||||
|
||||
inp_shapes = [1, 3, 20, 20]
|
||||
pt_model = make_pt_model_one_input()
|
||||
|
||||
def relu_bad(n):
|
||||
assert False, "Something happened"
|
||||
|
||||
# Check that mo raises error message of wrong argument.
|
||||
with self.assertRaisesRegex(Exception, ".*Conversion is failed for: aten::relu.*"):
|
||||
convert_model(pt_model, input=(inp_shapes, np.float32), extensions=[
|
||||
ConversionExtension("aten::relu", relu_bad)])
|
||||
Reference in new issue
Block a user