[TF FE] Use _output_shapes attribute for Placeholder (#14383)

* [TF FE] Use output_shapes attribute for Placeholder

Signed-off-by: Kazantsev, Roman <roman.kazantsev@intel.com>

* Add test model

* Apply code-review feedback and remove compile warnings

Signed-off-by: Kazantsev, Roman <roman.kazantsev@intel.com>
This commit is contained in:
Roman Kazantsev authored and GitHub committed 2022-12-05 23:13:53 +04:00
1 parent fcc4e60441
commit a945597802
6 files changed
+147 -26

No files matched your search

+18 -9
View File
@@ -108,9 +108,10 @@ ov::Any DecoderProto::get_attribute(const std::string& name) const {
if (tf_shape.unknown_rank()) {
return ov::PartialShape::dynamic();
}
std::vector<ov::Dimension> dims(tf_shape.dim_size());
for (int i = 0; i < tf_shape.dim_size(); ++i) {
dims[i] = tf_shape.dim(i).size();
auto shape_rank = tf_shape.dim_size();
std::vector<ov::Dimension> dims(shape_rank);
for (int i = 0; i < shape_rank; ++i) {
dims[i] = static_cast<ov::Dimension::value_type>(tf_shape.dim(i).size());
}
return ov::PartialShape(dims);
}
@@ -139,14 +140,22 @@ ov::Any DecoderProto::get_attribute(const std::string& name) const {
return std::vector<bool>(list.b().begin(), list.b().end());
if (list.shape_size()) {
std::vector<ov::PartialShape> res;
for (const auto& it : list.shape()) {
std::vector<ov::Dimension> dims;
for (int i = 0; i < it.dim_size(); i++) {
dims.emplace_back(it.dim(i).size());
auto shapes_size = list.shape_size();
std::vector<ov::PartialShape> res(shapes_size);
for (int shape_ind = 0; shape_ind < shapes_size; ++shape_ind) {
auto shape = list.shape(shape_ind);
if (shape.unknown_rank()) {
res[shape_ind] = ov::PartialShape::dynamic();
} else {
auto shape_rank = shape.dim_size();
std::vector<ov::Dimension> dims(shape_rank);
for (int dim_ind = 0; dim_ind < shape_rank; ++dim_ind) {
dims[dim_ind] = static_cast<ov::Dimension::value_type>(shape.dim(dim_ind).size());
}
res[shape_ind] = dims;
}
res.emplace_back(dims);
}
return res;
}
if (list.type_size()) {
@@ -126,6 +126,19 @@ void InputModel::InputModelTFImpl::loadPlaces() {
} else {
OPENVINO_DEBUG << "TensorFlow Frontend: Placeholder " << op_name << " does not have 'shape' attribute";
}
auto output_shapes_any = node_decoder->get_attribute("_output_shapes");
if (pshape.rank().is_static() && pshape.rank().get_length() == 0 &&
output_shapes_any.is<std::vector<ov::PartialShape>>()) {
// we know some cases when Placeholder operation has empty scalar `shape` attribute value
// and non-empty `_output_shapes` attribute value.
// `_output_shapes` attribute value turns to be correct in this case
auto output_shapes = output_shapes_any.as<std::vector<ov::PartialShape>>();
if (output_shapes.size() == 1 && output_shapes[0].rank().is_static()) {
pshape = output_shapes[0];
OPENVINO_DEBUG << "TensorFlow Frontend: Placeholder " << op_name
<< " has shape from '_output_shapes' attribute.";
}
}
auto dtype_any = node_decoder->get_attribute("dtype");
auto placeholder_name = node_decoder->get_op_name();
FRONT_END_GENERAL_CHECK(
@@ -14,10 +14,19 @@ namespace tensorflow {
namespace op {
OutputVector translate_placeholder_op(const NodeContext& node) {
auto tf_dtype = node.get_attribute<ov::element::Type>("dtype");
auto tf_shape = node.get_attribute<ov::PartialShape>("shape", ov::PartialShape::dynamic());
auto dtype = node.get_attribute<ov::element::Type>("dtype");
auto shape = node.get_attribute<ov::PartialShape>("shape", ov::PartialShape::dynamic());
if (shape.rank().is_static() && shape.rank().get_length() == 0 && node.has_attribute("_output_shapes")) {
// we know some cases when Placeholder operation has empty scalar `shape` attribute value
// and non-empty `_output_shapes` attribute value.
// `_output_shapes` attribute value turns to be correct in this case
auto output_shapes = node.get_attribute<std::vector<ov::PartialShape>>("_output_shapes");
if (output_shapes.size() == 1 && output_shapes[0].rank().is_static()) {
shape = output_shapes[0];
}
}
auto res = std::make_shared<Parameter>(tf_dtype, tf_shape);
auto res = std::make_shared<Parameter>(dtype, shape);
set_node_name(node.get_name(), res);
return res->outputs();
}
@@ -13,27 +13,59 @@ using namespace std;
using namespace ov;
using namespace ov::frontend;
TEST(FrontEndConvertModelTest, test_undefined_input_shape) {
namespace {
shared_ptr<Model> convert_model(const string& model_path) {
FrontEndManager fem;
FrontEnd::Ptr frontEnd;
InputModel::Ptr inputModel;
ASSERT_NO_THROW(frontEnd = fem.load_by_framework(TF_FE));
ASSERT_NE(frontEnd, nullptr);
auto model_filename = FrontEndTestUtils::make_model_path(string(TEST_TENSORFLOW_MODELS_DIRNAME) +
string("undefined_input_shape/undefined_input_shape.pb"));
ASSERT_NO_THROW(inputModel = frontEnd->load(model_filename));
ASSERT_NE(inputModel, nullptr);
auto front_end = fem.load_by_framework(TF_FE);
if (!front_end) {
throw "TensorFlow Frontend is not initialized";
}
auto model_filename = FrontEndTestUtils::make_model_path(string(TEST_TENSORFLOW_MODELS_DIRNAME) + model_path);
auto input_model = front_end->load(model_filename);
if (!input_model) {
throw "Input model is not read";
}
auto model = front_end->convert(input_model);
if (!model) {
throw "Model is not converted";
}
return model;
}
} // namespace
TEST(FrontEndConvertTrickyModels, undefined_input_shape) {
shared_ptr<Model> model;
ASSERT_NO_THROW(model = frontEnd->convert(inputModel));
ASSERT_NE(model, nullptr);
try {
model = convert_model("undefined_input_shape/undefined_input_shape.pb");
} catch (std::exception& ex) {
ASSERT_TRUE(false) << ex.what();
}
for (auto& node : model->get_ordered_ops()) {
if (node->get_friendly_name() == "x") {
ASSERT_TRUE(node->get_output_partial_shape(0).same_scheme(PartialShape::dynamic()));
ASSERT_TRUE(node->get_output_partial_shape(0).same_scheme(ov::PartialShape::dynamic()));
} else if (node->get_friendly_name() == "y") {
ASSERT_TRUE(node->get_output_partial_shape(0).same_scheme(PartialShape{2, 3}));
ASSERT_TRUE(node->get_output_partial_shape(0).same_scheme(ov::PartialShape{2, 3}));
} else if (node->get_friendly_name() == "z") {
ASSERT_TRUE(node->get_output_partial_shape(0).same_scheme(PartialShape::dynamic()));
ASSERT_TRUE(node->get_output_partial_shape(0).same_scheme(ov::PartialShape::dynamic()));
}
}
}
TEST(FrontEndConvertTrickyModels, model_with_output_shapes) {
shared_ptr<Model> model;
try {
model = convert_model("model_with_output_shapes_attr/model_with_output_shapes_attr.pb");
} catch (std::exception& ex) {
ASSERT_TRUE(false) << ex.what();
}
for (auto& node : model->get_ordered_ops()) {
if (node->get_friendly_name() == "x") {
ASSERT_TRUE(node->get_output_partial_shape(0).same_scheme(ov::PartialShape{2, 3}));
} else if (node->get_friendly_name() == "relu") {
ASSERT_TRUE(node->get_output_partial_shape(0).same_scheme(ov::PartialShape{2, 3}));
}
}
}
@@ -0,0 +1,43 @@
node {
name: "x"
op: "Placeholder"
attr {
key: "dtype"
value {
type: DT_FLOAT
}
}
attr {
key: "shape"
value {
shape {
}
}
}
attr {
key: "_output_shapes"
value {
list {
shape {
dim {
size: 2
}
dim {
size: 3
}
}
}
}
}
}
node {
name: "relu"
op: "Relu"
input: "x"
attr {
key: "T"
value {
type: DT_FLOAT
}
}
}
@@ -0,0 +1,15 @@
# Copyright (C) 2018-2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import tensorflow.compat.v1 as tf
tf.reset_default_graph()
with tf.Session() as sess:
x = tf.placeholder(dtype=tf.float32, shape=[2,3], name='x')
tf.nn.relu(x, name="relu")
tf.global_variables_initializer()
sess.graph.as_graph_def(add_shapes=True)
tf.io.write_graph(sess.graph, '.', 'model_with_output_shapes_attr.pbtxt', as_text=True)