Skip to content

Commit

Permalink
[TF FE] Fix conversion of FILM of keras.Model format (openvinotoolkit…
Browse files Browse the repository at this point in the history
…#20825)

Signed-off-by: Kazantsev, Roman <roman.kazantsev@intel.com>
  • Loading branch information
rkazants authored and alvoron committed Nov 6, 2023
1 parent 1215444 commit d9ec19b
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/frontends/tensorflow/src/input_model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ class InputModel : public ov::frontend::InputModel {
class InputModelTFImpl;
std::shared_ptr<InputModelTFImpl> _impl;

std::vector<std::string> get_input_names() const;
std::vector<std::string> get_output_names() const;
std::vector<std::shared_ptr<OpPlace>> get_op_places() const;
std::map<std::string, Output<Node>> get_tensor_values() const;
std::shared_ptr<InputModel> get_body_input_model(const std::string& body_input_model_name) const;

public:
explicit InputModel(const GraphIterator::Ptr& graph_iterator,
Expand Down Expand Up @@ -58,6 +56,8 @@ class InputModel : public ov::frontend::InputModel {
std::shared_ptr<CheckpointV1Reader> get_checkpoint_v1_reader() const;

std::map<std::string, std::shared_ptr<TensorPlace>> get_tensor_places() const;
std::shared_ptr<InputModel> get_body_input_model(const std::string& body_input_model_name) const;
std::vector<std::string> get_input_names() const;
};

} // namespace tensorflow
Expand Down
15 changes: 14 additions & 1 deletion src/frontends/tensorflow/src/op/partitioned_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<InputModel>(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) {
Expand Down
23 changes: 19 additions & 4 deletions src/frontends/tensorflow/src/tf_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,19 +456,34 @@ shared_ptr<v5::Loop> create_loop_for_tf_while(const std::string& while_node_name
void inject_body_model(std::shared_ptr<ov::Model> 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<std::string>& 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<v0::Parameter>(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<v0::Parameter>(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
Expand Down
3 changes: 2 additions & 1 deletion src/frontends/tensorflow/src/tf_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ std::shared_ptr<ov::op::v5::Loop> create_loop_for_tf_while(const std::string& wh
void inject_body_model(std::shared_ptr<ov::Model> 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<std::string>& ov_input_names = {});
} // namespace tensorflow
} // namespace frontend
} // namespace ov
47 changes: 35 additions & 12 deletions src/frontends/tensorflow/src/translate_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ std::vector<T> reorder_ops_by_names(const std::vector<std::string>& 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<T> resulted_ops(ops.size(), nullptr);
Expand Down Expand Up @@ -700,18 +701,40 @@ std::shared_ptr<ov::Model> 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]);
}
}
}

Expand Down
11 changes: 10 additions & 1 deletion tests/model_hub_tests/tf_hub_tests/test_tf_hub_api_notebooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ def load_model(self, model_name, model_link):
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)

Expand Down Expand Up @@ -58,6 +67,6 @@ def infer_fw_model(self, model_obj, inputs):
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)

0 comments on commit d9ec19b

Please sign in to comment.