diff --git a/src/common/low_precision_transformations/include/low_precision/fold_fake_quantize.hpp b/src/common/low_precision_transformations/include/low_precision/fold_fake_quantize.hpp index 6f5ca263152..8ea67f4bcc9 100644 --- a/src/common/low_precision_transformations/include/low_precision/fold_fake_quantize.hpp +++ b/src/common/low_precision_transformations/include/low_precision/fold_fake_quantize.hpp @@ -26,6 +26,7 @@ public: bool transform(TransformationContext& context, ngraph::pattern::Matcher &m) override; bool canBeTransformed(const TransformationContext& context, std::shared_ptr layer) const override; bool isPrecisionPreserved(std::shared_ptr layer) const noexcept override; + bool isConstantOutput(std::shared_ptr op) const; }; } // namespace low_precision diff --git a/src/common/low_precision_transformations/src/fold_fake_quantize.cpp b/src/common/low_precision_transformations/src/fold_fake_quantize.cpp index 3f69d8cdc17..51bb46c8c3a 100644 --- a/src/common/low_precision_transformations/src/fold_fake_quantize.cpp +++ b/src/common/low_precision_transformations/src/fold_fake_quantize.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "low_precision/network_helper.hpp" #include "itt.hpp" @@ -59,8 +60,27 @@ bool FoldFakeQuantizeTransformation::transform(TransformationContext& context, n return false; } +bool FoldFakeQuantizeTransformation::isConstantOutput(std::shared_ptr node) const { + const auto fakeQuantize = ov::as_type_ptr(node); + if (!fakeQuantize) { + return false; + } + + const auto outputLow = as_type_ptr(fakeQuantize->get_input_node_shared_ptr(3)); + const auto outputHigh = as_type_ptr(fakeQuantize->get_input_node_shared_ptr(4)); + + if (outputLow == nullptr || outputHigh == nullptr) { + return false; + } + + const auto vecLow = outputLow->cast_vector(); + const auto vecHigh = outputHigh->cast_vector(); + + return vecLow == vecHigh; +} + bool FoldFakeQuantizeTransformation::canBeTransformed(const TransformationContext& context, std::shared_ptr op) const { - return NetworkHelper::isConstantPath(op); + return NetworkHelper::isConstantPath(op) || isConstantOutput(op); } bool FoldFakeQuantizeTransformation::isPrecisionPreserved(std::shared_ptr layer) const noexcept { diff --git a/src/common/low_precision_transformations/src/network_helper.cpp b/src/common/low_precision_transformations/src/network_helper.cpp index 3fd5b76a59c..c7db834e0b2 100644 --- a/src/common/low_precision_transformations/src/network_helper.cpp +++ b/src/common/low_precision_transformations/src/network_helper.cpp @@ -742,6 +742,26 @@ std::shared_ptr NetworkHelper::foldFakeQuantize( foldConvert(fq->input_value(3), element::f32)); } + // y = FakeQuantize(x, inputLow, inputHigh, outputLow, outputHigh) + // given: + // outputLow is const + // outputHigh is const + // outputLow == outputHigh + // => y = Broadcast(Convert(outputLow, typeof(y))), ShapeOf(x)) + if (ov::is_type(fq->get_input_node_shared_ptr(3)) && + ov::is_type(fq->get_input_node_shared_ptr(4))) { + const auto outputLowValues = + ov::as_type_ptr(fq->get_input_node_shared_ptr(3))->cast_vector(); + const auto outputHighValues = + ov::as_type_ptr(fq->get_input_node_shared_ptr(4))->cast_vector(); + + if (outputLowValues == outputHighValues) { + const auto data_shape_node = fold(fq->input_value(0)); + const auto cvt_output_low = foldConvert(fq->input_value(3), fq->get_output_element_type(0)); + return fold(cvt_output_low, data_shape_node); + } + } + auto constant = ov::as_type_ptr(fq->get_input_node_shared_ptr(0)); if (constant) { diff --git a/src/common/low_precision_transformations/tests/fake_quantize_transformation.cpp b/src/common/low_precision_transformations/tests/fake_quantize_transformation.cpp index 1023ccd709d..f55ac25589a 100644 --- a/src/common/low_precision_transformations/tests/fake_quantize_transformation.cpp +++ b/src/common/low_precision_transformations/tests/fake_quantize_transformation.cpp @@ -183,13 +183,6 @@ const std::vector fakeQuantizeTransformati ngraph::element::i8, {{ngraph::element::f32, {{}, {-128.f}, {0.01f}}}, {ngraph::element::f16, {{}, {-128.f}, {0.01f}}}}, true}, - // dot interval - {LayerTransformation::createParamsI8I8(), - {256ul, {}, {0.f}, {2.55f}, {2.55f}, {2.55f}}, - {256ul, {}, {0.f}, {2.55f}, {1.f}, {1.f}}, - ngraph::element::Type_t::i8, - {{ngraph::element::f32, {{}, {}, {2.55f}}}}, - true}, // efficientnet-b0: efficientnet-b0/model/blocks_2/depthwise_conv2d/depthwise/fq_input_0, interval: -0.504395 - +0.5 // I8 symmetric: max ratio = 0.000907078 diff --git a/src/common/low_precision_transformations/tests/fold_fake_quantize_in_transformations.cpp b/src/common/low_precision_transformations/tests/fold_fake_quantize_in_transformations.cpp index 52f268c0182..f3ec5335bbf 100644 --- a/src/common/low_precision_transformations/tests/fold_fake_quantize_in_transformations.cpp +++ b/src/common/low_precision_transformations/tests/fold_fake_quantize_in_transformations.cpp @@ -67,12 +67,23 @@ public: const auto params = TestTransformationParams(testValues.params).setUpdatePrecisions(testValues.updatePrecision); - const auto constant = std::make_shared(testValues.actual.constPrecision, - testValues.constShape, - testValues.actual.constValues); + std::shared_ptr dataSource; + std::shared_ptr parameter; + bool useParameterAsDataSource = (testValues.actual.constValues.size() == 0); + + if (useParameterAsDataSource) { + // for data-independent const folding + parameter = + std::make_shared(testValues.actual.constPrecision, testValues.constShape); + dataSource = parameter; + } else { + dataSource = std::make_shared(testValues.actual.constPrecision, + testValues.constShape, + testValues.actual.constValues); + } std::shared_ptr fq = - ngraph::builder::subgraph::makeFakeQuantizeTypeRelaxed(constant, + ngraph::builder::subgraph::makeFakeQuantizeTypeRelaxed(dataSource, element::f32, testValues.actual.fakeQuantize); ngraph::pass::low_precision::NetworkHelper::setOutDataPrecision(as_type_ptr(fq), @@ -80,8 +91,10 @@ public: fq = ngraph::pass::low_precision::NetworkHelper::fold_fake_quantize(as_type_ptr(fq), testValues.roundValues); ngraph::ResultVector results{std::make_shared(fq)}; - actualFunction = - std::make_shared(results, ngraph::ParameterVector{}, "FoldFakeQuantizeFunction"); + actualFunction = std::make_shared( + results, + parameter ? ngraph::ParameterVector{parameter} : ngraph::ParameterVector{}, + "FoldFakeQuantizeFunction"); referenceFunction = ngraph::builder::subgraph::FoldFakeQuantizeFunction::getReference(testValues.expected.constPrecision, @@ -189,6 +202,37 @@ const std::vector testValues = { ngraph::element::i8}, {{-127, -128, -51, -3, 126, -28, -128, -1, -128, -64, -127, 127, -121, -128, -119, -128}, ngraph::element::i8}, }, + { + Shape{2, 2, 2, 2}, + LayerTransformation::createParamsU8I8(), + true, + false, + {{ + // use empty constValues for data independent const folding + }, + ngraph::element::u8, + {256ul, {}, {0.f}, {255.f}, {127.f}, {127.f}}, + ngraph::element::i8}, + {{ + 127, + 127, + 127, + 127, + 127, + 127, + 127, + 127, + 127, + 127, + 127, + 127, + 127, + 127, + 127, + 127, + }, + ngraph::element::i8}, + }, }; INSTANTIATE_TEST_SUITE_P(smoke_LPT, diff --git a/src/tests/functional/plugin/gpu/shared_tests_instances/low_precision_transformations/fake_quantize_transformation.cpp b/src/tests/functional/plugin/gpu/shared_tests_instances/low_precision_transformations/fake_quantize_transformation.cpp index 6c86036e8ba..8c855075cbe 100644 --- a/src/tests/functional/plugin/gpu/shared_tests_instances/low_precision_transformations/fake_quantize_transformation.cpp +++ b/src/tests/functional/plugin/gpu/shared_tests_instances/low_precision_transformations/fake_quantize_transformation.cpp @@ -43,9 +43,10 @@ const std::vector fakeQuantizeOnDataValues = { { 256ul, {}, { 0.f }, { 2.55f }, { -1.28f }, { 1.27f } }, "Pooling", "I8" }, + // nGraph: dot interval FQ node is being const-folded, so does Pooling { { 256ul, {}, { 0.f }, { 2.55f }, { 2.55f }, { 2.55f } }, - "Pooling", "U8" + "Pooling", "" }, { { 256ul, {}, { -127.5f }, { 0.f }, { -127.5f }, { 0.f } },