[TF FE] Fix conversion of FILM of keras.Model format (#20825)
Signed-off-by: Kazantsev, Roman <roman.kazantsev@intel.com>
This commit is contained in:
parent
3ff85ae70a
commit
4a6e6e64c5
@ -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,
|
||||
@ -58,6 +56,8 @@ public:
|
||||
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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user