Skip to content

Commit

Permalink
[core] Util to set (auto) names on model's input, output tensors (#28975
Browse files Browse the repository at this point in the history
)

### Details:
 - Add utils to core to set names on model input/output tensors
 - Define default tensor names and port separator
- Use auto naming in model created by python API. Model outputs will
have got names also.
- The auto naming can be part of Model API but requires to add
validation tools to find and resolve names collision
- Auto naming cannot be added to Model creation in C++ as it can have
impact on (de)serialization, requires more investigation

### Tickets:
 - CVS-159997

---------

Signed-off-by: Raasz, Pawel <[email protected]>
  • Loading branch information
praasz authored Feb 20, 2025
1 parent d9c2aee commit 015fe97
Show file tree
Hide file tree
Showing 10 changed files with 440 additions and 51 deletions.
59 changes: 21 additions & 38 deletions src/bindings/python/src/pyopenvino/graph/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "openvino/core/except.hpp"
#include "openvino/core/graph_util.hpp"
#include "openvino/core/model.hpp" // ov::Model
#include "openvino/core/model_util.hpp"
#include "openvino/core/partial_shape.hpp"
#include "openvino/op/assign.hpp"
#include "openvino/op/parameter.hpp" // ov::op::v0::Parameter
Expand All @@ -30,15 +31,14 @@ using PyRTMap = ov::RTMap;

PYBIND11_MAKE_OPAQUE(PyRTMap);

static void set_tensor_names(const ov::ParameterVector& parameters) {
for (const auto& param : parameters) {
ov::Output<ov::Node> p = param;
if (p.get_node()->output(0).get_names().empty()) {
std::unordered_set<std::string> p_names({p.get_node()->get_friendly_name()});
p.get_node()->output(0).set_names(p_names);
}
}
namespace {
template <class... Args>
std::shared_ptr<ov::Model> make_model_with_tensor_names(Args&&... args) {
auto model = std::make_shared<ov::Model>(std::forward<Args>(args)...);
ov::util::set_tensors_names(ov::AUTO, *model);
return model;
}
} // namespace

static std::shared_ptr<ov::Node> get_node_ptr(std::shared_ptr<ov::Node> node) {
return node;
Expand Down Expand Up @@ -163,9 +163,8 @@ void regclass_graph_Model(py::module m) {
const std::vector<std::shared_ptr<ov::Node>>& nodes,
const ov::ParameterVector& params,
const std::string& name) {
set_tensor_names(params);
const auto sinks = cast_to_sink_vector(nodes);
auto model = std::make_shared<ov::Model>(res, sinks, params, name);
auto model = make_model_with_tensor_names(res, sinks, params, name);
set_correct_variables_for_assign_ops(model, sinks);
return model;
}),
Expand All @@ -189,8 +188,7 @@ void regclass_graph_Model(py::module m) {
model.def(py::init([](const std::vector<std::shared_ptr<ov::Node>>& results,
const ov::ParameterVector& parameters,
const std::string& name) {
set_tensor_names(parameters);
return std::make_shared<ov::Model>(results, parameters, name);
return make_model_with_tensor_names(results, parameters, name);
}),
py::arg("results"),
py::arg("parameters"),
Expand All @@ -209,8 +207,7 @@ void regclass_graph_Model(py::module m) {
model.def(py::init([](const std::shared_ptr<ov::Node>& result,
const ov::ParameterVector& parameters,
const std::string& name) {
set_tensor_names(parameters);
return std::make_shared<ov::Model>(result, parameters, name);
return make_model_with_tensor_names(result, parameters, name);
}),
py::arg("result"),
py::arg("parameters"),
Expand All @@ -228,8 +225,7 @@ void regclass_graph_Model(py::module m) {

model.def(
py::init([](const ov::OutputVector& results, const ov::ParameterVector& parameters, const std::string& name) {
set_tensor_names(parameters);
return std::make_shared<ov::Model>(results, parameters, name);
return make_model_with_tensor_names(results, parameters, name);
}),
py::arg("results"),
py::arg("parameters"),
Expand All @@ -249,9 +245,8 @@ void regclass_graph_Model(py::module m) {
const std::vector<std::shared_ptr<ov::Node>>& nodes,
const ov::ParameterVector& parameters,
const std::string& name) {
set_tensor_names(parameters);
const auto sinks = cast_to_sink_vector(nodes);
auto model = std::make_shared<ov::Model>(results, sinks, parameters, name);
auto model = make_model_with_tensor_names(results, sinks, parameters, name);
set_correct_variables_for_assign_ops(model, sinks);
return model;
}),
Expand All @@ -276,9 +271,8 @@ void regclass_graph_Model(py::module m) {
const ov::OutputVector& nodes,
const ov::ParameterVector& parameters,
const std::string& name) {
set_tensor_names(parameters);
const auto sinks = cast_to_sink_vector(nodes);
auto model = std::make_shared<ov::Model>(results, sinks, parameters, name);
auto model = make_model_with_tensor_names(results, sinks, parameters, name);
set_correct_variables_for_assign_ops(model, sinks);
return model;
}),
Expand All @@ -304,9 +298,7 @@ void regclass_graph_Model(py::module m) {
const ov::ParameterVector& parameters,
const ov::op::util::VariableVector& variables,
const std::string& name) {
set_tensor_names(parameters);
const auto sinks = cast_to_sink_vector(nodes);
return std::make_shared<ov::Model>(results, sinks, parameters, variables, name);
return make_model_with_tensor_names(results, cast_to_sink_vector(nodes), parameters, variables, name);
}),
py::arg("results"),
py::arg("sinks"),
Expand All @@ -332,9 +324,8 @@ void regclass_graph_Model(py::module m) {
const ov::OutputVector& nodes,
const ov::ParameterVector& parameters,
const std::string& name) {
set_tensor_names(parameters);
const auto sinks = cast_to_sink_vector(nodes);
auto model = std::make_shared<ov::Model>(results, sinks, parameters, name);
auto model = make_model_with_tensor_names(results, sinks, parameters, name);
set_correct_variables_for_assign_ops(model, sinks);
return model;
}),
Expand All @@ -360,9 +351,7 @@ void regclass_graph_Model(py::module m) {
const ov::ParameterVector& parameters,
const ov::op::util::VariableVector& variables,
const std::string& name) {
set_tensor_names(parameters);
const auto sinks = cast_to_sink_vector(nodes);
return std::make_shared<ov::Model>(results, sinks, parameters, variables, name);
return make_model_with_tensor_names(results, cast_to_sink_vector(nodes), parameters, variables, name);
}),
py::arg("results"),
py::arg("sinks"),
Expand All @@ -389,9 +378,7 @@ void regclass_graph_Model(py::module m) {
const ov::ParameterVector& parameters,
const ov::op::util::VariableVector& variables,
const std::string& name) {
set_tensor_names(parameters);
const auto sinks = cast_to_sink_vector(nodes);
return std::make_shared<ov::Model>(results, sinks, parameters, variables, name);
return make_model_with_tensor_names(results, cast_to_sink_vector(nodes), parameters, variables, name);
}),
py::arg("results"),
py::arg("sinks"),
Expand All @@ -418,9 +405,7 @@ void regclass_graph_Model(py::module m) {
const ov::ParameterVector& parameters,
const ov::op::util::VariableVector& variables,
const std::string& name) {
set_tensor_names(parameters);
const auto sinks = cast_to_sink_vector(nodes);
return std::make_shared<ov::Model>(results, sinks, parameters, variables, name);
return make_model_with_tensor_names(results, cast_to_sink_vector(nodes), parameters, variables, name);
}),
py::arg("results"),
py::arg("sinks"),
Expand All @@ -444,8 +429,7 @@ void regclass_graph_Model(py::module m) {
const ov::ParameterVector& parameters,
const ov::op::util::VariableVector& variables,
const std::string& name) {
set_tensor_names(parameters);
return std::make_shared<ov::Model>(results, parameters, variables, name);
return make_model_with_tensor_names(results, parameters, variables, name);
}),
py::arg("results"),
py::arg("parameters"),
Expand All @@ -468,8 +452,7 @@ void regclass_graph_Model(py::module m) {
const ov::ParameterVector& parameters,
const ov::op::util::VariableVector& variables,
const std::string& name) {
set_tensor_names(parameters);
return std::make_shared<ov::Model>(results, parameters, variables, name);
return make_model_with_tensor_names(results, parameters, variables, name);
}),
py::arg("results"),
py::arg("parameters"),
Expand Down
6 changes: 4 additions & 2 deletions src/bindings/python/tests/test_graph/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from openvino import Dimension, Model, PartialShape, Shape

