Skip to content

Commit

Permalink
[TF FE] Support TF 2.17 and stick exact TF version for validation (op…
Browse files Browse the repository at this point in the history
…envinotoolkit#26379)

**Details:** Required for enabling Python 3.12. Stick exact version for
TF FE validation. This approach is more transparent about what TF
version is exactly tested against. We will select the highest available
version for each platform.

**Ticket:** 150601

Signed-off-by: Kazantsev, Roman <[email protected]>
  • Loading branch information
rkazants authored Sep 3, 2024
1 parent 82d1c99 commit 9db9a78
Show file tree
Hide file tree
Showing 31 changed files with 305 additions and 491 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/job_python_unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,9 @@ jobs:
TEST_PRECISION: FP16

- name: TensorFlow 2 Layer Tests - Legacy FE
if: fromJSON(inputs.affected-components).TF_FE.test
# no longer workable since TF 2.17
# will be removed in 2024.5
if: ${{ 'false' }}
run: python3 -m pytest ${{ env.LAYER_TESTS_INSTALL_DIR }}/tensorflow2_keras_tests/test_tf2_keras_activation.py --use_legacy_frontend --ir_version=11 -k "sigmoid" --junitxml=${{ env.INSTALL_TEST_DIR }}/TEST-tf2_Activation.xml
env:
TEST_DEVICE: CPU
Expand Down
41 changes: 31 additions & 10 deletions src/bindings/python/src/openvino/frontend/tensorflow/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@


import logging as log
import numpy as np
import sys
from openvino.runtime import PartialShape, Dimension, Type
from packaging.version import parse, Version
from typing import List, Dict, Union

import numpy as np
from openvino.runtime import PartialShape, Dimension, Type


# TODO: reuse this method in ovc and remove duplication
def get_static_shape(shape: [PartialShape, list, tuple], dynamic_value=None):
Expand Down Expand Up @@ -106,13 +105,32 @@ def trace_tf_model_if_needed(input_model, placeholder_shapes, placeholder_data_t
return trace_tf_model(input_model, placeholder_shapes, placeholder_data_types, example_input)


def get_input_spec_from_model(model):
def partial_shape_to_list(partial_shape: PartialShape):
if partial_shape.rank.is_dynamic:
return None
res_list = []
for dim in partial_shape:
if dim.is_static:
res_list.append(dim.get_length())
else:
res_list.append(None)
return res_list


def get_input_spec_from_model(model, input_shapes=None):
import tensorflow as tf
if hasattr(model, "_build_input_shape") and model._build_input_shape is not None:
if isinstance(model._build_input_shape, list):
input_spec = [[tf.TensorSpec(shape) for shape in model._build_input_shape]]
else:
input_spec = [tf.TensorSpec(model._build_input_shape)]
elif input_shapes and isinstance(input_shapes, list) and len(input_shapes) > 0:
input_spec = []
for input_shape in input_shapes:
if isinstance(input_shape, PartialShape):
input_spec.append(tf.TensorSpec(partial_shape_to_list(input_shape)))
else:
input_spec.append(tf.TensorSpec(None))
else:
input_spec = [tf.TensorSpec(None)]
return input_spec
Expand Down Expand Up @@ -199,10 +217,13 @@ def create_generic_function_from_keras_model(keras_model):
if tf_input_signature is not None:
@tf.function(input_signature=tf_input_signature)
def wrapper_function_dict(*args):
input_dict = {}
for ind, tensor_spec in enumerate(tf_input_signature):
input_dict[tensor_spec.name] = args[ind]
outputs = keras_model(input_dict)
if isinstance(keras_input_signature, list):
outputs = keras_model(args)
else:
input_dict = {}
for ind, tensor_spec in enumerate(tf_input_signature):
input_dict[tensor_spec.name] = args[ind]
outputs = keras_model(input_dict)
# need to wrap the output into dictionary
# it helps to preserve original keras tensor names
post_outputs = {}
Expand Down Expand Up @@ -276,7 +297,7 @@ def are_shapes_defined(shape: Union[List, Dict]):
"Could not trace the TF model with the following error: {}",
use_example_input=False)
else:
input_spec = get_input_spec_from_model(model)
input_spec = get_input_spec_from_model(model, input_shapes)
concrete_func = get_concrete_func(tf_function, input_spec, input_needs_packing,
"Could not trace the TF model with the following error: {}.\n"
"Please provide 'example_input'.")
Expand Down Expand Up @@ -457,4 +478,4 @@ def tf_type_to_ov_type(val):
}
if val not in tf_to_ov_type:
raise Exception("The provided data type is not supported by OpenVino {}.".format(val))
return tf_to_ov_type[val]
return tf_to_ov_type[val]
17 changes: 12 additions & 5 deletions src/frontends/tensorflow/src/input_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,14 +230,21 @@ void InputModel::InputModelTFImpl::load_places() {
if (dtype_any.is<ov::element::Type>()) {
type = dtype_any.as<ov::element::Type>();
}
std::vector<std::string> names = {op_name + ":0"};
std::string internal_tensor_name = op_name + ":0";
std::vector<std::string> names{internal_tensor_name};
auto tensor_place = std::make_shared<TensorPlace>(m_input_model, pshape, type, names, op_name);

m_default_places[op_name + ":0"] = tensor_place;
m_default_places[internal_tensor_name] = tensor_place;

if (op_type == "Placeholder") {
// by default, PlaceholderWithDefault is NOT used as input
m_inputs.push_back(tensor_place);
if (m_saved_model_input_names && (m_saved_model_input_names->size() > 0)) {
// if input signature is defined,
// found input must present in this signature
if (m_saved_model_input_names->find(internal_tensor_name) != m_saved_model_input_names->end()) {
m_inputs.push_back(tensor_place);
}
} else {
m_inputs.push_back(tensor_place);
}
}
} else if (op_type == "input_arg") {
if (m_input_names.size() > 0 &&
Expand Down
5 changes: 2 additions & 3 deletions tests/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ sympy>=1.10
wheel>=0.38.1
defusedxml>=0.7.1
fastjsonschema~=2.17.1
tensorflow>=2.5,<2.17.0
tensorflow>=2.5,<2.18.0
requests>=2.25.1
opencv-python>=4.5
paddlepaddle==2.6.1
Expand All @@ -26,8 +26,7 @@ jax<=0.4.14
jaxlib<=0.4.14
kornia==0.7.0
networkx<=3.3
keras>=2.0.0,<3.0.0
timm==1.0.8

--extra-index-url https://download.pytorch.org/whl/cpu
torch~=2.4.0
torch~=2.4.0
13 changes: 6 additions & 7 deletions tests/layer_tests/common/layer_test_class.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# Copyright (C) 2018-2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

import defusedxml.ElementTree as ET
import itertools
import numpy as np
import os
import re
import warnings
from pathlib import Path

import defusedxml.ElementTree as ET
import numpy as np
from common.constants import test_device, test_precision
from common.layer_utils import InferAPI
from common.utils.common_utils import generate_ir_python_api
from pathlib import Path


class CommonLayerTest:
Expand Down Expand Up @@ -177,9 +176,9 @@ def compare_ie_results_with_framework(self, infer_res, framework_res, framework_
rtol=framework_eps):
is_ok = False
if ie_res.dtype != bool:
print("Max diff is {}".format(
np.array(
abs(ie_res - framework_res[framework_out_name])).max()))
fw_res = np.array(framework_res[framework_out_name])
diff = np.array(abs(ie_res - fw_res)).max()
print("Max diff is {}".format(diff))
else:
print("Boolean results are not equal")
else:
Expand Down
3 changes: 2 additions & 1 deletion tests/layer_tests/common/tf2_layer_test_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
def save_to_tf2_savedmodel(tf2_model, path_to_saved_tf2_model):
import tensorflow as tf
assert int(tf.__version__.split('.')[0]) >= 2, "TensorFlow 2 must be used for this suite validation"
tf.keras.models.save_model(tf2_model, path_to_saved_tf2_model, save_format='tf')
# Since TF 2.16 this is only way to serialize Keras objects into SavedModel format
tf2_model.export(path_to_saved_tf2_model)
assert os.path.isdir(path_to_saved_tf2_model), "the model haven't been saved " \
"here: {}".format(path_to_saved_tf2_model)
return path_to_saved_tf2_model
Expand Down
25 changes: 13 additions & 12 deletions tests/layer_tests/mo_python_api_tests/test_mo_convert_extensions.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
# Copyright (C) 2018-2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

import pytest
import numpy as np

import openvino.runtime as ov
import pytest
from common.mo_convert_test_class import CommonMOConvertTest
from common.onnx_layer_test_class import save_to_onnx

import openvino.runtime as ov
from openvino.runtime import PartialShape, Model


Expand Down Expand Up @@ -199,14 +197,18 @@ def create_keras_model(self, temp_dir):
tf.compat.v1.reset_default_graph()

input_name = "Input1"
input_shape = [1, 2, 3]
input_shape = [None, 1, 2, 3]

x = tf.keras.Input(shape=input_shape, name=input_name)
y = tf.cos(x)
keras_net = tf.keras.Model(inputs=[x], outputs=[y])
tf.keras.backend.clear_session()
tf.compat.v1.reset_default_graph()

with tf.compat.v1.Session() as sess:
x = tf.compat.v1.placeholder(tf.float32, input_shape, input_name)
tf.raw_ops.Cos(x=x, name='res')

tf.compat.v1.global_variables_initializer()
tf_net = sess.graph_def

return keras_net
return tf_net

def create_custom_extension_cos_to_sin():
from openvino.frontend import ConversionExtension
Expand All @@ -228,9 +230,8 @@ def create_custom_op_extension_cos_to_sin():
def create_ref_graph():
shape = PartialShape([-1, 1, 2, 3])
param = ov.opset14.parameter(shape, dtype=np.float32)
param.get_output_tensor(0).set_names({"Input1"})
param.get_output_tensor(0).set_names({"Input1:0"})
y = ov.opset14.sin(param)
y.get_output_tensor(0).set_names({"tf.math.cos/Cos:0"})

parameter_list = [param]

Expand Down
Loading

0 comments on commit 9db9a78

Please sign in to comment.