From feb0c7eff095fa8bd1a28c4bb68916b69f59842e Mon Sep 17 00:00:00 2001 From: Chenhu Wang Date: Wed, 29 May 2024 18:45:07 +0800 Subject: [PATCH] GN decomposition/tokenization test and snippets func test (#23903) ### Details: - *GroupNormalization decomposition test* - *GroupNormalization tokenization test* - *GroupNormalization snippets func test* ### Tickets: - *[CVS-136159](https://jira.devtools.intel.com/browse/CVS-136159)* - *[CVS-137359](https://jira.devtools.intel.com/browse/CVS-137359)* --- .../snippets/src/pass/gn_decomposition.cpp | 1 + .../snippets/src/pass/gn_tokenization.cpp | 3 +- .../tests/include/pass/gn_decomposition.hpp | 33 +++++++ .../tests/include/pass/gn_tokenization.hpp | 32 ++++++ .../snippets/tests/src/lowering_utils.cpp | 4 + .../tests/src/pass/gn_decomposition.cpp | 65 ++++++++++++ .../tests/src/pass/gn_tokenization.cpp | 64 ++++++++++++ .../transformation_pipeline.cpp | 36 +++---- .../snippets/group_normalization.cpp | 47 +++++++++ .../include/snippets/group_normalization.hpp | 34 +++++++ .../src/snippets/group_normalization.cpp | 78 +++++++++++++++ .../include/subgraph_group_normalization.hpp | 65 ++++++++++++ .../src/subgraph_group_normalization.cpp | 99 +++++++++++++++++++ 13 files changed, 543 insertions(+), 18 deletions(-) create mode 100644 src/common/snippets/tests/include/pass/gn_decomposition.hpp create mode 100644 src/common/snippets/tests/include/pass/gn_tokenization.hpp create mode 100644 src/common/snippets/tests/src/pass/gn_decomposition.cpp create mode 100644 src/common/snippets/tests/src/pass/gn_tokenization.cpp create mode 100644 src/plugins/intel_cpu/tests/functional/shared_tests_instances/snippets/group_normalization.cpp create mode 100644 src/tests/functional/plugin/shared/include/snippets/group_normalization.hpp create mode 100644 src/tests/functional/plugin/shared/src/snippets/group_normalization.cpp create mode 100644 src/tests/ov_helpers/ov_snippets_models/include/subgraph_group_normalization.hpp create mode 100644 src/tests/ov_helpers/ov_snippets_models/src/subgraph_group_normalization.cpp diff --git a/src/common/snippets/src/pass/gn_decomposition.cpp b/src/common/snippets/src/pass/gn_decomposition.cpp index 04d3fdb0ac5..1bb82421abe 100644 --- a/src/common/snippets/src/pass/gn_decomposition.cpp +++ b/src/common/snippets/src/pass/gn_decomposition.cpp @@ -38,6 +38,7 @@ GNDecomposition::GNDecomposition() { // reshape [N, C, spatial] to [N, group, 1, (C / group) * spatial] const auto orig_shape = group_norm_node->get_input_partial_shape(0).to_shape(); size_t orig_rank = orig_shape.size(); + OPENVINO_ASSERT(orig_rank >= 2, "First input rank for group normalization op should be greater than 1"); size_t group_rank = 4; size_t c_in_group = orig_shape[1] / num_groups; size_t spatial_dim = 1; diff --git a/src/common/snippets/src/pass/gn_tokenization.cpp b/src/common/snippets/src/pass/gn_tokenization.cpp index c42a44c64cc..e6738472376 100644 --- a/src/common/snippets/src/pass/gn_tokenization.cpp +++ b/src/common/snippets/src/pass/gn_tokenization.cpp @@ -20,7 +20,8 @@ ov::snippets::pass::TokenizeGNSnippets::TokenizeGNSnippets() { ov::matcher_pass_callback callback = [=](ov::pass::pattern::Matcher& m) { OV_ITT_SCOPED_TASK(ov::pass::itt::domains::SnippetsTransform, "Snippets::pass::TokenizeGNSnippets") auto group_norm_node = ov::as_type_ptr(m.get_match_root()); - if (group_norm_node->is_dynamic() || group_norm_node->get_element_type() != element::f32) + if (group_norm_node->is_dynamic() || group_norm_node->get_element_type() != element::f32 || + GetSnippetsNodeType(group_norm_node) == SnippetsNodeType::SkippedByPlugin) return false; auto subgraph = op::Subgraph::wrap_node_as_subgraph(group_norm_node); diff --git a/src/common/snippets/tests/include/pass/gn_decomposition.hpp b/src/common/snippets/tests/include/pass/gn_decomposition.hpp new file mode 100644 index 00000000000..012bbd7247a --- /dev/null +++ b/src/common/snippets/tests/include/pass/gn_decomposition.hpp @@ -0,0 +1,33 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "lowering_utils.hpp" +#include "subgraph_group_normalization.hpp" + +/* The main purpose is to test that GNDecomposition properly decomposes groupNormalization operation + */ + +namespace ov { +namespace test { +namespace snippets { + +typedef std::tuple< + PartialShape, // Input 0 Shape + size_t, // numGroup + float // epsilon +> GroupNormalizationParams; + +class GNDecompositionTest : public LoweringTests, public testing::WithParamInterface { +public: + static std::string getTestCaseName(testing::TestParamInfo obj); +protected: + void SetUp() override; + std::shared_ptr snippets_model; +}; + +} // namespace snippets +} // namespace test +} // namespace ov diff --git a/src/common/snippets/tests/include/pass/gn_tokenization.hpp b/src/common/snippets/tests/include/pass/gn_tokenization.hpp new file mode 100644 index 00000000000..e1e5c7ee81f --- /dev/null +++ b/src/common/snippets/tests/include/pass/gn_tokenization.hpp @@ -0,0 +1,32 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "snippets/pass/tokenization.hpp" +#include "subgraph_group_normalization.hpp" + +namespace ov { +namespace test { +namespace snippets { + +typedef std::tuple< + PartialShape, // Input 0 Shape + size_t, // numGroup + float // epsilon +> GroupNormalizationParams; + +class TokenizeGNSnippetsTests : public TransformationTestsF, public testing::WithParamInterface { +public: + static std::string getTestCaseName(testing::TestParamInfo obj); +protected: + void SetUp() override; + std::shared_ptr snippets_model; +}; + +} // namespace snippets +} // namespace test +} // namespace ov diff --git a/src/common/snippets/tests/src/lowering_utils.cpp b/src/common/snippets/tests/src/lowering_utils.cpp index 9d429350c45..35d9882e8b7 100644 --- a/src/common/snippets/tests/src/lowering_utils.cpp +++ b/src/common/snippets/tests/src/lowering_utils.cpp @@ -7,6 +7,7 @@ #include "utils.hpp" #include "snippets/pass/tokenization.hpp" #include "snippets/pass/collapse_subgraph.hpp" +#include "snippets/pass/gn_tokenization.hpp" #include "snippets/lowered/expression.hpp" @@ -30,6 +31,7 @@ DummyTargetMachine::DummyTargetMachine(const std::vector& jitters[op::v1::Divide::get_type_info_static()] = dummy_functor; jitters[op::v1::Maximum::get_type_info_static()] = dummy_functor; jitters[op::v0::Exp::get_type_info_static()] = dummy_functor; + jitters[op::v0::Sqrt::get_type_info_static()] = dummy_functor; jitters[ov::snippets::op::PowerStatic::get_type_info_static()] = dummy_functor; jitters[ov::snippets::op::HorizonMax::get_type_info_static()] = dummy_functor; jitters[ov::snippets::op::HorizonSum::get_type_info_static()] = dummy_functor; @@ -57,6 +59,7 @@ DummyTargetMachine::DummyTargetMachine(const std::vector& jitters[ov::snippets::op::Fill::get_type_info_static()] = dummy_functor; jitters[ov::snippets::op::ReduceMax::get_type_info_static()] = dummy_functor; jitters[ov::snippets::op::ReduceSum::get_type_info_static()] = dummy_functor; + jitters[ov::snippets::op::Reshape::get_type_info_static()] = dummy_functor; for (const auto& elem : custom_opset) { jitters[elem] = dummy_functor; @@ -133,6 +136,7 @@ std::shared_ptr LoweringTests::getTokenizedSubgraph( ov::pass::Manager m; ov::snippets::pass::SnippetsTokenization::Config config = get_default_tokenization_config(); m.register_pass(); + m.register_pass(); m.register_pass(config); m.run_passes(f); // Perform lowering diff --git a/src/common/snippets/tests/src/pass/gn_decomposition.cpp b/src/common/snippets/tests/src/pass/gn_decomposition.cpp new file mode 100644 index 00000000000..ae501564c12 --- /dev/null +++ b/src/common/snippets/tests/src/pass/gn_decomposition.cpp @@ -0,0 +1,65 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include "pass/gn_decomposition.hpp" +#include "common_test_utils/common_utils.hpp" +#include "subgraph_group_normalization.hpp" +#include "subgraph_lowered.hpp" + +namespace ov { +namespace test { +namespace snippets { + +std::string GNDecompositionTest::getTestCaseName(testing::TestParamInfo obj) { + PartialShape input_shape; + size_t num_group; + float eps; + std::tie(input_shape, num_group, eps) = obj.param; + std::ostringstream result; + result << "IS=" << ov::test::utils::partialShape2str({input_shape}) << "_"; + result << "num_group=" << num_group << "_"; + result << "eps=" << eps; + return result.str(); +} + +void GNDecompositionTest::SetUp() { + LoweringTests::SetUp(); + PartialShape data_shape; + size_t num_group; + float eps; + std::tie(data_shape, num_group, eps) = this->GetParam(); + OPENVINO_ASSERT(data_shape.size() >= 2, "First input rank for group normalization op should be greater than 1"); + PartialShape scaleShiftShape = PartialShape{data_shape[1]}; + std::vector input_shapes = { data_shape, scaleShiftShape, scaleShiftShape}; + snippets_model = std::make_shared(input_shapes, num_group, eps); +} + +TEST_P(GNDecompositionTest, GNDecomposition) { + auto subgraph = getLoweredSubgraph(snippets_model->getOriginal()); + model = subgraph->body_ptr(); + model_ref = snippets_model->getLowered(); +} + +namespace { + +const std::vector input_shapes{ + {1, 8}, + {1, 8, 18}, + {1, 16, 8, 5}, + {3, 8, 2, 2, 3}, + {3, 8, 2, 2, 3, 3} +}; + +INSTANTIATE_TEST_SUITE_P(smoke_Snippets_GNDecomposition, + GNDecompositionTest, + ::testing::Combine(::testing::ValuesIn(input_shapes), + ::testing::Values(4), + ::testing::Values(0.0001)), + GNDecompositionTest::getTestCaseName); + +} // namespace +} // namespace snippets +} // namespace test +} // namespace ov \ No newline at end of file diff --git a/src/common/snippets/tests/src/pass/gn_tokenization.cpp b/src/common/snippets/tests/src/pass/gn_tokenization.cpp new file mode 100644 index 00000000000..21701f2d754 --- /dev/null +++ b/src/common/snippets/tests/src/pass/gn_tokenization.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include "snippets/pass/gn_tokenization.hpp" +#include "common_test_utils/common_utils.hpp" + +namespace ov { +namespace test { +namespace snippets { + +std::string TokenizeGNSnippetsTests::getTestCaseName(testing::TestParamInfo obj) { + PartialShape input_shape; + size_t num_group; + float eps; + std::tie(input_shape, num_group, eps) = obj.param; + std::ostringstream result; + result << "IS=" << ov::test::utils::partialShape2str({input_shape}) << "_"; + result << "num_group=" << num_group << "_"; + result << "eps=" << eps; + return result.str(); +} + +void TokenizeGNSnippetsTests::SetUp() { + TransformationTestsF::SetUp(); + PartialShape data_shape; + size_t num_group; + float eps; + std::tie(data_shape, num_group, eps) = this->GetParam(); + OPENVINO_ASSERT(data_shape.size() >= 2, "First input rank for group normalization op should be greater than 1"); + PartialShape scaleShiftShape = PartialShape{data_shape[1]}; + std::vector input_shapes = { data_shape, scaleShiftShape, scaleShiftShape}; + snippets_model = std::make_shared(input_shapes, num_group, eps); + manager.register_pass(); +} + +TEST_P(TokenizeGNSnippetsTests, smoke_TokenizeGNSnippets) { + model = snippets_model->getOriginal(); + model_ref = snippets_model->getReference(); +} + +namespace { + +const std::vector input_shapes{ + {3, 10}, + {3, 10, 1}, + {3, 10, 2, 2}, + {1, 20, 2, 2, 3}, + {1, 20, 2, 2, 3, 3} +}; + +INSTANTIATE_TEST_SUITE_P(smoke_Snippets_GNTokenize, + TokenizeGNSnippetsTests, + ::testing::Combine(::testing::ValuesIn(input_shapes), + ::testing::Values(5), + ::testing::Values(0.0001)), + TokenizeGNSnippetsTests::getTestCaseName); + +} // namespace +} // namespace snippets +} // namespace test +} // namespace ov diff --git a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp index c900bd5a85b..45995f5fd0c 100644 --- a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp +++ b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp @@ -521,24 +521,26 @@ void Transformations::PreLpt(const std::vector& defaultPrecis // 2. GroupNormalizationDecomposition produce MVN, and MVN have a conditional pass MVN6Decomposition. If call MVN6Decomposition again after // snippets pipeline as well, where MVN is decomposed to simple ops, these simple ops will not tokenized into subgraph again. // CVS-134277 to fully enable GN as snippets to disable this GroupNormalizationDecomposition entirly. - if (node->is_dynamic() || !one_of(inferencePrecision, element::f32, element::undefined)) - return false; - const auto group_norm = ov::as_type_ptr(node); - if (!group_norm || !implication(inferencePrecision == element::undefined, group_norm->get_element_type() == element::f32)) - return false; - const auto num_groups = static_cast(group_norm->get_num_groups()); - const auto shape = group_norm->get_input_partial_shape(0).to_shape(); - size_t snippets_work_amount = shape[0] * num_groups; - size_t concurrency = parallel_get_max_threads(); - if (concurrency > snippets_work_amount) - return false; - size_t spatial_dim = 1; - for (size_t i = 2; i < shape.size(); ++i) - spatial_dim = spatial_dim * shape[i]; - size_t snippets_tensor_size = spatial_dim * shape[1] / num_groups * node->get_element_type().size(); - size_t cache_size_l1 = dnnl::utils::get_cache_size(1, true); - if (snippets_tensor_size > cache_size_l1) { + if (node->is_dynamic() || !one_of(inferencePrecision, element::f32, element::undefined) || snippetsMode == Config::SnippetsMode::Disable) return false; + if (snippetsMode != Config::SnippetsMode::IgnoreCallback) { + const auto group_norm = ov::as_type_ptr(node); + if (!group_norm || !implication(inferencePrecision == element::undefined, group_norm->get_element_type() == element::f32)) + return false; + const auto num_groups = static_cast(group_norm->get_num_groups()); + const auto shape = group_norm->get_input_partial_shape(0).to_shape(); + size_t snippets_work_amount = shape[0] * num_groups; + size_t concurrency = parallel_get_max_threads(); + if (concurrency > snippets_work_amount) + return false; + size_t spatial_dim = 1; + for (size_t i = 2; i < shape.size(); ++i) + spatial_dim = spatial_dim * shape[i]; + size_t snippets_tensor_size = spatial_dim * shape[1] / num_groups * node->get_element_type().size(); + size_t cache_size_l1 = dnnl::utils::get_cache_size(1, true); + if (snippets_tensor_size > cache_size_l1) { + return false; + } } return true; diff --git a/src/plugins/intel_cpu/tests/functional/shared_tests_instances/snippets/group_normalization.cpp b/src/plugins/intel_cpu/tests/functional/shared_tests_instances/snippets/group_normalization.cpp new file mode 100644 index 00000000000..cbba412d66c --- /dev/null +++ b/src/plugins/intel_cpu/tests/functional/shared_tests_instances/snippets/group_normalization.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "snippets/group_normalization.hpp" +#include "common_test_utils/test_constants.hpp" + +namespace ov { +namespace test { +namespace snippets { + +namespace { + +// snippets ignore_callback is set in setup, so these tests will always run as snippets +const std::vector inputShape = { + {3, 8}, + {3, 8, 1}, + {3, 8, 7}, + {3, 8, 16}, + {3, 8, 21}, + {1, 4, 8, 8}, + {1, 8, 1, 22}, + {1, 16, 1, 33}, + {1, 4, 1, 1, 34}, + {1, 8, 1, 8, 2, 2}, + {1, 8, 1, 8, 2, 2, 2} +}; + +const std::vector numGroups = { + 2, 4, +}; + +INSTANTIATE_TEST_SUITE_P(smoke_Snippets_GroupNormalization, GroupNormalization, + ::testing::Combine( + ::testing::ValuesIn(ov::test::static_shapes_to_test_representation(inputShape)), + ::testing::ValuesIn(numGroups), // num_group + ::testing::Values(0.0001), // eps + ::testing::Values(1), // expected node number + ::testing::Values(1), // expected subgraph number + ::testing::Values(ov::test::utils::DEVICE_CPU)), + GroupNormalization::getTestCaseName); + +} // namespace + +} // namespace snippets +} // namespace test +} // namespace ov \ No newline at end of file diff --git a/src/tests/functional/plugin/shared/include/snippets/group_normalization.hpp b/src/tests/functional/plugin/shared/include/snippets/group_normalization.hpp new file mode 100644 index 00000000000..7f37032c830 --- /dev/null +++ b/src/tests/functional/plugin/shared/include/snippets/group_normalization.hpp @@ -0,0 +1,34 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "shared_test_classes/base/snippets_test_utils.hpp" + +namespace ov { +namespace test { +namespace snippets { + +typedef std::tuple< + InputShape, // Input 0 Shape + size_t, // numGroup + float, // epsilon + size_t, // Expected num nodes + size_t, // Expected num subgraphs + std::string // Target Device +> GroupNormalizationParams; + +class GroupNormalization : public testing::WithParamInterface, + virtual public ov::test::SnippetsTestsCommon { +public: + static std::string getTestCaseName(testing::TestParamInfo obj); + +protected: + void SetUp() override; + InputShape ExtractScaleShiftShape(const InputShape& shape); +}; + +} // namespace snippets +} // namespace test +} // namespace ov \ No newline at end of file diff --git a/src/tests/functional/plugin/shared/src/snippets/group_normalization.cpp b/src/tests/functional/plugin/shared/src/snippets/group_normalization.cpp new file mode 100644 index 00000000000..aa157be0e7d --- /dev/null +++ b/src/tests/functional/plugin/shared/src/snippets/group_normalization.cpp @@ -0,0 +1,78 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_test_utils/common_utils.hpp" +#include "snippets/group_normalization.hpp" +#include "subgraph_group_normalization.hpp" +#include "functional_test_utils/skip_tests_config.hpp" + +namespace ov { +namespace test { +namespace snippets { + +std::string GroupNormalization::getTestCaseName(testing::TestParamInfo obj) { + InputShape inputShapes; + size_t numGroup; + float eps; + std::string targetDevice; + size_t num_nodes, num_subgraphs; + std::tie(inputShapes, numGroup, eps, num_nodes, num_subgraphs, targetDevice) = obj.param; + + std::ostringstream result; + result << "IS=" << ov::test::utils::partialShape2str({inputShapes.first}) << "_"; + result << "TS="; + for (const auto& shape : inputShapes.second) { + result << "(" << ov::test::utils::vec2str(shape) << ")_"; + } + result << "numGroup=" << numGroup << "_"; + result << "epsilon=" << eps << "_"; + result << "#N=" << num_nodes << "_"; + result << "#S=" << num_subgraphs << "_"; + result << "targetDevice=" << targetDevice; + return result.str(); +} + +void GroupNormalization::SetUp() { + InputShape inputShape; + size_t numGroup; + float eps; + std::tie(inputShape, numGroup, eps, ref_num_nodes, ref_num_subgraphs, targetDevice) = this->GetParam(); + + InputShape scaleShiftShape = ExtractScaleShiftShape(inputShape); + + init_input_shapes({inputShape, scaleShiftShape, scaleShiftShape}); + + auto f = ov::test::snippets::GroupNormalizationFunction(inputDynamicShapes, numGroup, eps); + function = f.getOriginal(); + + if (!configuration.count("SNIPPETS_MODE")) { + configuration.insert({"SNIPPETS_MODE", "IGNORE_CALLBACK"}); + } + + abs_threshold = 1e-5; +} + +InputShape GroupNormalization::ExtractScaleShiftShape(const InputShape& shape) { + std::vector biasShape; + std::transform(shape.second.cbegin(), shape.second.cend(), std::back_inserter(biasShape), + [](const ov::Shape& s)->ov::Shape { + OPENVINO_ASSERT(s.size() >= 2, "First input rank for group normalization op should be greater than 1"); + return {s[1]}; + }); + InputShape biasInputShape { + shape.first.is_dynamic() ? ov::PartialShape{shape.first[1]} : shape.first, + std::move(biasShape) + }; + return biasInputShape; +} + +TEST_P(GroupNormalization, CompareWithRefImpl) { + SKIP_IF_CURRENT_TEST_IS_DISABLED() + run(); + validateNumSubgraphs(); +} + +} // namespace snippets +} // namespace test +} // namespace ov diff --git a/src/tests/ov_helpers/ov_snippets_models/include/subgraph_group_normalization.hpp b/src/tests/ov_helpers/ov_snippets_models/include/subgraph_group_normalization.hpp new file mode 100644 index 00000000000..742c44a5501 --- /dev/null +++ b/src/tests/ov_helpers/ov_snippets_models/include/subgraph_group_normalization.hpp @@ -0,0 +1,65 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "snippets_helpers.hpp" + +namespace ov { +namespace test { +namespace snippets { + +/* Graph with example shape propogation: + * Graph between Reshape[1,2,1,8] and Reshape[1,2,2,4] is a MVN + * Parameter[1,4,2,2], group_num is 2 + * | + * Reshape[1,2,1,8] + * | | + * | ReduceSum[1,2,1,1] Scalar + * | | / + * | Multiply[1,2,1,1] + * | / + * Substract[1,2,1,8] + * | | + * | PowerStatic[1,2,1,8] + * | | + * | ReduceSum[1,2,1,1] + * | | + * | FMA(Multiply+Add)[1,2,1,1] + * | | + * | sqrt[1,2,1,1] + * | | + * | PowerStatic[1,2,1,1] + * | / + * Multiply[1,2,1,8] Parameter[4] Parameter[4] + * | | | + * Reshape[1,2,2,4] Reshape[1,2,2,1] Reshape[1,2,2,1] + * \ | / + * \ | / + * FMA(Multiply+Add)[1,2,2,4] + * | + * Reshape[1,4,2,2] + * | + * Result[1,4,2,2] + */ +class GroupNormalizationFunction : public SnippetsFunctionBase { +public: + explicit GroupNormalizationFunction(const std::vector& inputShapes, const size_t& numGroup, const float& eps) + : SnippetsFunctionBase(inputShapes), num_groups(numGroup), epsilon(eps) { + OPENVINO_ASSERT(input_shapes.size() == 3, "Got invalid number of input shapes"); + } + +protected: + std::shared_ptr initOriginal() const override; + std::shared_ptr initReference() const override; + std::shared_ptr initLowered() const override; + +private: + size_t num_groups; + float epsilon; +}; + +} // namespace snippets +} // namespace test +} // namespace ov diff --git a/src/tests/ov_helpers/ov_snippets_models/src/subgraph_group_normalization.cpp b/src/tests/ov_helpers/ov_snippets_models/src/subgraph_group_normalization.cpp new file mode 100644 index 00000000000..e669200b008 --- /dev/null +++ b/src/tests/ov_helpers/ov_snippets_models/src/subgraph_group_normalization.cpp @@ -0,0 +1,99 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "subgraph_group_normalization.hpp" +#include + +namespace ov { +namespace test { +namespace snippets { + +std::shared_ptr GroupNormalizationFunction::initOriginal() const { + auto data = std::make_shared(precision, input_shapes[0]); + auto scale = std::make_shared(precision, input_shapes[1]); + auto shift = std::make_shared(precision, input_shapes[2]); + const auto groupNormalization = std::make_shared(data, scale, shift, num_groups, epsilon); + return std::make_shared(NodeVector{groupNormalization}, ParameterVector{data, scale, shift}); +} + +std::shared_ptr GroupNormalizationFunction::initReference() const { + auto data = std::make_shared(precision, input_shapes[0]); + auto scale = std::make_shared(precision, input_shapes[1]); + auto shift = std::make_shared(precision, input_shapes[2]); + auto data_ = std::make_shared(precision, input_shapes[0]); + auto scale_ = std::make_shared(precision, input_shapes[1]); + auto shift_ = std::make_shared(precision, input_shapes[2]); + const auto groupNormalization = std::make_shared(data_, scale_, shift_, num_groups, epsilon); + + auto subgraph = std::make_shared(NodeVector{data, scale, shift}, + std::make_shared(NodeVector{groupNormalization}, ParameterVector{data_, scale_, shift_})); + + return std::make_shared(NodeVector{subgraph}, ParameterVector{data, scale, shift}); +} + +std::shared_ptr GroupNormalizationFunction::initLowered() const { + auto data = std::make_shared(precision, input_shapes[0]); + auto scale = std::make_shared(precision, input_shapes[1]); + auto bias = std::make_shared(precision, input_shapes[2]); + + // reshape [N, C, spatial] to [N, group, 1, (C / group) * spatial] + const auto orig_shape = input_shapes[0].to_shape(); + size_t orig_rank = orig_shape.size(); + size_t group_rank = 4; + size_t c_in_group = orig_shape[1] / num_groups; + size_t spatial_dim = 1; + for (size_t i = 2; i < orig_rank; ++i) { + spatial_dim = spatial_dim * orig_shape[i]; + } + ov::Shape group_shape = {orig_shape[0], num_groups, 1ul, c_in_group * spatial_dim}; + std::shared_ptr reshaped_node_orig = std::make_shared(data, group_shape); + const auto reduce_sum = std::make_shared(reshaped_node_orig, group_rank - 1); + + // reduceMean + float group_size_inv = 1.0f / static_cast(group_shape[3]); + // scalar const -> scalar in data_flow_optimization. + const auto group_size_inv_node = std::make_shared(element::f32, Shape{1}, group_size_inv); + const auto reduce_mean = std::make_shared(reduce_sum, group_size_inv_node); + + // x - mean + std::shared_ptr reshaped_node2 = reshaped_node_orig; + auto sub_mean = std::make_shared(reshaped_node2, reduce_mean); + // (x - mean) ^ 2 + // power -> poweStatic in data_flow_optimization + auto sqr = std::make_shared(sub_mean, 2.0f); + // reduceSum((x - mean) ^ 2) + auto sqr_reduce_sum = std::make_shared(sqr, group_rank - 1); + // reduceMean((x - mean) ^ 2) + const auto group_size_inv_node_aux = std::make_shared(element::f32, Shape{1}, group_size_inv); + auto sqr_mean = std::make_shared(sqr_reduce_sum, group_size_inv_node_aux); + // reduceMean((x - mean) ^ 2) + eps + auto eps_node = std::make_shared(element::f32, Shape{1}, epsilon); + auto eps_add = std::make_shared(sqr_mean, eps_node); + // variance = sqrt( reducemean( (x - mean) ^ 2 ) + eps ) + auto variance = std::make_shared(eps_add); + // divide variance + const auto variance_inv = std::make_shared(variance, -1.f); + auto mvn = std::make_shared(sub_mean, variance_inv); + + // reshape mvn from [N, group, 1, (C / group) * spatial] to [N, group, C / group, spatial] + ov::Shape group_channel_shape = {orig_shape[0], num_groups, c_in_group, spatial_dim}; + const auto mvn_reshaped = std::make_shared(mvn, group_channel_shape); + + // reshape scale and bias to [1, group, C / group, 1] + ov::Shape scale_bias_shape = {1ul, num_groups, c_in_group, 1ul}; + std::shared_ptr reshape_scale = std::make_shared(scale, scale_bias_shape); + std::shared_ptr reshape_bias = std::make_shared(bias, scale_bias_shape); + + auto scaled_node = std::make_shared(mvn_reshaped, reshape_scale); + auto biased_node = std::make_shared(scaled_node, reshape_bias); + + // reshape_back [N, group, C / group, spatial] to [N, C, spatial] + const auto reshape_back_node = std::make_shared(biased_node, orig_shape); + + return std::make_shared(NodeVector{reshape_back_node}, ParameterVector{data, scale, bias}); +} + +} // namespace snippets +} // namespace test +} // namespace ov