diff --git a/src/common/transformations/include/transformations/common_optimizations/normalize_l2_fusion.hpp b/src/common/transformations/include/transformations/common_optimizations/normalize_l2_fusion.hpp index db55a0974f3..e2bf4034338 100644 --- a/src/common/transformations/include/transformations/common_optimizations/normalize_l2_fusion.hpp +++ b/src/common/transformations/include/transformations/common_optimizations/normalize_l2_fusion.hpp @@ -21,9 +21,14 @@ class TRANSFORMATIONS_API NormalizeL2Fusion; /** * @ingroup ie_transformation_common_api - * @brief NormalizeL2Fusion transformation replaces various sub-graphs with a NormalizeL2 op: - * x/(max(sqrt(sum(x[j0, ..., jN]**2), eps)) with a NormalizeL2 op. - * x/(add(sqrt(sum(x[j0, ..., jN]**2), eps)) with a NormalizeL2 op. + * @brief NormalizeL2Fusion transformation replaces sub-graphs: + * x/(sqrt(max(reduce_sum(x[j0, ..., jN]**2, axes), eps)) + * x/(sqrt(add(reduce_sum(x[j0, ..., jN]**2, axes), eps)) + * x/(pow(max(reduce_sum(x[j0, ..., jN]**2, axes), eps), 0.5) + * x/(pow(add(reduce_sum(x[j0, ..., jN]**2, axes), eps), 0.5) + * x*(pow(max(reduce_sum(x[j0, ..., jN]**2, axes), eps), -0.5) + * x*(pow(add(reduce_sum(x[j0, ..., jN]**2, axes), eps), -0.5) + * with a NormalizeL2(x, axes, eps, eps_mode[MAX|ADD]) op */ class ngraph::pass::NormalizeL2Fusion : public ngraph::pass::MatcherPass { public: diff --git a/src/common/transformations/src/transformations/common_optimizations/normalize_l2_fusion.cpp b/src/common/transformations/src/transformations/common_optimizations/normalize_l2_fusion.cpp index 14a493b3ec2..a378e972f5b 100644 --- a/src/common/transformations/src/transformations/common_optimizations/normalize_l2_fusion.cpp +++ b/src/common/transformations/src/transformations/common_optimizations/normalize_l2_fusion.cpp @@ -22,14 +22,24 @@ ngraph::pass::NormalizeL2Fusion::NormalizeL2Fusion() { auto pow = std::make_shared(input, exp); auto axes = ngraph::pattern::wrap_type(); auto reduce_sum = std::make_shared(pow, axes); - auto eps_const = ngraph::pattern::wrap_type(); + auto eps_const = ngraph::pattern::wrap_type(); auto max = std::make_shared(reduce_sum, eps_const); auto add = std::make_shared(reduce_sum, eps_const); auto max_or_add = std::make_shared(OutputVector{max, add}); + // Sqrt can be represented by Sqrt node or as Power node with exponent 0.5 auto sqrt = std::make_shared(max_or_add); - auto divide = std::make_shared(input, sqrt); + auto exp2 = ngraph::pattern::wrap_type(); + auto pow_as_sqrt = std::make_shared(max_or_add, exp2); + auto power_or_sqrt = std::make_shared(OutputVector{sqrt, pow_as_sqrt}); + + // divide(input,sqrt(..)) can be represented as mul(input, power(..., -0.5f)) + auto divide = std::make_shared(input, power_or_sqrt); + auto exp3 = ngraph::pattern::wrap_type(); + auto reversed_pow_as_sqrt = std::make_shared(max_or_add, exp3); + auto mul = std::make_shared(input, reversed_pow_as_sqrt); + auto divide_or_mul = std::make_shared(OutputVector{divide, mul}); ngraph::matcher_pass_callback callback = [=](ngraph::pattern::Matcher& m) { const auto& pattern_to_output = m.get_pattern_value_map(); @@ -41,20 +51,32 @@ ngraph::pass::NormalizeL2Fusion::NormalizeL2Fusion() { std::dynamic_pointer_cast(pattern_to_output.at(axes).get_node_shared_ptr()); const auto eps_attr = std::dynamic_pointer_cast(pattern_to_output.at(eps_const).get_node_shared_ptr()); + const auto exp2_input = + pattern_to_output.count(exp2) + ? std::dynamic_pointer_cast(pattern_to_output.at(exp2).get_node_shared_ptr()) + : nullptr; + const auto exp3_input = + pattern_to_output.count(exp3) + ? std::dynamic_pointer_cast(pattern_to_output.at(exp3).get_node_shared_ptr()) + : nullptr; - if (!exp_input || !axes_input || !eps_attr) { + if (exp_input && !op::util::has_constant_value(exp_input, 2.0f)) { return false; } - const bool is_square_pow = op::util::has_constant_value(exp_input, 2.0f); - if (!is_square_pow) { + if (exp2_input && !op::util::has_constant_value(exp2_input, 0.5f)) { return false; } - if (shape_size(eps_attr->get_shape()) > 1) { + + if (exp3_input && !op::util::has_constant_value(exp3_input, -0.5f)) { return false; } + + if (!eps_attr || shape_size(eps_attr->get_shape()) > 1) { + return false; + } + const auto eps_attr_value = eps_attr->cast_vector()[0]; - op::EpsMode mode; Output eps_node; if (pattern_to_output.count(max)) { @@ -68,20 +90,39 @@ ngraph::pass::NormalizeL2Fusion::NormalizeL2Fusion() { } auto normalize_l2 = std::make_shared(data_input, axes_input, eps_attr_value, mode); - if (transformation_callback(normalize_l2)) + if (transformation_callback(normalize_l2)) { return false; - + } normalize_l2->set_friendly_name(m.get_match_root()->get_friendly_name()); - ngraph::copy_runtime_info({pattern_to_output.at(pow).get_node_shared_ptr(), - pattern_to_output.at(reduce_sum).get_node_shared_ptr(), - pattern_to_output.at(sqrt).get_node_shared_ptr(), - pattern_to_output.at(divide).get_node_shared_ptr(), - eps_node.get_node_shared_ptr()}, - normalize_l2); + + OutputVector outputs_to_replace{pattern_to_output.at(pow), pattern_to_output.at(reduce_sum), eps_node}; + if (pattern_to_output.count(mul)) { + outputs_to_replace.emplace_back(pattern_to_output.at(mul)); + } + if (pattern_to_output.count(divide)) { + outputs_to_replace.emplace_back(pattern_to_output.at(divide)); + } + if (pattern_to_output.count(sqrt)) { + outputs_to_replace.emplace_back(pattern_to_output.at(sqrt)); + } + if (pattern_to_output.count(pow_as_sqrt)) { + outputs_to_replace.emplace_back(pattern_to_output.at(pow_as_sqrt)); + } + if (pattern_to_output.count(reversed_pow_as_sqrt)) { + outputs_to_replace.emplace_back(pattern_to_output.at(reversed_pow_as_sqrt)); + } + if (pattern_to_output.count(max)) { + outputs_to_replace.emplace_back(pattern_to_output.at(max)); + } + if (pattern_to_output.count(add)) { + outputs_to_replace.emplace_back(pattern_to_output.at(add)); + } + + ngraph::copy_runtime_info(as_node_vector(outputs_to_replace), normalize_l2); ngraph::replace_node(m.get_match_root(), normalize_l2); return true; }; - auto m = std::make_shared(divide, matcher_name); + auto m = std::make_shared(divide_or_mul, matcher_name); register_matcher(m, callback); } diff --git a/src/tests/functional/inference_engine/transformations/common_optimizations/normalize_l2_fusion_test.cpp b/src/tests/functional/inference_engine/transformations/common_optimizations/normalize_l2_fusion_test.cpp index 6813a28ccb6..72897ce5822 100644 --- a/src/tests/functional/inference_engine/transformations/common_optimizations/normalize_l2_fusion_test.cpp +++ b/src/tests/functional/inference_engine/transformations/common_optimizations/normalize_l2_fusion_test.cpp @@ -32,7 +32,6 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithMax) { auto divide = std::make_shared(input, sqrt); function = std::make_shared(ngraph::NodeVector{divide}, ngraph::ParameterVector{input}); - manager.register_pass(); } @@ -43,6 +42,8 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithMax) { function_ref = std::make_shared(ngraph::NodeVector{normalize_l2}, ngraph::ParameterVector{input}); } + comparator.enable(FunctionsComparator::CmpValues::ACCURACY); + comparator.enable(FunctionsComparator::CmpValues::CONST_VALUES); } TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxIncorrectExp) { @@ -62,20 +63,6 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxIncorrectExp) { manager.register_pass(); } - - { - auto input = std::make_shared(ngraph::element::f16, ngraph::PartialShape::dynamic(2)); - auto exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {3.f}); - auto pow = std::make_shared(input, exp); - auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{1}, {0}); - auto reduce_sum = std::make_shared(pow, axes_const); - auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value}); - auto max = std::make_shared(reduce_sum, eps_const); - auto sqrt = std::make_shared(max); - auto divide = std::make_shared(input, sqrt); - - function_ref = std::make_shared(ngraph::NodeVector{divide}, ngraph::ParameterVector{input}); - } } TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxIncorrectEpsValueShape) { @@ -94,20 +81,6 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxIncorrectEpsValueShape) { manager.register_pass(); } - - { - auto input = std::make_shared(ngraph::element::f16, ngraph::PartialShape::dynamic(2)); - auto exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {2.f}); - auto pow = std::make_shared(input, exp); - auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{1}, {0}); - auto reduce_sum = std::make_shared(pow, axes_const); - auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{2}, {1, 2}); - auto max = std::make_shared(reduce_sum, eps_const); - auto sqrt = std::make_shared(max); - auto divide = std::make_shared(input, sqrt); - - function_ref = std::make_shared(ngraph::NodeVector{divide}, ngraph::ParameterVector{input}); - } } TEST_F(TransformationTestsF, NormalizeL2FusionWithAdd) { @@ -135,6 +108,8 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithAdd) { function_ref = std::make_shared(ngraph::NodeVector{normalize_l2}, ngraph::ParameterVector{input}); } + comparator.enable(FunctionsComparator::CmpValues::ACCURACY); + comparator.enable(FunctionsComparator::CmpValues::CONST_VALUES); } TEST_F(TransformationTestsF, NormalizeL2FusionWithAddIncorrectExp) { @@ -151,23 +126,8 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithAddIncorrectExp) { auto divide = std::make_shared(input, sqrt); function = std::make_shared(ngraph::NodeVector{divide}, ngraph::ParameterVector{input}); - manager.register_pass(); } - - { - auto input = std::make_shared(ngraph::element::f16, ngraph::PartialShape::dynamic(2)); - auto exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {1.9f}); - auto pow = std::make_shared(input, exp); - auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1}); - auto reduce_sum = std::make_shared(pow, axes_const); - auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value}); - auto add = std::make_shared(reduce_sum, eps_const); - auto sqrt = std::make_shared(add); - auto divide = std::make_shared(input, sqrt); - - function_ref = std::make_shared(ngraph::NodeVector{divide}, ngraph::ParameterVector{input}); - } } TEST_F(TransformationTestsF, NormalizeL2FusionWithAddIncorrectEpsValueShape) { @@ -183,21 +143,116 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithAddIncorrectEpsValueShape) { auto divide = std::make_shared(input, sqrt); function = std::make_shared(ngraph::NodeVector{divide}, ngraph::ParameterVector{input}); + manager.register_pass(); + } +} +TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxMul) { + const float eps_value = 0.000099f; + { + auto input = + std::make_shared(ngraph::element::f16, ngraph::Shape{1, 2, 3}); + auto exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {2.f}); + auto pow = std::make_shared(input, exp); + auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1}); + auto reduce_sum = std::make_shared(pow, axes_const); + auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value}); + auto max = std::make_shared(reduce_sum, eps_const); + auto power_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {-0.5f}); + auto unsqrt = std::make_shared(max, power_const); + auto mul = std::make_shared(input, unsqrt); + + function = std::make_shared(ngraph::NodeVector{mul}, ngraph::ParameterVector{input}); manager.register_pass(); } { - auto input = std::make_shared(ngraph::element::f16, ngraph::PartialShape::dynamic(4)); + auto input = + std::make_shared(ngraph::element::f16, ngraph::Shape{1, 2, 3}); + auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1}); + auto normalize_l2 = + std::make_shared(input, axes_const, eps_value, ngraph::op::EpsMode::MAX); + + function_ref = + std::make_shared(ngraph::NodeVector{normalize_l2}, ngraph::ParameterVector{input}); + } + comparator.enable(FunctionsComparator::CmpValues::ACCURACY); + comparator.enable(FunctionsComparator::CmpValues::CONST_VALUES); +} + +TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxMulIncorrectSecondExp) { + const float eps_value = 0.000099f; + { + auto input = + std::make_shared(ngraph::element::f16, ngraph::PartialShape::dynamic(3)); auto exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {2.f}); auto pow = std::make_shared(input, exp); - auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{1}, {0}); + auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1}); auto reduce_sum = std::make_shared(pow, axes_const); - auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{2}, {1, 2}); - auto add = std::make_shared(reduce_sum, eps_const); - auto sqrt = std::make_shared(add); + auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value}); + auto max = std::make_shared(reduce_sum, eps_const); + auto power_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {-0.6f}); + auto unsqrt = std::make_shared(max, power_const); + auto mul = std::make_shared(input, unsqrt); + + function = std::make_shared(ngraph::NodeVector{mul}, ngraph::ParameterVector{input}); + + manager.register_pass(); + } + comparator.enable(FunctionsComparator::CmpValues::ACCURACY); + comparator.enable(FunctionsComparator::CmpValues::CONST_VALUES); +} + +TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxSqrtAsPower) { + const float eps_value = 0.000099f; + { + auto input = + std::make_shared(ngraph::element::f16, ngraph::PartialShape::dynamic(3)); + auto exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {2.f}); + auto pow = std::make_shared(input, exp); + auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1}); + auto reduce_sum = std::make_shared(pow, axes_const); + auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value}); + auto max = std::make_shared(reduce_sum, eps_const); + auto sqrt_exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {0.5f}); + auto sqrt = std::make_shared(max, sqrt_exp); auto divide = std::make_shared(input, sqrt); - function_ref = std::make_shared(ngraph::NodeVector{divide}, ngraph::ParameterVector{input}); + function = std::make_shared(ngraph::NodeVector{divide}, ngraph::ParameterVector{input}); + manager.register_pass(); + } + + { + auto input = + std::make_shared(ngraph::element::f16, ngraph::PartialShape::dynamic(3)); + auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1}); + auto normalize_l2 = + std::make_shared(input, axes_const, eps_value, ngraph::op::EpsMode::MAX); + + function_ref = + std::make_shared(ngraph::NodeVector{normalize_l2}, ngraph::ParameterVector{input}); + } + comparator.enable(FunctionsComparator::CmpValues::ACCURACY); + comparator.enable(FunctionsComparator::CmpValues::CONST_VALUES); +} + +TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxSqrtAsPowerIncorrectPowerExp) { + const float eps_value = 0.0009f; + { + auto input = + std::make_shared(ngraph::element::f16, ngraph::PartialShape::dynamic(3)); + auto exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {2.f}); + auto pow = std::make_shared(input, exp); + auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1}); + auto reduce_sum = std::make_shared(pow, axes_const); + auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value}); + auto max = std::make_shared(reduce_sum, eps_const); + auto sqrt_exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {0.9f}); + auto sqrt = std::make_shared(max, sqrt_exp); + auto divide = std::make_shared(input, sqrt); + + function = std::make_shared(ngraph::NodeVector{divide}, ngraph::ParameterVector{input}); + + manager.register_pass(); } }