`TensorIteratorTest` to API2.0 (#19869)

* `TensorIteratorTest` to API2.0

* Port `TensorIteratorBody` to ov::test::utils
This commit is contained in:
Vitaliy Urusovskij 2023-09-19 15:46:13 +04:00 committed by GitHub
parent a139fb48b7
commit ca344aea54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 365 additions and 76 deletions

View File

@ -3,59 +3,57 @@
//
#include <vector>
#include <ngraph/op/util/attr_types.hpp>
#include "single_layer_tests/tensor_iterator.hpp"
#include "single_op_tests/tensor_iterator.hpp"
#include "common_test_utils/test_constants.hpp"
using namespace LayerTestsDefinitions;
using ov::test::TensorIteratorTest;
namespace {
std::vector<bool> should_decompose = {true, false};
// output values increase rapidly without clip, so use only seq_lengths = 2
std::vector<size_t> seq_lengths_zero_clip{2};
std::vector<size_t> seq_lengths_clip_non_zero{20};
std::vector<size_t> batch{1, 10};
std::vector<size_t> hidden_size{1, 10};
// std::vector<size_t> input_size{10};
std::vector<size_t> sequence_axis{0, 1};
std::vector<ngraph::helpers::TensorIteratorBody> body_type
= {ngraph::helpers::TensorIteratorBody::LSTM, ngraph::helpers::TensorIteratorBody::RNN,
ngraph::helpers::TensorIteratorBody::GRU};
std::vector<float> clip{0.f};
std::vector<float> clip_non_zeros{0.7f};
std::vector<ngraph::op::RecurrentSequenceDirection> direction = {ngraph::op::RecurrentSequenceDirection::FORWARD,
ngraph::op::RecurrentSequenceDirection::REVERSE};
std::vector<InferenceEngine::Precision> netPrecisions = {InferenceEngine::Precision::FP32,
InferenceEngine::Precision::FP16};
std::vector<bool> should_decompose = {true, false};
// output values increase rapidly without clip, so use only seq_lengths = 2
std::vector<size_t> seq_lengths_zero_clip{2};
std::vector<size_t> seq_lengths_clip_non_zero{20};
std::vector<size_t> batch{1, 10};
std::vector<size_t> hidden_size{1, 10};
// std::vector<size_t> input_size{10};
std::vector<size_t> sequence_axis{0, 1};
std::vector<ov::test::utils::TensorIteratorBody> body_type
= {ov::test::utils::TensorIteratorBody::LSTM, ov::test::utils::TensorIteratorBody::RNN,
ov::test::utils::TensorIteratorBody::GRU};
std::vector<float> clip{0.f};
std::vector<float> clip_non_zeros{0.7f};
std::vector<ov::op::RecurrentSequenceDirection> direction = {ov::op::RecurrentSequenceDirection::FORWARD,
ov::op::RecurrentSequenceDirection::REVERSE};
std::vector<ov::element::Type> model_types = {ov::element::f32, ov::element::f16};
INSTANTIATE_TEST_SUITE_P(smoke_TensorIteratorCommon, TensorIteratorTest,
::testing::Combine(
::testing::ValuesIn(should_decompose),
::testing::ValuesIn(seq_lengths_zero_clip),
::testing::ValuesIn(batch),
::testing::ValuesIn(hidden_size),
//::testing::ValuesIn(input_size), // hardcoded to 10 due to Combine supports up to 10 args
::testing::ValuesIn(sequence_axis),
::testing::ValuesIn(clip),
::testing::ValuesIn(body_type),
::testing::ValuesIn(direction),
::testing::ValuesIn(netPrecisions),
::testing::Values(ov::test::utils::DEVICE_CPU)),
TensorIteratorTest::getTestCaseName);
INSTANTIATE_TEST_SUITE_P(smoke_TensorIteratorCommon, TensorIteratorTest,
::testing::Combine(
::testing::ValuesIn(should_decompose),
::testing::ValuesIn(seq_lengths_zero_clip),
::testing::ValuesIn(batch),
::testing::ValuesIn(hidden_size),
//::testing::ValuesIn(input_size), // hardcoded to 10 due to Combine supports up to 10 args
::testing::ValuesIn(sequence_axis),
::testing::ValuesIn(clip),
::testing::ValuesIn(body_type),
::testing::ValuesIn(direction),
::testing::ValuesIn(model_types),
::testing::Values(ov::test::utils::DEVICE_CPU)),
TensorIteratorTest::getTestCaseName);
INSTANTIATE_TEST_SUITE_P(smoke_TensorIteratorCommonClip, TensorIteratorTest,
::testing::Combine(
::testing::ValuesIn(should_decompose),
::testing::ValuesIn(seq_lengths_clip_non_zero),
::testing::ValuesIn(batch),
::testing::ValuesIn(hidden_size),
//::testing::ValuesIn(input_size), // hardcoded to 10 due to Combine supports up to 10 args
::testing::ValuesIn(sequence_axis),
::testing::ValuesIn(clip_non_zeros),
::testing::ValuesIn(body_type),
::testing::ValuesIn(direction),
::testing::ValuesIn(netPrecisions),
::testing::Values(ov::test::utils::DEVICE_CPU)),
TensorIteratorTest::getTestCaseName);
INSTANTIATE_TEST_SUITE_P(smoke_TensorIteratorCommonClip, TensorIteratorTest,
::testing::Combine(
::testing::ValuesIn(should_decompose),
::testing::ValuesIn(seq_lengths_clip_non_zero),
::testing::ValuesIn(batch),
::testing::ValuesIn(hidden_size),
//::testing::ValuesIn(input_size), // hardcoded to 10 due to Combine supports up to 10 args
::testing::ValuesIn(sequence_axis),
::testing::ValuesIn(clip_non_zeros),
::testing::ValuesIn(body_type),
::testing::ValuesIn(direction),
::testing::ValuesIn(model_types),
::testing::Values(ov::test::utils::DEVICE_CPU)),
TensorIteratorTest::getTestCaseName);
} // namespace

View File

@ -0,0 +1,15 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include "shared_test_classes/single_op/tensor_iterator.hpp"
namespace ov {
namespace test {
TEST_P(TensorIteratorTest, Inference) {
run();
};
} // namespace test
} // namespace ov

View File

@ -0,0 +1,39 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <tuple>
#include <string>
#include <vector>
#include <memory>
#include "shared_test_classes/base/ov_subgraph.hpp"
#include "common_test_utils/test_enums.hpp"
namespace ov {
namespace test {
using TensorIteratorParams = typename std::tuple<
bool, // using unroll tensor iterator transformation
size_t, // seq_lengths
size_t, // batch
size_t, // hidden size
// todo: fix. input size hardcoded to 10 due to limitation (10 args) of gtests Combine() func.
//size_t, // input size
size_t, // sequence axis
float, // clip
ov::test::utils::TensorIteratorBody, // body type
ov::op::RecurrentSequenceDirection, // direction
ov::element::Type, // Model type
ov::test::TargetDevice>; // Device name
class TensorIteratorTest : public testing::WithParamInterface<TensorIteratorParams>,
virtual public ov::test::SubgraphBaseTest {
public:
static std::string getTestCaseName(const testing::TestParamInfo<TensorIteratorParams> &obj);
protected:
void SetUp() override;
};
} // namespace test
} // namespace ov

View File

@ -0,0 +1,238 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "transformations/control_flow/unroll_tensor_iterator.hpp"
#include "shared_test_classes/single_op/tensor_iterator.hpp"
#include "openvino/pass/manager.hpp"
#include "ngraph_functions/builders.hpp"
namespace ov {
namespace test {
std::string TensorIteratorTest::getTestCaseName(const testing::TestParamInfo<TensorIteratorParams> &obj) {
bool should_decompose;
size_t seq_lengths;
size_t batch;
size_t hidden_size;
size_t input_size = 10;
size_t sequence_axis;
ov::test::utils::TensorIteratorBody ti_body;
float clip;
ov::op::RecurrentSequenceDirection direction;
ov::element::Type model_type;
std::string target_device;
std::tie(should_decompose, seq_lengths, batch, hidden_size, sequence_axis, clip, ti_body, direction, model_type,
target_device) = obj.param;
std::vector<ov::Shape> input_shapes = {};
switch (ti_body) {
case ov::test::utils::TensorIteratorBody::LSTM:
input_shapes = {
{{batch, input_size}, {batch, hidden_size}, {batch, hidden_size}, {4 * hidden_size, input_size},
{4 * hidden_size, hidden_size}, {4 * hidden_size}},
};
break;
case ov::test::utils::TensorIteratorBody::GRU:
input_shapes = {
{{batch, input_size}, {batch, hidden_size}, {3 * hidden_size, input_size},
{3 * hidden_size, hidden_size}, {3 * hidden_size}},
};
break;
case ov::test::utils::TensorIteratorBody::RNN:
input_shapes = {{batch, input_size}, {batch, hidden_size},
{hidden_size, input_size}, {hidden_size, hidden_size}, {hidden_size}};
break;
}
std::ostringstream result;
result << "unrolling=" << should_decompose << "_";
result << "seq_len=" << seq_lengths << "_";
result << "seq_len_axis=" << sequence_axis << "_";
result << "batch=" << batch << "_";
result << "hidden_size=" << hidden_size << "_";
result << "input_size=" << input_size << "_";
result << "IS=" << ov::test::utils::vec2str(input_shapes) << "_";
result << "TensorIteratorBody=" << ti_body << "_";
result << "direction=" << direction << "_";
result << "clip=" << clip << "_";
result << "modelType=" << model_type.to_string() << "_";
result << "targetDevice=" << target_device << "_";
return result.str();
}
void TensorIteratorTest::SetUp() {
size_t seq_lengths;
bool should_decompose;
size_t batch;
size_t hidden_size;
size_t input_size = 10;
size_t sequence_axis;
ov::test::utils::TensorIteratorBody ti_body;
float clip;
ov::op::RecurrentSequenceDirection direction;
ov::element::Type model_type;
std::tie(should_decompose, seq_lengths, batch, hidden_size, sequence_axis, clip, ti_body, direction, model_type,
targetDevice) = this->GetParam();
std::vector<ov::Shape> input_shapes;
auto tensor_iterator = std::make_shared<ov::op::v0::TensorIterator>();
// Each case consist of 3 steps:
// 1. Create TensorIterator body.
// 2. Set PortMap
// 3. Create outer function
auto axis = std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{1},
std::vector<int64_t>{static_cast<int64_t>(sequence_axis)});
switch (ti_body) {
case ov::test::utils::TensorIteratorBody::LSTM: {
input_shapes = {
{{batch, seq_lengths, input_size}, {batch, hidden_size}, {batch, hidden_size}, {4 * hidden_size, input_size},
{4 * hidden_size, hidden_size}, {4 * hidden_size}},
};
if (sequence_axis == 0) {
// swap batch and seq_lengths
std::swap(input_shapes[0][0], input_shapes[0][1]);
}
init_input_shapes(static_shapes_to_test_representation(input_shapes));
ov::ParameterVector outer_params{std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[0]),
std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[1]),
std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[2])};
// 1. Create TensorIterator body.
inputDynamicShapes[0][sequence_axis] = 1; // sliced dimension
ov::ParameterVector body_params{std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[0]),
std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[1]),
std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[2])};
auto squeeze = std::make_shared<ov::op::v0::Squeeze>(body_params[0], axis);
std::vector<ov::Shape> WRB = {input_shapes[3], input_shapes[4], input_shapes[5]};
ov::OutputVector out_vector = {squeeze, body_params[1], body_params[2]};
auto lstm_cell = ngraph::builder::makeLSTM(out_vector, WRB, hidden_size, {"sigmoid", "tanh", "tanh"}, {}, {}, clip);
auto unsqueeze = std::make_shared<ov::op::v0::Unsqueeze>(lstm_cell->output(0), axis);
ov::ResultVector results{std::make_shared<ov::op::v0::Result>(unsqueeze),
std::make_shared<ov::op::v0::Result>(lstm_cell->output(0)),
std::make_shared<ov::op::v0::Result>(lstm_cell->output(1))};
auto body = std::make_shared<ov::Model>(results, body_params, "lstm_cell");
tensor_iterator->set_function(body);
// 2. Set PortMap
if (direction == ov::op::RecurrentSequenceDirection::FORWARD) {
tensor_iterator->set_sliced_input(body_params[0], outer_params[0], 0, 1, 1, -1, sequence_axis);
tensor_iterator->get_concatenated_slices(results[0], 0, 1, 1, -1, sequence_axis);
} else if (direction == ov::op::RecurrentSequenceDirection::REVERSE) {
tensor_iterator->set_sliced_input(body_params[0], outer_params[0], -1, -1, 1, 0, sequence_axis);
tensor_iterator->get_concatenated_slices(results[0], -1, -1, 1, 0, sequence_axis);
} else {
OPENVINO_THROW("Bidirectional case is not supported.");
}
tensor_iterator->set_merged_input(body_params[1], outer_params[1], results[1]);
tensor_iterator->set_merged_input(body_params[2], outer_params[2], results[2]);
tensor_iterator->get_iter_value(results[1]);
tensor_iterator->get_iter_value(results[2]);
// 3. Outer model
function = std::make_shared<ov::Model>(tensor_iterator->outputs(), outer_params);
break;
}
case ov::test::utils::TensorIteratorBody::GRU: {
input_shapes = {
{{batch, seq_lengths, input_size}, {batch, hidden_size}, {3 * hidden_size, input_size},
{3 * hidden_size, hidden_size}, {3 * hidden_size}},
};
if (sequence_axis == 0) {
// swap batch and seq_lengths
std::swap(input_shapes[0][0], input_shapes[0][1]);
}
init_input_shapes(static_shapes_to_test_representation(input_shapes));
ov::ParameterVector outer_params{std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[0]),
std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[1])};
// 1. Create TensorIterator body.
inputDynamicShapes[0][sequence_axis] = 1; // sliced dimension
ov::ParameterVector body_params{std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[0]),
std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[1])};
std::vector<ov::Shape> WRB = {input_shapes[2], input_shapes[3], input_shapes[4]};
auto squeeze = std::make_shared<ov::op::v0::Squeeze>(body_params[0], axis);
ov::OutputVector out_vector = {squeeze, body_params[1]};
auto gru_cell = ngraph::builder::makeGRU(out_vector, WRB, hidden_size, {"sigmoid", "tanh"},
{}, {}, clip, false);
auto unsqueeze = std::make_shared<ov::op::v0::Unsqueeze>(gru_cell->output(0), axis);
ov::ResultVector results{std::make_shared<ov::op::v0::Result>(gru_cell->output(0)),
std::make_shared<ov::op::v0::Result>(unsqueeze)};
auto body = std::make_shared<ov::Model>(results, body_params, "gru_cell");
tensor_iterator->set_function(body);
// 2. Set PortMap
if (direction == ov::op::RecurrentSequenceDirection::FORWARD) {
tensor_iterator->set_sliced_input(body_params[0], outer_params[0], 0, 1, 1, -1, sequence_axis);
tensor_iterator->get_concatenated_slices(results[1], 0, 1, 1, -1, sequence_axis);
} else if (direction == ov::op::RecurrentSequenceDirection::REVERSE) {
tensor_iterator->set_sliced_input(body_params[0], outer_params[0], -1, -1, 1, 0, sequence_axis);
tensor_iterator->get_concatenated_slices(results[1], -1, -1, 1, 0, sequence_axis);
} else {
OPENVINO_THROW("Bidirectional case is not supported.");
}
tensor_iterator->set_merged_input(body_params[1], outer_params[1], results[0]);
tensor_iterator->get_iter_value(results[0]);
// 3. Outer function
function = std::make_shared<ov::Model>(ov::OutputVector{tensor_iterator->output(0), tensor_iterator->output(1)}, outer_params);
break;
}
case ov::test::utils::TensorIteratorBody::RNN: {
input_shapes = {{batch, seq_lengths, input_size},
{batch, hidden_size},
{hidden_size, input_size},
{hidden_size, hidden_size},
{hidden_size}};
if (sequence_axis == 0) {
// swap batch and seq_lengths
std::swap(input_shapes[0][0], input_shapes[0][1]);
}
init_input_shapes(static_shapes_to_test_representation(input_shapes));
ov::ParameterVector outer_params{std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[0]),
std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[1])};
// 1. Create TensorIterator body.
inputDynamicShapes[0][sequence_axis] = 1; // sliced dimension
ov::ParameterVector body_params{std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[0]),
std::make_shared<ov::op::v0::Parameter>(model_type, inputDynamicShapes[1])};
std::vector<ov::Shape> WRB = {input_shapes[2], input_shapes[3], input_shapes[4]};
auto squeeze = std::make_shared<ov::op::v0::Squeeze>(body_params[0], axis);
ov::OutputVector out_vector = {squeeze, body_params[1]};
auto rnn_cell = ngraph::builder::makeRNN(out_vector, WRB, hidden_size, {"tanh"}, {}, {}, clip);
auto unsqueeze = std::make_shared<ov::op::v0::Unsqueeze>(rnn_cell->output(0), axis);
ov::ResultVector results{std::make_shared<ov::op::v0::Result>(rnn_cell),
std::make_shared<ov::op::v0::Result>(unsqueeze)};
auto body = std::make_shared<ov::Model>(results, body_params, "rnn_cell");
tensor_iterator->set_function(body);
// 2. Set PortMap
if (direction == ov::op::RecurrentSequenceDirection::FORWARD) {
tensor_iterator->set_sliced_input(body_params[0], outer_params[0], 0, 1, 1, -1, sequence_axis);
tensor_iterator->get_concatenated_slices(results[1], 0, 1, 1, -1, sequence_axis);
} else if (direction == ov::op::RecurrentSequenceDirection::REVERSE) {
tensor_iterator->set_sliced_input(body_params[0], outer_params[0], -1, -1, 1, 0, sequence_axis);
tensor_iterator->get_concatenated_slices(results[1], -1, -1, 1, 0, sequence_axis);
} else {
OPENVINO_THROW("Bidirectional case is not supported.");
}
tensor_iterator->set_merged_input(body_params[1], outer_params[1], results[0]);
tensor_iterator->get_iter_value(results[0]);
// 3. Outer function
function = std::make_shared<ov::Model>(ov::OutputVector{tensor_iterator->output(0), tensor_iterator->output(1)}, outer_params);
break;
}
}
if (should_decompose) {
ov::pass::Manager m;
m.register_pass<ov::pass::UnrollTensorIterator>();
m.run_passes(function);
}
}
} // namespace test
} // namespace ov

