[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:
Maxim Vafin authored and GitHub committed 2023-06-15 12:04:01 +04:00
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
+5 -5
View File
@@ -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));
}
+63 -16
View File
@@ -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 {
+10 -2
View File
@@ -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)};
};
+4 -2
View File
@@ -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);
+2 -2
View File
@@ -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,
+42 -11
View File
@@ -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
+4 -1
View File
@@ -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)])