[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:
1 parent
fcc4e60441
commit
a945597802
6 files changed
+147
-26
No files matched your search
@@ -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}));
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -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)
|
||||
Reference in new issue
Block a user