[CPU] Ensure mathematical correctness of Convert node (#14174)

This commit is contained in:
Chen Xu authored and GitHub committed 2022-12-02 17:10:19 +04:00
1 parent 62aac52079
commit 541045b0ba
4 files changed
+84 -3

No files matched your search

@@ -36,7 +36,7 @@ class TRANSFORMATIONS_API ConvertPrecision;
* For all operations from opset1-opset4 this conversions can be applied without adding Conversion operations.
* That is possible because all operations that produces "FROM" type can produce "TO" type. And for this operations
* we have created special fuse_type_into_<type> functoin (can be found in cpp file) that performs type fusion
* into operation.
* into operation. m_additional_type_to_fuse_map allows to rewrite existing type convertors.
*
* List of operations that are supported by this transformations for i64 -> i32 conversion:
* opset4::Parameter
@@ -297,6 +297,10 @@ bool ov::pass::ConvertPrecision::run_on_model(const std::shared_ptr<ngraph::Func
{opset10::Unique::get_type_info_static(), fuse_type_to_unique_v10},
{opset8::RandomUniform::get_type_info_static(), fuse_type_to_random_uniform_v8}};
for (const auto& it : m_additional_type_to_fuse_map) {
type_to_fuse[it.first] = it.second;
}
type_to_fuse.insert(m_additional_type_to_fuse_map.begin(), m_additional_type_to_fuse_map.end());
static type_to_fuse_map type_to_extend{
+26 -1
View File
@@ -107,6 +107,7 @@
#include <ngraph/opsets/opset4.hpp>
#include <ngraph/opsets/opset5.hpp>
#include <ngraph/opsets/opset6.hpp>
#include <openvino/opsets/opset10.hpp>
#include <ngraph/op/util/op_types.hpp>
#include <ngraph/pass/manager.hpp>
#include <ngraph/graph_util.hpp>
@@ -261,6 +262,29 @@ Engine::~Engine() {
executorManager()->clear("CPUCallbackExecutor");
}
static bool fuse_type_to_convert(const std::shared_ptr<ngraph::Node>& node, ov::element::Type to, size_t idx) {
if (auto convert = ov::as_type_ptr<ov::opset10::Convert>(node)) {
// For Convert node, converting precision from floating point to boolean will lead to mathematical
// error, because here the output precision boolean is replaced by u8. E.g. floating point value 0.01
// is converted to be 1 for boolean, but 0 for u8. Thus an Abs and Ceil node should be added before the
// Convert node for this scenario.
if (convert->input(0).get_element_type().is_real() &&
convert->get_convert_element_type() == ngraph::element::boolean && to.is_integral_number()) {
auto abs = std::make_shared<ov::opset10::Abs>(convert->input_value(0).get_node_shared_ptr());
auto ceil = std::make_shared<ov::opset10::Ceiling>(abs);
auto new_convert = std::make_shared<ov::opset10::Convert>(ceil, to);
new_convert->set_friendly_name(convert->get_friendly_name());
ov::copy_runtime_info(convert, {abs, ceil, new_convert});
ov::replace_node(convert, new_convert);
return true;
} else {
convert->set_convert_element_type(to);
return true;
}
}
return false;
}
static void TransformationUpToCPUSpecificOpSet(std::shared_ptr<ngraph::Function> nGraphFunc, const bool _enableLPT, const bool _enableBF16,
const bool _enableSnippets, const bool isLegacyApi) {
ngraph::pass::Manager manager;
@@ -304,6 +328,7 @@ static void TransformationUpToCPUSpecificOpSet(std::shared_ptr<ngraph::Function>
};
static const auto precisions = get_convert_precisions();
type_to_fuse_map type_to_fuse = {{ov::opset10::Convert::get_type_info_static(), fuse_type_to_convert}};
manager.register_pass<ov::pass::AUGRUCellFusion>();
manager.register_pass<ngraph::pass::CommonOptimizations>();
@@ -330,7 +355,7 @@ static void TransformationUpToCPUSpecificOpSet(std::shared_ptr<ngraph::Function>
manager.register_pass<ngraph::pass::low_precision::ConvertSubtractConstant>(defaultPrecisions);
}
manager.register_pass<ngraph::pass::Validate>();
manager.register_pass<ngraph::pass::ConvertPrecision>(precisions);
manager.register_pass<ngraph::pass::ConvertPrecision>(precisions, type_to_fuse);
manager.register_pass<ngraph::pass::EliminateConvert>();
manager.register_pass<SwapConvertTranspose>();
manager.register_pass<ConvertToInteraction>();
@@ -3,6 +3,7 @@
//
#include "shared_test_classes/base/layer_test_utils.hpp"
#include "common_test_utils/ov_tensor_utils.hpp"
#include "test_utils/cpu_test_utils.hpp"
#include "ngraph_functions/builders.hpp"
#include "shared_test_classes/base/ov_subgraph.hpp"
@@ -47,7 +48,6 @@ protected:
targetDevice = CommonTestUtils::DEVICE_CPU;
InputShape shapes;
InferenceEngine::Precision inPrc, outPrc;
CPUSpecificParams cpuParams;
std::tie(shapes, inPrc, outPrc, cpuParams) = GetParam();
@@ -68,6 +68,45 @@ protected:
function = makeNgraphFunction(ngPrc, params, conversion, "ConversionCPU");
}
void generate_inputs(const std::vector<ngraph::Shape>& targetInputStaticShapes) override {
if (outPrc != Precision::BOOL) {
SubgraphBaseTest::generate_inputs(targetInputStaticShapes);
return;
}
// In the scenario where input precision is floating point and output precision is boolean,
// for CPU plugin, the output precision boolean will be converted to u8 during common transformation,
// the elements in the output tensor will retain the format of u8 with the range [0, 255].
// But the output precision in ngraph reference is literal boolean, the elements are either 0 or 1.
// Here input floating points values are set to be in the range of [-1, 1], so no extra precision
// converting between actual output and expected output will be needed from the side of single layer tests.
inputs.clear();
const auto& funcInputs = function->inputs();
auto shape = targetInputStaticShapes.front();
size_t size = shape_size(shape);
ov::Tensor tensor = ov::test::utils::create_and_fill_tensor(funcInputs[0].get_element_type(), shape, 2 * size);
if (inPrc == Precision::FP32) {
auto *rawBlobDataPtr = static_cast<float *>(tensor.data());
for (size_t i = 0; i < size; ++i) {
rawBlobDataPtr[i] = rawBlobDataPtr[i] / size - 1;
}
} else if (inPrc == Precision::BF16) {
auto *rawBlobDataPtr = static_cast<ngraph::bfloat16 *>(tensor.data());
for (size_t i = 0; i < size; ++i) {
rawBlobDataPtr[i] = rawBlobDataPtr[i] / size - 1;
}
} else {
FAIL() << "Generating inputs with precision" << inPrc << " isn't supported, if output precision is boolean.";
}
inputs.insert({funcInputs[0].get_node_shared_ptr(), tensor});
}
private:
InferenceEngine::Precision inPrc, outPrc;
};
TEST_P(ConvertCPULayerTest, CompareWithRefs) {
@@ -112,6 +151,11 @@ const std::vector<Precision> precisions = {
Precision::BF16
};
const std::vector<Precision> precisions_floating_point = {
Precision::FP32,
Precision::BF16
};
std::vector<CPUSpecificParams> memForm4D = {
CPUSpecificParams({nchw}, {nchw}, {}, {}),
CPUSpecificParams({nhwc}, {nhwc}, {}, {}),
@@ -127,4 +171,12 @@ INSTANTIATE_TEST_SUITE_P(smoke_ConvertCPULayerTest, ConvertCPULayerTest,
::testing::ValuesIn(memForm4D)),
ConvertCPULayerTest::getTestCaseName);
INSTANTIATE_TEST_SUITE_P(smoke_ConvertCPULayerTest_BOOL, ConvertCPULayerTest,
::testing::Combine(
::testing::ValuesIn(inShapes_4D),
::testing::ValuesIn(precisions_floating_point),
::testing::Values(Precision::BOOL),
::testing::Values(CPUSpecificParams({nchw}, {nchw}, {}, {}))),
ConvertCPULayerTest::getTestCaseName);
} // namespace CPULayerTestsDefinitions