diff --git a/src/frontends/tensorflow/src/input_model.hpp b/src/frontends/tensorflow/src/input_model.hpp index a95a4447cc0..64d60b48fb8 100644 --- a/src/frontends/tensorflow/src/input_model.hpp +++ b/src/frontends/tensorflow/src/input_model.hpp @@ -24,11 +24,9 @@ class InputModel : public ov::frontend::InputModel { class InputModelTFImpl; std::shared_ptr _impl; - std::vector get_input_names() const; std::vector get_output_names() const; std::vector> get_op_places() const; std::map> get_tensor_values() const; - std::shared_ptr get_body_input_model(const std::string& body_input_model_name) const; public: explicit InputModel(const GraphIterator::Ptr& graph_iterator, @@ -58,6 +56,8 @@ public: std::shared_ptr get_checkpoint_v1_reader() const; std::map> get_tensor_places() const; + std::shared_ptr get_body_input_model(const std::string& body_input_model_name) const; + std::vector get_input_names() const; }; } // namespace tensorflow diff --git a/src/frontends/tensorflow/src/op/partitioned_call.cpp b/src/frontends/tensorflow/src/op/partitioned_call.cpp index 635ea1a802d..70b9557c911 100644 --- a/src/frontends/tensorflow/src/op/partitioned_call.cpp +++ b/src/frontends/tensorflow/src/op/partitioned_call.cpp @@ -40,9 +40,22 @@ OutputVector translate_partitioned_call_op(const NodeContext& node) { "[TensorFlow Frontend] Internal error or incorrect input model: body graph is not found for " + operation_type + "."); + // retrieve input_names of the body graph + auto input_model = dynamic_pointer_cast(translate_session->get_input_model()); + TENSORFLOW_OP_VALIDATION( + node, + input_model, + "[TensorFlow Frontend] internal error: input_model must be of tensorflow::InputModel type"); + auto body_input_model = input_model->get_body_input_model(operation_type); + TENSORFLOW_OP_VALIDATION(node, + body_input_model, + "[TensorFlow Frontend] internal error or inconsistent model: body graph " + + operation_type + " is not found in the graph"); + auto body_input_names = body_input_model->get_input_names(); + // inject the body graph into the parent graph OutputVector ov_outputs; - inject_body_model(body_model, operation_type, ov_inputs, ov_outputs); + inject_body_model(body_model, operation_type, ov_inputs, ov_outputs, body_input_names); // set output tensor names for (size_t idx = 0; idx < ov_outputs.size(); ++idx) { diff --git a/src/frontends/tensorflow/src/tf_utils.cpp b/src/frontends/tensorflow/src/tf_utils.cpp index e298f49f928..68ddf3677a6 100644 --- a/src/frontends/tensorflow/src/tf_utils.cpp +++ b/src/frontends/tensorflow/src/tf_utils.cpp @@ -456,19 +456,34 @@ shared_ptr create_loop_for_tf_while(const std::string& while_node_name void inject_body_model(std::shared_ptr ov_model_to_inject, const std::string& operation_type, const ov::OutputVector& ov_inputs, - ov::OutputVector& ov_outputs) { + ov::OutputVector& ov_outputs, + const std::vector& ov_input_names) { ov_outputs.clear(); auto body_parameters = ov_model_to_inject->get_parameters(); - FRONT_END_GENERAL_CHECK(body_parameters.size() == ov_inputs.size(), + // some external inputs can be skipped if some body graph inputs turn to be Constant nodes + FRONT_END_GENERAL_CHECK(body_parameters.size() <= ov_inputs.size(), "[TensorFlow Error] Internal error or incorrect input models: number of " "inputs and arguments to the function " + operation_type + " do not match."); for (size_t param_ind = 0; param_ind < body_parameters.size(); ++param_ind) { + auto param_name = body_parameters[param_ind]->get_friendly_name(); + // find suitable index of external input + size_t ext_found_ind = param_ind; + if (ov_input_names.size() > 0) { + // only used for PartitionedCall translator + for (size_t ext_input_ind = 0; ext_input_ind < ov_input_names.size(); ++ext_input_ind) { + if (ov_input_names[ext_input_ind] == param_name) { + ext_found_ind = ext_input_ind; + break; + } + } + } + auto orig_type = body_parameters[param_ind]->get_element_type(); // avoid not needed tensor names from body graph Parameter node after replacing body_parameters[param_ind]->output(0).set_names({}); - body_parameters[param_ind]->output(0).replace(ov_inputs[param_ind]); - if (auto ext_parameter = as_type_ptr(ov_inputs[param_ind].get_node_shared_ptr())) { + body_parameters[param_ind]->output(0).replace(ov_inputs[ext_found_ind]); + if (auto ext_parameter = as_type_ptr(ov_inputs[ext_found_ind].get_node_shared_ptr())) { // save type of a Parameter as converted in the body // this is important if the external conversion extension is applied to body graph node // with setting its own type diff --git a/src/frontends/tensorflow/src/tf_utils.hpp b/src/frontends/tensorflow/src/tf_utils.hpp index 861fb56f552..82c86c51c9e 100644 --- a/src/frontends/tensorflow/src/tf_utils.hpp +++ b/src/frontends/tensorflow/src/tf_utils.hpp @@ -114,7 +114,8 @@ std::shared_ptr create_loop_for_tf_while(const std::string& wh void inject_body_model(std::shared_ptr ov_model_to_inject, const std::string& operation_type, const ov::OutputVector& ov_inputs, - ov::OutputVector& ov_outputs); + ov::OutputVector& ov_outputs, + const std::vector& ov_input_names = {}); } // namespace tensorflow } // namespace frontend } // namespace ov diff --git a/src/frontends/tensorflow/src/translate_session.cpp b/src/frontends/tensorflow/src/translate_session.cpp index 4038995c6cb..ba88b0be30b 100644 --- a/src/frontends/tensorflow/src/translate_session.cpp +++ b/src/frontends/tensorflow/src/translate_session.cpp @@ -26,7 +26,8 @@ std::vector reorder_ops_by_names(const std::vector& names, const // in case unspecified names, return the initial order of operations return ops; } - FRONT_END_GENERAL_CHECK(names.size() == ops.size(), + // some body graph input can turn to be a constant node + FRONT_END_GENERAL_CHECK(names.size() >= ops.size(), "[TensorFlow Frontend] Internal error: cannot perform reordering of operations. The number " "of names mismatches the number of operations."); std::vector resulted_ops(ops.size(), nullptr); @@ -700,18 +701,40 @@ std::shared_ptr TranslateSession::get_body_ov_model(const std::string // set input shapes and types for InputModel of the body graph // it allows to get more optimized model after the conversion, // for example, to get less sub-graphs with ShapeOf and Convert operations - auto inputs = body_input_model->get_inputs(); - size_t num_inputs = inputs.size(); - FRONT_END_GENERAL_CHECK(num_inputs == ov_inputs.size(), - "[TensorFlow Frontend] internal error: a number of external and internal inputs for a " - "body graph mismatch"); - for (size_t input_ind = 0; input_ind < num_inputs; ++input_ind) { - auto input_place = inputs[input_ind]; - if (input_types[input_ind].is_static()) { - body_input_model->set_element_type(input_place, input_types[input_ind]); + // input names set an order of body graph inputs + auto input_names = body_input_model->get_input_names(); + auto body_inputs = body_input_model->get_inputs(); + size_t int_num_inputs = body_inputs.size(); + size_t ext_num_inputs = ov_inputs.size(); + FRONT_END_GENERAL_CHECK(int_num_inputs <= ext_num_inputs, + "[TensorFlow Frontend] internal error: a number of external and " + "internal inputs for a body graph mismatch"); + FRONT_END_GENERAL_CHECK(input_names.size() == ext_num_inputs, + "[TensorFlow Frontend] internal error: a number of body graph names and external " + "inputs to body must match"); + for (size_t input_ind = 0; input_ind < ext_num_inputs; ++input_ind) { + auto required_input_name = input_names[input_ind]; + bool is_found_body_input = false; + size_t body_found_ind = 0; + for (size_t internal_ind = 0; internal_ind < int_num_inputs; ++internal_ind) { + auto body_input_place = body_inputs[internal_ind]; + auto body_input_names = body_input_place->get_names(); + if (std::find(body_input_names.begin(), body_input_names.end(), required_input_name) != + body_input_names.end()) { + is_found_body_input = true; + body_found_ind = internal_ind; + break; + } } - if (input_shapes[input_ind].rank().is_static()) { - body_input_model->set_partial_shape(input_place, input_shapes[input_ind]); + if (is_found_body_input) { + auto body_input_place = body_inputs[body_found_ind]; + // if body input with required name is found, set its type + if (input_types[input_ind].is_static()) { + body_input_model->set_element_type(body_input_place, input_types[input_ind]); + } + if (input_shapes[input_ind].rank().is_static()) { + body_input_model->set_partial_shape(body_input_place, input_shapes[input_ind]); + } } } diff --git a/tests/model_hub_tests/tf_hub_tests/test_tf_hub_api_notebooks.py b/tests/model_hub_tests/tf_hub_tests/test_tf_hub_api_notebooks.py index fe006d3860f..50c6f54f7c6 100644 --- a/tests/model_hub_tests/tf_hub_tests/test_tf_hub_api_notebooks.py +++ b/tests/model_hub_tests/tf_hub_tests/test_tf_hub_api_notebooks.py @@ -24,6 +24,15 @@ class TestTFHubApiNotebooks(TestConvertModel): softmax = tf.keras.layers.Dense(20, activation='softmax')(feature_vector) classification_model = tf.keras.Model(inputs=[image], outputs=[softmax]) return classification_model + elif model_name == 'film': + inputs = dict( + x0=tf.keras.layers.Input(shape=(200, 200, 3)), + x1=tf.keras.layers.Input(shape=(200, 200, 3)), + time=tf.keras.layers.Input(shape=(1)), + ) + film_layer = hub.KerasLayer("https://tfhub.dev/google/film/1")(inputs) + film_model = tf.keras.Model(inputs=inputs, outputs=list(film_layer.values())[0]) + return film_model else: raise "Unknown input model: {}".format(model_name) @@ -58,6 +67,6 @@ class TestTFHubApiNotebooks(TestConvertModel): return post_outputs @pytest.mark.precommit - @pytest.mark.parametrize("model_name", ['mobilenet_v2_100_224_dict', 'mobilenet_v2_100_224_list']) + @pytest.mark.parametrize("model_name", ['mobilenet_v2_100_224_dict', 'mobilenet_v2_100_224_list', 'film']) def test_tf_hub_api_notebook1(self, model_name, ie_device): self.run(model_name, '', ie_device)