[ DTS ] Reduces (#940)

This commit is contained in:
Evgenya Stepyreva
2020-06-18 11:36:07 +03:00
committed by GitHub
parent 88cccee0b7
commit bb44f17a06
6 changed files with 377 additions and 2 deletions

View File

@@ -0,0 +1,15 @@
// Copyright (C) 2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include "ngraph/node.hpp"
#include <memory>
namespace vpu {
void dynamicToStaticShapeReduce(std::shared_ptr<ngraph::Node> node);
} // namespace vpu

View File

@@ -5,6 +5,7 @@
#include "vpu/ngraph/transformations/dynamic_to_static_shape_broadcast.hpp"
#include "vpu/ngraph/transformations/dynamic_to_static_shape_concat.hpp"
#include "vpu/ngraph/transformations/dynamic_to_static_shape_unary_elementwise.hpp"
#include "vpu/ngraph/transformations/dynamic_to_static_shape_reduce.hpp"
#include "vpu/ngraph/transformations/dynamic_to_static_shape_roialign.hpp"
#include "vpu/ngraph/transformations/dynamic_to_static_shape_topk.hpp"
#include "vpu/ngraph/transformations/dynamic_to_static_shape_transpose.hpp"
@@ -86,6 +87,15 @@ const Transformations& getDefaultTransformations() {
{ngraph::opset3::ROIAlign::type_info, dynamicToStaticShapeROIAlign},
{ngraph::opset3::Reshape::type_info, dynamicToStaticShapeReshape},
{ngraph::opset3::Broadcast::type_info, dynamicToStaticShapeBroadcast},
// reduction
{ngraph::opset3::ReduceLogicalAnd::type_info, dynamicToStaticShapeReduce},
{ngraph::opset3::ReduceLogicalOr::type_info, dynamicToStaticShapeReduce},
{ngraph::opset3::ReduceMax::type_info, dynamicToStaticShapeReduce},
{ngraph::opset3::ReduceMean::type_info, dynamicToStaticShapeReduce},
{ngraph::opset3::ReduceMin::type_info, dynamicToStaticShapeReduce},
{ngraph::opset3::ReduceProd::type_info, dynamicToStaticShapeReduce},
{ngraph::opset3::ReduceSum::type_info, dynamicToStaticShapeReduce},
};
return transformations;
}

View File

