[LPT] Support ONNX quantized models coming from ORT PTQ (#14811)

* [LPT] FakeQuantize fuse

* GPU & CPU tests alignment

* refactoring & comments

* doc quick fix

* quick fix
This commit is contained in:
Edward Shogulin authored and GitHub committed 2023-04-14 21:22:55 +00:00
1 parent de2e9faa58
commit 8bdc5bc85f
24 files changed
+1209 -469

No files matched your search

+1
View File
@@ -69,6 +69,7 @@
<tab type="user" title="VariadicSplitTransformation" url="@ref openvino_docs_OV_UG_lpt_VariadicSplitTransformation"/>
</tab>
<tab type="user" title="Step 4. Cleanup transformations" url="@ref openvino_docs_OV_UG_lpt_step4_cleanup">
<tab type="user" title="EliminateFakeQuantizeTransformation" url="@ref openvino_docs_OV_UG_lpt_EliminateFakeQuantizeTransformation"/>
<tab type="user" title="FoldConvertTransformation" url="@ref openvino_docs_OV_UG_lpt_FoldConvertTransformation"/>
<tab type="user" title="FoldFakeQuantizeTransformation" url="@ref openvino_docs_OV_UG_lpt_FoldFakeQuantizeTransformation"/>
<tab type="user" title="FuseConvertTransformation" url="@ref openvino_docs_OV_UG_lpt_FuseConvertTransformation"/>
@@ -253,6 +253,7 @@ LPT cleanup transformations is final stage in LPT pipeline. In this step LPT tra
Transformations:
* :doc:`EliminateFakeQuantizeTransformation <openvino_docs_OV_UG_lpt_EliminateFakeQuantizeTransformation>`
* :doc:`FoldConvertTransformation <openvino_docs_OV_UG_lpt_FoldConvertTransformation>`
* :doc:`FoldFakeQuantizeTransformation <openvino_docs_OV_UG_lpt_FoldFakeQuantizeTransformation>`
* :doc:`FuseConvertTransformation <openvino_docs_OV_UG_lpt_FuseConvertTransformation>`
@@ -2,6 +2,7 @@
@sphinxdirective
* :doc:`EliminateFakeQuantizeTransformation <openvino_docs_OV_UG_lpt_EliminateFakeQuantizeTransformation>`
* :doc:`FoldConvertTransformation <openvino_docs_OV_UG_lpt_FoldConvertTransformation>`
* :doc:`FoldFakeQuantizeTransformation <openvino_docs_OV_UG_lpt_FoldFakeQuantizeTransformation>`
* :doc:`FuseConvertTransformation <openvino_docs_OV_UG_lpt_FuseConvertTransformation>`
@@ -0,0 +1,3 @@
# EliminateFakeQuantizeTransformation transformation {#openvino_docs_OV_UG_lpt_EliminateFakeQuantizeTransformation}
ngraph::pass::low_precision::EliminateFakeQuantizeTransformation class represents the `EliminateFakeQuantizeTransformation` transformation.
+1
View File
@@ -14,6 +14,7 @@ openvino_docs_OV_UG_lpt_convolutiontransformation.rst
openvino_docs_OV_UG_lpt_createattribute.rst
openvino_docs_OV_UG_lpt_createprecisionsdependentattribute.rst
openvino_docs_OV_UG_lpt_depthtospacetransformation.rst
openvino_docs_OV_UG_lpt_eliminatefakequantizetransformation.rst
openvino_docs_OV_UG_lpt_fakequantizedecompositiontransformation.rst
openvino_docs_OV_UG_lpt_fakequantizetransformation.rst
openvino_docs_OV_UG_lpt_foldconverttransformation.rst
@@ -0,0 +1,34 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <memory>
#include <ngraph/ngraph.hpp>
#include "low_precision/layer_transformation.hpp"
namespace ngraph {
namespace pass {
namespace low_precision {
/**
* @ingroup ie_transformation_common_api
* @brief EliminateFakeQuantizeTransformation removes FakeQuantize operations.
*
* For more details about the transformation, refer to
* [EliminateFakeQuantizeTransformation](@ref openvino_docs_OV_UG_lpt_EliminateFakeQuantizeTransformation) page
* in the Inference Engine Developer Guide.
*/
class LP_TRANSFORMATIONS_API EliminateFakeQuantizeTransformation : public LayerTransformation {
public:
OPENVINO_RTTI("EliminateFakeQuantizeTransformation", "0");
EliminateFakeQuantizeTransformation(const Params& params = Params());
bool transform(TransformationContext& context, ngraph::pattern::Matcher &m) override;
bool canBeTransformed(const TransformationContext& context, std::shared_ptr<Node> layer) const override;
bool isPrecisionPreserved(std::shared_ptr<Node> layer) const noexcept override;
};
} // namespace low_precision
} // namespace pass
} // namespace ngraph
@@ -0,0 +1,90 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "low_precision/eliminate_fake_quantize.hpp"
#include <memory>
#include <ngraph/ngraph.hpp>
#include <ngraph/pattern/op/wrap_type.hpp>
#include "itt.hpp"
namespace ngraph {
namespace pass {
namespace low_precision {
EliminateFakeQuantizeTransformation::EliminateFakeQuantizeTransformation(const Params& params) : LayerTransformation(params) {
MATCHER_SCOPE(FuseMultiplyToFakeQuantizeTransformation);
const auto matcher = pattern::wrap_type<opset1::FakeQuantize>({
pattern::any_input(),
pattern::wrap_type<opset1::Constant>(),
pattern::wrap_type<opset1::Constant>(),
pattern::wrap_type<opset1::Constant>(),
pattern::wrap_type<opset1::Constant>()
});
ngraph::graph_rewrite_callback callback = [this](pattern::Matcher& m) {
const auto op = m.get_match_root();
if (transformation_callback(op)) {
return false;
}
return transform(*context, m);
};
const auto m = std::make_shared<ngraph::pattern::Matcher>(matcher, matcher_name);
this->register_matcher(m, callback);
}
bool EliminateFakeQuantizeTransformation::transform(TransformationContext& context, ngraph::pattern::Matcher& m) {
const auto root = m.get_match_root();
if (!canBeTransformed(context, root)) {
return false;
}
return replace_output_update_name(root->output(0), root->input_value(0));
}
namespace {
bool check_interval(const std::shared_ptr<opset1::Constant>& constant, const float value) noexcept {
const auto& constant_values = constant->cast_vector<float>();
for (const auto constant_value : constant_values) {
if (std::fabs(constant_value - value) > std::numeric_limits<float>::epsilon()) {
return false;
}
}
return true;
}
bool check_intervals(const std::shared_ptr<opset1::FakeQuantize>& fakeQuantize) {
const auto& element_type = fakeQuantize->get_output_element_type(0);
const auto min_value = DataPrecision::getMinValue(element_type, fakeQuantize->get_levels());
const auto max_value = DataPrecision::getMaxValue(element_type, fakeQuantize->get_levels());
return
check_interval(ov::as_type_ptr<opset1::Constant>(fakeQuantize->get_input_node_shared_ptr(1)), min_value) &&
check_interval(ov::as_type_ptr<opset1::Constant>(fakeQuantize->get_input_node_shared_ptr(2)), max_value) &&
check_interval(ov::as_type_ptr<opset1::Constant>(fakeQuantize->get_input_node_shared_ptr(3)), min_value) &&
check_interval(ov::as_type_ptr<opset1::Constant>(fakeQuantize->get_input_node_shared_ptr(4)), max_value);
}
} // namespace
bool EliminateFakeQuantizeTransformation::canBeTransformed(const TransformationContext& context, std::shared_ptr<Node> operation) const {
const auto fakeQuantize = ov::as_type_ptr<opset1::FakeQuantize>(operation);
OPENVINO_ASSERT(fakeQuantize != nullptr, "unexpected operation type");
const auto& input_type = fakeQuantize->get_input_element_type(0);
const auto& output_type = fakeQuantize->get_output_element_type(0);
if ((input_type != output_type) || (!check_intervals(fakeQuantize))) {
return false;
}
return true;
}
bool EliminateFakeQuantizeTransformation::isPrecisionPreserved(std::shared_ptr<Node> layer) const noexcept {
return false;
}
} // namespace low_precision
} // namespace pass
} // namespace ngraph
@@ -77,6 +77,7 @@
// cleanup transformations
#include "itt.hpp"
#include "low_precision/convert.hpp"
#include "low_precision/eliminate_fake_quantize.hpp"
#include "low_precision/fold_fake_quantize.hpp"
#include "low_precision/fuse_convert.hpp"
#include "low_precision/fuse_multiply_to_fake_quantize.hpp"
@@ -268,6 +269,7 @@ bool ngraph::pass::low_precision::LowPrecision::run_on_model(const std::shared_p
ADD_MATCHER(common, VariadicSplitTransformation, params)
std::shared_ptr<ngraph::pass::GraphRewrite> cleanup = manager.register_pass<ngraph::pass::GraphRewrite>();
ADD_MATCHER(cleanup, EliminateFakeQuantizeTransformation, params)
ADD_MATCHER(cleanup, FoldConvertTransformation, params)
ADD_MATCHER(cleanup, FuseConvertTransformation, params)
ADD_MATCHER(cleanup, FuseSubtractToFakeQuantizeTransformation, params)
@@ -0,0 +1,138 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <memory>
#include <sstream>
#include "common_test_utils/ngraph_test_utils.hpp"
#include "layer_transformation.hpp"
#include "low_precision/eliminate_fake_quantize.hpp"
#include "low_precision/fake_quantize.hpp"
#include "low_precision/fake_quantize_decomposition.hpp"
#include "low_precision/max_pool.hpp"
#include "lpt_ngraph_functions/common/dequantization_operations.hpp"
#include "lpt_ngraph_functions/fuse_fake_quantize_function.hpp"
#include "simple_low_precision_transformer.hpp"
namespace {
using namespace testing;
using namespace ngraph;
using namespace ngraph::pass;
class TransformationTestValues {
public:
class Actual {
public:
ngraph::element::Type precisionBefore;
ngraph::builder::subgraph::FakeQuantizeOnData fakeQuantizeOnData1;
ngraph::builder::subgraph::FakeQuantizeOnData fakeQuantizeOnData2;
};
class Expected {
public:
ngraph::element::Type precisionBefore;
ngraph::builder::subgraph::FakeQuantizeOnData fakeQuantizeOnData1;
ngraph::builder::subgraph::FakeQuantizeOnData fakeQuantizeOnData2;
ngraph::builder::subgraph::DequantizationOperations dequantizationOperations2;
};
ngraph::PartialShape inputShape;
TestTransformationParams params;
Actual actual;
Expected expected;
};
class EliminateFakeQuantizeTransformation : public LayerTransformation,
public testing::WithParamInterface<TransformationTestValues> {
public:
void SetUp() override {
const TransformationTestValues testValues = GetParam();
actualFunction = ngraph::builder::subgraph::FuseFakeQuantizeFunction::get(testValues.inputShape,
testValues.actual.precisionBefore,
testValues.actual.fakeQuantizeOnData1,
testValues.actual.fakeQuantizeOnData2,
{});
SimpleLowPrecisionTransformer transformer;
transformer
.add<ngraph::pass::low_precision::FakeQuantizeDecompositionTransformation, ngraph::opset1::FakeQuantize>(
testValues.params);
transformer.add<ngraph::pass::low_precision::MaxPoolTransformation, ngraph::opset1::MaxPool>(testValues.params);
transformer.add<ngraph::pass::low_precision::FakeQuantizeTransformation, ngraph::opset1::FakeQuantize>(
testValues.params);
transformer.add<ngraph::pass::low_precision::EliminateFakeQuantizeTransformation, ngraph::opset1::FakeQuantize>(
testValues.params);
transformer.transform(actualFunction);
referenceFunction =
ngraph::builder::subgraph::FuseFakeQuantizeFunction::get(testValues.inputShape,
testValues.expected.precisionBefore,
testValues.expected.fakeQuantizeOnData1,
testValues.expected.fakeQuantizeOnData2,
testValues.expected.dequantizationOperations2);
}
static std::string getTestCaseName(testing::TestParamInfo<TransformationTestValues> obj) {
const TransformationTestValues testValues = obj.param;
std::ostringstream result;
result << testValues.inputShape << "_" << testValues.params.updatePrecisions << "_"
<< testValues.actual.precisionBefore << "_" << testValues.actual.fakeQuantizeOnData1 << "_"
<< testValues.actual.fakeQuantizeOnData2 << "_" << testValues.expected.precisionBefore << "_"
<< testValues.expected.fakeQuantizeOnData1 << "_" << testValues.expected.fakeQuantizeOnData2 << "_";
return result.str();
}
};
TEST_P(EliminateFakeQuantizeTransformation, CompareFunctions) {
actualFunction->validate_nodes_and_infer_types();
auto res = compare_functions(actualFunction, referenceFunction, true, true);
ASSERT_TRUE(res.first) << res.second;
ASSERT_TRUE(LayerTransformation::allNamesAreUnique(actualFunction)) << "Not all names are unique";
}
// clang-format off
const std::vector<TransformationTestValues> testValues = {
{
{1, 3, 16, 16},
TestTransformationParams(true, {ngraph::element::u8}, {ngraph::element::i8}),
{
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}},
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {255.f}, element::u8},
{},
{ ov::element::f32, {}, {{0.01f}, ov::element::f32, {}} }
}
},
{
{1, 3, 16, 16},
TestTransformationParams(true, {ngraph::element::u8}, {ngraph::element::i8}),
{
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}},
{256ul, {}, {0.f}, {2.55f / 2.f}, {0.f}, {2.55f / 2.f}}
},
{
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {255.f}, element::u8},
{256ul, {}, {0.f}, {127.5f}, {0.f}, {255.f}, element::u8},
{ ov::element::f32, {}, {{0.005f}, ov::element::f32, {}} }
}
}
};
// clang-format on
INSTANTIATE_TEST_SUITE_P(smoke_LPT,
EliminateFakeQuantizeTransformation,
::testing::ValuesIn(testValues),
EliminateFakeQuantizeTransformation::getTestCaseName);
} // namespace
@@ -0,0 +1,556 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include <low_precision/fake_quantize.hpp>
#include <low_precision/fake_quantize_decomposition.hpp>
#include <memory>
#include <sstream>
#include <string>
#include <transformations/init_node_info.hpp>
#include <transformations/utils/utils.hpp>
#include "common_test_utils/ngraph_test_utils.hpp"
#include "layer_transformation.hpp"
#include "lpt_ngraph_functions/common/add.hpp"
#include "lpt_ngraph_functions/common/dequantization_operations.hpp"
#include "lpt_ngraph_functions/common/fake_quantize_on_data.hpp"
#include "lpt_ngraph_functions/fuse_fake_quantize_function.hpp"
#include "simple_low_precision_transformer.hpp"
namespace {
using namespace testing;
using namespace ngraph;
using namespace ngraph::pass;
class FuseDequantizeToFakeQuantizeTransformationTestValues {
public:
class Actual {
public:
ngraph::element::Type precisionBeforeAdd;
ngraph::builder::subgraph::Add add;
ngraph::element::Type precisionBeforeDequantization;
ngraph::builder::subgraph::DequantizationOperations dequantization;
ngraph::element::Type precisionAfterDequantization;
ngraph::builder::subgraph::FakeQuantizeOnDataWithConstant fakeQuantizeOnData;
};
class Expected {
public:
ngraph::element::Type precisionBeforeAdd;
ngraph::builder::subgraph::Add add;
ngraph::element::Type precisionBeforeDequantization;
ngraph::builder::subgraph::DequantizationOperations dequantization;
ngraph::element::Type precisionAfterDequantization;
ngraph::element::Type precisionFakeQuantizeOnData;
ngraph::builder::subgraph::FakeQuantizeOnDataWithConstant fakeQuantizeOnData;
};
ngraph::PartialShape inputShape;
TestTransformationParams params;
Actual actual;
Expected expected;
};
class FuseDequantizeToFakeQuantizeTransformation
: public LayerTransformation,
public testing::WithParamInterface<FuseDequantizeToFakeQuantizeTransformationTestValues> {
public:
void SetUp() override {
const FuseDequantizeToFakeQuantizeTransformationTestValues testValues = GetParam();
actualFunction = ngraph::builder::subgraph::FuseFakeQuantizeFunction::getOriginal(
testValues.inputShape,
testValues.actual.precisionBeforeAdd,
testValues.actual.add,
testValues.actual.precisionBeforeDequantization,
testValues.actual.dequantization,
testValues.actual.precisionAfterDequantization,
testValues.actual.precisionAfterDequantization,
testValues.actual.fakeQuantizeOnData);
SimpleLowPrecisionTransformer transformer;
transformer
.add<ngraph::pass::low_precision::FakeQuantizeDecompositionTransformation, ngraph::opset1::FakeQuantize>(
testValues.params);
transformer.transform(actualFunction);
transformer.add<ngraph::pass::low_precision::FakeQuantizeTransformation, ngraph::opset1::FakeQuantize>(
testValues.params);
transformer.transform(actualFunction);
referenceFunction = ngraph::builder::subgraph::FuseFakeQuantizeFunction::getReference(
testValues.inputShape,
testValues.expected.precisionBeforeAdd,
testValues.expected.add,
testValues.expected.precisionBeforeDequantization,
testValues.expected.dequantization,
testValues.expected.precisionAfterDequantization,
testValues.expected.precisionFakeQuantizeOnData,
testValues.expected.fakeQuantizeOnData);
}
static std::string getTestCaseName(
testing::TestParamInfo<FuseDequantizeToFakeQuantizeTransformationTestValues> obj) {
const FuseDequantizeToFakeQuantizeTransformationTestValues testValues = obj.param;
std::ostringstream result;
result << testValues.inputShape << "_" << testValues.params.updatePrecisions << "_"
<< testValues.actual.precisionBeforeAdd << "_" << testValues.actual.add.values.size() << "_"
<< testValues.actual.add.outPrecision << "_" << testValues.actual.add.constantShape << "_"
<< testValues.actual.precisionBeforeDequantization << testValues.actual.dequantization << "_"
<< testValues.actual.precisionBeforeDequantization << "_" << testValues.actual.fakeQuantizeOnData << "_"
<< testValues.expected.dequantization << "_" << testValues.expected.add.values.size() << "_"
<< testValues.expected.add.outPrecision << "_" << testValues.expected.add.constantShape;
return result.str();
}
};
TEST_P(FuseDequantizeToFakeQuantizeTransformation, CompareFunctions) {
actualFunction->validate_nodes_and_infer_types();
auto res = compare_functions(actualFunction, referenceFunction, true, true);
ASSERT_TRUE(res.first) << res.second;
ASSERT_TRUE(LayerTransformation::allNamesAreUnique(actualFunction)) << "Not all names are unique";
}
// clang-format off
const std::vector<FuseDequantizeToFakeQuantizeTransformationTestValues> testValues = {
// Convert: U8 -> FP32, updatePrecisions = true
{
{1, 3, 16, 16},
TestTransformationParams(true, {ngraph::element::u8}, {ngraph::element::i8}),
{
element::f32,
{},
element::u8,
{{element::f32}, {}, {}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::u8,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {255.f}, element::u8}
}
},
// Convert: U8 -> FP32, updatePrecisions = false
{
{1, 3, 16, 16},
TestTransformationParams(false, {ngraph::element::u8}, {ngraph::element::i8}),
{
element::f32,
{},
element::u8,
{{element::f32}, {}, {}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::u8,
{{element::f32}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {255.f}, element::f32}
}
},
// 1) Multiply
{
{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::f32,
{{}, {}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {255.f}, {0.f}, {2.55f}}
}
},
// 1) Multiply
{
{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.02f, 0.03f}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {{1, 3, 1, 1}, {1, 3, 1, 1}, {}, {}}, {0.f, 0.f, 0.f}, {255.f, 127.5f, 85.f}, {0.f}, {2.55f}}
}
},
// 1) Per-channel multiply and dynamic shape
{
{Dimension::dynamic(), 3, Dimension::dynamic(), Dimension::dynamic()},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.02f, 0.03f}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {{1, 3, 1, 1}, {1, 3, 1, 1}, {}, {}}, {0.f, 0.f, 0.f}, {255.f, 127.5f, 85.f}, {0.f}, {2.55f}}
}
},
// 1) Per-channel multiply and dynamic shape with dynamic channels
{
{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.02f, 0.03f}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.02f, 0.03f}}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
}
},
// 1) Multiply with different input and output shape
{
{128, 1},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.1f, 1.f}, ngraph::element::f32, {1, 3}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.1f, 1.f}, ngraph::element::f32, {1, 3}}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
}
},
// Dynamic shape
{
{Dimension::dynamic(), Dimension::dynamic()},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.1f, 1.f}, ngraph::element::f32, {1, 3}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.1f, 1.f}, ngraph::element::f32, {1, 3}}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
}
},
// 1) Multiply + 2) Add
{
{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{{128}, element::f32},
element::f32,
{{}, {}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {-128.f}, {127.f}, {0.f}, {2.55f}}
}
},
// 1) Subtract + Multiply
{
{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::f32,
{{}, {-128}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {-128.f}, {127.f}, {0.f}, {2.55f}}
}
},
// 1) Convert + Subtract + Multiply
{
{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::u8,
{{element::f32}, {-128}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::u8,
{{}, {}, {}},
element::u8,
element::f32,
{256ul, {}, {-128.f}, {127.f}, {0.f}, {2.55f}}
}
},
// 1) Convert + Subtract + Multiply 2) Add
{
{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{{127}, element::f32},
element::f32,
{{element::f32}, {-128}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {-255.f}, {0.f}, {0.f}, {2.55f}}
}
},
// Dynamic shape
{
{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{{127}, element::f32},
element::f32,
{{element::f32}, {-128}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {-255.f}, {0.f}, {0.f}, {2.55f}}
}
},
// Dynamic rank
{
PartialShape::dynamic(),
LayerTransformation::createParamsU8I8(),
{
element::f32,
{{127}, element::f32},
element::f32,
{{element::f32}, {-128}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {-255.f}, {0.f}, {0.f}, {2.55f}}
}
},
// negative multiply
{
{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::f32,
{{}, {-128}, {-0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::f32,
{{}, {-128}, {-0.01f}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
}
},
// issue #40611 for FP32
{
{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{
{},
{},
ngraph::element::i32,
{{ngraph::element::f32}, {}, {}},
ngraph::element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
{},
{},
ngraph::element::i32,
{{ngraph::element::f32}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
}
},
// issue #40611 for FP16
{
{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{
{},
{},
ngraph::element::i32,
{{ngraph::element::f16}, {}, {}},
ngraph::element::f16,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
{},
{},
ngraph::element::i32,
{{ngraph::element::f16}, {}, {}},
element::f16,
element::f16,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
}
},
// multiply by zero
{
{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::u8,
{{element::f32}, {{-128, -128, -128}}, {{0.01f, 0.f, 0.01f}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::u8,
{{element::f32}, {{-128, -128, -128}}, {{0.01f, 0.f, 0.01f}}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
}
},
// non per-channel operation
{
{1, 3, 4},
LayerTransformation::createParamsU8I8(),
{
element::f32,
{},
element::u8,
{
{element::f32},
{{-128, -128, -128, -128}, ngraph::element::f32, {1, 1, 4}},
{{0.01f, 0.02f, 0.03f, 0.04f}, ngraph::element::f32, {1, 1, 4}}
},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
},
{
element::f32,
{},
element::u8,
{
{element::f32},
{{-128, -128, -128, -128}, ngraph::element::f32, {1, 1, 4}},
{{0.01f, 0.02f, 0.03f, 0.04f}, ngraph::element::f32, {1, 1, 4}}
},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}
}
},
};
// clang-format on
INSTANTIATE_TEST_SUITE_P(smoke_LPT,
FuseDequantizeToFakeQuantizeTransformation,
::testing::ValuesIn(testValues),
FuseDequantizeToFakeQuantizeTransformation::getTestCaseName);
} // namespace
@@ -1,424 +0,0 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include <low_precision/fake_quantize.hpp>
#include <low_precision/fake_quantize_decomposition.hpp>
#include <memory>
#include <sstream>
#include <string>
#include <transformations/init_node_info.hpp>
#include <transformations/utils/utils.hpp>
#include "common_test_utils/ngraph_test_utils.hpp"
#include "layer_transformation.hpp"
#include "lpt_ngraph_functions/common/add.hpp"
#include "lpt_ngraph_functions/common/dequantization_operations.hpp"
#include "lpt_ngraph_functions/common/fake_quantize_on_data.hpp"
#include "lpt_ngraph_functions/fuse_fake_quantize_function.hpp"
#include "simple_low_precision_transformer.hpp"
namespace {
using namespace testing;
using namespace ngraph;
using namespace ngraph::pass;
class FuseFakeQuantizeTransformationTestValues {
public:
class Actual {
public:
ngraph::element::Type precisionBeforeAdd;
ngraph::builder::subgraph::Add add;
ngraph::element::Type precisionBeforeDequantization;
ngraph::builder::subgraph::DequantizationOperations dequantization;
ngraph::element::Type precisionAfterDequantization;
ngraph::builder::subgraph::FakeQuantizeOnDataWithConstant fakeQuantizeOnData;
};
class Expected {
public:
ngraph::element::Type precisionBeforeAdd;
ngraph::builder::subgraph::Add add;
ngraph::element::Type precisionBeforeDequantization;
ngraph::builder::subgraph::DequantizationOperations dequantization;
ngraph::element::Type precisionAfterDequantization;
ngraph::element::Type precisionFakeQuantizeOnData;
ngraph::builder::subgraph::FakeQuantizeOnDataWithConstant fakeQuantizeOnData;
};
ngraph::PartialShape inputShape;
TestTransformationParams params;
Actual actual;
Expected expected;
};
class FuseFakeQuantizeTransformation : public LayerTransformation,
public testing::WithParamInterface<FuseFakeQuantizeTransformationTestValues> {
public:
void SetUp() override {
const FuseFakeQuantizeTransformationTestValues testValues = GetParam();
actualFunction = ngraph::builder::subgraph::FuseFakeQuantizeFunction::getOriginal(
testValues.inputShape,
testValues.actual.precisionBeforeAdd,
testValues.actual.add,
testValues.actual.precisionBeforeDequantization,
testValues.actual.dequantization,
testValues.actual.precisionAfterDequantization,
testValues.actual.precisionAfterDequantization,
testValues.actual.fakeQuantizeOnData);
SimpleLowPrecisionTransformer transformer;
transformer
.add<ngraph::pass::low_precision::FakeQuantizeDecompositionTransformation, ngraph::opset1::FakeQuantize>(
testValues.params);
transformer.transform(actualFunction);
transformer.add<ngraph::pass::low_precision::FakeQuantizeTransformation, ngraph::opset1::FakeQuantize>(
testValues.params);
transformer.transform(actualFunction);
referenceFunction = ngraph::builder::subgraph::FuseFakeQuantizeFunction::getReference(
testValues.inputShape,
testValues.expected.precisionBeforeAdd,
testValues.expected.add,
testValues.expected.precisionBeforeDequantization,
testValues.expected.dequantization,
testValues.expected.precisionAfterDequantization,
testValues.expected.precisionFakeQuantizeOnData,
testValues.expected.fakeQuantizeOnData);
}
static std::string getTestCaseName(testing::TestParamInfo<FuseFakeQuantizeTransformationTestValues> obj) {
const FuseFakeQuantizeTransformationTestValues testValues = obj.param;
std::ostringstream result;
result << testValues.inputShape << "_" << testValues.params.updatePrecisions << "_"
<< testValues.actual.precisionBeforeAdd << "_" << testValues.actual.add.values.size() << "_"
<< testValues.actual.add.outPrecision << "_" << testValues.actual.add.constantShape << "_"
<< testValues.actual.precisionBeforeDequantization << testValues.actual.dequantization << "_"
<< testValues.actual.precisionBeforeDequantization << "_" << testValues.actual.fakeQuantizeOnData << "_"
<< testValues.expected.dequantization << "_" << testValues.expected.add.values.size() << "_"
<< testValues.expected.add.outPrecision << "_" << testValues.expected.add.constantShape;
return result.str();
}
};
TEST_P(FuseFakeQuantizeTransformation, CompareFunctions) {
actualFunction->validate_nodes_and_infer_types();
auto res = compare_functions(actualFunction, referenceFunction, true, true);
ASSERT_TRUE(res.first) << res.second;
ASSERT_TRUE(LayerTransformation::allNamesAreUnique(actualFunction)) << "Not all names are unique";
}
const std::vector<FuseFakeQuantizeTransformationTestValues> testValues = {
// Convert: U8 -> FP32, updatePrecisions = true
{{1, 3, 16, 16},
TestTransformationParams(true, {ngraph::element::u8}, {ngraph::element::i8}),
{element::f32,
{},
element::u8,
{{element::f32}, {}, {}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::u8,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {255.f}, element::u8}}},
// Convert: U8 -> FP32, updatePrecisions = false
{{1, 3, 16, 16},
TestTransformationParams(false, {ngraph::element::u8}, {ngraph::element::i8}),
{element::f32,
{},
element::u8,
{{element::f32}, {}, {}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::u8,
{{element::f32}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {255.f}, element::f32}}},
// 1) Multiply
{{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{element::f32, {}, element::f32, {{}, {}, {0.01f}}, element::f32, {256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {255.f}, {0.f}, {2.55f}}}},
// 1) Multiply
{{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.02f, 0.03f}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {{1, 3, 1, 1}, {1, 3, 1, 1}, {}, {}}, {0.f, 0.f, 0.f}, {255.f, 127.5f, 85.f}, {0.f}, {2.55f}}}},
// 1) Per-channel multiply and dynamic shape
{{Dimension::dynamic(), 3, Dimension::dynamic(), Dimension::dynamic()},
LayerTransformation::createParamsU8I8(),
{element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.02f, 0.03f}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {{1, 3, 1, 1}, {1, 3, 1, 1}, {}, {}}, {0.f, 0.f, 0.f}, {255.f, 127.5f, 85.f}, {0.f}, {2.55f}}}},
// 1) Per-channel multiply and dynamic shape with dynamic channels
{{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()},
LayerTransformation::createParamsU8I8(),
{element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.02f, 0.03f}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.02f, 0.03f}}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}}},
// 1) Multiply with different input and output shape
{{128, 1},
LayerTransformation::createParamsU8I8(),
{element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.1f, 1.f}, ngraph::element::f32, {1, 3}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.1f, 1.f}, ngraph::element::f32, {1, 3}}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}}},
// Dynamic shape
{{Dimension::dynamic(), Dimension::dynamic()},
LayerTransformation::createParamsU8I8(),
{element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.1f, 1.f}, ngraph::element::f32, {1, 3}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {{0.01f, 0.1f, 1.f}, ngraph::element::f32, {1, 3}}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}}},
// 1) Multiply + 2) Add
{{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{element::f32,
{{128}, element::f32},
element::f32,
{{}, {}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {-128.f}, {127.f}, {0.f}, {2.55f}}}},
// 1) Subtract + Multiply
{{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{element::f32, {}, element::f32, {{}, {-128}, {0.01f}}, element::f32, {256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {-128.f}, {127.f}, {0.f}, {2.55f}}}},
// 1) Convert + Subtract + Multiply
{{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{element::f32,
{},
element::u8,
{{element::f32}, {-128}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::u8,
{{}, {}, {}},
element::u8,
element::f32,
{256ul, {}, {-128.f}, {127.f}, {0.f}, {2.55f}}}},
// 1) Convert + Subtract + Multiply 2) Add
{{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{element::f32,
{{127}, element::f32},
element::f32,
{{element::f32}, {-128}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {-255.f}, {0.f}, {0.f}, {2.55f}}}},
// Dynamic shape
{{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()},
LayerTransformation::createParamsU8I8(),
{element::f32,
{{127}, element::f32},
element::f32,
{{element::f32}, {-128}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {-255.f}, {0.f}, {0.f}, {2.55f}}}},
// Dynamic rank
{PartialShape::dynamic(),
LayerTransformation::createParamsU8I8(),
{element::f32,
{{127}, element::f32},
element::f32,
{{element::f32}, {-128}, {0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {-255.f}, {0.f}, {0.f}, {2.55f}}}},
// negative multiply
{{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{element::f32,
{},
element::f32,
{{}, {-128}, {-0.01f}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::f32,
{{}, {-128}, {-0.01f}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}}},
// issue #40611 for FP32
{{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{{},
{},
ngraph::element::i32,
{{ngraph::element::f32}, {}, {}},
ngraph::element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{{},
{},
ngraph::element::i32,
{{ngraph::element::f32}, {}, {}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}}},
// issue #40611 for FP16
{{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{{},
{},
ngraph::element::i32,
{{ngraph::element::f16}, {}, {}},
ngraph::element::f16,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{{},
{},
ngraph::element::i32,
{{ngraph::element::f16}, {}, {}},
element::f16,
element::f16,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}}},
// multiply by zero
{{1, 3, 16, 16},
LayerTransformation::createParamsU8I8(),
{element::f32,
{},
element::u8,
{{element::f32}, {{-128, -128, -128}}, {{0.01f, 0.f, 0.01f}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::u8,
{{element::f32}, {{-128, -128, -128}}, {{0.01f, 0.f, 0.01f}}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}}},
// non per-channel operation
{{1, 3, 4},
LayerTransformation::createParamsU8I8(),
{element::f32,
{},
element::u8,
{{element::f32},
{{-128, -128, -128, -128}, ngraph::element::f32, {1, 1, 4}},
{{0.01f, 0.02f, 0.03f, 0.04f}, ngraph::element::f32, {1, 1, 4}}},
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}},
{element::f32,
{},
element::u8,
{{element::f32},
{{-128, -128, -128, -128}, ngraph::element::f32, {1, 1, 4}},
{{0.01f, 0.02f, 0.03f, 0.04f}, ngraph::element::f32, {1, 1, 4}}},
element::f32,
element::f32,
{256ul, {}, {0.f}, {2.55f}, {0.f}, {2.55f}}}},
};
INSTANTIATE_TEST_SUITE_P(smoke_LPT,
FuseFakeQuantizeTransformation,
::testing::ValuesIn(testValues),
FuseFakeQuantizeTransformation::getTestCaseName);
} // namespace
@@ -0,0 +1,53 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "low_precision_transformations/eliminate_fake_quantize_transformation.hpp"
#include <vector>
#include <gtest/gtest.h>
using namespace LayerTestsDefinitions;
using namespace InferenceEngine::details;
namespace {
const std::vector<EliminateFakeQuantizeTransformationTestValues> testValues = {
{
{1, 3, 16, 16},
LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParamsU8I8(),
{
ngraph::element::f32,
{ 256ul, {}, { 0.f }, { 255.f / 2.f }, { 0.f }, { 255.f / 2.f }, ngraph::element::f32 },
{ 256ul, {}, { 0.f }, { 255.f / 2.f }, { 0.f }, { 255.f / 2.f }, ngraph::element::f32 }
},
{
{ "fakeQuantize1" },
{ "fakeQuantize2" }, // was fused to fakeQuantize1
2ull
}
},
{
{1, 3, 16, 16},
LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParamsU8I8(),
{
ngraph::element::f32,
{ 256ul, {}, { 0.f }, { 255.f / 2.f }, { 0.f }, { 255.f / 2.f }, ngraph::element::f32 },
{ 256ul, {}, { 0.f }, { 255.f / 2.1f }, { 0.f }, { 255.f / 2.1f }, ngraph::element::f32 }
},
{
{ "fakeQuantize1", "fakeQuantize2" }, // not fused
{ },
2ull
}
}
};
INSTANTIATE_TEST_SUITE_P(smoke_LPT,
EliminateFakeQuantizeTransformation,
::testing::Combine(
::testing::Values(CommonTestUtils::DEVICE_CPU),
::testing::ValuesIn(testValues)),
EliminateFakeQuantizeTransformation::getTestCaseName);
} // namespace
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
//
#include "low_precision_transformations/fuse_fake_quantize_transformation.hpp"
#include "low_precision_transformations/fuse_dequantize_to_fake_quantize_transformation.hpp"
#include <vector>
#include <gtest/gtest.h>
@@ -11,7 +11,8 @@ using namespace InferenceEngine::details;
namespace {
const std::vector<FuseFakeQuantizeTransformationTestValues> testValues = {
// clang-format off
const std::vector<FuseDequantizeToFakeQuantizeTransformationTestValues> testValues = {
// 1) Multiply
{
{1, 3, 16, 16},
@@ -103,10 +104,11 @@ const std::vector<FuseFakeQuantizeTransformationTestValues> testValues = {
}
},
};
// clang-format on
INSTANTIATE_TEST_SUITE_P(smoke_LPT, FuseFakeQuantizeTransformation,
INSTANTIATE_TEST_SUITE_P(smoke_LPT, FuseDequantizeToFakeQuantizeTransformation,
::testing::Combine(
::testing::Values(CommonTestUtils::DEVICE_CPU),
::testing::ValuesIn(testValues)),
FuseFakeQuantizeTransformation::getTestCaseName);
FuseDequantizeToFakeQuantizeTransformation::getTestCaseName);
} // namespace
@@ -0,0 +1,51 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "low_precision_transformations/eliminate_fake_quantize_transformation.hpp"
#include <vector>
#include <gtest/gtest.h>
using namespace LayerTestsDefinitions;
using namespace InferenceEngine::details;
namespace {
const std::vector<EliminateFakeQuantizeTransformationTestValues> testValues = {
{
{1, 3, 16, 16},
LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParamsU8I8(),
{
ngraph::element::f32,
{ 256ul, {}, { 0.f }, { 255.f / 2.f }, { 0.f }, { 255.f / 2.f }, ngraph::element::f32 },
{ 256ul, {}, { 0.f }, { 255.f / 2.f }, { 0.f }, { 255.f / 2.f }, ngraph::element::f32 }
},
{
{ "fakeQuantize1" },
{ "fakeQuantize2" }, // was fused to fakeQuantize1
2ull
}
},
{
{1, 3, 16, 16},
LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParamsU8I8(),
{
ngraph::element::f32,
{ 256ul, {}, { 0.f }, { 255.f / 2.f }, { 0.f }, { 255.f / 2.f }, ngraph::element::f32 },
{ 256ul, {}, { 0.f }, { 255.f / 2.1f }, { 0.f }, { 255.f / 2.1f }, ngraph::element::f32 }
},
{
{ "fakeQuantize1", "fakeQuantize2" }, // not fused
{ },
2ull
}
}
};
INSTANTIATE_TEST_SUITE_P(smoke_LPT,
EliminateFakeQuantizeTransformation,
::testing::Combine(
::testing::Values(CommonTestUtils::DEVICE_GPU),
::testing::ValuesIn(testValues)),
EliminateFakeQuantizeTransformation::getTestCaseName);
} // namespace
@@ -1,8 +1,8 @@
// Copyright (C) 2018-2023 Intel Corporation
// Copyright (C) 2018-2022 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "low_precision_transformations/fuse_fake_quantize_transformation.hpp"
#include "low_precision_transformations/fuse_dequantize_to_fake_quantize_transformation.hpp"
#include <vector>
#include <gtest/gtest.h>
@@ -10,7 +10,9 @@ using namespace LayerTestsDefinitions;
using namespace InferenceEngine::details;
namespace {
const std::vector<FuseFakeQuantizeTransformationTestValues> testValues = {
// clang-format off
const std::vector<FuseDequantizeToFakeQuantizeTransformationTestValues> testValues = {
// 1) Multiply
{
{1, 3, 16, 16},
@@ -103,10 +105,11 @@ const std::vector<FuseFakeQuantizeTransformationTestValues> testValues = {
}
},
};
// clang-format on
INSTANTIATE_TEST_SUITE_P(smoke_LPT, FuseFakeQuantizeTransformation,
INSTANTIATE_TEST_SUITE_P(smoke_LPT, FuseDequantizeToFakeQuantizeTransformation,
::testing::Combine(
::testing::Values(CommonTestUtils::DEVICE_GPU),
::testing::ValuesIn(testValues)),
FuseFakeQuantizeTransformation::getTestCaseName);
FuseDequantizeToFakeQuantizeTransformation::getTestCaseName);
} // namespace
@@ -0,0 +1,54 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <string>
#include <set>
#include <ngraph/ngraph.hpp>
#include "lpt_ngraph_functions/common/add.hpp"
#include "lpt_ngraph_functions/common/fake_quantize_on_data.hpp"
#include "lpt_ngraph_functions/common/dequantization_operations.hpp"
#include "shared_test_classes/base/low_precision_transformations/layer_transformation.hpp"
namespace LayerTestsDefinitions {
class EliminateFakeQuantizeTransformationTestValues {
public:
class Actual {
public:
ngraph::element::Type precisionBefore;
ngraph::builder::subgraph::FakeQuantizeOnData fakeQuantizeOnData1;
ngraph::builder::subgraph::FakeQuantizeOnData fakeQuantizeOnData2;
};
class Expected {
public:
std::set<std::string> exist;
std::set<std::string> absent;
size_t int8_convolutions;
};
ngraph::PartialShape inputShape;
ngraph::pass::low_precision::LayerTransformation::Params params;
Actual actual;
Expected expected;
};
typedef std::tuple<
std::string,
EliminateFakeQuantizeTransformationTestValues> EliminateFakeQuantizeTransformationParams;
class EliminateFakeQuantizeTransformation
: public testing::WithParamInterface<EliminateFakeQuantizeTransformationParams>,
public LayerTestsUtils::LayerTransformation {
public:
static std::string getTestCaseName(const testing::TestParamInfo<EliminateFakeQuantizeTransformationParams>& obj);
protected:
void SetUp() override;
};
} // namespace LayerTestsDefinitions
@@ -14,7 +14,7 @@
namespace LayerTestsDefinitions {
class FuseFakeQuantizeTransformationTestValues {
class FuseDequantizeToFakeQuantizeTransformationTestValues {
public:
class Actual {
public:
@@ -33,13 +33,13 @@ public:
typedef std::tuple<
std::string,
FuseFakeQuantizeTransformationTestValues> FuseFakeQuantizeTransformationParams;
FuseDequantizeToFakeQuantizeTransformationTestValues> FuseDequantizeToFakeQuantizeTransformationParams;
class FuseFakeQuantizeTransformation :
public testing::WithParamInterface<FuseFakeQuantizeTransformationParams>,
public LayerTestsUtils::LayerTransformation {
class FuseDequantizeToFakeQuantizeTransformation
: public testing::WithParamInterface<FuseDequantizeToFakeQuantizeTransformationParams>,
public LayerTestsUtils::LayerTransformation {
public:
static std::string getTestCaseName(const testing::TestParamInfo<FuseFakeQuantizeTransformationParams>& obj);
static std::string getTestCaseName(const testing::TestParamInfo<FuseDequantizeToFakeQuantizeTransformationParams>& obj);
protected:
void SetUp() override;
@@ -0,0 +1,85 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "low_precision_transformations/eliminate_fake_quantize_transformation.hpp"
#include <tuple>
#include <sstream>
#include <string>
#include <vector>
#include <transformations/init_node_info.hpp>
#include "openvino/util/common_util.hpp"
#include "lpt_ngraph_functions/fuse_fake_quantize_function.hpp"
namespace LayerTestsDefinitions {
std::string EliminateFakeQuantizeTransformation::getTestCaseName(const testing::TestParamInfo<EliminateFakeQuantizeTransformationParams>& obj) {
std::string targetDevice;
EliminateFakeQuantizeTransformationTestValues testValues;
std::tie(targetDevice, testValues) = obj.param;
std::ostringstream result;
result << targetDevice << "_" <<
testValues.actual.precisionBefore << "_" <<
testValues.actual.fakeQuantizeOnData1 << "_" <<
testValues.actual.fakeQuantizeOnData2;
return result.str();
}
void EliminateFakeQuantizeTransformation::SetUp() {
EliminateFakeQuantizeTransformationTestValues testValues;
std::tie(targetDevice, testValues) = this->GetParam();
// Convolution is used in a model as operation with specific precision requirements on data branch
// to test the transformation place in LPT pipeline:
// markup transformations and FakeQuantize operation decomposition transformation have to handle FakeQuantize as usual
function = ngraph::builder::subgraph::FuseFakeQuantizeFunction::get(
testValues.inputShape,
testValues.actual.precisionBefore,
testValues.actual.fakeQuantizeOnData1,
testValues.actual.fakeQuantizeOnData2,
{});
ov::pass::InitNodeInfo().run_on_model(function);
}
TEST_P(EliminateFakeQuantizeTransformation, CompareWithRefImpl) {
Run();
EliminateFakeQuantizeTransformationTestValues testValues;
std::tie(targetDevice, testValues) = this->GetParam();
const auto& rtInfo = LayerTestsCommon::getRuntimeInfo();
auto exist = testValues.expected.exist;
auto absent = testValues.expected.absent;
auto int8_convolutions = 0ull;
for (const auto& it : rtInfo) {
const auto& nameIt = it.second.find("originalLayersNames");
const auto& fused_name = nameIt->second.as<std::string>();
const auto names = ov::util::split(fused_name, ',', true);
for (const auto& name : names) {
ASSERT_TRUE(absent.find(name) == absent.end());
exist.erase(name);
}
const auto& type_it = it.second.find("layerType");
const auto& type = type_it->second.as<std::string>();
if (type == "Convolution") {
const auto& precision_it = it.second.find("runtimePrecision");
const auto& precision = precision_it->second.as<std::string>();
if (precision == "U8") {
int8_convolutions++;
}
}
}
ASSERT_TRUE(exist.empty());
ASSERT_EQ(testValues.expected.int8_convolutions, int8_convolutions);
};
} // namespace LayerTestsDefinitions
@@ -1,8 +1,8 @@
// Copyright (C) 2018-2023 Intel Corporation
// Copyright (C) 2018-2022 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "low_precision_transformations/fuse_fake_quantize_transformation.hpp"
#include "low_precision_transformations/fuse_dequantize_to_fake_quantize_transformation.hpp"
#include <tuple>
#include <sstream>
@@ -14,13 +14,13 @@
namespace LayerTestsDefinitions {
std::string FuseFakeQuantizeTransformation::getTestCaseName(const testing::TestParamInfo<FuseFakeQuantizeTransformationParams>& obj) {
std::string FuseDequantizeToFakeQuantizeTransformation::getTestCaseName(const testing::TestParamInfo<FuseDequantizeToFakeQuantizeTransformationParams>& obj) {
std::string targetDevice;
FuseFakeQuantizeTransformationTestValues testValues;
FuseDequantizeToFakeQuantizeTransformationTestValues testValues;
std::tie(targetDevice, testValues) = obj.param;
std::ostringstream result;
result << "targetDevice=" << targetDevice << "_" <<
result << targetDevice << "_" <<
testValues.actual.precisionBeforeAdd << "_" <<
testValues.actual.add.values.size() << "_" <<
testValues.actual.add.outPrecision << "_" <<
@@ -32,8 +32,8 @@ std::string FuseFakeQuantizeTransformation::getTestCaseName(const testing::TestP
return result.str();
}
void FuseFakeQuantizeTransformation::SetUp() {
FuseFakeQuantizeTransformationTestValues testValues;
void FuseDequantizeToFakeQuantizeTransformation::SetUp() {
FuseDequantizeToFakeQuantizeTransformationTestValues testValues;
std::tie(targetDevice, testValues) = this->GetParam();
function = ngraph::builder::subgraph::FuseFakeQuantizeFunction::getOriginal(
@@ -45,11 +45,9 @@ void FuseFakeQuantizeTransformation::SetUp() {
testValues.actual.precisionAfterDequantization,
testValues.actual.precisionAfterDequantization,
testValues.actual.fakeQuantizeOnData);
ov::pass::InitNodeInfo().run_on_model(function);
}
TEST_P(FuseFakeQuantizeTransformation, CompareWithRefImpl) {
TEST_P(FuseDequantizeToFakeQuantizeTransformation, CompareWithRefImpl) {
Run();
};
@@ -744,6 +744,7 @@ void Comparator::compare_inputs(ngraph::Node* node1, ngraph::Node* node2, std::o
auto const2 = ngraph::as_type_ptr<Constant>(node2->get_input_node_shared_ptr(i));
if (const1 && const2 && !equal_value(const1, const2)) {
err_log << "Different Constant values detected\n"
<< const1->get_friendly_name() << " & " << const2->get_friendly_name() << "\n"
<< node1->description() << " Input(" << i << ") and " << node2->description() << " Input(" << i
<< ")" << std::endl;
}
@@ -1,13 +1,13 @@
// Copyright (C) 2018-2023 Intel Corporation
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <algorithm>
#include <memory>
#include <ngraph/ngraph.hpp>
#include "low_precision/layer_transformation.hpp"
#include "common/add.hpp"
#include "common/fake_quantize_on_data.hpp"
#include "common/dequantization_operations.hpp"
@@ -36,14 +36,21 @@ public:
const FakeQuantizeOnDataWithConstant& fqOnData);
static std::shared_ptr<ngraph::Function> getReference(
const ngraph::PartialShape& inputShape,
const ngraph::element::Type precisionBeforeAdd,
const Add& add,
const ngraph::element::Type precisionBeforeDequantization,
const DequantizationOperations& dequantization,
const ngraph::element::Type precisionAfterDequantization,
const ngraph::element::Type precisionFqOnData,
const FakeQuantizeOnDataWithConstant& fqOnData);
const ngraph::PartialShape& inputShape,
const ngraph::element::Type precisionBeforeAdd,
const Add& add,
const ngraph::element::Type precisionBeforeDequantization,
const DequantizationOperations& dequantization,
const ngraph::element::Type precisionAfterDequantization,
const ngraph::element::Type precisionFqOnData,
const FakeQuantizeOnDataWithConstant& fqOnData);
static std::shared_ptr<ngraph::Function> get(
const ngraph::PartialShape& inputShape,
const ngraph::element::Type precisionBefore,
const FakeQuantizeOnData& fqOnData1,
const FakeQuantizeOnData& fqOnData2,
const DequantizationOperations& dequantizationOperations2);
static std::shared_ptr<ngraph::Function> get(
const ngraph::PartialShape& inputShape,
@@ -194,11 +194,11 @@ std::shared_ptr<Node> makeTranspose(const Output<Node>& data, const Transpose& t
std::shared_ptr<ngraph::opset1::FakeQuantize> makeFakeQuantize(
const Output<Node>& output,
const ngraph::element::Type precision,
const ngraph::element::Type constantType,
const FakeQuantizeOnData& fqOnData) {
return ov::as_type_ptr<ngraph::opset1::FakeQuantize>(ngraph::builder::makeFakeQuantize(
output,
precision,
constantType,
fqOnData.quantizationLevel,
fqOnData.constantShape,
fqOnData.inputLowValues,
@@ -1,4 +1,4 @@
// Copyright (C) 2018-2023 Intel Corporation
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
@@ -6,8 +6,8 @@
#include <ngraph/opsets/opset1.hpp>
#include "ov_ops/type_relaxed.hpp"
#include "ngraph_functions/subgraph_builders.hpp"
#include "low_precision/network_helper.hpp"
#include "ngraph_functions/subgraph_builders.hpp"
#include "lpt_ngraph_functions/common/builders.hpp"
#include "lpt_ngraph_functions/common/fake_quantize_on_data.hpp"
@@ -47,6 +47,39 @@ std::shared_ptr<ngraph::Function> FuseFakeQuantizeFunction::getOriginal(
return std::make_shared<ngraph::Function>(results, ngraph::ParameterVector{ input }, "FuseFakeQuantizeFunction");
}
namespace {
std::shared_ptr<ngraph::opset1::Convolution> make_convolution(
const ngraph::PartialShape& inputShape,
const ngraph::element::Type precisionBefore,
const std::shared_ptr<Node>& parent,
const size_t index) {
const ov::Shape shape = inputShape.to_shape();
const ov::Shape weightsShape({ shape[1], shape[1], 1ull, 1ull });
auto weightsConstant = std::make_shared<ngraph::op::Constant>(ngraph::element::f32, weightsShape, std::vector<float>(9, 1.f));
auto weights = makeFakeQuantize(
weightsConstant,
precisionBefore,
FakeQuantizeOnData(
255,
ov::Shape({ shape[1], 1ull, 1ull, 1ull }),
{ -1.27f, -1.27f, -1.27f },
{ 1.28f, 1.28f, 1.28f },
{ -1.27f, -1.27f, -1.27f },
{ 1.28f, 1.28f, 1.28f },
precisionBefore));
auto convolution = std::make_shared<ngraph::opset1::Convolution>(
parent,
weights,
ngraph::Strides{ 1, 1 },
ngraph::CoordinateDiff{ 0, 0 },
ngraph::CoordinateDiff{ 0, 0 },
ngraph::Strides{ 1, 1 });
convolution->set_friendly_name("convolution" + std::to_string(index));
return convolution;
}
} // namespace
std::shared_ptr<ngraph::Function> FuseFakeQuantizeFunction::getReference(
const ngraph::PartialShape& inputShape,
const ngraph::element::Type precisionBeforeAdd,
@@ -87,6 +120,56 @@ std::shared_ptr<ngraph::Function> FuseFakeQuantizeFunction::getOriginal(
return std::make_shared<ngraph::Function>(results, ngraph::ParameterVector{ input }, "FuseFakeQuantizeFunction");
}
std::shared_ptr<ngraph::Function> FuseFakeQuantizeFunction::get(
const ngraph::PartialShape& inputShape,
const ngraph::element::Type precisionBefore,
const FakeQuantizeOnData& fqOnData1,
const FakeQuantizeOnData& fqOnData2,
const DequantizationOperations& dequantizationOperations2) {
const auto input = std::make_shared<ngraph::opset1::Parameter>(precisionBefore, inputShape);
input->set_friendly_name("input");
std::shared_ptr<Node> parent = input;
if (!fqOnData1.empty()) {
parent = fqOnData1.outputPrecision == precisionBefore ?
makeFakeQuantize(parent, precisionBefore, fqOnData1) :
makeFakeQuantizeTypeRelaxed(parent, precisionBefore, fqOnData1);
parent->set_friendly_name("fakeQuantize1");
}
const std::vector<size_t> kernel = { 3, 3 };
const std::vector<size_t> stride = { 1, 1 };
const std::vector<size_t> padBegin = { 0, 0 };
const std::vector<size_t> padEnd = { 0, 0 };
const ngraph::op::PadType padType = ngraph::op::PadType::NOTSET;
const ngraph::op::RoundingType roundingType = ngraph::op::RoundingType::FLOOR;
parent = std::make_shared<ngraph::opset1::MaxPool>(
parent,
stride,
padBegin,
padEnd,
kernel,
roundingType,
padType);
if (!fqOnData2.empty()) {
parent = makeFakeQuantize(parent, precisionBefore, fqOnData2);
parent->set_friendly_name("fakeQuantize2");
}
if (!dequantizationOperations2.empty()) {
parent = makeDequantization(parent, dequantizationOperations2);
}
ngraph::ResultVector results{
std::make_shared<ngraph::opset1::Result>(make_convolution(inputShape, precisionBefore, parent, 0)),
std::make_shared<ngraph::opset1::Result>(make_convolution(inputShape, precisionBefore, parent, 1))
};
return std::make_shared<ngraph::Function>(results, ngraph::ParameterVector{ input }, "FuseFakeQuantizeFunction");
}
std::shared_ptr<ngraph::Function> FuseFakeQuantizeFunction::get(
const ngraph::PartialShape& inputShape,
const std::vector<Branch>& branches,
@@ -10,17 +10,17 @@ namespace ngraph {
namespace builder {
std::shared_ptr<Node> makeFakeQuantize(const ngraph::Output<Node> &in,
const element::Type &type,
const element::Type &constantType,
std::size_t levels,
std::vector<size_t> constShapes,
const std::vector<float> &inputLowData,
const std::vector<float> &inputHighData,
const std::vector<float> &outputLowData,
const std::vector<float> &outputHighData) {
auto inputLowNode = makeConstant(type, constShapes, inputLowData, inputLowData.empty());
auto inputHighNode = makeConstant(type, constShapes, inputHighData, inputHighData.empty());
auto outputLowNode = makeConstant(type, constShapes, outputLowData, outputLowData.empty());
auto outputHighNode = makeConstant(type, constShapes, outputHighData, outputHighData.empty());
auto inputLowNode = makeConstant(constantType, constShapes, inputLowData, inputLowData.empty());
auto inputHighNode = makeConstant(constantType, constShapes, inputHighData, inputHighData.empty());
auto outputLowNode = makeConstant(constantType, constShapes, outputLowData, outputLowData.empty());
auto outputHighNode = makeConstant(constantType, constShapes, outputHighData, outputHighData.empty());
auto fq = std::make_shared<ngraph::opset1::FakeQuantize>(in, inputLowNode, inputHighNode, outputLowNode, outputHighNode, levels);