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)*
This commit is contained in:
parent
4dfca61bd2
commit
feb0c7eff0
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<ov::op::v12::GroupNormalization>(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);
|
||||
|
|
|
|||
|
|
@ -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<GroupNormalizationParams> {
|
||||
public:
|
||||
static std::string getTestCaseName(testing::TestParamInfo<GroupNormalizationParams> obj);
|
||||
protected:
|
||||
void SetUp() override;
|
||||
std::shared_ptr<SnippetsFunctionBase> snippets_model;
|
||||
};
|
||||
|
||||
} // namespace snippets
|
||||
} // namespace test
|
||||
} // namespace ov
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common_test_utils/ov_test_utils.hpp>
|
||||
|
||||
#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<GroupNormalizationParams> {
|
||||
public:
|
||||
static std::string getTestCaseName(testing::TestParamInfo<GroupNormalizationParams> obj);
|
||||
protected:
|
||||
void SetUp() override;
|
||||
std::shared_ptr<GroupNormalizationFunction> snippets_model;
|
||||
};
|
||||
|
||||
} // namespace snippets
|
||||
} // namespace test
|
||||
} // namespace ov
|
||||
|
|
@ -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<ov::Node::type_info_t>&
|
|||
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<ov::Node::type_info_t>&
|
|||
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<ov::snippets::op::Subgraph> LoweringTests::getTokenizedSubgraph(
|
|||
ov::pass::Manager m;
|
||||
ov::snippets::pass::SnippetsTokenization::Config config = get_default_tokenization_config();
|
||||
m.register_pass<ov::snippets::pass::EnumerateNodes>();
|
||||
m.register_pass<ov::snippets::pass::TokenizeGNSnippets>();
|
||||
m.register_pass<ov::snippets::pass::TokenizeSnippets>(config);
|
||||
m.run_passes(f);
|
||||
// Perform lowering
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#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<GroupNormalizationParams> 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<PartialShape> input_shapes = { data_shape, scaleShiftShape, scaleShiftShape};
|
||||
snippets_model = std::make_shared<GroupNormalizationFunction>(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<ov::PartialShape> 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
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <pass/gn_tokenization.hpp>
|
||||
#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<GroupNormalizationParams> 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<PartialShape> input_shapes = { data_shape, scaleShiftShape, scaleShiftShape};
|
||||
snippets_model = std::make_shared<GroupNormalizationFunction>(input_shapes, num_group, eps);
|
||||
manager.register_pass<ov::snippets::pass::TokenizeGNSnippets>();
|
||||
}
|
||||
|
||||
TEST_P(TokenizeGNSnippetsTests, smoke_TokenizeGNSnippets) {
|
||||
model = snippets_model->getOriginal();
|
||||
model_ref = snippets_model->getReference();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const std::vector<ov::PartialShape> 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
|
||||
|
|
@ -521,24 +521,26 @@ void Transformations::PreLpt(const std::vector<ov::element::Type>& 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<const ov::op::v12::GroupNormalization>(node);
|
||||
if (!group_norm || !implication(inferencePrecision == element::undefined, group_norm->get_element_type() == element::f32))
|
||||
return false;
|
||||
const auto num_groups = static_cast<size_t>(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<const ov::op::v12::GroupNormalization>(node);
|
||||
if (!group_norm || !implication(inferencePrecision == element::undefined, group_norm->get_element_type() == element::f32))
|
||||
return false;
|
||||
const auto num_groups = static_cast<size_t>(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;
|
||||
|
|
|
|||
|
|
@ -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<ov::Shape> 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<size_t> 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
|
||||
|
|
@ -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<ov::test::snippets::GroupNormalizationParams>,
|
||||
virtual public ov::test::SnippetsTestsCommon {
|
||||
public:
|
||||
static std::string getTestCaseName(testing::TestParamInfo<ov::test::snippets::GroupNormalizationParams> obj);
|
||||
|
||||
protected:
|
||||
void SetUp() override;
|
||||
InputShape ExtractScaleShiftShape(const InputShape& shape);
|
||||
};
|
||||
|
||||
} // namespace snippets
|
||||
} // namespace test
|
||||
} // namespace ov
|
||||
|
|
@ -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<ov::test::snippets::GroupNormalizationParams> 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<ov::Shape> 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
|
||||
|
|
@ -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<PartialShape>& 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<ov::Model> initOriginal() const override;
|
||||
std::shared_ptr<ov::Model> initReference() const override;
|
||||
std::shared_ptr<ov::Model> initLowered() const override;
|
||||
|
||||
private:
|
||||
size_t num_groups;
|
||||
float epsilon;
|
||||
};
|
||||
|
||||
} // namespace snippets
|
||||
} // namespace test
|
||||
} // namespace ov
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "subgraph_group_normalization.hpp"
|
||||
#include <snippets/op/subgraph.hpp>
|
||||
|
||||
namespace ov {
|
||||
namespace test {
|
||||
namespace snippets {
|
||||
|
||||
std::shared_ptr<ov::Model> GroupNormalizationFunction::initOriginal() const {
|
||||
auto data = std::make_shared<op::v0::Parameter>(precision, input_shapes[0]);
|
||||
auto scale = std::make_shared<op::v0::Parameter>(precision, input_shapes[1]);
|
||||
auto shift = std::make_shared<op::v0::Parameter>(precision, input_shapes[2]);
|
||||
const auto groupNormalization = std::make_shared<ov::op::v12::GroupNormalization>(data, scale, shift, num_groups, epsilon);
|
||||
return std::make_shared<ov::Model>(NodeVector{groupNormalization}, ParameterVector{data, scale, shift});
|
||||
}
|
||||
|
||||
std::shared_ptr<ov::Model> GroupNormalizationFunction::initReference() const {
|
||||
auto data = std::make_shared<op::v0::Parameter>(precision, input_shapes[0]);
|
||||
auto scale = std::make_shared<op::v0::Parameter>(precision, input_shapes[1]);
|
||||
auto shift = std::make_shared<op::v0::Parameter>(precision, input_shapes[2]);
|
||||
auto data_ = std::make_shared<op::v0::Parameter>(precision, input_shapes[0]);
|
||||
auto scale_ = std::make_shared<op::v0::Parameter>(precision, input_shapes[1]);
|
||||
auto shift_ = std::make_shared<op::v0::Parameter>(precision, input_shapes[2]);
|
||||
const auto groupNormalization = std::make_shared<ov::op::v12::GroupNormalization>(data_, scale_, shift_, num_groups, epsilon);
|
||||
|
||||
auto subgraph = std::make_shared<ov::snippets::op::Subgraph>(NodeVector{data, scale, shift},
|
||||
std::make_shared<ov::Model>(NodeVector{groupNormalization}, ParameterVector{data_, scale_, shift_}));
|
||||
|
||||
return std::make_shared<ov::Model>(NodeVector{subgraph}, ParameterVector{data, scale, shift});
|
||||
}
|
||||
|
||||
std::shared_ptr<ov::Model> GroupNormalizationFunction::initLowered() const {
|
||||
auto data = std::make_shared<op::v0::Parameter>(precision, input_shapes[0]);
|
||||
auto scale = std::make_shared<op::v0::Parameter>(precision, input_shapes[1]);
|
||||
auto bias = std::make_shared<op::v0::Parameter>(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<ov::Node> reshaped_node_orig = std::make_shared<ov::snippets::op::Reshape>(data, group_shape);
|
||||
const auto reduce_sum = std::make_shared<ov::snippets::op::ReduceSum>(reshaped_node_orig, group_rank - 1);
|
||||
|
||||
// reduceMean
|
||||
float group_size_inv = 1.0f / static_cast<float>(group_shape[3]);
|
||||
// scalar const -> scalar in data_flow_optimization.
|
||||
const auto group_size_inv_node = std::make_shared<ov::snippets::op::Scalar>(element::f32, Shape{1}, group_size_inv);
|
||||
const auto reduce_mean = std::make_shared<ov::op::v1::Multiply>(reduce_sum, group_size_inv_node);
|
||||
|
||||
// x - mean
|
||||
std::shared_ptr<ov::Node> reshaped_node2 = reshaped_node_orig;
|
||||
auto sub_mean = std::make_shared<ov::op::v1::Subtract>(reshaped_node2, reduce_mean);
|
||||
// (x - mean) ^ 2
|
||||
// power -> poweStatic in data_flow_optimization
|
||||
auto sqr = std::make_shared<ov::snippets::op::PowerStatic>(sub_mean, 2.0f);
|
||||
// reduceSum((x - mean) ^ 2)
|
||||
auto sqr_reduce_sum = std::make_shared<ov::snippets::op::ReduceSum>(sqr, group_rank - 1);
|
||||
// reduceMean((x - mean) ^ 2)
|
||||
const auto group_size_inv_node_aux = std::make_shared<ov::snippets::op::Scalar>(element::f32, Shape{1}, group_size_inv);
|
||||
auto sqr_mean = std::make_shared<ov::op::v1::Multiply>(sqr_reduce_sum, group_size_inv_node_aux);
|
||||
// reduceMean((x - mean) ^ 2) + eps
|
||||
auto eps_node = std::make_shared<ov::snippets::op::Scalar>(element::f32, Shape{1}, epsilon);
|
||||
auto eps_add = std::make_shared<ov::op::v1::Add>(sqr_mean, eps_node);
|
||||
// variance = sqrt( reducemean( (x - mean) ^ 2 ) + eps )
|
||||
auto variance = std::make_shared<ov::op::v0::Sqrt>(eps_add);
|
||||
// divide variance
|
||||
const auto variance_inv = std::make_shared<ov::snippets::op::PowerStatic>(variance, -1.f);
|
||||
auto mvn = std::make_shared<ov::op::v1::Multiply>(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<ov::snippets::op::Reshape>(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<ov::Node> reshape_scale = std::make_shared<ov::snippets::op::Reshape>(scale, scale_bias_shape);
|
||||
std::shared_ptr<ov::Node> reshape_bias = std::make_shared<ov::snippets::op::Reshape>(bias, scale_bias_shape);
|
||||
|
||||
auto scaled_node = std::make_shared<ov::op::v1::Multiply>(mvn_reshaped, reshape_scale);
|
||||
auto biased_node = std::make_shared<ov::op::v1::Add>(scaled_node, reshape_bias);
|
||||
|
||||
// reshape_back [N, group, C / group, spatial] to [N, C, spatial]
|
||||
const auto reshape_back_node = std::make_shared<ov::snippets::op::Reshape>(biased_node, orig_shape);
|
||||
|
||||
return std::make_shared<ov::Model>(NodeVector{reshape_back_node}, ParameterVector{data, scale, bias});
|
||||
}
|
||||
|
||||
} // namespace snippets
|
||||
} // namespace test
|
||||
} // namespace ov
|
||||
Loading…
Reference in New Issue