View File

@ -85,19 +85,11 @@ enum QuantizationGranularity {
Perchannel
};
using ov::test::utils::TensorIteratorBody;
using ov::test::utils::ReductionType;
using ov::test::utils::DFTOpType;
using ov::test::utils::InputLayerType;
using ov::test::utils::PadMode;
enum class TensorIteratorBody {
RNN,
GRU,
LSTM,
// CNN todo: implement
};
using ov::test::utils::SequenceTestsMode;
enum class MemoryTransformation {
@ -169,8 +161,6 @@ std::vector<std::uint8_t> convertOutputPrecision(const std::vector<std::uint8_t>
const element::Type_t& toPrecision,
const size_t elementsCount);
std::ostream& operator<<(std::ostream& os, TensorIteratorBody type);
std::ostream& operator<<(std::ostream& os, MemoryTransformation type);
void resize_function(std::shared_ptr<ov::Model> function, const std::vector<ov::Shape>& targetInputStaticShapes);

View File

@ -650,23 +650,6 @@ std::vector<std::uint8_t> convertOutputPrecision(const std::vector<std::uint8_t>
}
}
std::ostream& operator<<(std::ostream& os, TensorIteratorBody type) {
switch (type) {
case TensorIteratorBody::LSTM:
os << "LSTM";
break;
case TensorIteratorBody::RNN:
os << "RNN";
break;
case TensorIteratorBody::GRU:
os << "GRU";
break;
default:
throw std::runtime_error("NOT_SUPPORTED_OP_TYPE");
}
return os;
}
std::ostream& operator<<(std::ostream& os, MemoryTransformation type) {
switch (type) {
case MemoryTransformation::NONE:

View File

@ -152,6 +152,13 @@ enum class DFTOpType {
INVERSE
};
enum class TensorIteratorBody {
RNN,
GRU,
LSTM,
// CNN todo: implement
};
// clang-format on
std::ostream& operator<<(std::ostream& os, const ReductionType& m);
@ -182,6 +189,8 @@ std::ostream& operator<<(std::ostream& os, ov::op::v8::MatrixNms::SortResultType
std::ostream& operator<<(std::ostream& os, ov::op::v8::MatrixNms::DecayFunction type);
std::ostream& operator<<(std::ostream& os, TensorIteratorBody type);
} // namespace utils
} // namespace test
} // namespace ov

View File

@ -322,6 +322,23 @@ std::ostream& operator<<(std::ostream& os, op::v8::MatrixNms::DecayFunction type
return os;
}
std::ostream& operator<<(std::ostream& os, TensorIteratorBody type) {
switch (type) {
case TensorIteratorBody::LSTM:
os << "LSTM";
break;
case TensorIteratorBody::RNN:
os << "RNN";
break;
case TensorIteratorBody::GRU:
os << "GRU";
break;
default:
throw std::runtime_error("NOT_SUPPORTED_OP_TYPE");
}
return os;
}
} // namespace utils
} // namespace test
} // namespace ov