From 149381ef8cda82b42d5dfaa0a4ab81700e0a7322 Mon Sep 17 00:00:00 2001 From: Georgy Krivoruchko Date: Thu, 14 Mar 2024 14:47:53 +0400 Subject: [PATCH] [ONNX] Reduce* refactoring (#23429) ### Details: - Refactored source code to be able co-work with others - Removed unnecessary comments - Added check for a wrong input type - Added tests for an exception for wrong input type ### Tickets: - 125493 --- src/frontends/onnx/frontend/src/op/reduce.cpp | 64 ++++--- src/frontends/onnx/frontend/src/op/reduce.hpp | 157 +++--------------- .../models/reduce_wrong_type_v1.prototxt | 48 ++++++ .../models/reduce_wrong_type_v2.prototxt | 48 ++++++ .../onnx/tests/onnx_import_exceptions.cpp | 34 ++++ 5 files changed, 195 insertions(+), 156 deletions(-) create mode 100644 src/frontends/onnx/tests/models/reduce_wrong_type_v1.prototxt create mode 100644 src/frontends/onnx/tests/models/reduce_wrong_type_v2.prototxt diff --git a/src/frontends/onnx/frontend/src/op/reduce.cpp b/src/frontends/onnx/frontend/src/op/reduce.cpp index 3322af52e76447..9a8c1cd4ec3ea2 100644 --- a/src/frontends/onnx/frontend/src/op/reduce.cpp +++ b/src/frontends/onnx/frontend/src/op/reduce.cpp @@ -90,74 +90,98 @@ std::shared_ptr get_reduction_axes_from_attr(const Node& node) { return v0::Constant::create(ov::element::i64, ov::Shape{reduction_axes.size()}, reduction_axes); } +const std::set supported_types_v1 = + {element::u32, element::u64, element::i32, element::i64, element::f16, element::f32, element::f64}; +const std::set supported_types_v2 = + {element::u32, element::u64, element::i32, element::i64, element::f16, element::f32, element::f64, element::bf16}; + template -std::shared_ptr make_ng_reduction_op(const Node& node, - const ov::Output& ng_input, +std::shared_ptr make_ov_reduction_op(const Node& node, + const ov::Output& ov_input, + const std::set& supported_types, bool axes_as_attr = true) { const std::int64_t keepdims = node.get_attribute_value("keepdims", 1); + CHECK_VALID_NODE(node, + supported_types.find(ov_input.get_element_type()) != supported_types.end(), + "Unsupported input type ", + ov_input.get_element_type().get_type_name()); + const auto reduction_axes = axes_as_attr ? get_reduction_axes_from_attr(node) : get_reduction_axes_from_input(node); if (reduction_axes != nullptr) { - return std::make_shared(ng_input, reduction_axes, static_cast(keepdims)); + return std::make_shared(ov_input, reduction_axes, static_cast(keepdims)); } else { return set_1::identity(node).at(0).get_node_shared_ptr(); } } } // namespace -namespace set_13 { -ov::OutputVector reduce_sum(const ov::frontend::onnx::Node& node) { - return {make_ng_reduction_op(node, node.get_ov_inputs().at(0), false)}; -} -} // namespace set_13 - namespace set_1 { ov::OutputVector reduce_log_sum(const ov::frontend::onnx::Node& node) { - const ov::Output sum_node = make_ng_reduction_op(node, node.get_ov_inputs().at(0)); + const ov::Output sum_node = + make_ov_reduction_op(node, node.get_ov_inputs().at(0), supported_types_v1); return {std::make_shared(sum_node)}; } ov::OutputVector reduce_log_sum_exp(const ov::frontend::onnx::Node& node) { const auto exp_node = std::make_shared(node.get_ov_inputs().at(0)); - const ov::Output sum_node = make_ng_reduction_op(node, exp_node); + const ov::Output sum_node = make_ov_reduction_op(node, exp_node, supported_types_v1); return {std::make_shared(sum_node)}; } ov::OutputVector reduce_l1(const ov::frontend::onnx::Node& node) { - return {make_ng_reduction_op(node, node.get_ov_inputs().at(0))}; + return {make_ov_reduction_op(node, node.get_ov_inputs().at(0), supported_types_v1)}; } ov::OutputVector reduce_l2(const ov::frontend::onnx::Node& node) { - return {make_ng_reduction_op(node, node.get_ov_inputs().at(0))}; + return {make_ov_reduction_op(node, node.get_ov_inputs().at(0), supported_types_v1)}; } ov::OutputVector reduce_max(const ov::frontend::onnx::Node& node) { - return {make_ng_reduction_op(node, node.get_ov_inputs().at(0))}; + return {make_ov_reduction_op(node, node.get_ov_inputs().at(0), supported_types_v1)}; } ov::OutputVector reduce_mean(const ov::frontend::onnx::Node& node) { - return {make_ng_reduction_op(node, node.get_ov_inputs().at(0))}; + return {make_ov_reduction_op(node, node.get_ov_inputs().at(0), supported_types_v1)}; } ov::OutputVector reduce_min(const ov::frontend::onnx::Node& node) { - return {make_ng_reduction_op(node, node.get_ov_inputs().at(0))}; + return {make_ov_reduction_op(node, node.get_ov_inputs().at(0), supported_types_v1)}; } ov::OutputVector reduce_prod(const ov::frontend::onnx::Node& node) { - return {make_ng_reduction_op(node, node.get_ov_inputs().at(0))}; + return {make_ov_reduction_op(node, node.get_ov_inputs().at(0), supported_types_v1)}; } ov::OutputVector reduce_sum(const ov::frontend::onnx::Node& node) { - return {make_ng_reduction_op(node, node.get_ov_inputs().at(0))}; + return {make_ov_reduction_op(node, node.get_ov_inputs().at(0), supported_types_v1)}; } ov::OutputVector reduce_sum_square(const ov::frontend::onnx::Node& node) { const auto input = ov::Output{node.get_ov_inputs().at(0)}; const auto square_node = std::make_shared(input, input); - return {make_ng_reduction_op(node, square_node)}; + return {make_ov_reduction_op(node, square_node, supported_types_v1)}; } - } // namespace set_1 + +/* + Opset 11 is skipped because there are no significant difference between opset1 and opset 11. + Found difference is: + 1. Operations (except ReduceMin and ReduceMax) are lost mention of zero-rank input behavior + from their description. We assume it shouldn't be worse than opset 1. + 2. Opset 11 introduced requirement for axes values to be in a range [-r, r-1] where r = rank(data) + Same time Reduce* operations in OpenVINO has same requirement from first version +*/ + +namespace set_13 { +ov::OutputVector reduce_sum(const ov::frontend::onnx::Node& node) { + return {make_ov_reduction_op(node, node.get_ov_inputs().at(0), supported_types_v2, false)}; +} +} // namespace set_13 + +namespace set_18 { +// Placeholder +} // namespace set_18 } // namespace op } // namespace onnx } // namespace frontend diff --git a/src/frontends/onnx/frontend/src/op/reduce.hpp b/src/frontends/onnx/frontend/src/op/reduce.hpp index 25b4fe5b73a2da..defa7c4e33dce2 100644 --- a/src/frontends/onnx/frontend/src/op/reduce.hpp +++ b/src/frontends/onnx/frontend/src/op/reduce.hpp @@ -10,162 +10,47 @@ namespace ov { namespace frontend { namespace onnx { namespace op { -namespace set_13 { -/// \brief Compute the sum of the input tensor's elements along the provided -/// axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor has the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// -ov::OutputVector reduce_sum(const ov::frontend::onnx::Node& node); -} // namespace set_13 namespace set_1 { -/// \brief Compute the log sum of the input tensor's elements along the -/// provided axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor have the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// ov::OutputVector reduce_log_sum(const ov::frontend::onnx::Node& node); +} // namespace set_1 -/// \brief Compute the log sum exponent of the input tensor's elements along -/// the provided axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor have the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// +namespace set_1 { ov::OutputVector reduce_log_sum_exp(const ov::frontend::onnx::Node& node); +} // namespace set_1 -/// \brief Compute the L1 norm of the input tensor's element along the provided -/// axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor have the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// +namespace set_1 { ov::OutputVector reduce_l1(const ov::frontend::onnx::Node& node); +} // namespace set_1 -/// \brief Compute the L2 norm of the input tensor's element along the provided -/// axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor have the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// +namespace set_1 { ov::OutputVector reduce_l2(const ov::frontend::onnx::Node& node); +} // namespace set_1 -/// \brief Compute the maximum value of the input tensor's elements along the -/// provided axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor have the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// +namespace set_1 { ov::OutputVector reduce_max(const ov::frontend::onnx::Node& node); +} // namespace set_1 -/// \brief Compute the mean value of the input tensor's elements along the -/// provided axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor have the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// +namespace set_1 { ov::OutputVector reduce_mean(const ov::frontend::onnx::Node& node); +} // namespace set_1 -/// \brief Compute the minimum value of the input tensor's elements along the -/// provided axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor have the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// +namespace set_1 { ov::OutputVector reduce_min(const ov::frontend::onnx::Node& node); +} // namespace set_1 -/// \brief Compute the product of the input tensor's elements along the -/// provided axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor have the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// +namespace set_1 { ov::OutputVector reduce_prod(const ov::frontend::onnx::Node& node); +} // namespace set_1 -/// \brief Compute the sum of the input tensor's elements along the provided -/// axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor have the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// +namespace set_1 { ov::OutputVector reduce_sum(const ov::frontend::onnx::Node& node); +} // namespace set_1 +namespace set_13 { +ov::OutputVector reduce_sum(const ov::frontend::onnx::Node& node); +} // namespace set_13 -/// \brief Compute the sum square of the input tensor's element along the -/// provided axes. -/// -/// \par Overview -/// The output tensor has the same rank as the input if Node attribute keepdims -/// equals 1. If keepdims equals 0, then the output tensor have the reduced -/// dimension pruned. -/// -/// \param[in] node The ONNX node representing operation. -/// -/// \return The OV node equivalent of the ONNX operation. -/// +namespace set_1 { ov::OutputVector reduce_sum_square(const ov::frontend::onnx::Node& node); - } // namespace set_1 } // namespace op } // namespace onnx diff --git a/src/frontends/onnx/tests/models/reduce_wrong_type_v1.prototxt b/src/frontends/onnx/tests/models/reduce_wrong_type_v1.prototxt new file mode 100644 index 00000000000000..efd2fbc568c24f --- /dev/null +++ b/src/frontends/onnx/tests/models/reduce_wrong_type_v1.prototxt @@ -0,0 +1,48 @@ +ir_version: 3 +producer_name: "OpenVINO ONNX Frontend" +graph { + node { + input: "A" + output: "B" + op_type: "ReduceProd" + } + name: "compute_graph" + input { + name: "A" + type { + tensor_type { + elem_type: 16 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4 + } + dim { + dim_value: 4 + } + } + } + } + } + output { + name: "B" + type { + tensor_type { + elem_type: 16 + shape { + dim { + dim_value: 1 + } + } + } + } + } +} +opset_import { + version: 1 +} diff --git a/src/frontends/onnx/tests/models/reduce_wrong_type_v2.prototxt b/src/frontends/onnx/tests/models/reduce_wrong_type_v2.prototxt new file mode 100644 index 00000000000000..dbe50364fbe5f7 --- /dev/null +++ b/src/frontends/onnx/tests/models/reduce_wrong_type_v2.prototxt @@ -0,0 +1,48 @@ +ir_version: 3 +producer_name: "OpenVINO ONNX Frontend" +graph { + node { + input: "A" + output: "B" + op_type: "ReduceSum" + } + name: "compute_graph" + input { + name: "A" + type { + tensor_type { + elem_type: 3 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4 + } + dim { + dim_value: 4 + } + } + } + } + } + output { + name: "B" + type { + tensor_type { + elem_type: 3 + shape { + dim { + dim_value: 1 + } + } + } + } + } +} +opset_import { + version: 13 +} diff --git a/src/frontends/onnx/tests/onnx_import_exceptions.cpp b/src/frontends/onnx/tests/onnx_import_exceptions.cpp index 403f9664f41950..586f5bbc3d4d2f 100644 --- a/src/frontends/onnx/tests/onnx_import_exceptions.cpp +++ b/src/frontends/onnx/tests/onnx_import_exceptions.cpp @@ -58,3 +58,37 @@ TEST(onnx_importer, exception_msg_std_err_wrapped) { FAIL() << "The ONNX model importer failed for unexpected reason"; } } + +TEST(onnx_importer, exception_msg_onnx_reduce_wrong_type_v1) { + try { + convert_model("reduce_wrong_type_v1.onnx"); + // Should have thrown, so fail if it didn't + FAIL() << "ONNX Importer did not detected incorrect model!"; + } catch (const ::ov::Exception& e) { + EXPECT_HAS_SUBSTRING(e.what(), std::string("Unsupported input type bf16")); + } + // On MacOS after we re-throw ov::Exception exception, we couldn't catch it as is, + // thus below workaround. + catch (const std::exception& e) { + EXPECT_HAS_SUBSTRING(e.what(), std::string("Unsupported input type bf16")); + } catch (...) { + FAIL() << "The ONNX model importer failed for unexpected reason"; + } +} + +TEST(onnx_importer, exception_msg_onnx_reduce_wrong_type_v2) { + try { + convert_model("reduce_wrong_type_v2.onnx"); + // Should have thrown, so fail if it didn't + FAIL() << "ONNX Importer did not detected incorrect model!"; + } catch (const ::ov::Exception& e) { + EXPECT_HAS_SUBSTRING(e.what(), std::string("Unsupported input type i8")); + } + // On MacOS after we re-throw ov::Exception exception, we couldn't catch it as is, + // thus below workaround. + catch (const std::exception& e) { + EXPECT_HAS_SUBSTRING(e.what(), std::string("Unsupported input type i8")); + } catch (...) { + FAIL() << "The ONNX model importer failed for unexpected reason"; + } +}