[TF FE] Implement translators for ExtractImagePatches and MatrixDiag (#12593)
* [TF FE] Implement translators for ExtractImagePatches and MatrixDiag It allows to convert Inpaint model and infer it correctly Signed-off-by: Kazantsev, Roman <roman.kazantsev@intel.com> * Apply code-review feedback: correct comments, use set Signed-off-by: Kazantsev, Roman <roman.kazantsev@intel.com> * Apply suggestions from code review Co-authored-by: Maxim Vafin <maxim.vafin@intel.com> Signed-off-by: Kazantsev, Roman <roman.kazantsev@intel.com> Co-authored-by: Maxim Vafin <maxim.vafin@intel.com>
This commit is contained in:
parent
6fd23416d4
commit
190d692c4d
58
src/frontends/tensorflow/src/op/extract_image_patches.cpp
Normal file
58
src/frontends/tensorflow/src/op/extract_image_patches.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "op_table.hpp"
|
||||
#include "openvino/op/util/attr_types.hpp"
|
||||
#include "openvino/opsets/opset8.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov::opset8;
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace tensorflow {
|
||||
namespace op {
|
||||
|
||||
OutputVector translate_extract_image_patches_op(const NodeContext& node) {
|
||||
TENSORFLOW_OP_VALIDATION(node, node.get_input_size() >= 0, "ExtractImagePatches must have at least one input.");
|
||||
auto images = node.get_input(0);
|
||||
|
||||
// retrieve attributes for ExtractImagePatches
|
||||
auto tf_ksizes = node.get_attribute<std::vector<int64_t>>("ksizes");
|
||||
auto tf_strides = node.get_attribute<std::vector<int64_t>>("strides");
|
||||
auto tf_rates = node.get_attribute<std::vector<int64_t>>("rates");
|
||||
auto tf_padding_type = node.get_attribute<std::string>("padding");
|
||||
ov::op::PadType auto_pad = convert_tf_padding(node, tf_padding_type);
|
||||
TENSORFLOW_OP_VALIDATION(node,
|
||||
auto_pad == ov::op::PadType::SAME_UPPER || auto_pad == ov::op::PadType::VALID,
|
||||
"Only SAME_UPPER and VALID padding modes are supported for ExtractImagePatches.");
|
||||
|
||||
// prepare attributes for OpenVINO ExtractImagePatches
|
||||
Shape sizes(2);
|
||||
Shape rates(2);
|
||||
Strides strides(2);
|
||||
|
||||
// layout for this operation is always NHWC
|
||||
bool is_nhwc = true;
|
||||
convert_nhwc_to_hw(is_nhwc, tf_ksizes, sizes);
|
||||
convert_nhwc_to_hw(is_nhwc, tf_strides, strides);
|
||||
convert_nhwc_to_hw(is_nhwc, tf_rates, rates);
|
||||
|
||||
// prepare input to ExtractImagePatches
|
||||
convert_nhwc_to_nchw(is_nhwc, images);
|
||||
|
||||
auto extract_image_patches = make_shared<ExtractImagePatches>(images, sizes, strides, rates, auto_pad);
|
||||
|
||||
// prepare output to return the original layout NHWC
|
||||
auto extract_image_patches_output = extract_image_patches->output(0);
|
||||
convert_nchw_to_nhwc(is_nhwc, extract_image_patches_output);
|
||||
|
||||
set_node_name(node.get_name(), extract_image_patches_output.get_node_shared_ptr());
|
||||
return {extract_image_patches_output};
|
||||
}
|
||||
} // namespace op
|
||||
} // namespace tensorflow
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
94
src/frontends/tensorflow/src/op/matrix_diag.cpp
Normal file
94
src/frontends/tensorflow/src/op/matrix_diag.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright (C) 2018-2022 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "op_table.hpp"
|
||||
#include "openvino/op/util/attr_types.hpp"
|
||||
#include "openvino/opsets/opset8.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov::opset8;
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace tensorflow {
|
||||
namespace op {
|
||||
|
||||
OutputVector translate_matrix_diag_op(const NodeContext& node) {
|
||||
// The translation of MatrixDiag to OpenVINO opset relies on padding of input tensor with zeros,
|
||||
// reshape to a special form and cutting of unneeded padding part.
|
||||
// Here is a basic idea described by an example,
|
||||
// let us have a tensor [1, 2, 3] and generate padding tensor of zeros with a shape [3, 3].
|
||||
// Concatenate input tensor with padding and get the following:
|
||||
// [[1, 0, 0, 0]
|
||||
// [2, 0, 0, 0]
|
||||
// [3, 0, 0, 0]] of shape [3, 4]
|
||||
// Reshape to tensor of a shape [12] equal to [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]
|
||||
// Cut off last 3 elements and get [1, 0, 0, 0, 2, 0, 0, 0, 3] and reshape to [3, 3]
|
||||
// This idea is generalized to higher rank tensors
|
||||
TENSORFLOW_OP_VALIDATION(node, node.get_input_size() > 0, "MatrixDiag must have at least one input.");
|
||||
// diagonal is the single input to MatrixDiag operation and has a shape [I, J, ..., M, N]
|
||||
auto diagonal = node.get_input(0);
|
||||
auto diagonal_type = diagonal.get_element_type();
|
||||
|
||||
// 1. unsqueeze to have at least three rank input of a shape [1, I, J, ..., M, N, 1]
|
||||
// because dimensions [I, J, ..., M] can be absent
|
||||
auto unsqueeze_axis = make_shared<Constant>(element::i64, Shape{2}, std::vector<int64_t>{0, -1});
|
||||
auto unsqueeze_diag = make_shared<Unsqueeze>(diagonal, unsqueeze_axis);
|
||||
|
||||
// 2. compute a size of the last dimension of the diagonal input of a shape [I, J, ..., M, N],
|
||||
// i.e. N that will be diagonalized
|
||||
auto unsqueeze_diag_shape = make_shared<ShapeOf>(unsqueeze_diag);
|
||||
auto last_dim = make_shared<StridedSlice>(unsqueeze_diag_shape,
|
||||
make_shared<Constant>(element::i64, Shape{1}, std::vector<int64_t>{-2}),
|
||||
make_shared<Constant>(element::i64, Shape{1}, std::vector<int64_t>{-1}),
|
||||
make_shared<Constant>(element::i64, Shape{1}, std::vector<int64_t>{1}),
|
||||
std::vector<int64_t>({0}),
|
||||
std::vector<int64_t>({0}));
|
||||
|
||||
// 3. generate a tensor of zeros of a shape [1, I, J, ..., M, N, N]
|
||||
auto diag_shape = make_shared<ShapeOf>(diagonal);
|
||||
auto one_dim = make_shared<Constant>(last_dim->get_element_type(), Shape{1}, std::vector<int64_t>{1});
|
||||
auto padding_shape = make_shared<Concat>(OutputVector({one_dim, diag_shape, last_dim}), 0);
|
||||
auto padding =
|
||||
make_shared<Broadcast>(make_shared<Constant>(diagonal_type, Shape{1}, std::vector<int64_t>{0}), padding_shape);
|
||||
|
||||
// 4. concatenate to get input tensor with zero padding of a shape [1, I, J, ..., M, N, N + 1]
|
||||
auto zero_padded_diag = make_shared<Concat>(OutputVector({unsqueeze_diag, padding}), -1);
|
||||
|
||||
// reshape padded tensor to get a shape [I, J, ..., M, N * N + N]
|
||||
// 4.1 retrieve a part of the shape value [1, I, J, ..., M]
|
||||
auto new_shape_padded_diag1 =
|
||||
make_shared<StridedSlice>(unsqueeze_diag_shape,
|
||||
make_shared<Constant>(element::i64, Shape{1}, std::vector<int64_t>{0}),
|
||||
make_shared<Constant>(element::i64, Shape{1}, std::vector<int64_t>{-2}),
|
||||
make_shared<Constant>(element::i64, Shape{1}, std::vector<int64_t>{1}),
|
||||
std::vector<int64_t>({0}),
|
||||
std::vector<int64_t>({0}));
|
||||
// 4.2 compute the last part of a shape that is [N * N + N]
|
||||
auto last_dim_squared = make_shared<Multiply>(last_dim, last_dim);
|
||||
auto new_shape_padded_diag2 = make_shared<Add>(last_dim_squared, last_dim);
|
||||
// 4.3 compute a new shape and reshape padded diagonal
|
||||
auto new_shape_padded_diag = make_shared<Concat>(OutputVector({new_shape_padded_diag1, new_shape_padded_diag2}), 0);
|
||||
auto reshaped_padded_diag = make_shared<Reshape>(zero_padded_diag, new_shape_padded_diag, false);
|
||||
|
||||
// 5. cut off padding in the reshaped padded tensor to get a shape [1, I, J, ..., M, N * N]
|
||||
auto cut_padded_diag = make_shared<Slice>(
|
||||
reshaped_padded_diag,
|
||||
make_shared<Constant>(last_dim_squared->get_element_type(), Shape{1}, std::vector<int64_t>{0}),
|
||||
last_dim_squared,
|
||||
make_shared<Constant>(last_dim_squared->get_element_type(), Shape{1}, std::vector<int64_t>{1}),
|
||||
make_shared<Constant>(element::i64, Shape{1}, std::vector<int64_t>{-1}));
|
||||
|
||||
// 6. return the expected shape for the result [I, J, ..., M, N, N]
|
||||
auto resulted_shape = make_shared<Concat>(OutputVector({diag_shape, last_dim}), 0);
|
||||
auto resulted_diag = make_shared<Reshape>(cut_padded_diag, resulted_shape, false);
|
||||
|
||||
set_node_name(node.get_name(), resulted_diag);
|
||||
return {resulted_diag};
|
||||
}
|
||||
} // namespace op
|
||||
} // namespace tensorflow
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
@ -14,13 +14,25 @@ namespace tensorflow {
|
||||
namespace op {
|
||||
|
||||
OutputVector translate_placeholder_op(const NodeContext& node) {
|
||||
auto ng_et = node.get_attribute<ov::element::Type>("dtype");
|
||||
auto ng_shape = node.get_attribute<ov::PartialShape>("shape", ov::PartialShape());
|
||||
auto tf_dtype = node.get_attribute<ov::element::Type>("dtype");
|
||||
auto tf_shape = node.get_attribute<ov::PartialShape>("shape", ov::PartialShape::dynamic());
|
||||
|
||||
auto res = std::make_shared<Parameter>(ng_et, ng_shape);
|
||||
auto res = std::make_shared<Parameter>(tf_dtype, tf_shape);
|
||||
set_node_name(node.get_name(), res);
|
||||
return res->outputs();
|
||||
}
|
||||
|
||||
OutputVector translate_placeholder_with_default_op(const NodeContext& node) {
|
||||
// For parity with legacy frontend, it creates a constant node with the default value
|
||||
// As a rule, PlaceholderWithDefault is mainly used for is_training variables in the model
|
||||
TENSORFLOW_OP_VALIDATION(node,
|
||||
node.get_input_size() > 0,
|
||||
"PlaceholderWithDefault must have at least one input that is the default value.");
|
||||
auto input = node.get_input(0);
|
||||
set_out_name(node.get_name(), input);
|
||||
return {input};
|
||||
}
|
||||
|
||||
} // namespace op
|
||||
} // namespace tensorflow
|
||||
} // namespace frontend
|
||||
|
@ -41,6 +41,7 @@ OP_CONVERTER(translate_depth_to_space_op);
|
||||
OP_CONVERTER(translate_depthwise_conv_2d_native_op);
|
||||
OP_CONVERTER(translate_elu_op);
|
||||
OP_CONVERTER(translate_expand_dims_op);
|
||||
OP_CONVERTER(translate_extract_image_patches_op);
|
||||
OP_CONVERTER(translate_fake_quant_op);
|
||||
OP_CONVERTER(translate_fill_op);
|
||||
OP_CONVERTER(translate_floor_div_op);
|
||||
@ -58,10 +59,12 @@ OP_CONVERTER(translate_log_softmax_op);
|
||||
OP_CONVERTER(translate_log_1p_op);
|
||||
OP_CONVERTER(translate_lrn_op);
|
||||
OP_CONVERTER(translate_mat_mul_op);
|
||||
OP_CONVERTER(translate_matrix_diag_op);
|
||||
OP_CONVERTER(translate_max_pool_op);
|
||||
OP_CONVERTER(translate_non_max_suppression_op);
|
||||
OP_CONVERTER(translate_pad_op);
|
||||
OP_CONVERTER(translate_placeholder_op);
|
||||
OP_CONVERTER(translate_placeholder_with_default_op);
|
||||
OP_CONVERTER(translate_no_op);
|
||||
OP_CONVERTER(translate_one_hot_op);
|
||||
OP_CONVERTER(translate_pack_op);
|
||||
@ -181,6 +184,7 @@ const std::map<std::string, CreatorFunction> get_supported_ops() {
|
||||
{"DepthwiseConv2dNative", translate_depthwise_conv_2d_native_op},
|
||||
{"Elu", translate_elu_op},
|
||||
{"ExpandDims", translate_expand_dims_op},
|
||||
{"ExtractImagePatches", translate_extract_image_patches_op},
|
||||
{"FakeQuantWithMinMaxVars", translate_fake_quant_op},
|
||||
{"Fill", translate_fill_op},
|
||||
{"FloorDiv", translate_floor_div_op},
|
||||
@ -200,6 +204,7 @@ const std::map<std::string, CreatorFunction> get_supported_ops() {
|
||||
{"Log1p", translate_log_1p_op},
|
||||
{"LRN", translate_lrn_op},
|
||||
{"MatMul", translate_mat_mul_op},
|
||||
{"MatrixDiag", translate_matrix_diag_op},
|
||||
{"MaxPool", translate_max_pool_op},
|
||||
{"MaxPoolV2", translate_max_pool_op},
|
||||
{"MaxPool3D", translate_max_pool_op},
|
||||
@ -215,6 +220,7 @@ const std::map<std::string, CreatorFunction> get_supported_ops() {
|
||||
{"Pad", translate_pad_op},
|
||||
{"PadV2", translate_pad_op},
|
||||
{"Placeholder", translate_placeholder_op},
|
||||
{"PlaceholderWithDefault", translate_placeholder_with_default_op},
|
||||
{"PreventGradient", translate_identity_op},
|
||||
{"Range", translate_range_op},
|
||||
{"Rank", translate_rank_op},
|
||||
|
@ -34,13 +34,19 @@ void ov::frontend::tensorflow::set_out_name(const std::string& out_name, const o
|
||||
|
||||
ov::op::PadType ov::frontend::tensorflow::convert_tf_padding(const ov::frontend::tensorflow::NodeContext& node,
|
||||
const std::string& tf_padding) {
|
||||
std::set<std::string> supported_ops = {"Conv2D",
|
||||
"Conv2DBackpropInput",
|
||||
"Conv3D",
|
||||
"Conv3DBackpropInputV2",
|
||||
"MaxPool",
|
||||
"MaxPoolV2",
|
||||
"MaxPool3D",
|
||||
"ExtractImagePatches"};
|
||||
auto op_type = node.get_op_type();
|
||||
|
||||
TENSORFLOW_OP_VALIDATION(node,
|
||||
op_type == "Conv2D" || op_type == "Conv2DBackpropInput" || op_type == "Conv3D" ||
|
||||
op_type == "Conv3DBackpropInputV2" || op_type == "MaxPool" || op_type == "MaxPoolV2" ||
|
||||
op_type == "MaxPool3D",
|
||||
"The convert_conv_tf_padding routine supports only convolutional operations.");
|
||||
supported_ops.count(op_type),
|
||||
"Conversion of padding mode for " + op_type + " is not supported.");
|
||||
TENSORFLOW_OP_VALIDATION(
|
||||
node,
|
||||
tf_padding == "VALID" || tf_padding == "SAME" || tf_padding == "EXPLICIT",
|
||||
@ -57,7 +63,7 @@ ov::op::PadType ov::frontend::tensorflow::convert_tf_padding(const ov::frontend:
|
||||
return ov::op::PadType::SAME_LOWER;
|
||||
}
|
||||
} else if (op_type == "Conv2D" || op_type == "Conv3D" || op_type == "MaxPool" || op_type == "MaxPoolV2" ||
|
||||
op_type == "MaxPool3D") {
|
||||
op_type == "MaxPool3D" || op_type == "ExtractImagePatches") {
|
||||
if (tf_padding == "SAME") {
|
||||
// According to the formulas for calculating auto_pad values of the
|
||||
// Conv layer in the Operation specification,
|
||||
|
Loading…
Reference in New Issue
Block a user