@@ -0,0 +1,71 @@
// Copyright (C) 2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "vpu/ngraph/transformations/dynamic_to_static_shape_reduce.hpp"
#include "vpu/ngraph/operations/dynamic_shape_resolver.hpp"
#include <vpu/utils/error.hpp>
#include "ngraph/graph_util.hpp"
#include "ngraph/opsets/opset3.hpp"
#include <memory>
#include <numeric>
#include <ngraph/validation_util.hpp>
namespace vpu {
void dynamicToStaticShapeReduce(std::shared_ptr<ngraph::Node> target) {
const auto dsr = ngraph::as_type_ptr<ngraph::vpu::op::DynamicShapeResolver>(target->input_value(0).get_node_shared_ptr());
VPU_THROW_UNLESS(dsr, "DynamicToStaticShape transformation for {} of type {} expects {} as input with index {}",
target->get_friendly_name(), target->get_type_info(), ngraph::vpu::op::DynamicShapeResolver::type_info, 0);
VPU_THROW_UNLESS(std::dynamic_pointer_cast<ngraph::op::util::ArithmeticReductionKeepDims>(target) ||
std::dynamic_pointer_cast<ngraph::op::util::LogicalReductionKeepDims>(target),
"dynamicToStaticShapeReduce transformation expects arithmetic or logical reduction, but it got {} node of type {}",
target->get_friendly_name(), target->get_type_info());
const auto axes_const_node = ngraph::as_type_ptr<ngraph::opset3::Constant>(target->get_argument(1));
VPU_THROW_UNLESS(axes_const_node,
"dynamicToStaticShapeReduce transformation for {} of type {} expects {} as input with index {}, but it has {} node of type {} instead",
target->get_friendly_name(), target->get_type_info(), ngraph::opset3::Constant::type_info, 1,
target->get_argument(1)->get_friendly_name(), target->get_argument(1)->get_type_info());
const auto axes = axes_const_node->cast_vector<int64_t>();
const auto data_rank = target->get_input_partial_shape(0).rank();
VPU_THROW_UNLESS(data_rank.is_static(), "dynamicToStaticShapeReduce transformation for {} doesn't support dynamic rank", target);
const auto data_rank_value = data_rank.get_length();
bool keep_dims = false;
if (const auto arithmetic_reduce = std::dynamic_pointer_cast<ngraph::op::util::ArithmeticReductionKeepDims>(target)) {
keep_dims = arithmetic_reduce->get_keep_dims();
} else if (const auto logical_reduce = std::dynamic_pointer_cast<ngraph::op::util::LogicalReductionKeepDims>(target)) {
keep_dims = logical_reduce->get_keep_dims();
} // assertion earlier excluded other variants
const auto data_shape = dsr->input_value(1);
ngraph::Output<ngraph::Node> output_shape;
if (keep_dims) {
output_shape = std::make_shared<ngraph::opset3::ScatterElementsUpdate>(
data_shape,
ngraph::opset3::Constant::create(ngraph::element::i64, {axes.size()}, axes),
ngraph::opset3::Constant::create(ngraph::element::i64, {axes.size()}, std::vector<int64_t>(axes.size(), 1)),
ngraph::opset3::Constant::create(ngraph::element::i64, {1}, {0}));
} else {
std::vector<int64_t> range(data_rank_value);
std::iota(range.begin(), range.end(), 0);
std::vector<int64_t> indices;
std::copy_if(range.cbegin(), range.cend(), std::back_inserter(indices),
[&axes](int64_t i) { return std::find(axes.cbegin(), axes.cend(), i) == axes.cend(); });
output_shape = std::make_shared<ngraph::opset3::Gather>(
data_shape,
ngraph::opset3::Constant::create(ngraph::element::i64, {indices.size()}, indices),
ngraph::opset3::Constant::create(ngraph::element::i64, {1}, {0}));
}
const auto copied = target->clone_with_new_inputs(target->input_values());
ngraph::replace_node(target, std::make_shared<ngraph::vpu::op::DynamicShapeResolver>(copied, output_shape));
}
} // namespace vpu

View File

