Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d8b40d1
Add normalizer ovtokenizer ops (#3)
sfatimar Apr 24, 2026
2a4c35c
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 24, 2026
639fd88
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 25, 2026
fd21dcb
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 27, 2026
cea5982
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 27, 2026
9ef97c7
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 27, 2026
3c111d7
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 27, 2026
69e6229
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 27, 2026
54095ef
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 28, 2026
b390d5e
Removed Layer tests
sfatimar Apr 28, 2026
f451665
Deleted Normalizer Model Prototxt
sfatimar Apr 28, 2026
e299998
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 28, 2026
90c88e4
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 28, 2026
82ab91b
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 28, 2026
b81d0f1
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 29, 2026
024bd0b
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 29, 2026
ce4a8c9
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 29, 2026
3e49732
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 29, 2026
a86e9d0
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 29, 2026
a040cf1
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 29, 2026
c116aa1
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 29, 2026
6e1dd9e
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 30, 2026
48aa283
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 30, 2026
46c46eb
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 30, 2026
93f0a4b
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 30, 2026
8bddc6b
Merge branch 'master' into add_normalizer_ovtokenizer_ops
sfatimar Apr 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/frontends/onnx/docs/check_supported_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ def run_in_ci():
"OPENVINO_ONNX_DOMAIN":"org.openvinotoolkit",
"MICROSOFT_DOMAIN":"com.microsoft",
"PYTORCH_ATEN_DOMAIN":"org.pytorch.aten",
"MMDEPLOY_DOMAIN":"mmdeploy"
"MMDEPLOY_DOMAIN":"mmdeploy",
"AIONNX_ML_DOMAIN":"ai.onnx.ml"
}

hdr = ""
Expand Down
1 change: 1 addition & 0 deletions src/frontends/onnx/docs/supported_ops.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,4 @@ OpenVINO provides support for operations of Default Opset (empty in table below)
|org.pytorch.aten |adaptive_avg_pool2d |1 |1 | |
|mmdeploy |MMCVRoIAlignRotated |1 |1 | |
|mmdeploy |NMSRotated |1 |1 | |
|ai.onnx.ml |Normalizer |1 |1 | |
1 change: 1 addition & 0 deletions src/frontends/onnx/frontend/src/core/operator_set.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ extern const char* OPENVINO_ONNX_DOMAIN;
extern const char* MICROSOFT_DOMAIN;
extern const char* PYTORCH_ATEN_DOMAIN;
extern const char* MMDEPLOY_DOMAIN;
extern const char* AIONNX_ML_DOMAIN;