import openvino.opset8 as ov
import openvino.opset13 as ov


def test_dimension():
Expand Down Expand Up @@ -408,14 +408,16 @@ def test_repr_dynamic_shape():
parameter_a = ov.parameter(shape, dtype=np.float32, name="A")
parameter_b = ov.parameter(shape, dtype=np.float32, name="B")
param_sum = parameter_a + parameter_b
# set tensor name to have deterministic output name of model (default use unique node name)
param_sum.output(0).set_names({"sum"})
model = Model(param_sum, [parameter_a, parameter_b], "simple_dyn_shapes_graph")

assert (
repr(model)
== "<Model: 'simple_dyn_shapes_graph'\ninputs["
+ "\n<ConstOutput: names[A] shape[?,2] type: f32>,"
+ "\n<ConstOutput: names[B] shape[?,2] type: f32>\n]"
+ "\noutputs[\n<ConstOutput: names[] shape[?,2] type: f32>\n]>"
+ "\noutputs[\n<ConstOutput: names[sum] shape[?,2] type: f32>\n]>"
)

ops = model.get_ordered_ops()
Expand Down
4 changes: 2 additions & 2 deletions src/bindings/python/tests/test_runtime/test_ovdict.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ def test_ovdict_single_output_noname(device, is_direct):
_ = result["some_name"]
assert "some_name" in str(e0.value)

# Check if returned names are tuple with one empty set
# Check if returned names are tuple with one default name set
assert len(result.names()) == 1
assert result.names()[0] == set()
assert result.names()[0] != set()


