Low precision types support in Convert operation (#5640)

* Add initial version of u1 type support.

* Turn off u8_to_u1 test in IE.CPU.

* Fix compilation issue.

* Replace std::memset with std::fill.

* Add u4 type support.

* Add i4 support.

* LP types support generalized.

* Remove std::copy optimization.

* Fix backend test for LP types.

* Fixed arm plugin compilation.

* Add LP types to Serialization SLT.

* Add Convert to summarize.py report.
This commit is contained in:
Jozef Daniecki authored and GitHub committed 2021-05-21 13:11:43 +03:00
1 parent 5ea8437c91
commit 9e46be75af
6 files changed
+336 -63

No files matched your search

@@ -12,13 +12,14 @@ namespace {
const std::vector<std::vector<size_t>> inShape = {{1, 2, 3, 4}};
const std::vector<InferenceEngine::Precision> precisions = {
InferenceEngine::Precision::BOOL, InferenceEngine::Precision::U8,
InferenceEngine::Precision::I8, InferenceEngine::Precision::U16,
InferenceEngine::Precision::I16, InferenceEngine::Precision::U32,
InferenceEngine::Precision::I32, InferenceEngine::Precision::U64,
InferenceEngine::Precision::I64, InferenceEngine::Precision::BF16,
InferenceEngine::Precision::FP16, InferenceEngine::Precision::FP32,
InferenceEngine::Precision::FP64};
InferenceEngine::Precision::BOOL, InferenceEngine::Precision::BIN,
InferenceEngine::Precision::U4, InferenceEngine::Precision::U8,
InferenceEngine::Precision::I4, InferenceEngine::Precision::I8,
InferenceEngine::Precision::U16, InferenceEngine::Precision::I16,
InferenceEngine::Precision::U32, InferenceEngine::Precision::I32,
InferenceEngine::Precision::U64, InferenceEngine::Precision::I64,
InferenceEngine::Precision::BF16, InferenceEngine::Precision::FP16,
InferenceEngine::Precision::FP32, InferenceEngine::Precision::FP64};
TEST_P(ConvertLayerTest, Serialize) {
Serialize();
@@ -16,6 +16,7 @@ VERIFIED_OP_REFERENCES = [
'CTCGreedyDecoder-1',
'CTCGreedyDecoderSeqLen-6',
'Concat-1',
'Convert-1',
'ConvertLike-1',
'Convolution-1',
'Constant-1',
@@ -6,6 +6,7 @@
#include <cstddef>
#include "ngraph/type/element_type.hpp"
#include "ngraph/type/float16.hpp"
namespace ngraph
@@ -14,6 +15,118 @@ namespace ngraph
{
namespace reference
{
namespace detail
{
inline void set_u1(uint8_t* buf, size_t idx, uint8_t val)
{
const int byte_idx = idx / 8;
const int bit_idx = 7 - (idx % 8);
if (val)
{
buf[byte_idx] |= (1 << bit_idx);
}
else
{
buf[byte_idx] &= ~(1 << bit_idx);
}
}
inline uint8_t get_u1(const uint8_t* buf, size_t idx)
{
const int byte_idx = idx / 8;
const int bit_idx = 7 - (idx % 8);
return (buf[byte_idx] & (1 << bit_idx)) ? 1 : 0;
}
inline void set_u4(uint8_t* buf, size_t idx, uint8_t val)
{
const int byte_idx = idx / 2;
const int bit_shift = 4 * (++idx % 2);
buf[byte_idx] &= ~(0xF << bit_shift); // half byte zeroed
buf[byte_idx] |= (val << bit_shift); // set 1's
}
inline uint8_t get_u4(const uint8_t* buf, size_t idx)
{
const int byte_idx = idx / 2;
const int bit_shift = 4 * (++idx % 2);
return (buf[byte_idx] >> bit_shift) & 0xF;
}
inline void set_i4(uint8_t* buf, size_t idx, int8_t val)
{
const int byte_idx = idx / 2;
const int bit_shift = 4 * (++idx % 2);
buf[byte_idx] &= ~(0xF << bit_shift); // half byte zeroed
buf[byte_idx] |= (val << bit_shift); // set 1's
}
inline int8_t get_i4(const uint8_t* buf, size_t idx)
{
const int byte_idx = idx / 2;
const int bit_shift = 4 * (++idx % 2);
uint8_t val = (buf[byte_idx] >> bit_shift) & 0xF;
if (val & 0x08)
{ // negative number
val |= 0xF0;
}
return val;
}
template <typename T>
T get_value(const uint8_t* buf, size_t idx, element::Type type)
{
if (type == element::u1)
{
return detail::get_u1(buf, idx);
}
if (type == element::u4)
{
return detail::get_u4(buf, idx);
}
if (type == element::i4)
{
return detail::get_i4(buf, idx);
}
return static_cast<T>(buf[idx]);
}
template <typename TI, typename TO>
void lp_convert(const TI* arg,
TO* out,
size_t count,
element::Type_t src_type,
element::Type_t dst_type)
{
const uint8_t* input = reinterpret_cast<const uint8_t*>(arg);
uint8_t* output = reinterpret_cast<uint8_t*>(out);
for (size_t i = 0; i < count; ++i)
{
if (dst_type == element::u1)
{
detail::set_u1(
output, i, detail::get_value<uint8_t>(input, i, src_type));
}
else if (dst_type == element::u4)
{
detail::set_u4(
output, i, detail::get_value<uint8_t>(input, i, src_type));
}
else if (dst_type == element::i4)
{
detail::set_i4(
output, i, detail::get_value<int8_t>(input, i, src_type));
}
else
{
out[i] = detail::get_value<TO>(input, i, src_type);
}
}
}
} // namespace detail
template <typename TI, typename TO>
typename std::enable_if<!std::is_same<TO, char>::value>::type
convert(const TI* arg, TO* out, size_t count)
+27 -20
View File
@@ -26,22 +26,6 @@ op::Convert::Convert(const Output<Node>& arg, const element::Type& destination_t
void op::Convert::validate_and_infer_types()
{
NGRAPH_OP_SCOPE(v0_Convert_validate_and_infer_types);
const element::Type data_et = get_input_element_type(0);
const element::Type destination_et = m_destination_type;
NODE_VALIDATION_CHECK(this,
data_et != element::u1 && data_et != element::u4 &&
data_et != element::i4,
"Input element type '",
data_et,
"' is not supported.");
NODE_VALIDATION_CHECK(this,
destination_et != element::u1 && destination_et != element::u4 &&
destination_et != element::i4,
"Destination element type '",
destination_et,
"' is not supported.");
set_output_type(0, m_destination_type, get_input_partial_shape(0));
}
@@ -68,10 +52,27 @@ namespace convert
{
out->set_shape(arg->get_shape());
size_t element_count = shape_size(out->get_shape());
return (INPUT_ET == arg->get_element_type()) && OUTPUT_ET == out->get_element_type() &&
(runtime::reference::convert(
arg->get_data_ptr<INPUT_ET>(), out->get_data_ptr<OUTPUT_ET>(), element_count),
true);
if ((INPUT_ET != arg->get_element_type()) || OUTPUT_ET != out->get_element_type())
{
return false;
}
if (((INPUT_ET == element::u1) || (OUTPUT_ET == element::u1)) ||
((INPUT_ET == element::u4) || (OUTPUT_ET == element::u4)) ||
((INPUT_ET == element::i4) || (OUTPUT_ET == element::i4)))
{
runtime::reference::detail::lp_convert(arg->get_data_ptr<INPUT_ET>(),
out->get_data_ptr<OUTPUT_ET>(),
element_count,
INPUT_ET,
OUTPUT_ET);
}
else
{
runtime::reference::convert(
arg->get_data_ptr<INPUT_ET>(), out->get_data_ptr<OUTPUT_ET>(), element_count);
}
return true;
}
#define TYPE_OUT_CASE(a, ...) \
@@ -89,10 +90,13 @@ namespace convert
switch (out->get_element_type())
{
TYPE_OUT_CASE(i4, arg, out);
TYPE_OUT_CASE(i8, arg, out);
TYPE_OUT_CASE(i16, arg, out);
TYPE_OUT_CASE(i32, arg, out);
TYPE_OUT_CASE(i64, arg, out);
TYPE_OUT_CASE(u1, arg, out);
TYPE_OUT_CASE(u4, arg, out);
TYPE_OUT_CASE(u8, arg, out);
TYPE_OUT_CASE(u16, arg, out);
TYPE_OUT_CASE(u32, arg, out);
@@ -112,7 +116,10 @@ namespace convert
bool rc = true;
switch (arg->get_element_type())
{
NGRAPH_TYPE_CASE(evaluate_convert, u1, arg, out);
NGRAPH_TYPE_CASE(evaluate_convert, u4, arg, out);
NGRAPH_TYPE_CASE(evaluate_convert, u8, arg, out);
NGRAPH_TYPE_CASE(evaluate_convert, i4, arg, out);
NGRAPH_TYPE_CASE(evaluate_convert, i8, arg, out);
NGRAPH_TYPE_CASE(evaluate_convert, i32, arg, out);
NGRAPH_TYPE_CASE(evaluate_convert, i16, arg, out);
+178 -36
View File
@@ -23,6 +23,15 @@ static string s_manifest = "${MANIFEST}";
using TestEngine = test::ENGINE_CLASS_NAME(${BACKEND_NAME});
namespace
{
std::shared_ptr<Function> CreateFunction(const Shape& input_shape,
const element::Type& input_type,
const element::Type& expected_output_type)
{
const auto in = make_shared<op::Parameter>(input_type, input_shape);
const auto convert = make_shared<op::Convert>(in, expected_output_type);
return make_shared<Function>(NodeVector{convert}, ParameterVector{in});
}
template <typename T_IN, typename T_OUT>
void ConvertTest(const std::vector<T_IN>& input,
const Shape& input_shape,
@@ -30,16 +39,34 @@ namespace
const std::vector<T_OUT>& expected_output,
const ngraph::element::Type& expected_output_type)
{
const auto in = make_shared<op::Parameter>(input_type, input_shape);
const auto convert = make_shared<op::Convert>(in, expected_output_type);
const auto f = make_shared<Function>(NodeVector{convert}, ParameterVector{in});
const auto f = CreateFunction(input_shape, input_type, expected_output_type);
auto test_case = test::TestCase<TestEngine>(f);
test_case.add_input(input);
test_case.add_expected_output(expected_output);
test_case.run();
}
// TestCase doesn't support LP types
template <typename T_IN, typename T_OUT>
void LPConvertTest(const std::vector<T_IN>& input,
const Shape& input_shape,
const ngraph::element::Type& input_type,
const std::vector<T_OUT>& expected_output,
const ngraph::element::Type& expected_output_type)
{
const auto f = CreateFunction(input_shape, input_type, expected_output_type);
auto backend = runtime::Backend::create("${BACKEND_NAME}");
auto input_tesnor = backend->create_tensor(input_type, input_shape);
copy_data(input_tesnor, input);
auto output = backend->create_tensor(expected_output_type, input_shape);
auto handle = backend->compile(f);
handle->call_with_validate({output}, {input_tesnor});
std::vector<uint8_t> result(expected_output.size());
output->read(result.data(), result.size() * sizeof(uint8_t));
EXPECT_TRUE(test::all_close(expected_output, result));
}
} // namespace
// destination: boolean
@@ -119,17 +146,28 @@ NGRAPH_TEST(${BACKEND_NAME}, convert_u8_to_f16)
}
// destination: f32
NGRAPH_TEST(${BACKEND_NAME}, convert_i4_to_f32_is_not_supported_yet)
NGRAPH_TEST(${BACKEND_NAME}, convert_i4_to_f32)
{
const std::vector<int8_t> input{0x00, 0x00};
const std::vector<uint8_t> input{0xFE, 0xF2};
const Shape input_shape{2, 2};
const element::Type input_type = ngraph::element::i4;
const std::vector<float> expected_output{0.0f, 0.0f, 0.0f, 0.0f};
const std::vector<float> expected_output{-1.0f, -2.0f, -1.0f, 2.0f};
const element::Type expected_output_type = ngraph::element::f32;
ASSERT_THROW(ConvertTest(input, input_shape, input_type, expected_output, expected_output_type),
ngraph::NodeValidationFailure);
{
const auto f = CreateFunction(input_shape, input_type, expected_output_type);
auto backend = runtime::Backend::create("${BACKEND_NAME}");
auto input_tesnor = backend->create_tensor(input_type, input_shape);
copy_data(input_tesnor, input);
auto output = backend->create_tensor(expected_output_type, input_shape);
auto handle = backend->compile(f);
handle->call_with_validate({output}, {input_tesnor});
std::vector<float> result(expected_output.size());
output->read(result.data(), result.size() * sizeof(float));
EXPECT_TRUE(test::all_close_f(expected_output, result));
}
}
NGRAPH_TEST(${BACKEND_NAME}, convert_i8_to_f32)
@@ -180,30 +218,52 @@ NGRAPH_TEST(${BACKEND_NAME}, convert_i64_to_f32)
ConvertTest(input, input_shape, input_type, expected_output, expected_output_type);
}
NGRAPH_TEST(${BACKEND_NAME}, convert_u1_to_f32_is_not_supported_yet)
NGRAPH_TEST(${BACKEND_NAME}, convert_u1_to_f32)
{
const std::vector<uint8_t> input{0x00};
const std::vector<uint8_t> input{0xA0};
const Shape input_shape{2, 2};
const element::Type input_type = ngraph::element::u1;
const std::vector<float> expected_output{0.0f, 0.0f, 0.0f, 0.0f};
const std::vector<float> expected_output{1.0f, 0.0f, 1.0f, 0.0f};
const element::Type expected_output_type = ngraph::element::f32;
ASSERT_THROW(ConvertTest(input, input_shape, input_type, expected_output, expected_output_type),
ngraph::NodeValidationFailure);
{
const auto f = CreateFunction(input_shape, input_type, expected_output_type);
auto backend = runtime::Backend::create("${BACKEND_NAME}");
auto input_tesnor = backend->create_tensor(input_type, input_shape);
copy_data(input_tesnor, input);
auto output = backend->create_tensor(expected_output_type, input_shape);
auto handle = backend->compile(f);
handle->call_with_validate({output}, {input_tesnor});
std::vector<float> result(expected_output.size());
output->read(result.data(), result.size() * sizeof(float));
EXPECT_TRUE(test::all_close_f(expected_output, result));
}
}
NGRAPH_TEST(${BACKEND_NAME}, convert_u4_to_f32_is_not_supported_yet)
NGRAPH_TEST(${BACKEND_NAME}, convert_u4_to_f32)
{
const std::vector<uint8_t> input{0x00, 0x00};
const std::vector<uint8_t> input{0xFB, 0x0A};
const Shape input_shape{2, 2};
const element::Type input_type = ngraph::element::u4;
const std::vector<float> expected_output{0.0f, 0.0f, 0.0f, 0.0f};
const std::vector<float> expected_output{15.0f, 11.0f, 0.0f, 10.0f};
const element::Type expected_output_type = ngraph::element::f32;
ASSERT_THROW(ConvertTest(input, input_shape, input_type, expected_output, expected_output_type),
ngraph::NodeValidationFailure);
{
const auto f = CreateFunction(input_shape, input_type, expected_output_type);
auto backend = runtime::Backend::create("${BACKEND_NAME}");
auto input_tesnor = backend->create_tensor(input_type, input_shape);
copy_data(input_tesnor, input);
auto output = backend->create_tensor(expected_output_type, input_shape);
auto handle = backend->compile(f);
handle->call_with_validate({output}, {input_tesnor});
std::vector<float> result(expected_output.size());
output->read(result.data(), result.size() * sizeof(float));
EXPECT_TRUE(test::all_close_f(expected_output, result));
}
}
NGRAPH_TEST(${BACKEND_NAME}, convert_u8_to_f32)
@@ -295,17 +355,28 @@ NGRAPH_TEST(${BACKEND_NAME}, convert_f32_to_f32)
// not supported by IE, hence no tests
// destination: i4
NGRAPH_TEST(${BACKEND_NAME}, convert_u8_to_i4_is_not_supported_yet)
NGRAPH_TEST(${BACKEND_NAME}, convert_u8_to_i4)
{
const std::vector<uint8_t> input{0, 0, 0, 0};
const std::vector<uint8_t> input{1, 2, 0, 3};
const Shape input_shape{4};
const element::Type input_type = ngraph::element::u8;
const std::vector<uint8_t> expected_output{0x00, 0x00};
const std::vector<uint8_t> expected_output{0x12, 0x03};
const element::Type expected_output_type = ngraph::element::i4;
ASSERT_THROW(ConvertTest(input, input_shape, input_type, expected_output, expected_output_type),
ngraph::NodeValidationFailure);
{
const auto f = CreateFunction(input_shape, input_type, expected_output_type);
auto backend = runtime::Backend::create("${BACKEND_NAME}");
auto input_tesnor = backend->create_tensor(input_type, input_shape);
copy_data(input_tesnor, input);
auto output = backend->create_tensor(expected_output_type, input_shape);
auto handle = backend->compile(f);
handle->call_with_validate({output}, {input_tesnor});
std::vector<uint8_t> result(expected_output.size());
output->read(result.data(), result.size() * sizeof(uint8_t));
EXPECT_TRUE(test::all_close(expected_output, result));
}
}
// destination: i8
@@ -361,31 +432,102 @@ NGRAPH_TEST(${BACKEND_NAME}, convert_u8_to_i64)
}
// destination: u1
NGRAPH_TEST(${BACKEND_NAME}, convert_u8_to_u1_is_not_supported_yet)
NGRAPH_TEST(${BACKEND_NAME}, convert_u1_to_u1)
{
const std::vector<uint8_t> input{0, 0, 0, 0};
const Shape input_shape{4};
const element::Type input_type = ngraph::element::u8;
const std::vector<uint8_t> input{0xF0};
const Shape input_shape{8};
const element::Type input_type = ngraph::element::u1;
const std::vector<uint8_t> expected_output{0x00};
const std::vector<uint8_t> expected_output{0xF0};
const element::Type expected_output_type = ngraph::element::u1;
ASSERT_THROW(ConvertTest(input, input_shape, input_type, expected_output, expected_output_type),
ngraph::NodeValidationFailure);
LPConvertTest(input, input_shape, input_type, expected_output, expected_output_type);
}
NGRAPH_TEST(${BACKEND_NAME}, convert_u4_to_u1)
{
const std::vector<uint8_t> input{0x10, 0x01, 0x00, 0x00};
const Shape input_shape{8};
const element::Type input_type = ngraph::element::u4;
const std::vector<uint8_t> expected_output{0x90};
const element::Type expected_output_type = ngraph::element::u1;
LPConvertTest(input, input_shape, input_type, expected_output, expected_output_type);
}
NGRAPH_TEST(${BACKEND_NAME}, convert_u8_to_u1)
{
const std::vector<uint8_t> input{1, 0, 1, 0, 0, 0, 0, 1};
const Shape input_shape{8};
const element::Type input_type = ngraph::element::u8;
const std::vector<uint8_t> expected_output{0xA1};
const element::Type expected_output_type = ngraph::element::u1;
LPConvertTest(input, input_shape, input_type, expected_output, expected_output_type);
}
NGRAPH_TEST(${BACKEND_NAME}, convert_i4_to_u1)
{
const std::vector<uint8_t> input{0x10, 0x01, 0x00, 0x00};
const Shape input_shape{8};
const element::Type input_type = ngraph::element::u4;
const std::vector<uint8_t> expected_output{0x90};
const element::Type expected_output_type = ngraph::element::u1;
LPConvertTest(input, input_shape, input_type, expected_output, expected_output_type);
}
// destination: u4
NGRAPH_TEST(${BACKEND_NAME}, convert_u8_to_u4_is_not_supported_yet)
NGRAPH_TEST(${BACKEND_NAME}, convert_u1_to_u4)
{
const std::vector<uint8_t> input{0, 0, 0, 0};
const std::vector<uint8_t> input{0xF0};
const Shape input_shape{4};
const element::Type input_type = ngraph::element::u1;
const std::vector<uint8_t> expected_output{0x11, 0x11};
const element::Type expected_output_type = ngraph::element::u4;
LPConvertTest(input, input_shape, input_type, expected_output, expected_output_type);
}
NGRAPH_TEST(${BACKEND_NAME}, convert_u4_to_u4)
{
const std::vector<uint8_t> input{0x22, 0x33};
const Shape input_shape{4};
const element::Type input_type = ngraph::element::u4;
const std::vector<uint8_t> expected_output{0x22, 0x33};
const element::Type expected_output_type = ngraph::element::u4;
LPConvertTest(input, input_shape, input_type, expected_output, expected_output_type);
}
NGRAPH_TEST(${BACKEND_NAME}, convert_u8_to_u4)
{
const std::vector<uint8_t> input{7, 0, 1, 15};
const Shape input_shape{4};
const element::Type input_type = ngraph::element::u8;
const std::vector<uint8_t> expected_output{0x00, 0x00};
const std::vector<uint8_t> expected_output{0x70, 0x1F};
const element::Type expected_output_type = ngraph::element::u4;
ASSERT_THROW(ConvertTest(input, input_shape, input_type, expected_output, expected_output_type),
ngraph::NodeValidationFailure);
LPConvertTest(input, input_shape, input_type, expected_output, expected_output_type);
}
NGRAPH_TEST(${BACKEND_NAME}, convert_i4_to_u4)
{
const std::vector<uint8_t> input{0x22, 0x33};
const Shape input_shape{4};
const element::Type input_type = ngraph::element::i4;
const std::vector<uint8_t> expected_output{0x22, 0x33};
const element::Type expected_output_type = ngraph::element::u4;
LPConvertTest(input, input_shape, input_type, expected_output, expected_output_type);
}
// destination: u8
@@ -1050,8 +1050,11 @@ roll_negative_axes
# convert operation
IE_CPU.convert_f16_to_f32
IE_CPU.convert_u8_to_f16
IE_CPU.convert_u8_to_i4
IE_CPU.convert_u8_to_i16
IE_CPU.convert_u8_to_i64
IE_CPU.convert_u8_to_u1
IE_CPU.convert_u8_to_u4
IE_CPU.convert_u8_to_u16
IE_CPU.convert_u8_to_u32
IE_CPU.convert_u8_to_u64
@@ -1062,6 +1065,12 @@ IE_CPU.convert_u32_to_f32 # NOT_IMPLEMENTED
IE_CPU.convert_i4_to_f32 # NOT_IMPLEMENTED
IE_CPU.convert_u1_to_f32 # NOT_IMPLEMENTED
IE_CPU.convert_u4_to_f32 # NOT_IMPLEMENTED
IE_CPU.convert_u1_to_u1
IE_CPU.convert_u4_to_u1
IE_CPU.convert_i4_to_u1
IE_CPU.convert_u1_to_u4
IE_CPU.convert_u4_to_u4
IE_CPU.convert_i4_to_u4
#-------------------------------------------------------------------------------
#