/// \brief Registering a versions range of translator in global map of translators (preferred to use)
extern bool register_translator(const std::string name,
Expand Down
40 changes: 39 additions & 1 deletion src/frontends/onnx/frontend/src/frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,39 @@ void FrontEnd::normalize(const std::shared_ptr<ov::Model>& model) const {
manager.run_passes(model);
}

ov::frontend::AdditionalErrorCallback make_onnx_tokenizer_callback() {
return [](const std::set<std::string>& unsupported_ops) -> std::string {
std::stringstream additional_info;
additional_info << "To facilitate the conversion of unsupported operations, refer to Frontend Extension "
"documentation: "
"https://docs.openvino.ai/latest/openvino_docs_Extensibility_UG_Frontend_Extensions.html \n";

// Check for tokenizer operations
const auto& all_tokenizer_ops = ov::frontend::onnx::get_supported_ops_via_tokenizers();
std::string unsupported_ops_from_tokenizers;
size_t tokenizer_counter = 0;
for (const auto& unsupported_operation : unsupported_ops) {
if (std::find(all_tokenizer_ops.begin(), all_tokenizer_ops.end(), unsupported_operation) !=
all_tokenizer_ops.end()) {
Comment thread
sfatimar marked this conversation as resolved.
if (tokenizer_counter > 0) {
unsupported_ops_from_tokenizers += ", ";
}
unsupported_ops_from_tokenizers += unsupported_operation;
++tokenizer_counter;
Comment thread
sfatimar marked this conversation as resolved.
}
}

if (!unsupported_ops_from_tokenizers.empty()) {
additional_info << "\nEncountered unconverted operation(s) for which openvino-tokenizers package "
"provides conversion extension(s): "
<< unsupported_ops_from_tokenizers
<< ". Install OpenVINO Tokenizers, refer to the documentation: "
"https://docs.openvino.ai/2026/openvino-workflow-generative/ov-tokenizers.html \n";
}
return additional_info.str();
};
}

std::shared_ptr<ov::Model> FrontEnd::convert(const InputModel::Ptr& input_model) const {
auto unify_model = std::dynamic_pointer_cast<unify::InputModel>(input_model);
if (unify_model != nullptr) {
Expand All @@ -300,7 +333,12 @@ std::shared_ptr<ov::Model> FrontEnd::convert(const InputModel::Ptr& input_model)
normalize(converted_model);

const auto report = ov::frontend::collect_unconverted_ops(converted_model, make_onnx_extractor());
ov::frontend::check_unconverted_ops(report, m_extensions.telemetry, "onnx", "[ONNX Frontend] ");
ov::frontend::check_unconverted_ops(report,
m_extensions.telemetry,
"onnx",
"[ONNX Frontend] ",
"",
make_onnx_tokenizer_callback());

return converted_model;
}
Expand Down
81 changes: 81 additions & 0 deletions src/frontends/onnx/frontend/src/op/ai.onnx.ml/normalizer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (C) 2018-2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <cmath>
#include <numeric>

#include "core/operator_set.hpp"
#include "exceptions.hpp"
#include "openvino/frontend/exception.hpp"
#include "openvino/op/abs.hpp"
#include "openvino/op/constant.hpp"
#include "openvino/op/convert.hpp"
#include "openvino/op/divide.hpp"
#include "openvino/op/equal.hpp"
#include "openvino/op/multiply.hpp"
#include "openvino/op/power.hpp"
#include "openvino/op/reduce_max.hpp"
#include "openvino/op/reduce_sum.hpp"
#include "openvino/op/select.hpp"
#include "openvino/op/sqrt.hpp"
#include "utils/common.hpp"

using namespace ov::op;

Comment thread
sfatimar marked this conversation as resolved.
namespace ov {
namespace frontend {
namespace onnx {
namespace ai_onnx_ml {
namespace opset_1 {

const ov::Output<ov::Node> check_zero_divisor(const ov::Output<ov::Node>& val) {
const auto zero = ov::op::v0::Constant::create(val.get_element_type(), ov::Shape{}, {0.0f});
const auto one_val = ov::op::v0::Constant::create(val.get_element_type(), ov::Shape{}, {1.0f});
const auto is_zero = std::make_shared<ov::op::v1::Equal>(val, zero);
return std::make_shared<ov::op::v1::Select>(is_zero, one_val, val);
}

ov::OutputVector normalizer(const ov::frontend::onnx::Node& node) {
// Step 1: Get input tensor
const auto input = node.get_ov_inputs()[0];

CHECK_VALID_NODE(node,
input.get_element_type() == ov::element::f64 || input.get_element_type() == ov::element::f32 ||
input.get_element_type() == ov::element::i64 || input.get_element_type() == ov::element::i32,
"Unsupported input type, accepted FP32, FP64, I64, I32 got: ",
input.get_element_type());

const auto normalization_type = node.get_attribute_value<std::string>("norm", "MAX");

CHECK_VALID_NODE(node,
normalization_type == "MAX" || normalization_type == "L1" || normalization_type == "L2",
"Normalization Mode should be either MAX, L1 or L2, got ",
normalization_type);

auto float_input = std::make_shared<ov::op::v0::Convert>(input, ov::element::f32);
const auto x = std::make_shared<ov::op::v0::Abs>(float_input);
const auto axes = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{1}, {-1}); // e.g., last axis
if (normalization_type == "L1") {
const auto sum_x = std::make_shared<ov::op::v1::ReduceSum>(x, axes, true);
const auto safe_x = check_zero_divisor(sum_x);
return {std::make_shared<ov::op::v1::Divide>(float_input, safe_x)};
} else if (normalization_type == "L2") {
const auto x_squared = std::make_shared<ov::op::v1::Multiply>(x, x);
const auto sum_sqrdx = std::make_shared<ov::op::v1::ReduceSum>(x_squared, axes, true);
const auto sqrt_sum_sqrdx = std::make_shared<ov::op::v0::Sqrt>(sum_sqrdx);
const auto safe_x = check_zero_divisor(sqrt_sum_sqrdx);
const auto result = std::make_shared<ov::op::v1::Divide>(float_input, safe_x);
return {result};
} else { // Must be max
const auto max_x = std::make_shared<ov::op::v1::ReduceMax>(x, axes, /*keep_dims=*/true);
const auto safe_x = check_zero_divisor(max_x);
return {std::make_shared<ov::op::v1::Divide>(float_input, safe_x)};
}
}

ONNX_OP("Normalizer", OPSET_SINCE(1), ai_onnx_ml::opset_1::normalizer, AIONNX_ML_DOMAIN);
} // namespace opset_1
} // namespace ai_onnx_ml
} // namespace onnx
} // namespace frontend
} // namespace ov
4 changes: 4 additions & 0 deletions src/frontends/onnx/frontend/src/ops_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const char* OPENVINO_ONNX_DOMAIN = "org.openvinotoolkit";
const char* MICROSOFT_DOMAIN = "com.microsoft";
const char* PYTORCH_ATEN_DOMAIN = "org.pytorch.aten";
const char* MMDEPLOY_DOMAIN = "mmdeploy";
const char* AIONNX_ML_DOMAIN = "ai.onnx.ml";

// Central storage of supported translators for operations
typedef std::unordered_map<std::string, DomainOpset> SupportedOps;
Expand Down Expand Up @@ -264,6 +265,9 @@ OperatorsBridge::OperatorsBridge() {
// custom ops
}

const std::vector<std::string> get_supported_ops_via_tokenizers() {
return {"StringNormalizer", "LabelEncoder", "Tokenizer", "TfIdfVectorizer"};
}
#undef REGISTER_OPERATOR
#undef REGISTER_OPERATOR_WITH_DOMAIN
} // namespace onnx
Expand Down
2 changes: 2 additions & 0 deletions src/frontends/onnx/frontend/src/ops_bridge.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class OperatorsBridge {
std::unordered_map<std::string, DomainOpset> m_map;
};