@@ -0,0 +1,166 @@
// Copyright (C) 2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <common_test_utils/test_common.hpp>
#include <ngraph/shape.hpp>
#include <ngraph/type/element_type.hpp>
#include <ngraph/op/parameter.hpp>
#include <vpu/ngraph/operations/dynamic_shape_resolver.hpp>
#include <numeric>
#include <random>
#include <ngraph/opsets/opset3.hpp>
#include <vpu/ngraph/transformations/dynamic_to_static_shape_reduce.hpp>
#include <queue>
#include <ngraph_functions/utils/ngraph_helpers.hpp>
#include <vpu/ngraph/transformations/dynamic_to_static_shape.hpp>
#include <vpu/utils/error.hpp>
namespace {
using DataType = ngraph::element::Type_t;
using DataDims = ngraph::Shape;
struct ReduceTestCase {
ngraph::Shape data_shape;
std::vector<int64_t> axes;
bool keep_dims;
std::vector<int64_t> gather_indices;
};
const auto arithmetic_combinations = testing::Combine(
testing::Values(
ngraph::opset3::ReduceMax::type_info,
ngraph::opset3::ReduceMean::type_info,
ngraph::opset3::ReduceMin::type_info,
ngraph::opset3::ReduceProd::type_info,
ngraph::opset3::ReduceSum::type_info),
testing::Values(
ngraph::element::f16,
ngraph::element::f32,
ngraph::element::i32,
ngraph::element::i64,
ngraph::element::u8),
testing::Values(
ngraph::element::i32,
ngraph::element::i64,
ngraph::element::u8),
testing::Values(
// data_shape, axes, keep_dims, gather_indices, axes_shape
ReduceTestCase{{1, 3, 224, 224}, {2, 3}, true, {1, 1}},
ReduceTestCase{{1, 3, 224, 224}, {2, 3}, false, {0, 1}},
ReduceTestCase{{1, 3, 224, 224}, {0, 1, 2, 3}, true, {1, 1, 1, 1}},
ReduceTestCase{{1, 3, 224, 224}, {1, 3}, false, {0, 2}},
ReduceTestCase{{4}, {0}, true, {1}}));
const auto logical_combinations = testing::Combine(
testing::Values(
ngraph::opset3::ReduceLogicalAnd::type_info,
ngraph::opset3::ReduceLogicalOr::type_info),
testing::Values(ngraph::element::boolean),
testing::Values(
ngraph::element::i32,
ngraph::element::i64,
ngraph::element::u8),
testing::Values(
// data_shape, axes, keep_dims, gather_indices
ReduceTestCase{{1, 3, 224, 224}, {2, 3}, true, {1, 1}},
ReduceTestCase{{1, 3, 224, 224}, {2, 3}, false, {0, 1}},
ReduceTestCase{{1, 3, 224, 224}, {0, 1, 2, 3}, true, {1, 1, 1, 1}},
ReduceTestCase{{1, 3, 224, 224}, {1, 3}, false, {0, 2}},
ReduceTestCase{{4}, {0}, true, {1}}));
class DynamicToStaticShapeReduce: public CommonTestUtils::TestsCommon,
public testing::WithParamInterface<std::tuple<ngraph::NodeTypeInfo, DataType, DataType, ReduceTestCase>> {
public:
void SetUp() override {
const auto& parameters = GetParam();
const auto& reduce_type = std::get<0>(parameters);
const auto& data_type = std::get<1>(parameters);
const auto& axes_type = std::get<2>(parameters);
const auto& reduce_setup = std::get<3>(parameters);
ngraph::helpers::CompareFunctions(*transform(reduce_type, data_type, axes_type, reduce_setup),
*reference(reduce_type, data_type, axes_type, reduce_setup));
}
protected:
std::shared_ptr<const ngraph::Function> transform(
const ngraph::NodeTypeInfo type_info,
const ngraph::element::Type_t& data_type,
const ngraph::element::Type_t& axes_type,
const ReduceTestCase& reduce_setup) const {
const auto data = std::make_shared<ngraph::opset3::Parameter>(data_type, reduce_setup.data_shape);
const auto axes = ngraph::opset3::Constant::create(axes_type, {reduce_setup.axes.size()}, reduce_setup.axes);
const auto dims = std::make_shared<ngraph::opset3::Parameter>(ngraph::element::i64, ngraph::Shape{reduce_setup.data_shape.size()});
const auto dsr = std::make_shared<ngraph::vpu::op::DynamicShapeResolver>(data, dims);
const auto node = ngraph::helpers::getNodeSharedPtr(type_info, {dsr, axes});
if (auto arithmetic_reduce = std::dynamic_pointer_cast<ngraph::op::util::ArithmeticReductionKeepDims>(node))
arithmetic_reduce->set_keep_dims(reduce_setup.keep_dims);
else if (auto logical_reduce = std::dynamic_pointer_cast<ngraph::op::util::LogicalReductionKeepDims>(node))
logical_reduce->set_keep_dims(reduce_setup.keep_dims);
node->validate_and_infer_types();
auto outputShape = node->get_output_partial_shape(0);
const auto function = std::make_shared<ngraph::Function>(
ngraph::NodeVector{node},
ngraph::ParameterVector{data, dims},
"Actual");
node->set_output_type(0, data_type, ngraph::PartialShape::dynamic(node->get_output_partial_shape(0).rank()));
const auto transformations = vpu::Transformations{{type_info, vpu::dynamicToStaticShapeReduce}};
vpu::DynamicToStaticShape(transformations).transform(function);
return function;
}
std::shared_ptr<const ngraph::Function> reference(
const ngraph::NodeTypeInfo type_info,
const ngraph::element::Type_t& data_type,
const ngraph::element::Type_t& axes_type,
const ReduceTestCase& reduce_setup) const {
const auto data = std::make_shared<ngraph::opset3::Parameter>(data_type, reduce_setup.data_shape);
const auto axes = ngraph::opset3::Constant::create(axes_type, {reduce_setup.axes.size()}, reduce_setup.axes);
const auto dims = std::make_shared<ngraph::opset3::Parameter>(ngraph::element::i64, ngraph::Shape{reduce_setup.data_shape.size()});
const auto dsr = std::make_shared<ngraph::vpu::op::DynamicShapeResolver>(data, dims);
const auto node = ngraph::helpers::getNodeSharedPtr(type_info, {dsr, axes});
if (auto arithmetic_reduce = std::dynamic_pointer_cast<ngraph::op::util::ArithmeticReductionKeepDims>(node))
arithmetic_reduce->set_keep_dims(reduce_setup.keep_dims);
else if (auto logical_reduce = std::dynamic_pointer_cast<ngraph::op::util::LogicalReductionKeepDims>(node))
logical_reduce->set_keep_dims(reduce_setup.keep_dims);
node->validate_and_infer_types();
const auto data_rank_value = reduce_setup.data_shape.size();
ngraph::Output<ngraph::Node> output_shape;
if (reduce_setup.keep_dims) {
output_shape = std::make_shared<ngraph::opset3::ScatterElementsUpdate>(
dims,
ngraph::opset3::Constant::create(ngraph::element::i64, {reduce_setup.axes.size()}, reduce_setup.axes),
ngraph::opset3::Constant::create(ngraph::element::i64, {reduce_setup.gather_indices.size()}, reduce_setup.gather_indices),
ngraph::opset3::Constant::create(ngraph::element::i64, {1}, {0}));
} else {
output_shape = std::make_shared<ngraph::opset3::Gather>(
dims,
ngraph::opset3::Constant::create(ngraph::element::i64, {reduce_setup.gather_indices.size()}, reduce_setup.gather_indices),
ngraph::opset3::Constant::create(ngraph::element::i64, {1}, {0}));
}
const auto dsr1 = std::make_shared<ngraph::vpu::op::DynamicShapeResolver>(node, output_shape);
return std::make_shared<ngraph::Function>(
ngraph::NodeVector{dsr1},
ngraph::ParameterVector{data, dims},
"Expected");
}
};
TEST_P(DynamicToStaticShapeReduce, CompareFunctions) {
}
INSTANTIATE_TEST_CASE_P(Arithmetic, DynamicToStaticShapeReduce, arithmetic_combinations);
INSTANTIATE_TEST_CASE_P(Logical, DynamicToStaticShapeReduce, logical_combinations);
} // namespace