@pytest.mark.parametrize("is_direct", [True, False])
Expand Down
3 changes: 3 additions & 0 deletions src/core/dev_api/openvino/core/descriptor_tensor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
namespace ov {
namespace descriptor {

/// @brief Defines tensor name port separator.
inline constexpr auto port_separator = ':';

class Tensor;
class Input;
class Output;
Expand Down
98 changes: 98 additions & 0 deletions src/core/dev_api/openvino/core/model_util.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (C) 2018-2025 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#pragma once

#include <unordered_map>

#include "openvino/core/core_visibility.hpp"
#include "openvino/core/descriptor/tensor.hpp"
#include "openvino/core/model.hpp"

namespace ov {

/** @brief Generic atg suggests automated functionality.
*
* Can be use for template specialization or function overloading
*/
struct AutoTag {};
inline constexpr AutoTag AUTO{};

/** @brief Alias to map of port number and tensor names */
using TensorNamesMap = std::unordered_map<size_t, TensorNames>;
} // namespace ov

namespace ov::util {

/** @brief Set input tensors names for the model
*
* Sets only tensors defined in the input map.
* The tensors defined in map but not existing in the model will be ignored.
*
* @param model Model to set its input tensors names.
* @param inputs_names Map of input tensor names.
*/
OPENVINO_API void set_input_tensors_names(Model& model, const TensorNamesMap& inputs_names);

/** @brief Set input tensors names for the model
*
* Sets tensors defined in the input map.
* Tensors not defined in the map are set to default names if the tensor hasn't got names.
* The tensors defined in the map but not existing in the model will be ignored.
*
* @param model Model to set its input tensors names.
* @param inputs_names Map of input tensor names. Default empty.
*/
OPENVINO_API void set_input_tensors_names(const AutoTag&, Model& model, const TensorNamesMap& inputs_names = {});

/** @brief Set output tensors names for the model
*
* Sets only tensors defined in the output map.
* The tensors defined in map but not existing in the model will be ignored.
*
* @param model Model to set its output tensors names.
* @param outputs_names Map of output tensor names.AnyMap
*/
OPENVINO_API void set_output_tensor_names(Model& model, const TensorNamesMap& outputs_names);

/** @brief Set output tensors names for the model.
*
* Sets tensors defined in the output map.
* Tensors not defined in the map are set to default names if the tensor hasn't got names.
* The tensors defined in the map but not existing in the model will be ignored.
*
* @param model Model to set its output tensors names.
* @param outputs_names Map of output tensor names. Default empty.
*/
OPENVINO_API void set_output_tensor_names(const AutoTag&, Model& model, const TensorNamesMap& outputs_names = {});

/** @brief Set input and output tensors names for the model
*
* Sets only tensors defined in the input and output maps.
* The tensors defined in maps but not existing in the model will be ignored.
*
* @param model Model to set its input and output tensors names.
* @param inputs_names Map of input tensor names.
* @param outputs_names Map of output tensor names.
*/
OPENVINO_API void set_tensors_names(Model& model,
const TensorNamesMap& inputs_names,
const TensorNamesMap& outputs_names);

/** @brief Set input and output tensors names for the model.
*
* Sets tensors defined in the input and output maps.
* Tensors not defined in the maps are set to default names if the tensor hasn't got names.
* The tensors defined in the maps but not existing in the model will be ignored.
*
* @param model Model to set its input and output tensors names.
* @param inputs_names Map of input tensor names. Default empty.
* @param outputs_names Map of output tensor names. Default empty.
*/
OPENVINO_API void set_tensors_names(const AutoTag&,
Model& model,
const TensorNamesMap& inputs_names = {},
const TensorNamesMap& outputs_names = {});

} // namespace ov::util
17 changes: 14 additions & 3 deletions src/core/dev_api/openvino/op/util/node_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,29 @@

#pragma once

#include <string>

#include "openvino/core/node.hpp"

namespace ov {
namespace op {
namespace util {
/**
* @brief Creates default tensor name for given Node's output.
* The name format is "node_name:output_port".
*
* @param output - Node's output to create name for tensor.
* @return Default tensor name.
*/
OPENVINO_API std::string make_default_tensor_name(const Output<const Node>& output);
} // namespace util

namespace op::util {
/**
* @brief Set name for both node and output tensor. Any other names will be overriden by a given single name
* @param node - node to rename
* @param name - new name
* @param output_port - output port to rename
*/
void OPENVINO_API set_name(ov::Node& node, const std::string& name, size_t output_port = 0);
} // namespace util
} // namespace op
} // namespace op::util
} // namespace ov
2 changes: 2 additions & 0 deletions src/core/include/openvino/core/descriptor/tensor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ using TensorSymbol = std::vector<std::shared_ptr<Symbol>>;
/// \brief Alias for vector of symbol tensors.
using TensorSymbolVector = std::vector<TensorSymbol>;

/// \brief Alias for set of tensor names.
using TensorNames = std::unordered_set<std::string>;
namespace descriptor {
class ITensorDescriptor;

Expand Down
Loading

0 comments on commit 015fe97

Please sign in to comment.