const std::vector<std::string> get_supported_ops_via_tokenizers();

} // namespace onnx
} // namespace frontend
} // namespace ov
1 change: 1 addition & 0 deletions src/frontends/onnx/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ endif()
set(MULTI_TEST_SRC
onnx_import.in.cpp
onnx_import_com_microsoft.in.cpp
onnx_import_ml_normalizer.in.cpp
onnx_import_controlflow.in.cpp
onnx_import_const_folding.in.cpp
onnx_import_convpool.in.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
ir_version: 7
producer_name: "OpenVINO ONNX Frontend"
graph {
node {
input: "X"
output: "Y"
name: "Normalizer_L1"
op_type: "Normalizer"
attribute {
name: "norm"
s: "L1"
type: STRING
}
domain: "ai.onnx.ml"
}
name: "normalizer_l1_graph"
input {
name: "X"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 2
}
dim {
dim_value: 3
}
}
}
}
}
output {
name: "Y"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 2
}
dim {
dim_value: 3
}
}
}
}
}
}
opset_import {
version: 15
}
opset_import {
domain: "ai.onnx.ml"
version: 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
ir_version: 7
producer_name: "OpenVINO ONNX Frontend"
graph {
node {
input: "X"
output: "Y"
name: "Normalizer_L2"
op_type: "Normalizer"
attribute {
name: "norm"
s: "L2"
type: STRING
}
domain: "ai.onnx.ml"
}
name: "normalizer_l2_graph"
input {
name: "X"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 3
}
dim {
dim_value: 4
}
}
}
}
}
output {
name: "Y"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 3
}
dim {
dim_value: 4
}
}
}
}
}
}
opset_import {
version: 15
}
opset_import {
domain: "ai.onnx.ml"
version: 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
ir_version: 7
producer_name: "OpenVINO ONNX Frontend"
graph {
node {
input: "X"
output: "Y"
name: "Normalizer_MAX"
op_type: "Normalizer"
attribute {
name: "norm"
s: "MAX"
type: STRING
}
domain: "ai.onnx.ml"
}
name: "normalizer_max_graph"
input {
name: "X"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 2
}
dim {
dim_value: 5
}
}
}
}
}
output {
name: "Y"
type {
tensor_type {
elem_type: 1
shape {
dim {
dim_value: 2
}
dim {
dim_value: 5
}
}
}
}
}
}
opset_import {
version: 15
}
opset_import {
domain: "ai.onnx.ml"
version: 3
}
Loading
Loading