From 835f51a5d2b3e763690c16b4b223864f206a2076 Mon Sep 17 00:00:00 2001 From: Georgy Krivoruchko Date: Fri, 28 Apr 2023 13:03:33 +0400 Subject: [PATCH] [TF FE] Added additional pruned inputs for MetaGraph support (#17237) * Added handling of additional pruned inputs Added possible topology of RestoreV2 -> AssignVariableOp Added additional checks * Extended tests coverage --- src/frontends/tensorflow/src/input_model.cpp | 2 + .../tensorflow/src/variables_index.cpp | 21 ++++- .../tests/convert_tricky_models.cpp | 79 ++++++++++++++++++- .../generate_metagraph_variables.py | 19 ++++- 4 files changed, 116 insertions(+), 5 deletions(-) diff --git a/src/frontends/tensorflow/src/input_model.cpp b/src/frontends/tensorflow/src/input_model.cpp index 7ed1865a34f..6083455a529 100644 --- a/src/frontends/tensorflow/src/input_model.cpp +++ b/src/frontends/tensorflow/src/input_model.cpp @@ -337,6 +337,7 @@ std::vector> InputModel::InputModelTFImpl::topologicall if (m_tensor_places.find(output_port_name) != m_tensor_places.end()) { const auto& tensor_place = m_tensor_places[output_port_name]; is_input |= tensor_place->is_input(); + m_found_inputs.insert(output_port_name); } // 3. check if the current node is an input @@ -346,6 +347,7 @@ std::vector> InputModel::InputModelTFImpl::topologicall if (m_tensor_places.find(producer_name) != m_tensor_places.end()) { const auto& tensor_place = m_tensor_places[producer_name]; is_input |= tensor_place->is_input(); + m_found_inputs.insert(producer_name); } // in case presence of NextIteration in the graph (or cycle created by other operation), diff --git a/src/frontends/tensorflow/src/variables_index.cpp b/src/frontends/tensorflow/src/variables_index.cpp index c2767d63e70..662cd47be0b 100644 --- a/src/frontends/tensorflow/src/variables_index.cpp +++ b/src/frontends/tensorflow/src/variables_index.cpp @@ -448,8 +448,20 @@ void VariablesIndex::map_assignvariable(const std::shared_ptr<::tensorflow::Grap if (restorev2_nodes.size() == 1 && varhandle_nodes.size() == 1) { std::vector restore_output; - // Expected path is: RestoreV2 -(output_index)-(0)-> Identity -(0)-(1)-> AssignVariableOp - PtrNode::parse_node_name(node.second->inputs[1]->node->input(0), restore_output); + + FRONT_END_GENERAL_CHECK(node.second->inputs.size() >= 2, + "Amount of AssignVariableOp inputs is less than expected"); + // Here is known ways to find a correct RestoreV2 output index: + if (node.second->inputs[1]->inputs.size() >= 1 && + node.second->inputs[1]->inputs[0]->node->op() == "RestoreV2") { + // Expected path is: RestoreV2 -(output_index)-(0)-> AnyNode -(0)-(1)-> AssignVariableOp + PtrNode::parse_node_name(node.second->inputs[1]->node->input(0), restore_output); + } else if (node.second->inputs[1]->node->op() == "RestoreV2" && node.second->node->input_size() >= 2) { + // Expected path is: RestoreV2 -(output_index)-(1)-> AssignVariableOp + PtrNode::parse_node_name(node.second->node->input(1), restore_output); + } else { + FRONT_END_THROW("Unexpected topology near AssignVariableOp"); + } int output_index = std::atoi(restore_output[restore_output.size() - 1].c_str()); @@ -473,7 +485,10 @@ void VariablesIndex::map_assignvariable(const std::shared_ptr<::tensorflow::Grap if (restorev2_nodes.size() == 1 && variablev2_nodes.size() == 1) { std::vector restore_output; - // Expected path is: RestoreV2 -(output_index)-(0)-> Assign + + FRONT_END_GENERAL_CHECK(node.second->node->input_size() >= 2, + "Amount of Assign inputs is less than expected"); + // Expected path is: RestoreV2 -(output_index)-(1)-> Assign PtrNode::parse_node_name(node.second->node->input(1), restore_output); int output_index = std::atoi(restore_output[restore_output.size() - 1].c_str()); diff --git a/src/frontends/tensorflow/tests/convert_tricky_models.cpp b/src/frontends/tensorflow/tests/convert_tricky_models.cpp index 1fe89673c08..bc02fb34416 100644 --- a/src/frontends/tensorflow/tests/convert_tricky_models.cpp +++ b/src/frontends/tensorflow/tests/convert_tricky_models.cpp @@ -545,11 +545,88 @@ TEST_F(TransformationTestsF, MetaGraphVariables) { // create a reference graph auto x = make_shared(element::f32, Shape{2, 3}, vector{1, 2, 3, 3, 2, 1}); auto y = make_shared(element::f32, Shape{1}); + auto z = make_shared(element::f32, Shape{2, 3}, vector{2, 2, 1, 1, 1, 2}); auto add = make_shared(x, y); + auto sub = make_shared(add, z); - model_ref = make_shared(OutputVector{add}, ParameterVector{y}); + model_ref = make_shared(OutputVector{sub}, ParameterVector{y}); } } + +TEST_F(TransformationTestsF, MetaGraphCut) { + { + model = convert_model("metagraph_variables/graph.meta", nullptr, {"y"}); + model->validate_nodes_and_infer_types(); + } + { + // create a reference graph + auto x = make_shared(element::f32, Shape{2, 3}, vector{1, 2, 3, 3, 2, 1}); + auto y = make_shared(element::f32, Shape{1}); + auto z = make_shared(element::f32, Shape{2, 3}, vector{2, 2, 1, 1, 1, 2}); + auto add = make_shared(x, y); + auto sub = make_shared(add, z); + + model_ref = make_shared(OutputVector{sub}, ParameterVector{y}); + } +} + +TEST_F(TransformationTestsF, MetaGraphCutInputTensor) { + { + model = convert_model("metagraph_variables/graph.meta", + nullptr, + {"0:SubOperation"}, + {ov::element::f32}, + {Shape{2, 3}}); + model->validate_nodes_and_infer_types(); + } + { + // create a reference graph + auto x = make_shared(element::f32, Shape{2, 3}); + auto z = make_shared(element::f32, Shape{2, 3}, vector{2, 2, 1, 1, 1, 2}); + auto sub = make_shared(x, z); + + model_ref = make_shared(OutputVector{sub}, ParameterVector{x}); + } +} + +TEST_F(TransformationTestsF, MetaGraphCutOutputTensor) { + { + model = convert_model("metagraph_variables/graph.meta", + nullptr, + {"AddOperation:0"}, + {ov::element::f32}, + {Shape{2, 3}}); + model->validate_nodes_and_infer_types(); + } + { + // create a reference graph + auto x = make_shared(element::f32, Shape{2, 3}); + auto z = make_shared(element::f32, Shape{2, 3}, vector{2, 2, 1, 1, 1, 2}); + auto sub = make_shared(x, z); + + model_ref = make_shared(OutputVector{sub}, ParameterVector{x}); + } +} + +TEST_F(TransformationTestsF, MetaGraphCutIdentity) { + { + model = convert_model("metagraph_variables/graph.meta", + nullptr, + {"AddIdentity"}, + {ov::element::f32}, + {Shape{2, 3}}); + model->validate_nodes_and_infer_types(); + } + { + // create a reference graph + auto x = make_shared(element::f32, Shape{2, 3}); + auto z = make_shared(element::f32, Shape{2, 3}, vector{2, 2, 1, 1, 1, 2}); + auto sub = make_shared(x, z); + + model_ref = make_shared(OutputVector{sub}, ParameterVector{x}); + } +} + TEST_F(TransformationTestsF, SplitInFunction) { { // create FAKE conversion extension for Split using named ports, this is not required for Split, but it tests diff --git a/src/frontends/tensorflow/tests/test_models/gen_scripts/generate_metagraph_variables.py b/src/frontends/tensorflow/tests/test_models/gen_scripts/generate_metagraph_variables.py index 6265362c66d..92a44e7b61f 100644 --- a/src/frontends/tensorflow/tests/test_models/gen_scripts/generate_metagraph_variables.py +++ b/src/frontends/tensorflow/tests/test_models/gen_scripts/generate_metagraph_variables.py @@ -10,10 +10,27 @@ import tensorflow as tf tf.compat.v1.reset_default_graph() with tf.compat.v1.Session() as sess: x_value = [[1.,2.,3.],[3.,2.,1.]] + z_value = [[2.,2.,1.],[1.,1.,2.]] tf_x = tf.Variable(x_value) tf_y = tf.compat.v1.placeholder(dtype=tf.float32, shape=[1], name='y') - tf_z = tf.add(tf_x, tf_y, name="AddOperation") + tf_z = tf.constant(z_value) + tf_add = tf.add(tf_x, tf_y, name="AddOperation") + tf_identity = tf.identity(tf_add, name="AddIdentity") + tf.subtract(tf_identity, tf_z, name="SubOperation") sess.run(tf.compat.v1.global_variables_initializer()) + # Produces RestoreV2 -> Identity -> AssignVariableOp saver = tf.compat.v1.train.Saver([tf_x]) + + input_name = tf.compat.v1.get_default_graph().get_tensor_by_name("save/Const:0") + var_handle = tf.compat.v1.get_default_graph().get_tensor_by_name("Variable:0") + + # Produces RestoreV2 -> Pack -> AssignVariableOp + restorev2 = tf.raw_ops.RestoreV2(prefix=input_name, tensor_names=["Variable"], shape_and_slices=[""], dtypes=[tf.float32], name="save/RestoreV2/wPack") + assign_var = tf.raw_ops.AssignVariableOp(resource = var_handle, value = restorev2) + + # Produces RestoreV2 -> AssignVariableOp + restorev2 = tf.raw_ops.RestoreV2(prefix=input_name, tensor_names=["Variable"], shape_and_slices=[""], dtypes=[tf.float32], name="save/RestoreV2/Direct") + assign_var = tf.compat.v1.raw_ops.AssignVariableOp(resource = var_handle, value = restorev2[0]) + os.makedirs(os.path.join(sys.argv[1], "metagraph_variables")) saver.save(sess, os.path.join(sys.argv[1], "metagraph_variables", "graph"))