View File

@@ -0,0 +1,110 @@
// Copyright (C) 2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <functional_test_utils/layer_test_utils.hpp>
#include <ngraph_functions/builders.hpp>
#include <vpu/ngraph/operations/dynamic_shape_resolver.hpp>
namespace {
using DataType = ngraph::element::Type_t;
using DataDims = ngraph::Shape;
struct ReduceTestCase {
ngraph::Shape data_shape;
std::vector<int64_t> axes;
bool keep_dims;
};
const auto arithmetic_combinations = testing::Combine(
testing::Values(
ngraph::opset3::ReduceMax::type_info,
ngraph::opset3::ReduceMean::type_info,
ngraph::opset3::ReduceMin::type_info,
ngraph::opset3::ReduceProd::type_info,
ngraph::opset3::ReduceSum::type_info),
testing::Values(
ngraph::element::f16,
ngraph::element::f32,
ngraph::element::i32,
ngraph::element::i64,
ngraph::element::u8),
testing::Values(
ngraph::element::i32,
ngraph::element::i64,
ngraph::element::u8),
testing::Values(
// data_shape, axes, keep_dims
ReduceTestCase{{1, 3, 224, 224}, {2, 3}, true},
ReduceTestCase{{1, 3, 224, 224}, {2, 3}, false},
ReduceTestCase{{1, 3, 224, 224}, {0, 1, 2, 3}, true},
ReduceTestCase{{1, 3, 224, 224}, {1, 3}, false},
ReduceTestCase{{4}, {0}, true}),
testing::Values(CommonTestUtils::DEVICE_MYRIAD));
const auto logical_combinations = testing::Combine(
testing::Values(
ngraph::opset3::ReduceLogicalAnd::type_info,
ngraph::opset3::ReduceLogicalOr::type_info),
testing::Values(ngraph::element::boolean),
testing::Values(
ngraph::element::i32,
ngraph::element::i64,
ngraph::element::u8),
testing::Values(
// data_shape, axes, keep_dims
ReduceTestCase{{1, 3, 224, 224}, {2, 3}, true},
ReduceTestCase{{1, 3, 224, 224}, {2, 3}, false},
ReduceTestCase{{1, 3, 224, 224}, {0, 1, 2, 3}, true},
ReduceTestCase{{1, 3, 224, 224}, {1, 3}, false},
ReduceTestCase{{4}, {0}, true}),
testing::Values(CommonTestUtils::DEVICE_MYRIAD));
using Parameters = std::tuple<
ngraph::NodeTypeInfo,
DataType,
DataType,
ReduceTestCase,
LayerTestsUtils::TargetDevice
>;
class DSR_Reduce : public testing::WithParamInterface<Parameters>,
public LayerTestsUtils::LayerTestsCommon {
protected:
void SetUp() override {
const auto& parameters = GetParam();
const auto& reduce_type = std::get<0>(parameters);
const auto& data_type = std::get<1>(parameters);
const auto& axes_type = std::get<2>(parameters);
const auto& reduce_setup = std::get<3>(parameters);
targetDevice = std::get<4>(parameters);
const auto data = std::make_shared<ngraph::opset3::Parameter>(data_type, reduce_setup.data_shape);
const auto axes = ngraph::opset3::Constant::create(axes_type, {reduce_setup.axes.size()}, reduce_setup.axes);
const auto dims = std::make_shared<ngraph::opset3::Parameter>(ngraph::element::i64, ngraph::Shape{reduce_setup.data_shape.size()});
const auto dsr = std::make_shared<ngraph::vpu::op::DynamicShapeResolver>(data, dims);
const auto node = ngraph::helpers::getNodeSharedPtr(reduce_type, {dsr, axes});
if (auto arithmetic_reduce = std::dynamic_pointer_cast<ngraph::op::util::ArithmeticReductionKeepDims>(node))
arithmetic_reduce->set_keep_dims(reduce_setup.keep_dims);
else if (auto logical_reduce = std::dynamic_pointer_cast<ngraph::op::util::LogicalReductionKeepDims>(node))
logical_reduce->set_keep_dims(reduce_setup.keep_dims);
node->validate_and_infer_types();
const auto result = std::make_shared<ngraph::opset3::Result>(node);
function = std::make_shared<ngraph::Function>(ngraph::ResultVector{result},
ngraph::ParameterVector{data, dims}, "DSR-Reduce");
}
};
TEST_P(DSR_Reduce, CompareWithReference) {
Run();
}
INSTANTIATE_TEST_CASE_P(DISABLED_DynamicArithmeticReduce, DSR_Reduce, arithmetic_combinations);
INSTANTIATE_TEST_CASE_P(DISABLED_DynamicLogicalReduce, DSR_Reduce, logical_combinations);
} // namespace

View File

@@ -849,8 +849,11 @@ int64_t ngraph::normalize_axis(const std::string& node_description,
}
const auto tensor_rank_value = tensor_rank.get_length();
return normalize_axis(
node_description, axis, tensor_rank_value, -tensor_rank_value, tensor_rank_value - 1);
return normalize_axis(node_description,
axis,
tensor_rank_value,
-tensor_rank_value,
tensor_rank_value ? (tensor_rank_value - 1) : 0);
}
int64_t ngraph::normalize_axis(const Node* node,