[CPU][GPU] Mismatch tensor and port types: i64 vs i32 (#23859)

HETERO plugin ('HETERO:GPU,CPU') raises an error [GPU] Mismatch tensor
and port types: i64 vs i32

### Details:
 - *Do not convert Result element type after Extension if required;*
- *Eliminate converts after the second call of the ConvertPrecisoin
pass;*
 - *Support string input in the `benchmark_app`*.


### Tickets:
 - *136752*
This commit is contained in:
Nikolay Shchegolev 2024-04-18 11:18:15 +04:00 committed by GitHub
parent a3ababde4d
commit 77f69957b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 136 additions and 49 deletions

View File

@ -584,6 +584,24 @@ ov::Tensor get_random_tensor(const std::pair<std::string, benchmark_app::InputIn
return create_tensor_random<int16_t, int16_t>(inputInfo.second);
} else if (type == ov::element::boolean) {
return create_tensor_random<uint8_t, uint32_t>(inputInfo.second, 0, 1);
} else if (type == ov::element::string) {
const auto& in_info = inputInfo.second;
const auto tensor_size = ov::shape_size(in_info.dataShape);
auto tensor = ov::Tensor(in_info.type, in_info.dataShape);
auto data = tensor.data<std::string>();
std::mt19937 str_len_gen(0);
uniformDistribution<uint32_t> len_distribution(20, 50);
std::mt19937 char_val_gen(0);
uniformDistribution<uint32_t> char_distribution(0, 127);
for (size_t i = 0; i < tensor_size; i++) {
data[i].resize(len_distribution(str_len_gen));
for (size_t j = 0lu; j < data[i].size(); j++) {
data[i][j] = static_cast<char>(char_distribution(char_val_gen));
}
}
return tensor;
} else {
OPENVINO_THROW("Input type is not supported for " + inputInfo.first);
}

View File

@ -1,15 +1,15 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2018-2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "insert_convert_after_extension.hpp"
#include <openvino/op/convert.hpp>
#include "openvino/op/convert.hpp"
#include "cpu_types.h"
#include "itt.hpp"
#include <transformations/utils/utils.hpp>
#include "transformations/utils/utils.hpp"
ov::pass::InsertConvertAfterExtension::InsertConvertAfterExtension() {
ov::pass::InsertConvertAfterExtension::InsertConvertAfterExtension(bool convert_output_precision) {
MATCHER_SCOPE(InsertConvertAfterExtension);
auto i64_extension = [](const ov::Output<ov::Node>& output) -> bool {
@ -29,6 +29,10 @@ ov::pass::InsertConvertAfterExtension::InsertConvertAfterExtension() {
auto convert = std::make_shared<op::v0::Convert>(output, ov::element::i32);
for (const auto& targetInput : targetInputs) {
// Keep the original output element type if required.
if (!convert_output_precision && is_type<op::v0::Result>(targetInput.get_node())) {
continue;
}
targetInput.replace_source_output(convert);
}

View File

@ -1,10 +1,10 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2018-2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <openvino/pass/graph_rewrite.hpp>
#include "openvino/pass/graph_rewrite.hpp"
namespace ov {
namespace pass {
@ -15,7 +15,7 @@ namespace pass {
class InsertConvertAfterExtension: public ov::pass::MatcherPass {
public:
OPENVINO_RTTI("InsertConvertAfterExtension", "0");
InsertConvertAfterExtension();
InsertConvertAfterExtension(bool convert_output_precision = true);
};
} // namespace pass

View File

@ -54,6 +54,7 @@ inline void ConvertToCPUSpecificOpset(std::shared_ptr<ov::Model> &nGraphFunc, in
false,
false);
CPU_REGISTER_PASS_COMMON(manager, ov::pass::Validate);
CPU_REGISTER_PASS_COMMON(manager, ov::pass::EliminateConvert); // Need to clean up after the ConvertPrecision.
manager.run_passes(nGraphFunc);
}

View File

@ -405,9 +405,12 @@ void Transformations::PreLpt(const std::vector<ov::element::Type>& defaultPrecis
// Common ConvertPrecision pass handles only a limited set of opevino operations to match the list of precisions supported by the plugin.
// However, if the extension operation produces an output precision that is not natively supported, this may lead to inconsistency during
// element type propagation. This transformation is called before the ConvertPrecision pass to align the actual precisions with the list of supported ones.
CPU_REGISTER_PASS_COMMON(manager, ov::pass::InsertConvertAfterExtension);
constexpr bool convert_input_output_precision = false;
CPU_REGISTER_PASS_COMMON(manager, ov::pass::InsertConvertAfterExtension, convert_input_output_precision);
// Do not insert pass::Validate between pass::InsertConvertAfterExtension and pass::ConvertPrecision.
// This may result in the loss of the original Element type of the Output .
// element type convert is disabled.
CPU_REGISTER_PASS_COMMON(manager, ov::pass::ConvertPrecision, precisions, type_to_fuse, false, false);
CPU_REGISTER_PASS_COMMON(manager, ov::pass::ConvertPrecision, precisions, type_to_fuse, false, convert_input_output_precision);
CPU_REGISTER_PASS_COMMON(manager, ov::pass::EliminateConvert);
CPU_REGISTER_PASS_COMMON(manager, SwapConvertTranspose);

View File

@ -2,46 +2,71 @@
// SPDX-License-Identifier: Apache-2.0
//
#include <common_test_utils/ov_tensor_utils.hpp>
#include <openvino/op/op.hpp>
#include <shared_test_classes/base/ov_subgraph.hpp>
#include "common_test_utils/ov_tensor_utils.hpp"
#include "openvino/op/op.hpp"
#include "shared_test_classes/base/ov_subgraph.hpp"
#include "utils/cpu_test_utils.hpp"
// --------------------- ---------------------
// | PARAMETER | | PARAMETER |
// --------------------- ---------------------
// | | | |
// ------------- ---------- ------------- ----------
// | EXT_I64 | | CPU_OP | | EXT_I64 | | CPU_OP |
// ------------- ---------- -------\ ------------- ----------
// |i64 |i32 | \ |i64 |i32 |
// -------- --------- ------------- / -------------- --------- -------------
// | CPU_OP | | RES_I32 | | EXT_I64 | -------/ | CVT I64->I32 | | RES_I32 | | EXT_I64 |
// -------- --------- ------------- -------------- --------- -------------
// |i64 |i64 |i32 |i32 |i64 |i32
// --------- --------- --------- -------- --------- ---------
// | RES_I64 | | RES_I64 | | RES_I32 | | CPU_OP | | RES_I64 | | RES_I32 |
// --------- --------- --------- -------- --------- ---------
// |i32
// --------------
// | CVT I32->I64 |
// --------------
// |i64
// ---------
// | RES_I64 |
// ---------
using namespace ov::test;
using namespace CPUTestUtils;
namespace ov {
namespace test {
using CustomOpI64CPUTestParams = std::tuple<ElementType, InputShape>;
using CustomOpI64CPUTestParams = std::tuple<
ElementType, // Input element type
InputShape // Input shape
>;
class CustomOpI64 : public ov::op::Op {
public:
OPENVINO_OP("CustomOpI64");
CustomOpI64() = default;
CustomOpI64(const ov::OutputVector& args) : Op(args) {
CustomOpI64(const Output<Node>& arg) : Op({arg}) {
constructor_validate_and_infer_types();
}
void validate_and_infer_types() override {
const auto& inputs_count = input_values().size();
OPENVINO_ASSERT(inputs_count == 1,
"Input count must be 1, Got: ",
inputs_count);
OPENVINO_ASSERT(get_input_element_type(0) == ov::element::Type_t::i32,
"The input must be i32.");
OPENVINO_ASSERT(inputs_count == 1, "Input count must be 1, Got: ", inputs_count);
OPENVINO_ASSERT(get_input_element_type(0) == element::i32, "The input must be i32.");
set_output_size(2);
auto inShape = get_input_partial_shape(0);
auto in_shape = get_input_partial_shape(0);
set_output_type(0, ov::element::Type_t::i64, inShape);
set_output_type(1, ov::element::Type_t::i32, inShape);
set_output_type(0, element::i64, in_shape);
set_output_type(1, element::i32, in_shape);
}
std::shared_ptr<ov::Node> clone_with_new_inputs(const ov::OutputVector& new_args) const override {
OPENVINO_ASSERT(new_args.size() == 1, "Incorrect number of new arguments");
return std::make_shared<CustomOpI64>(new_args);
return std::make_shared<CustomOpI64>(new_args[0]);
}
bool visit_attributes(ov::AttributeVisitor& visitor) override {
@ -81,42 +106,44 @@ class CustomOpConvertI64CPUTest : public testing::WithParamInterface<CustomOpI64
public CPUTestsBase {
public:
static std::string getTestCaseName(const testing::TestParamInfo<CustomOpI64CPUTestParams>& obj) {
ElementType inType;
InputShape inputShape;
std::tie(inType, inputShape) = obj.param;
std::ostringstream result;
result << "IS=" << inputShape << "_";
result << "Prc=" << inType;
result << "InElType=" << std::get<0>(obj.param) << "_";
result << "IS=" << std::get<1>(obj.param);
return result.str();
}
protected:
void SetUp() override {
targetDevice = ov::test::utils::DEVICE_CPU;
targetDevice = test::utils::DEVICE_CPU;
ElementType inType;
InputShape inputShape;
std::tie(inType, inputShape) = this->GetParam();
const auto& params = this->GetParam();
const auto& in_el_type = std::get<0>(params);
const auto& in_shape = std::get<1>(params);
init_input_shapes({inputShape});
ov::ParameterVector inputParams;
ov::OutputVector paramsOuts;
for (auto&& shape : inputDynamicShapes) {
auto param = std::make_shared<ov::op::v0::Parameter>(inType, shape);
inputParams.push_back(param);
paramsOuts.push_back(param);
}
auto customOp = std::make_shared<CustomOpI64>(paramsOuts);
init_input_shapes({in_shape});
auto param = std::make_shared<op::v0::Parameter>(in_el_type, inputDynamicShapes[0]);
ov::ResultVector results{std::make_shared<ov::op::v0::Result>(customOp)};
function = std::make_shared<ov::Model>(results, inputParams, "customOpTest");
auto custom_op_0 = std::make_shared<CustomOpI64>(param);
auto i32_op_0 = std::make_shared<op::v1::LogicalNot>(custom_op_0->output(0));
auto i32_op_1 = std::make_shared<op::v1::LogicalNot>(param);
auto custom_op_1 = std::make_shared<CustomOpI64>(i32_op_1);
OutputVector results{
i32_op_0->output(0),
custom_op_0->output(1),
custom_op_1->output(0),
custom_op_1->output(1)
};
function = std::make_shared<ov::Model>(results, ParameterVector{param}, "customOpI64Test");
}
void generate_inputs(const std::vector<ov::Shape>& targetInputStaticShapes) override {
inputs.clear();
const auto& funcInputs = function->inputs();
for (size_t i = 0; i < funcInputs.size(); ++i) {
for (size_t i = 0lu; i < funcInputs.size(); ++i) {
const auto& funcInput = funcInputs[i];
auto tensor = ov::test::utils::create_and_fill_tensor(funcInput.get_element_type(), targetInputStaticShapes[i]);
inputs.insert({funcInput.get_node_shared_ptr(), tensor});
@ -138,8 +165,41 @@ protected:
TEST_P(CustomOpConvertI64CPUTest, CompareWithRefs) {
run();
// TODO: Graph could not be dumped with int64 for now. Swith on this in scope of int64 enabling.
// CPUTestUtils::CheckNumberOfNodesWithType(compiledModel, "Convert", 1);
size_t cvt_i64_i32_num = 0lu;
size_t cvt_i32_i64_num = 0lu;
size_t res_i64_num = 0lu;
size_t res_i32_num = 0lu;
for (const auto& node : compiledModel.get_runtime_model()->get_ops()) {
auto rt_info = node->get_rt_info();
auto it = rt_info.find(exec_model_info::LAYER_TYPE);
ASSERT_NE(rt_info.end(), it);
if (it->second.as<std::string>() == "Convert") {
if (node->get_output_element_type(0) == element::i64) {
cvt_i32_i64_num++;
} else if (node->get_output_element_type(0) == element::i32) {
cvt_i64_i32_num++;
} else {
FAIL() << "Unexpected convertion type: " << node->get_output_element_type(0);
}
}
if (it->second.as<std::string>() == "Output") {
if (node->get_output_element_type(0) == element::i64) {
res_i64_num++;
} else if (node->get_output_element_type(0) == element::i32) {
res_i32_num++;
} else {
FAIL() << "Unexpected Result type: " << node->get_output_element_type(0);
}
}
}
ASSERT_EQ(cvt_i32_i64_num, 1lu) << "Unexpected number of the Convert i32->i64 nodes.";
ASSERT_EQ(cvt_i64_i32_num, 1lu) << "Unexpected number of the Convert i64->i32 nodes.";
ASSERT_EQ(res_i64_num, 2lu) << "Unexpected number of the Result nodes with type i64.";
ASSERT_EQ(res_i32_num, 2lu) << "Unexpected number of the Result nodes with type i32.";
}
const InputShape inputShapes = {
@ -148,8 +208,9 @@ const InputShape inputShapes = {
INSTANTIATE_TEST_SUITE_P(smoke_CustomOp,
CustomOpConvertI64CPUTest,
::testing::Combine(::testing::Values(ElementType::i32), ::testing::Values(inputShapes)),
::testing::Combine(
::testing::Values(ElementType::i32),
::testing::Values(inputShapes)),
CustomOpConvertI64CPUTest::getTestCaseName);
} // namespace test
} // namespace ov