Review experimental detectron topk rois for shape inference aspects (#17018)

* Review experimental detectron topk rois for:
- interval shape and label propagation
- template implementation of shape_infer
- expand unit test fot partial and static shapes

* Fix build issues
This commit is contained in:
Pawel Raasz
2023-05-08 08:34:56 +02:00
committed by GitHub
parent f93c8dda78
commit 8d81223a28
6 changed files with 325 additions and 109 deletions

View File

@@ -34,17 +34,14 @@ public:
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
void set_max_rois(size_t max_rois);
size_t get_max_rois() const {
return m_max_rois;
}
private:
size_t m_max_rois{0};
template <class T>
friend void shape_infer(ExperimentalDetectronTopKROIs* op,
const std::vector<T>& input_shapes,
std::vector<T>& output_shapes);
};
} // namespace v6
} // namespace op

View File

@@ -8,44 +8,50 @@ namespace ov {
namespace op {
namespace v6 {
template <class T>
void shape_infer(ExperimentalDetectronTopKROIs* op, const std::vector<T>& input_shapes, std::vector<T>& output_shapes) {
NODE_VALIDATION_CHECK(op, input_shapes.size() == 2 && output_shapes.size() == 1);
template <class TShape>
std::vector<TShape> shape_infer(ExperimentalDetectronTopKROIs* op, const std::vector<TShape>& input_shapes) {
NODE_VALIDATION_CHECK(op, input_shapes.size() == 2);
const auto input_rois_shape = input_shapes[0];
const auto rois_probs_shape = input_shapes[1];
const auto& input_rois_shape = input_shapes[0];
const auto& rois_probs_shape = input_shapes[1];
if (input_rois_shape.rank().is_static()) {
const auto input_rois_rank = input_rois_shape.rank();
const auto rois_probs_rank = rois_probs_shape.rank();
if (input_rois_rank.is_static()) {
NODE_VALIDATION_CHECK(op,
input_rois_shape.rank().get_length() == 2,
input_rois_rank.get_length() == 2,
"The 'input_rois' input is expected to be a 2D. Got: ",
input_rois_shape);
input_rois_rank);
NODE_VALIDATION_CHECK(op,
input_rois_shape[1].compatible(4),
"The second dimension of 'input_rois' should be 4. Got: ",
input_rois_shape[1]);
}
NODE_VALIDATION_CHECK(op,
rois_probs_shape.rank().compatible(1),
"The 'rois_probs' input is expected to be a 1D. Got: ",
rois_probs_shape);
if (input_rois_shape.rank().is_static() && rois_probs_shape.rank().is_static()) {
NODE_VALIDATION_CHECK(op,
rois_probs_rank.compatible(1),
"The 'rois_probs' input is expected to be a 1D. Got: ",
rois_probs_rank);
if (input_rois_rank.is_static() && rois_probs_rank.is_static()) {
NODE_VALIDATION_CHECK(op,
input_rois_shape[0].compatible(rois_probs_shape[0]),
"Number of rois and number of probabilities should be equal. Got: ",
input_rois_shape[0],
" ",
rois_probs_shape[0]);
}
auto& output_shape = output_shapes[0];
auto max_rois = op->m_max_rois;
output_shape.resize(2);
output_shape[0] = max_rois;
output_shape[1] = 4;
return {{static_cast<typename TShape::value_type>(op->get_max_rois()), 4}};
}
template <class TShape>
void shape_infer(ExperimentalDetectronTopKROIs* op,
const std::vector<TShape>& input_shapes,
std::vector<TShape>& output_shapes) {
output_shapes = shape_infer(op, input_shapes);
}
} // namespace v6
} // namespace op
} // namespace ov

View File

@@ -2,18 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
//
#include "ngraph/op/experimental_detectron_topkrois.hpp"
#include <experimental_detectron_topkrois_shape_inference.hpp>
#include "openvino/op/experimental_detectron_topkrois.hpp"
#include "experimental_detectron_topkrois_shape_inference.hpp"
#include "itt.hpp"
#include "ngraph/attribute_visitor.hpp"
#include "ngraph/op/util/op_types.hpp"
#include "ngraph/validation_util.hpp"
#include "openvino/core/attribute_visitor.hpp"
#include "openvino/core/validation_util.hpp"
using namespace std;
using namespace ngraph;
namespace ov {
op::v6::ExperimentalDetectronTopKROIs::ExperimentalDetectronTopKROIs(const Output<Node>& input_rois,
const Output<Node>& rois_probs,
size_t max_rois)
@@ -36,11 +33,22 @@ shared_ptr<Node> op::v6::ExperimentalDetectronTopKROIs::clone_with_new_inputs(co
void op::v6::ExperimentalDetectronTopKROIs::validate_and_infer_types() {
OV_OP_SCOPE(v6_ExperimentalDetectronTopKROIs_validate_and_infer_types);
const auto input_rois_shape = get_input_partial_shape(0);
const auto rois_probs_shape = get_input_partial_shape(1);
std::vector<ov::PartialShape> output_shapes = {ov::PartialShape{}};
std::vector<ov::PartialShape> input_shapes = {input_rois_shape, rois_probs_shape};
shape_infer(this, input_shapes, output_shapes);
set_output_type(0, get_input_element_type(0), output_shapes[0]);
auto out_et = element::dynamic;
NODE_VALIDATION_CHECK(this,
element::Type::merge(out_et, get_input_element_type(0), get_input_element_type(1)) &&
(out_et.is_dynamic() || out_et.is_real()),
"ROIs and probabilities of ROIs must same floating-point type.");
OPENVINO_SUPPRESS_DEPRECATED_START
const auto input_shapes = get_node_input_partial_shapes(*this);
OPENVINO_SUPPRESS_DEPRECATED_START
const auto output_shapes = shape_infer(this, input_shapes);
set_output_type(0, out_et, output_shapes[0]);
}
void op::v6::ExperimentalDetectronTopKROIs::set_max_rois(size_t max_rois) {
m_max_rois = max_rois;
}
} // namespace ov

View File

@@ -2,51 +2,197 @@
// SPDX-License-Identifier: Apache-2.0
//
#include <vector>
#include "common_test_utils/test_assertions.hpp"
#include "gtest/gtest.h"
#include "ngraph/ngraph.hpp"
#include "openvino/opsets/opset11.hpp"
#include "util/type_prop.hpp"
using namespace ngraph;
using namespace ov;
using namespace ov::opset11;
using namespace testing;
using ExperimentalTopKROIs = op::v6::ExperimentalDetectronTopKROIs;
class TypePropExperimentalDetectronTopKROIsV6 : public TypePropOpTest<op::v6::ExperimentalDetectronTopKROIs> {};
TEST(type_prop, detectron_topk_rois) {
auto input_rois = std::make_shared<op::Parameter>(element::f32, Shape{5000, 4});
auto rois_probs = std::make_shared<op::Parameter>(element::f32, Shape{5000});
size_t max_rois = 1000;
auto topk_rois = std::make_shared<ExperimentalTopKROIs>(input_rois, rois_probs, max_rois);
TEST_F(TypePropExperimentalDetectronTopKROIsV6, default_ctor) {
const auto input_rois = std::make_shared<Parameter>(element::f32, Shape{500, 4});
const auto rois_probs = std::make_shared<Parameter>(element::f32, Shape{500});
ASSERT_EQ(topk_rois->get_output_element_type(0), element::f32);
EXPECT_EQ(topk_rois->get_output_shape(0), (Shape{max_rois, 4}));
const auto op = make_op();
op->set_arguments(OutputVector{input_rois, rois_probs});
op->set_max_rois(20);
op->validate_and_infer_types();
EXPECT_EQ(op->get_input_size(), 2);
EXPECT_EQ(op->get_output_size(), 1);
EXPECT_EQ(op->get_max_rois(), 20);
EXPECT_EQ(op->get_output_element_type(0), element::f32);
EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({20, 4}));
}
TEST(type_prop, detectron_topk_rois_dynamic) {
struct ShapesAndMaxROIs {
PartialShape input_rois_shape;
PartialShape rois_probs_shape;
size_t max_rois;
};
TEST_F(TypePropExperimentalDetectronTopKROIsV6, simple_shape_infer) {
const auto input_rois = std::make_shared<Parameter>(element::f32, Shape{5000, 4});
const auto rois_probs = std::make_shared<Parameter>(element::f32, Shape{5000});
const auto op = make_op(input_rois, rois_probs, 1000);
const auto dyn_dim = Dimension::dynamic();
const auto dyn_shape = PartialShape::dynamic();
std::vector<ShapesAndMaxROIs> shapes_and_attrs = {{{3000, 4}, dyn_shape, 700},
{dyn_shape, {4000}, 600},
{dyn_shape, dyn_shape, 500},
{{dyn_dim, 4}, dyn_shape, 700},
{dyn_shape, {dyn_dim}, 600},
{dyn_shape, dyn_shape, 500},
{{dyn_dim, 4}, {dyn_dim}, 700}};
for (const auto& s : shapes_and_attrs) {
auto input_rois = std::make_shared<op::Parameter>(element::f32, s.input_rois_shape);
auto rois_probs = std::make_shared<op::Parameter>(element::f32, s.rois_probs_shape);
size_t max_rois = s.max_rois;
auto topk_rois = std::make_shared<ExperimentalTopKROIs>(input_rois, rois_probs, max_rois);
ASSERT_EQ(topk_rois->get_output_element_type(0), element::f32);
EXPECT_EQ(topk_rois->get_output_shape(0), (Shape{max_rois, 4}));
}
EXPECT_EQ(op->get_output_size(), 1);
EXPECT_EQ(op->get_output_element_type(0), element::f32);
EXPECT_EQ(op->get_output_shape(0), Shape({1000, 4}));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, dynamic_rank_rois_and_probabilities) {
const auto input_rois = std::make_shared<Parameter>(element::f64, PartialShape::dynamic());
const auto rois_probs = std::make_shared<Parameter>(element::f64, PartialShape::dynamic());
const auto op = make_op(input_rois, rois_probs, 1000);
EXPECT_EQ(op->get_output_size(), 1);
EXPECT_EQ(op->get_output_element_type(0), element::f64);
EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({1000, 4}));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, static_rank_rois_and_probabilities) {
const auto input_rois = std::make_shared<Parameter>(element::f64, PartialShape::dynamic(2));
const auto rois_probs = std::make_shared<Parameter>(element::f64, PartialShape::dynamic(1));
const auto op = make_op(input_rois, rois_probs, 1000);
EXPECT_EQ(op->get_output_size(), 1);
EXPECT_EQ(op->get_output_element_type(0), element::f64);
EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({1000, 4}));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, interval_num_of_rois_and_dynamic_probabilities) {
auto input_rois_shape = PartialShape{{20, 30}, 4};
auto rois_prop_shape = PartialShape{-1};
set_shape_labels(input_rois_shape, 10);
set_shape_labels(input_rois_shape, 20);
const auto input_rois = std::make_shared<Parameter>(element::dynamic, input_rois_shape);
const auto rois_probs = std::make_shared<Parameter>(element::dynamic, rois_prop_shape);
const auto op = make_op(input_rois, rois_probs, 20);
EXPECT_EQ(op->get_output_element_type(0), element::dynamic);
EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({20, 4}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(ov::no_label, ov::no_label));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, interval_num_of_rois_and_probabilities) {
auto input_rois_shape = PartialShape{{20, 30}, 4};
auto rois_prop_shape = PartialShape{{10, 35}};
set_shape_labels(input_rois_shape, 10);
set_shape_labels(input_rois_shape, 20);
const auto input_rois = std::make_shared<Parameter>(element::dynamic, input_rois_shape);
const auto rois_probs = std::make_shared<Parameter>(element::dynamic, rois_prop_shape);
const auto op = make_op(input_rois, rois_probs, 20);
EXPECT_EQ(op->get_output_element_type(0), element::dynamic);
EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({20, 4}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(ov::no_label, ov::no_label));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, dynamic_num_rois_and_interval_probabilities) {
auto input_rois_shape = PartialShape{-1, 4};
auto rois_prop_shape = PartialShape{{10, 15}};
set_shape_labels(input_rois_shape, 10);
set_shape_labels(input_rois_shape, 20);
const auto input_rois = std::make_shared<Parameter>(element::dynamic, input_rois_shape);
const auto rois_probs = std::make_shared<Parameter>(element::dynamic, rois_prop_shape);
const auto op = make_op(input_rois, rois_probs, 200);
EXPECT_EQ(op->get_output_element_type(0), element::dynamic);
EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({200, 4}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(ov::no_label, ov::no_label));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, element_type_of_inputs_are_not_same) {
const auto input_rois = std::make_shared<Parameter>(element::f32, Shape{100, 4});
const auto rois_probs = std::make_shared<Parameter>(element::f64, Shape{100});
OV_EXPECT_THROW(auto op = make_op(input_rois, rois_probs, 200),
NodeValidationFailure,
HasSubstr("ROIs and probabilities of ROIs must same floating-point type"));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, element_type_of_inputs_rois_is_not_floating_point) {
const auto input_rois = std::make_shared<Parameter>(element::u32, Shape{100, 4});
const auto rois_probs = std::make_shared<Parameter>(element::dynamic, Shape{100});
OV_EXPECT_THROW(auto op = make_op(input_rois, rois_probs, 200),
NodeValidationFailure,
HasSubstr("ROIs and probabilities of ROIs must same floating-point type"));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, element_type_of_probabilities_is_not_floating_point) {
const auto input_rois = std::make_shared<Parameter>(element::dynamic, Shape{100, 4});
const auto rois_probs = std::make_shared<Parameter>(element::i64, Shape{100});
OV_EXPECT_THROW(auto op = make_op(input_rois, rois_probs, 200),
NodeValidationFailure,
HasSubstr("ROIs and probabilities of ROIs must same floating-point type"));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, input_rois_not_2d) {
const auto rois_probs = std::make_shared<Parameter>(element::f32, Shape{100});
OV_EXPECT_THROW(auto op = make_op(std::make_shared<Parameter>(element::f32, Shape{100}), rois_probs, 200),
NodeValidationFailure,
HasSubstr("The 'input_rois' input is expected to be a 2D. Got: 1"));
OV_EXPECT_THROW(auto op = make_op(std::make_shared<Parameter>(element::f32, Shape{100, 4, 2}), rois_probs, 200),
NodeValidationFailure,
HasSubstr("The 'input_rois' input is expected to be a 2D. Got: 3"));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, input_rois_second_dim_is_not_4) {
const auto rois_probs = std::make_shared<Parameter>(element::f32, Shape{100});
OV_EXPECT_THROW(auto op = make_op(std::make_shared<Parameter>(element::f32, PartialShape{100, 3}), rois_probs, 200),
NodeValidationFailure,
HasSubstr("The second dimension of 'input_rois' should be 4. Got: 3"));
OV_EXPECT_THROW(auto op = make_op(std::make_shared<Parameter>(element::f32, PartialShape{100, 5}), rois_probs, 200),
NodeValidationFailure,
HasSubstr("The second dimension of 'input_rois' should be 4. Got: 5"));
OV_EXPECT_THROW(
auto op = make_op(std::make_shared<Parameter>(element::f32, PartialShape{100, {5, 6}}), rois_probs, 200),
NodeValidationFailure,
HasSubstr("The second dimension of 'input_rois' should be 4. Got: 5..6"));
OV_EXPECT_THROW(
auto op = make_op(std::make_shared<Parameter>(element::f32, PartialShape{100, {1, 3}}), rois_probs, 200),
NodeValidationFailure,
HasSubstr("The second dimension of 'input_rois' should be 4. Got: 1..3"));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, rois_probabilities_not_1d) {
const auto input_rois = std::make_shared<Parameter>(element::f32, Shape{200, 4});
OV_EXPECT_THROW(auto op = make_op(input_rois, std::make_shared<Parameter>(element::f32, Shape{}), 200),
NodeValidationFailure,
HasSubstr("The 'rois_probs' input is expected to be a 1D. Got: 0"));
OV_EXPECT_THROW(auto op = make_op(input_rois, std::make_shared<Parameter>(element::f32, Shape{200, 2}), 200),
NodeValidationFailure,
HasSubstr("The 'rois_probs' input is expected to be a 1D. Got: 2"));
}
TEST_F(TypePropExperimentalDetectronTopKROIsV6, number_of_rois_not_compatible_with_rois_probabilities) {
OV_EXPECT_THROW(auto op = make_op(std::make_shared<Parameter>(element::f32, PartialShape{200, 4}),
std::make_shared<Parameter>(element::f32, PartialShape{100}),
10),
NodeValidationFailure,
HasSubstr("Number of rois and number of probabilities should be equal."));
OV_EXPECT_THROW(auto op = make_op(std::make_shared<Parameter>(element::f32, PartialShape{{10, 20}, 4}),
std::make_shared<Parameter>(element::f32, PartialShape{{21, 25}}),
10),
NodeValidationFailure,
HasSubstr("Number of rois and number of probabilities should be equal."));
OV_EXPECT_THROW(auto op = make_op(std::make_shared<Parameter>(element::f32, PartialShape{{10, 20}, 4}),
std::make_shared<Parameter>(element::f32, PartialShape{5}),
10),
NodeValidationFailure,
HasSubstr("Number of rois and number of probabilities should be equal."));
}

View File

@@ -1,33 +0,0 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include <experimental_detectron_topkrois_shape_inference.hpp>
#include <openvino/core/coordinate_diff.hpp>
#include <openvino/op/ops.hpp>
#include <openvino/op/parameter.hpp>
#include "utils/shape_inference/static_shape.hpp"
using namespace ov;
using namespace ov::intel_cpu;
TEST(StaticShapeInferenceTest, ExperimentalDetectronTopKROIsTest) {
auto input_rois = std::make_shared<ov::op::v0::Parameter>(element::f32, PartialShape{-1, -1});
auto rois_probs = std::make_shared<ov::op::v0::Parameter>(element::f32, PartialShape{-1});
size_t max_rois = 5;
auto rois = std::make_shared<op::v6::ExperimentalDetectronTopKROIs>(input_rois, rois_probs, max_rois);
std::vector<PartialShape> input_shapes = {PartialShape{10, 4}, PartialShape{10}}, output_shapes = {PartialShape{}};
shape_infer(rois.get(), input_shapes, output_shapes);
ASSERT_EQ(output_shapes[0], PartialShape({5, 4}));
std::vector<StaticShape> static_input_shapes = {StaticShape{10, 4}, StaticShape{10}}, static_output_shapes = {StaticShape{}};
shape_infer(rois.get(), static_input_shapes, static_output_shapes);
ASSERT_EQ(static_output_shapes[0], StaticShape({5, 4}));
}

View File

@@ -0,0 +1,92 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include "common_test_utils/test_assertions.hpp"
#include "experimental_detectron_topkrois_shape_inference.hpp"
#include "utils.hpp"
using namespace ov;
using namespace ov::intel_cpu;
using namespace testing;
class ExperimentalDetectronTopKROIsV6StaticShapeInferenceTest
: public OpStaticShapeInferenceTest<op::v6::ExperimentalDetectronTopKROIs> {
protected:
void SetUp() override {
output_shapes.resize(1);
}
};
TEST_F(ExperimentalDetectronTopKROIsV6StaticShapeInferenceTest, default_ctor) {
op = make_op();
op->set_max_rois(100);
input_shapes = ShapeVector{{12, 4}, {12}};
shape_inference(op.get(), input_shapes, output_shapes);
EXPECT_EQ(output_shapes.size(), 1);
EXPECT_EQ(output_shapes.front(), StaticShape({100, 4}));
}
TEST_F(ExperimentalDetectronTopKROIsV6StaticShapeInferenceTest, inputs_dynamic_rank) {
const auto input_rois = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
const auto rois_probs = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
op = make_op(input_rois, rois_probs, 5);
input_shapes = ShapeVector{{10, 4}, {10}};
shape_inference(op.get(), input_shapes, output_shapes);
EXPECT_EQ(output_shapes.size(), 1);
EXPECT_EQ(output_shapes.front(), StaticShape({5, 4}));
}
TEST_F(ExperimentalDetectronTopKROIsV6StaticShapeInferenceTest, inputs_static_rank) {
const auto input_rois = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(2));
const auto rois_probs = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(1));
op = make_op(input_rois, rois_probs, 15);
input_shapes = ShapeVector{{100, 4}, {100}};
shape_inference(op.get(), input_shapes, output_shapes);
EXPECT_EQ(output_shapes.size(), 1);
EXPECT_EQ(output_shapes.front(), StaticShape({15, 4}));
}
TEST_F(ExperimentalDetectronTopKROIsV6StaticShapeInferenceTest, input_rois_not_2d) {
const auto input_rois = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
const auto rois_probs = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(1));
op = make_op(input_rois, rois_probs, 5);
input_shapes = ShapeVector{{10, 4, 10}, {10}};
OV_EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes),
NodeValidationFailure,
HasSubstr("The 'input_rois' input is expected to be a 2D."));
}
TEST_F(ExperimentalDetectronTopKROIsV6StaticShapeInferenceTest, rois_prob_not_1d) {
const auto input_rois = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(2));
const auto rois_probs = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
op = make_op(input_rois, rois_probs, 5);
input_shapes = ShapeVector{{10, 4}, {10, 2}};
OV_EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes),
NodeValidationFailure,
HasSubstr("The 'rois_probs' input is expected to be a 1D."));
}
TEST_F(ExperimentalDetectronTopKROIsV6StaticShapeInferenceTest, input_rois_second_dim_is_not_4) {
const auto input_rois = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
const auto rois_probs = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(1));
op = make_op(input_rois, rois_probs, 5);
input_shapes = ShapeVector{{10, 5}, {10}};
OV_EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes),
NodeValidationFailure,
HasSubstr("The second dimension of 'input_rois' should be 4."));
}