[LPT][CommonOptimizations] ShuffleChannelsTransformation and ShuffleChannelsFusion (#4916)

* [nGraph] op::v0::ShuffleChannels: added type_info macros

* [LPT] ShuffleChannelsTransformation

* [CPU] ShuffleChannels decomposition disabled

* [LPT][TESTS] ShuffleChannelsTransformation functional tests

* [LPT][TESTS] ShuffleChannelsTransformation plugin tests

* [CommonOptimizations] Added ShuffleChannelsFusion transformation

* [CommonOptimizations][TESTS] ShuffleChannelsFusion tests

* refactoring and adding comments

* [CommonOptimizations] ShuffleChannelsFusion refactoring and fixes

* [CommonOptimizations] ShuffleChannelsFusion: removed unnecessary check

* [CommonOptimizations] transformation refactored and test-cases with dynamic shape added
This commit is contained in:
Vladislav Golubev 2021-04-23 13:26:53 +03:00 committed by GitHub
parent 576e692b1d
commit 05c23dfd94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1132 additions and 6 deletions

View File

@ -342,7 +342,7 @@ protected:
void addPattern(ngraph::pass::GraphRewrite& pass, TransformationContext& context, std::shared_ptr<Node> patternRoot) const;
//TODO: replace with canBeTransformed when quantization by special dimension is supported for all transformations
bool canBeTransformedSpecialDimension(const TransformationContext& context, std::shared_ptr<Node> layer) const;
bool canBeTransformedSpatialDimension(const TransformationContext& context, std::shared_ptr<Node> layer) const;
template <typename Operation>
void addSingleNodePattern(ngraph::pass::GraphRewrite& pass, TransformationContext& context) const {

View File

@ -0,0 +1,25 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <ngraph/ngraph.hpp>
#include "low_precision/layer_transformation.hpp"
namespace ngraph {
namespace pass {
namespace low_precision {
class TRANSFORMATIONS_API ShuffleChannelsTransformation : public LayerTransformation {
public:
ShuffleChannelsTransformation(const Params& params);
void registerMatcherIn(GraphRewrite& pass, TransformationContext& context) const override;
bool transform(TransformationContext& context, ngraph::pattern::Matcher& m) const override;
bool isPrecisionPreserved(std::shared_ptr<Node> layer) const noexcept override;
bool canBeTransformed(const TransformationContext& context, std::shared_ptr<Node> op) const override;
};
} // namespace low_precision
} // namespace pass
} // namespace ngraph

View File

@ -115,7 +115,7 @@ bool LayerTransformation::canBeTransformed(const TransformationContext& context,
return true;
}
bool LayerTransformation::canBeTransformedSpecialDimension(const TransformationContext& context, std::shared_ptr<Node> layer) const {
bool LayerTransformation::canBeTransformedSpatialDimension(const TransformationContext& context, std::shared_ptr<Node> layer) const {
if (!isQuantized(layer)) {
return false;
}

View File

@ -163,7 +163,7 @@ bool MatMulTransformation::isPrecisionPreserved(std::shared_ptr<Node> layer) con
}
bool MatMulTransformation::canBeTransformed(const TransformationContext& context, std::shared_ptr<Node> layer) const {
if (!LayerTransformation::canBeTransformedSpecialDimension(context, layer)) {
if (!LayerTransformation::canBeTransformedSpatialDimension(context, layer)) {
return false;
}

View File

@ -0,0 +1,93 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "low_precision/shuffle_channels.hpp"
#include <memory>
#include <ngraph/ngraph.hpp>
#include <ngraph/opsets/opset1.hpp>
#include "low_precision/network_helper.hpp"
namespace ngraph {
namespace pass {
namespace low_precision {
ShuffleChannelsTransformation::ShuffleChannelsTransformation(const Params& params) : LayerTransformation(params) {}
void ShuffleChannelsTransformation::registerMatcherIn(GraphRewrite& pass, TransformationContext& context) const {
addPattern(
pass,
context,
make_op_pattern<opset1::ShuffleChannels>({ make_op_label<opset1::Multiply>() }));
}
bool ShuffleChannelsTransformation::transform(TransformationContext& context, ngraph::pattern::Matcher& m) const {
if (!canBeTransformed(context, m.get_match_root())) {
return false;
}
const auto shuffleChannels = as_type_ptr<opset1::ShuffleChannels>(NetworkHelper::separateInStandaloneBranch(m.get_match_root()));
auto dequantization = NetworkHelper::getDequantization(shuffleChannels);
const auto shuffleDequantizationConstant = [&](const std::shared_ptr<Node>& eltwise) {
const auto normalizedConst = NetworkHelper::normalizeDequantizationShape(eltwise);
const auto constShape = normalizedConst->get_shape();
if (shape_size(constShape) == 1ul) {
return NetworkHelper::toScalar(normalizedConst);
} else {
const size_t normalizedAxis = ngraph::normalize_axis(
shuffleChannels->get_friendly_name(),
shuffleChannels->get_axis(),
shuffleChannels->get_input_partial_shape(0).rank());
if (constShape[normalizedAxis] == 1ul) {
return normalizedConst;
} else {
const auto group = shuffleChannels->get_group();
const auto shuffledConst = fold<ngraph::opset1::ShuffleChannels>(normalizedConst, normalizedAxis, group);
return as_type_ptr<opset1::Constant>(shuffledConst);
}
}
};
if (dequantization.subtract) {
const auto shuffledSubConst = shuffleDequantizationConstant(dequantization.subtract);
replace_node(dequantization.subtractConstant, shuffledSubConst);
dequantization.subtractConstant = shuffledSubConst;
}
const auto shuffledMulConst = shuffleDequantizationConstant(dequantization.multiply);
replace_node(dequantization.multiplyConstant, shuffledMulConst);
dequantization.multiplyConstant = shuffledMulConst;
moveDequantizationAfter(context, shuffleChannels, dequantization, false);
return true;
}
bool ShuffleChannelsTransformation::canBeTransformed(const TransformationContext& context, std::shared_ptr<Node> op) const {
if (!LayerTransformation::canBeTransformedSpatialDimension(context, op)) {
return false;
}
const auto shuffleChannels = as_type_ptr<opset1::ShuffleChannels>(op);
if (shuffleChannels == nullptr) {
return false;
}
const FakeQuantizeDequantization dequantization = NetworkHelper::getDequantization(shuffleChannels);
if (dequantization.empty()) {
return false;
}
return true;
}
bool ShuffleChannelsTransformation::isPrecisionPreserved(std::shared_ptr<Node> layer) const noexcept {
return true;
}
} // namespace low_precision
} // namespace pass
} // namespace ngraph

View File

@ -45,6 +45,7 @@
#include "low_precision/prelu.hpp"
#include "low_precision/reshape.hpp"
#include "low_precision/relu.hpp"
#include "low_precision/shuffle_channels.hpp"
#include "low_precision/squeeze.hpp"
#include "low_precision/subtract.hpp"
#include "low_precision/split.hpp"
@ -228,6 +229,7 @@ LowPrecisionTransformations LowPrecisionTransformer::getAllTransformations(const
add<PReluTransformation, opset1::PRelu>(params).
add<ReluTransformation, opset1::Relu>(params).
add<ReshapeTransformation, opset1::Reshape>(params).
add<ShuffleChannelsTransformation, opset1::ShuffleChannels>(params).
add<SqueezeTransformation, opset1::Squeeze>(params).
add<SplitTransformation, opset1::Split>(params).
add<StridedSliceTransformation, opset1::StridedSlice>(params).

View File

@ -38,6 +38,7 @@
#include <transformations/common_optimizations/depth_to_space_fusion.hpp>
#include <transformations/common_optimizations/softmax_fusion.hpp>
#include <transformations/op_conversions/convert_depth_to_space.hpp>
#include <transformations/op_conversions/convert_shuffle_channels3.hpp>
#include <transformations/op_conversions/convert_space_to_depth.hpp>
#include <transformations/op_conversions/convert_gelu.hpp>
#include <transformations/op_conversions/gelu7_downgrade.hpp>
@ -269,6 +270,7 @@ static void Transformation(CNNNetwork& clonedNetwork, const Config& conf) {
// List of enabled/disabled transformations
pass_config->disable<ngraph::pass::ConvertGELU>();
pass_config->disable<ngraph::pass::ConvertShuffleChannels3>();
pass_config->disable<ngraph::pass::Gelu7Downgrade>();
pass_config->disable<ngraph::pass::HSwishDecomposition>();
pass_config->disable<ngraph::pass::ReduceL1Decomposition>();

View File

@ -0,0 +1,40 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <vector>
#include <memory>
#include <transformations_visibility.hpp>
#include <ngraph/pass/graph_rewrite.hpp>
namespace ngraph {
namespace pass {
class TRANSFORMATIONS_API ShuffleChannelsFusion;
} // namespace pass
} // namespace ngraph
/**
* @ingroup ie_transformation_common_api
* @brief ShuffleChannelsFusion transformation detects Reshape-Transpose-Reshape pattern
* and tries to fuse it into a single ShuffleChannels layer with axis = 1.
*
* x' = reshape(x, [N, group, C / group, H, W]) or reshape(x, [N, group, C / group, H * W])
* x'' = transpose(x', [0, 2, 1, 3, 4]) or transpose(x', [0, 2, 1, 3])
* y = reshape(x'', [N, C, H, W])
*
* @param reshape_constants_check the flag that defines the need for additional checks of reshapes constant
* Additional checks are required when ShuffleChannelsFusion using inside offline transformations
* and are not necessary when ShuffleChannelsFusion using inside CommonOptimizations
*/
class ngraph::pass::ShuffleChannelsFusion : public ngraph::pass::MatcherPass {
public:
NGRAPH_RTTI_DECLARATION;
ShuffleChannelsFusion(const bool reshape_constants_check);
};

View File

@ -32,6 +32,7 @@
#include "transformations/common_optimizations/clamp_fusion.hpp"
#include "transformations/common_optimizations/pad_fusion.hpp"
#include "transformations/common_optimizations/eliminate_unsqueeze_gather.hpp"
#include "transformations/common_optimizations/shuffle_channels_fusion.hpp"
#include "transformations/common_optimizations/softmax_fusion.hpp"
#include "transformations/common_optimizations/mvn_fusion.hpp"
#include "transformations/common_optimizations/binarize_weights.hpp"
@ -104,6 +105,7 @@ bool ngraph::pass::CommonOptimizations::run_on_function(std::shared_ptr<ngraph::
common_fusions->add_matcher<ngraph::pass::SoftPlusFusion>();
common_fusions->add_matcher<ngraph::pass::SoftPlusToMishFusion>();
common_fusions->add_matcher<ngraph::pass::SwishFusion>();
common_fusions->add_matcher<ngraph::pass::ShuffleChannelsFusion>(false);
common_fusions->add_matcher<ngraph::pass::HSwishFusion>();
common_fusions->add_matcher<ngraph::pass::HSigmoidFusion>();
common_fusions->add_matcher<ngraph::pass::NormalizeL2Fusion>();

View File

@ -0,0 +1,121 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "transformations/common_optimizations/shuffle_channels_fusion.hpp"
#include "itt.hpp"
#include <memory>
#include <vector>
#include <ngraph/opsets/opset6.hpp>
#include <ngraph/pattern/op/wrap_type.hpp>
#include <ngraph/rt_info.hpp>
bool check_shapes(const ngraph::Shape& shape_input, const ngraph::Shape& shape_reshape_before,
const ngraph::AxisVector& transpose_constant_values, const ngraph::Shape& shape_reshape_after) {
// x: [N, C, H, W]
bool is_transformation_valid = (shape_input.size() == 4);
// x'= reshape(x, [N, group, C / group, H * W]) or reshape(x, [N, group, C / group, H, W])
bool is_reshape_before_valid = (shape_reshape_before.size() == 4 || shape_reshape_before.size() == 5);
if (is_reshape_before_valid) {
size_t group = shape_reshape_before[1];
ngraph::Shape expected_reshape_before = { shape_input[0], group, shape_input[1] / group };
if (shape_reshape_before.size() == 4) {
expected_reshape_before.push_back(shape_input[2] * shape_input[3]);
} else {
expected_reshape_before.push_back(shape_input[2]);
expected_reshape_before.push_back(shape_input[3]);
}
is_reshape_before_valid &= (expected_reshape_before == shape_reshape_before);
}
// x''= transpose(x', [0, 2, 1, 3]) or transpose(x', [0, 2, 1, 3, 4])
bool is_transpose_valid = (transpose_constant_values.size() == 4 || transpose_constant_values.size() == 5);
if (is_transpose_valid) {
ngraph::AxisVector expected_transpose_values{ 0, 2, 1, 3 };
if (transpose_constant_values.size() == 5) {
expected_transpose_values.push_back(4);
}
is_transpose_valid &= (expected_transpose_values == transpose_constant_values);
}
// y = reshape(x'', [N, C, H, W])
bool is_reshape_after_valid = (shape_input == shape_reshape_after);
is_transformation_valid &= is_reshape_before_valid & is_transpose_valid & is_reshape_after_valid;
return is_transformation_valid;
}
NGRAPH_RTTI_DEFINITION(ngraph::pass::ShuffleChannelsFusion, "ShuffleChannelsFusion", 0);
ngraph::pass::ShuffleChannelsFusion::ShuffleChannelsFusion(const bool reshape_constants_check) {
MATCHER_SCOPE(ShuffleChannelsFusion);
auto input = ngraph::pattern::any_input(pattern::has_static_shape());
auto reshape_before_const_pattern = ngraph::pattern::wrap_type<ngraph::opset6::Constant>();
auto transpose_const_pattern = ngraph::pattern::wrap_type<ngraph::opset6::Constant>();
auto reshape_after_const_pattern = ngraph::pattern::wrap_type<ngraph::opset6::Constant>();
auto has_static_shape_and_single_consumer = [](const Output<Node>& output) {
return pattern::has_static_shape()(output) && pattern::consumers_count(1)(output);
};
auto reshape_before_pattern = ngraph::pattern::wrap_type<ngraph::opset6::Reshape>({input, reshape_before_const_pattern},
has_static_shape_and_single_consumer);
auto transpose_pattern = ngraph::pattern::wrap_type<ngraph::opset6::Transpose>({reshape_before_pattern, transpose_const_pattern},
has_static_shape_and_single_consumer);
auto reshape_after_pattern = ngraph::pattern::wrap_type<ngraph::opset6::Reshape>({transpose_pattern, reshape_after_const_pattern},
pattern::has_static_shape());
ngraph::matcher_pass_callback callback = [=](pattern::Matcher& m) {
const auto& pattern_map = m.get_pattern_value_map();
auto data = pattern_map.at(input);
auto reshape_before = std::dynamic_pointer_cast<ngraph::opset6::Reshape>(pattern_map.at(reshape_before_pattern).get_node_shared_ptr());
auto transpose = std::dynamic_pointer_cast<ngraph::opset6::Transpose>(pattern_map.at(transpose_pattern).get_node_shared_ptr());
auto reshape_after = std::dynamic_pointer_cast<ngraph::opset6::Reshape>(pattern_map.at(reshape_after_pattern).get_node_shared_ptr());
if (!reshape_after || !transpose || !reshape_after)
return false;
if (reshape_constants_check) {
auto reshape_before_constant = std::dynamic_pointer_cast<ngraph::opset6::Constant>(
pattern_map.at(reshape_before_const_pattern).get_node_shared_ptr());
auto reshape_after_constant = std::dynamic_pointer_cast<ngraph::opset6::Constant>(
pattern_map.at(reshape_after_const_pattern).get_node_shared_ptr());
if (!reshape_before_constant || !reshape_after_constant)
return false;
const auto& reshape_before_values = reshape_before_constant->cast_vector<int64_t>();
const auto& reshape_after_values = reshape_after_constant->cast_vector<int64_t>();
if (std::any_of(reshape_before_values.cbegin(), reshape_before_values.cend(), [](const int64_t& value) { return value == -1; }) ||
std::any_of(reshape_after_values.cbegin(), reshape_after_values.cend(), [](const int64_t& value) { return value == -1; })) {
return false;
}
}
auto shape_input = reshape_before->get_input_shape(0);
auto shape_reshape_before = reshape_before->get_output_shape(0);
auto shape_reshape_after = reshape_after->get_output_shape(0);
auto transpose_constant = std::dynamic_pointer_cast<ngraph::opset6::Constant>(pattern_map.at(transpose_const_pattern).get_node_shared_ptr());
auto transpose_constant_values = transpose_constant->get_axis_vector_val();
if (!check_shapes(shape_input, shape_reshape_before, transpose_constant_values, shape_reshape_after))
return false;
int64_t axis = 1ul;
int64_t group = shape_reshape_before[1];
auto shuffle_shannels = std::make_shared<ngraph::opset6::ShuffleChannels>(data, axis, group);
shuffle_shannels->set_friendly_name(reshape_after->get_friendly_name());
ngraph::copy_runtime_info({ reshape_before, transpose, reshape_after }, shuffle_shannels);
ngraph::replace_node(reshape_after, shuffle_shannels);
return true;
};
auto m = std::make_shared<ngraph::pattern::Matcher>(reshape_after_pattern, matcher_name);
register_matcher(m, callback);
}

View File

@ -0,0 +1,290 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "layer_transformation.hpp"
#include <string>
#include <memory>
#include <gtest/gtest.h>
#include <transformations/utils/utils.hpp>
#include <low_precision/shuffle_channels.hpp>
#include "common_test_utils/ngraph_test_utils.hpp"
#include "simple_low_precision_transformer.hpp"
#include "lpt_ngraph_functions/shuffle_channels_function.hpp"
#include "lpt_ngraph_functions/common/dequantization_operations.hpp"
namespace {
using namespace testing;
using namespace ngraph::pass;
class ShuffleChannelsTransformationTestValues {
public:
public:
class Actual {
public:
ngraph::element::Type inputPrecision;
ngraph::builder::subgraph::DequantizationOperations dequantization;
};
class Expected {
public:
ngraph::element::Type inputPrecision;
ngraph::builder::subgraph::DequantizationOperations dequantizationBefore;
ngraph::element::Type preicsionAfterOperation;
ngraph::builder::subgraph::DequantizationOperations dequantizationAfter;
};
ngraph::pass::low_precision::LayerTransformation::Params params;
std::int64_t axis;
std::int64_t group;
Actual actual;
Expected expected;
};
typedef std::tuple<
ngraph::Shape,
ShuffleChannelsTransformationTestValues> ShuffleChannelsTransformationParams;
class ShuffleChannelsTransformation : public LayerTransformation, public testing::WithParamInterface<ShuffleChannelsTransformationParams> {
public:
void SetUp() override {
ngraph::Shape inputShape = std::get<0>(GetParam());
ShuffleChannelsTransformationTestValues testValues = std::get<1>(GetParam());
actualFunction = ngraph::builder::subgraph::ShuffleChannelsFunction::getOriginal(
testValues.actual.inputPrecision,
inputShape,
testValues.actual.dequantization,
testValues.axis,
testValues.group);
SimpleLowPrecisionTransformer transform;
transform.add<ngraph::pass::low_precision::ShuffleChannelsTransformation, ngraph::opset1::ShuffleChannels>(testValues.params);
transform.transform(actualFunction);
referenceFunction = ngraph::builder::subgraph::ShuffleChannelsFunction::getReference(
testValues.expected.inputPrecision,
inputShape,
testValues.expected.dequantizationBefore,
testValues.axis,
testValues.group,
testValues.expected.preicsionAfterOperation,
testValues.expected.dequantizationAfter);
}
static std::string getTestCaseName(testing::TestParamInfo<ShuffleChannelsTransformationParams> obj) {
ngraph::Shape inputShape = std::get<0>(obj.param);
ShuffleChannelsTransformationTestValues testValues = std::get<1>(obj.param);
std::ostringstream result;
result <<
LayerTransformation::getTestCaseNameByParams(testValues.actual.inputPrecision, inputShape, testValues.params) << "_" <<
testValues.actual.dequantization << "_axis_" <<
testValues.axis << "_group_" << testValues.group;
return result.str();
}
};
TEST_P(ShuffleChannelsTransformation, CompareFunctions) {
actualFunction->validate_nodes_and_infer_types();
auto res = compare_functions(referenceFunction, actualFunction, true, true);
ASSERT_TRUE(res.first) << res.second;
}
const std::vector<ngraph::Shape> inputShapes = {
{ 1, 3, 8, 10 },
{ 4, 3, 8, 10 },
};
const std::vector<ShuffleChannelsTransformationTestValues> testValues = {
// U8 per tensor quantization
{
LayerTransformation::createParamsU8I8(),
1, // axis
1, // group
{
ngraph::element::u8,
{{ngraph::element::f32}, {128.f}, {0.02f}}
},
{
ngraph::element::u8,
{},
ngraph::element::u8,
{{ngraph::element::f32}, {128.f}, {0.02f}}
}
},
// U8 per channel quantization
{
LayerTransformation::createParamsU8I8(),
1,
1,
{
ngraph::element::u8,
{{ngraph::element::f32}, {{128.f, 64.f, 32.f}}, {{0.01f, 0.02f, 0.03f}}}
},
{
ngraph::element::u8,
{},
ngraph::element::u8,
{{ngraph::element::f32}, {{128.f, 64.f, 32.f}}, {{0.01f, 0.02f, 0.03f}}}
}
},
// U8 quantization by special dimension, shuffling by the same dimension
{
LayerTransformation::createParamsU8I8(),
2,
4,
{
ngraph::element::u8,
{
{ngraph::element::f32},
{{121.f, 122.f, 123.f, 124.f, 125.f, 126.f, 127.f, 128.f}, ngraph::element::f32, ngraph::Shape{1, 1, 8, 1}},
{{1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f}, ngraph::element::f32, ngraph::Shape{1, 1, 8, 1}}
}
},
{
ngraph::element::u8,
{},
ngraph::element::u8,
{
{ngraph::element::f32},
{{121.f, 123.f, 125.f, 127.f, 122.f, 124.f, 126.f, 128.f}, ngraph::element::f32, ngraph::Shape{1, 1, 8, 1}},
{{1.f, 3.f, 5.f, 7.f, 2.f, 4.f, 6.f, 8.f}, ngraph::element::f32, ngraph::Shape {1, 1, 8, 1}},
}
}
},
// U8 per channel quantization, shuffling by special dimension
{
LayerTransformation::createParamsU8I8(),
-2,
4,
{
ngraph::element::u8,
{{ngraph::element::f32}, {{128.f, 64.f, 32.f}}, {{0.01f, 0.02f, 0.03f}}}
},
{
ngraph::element::u8,
{},
ngraph::element::u8,
{{ngraph::element::f32}, {{128.f, 64.f, 32.f}}, {{0.01f, 0.02f, 0.03f}}}
}
},
// I8 per tensor quantization
{
LayerTransformation::createParamsI8I8(),
1,
1,
{
ngraph::element::i8,
{{ngraph::element::f32}, {128.f}, {0.02f}}
},
{
ngraph::element::i8,
{},
ngraph::element::i8,
{{ngraph::element::f32}, {128.f}, {0.02f}}
}
},
// I8 per channel quantization
{
LayerTransformation::createParamsI8I8(),
1,
1,
{
ngraph::element::i8,
{{ngraph::element::f32}, {{128.f, 64.f, 32.f}}, {{0.01f, 0.02f, 0.03f}}}
},
{
ngraph::element::i8,
{},
ngraph::element::i8,
{{ngraph::element::f32}, {{128.f, 64.f, 32.f}}, {{0.01f, 0.02f, 0.03f}}}
}
},
// I8 quantization by special dimension, shuffling by the same dimension
{
LayerTransformation::createParamsI8I8(),
2,
4,
{
ngraph::element::i8,
{
{ngraph::element::f32},
{{121.f, 122.f, 123.f, 124.f, 125.f, 126.f, 127.f, 128.f}, ngraph::element::f32, ngraph::Shape{1, 1, 8, 1}},
{{1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f}, ngraph::element::f32, ngraph::Shape{1, 1, 8, 1}}
}
},
{
ngraph::element::i8,
{},
ngraph::element::i8,
{
{ngraph::element::f32},
{{121.f, 123.f, 125.f, 127.f, 122.f, 124.f, 126.f, 128.f}, ngraph::element::f32, ngraph::Shape{1, 1, 8, 1}},
{{1.f, 3.f, 5.f, 7.f, 2.f, 4.f, 6.f, 8.f}, ngraph::element::f32, ngraph::Shape {1, 1, 8, 1}},
}
}
},
// I8 per channel quantization, shuffling by special dimension
{
LayerTransformation::createParamsI8I8(),
-2,
4,
{
ngraph::element::i8,
{{ngraph::element::f32}, {{128.f, 64.f, 32.f}}, {{0.01f, 0.02f, 0.03f}}}
},
{
ngraph::element::i8,
{},
ngraph::element::i8,
{{ngraph::element::f32}, {{128.f, 64.f, 32.f}}, {{0.01f, 0.02f, 0.03f}}}
}
},
// U8 per tensor quantization, not update precision
{
LayerTransformation::createParamsU8I8().setUpdatePrecisions(false),
3,
5,
{
ngraph::element::f32,
{{}, {128.f}, {0.02f}}
},
{
ngraph::element::f32,
{},
ngraph::element::f32,
{{}, {128.f}, {0.02f}}
}
},
// U8 without dequantization operations
{
LayerTransformation::createParamsU8I8(),
2,
4,
{
ngraph::element::u8,
{{}, {}, {}}
},
{
ngraph::element::u8,
{},
ngraph::element::u8,
{{}, {}, {}}
}
},
};
INSTANTIATE_TEST_CASE_P(
smoke_LPT,
ShuffleChannelsTransformation,
::testing::Combine(
::testing::ValuesIn(inputShapes),
::testing::ValuesIn(testValues)),
ShuffleChannelsTransformation::getTestCaseName);
} // namespace

View File

@ -0,0 +1,126 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include "common_test_utils/test_common.hpp"
#include <string>
#include <memory>
#include <ngraph/function.hpp>
#include <ngraph/opsets/opset6.hpp>
#include <transformations/common_optimizations/shuffle_channels_fusion.hpp>
#include <transformations/init_node_info.hpp>
#include <ngraph/pass/manager.hpp>
#include "common_test_utils/ngraph_test_utils.hpp"
namespace {
using namespace testing;
using namespace ngraph;
class ShuffleChannelsFusionTestValues {
public:
bool dynamicShape;
std::vector<int64_t> reshape_before_val;
std::vector<size_t> transpose_val;
std::vector<int64_t> reshape_after_val;
size_t batch_size;
bool check_reshape_values;
bool fuse_happened;
};
template <typename T>
inline std::ostream& operator<<(std::ostream& os, const std::vector<T>& values) {
os << "{ ";
for (size_t i = 0; i < values.size(); ++i) {
os << values[i];
if (i != (values.size() - 1ul)) {
os << ", ";
}
}
os << " }";
return os;
}
class ShuffleChannelsFusion : public ::testing::Test, public testing::WithParamInterface<ShuffleChannelsFusionTestValues> {
public:
void SetUp() override {
const auto values = GetParam();
{
const PartialShape inputPartialShape = values.dynamicShape ? PartialShape::dynamic() : Shape{ values.batch_size, 128, 720, 480 };
auto input0 = std::make_shared<opset6::Parameter>(element::f32, inputPartialShape);
auto shape_reshape_before = opset6::Constant::create(element::i64, Shape{ values.reshape_before_val.size() }, values.reshape_before_val);
auto permutation = opset6::Constant::create(element::i64, Shape{ values.transpose_val.size() }, values.transpose_val);
auto shape_reshape_after = opset6::Constant::create(element::i64, Shape{ values.reshape_after_val.size() }, values.reshape_after_val);
auto reshape_before = std::make_shared<ngraph::opset6::Reshape>(input0, shape_reshape_before, false);
auto permute = std::make_shared<ngraph::opset6::Transpose>(reshape_before, permutation);
auto reshape_after = std::make_shared<ngraph::opset6::Reshape>(permute, shape_reshape_after, false);
f = std::make_shared<ngraph::Function>(ngraph::NodeVector{ reshape_after }, ngraph::ParameterVector{ input0 });
ngraph::pass::Manager manager;
auto pass_config = manager.get_pass_config();
manager.register_pass<ngraph::pass::InitNodeInfo>();
manager.register_pass<ngraph::pass::ShuffleChannelsFusion>(values.check_reshape_values);
manager.run_passes(f);
ASSERT_NO_THROW(check_rt_info(f));
}
if (values.fuse_happened) {
auto input0 = std::make_shared<ngraph::opset6::Parameter>(ngraph::element::f32, ngraph::Shape{ values.batch_size, 128, 720, 480 });
auto shuffle_channels = std::make_shared<ngraph::opset6::ShuffleChannels>(input0, 1, values.reshape_before_val[1]);
f_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{ shuffle_channels }, ngraph::ParameterVector{ input0 });
} else {
f_ref = f;
}
}
static std::string getTestCaseName(testing::TestParamInfo<ShuffleChannelsFusionTestValues> obj) {
const ShuffleChannelsFusionTestValues testValues = obj.param;
std::ostringstream result;
if (testValues.dynamicShape) {
result << "_dynamic_shape_";
} else {
result << "_batch_size_" << testValues.batch_size;
}
result << "_before_" << testValues.reshape_before_val
<< "_transpose_" << testValues.transpose_val << "_after_" << testValues.reshape_after_val
<< (testValues.check_reshape_values ? "check_reshape_values" : "");
return result.str();
}
protected:
std::shared_ptr<ngraph::Function> f;
std::shared_ptr<ngraph::Function> f_ref;
};
TEST_P(ShuffleChannelsFusion, CompareFunctions) {
auto res = compare_functions(f, f_ref);
ASSERT_TRUE(res.first) << res.second;
}
const std::vector<ShuffleChannelsFusionTestValues> testValues = {
{ true, {1, 2, 64, 720, 480}, {0, 2, 1, 3, 4}, {1, 128, 720, 480}, 1, false, false },
{ false, {1, 2, 64, 720, 480}, {0, 2, 1, 3, 4}, {1, 128, 720, 480}, 1, false, true },
{ false, {1, 2, 64, 720, 480}, {0, 2, 1, 3, 4}, {1, 128, 720, 480}, 1, true, true },
{ false, {1, 2, 64, 720, 480}, {0, 2, 1, 3, 4}, {1, -1, 720, 480}, 1, false, true },
{ false, {4, 2, 64, 720, 480}, {0, 2, 1, 3, 4}, {1, -1, 720, 480}, 4, false, false },
{ false, {1, 2, 64, 720, 480}, {0, 2, 1, 3, 4}, {1, -1, 720, 480}, 1, true, false },
{ true, {1, 4, 32, 720 * 480}, {0, 2, 1, 3}, {1, 128, 720, 480}, 1, false, false },
{ false, {1, 4, 32, 720 * 480}, {0, 2, 1, 3}, {1, 128, 720, 480}, 1, false, true },
{ false, {1, 2, 64, 720 * 480}, {0, 2, 1, 3}, {1, 128, 720, 480}, 1, true, true },
{ false, {1, 2, 64, 720 * 480}, {0, 2, 1, 3}, {1, -1, 720, 480}, 1, false, true },
{ false, {4, 2, 64, 720 * 480}, {0, 2, 1, 3}, {1, -1, 720, 480}, 4, false, false },
{ false, {1, 2, 64, 720 * 480}, {0, 2, 1, 3}, {1, -1, 720, 480}, 1, true, false },
};
INSTANTIATE_TEST_CASE_P(
TransformationTests,
ShuffleChannelsFusion,
::testing::ValuesIn(testValues),
ShuffleChannelsFusion::getTestCaseName);
} // namespace

View File

@ -0,0 +1,101 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <vector>
#include "low_precision_transformations/shuffle_channels_transformation.hpp"
#include "common_test_utils/test_constants.hpp"
using namespace LayerTestsDefinitions;
namespace {
const std::vector<ngraph::element::Type> netPrecisions = {
ngraph::element::f32,
// ngraph::element::f16
};
const std::vector<ngraph::Shape> inputShapes = {
{ 1, 3, 16, 16 },
{ 4, 3, 16, 16 }
};
const std::vector<ngraph::pass::low_precision::LayerTransformation::Params> trasformationParamValues = {
LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParams().setUpdatePrecisions(true),
};
const std::vector<LayerTestsDefinitions::ShuffleChannelsTransformationParam> params = {
{
{ 256ul, ngraph::Shape { 1, 1, 1, 1 }, { 0.f }, { 255.f }, { 0.f }, { 25.5f } },
0,
1,
"output_original",
"U8"
},
{
{ 256ul, ngraph::Shape { 1, 1, 1, 1 }, { 0.f }, { 255.f }, { 0.f }, { 25.5f } },
-3,
1,
"output_original",
"U8"
},
{
{
256ul,
ngraph::Shape { 1, 3, 1, 1 },
{ 0.f },
{ 25.5f },
{ 0.f, 0.f, 0.f },
{ 25.5f / 2.f, 25.5f / 4.f, 25.5f }
},
-3,
1,
"output_original",
"U8"
},
{
{
256ul,
ngraph::Shape { 1, 3, 1, 1 },
{ 0.f },
{ 25.5f },
{ -4.f, -3.f, 0.f },
{ 10.f, 12.f, 25.5f }
},
-3,
1,
"output_original",
"U8"
},
{
{ 256ul, ngraph::Shape { 1, 1, 1, 1 }, { 0.f }, { 255.f }, { 0.f }, { 25.5f } },
2,
4,
"output_original",
"U8"
},
{
{
256ul,
ngraph::Shape { 1, 3, 1, 1 },
{ 0.f },
{ 25.5f },
{ 0.f, 0.f, 0.f },
{ 25.5f / 2.f, 25.5f / 4.f, 25.5f }
},
-1,
8,
"output_original",
"U8"
},
};
INSTANTIATE_TEST_CASE_P(smoke_LPT, ShuffleChannelsTransformation,
::testing::Combine(
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(inputShapes),
::testing::Values(CommonTestUtils::DEVICE_CPU),
::testing::ValuesIn(trasformationParamValues),
::testing::ValuesIn(params)),
ShuffleChannelsTransformation::getTestCaseName);
} // namespace

View File

@ -0,0 +1,88 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <vector>
#include "low_precision_transformations/shuffle_channels_transformation.hpp"
#include "common_test_utils/test_constants.hpp"
using namespace LayerTestsDefinitions;
namespace {
const std::vector<ngraph::element::Type> netPrecisions = {
ngraph::element::f32,
ngraph::element::f16
};
const std::vector<ngraph::Shape> inputShapes = {
{ 1, 3, 16, 16 }
};
const std::vector<ngraph::pass::low_precision::LayerTransformation::Params> trasformationParamValues = {
LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParams(),
};
const std::vector<LayerTestsDefinitions::ShuffleChannelsTransformationParam> params = {
{
{ 256ul, ngraph::Shape { 1, 1, 1, 1 }, { 0.f }, { 255.f }, { 0.f }, { 25.5f } },
0,
1,
},
{
{ 256ul, ngraph::Shape { 1, 1, 1, 1 }, { 0.f }, { 255.f }, { 0.f }, { 25.5f } },
-3,
1,
},
{
{
256ul,
ngraph::Shape { 1, 3, 1, 1 },
{ 0.f },
{ 25.5f },
{ 0.f, 0.f, 0.f },
{ 25.5f / 2.f, 25.5f / 4.f, 25.5f }
},
-3,
1,
},
{
{
256ul,
ngraph::Shape { 1, 3, 1, 1 },
{ 0.f },
{ 25.5f },
{ -4.f, -3.f, 0.f },
{ 10.f, 12.f, 25.5f }
},
-3,
1,
},
{
{ 256ul, ngraph::Shape { 1, 1, 1, 1 }, { 0.f }, { 255.f }, { 0.f }, { 25.5f } },
2,
4,
},
{
{
256ul,
ngraph::Shape { 1, 3, 1, 1 },
{ 0.f },
{ 25.5f },
{ 0.f, 0.f, 0.f },
{ 25.5f / 2.f, 25.5f / 4.f, 25.5f }
},
-1,
8,
},
};
INSTANTIATE_TEST_CASE_P(smoke_LPT, ShuffleChannelsTransformation,
::testing::Combine(
::testing::ValuesIn(netPrecisions),
::testing::ValuesIn(inputShapes),
::testing::Values(CommonTestUtils::DEVICE_GPU),
::testing::ValuesIn(trasformationParamValues),
::testing::ValuesIn(params)),
ShuffleChannelsTransformation::getTestCaseName);
} // namespace

View File

@ -0,0 +1,43 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <string>
#include <memory>
#include "shared_test_classes/base/low_precision_transformations/layer_transformation.hpp"
#include "lpt_ngraph_functions/common/fake_quantize_on_data.hpp"
namespace LayerTestsDefinitions {
class ShuffleChannelsTransformationParam {
public:
ngraph::builder::subgraph::FakeQuantizeOnData fakeQuantizeOnData;
std::int64_t axis;
std::int64_t group;
std::string layerName;
std::string expectedKernelType;
};
typedef std::tuple<
ngraph::element::Type,
ngraph::Shape,
std::string,
ngraph::pass::low_precision::LayerTransformation::Params,
ShuffleChannelsTransformationParam
> ShuffleChannelsTransformationParams;
class ShuffleChannelsTransformation :
public testing::WithParamInterface<ShuffleChannelsTransformationParams>,
public LayerTestsUtils::LayerTransformation {
public:
static std::string getTestCaseName(testing::TestParamInfo<ShuffleChannelsTransformationParams> obj);
protected:
void SetUp() override;
void Run() override;
};
} // namespace LayerTestsDefinitions

View File

@ -0,0 +1,62 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "low_precision_transformations/shuffle_channels_transformation.hpp"
#include <memory>
#include <tuple>
#include <vector>
#include <string>
#include <ie_core.hpp>
#include "common_test_utils/common_utils.hpp"
#include "functional_test_utils/plugin_cache.hpp"
#include "shared_test_classes/base/layer_test_utils.hpp"
#include "lpt_ngraph_functions/shuffle_channels_function.hpp"
namespace LayerTestsDefinitions {
std::string ShuffleChannelsTransformation::getTestCaseName(testing::TestParamInfo<ShuffleChannelsTransformationParams> obj) {
ngraph::element::Type netPrecision;
ngraph::Shape inputShape;
std::string targetDevice;
ngraph::pass::low_precision::LayerTransformation::Params params;
ShuffleChannelsTransformationParam param;
std::tie(netPrecision, inputShape, targetDevice, params, param) = obj.param;
std::ostringstream result;
result << getTestCaseNameByParams(netPrecision, inputShape, targetDevice, params) << "_" <<
param.fakeQuantizeOnData << "_axis_" << param.axis << "_group_" << param.group;
return result.str();
}
void ShuffleChannelsTransformation::SetUp() {
ngraph::element::Type netPrecision;
ngraph::Shape inputShape;
ngraph::pass::low_precision::LayerTransformation::Params params;
ShuffleChannelsTransformationParam param;
std::tie(netPrecision, inputShape, targetDevice, params, param) = this->GetParam();
function = ngraph::builder::subgraph::ShuffleChannelsFunction::getOriginal(
netPrecision,
inputShape,
param.fakeQuantizeOnData,
param.axis,
param.group);
}
void ShuffleChannelsTransformation::Run() {
LayerTestsCommon::Run();
const auto params = std::get<4>(GetParam());
const auto actualType = getRuntimePrecision(params.layerName);
EXPECT_EQ(actualType, params.expectedKernelType);
}
TEST_P(ShuffleChannelsTransformation, CompareWithRefImpl) {
Run();
};
} // namespace LayerTestsDefinitions

View File

@ -0,0 +1,46 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <memory>
#include <ngraph/ngraph.hpp>
#include <low_precision/layer_transformation.hpp>
#include "lpt_ngraph_functions/common/dequantization_operations.hpp"
#include "lpt_ngraph_functions/common/builders.hpp"
namespace ngraph {
namespace builder {
namespace subgraph {
class ShuffleChannelsFunction {
public:
static std::shared_ptr<ngraph::Function> getOriginal(
const ngraph::element::Type inputPrecision,
const ngraph::Shape& inputShape,
const ngraph::builder::subgraph::DequantizationOperations& deqBefore,
const std::int64_t axis,
const std::int64_t group);
static std::shared_ptr<ngraph::Function> getOriginal(
const ngraph::element::Type inputPrecision,
const ngraph::Shape& inputShape,
const ngraph::builder::subgraph::FakeQuantizeOnData& fqOnData,
const std::int64_t axis,
const std::int64_t group);
static std::shared_ptr<ngraph::Function> getReference(
const ngraph::element::Type inputPrecision,
const ngraph::Shape& inputShape,
const ngraph::builder::subgraph::DequantizationOperations& deqBefore,
const std::int64_t axis,
const std::int64_t group,
const ngraph::element::Type precisionAfterOperation,
const ngraph::builder::subgraph::DequantizationOperations& deqAfter);
};
} // namespace subgraph
} // namespace builder
} // namespace ngraph

View File

@ -0,0 +1,83 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <ngraph/opsets/opset1.hpp>
#include "low_precision/network_helper.hpp"
#include "lpt_ngraph_functions/common/builders.hpp"
#include "lpt_ngraph_functions/shuffle_channels_function.hpp"
#include "ngraph_functions/subgraph_builders.hpp"
namespace ngraph {
namespace builder {
namespace subgraph {
std::shared_ptr<Function> ShuffleChannelsFunction::getOriginal(
const element::Type inputPrecision,
const Shape& inputShape,
const builder::subgraph::DequantizationOperations& deqBefore,
const std::int64_t axis,
const std::int64_t group) {
const auto input = std::make_shared<opset1::Parameter>(inputPrecision, inputShape);
const auto dequantization = makeDequantization(input, deqBefore);
const auto shuffleChannels = std::make_shared<opset1::ShuffleChannels>(dequantization, axis, group);
shuffleChannels->set_friendly_name("output");
const auto function = std::make_shared<ngraph::Function>(
ResultVector{ std::make_shared<opset1::Result>(shuffleChannels) },
ParameterVector{ input },
"ShuffleChannelsFunction");
return function;
}
std::shared_ptr<ngraph::Function> ShuffleChannelsFunction::getOriginal(
const ngraph::element::Type inputPrecision,
const ngraph::Shape& inputShape,
const ngraph::builder::subgraph::FakeQuantizeOnData& fqOnData,
const std::int64_t axis,
const std::int64_t group) {
const auto input = std::make_shared<opset1::Parameter>(inputPrecision, inputShape);
const auto fakeQuantize = makeFakeQuantize(input, inputPrecision, fqOnData);
const auto shuffleChannels = std::make_shared<opset1::ShuffleChannels>(fakeQuantize, axis, group);
shuffleChannels->set_friendly_name("output");
const auto function = std::make_shared<ngraph::Function>(
ResultVector{ std::make_shared<opset1::Result>(shuffleChannels) },
ParameterVector{ input },
"ShuffleChannelsFunction");
return function;
}
std::shared_ptr<ngraph::Function> ShuffleChannelsFunction::getReference(
const ngraph::element::Type inputPrecision,
const ngraph::Shape& inputShape,
const ngraph::builder::subgraph::DequantizationOperations& deqBefore,
const std::int64_t axis,
const std::int64_t group,
const ngraph::element::Type precisionAfterOperation,
const ngraph::builder::subgraph::DequantizationOperations& deqAfter) {
const auto input = std::make_shared<opset1::Parameter>(inputPrecision, inputShape);
const auto dequantizationBefore = makeDequantization(input, deqBefore);
const auto shuffleChannels = std::make_shared<opset1::ShuffleChannels>(dequantizationBefore, axis, group);
ngraph::pass::low_precision::NetworkHelper::setOutDataPrecision(shuffleChannels, precisionAfterOperation);
const auto dequantizationAfter = makeDequantization(shuffleChannels, deqAfter);
dequantizationAfter->set_friendly_name("output");
const auto function = std::make_shared<ngraph::Function>(
ResultVector{ std::make_shared<opset1::Result>(dequantizationAfter) },
ParameterVector{ input },
"ShuffleChannelsFunction");
return function;
}
} // namespace subgraph
} // namespace builder
} // namespace ngraph

View File

@ -19,8 +19,8 @@ namespace ngraph
class NGRAPH_API ShuffleChannels : public Op
{
public:
static constexpr NodeTypeInfo type_info{"ShuffleChannels", 0};
const NodeTypeInfo& get_type_info() const override { return type_info; }
NGRAPH_RTTI_DECLARATION;
ShuffleChannels() = default;
/// \brief Constructs a ShuffleChannels node.
///

View File

@ -16,7 +16,9 @@
using namespace std;
using namespace ngraph;
constexpr NodeTypeInfo op::ShuffleChannels::type_info;
NGRAPH_SUPPRESS_DEPRECATED_START
NGRAPH_RTTI_DEFINITION(op::v0::ShuffleChannels, "ShuffleChannels", 0);
op::ShuffleChannels::ShuffleChannels(const Output<Node>& data,
const int64_t axis,