Improve L2NormFusion transformation (#11765)

This commit is contained in:
Mateusz Bencer authored and GitHub committed 2022-06-03 18:51:21 +02:00
1 parent 4650ecd0b5
commit dba4dbb9d6
3 files changed
+170 -69

No files matched your search

@@ -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:
@@ -22,14 +22,24 @@ ngraph::pass::NormalizeL2Fusion::NormalizeL2Fusion() {
auto pow = std::make_shared<ngraph::opset8::Power>(input, exp);
auto axes = ngraph::pattern::wrap_type<ngraph::opset8::Constant>();
auto reduce_sum = std::make_shared<ngraph::opset8::ReduceSum>(pow, axes);
auto eps_const = ngraph::pattern::wrap_type<ngraph::opset8::Constant>();
auto eps_const = ngraph::pattern::wrap_type<ngraph::opset8::Constant>();
auto max = std::make_shared<ngraph::opset8::Maximum>(reduce_sum, eps_const);
auto add = std::make_shared<ngraph::opset8::Add>(reduce_sum, eps_const);
auto max_or_add = std::make_shared<pattern::op::Or>(OutputVector{max, add});
// Sqrt can be represented by Sqrt node or as Power node with exponent 0.5
auto sqrt = std::make_shared<ngraph::opset8::Sqrt>(max_or_add);
auto divide = std::make_shared<ngraph::opset8::Divide>(input, sqrt);
auto exp2 = ngraph::pattern::wrap_type<ngraph::opset8::Constant>();
auto pow_as_sqrt = std::make_shared<ngraph::opset8::Power>(max_or_add, exp2);
auto power_or_sqrt = std::make_shared<pattern::op::Or>(OutputVector{sqrt, pow_as_sqrt});
// divide(input,sqrt(..)) can be represented as mul(input, power(..., -0.5f))
auto divide = std::make_shared<ngraph::opset8::Divide>(input, power_or_sqrt);
auto exp3 = ngraph::pattern::wrap_type<ngraph::opset8::Constant>();
auto reversed_pow_as_sqrt = std::make_shared<ngraph::opset8::Power>(max_or_add, exp3);
auto mul = std::make_shared<ngraph::opset8::Multiply>(input, reversed_pow_as_sqrt);
auto divide_or_mul = std::make_shared<pattern::op::Or>(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<ngraph::opset8::Constant>(pattern_to_output.at(axes).get_node_shared_ptr());
const auto eps_attr =
std::dynamic_pointer_cast<ngraph::opset8::Constant>(pattern_to_output.at(eps_const).get_node_shared_ptr());
const auto exp2_input =
pattern_to_output.count(exp2)
? std::dynamic_pointer_cast<ngraph::opset8::Constant>(pattern_to_output.at(exp2).get_node_shared_ptr())
: nullptr;
const auto exp3_input =
pattern_to_output.count(exp3)
? std::dynamic_pointer_cast<ngraph::opset8::Constant>(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<float>(exp_input, 2.0f)) {
return false;
}
const bool is_square_pow = op::util::has_constant_value<float>(exp_input, 2.0f);
if (!is_square_pow) {
if (exp2_input && !op::util::has_constant_value<float>(exp2_input, 0.5f)) {
return false;
}
if (shape_size(eps_attr->get_shape()) > 1) {
if (exp3_input && !op::util::has_constant_value<float>(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<float>()[0];
op::EpsMode mode;
Output<Node> eps_node;
if (pattern_to_output.count(max)) {
@@ -68,20 +90,39 @@ ngraph::pass::NormalizeL2Fusion::NormalizeL2Fusion() {
}
auto normalize_l2 = std::make_shared<ngraph::opset8::NormalizeL2>(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<ngraph::pattern::Matcher>(divide, matcher_name);
auto m = std::make_shared<ngraph::pattern::Matcher>(divide_or_mul, matcher_name);
register_matcher(m, callback);
}
@@ -32,7 +32,6 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithMax) {
auto divide = std::make_shared<ngraph::opset4::Divide>(input, sqrt);
function = std::make_shared<ngraph::Function>(ngraph::NodeVector{divide}, ngraph::ParameterVector{input});
manager.register_pass<ngraph::pass::NormalizeL2Fusion>();
}
@@ -43,6 +42,8 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithMax) {
function_ref = std::make_shared<ngraph::Function>(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<ngraph::pass::NormalizeL2Fusion>();
}
{
auto input = std::make_shared<ngraph::opset4::Parameter>(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<ngraph::opset4::Power>(input, exp);
auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{1}, {0});
auto reduce_sum = std::make_shared<ngraph::opset4::ReduceSum>(pow, axes_const);
auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value});
auto max = std::make_shared<ngraph::opset4::Maximum>(reduce_sum, eps_const);
auto sqrt = std::make_shared<ngraph::opset4::Sqrt>(max);
auto divide = std::make_shared<ngraph::opset4::Divide>(input, sqrt);
function_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{divide}, ngraph::ParameterVector{input});
}
}
TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxIncorrectEpsValueShape) {
@@ -94,20 +81,6 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxIncorrectEpsValueShape) {
manager.register_pass<ngraph::pass::NormalizeL2Fusion>();
}
{
auto input = std::make_shared<ngraph::opset4::Parameter>(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<ngraph::opset4::Power>(input, exp);
auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{1}, {0});
auto reduce_sum = std::make_shared<ngraph::opset4::ReduceSum>(pow, axes_const);
auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{2}, {1, 2});
auto max = std::make_shared<ngraph::opset4::Maximum>(reduce_sum, eps_const);
auto sqrt = std::make_shared<ngraph::opset4::Sqrt>(max);
auto divide = std::make_shared<ngraph::opset4::Divide>(input, sqrt);
function_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{divide}, ngraph::ParameterVector{input});
}
}
TEST_F(TransformationTestsF, NormalizeL2FusionWithAdd) {
@@ -135,6 +108,8 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithAdd) {
function_ref = std::make_shared<ngraph::Function>(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<ngraph::opset4::Divide>(input, sqrt);
function = std::make_shared<ngraph::Function>(ngraph::NodeVector{divide}, ngraph::ParameterVector{input});
manager.register_pass<ngraph::pass::NormalizeL2Fusion>();
}
{
auto input = std::make_shared<ngraph::opset4::Parameter>(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<ngraph::opset4::Power>(input, exp);
auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1});
auto reduce_sum = std::make_shared<ngraph::opset4::ReduceSum>(pow, axes_const);
auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value});
auto add = std::make_shared<ngraph::opset4::Add>(reduce_sum, eps_const);
auto sqrt = std::make_shared<ngraph::opset4::Sqrt>(add);
auto divide = std::make_shared<ngraph::opset4::Divide>(input, sqrt);
function_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{divide}, ngraph::ParameterVector{input});
}
}
TEST_F(TransformationTestsF, NormalizeL2FusionWithAddIncorrectEpsValueShape) {
@@ -183,21 +143,116 @@ TEST_F(TransformationTestsF, NormalizeL2FusionWithAddIncorrectEpsValueShape) {
auto divide = std::make_shared<ngraph::opset4::Divide>(input, sqrt);
function = std::make_shared<ngraph::Function>(ngraph::NodeVector{divide}, ngraph::ParameterVector{input});
manager.register_pass<ngraph::pass::NormalizeL2Fusion>();
}
}
TEST_F(TransformationTestsF, NormalizeL2FusionWithMaxMul) {
const float eps_value = 0.000099f;
{
auto input =
std::make_shared<ngraph::opset4::Parameter>(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<ngraph::opset4::Power>(input, exp);
auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1});
auto reduce_sum = std::make_shared<ngraph::opset4::ReduceSum>(pow, axes_const);
auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value});
auto max = std::make_shared<ngraph::opset4::Maximum>(reduce_sum, eps_const);
auto power_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {-0.5f});
auto unsqrt = std::make_shared<ngraph::opset8::Power>(max, power_const);
auto mul = std::make_shared<ngraph::opset4::Multiply>(input, unsqrt);
function = std::make_shared<ngraph::Function>(ngraph::NodeVector{mul}, ngraph::ParameterVector{input});
manager.register_pass<ngraph::pass::NormalizeL2Fusion>();
}
{
auto input = std::make_shared<ngraph::opset4::Parameter>(ngraph::element::f16, ngraph::PartialShape::dynamic(4));
auto input =
std::make_shared<ngraph::opset4::Parameter>(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<ngraph::opset4::NormalizeL2>(input, axes_const, eps_value, ngraph::op::EpsMode::MAX);
function_ref =
std::make_shared<ngraph::Function>(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::opset4::Parameter>(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<ngraph::opset4::Power>(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<ngraph::opset4::ReduceSum>(pow, axes_const);
auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{2}, {1, 2});
auto add = std::make_shared<ngraph::opset4::Add>(reduce_sum, eps_const);
auto sqrt = std::make_shared<ngraph::opset4::Sqrt>(add);
auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value});
auto max = std::make_shared<ngraph::opset4::Maximum>(reduce_sum, eps_const);
auto power_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {-0.6f});
auto unsqrt = std::make_shared<ngraph::opset8::Power>(max, power_const);
auto mul = std::make_shared<ngraph::opset4::Multiply>(input, unsqrt);
function = std::make_shared<ngraph::Function>(ngraph::NodeVector{mul}, ngraph::ParameterVector{input});
manager.register_pass<ngraph::pass::NormalizeL2Fusion>();
}
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::opset4::Parameter>(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<ngraph::opset4::Power>(input, exp);
auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1});
auto reduce_sum = std::make_shared<ngraph::opset4::ReduceSum>(pow, axes_const);
auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value});
auto max = std::make_shared<ngraph::opset4::Maximum>(reduce_sum, eps_const);
auto sqrt_exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {0.5f});
auto sqrt = std::make_shared<ngraph::opset4::Power>(max, sqrt_exp);
auto divide = std::make_shared<ngraph::opset4::Divide>(input, sqrt);
function_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{divide}, ngraph::ParameterVector{input});
function = std::make_shared<ngraph::Function>(ngraph::NodeVector{divide}, ngraph::ParameterVector{input});
manager.register_pass<ngraph::pass::NormalizeL2Fusion>();
}
{
auto input =
std::make_shared<ngraph::opset4::Parameter>(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<ngraph::opset4::NormalizeL2>(input, axes_const, eps_value, ngraph::op::EpsMode::MAX);
function_ref =
std::make_shared<ngraph::Function>(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::opset4::Parameter>(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<ngraph::opset4::Power>(input, exp);
auto axes_const = ngraph::opset4::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {0, 1});
auto reduce_sum = std::make_shared<ngraph::opset4::ReduceSum>(pow, axes_const);
auto eps_const = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {eps_value});
auto max = std::make_shared<ngraph::opset4::Maximum>(reduce_sum, eps_const);
auto sqrt_exp = ngraph::opset4::Constant::create(ngraph::element::f16, ngraph::Shape{}, {0.9f});
auto sqrt = std::make_shared<ngraph::opset4::Power>(max, sqrt_exp);
auto divide = std::make_shared<ngraph::opset4::Divide>(input, sqrt);
function = std::make_shared<ngraph::Function>(ngraph::NodeVector{divide}, ngraph::ParameterVector{input});
manager.register_pass<ngraph::pass::NormalizeL2Fusion>();
}
}