BinaryConvolution Reference Implementation (#4278)

* Add BinaryConvolution unit tests.

* Changed types to u1.

* Add BIN precision handling in TestCase class.

* Refactored validate and infer types to enhance dynamic shape inference

* Add type_prop test to cover invalid op cases and dynamic shapes

* Fix style

* Disable check for float type of data batch input

* Add type_prop test for incompatible input channels in inputs

* Disable backend unit tests

* Fix style

* Add reference implementation

* Add backend tests

* Add single layer tests

* Add check for float element type of batch data input

* Refactor backend test cases to compare with regular convolution

* Add serialization tests

* Clean up

* Add 1D and 3D tests into op_eval

* Changes in reference implementation to improve readability

* Add ticket information for todo tasks

* Fix implementation misbehavior for filter channels

* Add backend unit tests to cover strides, dilations, padding, channels and batches

* Add end of line into files

* Change name of type_prop unit tests

* Simplified lambda to get spatial dimensions of filters

* Add comment to support filters input as Parameter

* Add namespace details for BinaryConvolution utility functions

* Address review comments

Co-authored-by: jdanieck <jozef.daniecki@intel.com>
This commit is contained in:
Gabriele Galiero Casay 2021-03-09 20:58:25 +01:00 committed by GitHub
parent 99f94ca09c
commit bd949c6baf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1860 additions and 64 deletions

View File

@ -0,0 +1,63 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <vector>
#include "shared_test_classes/single_layer/binary_convolution.hpp"
using namespace LayerTestsDefinitions;
namespace {
TEST_P(BinaryConvolutionLayerTest, Serialize) {
Serialize();
}
const std::vector<InferenceEngine::Precision> netPrecisions = {
InferenceEngine::Precision::FP32, InferenceEngine::Precision::FP16};
const std::vector<std::vector<size_t>> kernels = {{3, 5}};
const std::vector<std::vector<size_t>> strides = {{1, 3}};
const std::vector<std::vector<ptrdiff_t>> padBegins = {{0, 3}};
const std::vector<std::vector<ptrdiff_t>> padEnds = {{0, 3}};
const std::vector<std::vector<size_t>> dilations = {{3, 1}};
const std::vector<size_t> numOutChannels = {5};
const std::vector<float> pad_values = {0, 1};
const auto binConv2DParams_ExplicitPadding = ::testing::Combine(
::testing::ValuesIn(kernels), ::testing::ValuesIn(strides),
::testing::ValuesIn(padBegins), ::testing::ValuesIn(padEnds),
::testing::ValuesIn(dilations), ::testing::ValuesIn(numOutChannels),
::testing::Values(ngraph::op::PadType::EXPLICIT),
::testing::ValuesIn(pad_values));
const auto binConv2DParams_AutoPadValid = ::testing::Combine(
::testing::ValuesIn(kernels), ::testing::ValuesIn(strides),
::testing::Values(std::vector<ptrdiff_t>({0, 0})),
::testing::Values(std::vector<ptrdiff_t>({0, 0})),
::testing::ValuesIn(dilations), ::testing::ValuesIn(numOutChannels),
::testing::Values(ngraph::op::PadType::VALID),
::testing::ValuesIn(pad_values));
INSTANTIATE_TEST_CASE_P(
smoke_BinaryConvolution2D_Serialization_ExplicitPadding, BinaryConvolutionLayerTest,
::testing::Combine(
binConv2DParams_ExplicitPadding, ::testing::ValuesIn(netPrecisions),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::Values(std::vector<size_t>({1, 3, 30, 30})),
::testing::Values(CommonTestUtils::DEVICE_CPU)),
BinaryConvolutionLayerTest::getTestCaseName);
INSTANTIATE_TEST_CASE_P(
smoke_BinaryConvolution2D__Serialization_AutoPadValid, BinaryConvolutionLayerTest,
::testing::Combine(
binConv2DParams_AutoPadValid, ::testing::ValuesIn(netPrecisions),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::Values(std::vector<size_t>({1, 3, 30, 30})),
::testing::Values(CommonTestUtils::DEVICE_CPU)),
BinaryConvolutionLayerTest::getTestCaseName);
} // namespace

View File

@ -0,0 +1,72 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <vector>
#include "single_layer_tests/binary_convolution.hpp"
#include "common_test_utils/test_constants.hpp"
using namespace LayerTestsDefinitions;
namespace {
const std::vector<InferenceEngine::Precision> netPrecisions = {
InferenceEngine::Precision::FP32, InferenceEngine::Precision::FP16};
/* ============= 2D Binary Convolution ============= */
const std::vector<std::vector<size_t>> kernels = {{3, 3}, {3, 5}};
const std::vector<std::vector<size_t>> strides = {{1, 1}, {1, 3}};
const std::vector<std::vector<ptrdiff_t>> padsBegin = {{0, 0}, {0, 3}};
const std::vector<std::vector<ptrdiff_t>> padsEnd = {{0, 0}, {0, 3}};
const std::vector<std::vector<size_t>> dilations = {{1, 1}, {3, 1}};
const std::vector<size_t> numOutChannels = {1, 5};
const std::vector<float> padValues = {0, 1};
const auto binConv2DParams_ExplicitPadding = ::testing::Combine(
::testing::ValuesIn(kernels),
::testing::ValuesIn(strides),
::testing::ValuesIn(padsBegin),
::testing::ValuesIn(padsEnd),
::testing::ValuesIn(dilations),
::testing::ValuesIn(numOutChannels),
::testing::Values(ngraph::op::PadType::EXPLICIT),
::testing::ValuesIn(padValues));
const auto binConv2DParams_ValidPadding = ::testing::Combine(
::testing::ValuesIn(kernels),
::testing::ValuesIn(strides),
::testing::Values(std::vector<ptrdiff_t>({0, 0})),
::testing::Values(std::vector<ptrdiff_t>({0, 0})),
::testing::ValuesIn(dilations),
::testing::ValuesIn(numOutChannels),
::testing::Values(ngraph::op::PadType::VALID),
::testing::ValuesIn(padValues));
INSTANTIATE_TEST_CASE_P(
smoke_BinaryConvolution2D_ExplicitPadding, BinaryConvolutionLayerTest,
::testing::Combine(
binConv2DParams_ExplicitPadding,
::testing::ValuesIn(netPrecisions),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::Values(std::vector<size_t>({1, 3, 30, 30})),
::testing::Values(CommonTestUtils::DEVICE_CPU)),
BinaryConvolutionLayerTest::getTestCaseName);
INSTANTIATE_TEST_CASE_P(
smoke_BinaryConvolution2D_AutoPadValid, BinaryConvolutionLayerTest,
::testing::Combine(
binConv2DParams_ValidPadding,
::testing::ValuesIn(netPrecisions),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::Values(std::vector<size_t>({1, 3, 30, 30})),
::testing::Values(CommonTestUtils::DEVICE_CPU)),
BinaryConvolutionLayerTest::getTestCaseName);
} // namespace

View File

@ -57,6 +57,8 @@ std::vector<std::string> disabledTestPatterns() {
R"(.*decomposition1_batch=5_hidden_size=10_input_size=30_.*tanh.relu.*_clip=0_linear_before_reset=1.*_targetDevice=CPU_.*)", R"(.*decomposition1_batch=5_hidden_size=10_input_size=30_.*tanh.relu.*_clip=0_linear_before_reset=1.*_targetDevice=CPU_.*)",
// Skip platforms that do not support BF16 (i.e. sse, avx, avx2) // Skip platforms that do not support BF16 (i.e. sse, avx, avx2)
R"(.*BF16.*(jit_avx(?!5)|jit_sse).*)", R"(.*BF16.*(jit_avx(?!5)|jit_sse).*)",
// TODO: Incorrect blob sizes for node BinaryConvolution_X
R"(.*BinaryConvolutionLayerTest.*)"
}; };
if (!InferenceEngine::with_cpu_x86_avx512_core()) { if (!InferenceEngine::with_cpu_x86_avx512_core()) {

View File

@ -0,0 +1,14 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include "shared_test_classes/single_layer/binary_convolution.hpp"
namespace LayerTestsDefinitions {
TEST_P(BinaryConvolutionLayerTest, CompareWithRefs) {
Run();
}
} // namespace LayerTestsDefinitions

View File

@ -0,0 +1,48 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <tuple>
#include <vector>
#include <string>
#include <memory>
#include "shared_test_classes/base/layer_test_utils.hpp"
#include "ngraph_functions/builders.hpp"
#include "ngraph_functions/utils/ngraph_helpers.hpp"
namespace LayerTestsDefinitions {
using binConvSpecificParams = std::tuple<
InferenceEngine::SizeVector, // Kernel size
InferenceEngine::SizeVector, // Strides
std::vector<ptrdiff_t>, // Pads begin
std::vector<ptrdiff_t>, // Pads end
InferenceEngine::SizeVector, // Dilations
size_t, // Num Output channels
ngraph::op::PadType, // Padding type
float>; // Padding value
using binaryConvolutionTestParamsSet = std::tuple<
binConvSpecificParams, //
InferenceEngine::Precision, // Network precision
InferenceEngine::Precision, // Input precision
InferenceEngine::Precision, // Output precision
InferenceEngine::Layout, // Input layout
InferenceEngine::Layout, // Output layout
InferenceEngine::SizeVector, // Input shape
LayerTestsUtils::TargetDevice>; // Device name
class BinaryConvolutionLayerTest : public testing::WithParamInterface<binaryConvolutionTestParamsSet>,
virtual public LayerTestsUtils::LayerTestsCommon {
public:
static std::string getTestCaseName(testing::TestParamInfo<binaryConvolutionTestParamsSet> obj);
InferenceEngine::Blob::Ptr GenerateInput(const InferenceEngine::InputInfo &info) const override;
protected:
void SetUp() override;
};
} // namespace LayerTestsDefinitions

View File

@ -0,0 +1,79 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "shared_test_classes/single_layer/binary_convolution.hpp"
namespace LayerTestsDefinitions {
std::string BinaryConvolutionLayerTest::getTestCaseName(testing::TestParamInfo<binaryConvolutionTestParamsSet> obj) {
binConvSpecificParams binConvParams;
InferenceEngine::Precision netPrecision;
InferenceEngine::Precision inPrc, outPrc;
InferenceEngine::Layout inLayout, outLayout;
InferenceEngine::SizeVector inputShape;
std::string targetDevice;
std::tie(binConvParams, netPrecision, inPrc, outPrc, inLayout, outLayout, inputShape, targetDevice) = obj.param;
ngraph::op::PadType padType;
InferenceEngine::SizeVector kernel, stride, dilation;
std::vector<ptrdiff_t> padBegin, padEnd;
size_t convOutChannels;
float padValue;
std::tie(kernel, stride, padBegin, padEnd, dilation, convOutChannels, padType, padValue) = binConvParams;
std::ostringstream result;
result << "IS=" << CommonTestUtils::vec2str(inputShape) << "_";
result << "KS=" << CommonTestUtils::vec2str(kernel) << "_";
result << "S=" << CommonTestUtils::vec2str(stride) << "_";
result << "PB=" << CommonTestUtils::vec2str(padBegin) << "_";
result << "PE=" << CommonTestUtils::vec2str(padEnd) << "_";
result << "D=" << CommonTestUtils::vec2str(dilation) << "_";
result << "O=" << convOutChannels << "_";
result << "AP=" << padType << "_";
result << "PV=" << padValue << "_";
result << "netPRC=" << netPrecision.name() << "_";
result << "inPRC=" << inPrc.name() << "_";
result << "outPRC=" << outPrc.name() << "_";
result << "inL=" << inLayout << "_";
result << "outL=" << outLayout << "_";
result << "trgDev=" << targetDevice;
return result.str();
}
InferenceEngine::Blob::Ptr BinaryConvolutionLayerTest::GenerateInput(const InferenceEngine::InputInfo &info) const {
InferenceEngine::Blob::Ptr blobPtr;
const std::string name = info.name();
// there is no input generation for filters since CPU implementation uses Constant
// TODO: enable filters input generation as Parameter when supported (Issue 50148)
if (name == "a_data_batch") {
blobPtr = FuncTestUtils::createAndFillBlob(info.getTensorDesc(), 1, 0, 1, 7235346);
}
return blobPtr;
}
void BinaryConvolutionLayerTest::SetUp() {
binConvSpecificParams binConvParams;
InferenceEngine::Precision netPrecision;
InferenceEngine::SizeVector inputShape;
std::tie(binConvParams, netPrecision, inPrc, outPrc, inLayout, outLayout, inputShape, targetDevice) =
this->GetParam();
ngraph::op::PadType padType;
InferenceEngine::SizeVector kernelSize, strides, dilations;
std::vector<ptrdiff_t> padsBegin, padsEnd;
size_t numOutChannels;
float padValue;
std::tie(kernelSize, strides, padsBegin, padsEnd, dilations, numOutChannels, padType, padValue) = binConvParams;
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
auto params = ngraph::builder::makeParams(ngPrc, {{"a_data_batch", inputShape}});
// TODO: refactor build BinaryConvolution op to accept filters input as Parameter
auto binConv = ngraph::builder::makeBinaryConvolution(params[0], kernelSize, strides, padsBegin, padsEnd, dilations, padType, numOutChannels,
padValue);
ngraph::ResultVector results{std::make_shared<ngraph::opset1::Result>(binConv)};
function = std::make_shared<ngraph::Function>(results, params, "BinaryConvolution");
}
} // namespace LayerTestsDefinitions

View File

@ -0,0 +1,224 @@
//*****************************************************************************
// Copyright 2021 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//*****************************************************************************
#pragma once
#include "ngraph/runtime/reference/convolution.hpp"
#include "ngraph/shape.hpp"
namespace ngraph
{
namespace runtime
{
namespace reference
{
namespace details
{
inline uint8_t extract_bit(uint8_t val, uint8_t bit)
{
return (uint8_t)((val >> bit) & 0x01);
}
template <typename T>
inline bool xnor(T a, T b)
{
return a == b;
}
template <typename T_IN, typename T_F>
void binary_convolve_3D_channels(const ConvolutionParams& p,
const T_IN* batch,
const Shape& batch_shape,
const T_F* filter,
const Shape& filter_shape,
T_IN*& out,
const float pad_value)
{
const int n_bits = 8;
const int input_size_z = batch_shape[1];
const int input_size_y = batch_shape[2];
const int input_size_x = batch_shape[3];
const int filter_size_z = filter_shape[1];
const int filter_size_y = filter_shape[2];
const int filter_size_x = filter_shape[3];
const int dilated_filter_size_z =
filter_size_z + (filter_size_z - 1) * (p.dilation[0] - 1);
const int dilated_filter_size_y =
filter_size_y + (filter_size_y - 1) * (p.dilation[1] - 1);
const int dilated_filter_size_x =
filter_size_x + (filter_size_x - 1) * (p.dilation[2] - 1);
const Shape input_channel_shape(++batch_shape.begin(), batch_shape.end());
const size_t input_channel_size = shape_size(input_channel_shape);
const Shape filter_channel_shape(++filter_shape.begin(), filter_shape.end());
const size_t filter_channel_size = shape_size(filter_channel_shape);
const T_IN bit_count = static_cast<T_IN>(filter_channel_size);
for (int i_z = -p.pads_begin[0];
i_z <= (p.pads_end[0] + input_size_z - dilated_filter_size_z);
i_z += p.strides[0])
{
for (int i_y = -p.pads_begin[1];
i_y <= (p.pads_end[1] + input_size_y - dilated_filter_size_y);
i_y += p.strides[1])
{
for (int i_x = -p.pads_begin[2];
i_x <= (p.pads_end[2] + input_size_x - dilated_filter_size_x);
i_x += p.strides[2])
{
auto input_channel = batch;
size_t filter_channels_count = filter_shape[0];
int filter_count = 0;
T_IN sum = 0;
while (filter_channels_count--)
{
T_IN popcount = 0;
for (int f_z = 0; f_z < filter_size_z; ++f_z)
{
for (int f_y = 0; f_y < filter_size_y; ++f_y)
{
for (int f_x = 0; f_x < filter_size_x; ++f_x)
{
int rel_i_z = i_z + (f_z * p.dilation[0]);
int rel_i_y = i_y + (f_y * p.dilation[1]);
int rel_i_x = i_x + (f_x * p.dilation[2]);
bool padding =
!(in_range(rel_i_x, {0, input_size_x}) &&
in_range(rel_i_y, {0, input_size_y}) &&
in_range(rel_i_z, {0, input_size_z}));
int i_buf_idx =
(rel_i_z * input_size_y * input_size_x) +
(rel_i_y * input_size_x) + rel_i_x;
T_IN in_val = padding
? static_cast<T_IN>(pad_value)
: static_cast<T_IN>(
input_channel[i_buf_idx]);
int f_buf_idx =
(f_z * filter_size_y * filter_size_x) +
(f_y * filter_size_x) + f_x;
int f_byte_idx =
(f_buf_idx + filter_count) / n_bits;
int bit_idx = (n_bits - 1) -
((f_buf_idx + filter_count) % n_bits);
uint8_t f_val =
extract_bit(filter[f_byte_idx], bit_idx);
if (xnor(in_val, static_cast<T_IN>(f_val)))
{
popcount += static_cast<T_IN>(1);
}
}
}
}
input_channel += input_channel_size;
filter_count += filter_channel_size;
sum += (2 * popcount - bit_count);
}
*out = sum;
++out;
}
}
}
}
}
void validate_convolution_parameters(const Shape& in_shape,
const Shape& f_shape,
const Strides& strides,
const Strides& dilations,
const CoordinateDiff& pads_begin,
const CoordinateDiff& pads_end)
{
// this implementation supports 1D, 2D and 3D convolutions
NGRAPH_CHECK(in_shape.size() >= 3 && in_shape.size() <= 5,
"Unsupported input rank: ",
in_shape);
NGRAPH_CHECK(in_shape.size() == f_shape.size(),
"Incompatible input ranks: ",
in_shape.size(),
" and ",
f_shape.size());
const auto spatial_dims = in_shape.size() - 2;
NGRAPH_CHECK(strides.size() == spatial_dims,
"Strides not definied for all and only spatial dimensions");
NGRAPH_CHECK(dilations.size() == spatial_dims,
"Dilations not defined for all and only spatial dimensions");
NGRAPH_CHECK((pads_begin.size() == pads_end.size()) &&
(pads_begin.size() == spatial_dims),
"Pads not defined for all and only spatial dimensions");
}
template <typename T_IN, typename T_F>
void binary_convolution(const T_IN* in,
const T_F* f,
T_IN* out,
const Shape& in_shape,
const Shape& f_shape,
const Shape& out_shape,
const Strides& strides,
const Strides& dilations,
const CoordinateDiff& pads_begin,
const CoordinateDiff& pads_end,
const float pad_value)
{
validate_convolution_parameters(
in_shape, f_shape, strides, dilations, pads_begin, pads_end);
// here we are converting all param types to int's to avoid arithmetic issues
// (e.g signed + unsigned) in indexes calculation later
ConvolutionParams params{strides, dilations, pads_begin, pads_end};
// here we are extending spatial dimensions to 3D, because we are going to use 3D
// convolution implementation to convolve also in 1D & 2D case
Shape input_shape{in_shape};
Shape filters_shape{f_shape};
if (in_shape.size() < 5)
{
extend_to_3D(params, input_shape, filters_shape);
}
const size_t batches_count = input_shape[in_batch_axis];
const Shape batch_shape(++input_shape.begin(), input_shape.end());
const size_t batch_size = shape_size(batch_shape);
const size_t filters_count = filters_shape[filter_out_ch_axis];
const Shape filter_shape(++filters_shape.begin(), filters_shape.end());
const size_t filter_size = shape_size(filter_shape);
auto batch = in;
for (size_t batch_idx = 0; batch_idx < batches_count; ++batch_idx)
{
auto filter = f;
for (size_t f_idx = 0; f_idx < filters_count; ++f_idx)
{
details::binary_convolve_3D_channels(
params, batch, batch_shape, filter, filter_shape, out, pad_value);
filter += filter_size;
}
batch += batch_size;
}
}
} // namespace reference
} // namespace runtime
} // namespace ngraph

View File

@ -73,64 +73,100 @@ op::v1::BinaryConvolution::BinaryConvolution(const Output<Node>& data,
void op::v1::BinaryConvolution::validate_and_infer_types() void op::v1::BinaryConvolution::validate_and_infer_types()
{ {
NGRAPH_OP_SCOPE(v1_BinaryConvolution_validate_and_infer_types); NGRAPH_OP_SCOPE(v1_BinaryConvolution_validate_and_infer_types);
const PartialShape& data_batch_shape = get_input_partial_shape(0); const PartialShape& data_batch_pshape = get_input_partial_shape(0);
element::Type data_batch_et = get_input_element_type(0); element::Type data_batch_et = get_input_element_type(0);
const PartialShape& filters_shape = get_input_partial_shape(1); const PartialShape& filters_pshape = get_input_partial_shape(1);
PartialShape result_shape = PartialShape::dynamic(); NODE_VALIDATION_CHECK(this,
if (data_batch_shape.rank().is_static()) data_batch_et.is_real(),
{ "Data batch element type must be float point. Got: ",
result_shape = data_batch_et);
std::vector<Dimension>(data_batch_shape.rank().get_length(), Dimension::dynamic());
if (data_batch_shape.rank().get_length() > 1) // TODO: Add NodeValidationCheck to filters et once u1 is supported in nGraph Python API
{ // (#49517)
result_shape[0] = data_batch_shape[0]; // batch size
}
if (filters_shape.rank().is_static() && filters_shape.rank().get_length() > 1) NODE_VALIDATION_CHECK(this,
{ data_batch_pshape.rank().compatible(filters_pshape.rank()),
result_shape[1] = filters_shape[0]; // filter channel size "Shapes for data batch and filters must have same rank. Got: ",
} data_batch_pshape,
} "and ",
filters_pshape);
if (m_strides.size() == 0) if (m_strides.size() == 0)
{ {
m_strides = conv_default_strides(this, data_batch_shape, filters_shape); m_strides = conv_default_strides(this, data_batch_pshape, filters_pshape);
} }
if (m_dilations.size() == 0) if (m_dilations.size() == 0)
{ {
m_dilations = conv_default_strides(this, data_batch_shape, filters_shape); m_dilations = conv_default_strides(this, data_batch_pshape, filters_pshape);
} }
if (m_pads_begin.size() == 0) if (m_pads_begin.size() == 0)
{ {
m_pads_begin = conv_default_padding(this, data_batch_shape, filters_shape); m_pads_begin = conv_default_padding(this, data_batch_pshape, filters_pshape);
} }
if (m_pads_end.size() == 0) if (m_pads_end.size() == 0)
{ {
m_pads_end = conv_default_padding(this, data_batch_shape, filters_shape); m_pads_end = conv_default_padding(this, data_batch_pshape, filters_pshape);
} }
PartialShape result_shape = PartialShape::dynamic();
if (data_batch_pshape.rank().is_static() || filters_pshape.rank().is_static())
{
const bool is_data_batch_ps_static = data_batch_pshape.rank().is_static();
const auto output_ps_rank =
is_data_batch_ps_static ? data_batch_pshape.rank() : filters_pshape.rank();
const auto num_spatial_dims = output_ps_rank.get_length() - 2;
NODE_VALIDATION_CHECK(this,
m_strides.size() == num_spatial_dims,
"Strides should be defined for all and only spatial features.");
NODE_VALIDATION_CHECK(this,
m_dilations.size() == num_spatial_dims,
"Dilations should be defined for all and only spatial features.");
NODE_VALIDATION_CHECK(this,
m_pads_begin.size() == num_spatial_dims &&
m_pads_end.size() == num_spatial_dims,
"Pads should be defined for all and only spatial features.");
result_shape = std::vector<Dimension>(output_ps_rank.get_length(), Dimension::dynamic());
if (data_batch_pshape.rank().is_static())
{
result_shape[0] = data_batch_pshape[0]; // batch size
}
if (filters_pshape.rank().is_static())
{
result_shape[1] = filters_pshape[0]; // filter channel size
}
if (m_auto_pad == PadType::SAME_UPPER || m_auto_pad == PadType::SAME_LOWER) if (m_auto_pad == PadType::SAME_UPPER || m_auto_pad == PadType::SAME_LOWER)
{ {
bool auto_padding_applied = false; bool auto_padding_applied = false;
if (filters_shape.is_static()) if (filters_pshape.rank().is_static() && filters_pshape.rank().get_length() > 2)
{ {
m_pads_begin.clear(); m_pads_begin.clear();
m_pads_end.clear(); m_pads_end.clear();
auto filter_shape = filters_shape.to_shape();
filter_shape.erase(filter_shape.begin(), filter_shape.begin() + 2); // Remove {O,I} const PartialShape filter_spatial_shape = [filters_pshape]() {
auto_padding_applied = try_apply_auto_padding(data_batch_shape, vector<Dimension> filter_dims{filters_pshape};
filter_shape, filter_dims.erase(filter_dims.begin(), filter_dims.begin() + 2); // Remove {O,I}
return PartialShape{filter_dims};
}();
if (filter_spatial_shape.is_static())
{
auto_padding_applied = try_apply_auto_padding(data_batch_pshape,
filter_spatial_shape.to_shape(),
m_strides, m_strides,
m_dilations, m_dilations,
m_auto_pad, m_auto_pad,
m_pads_end, m_pads_end,
m_pads_begin); m_pads_begin);
} }
}
if (!auto_padding_applied) if (!auto_padding_applied)
{ {
set_output_type(0, data_batch_et, result_shape); set_output_type(0, data_batch_et, result_shape);
@ -139,14 +175,14 @@ void op::v1::BinaryConvolution::validate_and_infer_types()
} }
result_shape = infer_convolution_forward(this, result_shape = infer_convolution_forward(this,
data_batch_shape, data_batch_pshape,
Strides(data_batch_shape.rank().get_length() - 2, 1), Strides(num_spatial_dims, 1),
m_pads_begin, m_pads_begin,
m_pads_end, m_pads_end,
filters_shape, filters_pshape,
m_strides, m_strides,
m_dilations); m_dilations);
}
set_output_type(0, data_batch_et, result_shape); set_output_type(0, data_batch_et, result_shape);
} }

View File

@ -36,7 +36,7 @@ integral_np_types = [
] ]
@pytest.mark.parametrize("dtype", np_types) @pytest.mark.parametrize("dtype", [np.float32, np.float64])
def test_binary_convolution(dtype): def test_binary_convolution(dtype):
strides = np.array([1, 1]) strides = np.array([1, 1])
pads_begin = np.array([0, 0]) pads_begin = np.array([0, 0])

View File

@ -71,6 +71,7 @@ set(SRC
ngraph_api.cpp ngraph_api.cpp
node_input_output.cpp node_input_output.cpp
op.cpp op.cpp
op_eval/binary_convolution.cpp
op_eval/bucketize.cpp op_eval/bucketize.cpp
op_eval/floor_mod.cpp op_eval/floor_mod.cpp
op_eval/gelu.cpp op_eval/gelu.cpp
@ -268,6 +269,7 @@ set(MULTI_TEST_SRC
backend/convert.in.cpp backend/convert.in.cpp
backend/convert_like.in.cpp backend/convert_like.in.cpp
backend/convolution.in.cpp backend/convolution.in.cpp
backend/binary_convolution.in.cpp
backend/cos.in.cpp backend/cos.in.cpp
backend/cosh.in.cpp backend/cosh.in.cpp
backend/ctc_greedy_decoder.in.cpp backend/ctc_greedy_decoder.in.cpp

View File

@ -0,0 +1,654 @@
//*****************************************************************************
// Copyright 2021 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//*****************************************************************************
#include "gtest/gtest.h"
#include "ngraph/ngraph.hpp"
#include "ngraph/runtime/tensor.hpp"
#include "runtime/backend.hpp"
#include "util/all_close.hpp"
#include "util/all_close_f.hpp"
#include "util/test_control.hpp"
using namespace std;
using namespace ngraph;
static string s_manifest = "${MANIFEST}";
template <typename T_IN, typename T_KERN>
static void BinaryConvolutionTest(const std::vector<T_IN>& inputs,
const Shape inputs_shape,
const std::vector<T_KERN>& filters,
const Shape filters_shape,
const std::vector<T_IN>& outputs,
const Shape outputs_shape,
const Strides& strides,
const CoordinateDiff& padding,
const Strides& dilations,
const float pad_value = 0.0f)
{
const CoordinateDiff pads_begin{padding};
const CoordinateDiff pads_end{padding};
const op::PadType auto_pad{op::PadType::EXPLICIT};
auto inputs_param = make_shared<op::Parameter>(element::from<T_IN>(), inputs_shape);
auto filters_const = make_shared<op::Constant>(element::u1, filters_shape, &filters[0]);
auto bin_conv = make_shared<op::v1::BinaryConvolution>(
inputs_param,
filters_const,
strides,
pads_begin,
pads_end,
dilations,
op::v1::BinaryConvolution::BinaryConvolutionMode::XNOR_POPCOUNT,
pad_value,
auto_pad);
auto f = make_shared<Function>(bin_conv, ParameterVector{inputs_param});
auto backend = runtime::Backend::create("${BACKEND_NAME}");
auto input_tensor = backend->create_tensor(element::from<T_IN>(), inputs_shape);
copy_data(input_tensor, inputs);
auto result = backend->create_tensor(element::from<T_IN>(), outputs_shape);
auto handle = backend->compile(f);
handle->call_with_validate({result}, {input_tensor});
EXPECT_TRUE(test::all_close_f((outputs), read_vector<T_IN>(result), MIN_FLOAT_TOLERANCE_BITS));
}
template <typename T_IN>
static void ConvolutionTest(const std::vector<T_IN>& inputs,
const Shape inputs_shape,
const std::vector<T_IN>& filters,
const Shape filters_shape,
const std::vector<T_IN>& outputs,
const Shape outputs_shape,
const Strides& strides,
const CoordinateDiff& padding,
const Strides& dilations)
{
const CoordinateDiff pads_begin{padding};
const CoordinateDiff pads_end{padding};
const op::PadType auto_pad{op::PadType::EXPLICIT};
auto inputs_param = make_shared<op::Parameter>(element::from<T_IN>(), inputs_shape);
auto filters_param = make_shared<op::Parameter>(element::from<T_IN>(), filters_shape);
auto conv = make_shared<op::v1::Convolution>(
inputs_param, filters_param, strides, pads_begin, pads_end, dilations, auto_pad);
auto f = make_shared<Function>(conv, ParameterVector{inputs_param, filters_param});
auto backend = runtime::Backend::create("${BACKEND_NAME}");
auto input_tensor = backend->create_tensor(element::from<T_IN>(), inputs_shape);
copy_data(input_tensor, inputs);
auto filters_tensor = backend->create_tensor(element::from<T_IN>(), filters_shape);
copy_data(filters_tensor, filters);
auto result = backend->create_tensor(element::from<T_IN>(), outputs_shape);
auto handle = backend->compile(f);
handle->call_with_validate({result}, {input_tensor, filters_tensor});
EXPECT_TRUE(test::all_close_f((outputs), read_vector<T_IN>(result), MIN_FLOAT_TOLERANCE_BITS));
}
// clang-format off
// --------------------- 2D convolution ------------------------------------------
NGRAPH_TEST(${BACKEND_NAME}, bin_convolution_2D_1batch_1channel)
{
const Strides strides{1, 1};
const CoordinateDiff padding{0, 0};
const Strides dilations{1, 1};
const Shape inputs_shape{1, 1, 4, 4};
const std::vector<float> inputs_conv{1.0f, -1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f, 1.0f};
const std::vector<float> inputs_bin_conv{1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f};
const Shape filters_shape{1, 1, 3, 3};
const std::vector<float> filters_conv{1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f};
const std::vector<uint8_t> filters_bin_conv{0xAA, 0x80}; // 10101010 10000000
const Shape outputs_shape{1, 1, 2, 2};
const std::vector<float> outputs{1.0f, 1.0f,
3.0f, -1.0f};
BinaryConvolutionTest(
inputs_bin_conv,
inputs_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
ConvolutionTest(
inputs_conv,
inputs_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
}
NGRAPH_TEST(${BACKEND_NAME}, bin_convolution_2D_1batch_1channel_padding_pad_val_0)
{
const Strides strides{1, 1};
const Strides dilations{1, 1};
const CoordinateDiff padding_conv{0, 0};
const Shape inputs_conv_shape{1, 1, 6, 6};
const std::vector<float> inputs_conv{-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f};
const CoordinateDiff padding_bin_conv{1, 1};
const Shape inputs_bin_conv_shape{1, 1, 4, 4};
const std::vector<float> inputs_bin_conv{1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f};
const Shape filters_shape{1, 1, 3, 3};
const std::vector<float> filters_conv{1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f};
const std::vector<uint8_t> filters_bin_conv{0xAA, 0x80}; // 10101010 10000000
const Shape outputs_shape{1, 1, 4, 4};
const std::vector<float> outputs{1.0f, -3.0f, -1.0f, 1.0f,
-3.0f, 1.0f, 1.0f, -5.0f,
-3.0f, 3.0f, -1.0f, 1.0f,
1.0f, -5.0f, 1.0f, -3.0f};
BinaryConvolutionTest(
inputs_bin_conv,
inputs_bin_conv_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding_bin_conv,
dilations);
ConvolutionTest(
inputs_conv,
inputs_conv_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding_conv,
dilations);
}
NGRAPH_TEST(${BACKEND_NAME}, bin_convolution_2D_1batch_1channel_padding_pad_val_1)
{
const Strides strides{1, 1};
const Strides dilations{1, 1};
const float pad_value = 1.0f;
const CoordinateDiff padding_conv{0, 0};
const Shape inputs_conv_shape{1, 1, 6, 6};
const std::vector<float> inputs_conv{1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
const CoordinateDiff padding_bin_conv{1, 1};
const Shape inputs_bin_conv_shape{1, 1, 4, 4};
const std::vector<float> inputs_bin_conv{1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f};
const Shape filters_shape{1, 1, 3, 3};
const std::vector<float> filters_conv{1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f};
const std::vector<uint8_t> filters_bin_conv{0xAA, 0x80}; // 10101010 10000000
const Shape outputs_shape{1, 1, 4, 4};
const std::vector<float> outputs{3.0f, -1.0f, 1.0f, 3.0f,
-1.0f, 1.0f, 1.0f, -3.0f,
-1.0f, 3.0f, -1.0f, 3.0f,
3.0f, -3.0f, 3.0f, -1.0f,};
BinaryConvolutionTest(
inputs_bin_conv,
inputs_bin_conv_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding_bin_conv,
dilations,
pad_value);
ConvolutionTest(
inputs_conv,
inputs_conv_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding_conv,
dilations);
}
NGRAPH_TEST(${BACKEND_NAME}, bin_convolution_2D_1batch_1channel_stride)
{
const Strides strides{2, 2};
const CoordinateDiff padding{0, 0};
const Strides dilations{1, 1};
const Shape inputs_shape{1, 1, 5, 5};
const std::vector<float> inputs_conv{-1.0f, 1.0f, 1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
const std::vector<float> inputs_bin_conv{0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f, 1.0f};
const Shape filters_shape{1, 1, 3, 3};
const std::vector<float> filters_conv{-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f};
const std::vector<uint8_t> filters_bin_conv{0x2E, 0x00}; // 00101110 00000000
const Shape outputs_shape{1, 1, 2, 2};
const std::vector<float> outputs{-1.0f, 3.0f,
1.0f, 1.0f};
BinaryConvolutionTest(
inputs_bin_conv,
inputs_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
ConvolutionTest(
inputs_conv,
inputs_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
}
NGRAPH_TEST(${BACKEND_NAME}, bin_convolution_2D_1batch_1channel_dilation)
{
const Strides strides{1, 1};
const CoordinateDiff padding{0, 0};
const Strides dilations{2, 2};
const Shape inputs_shape{1, 1, 7, 7};
const std::vector<float> inputs_conv{1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f,
1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f};
const std::vector<float> inputs_bin_conv{1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f};
const Shape filters_shape{1, 1, 3, 3};
const std::vector<float> filters_conv{-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f};
const std::vector<uint8_t> filters_bin_conv{0x6B, 0x00}; // 01101011 00000000
const Shape outputs_shape{1, 1, 3, 3};
const std::vector<float> outputs{-5.0f, -3.0f, -5.0f,
5.0f, 1.0f, 3.0f,
-1.0f, -1.0f, 3.0f};
BinaryConvolutionTest(
inputs_bin_conv,
inputs_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
ConvolutionTest(
inputs_conv,
inputs_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
}
NGRAPH_TEST(${BACKEND_NAME},
bin_convolution_2D_1batch_1channel_strides_dilation_padding_pad_val_0)
{
const Strides strides{2, 2};
const Strides dilations{2, 2};
const CoordinateDiff padding_conv{0, 0};
const Shape inputs_conv_shape{1, 1, 11, 11};
const std::vector<float> inputs_conv{
-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,-1.0f, -1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f};
const CoordinateDiff padding_bin_conv{2, 2};
const Shape inputs_bin_conv_shape{1, 1, 7, 7};
const std::vector<float> inputs_bin_conv{1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f};
const Shape filters_shape{1, 1, 3, 3};
const std::vector<float> filters_conv{-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f};
const std::vector<uint8_t> filters_bin_conv{0x6B, 0x00}; // 01101011 00000000
const Shape outputs_shape{1, 1, 4, 4};
const std::vector<float> outputs{1.0f, 1.0f, -1.0f, 1.0f,
1.0f, -5.0f, -5.0f, 5.0f,
3.0f, -1.0f, 3.0f, 3.0f,
-1.0f, -1.0f, 3.0f, -3.0f};
BinaryConvolutionTest(
inputs_bin_conv,
inputs_bin_conv_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding_bin_conv,
dilations);
ConvolutionTest(
inputs_conv,
inputs_conv_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding_conv,
dilations);
}
NGRAPH_TEST(${BACKEND_NAME},
bin_convolution_2D_1batch_1channel_strides_dilation_padding_pad_val_1)
{
const Strides strides{2, 2};
const Strides dilations{2, 2};
const float pad_value = 1.0f;
const CoordinateDiff padding_conv{0, 0};
const Shape inputs_conv_shape{1, 1, 11, 11};
const std::vector<float> inputs_conv{
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
const CoordinateDiff padding_bin_conv{2, 2};
const Shape inputs_bin_conv_shape{1, 1, 7, 7};
const std::vector<float> inputs_bin_conv{1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f};
const Shape filters_shape{1, 1, 3, 3};
const std::vector<float> filters_conv{-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f};
const std::vector<uint8_t> filters_bin_conv{0x6B, 0x00}; // 01101011 00000000
const Shape outputs_shape{1, 1, 4, 4};
const std::vector<float> outputs{3.0f, 3.0f, 1.0f, -1.0f,
-1.0f, -5.0f, -5.0f, 3.0f,
1.0f, -1.0f, 3.0f, 1.0f,
-3.0f, 1.0f, 5.0f, -1.0f};
BinaryConvolutionTest(
inputs_bin_conv,
inputs_bin_conv_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding_bin_conv,
dilations,
pad_value);
ConvolutionTest(
inputs_conv,
inputs_conv_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding_conv,
dilations);
}
NGRAPH_TEST(${BACKEND_NAME}, bin_convolution_2D_1batch_2channel)
{
const Strides strides{1, 1};
const CoordinateDiff padding{0, 0};
const Strides dilations{1, 1};
const Shape inputs_shape{1, 2, 4, 4};
const std::vector<float> inputs_conv{
// channel 1
1.0f, -1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f, 1.0f,
// channel 2
-1.0f, 1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f, -1.0f};
const std::vector<float> inputs_bin_conv{
// channel 1
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
// channel 2
0.0f, 1.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f};
const Shape filters_shape{1, 2, 3, 3};
const std::vector<float> filters_conv{
// channel 1
1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
// channel 2
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f};
// 10101010 10101010 10000000
const std::vector<uint8_t> filters_bin_conv{0xAA, 0xAA, 0x80};
const Shape outputs_shape{1, 1, 2, 2};
const std::vector<float> outputs{2.0f, 2.0f,
6.0f, -2.0f};
BinaryConvolutionTest(
inputs_bin_conv,
inputs_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
ConvolutionTest(
inputs_conv,
inputs_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
}
NGRAPH_TEST(${BACKEND_NAME}, bin_convolution_2D_2batch_1channel)
{
const Strides strides{1, 1};
const CoordinateDiff padding{0, 0};
const Strides dilations{1, 1};
const Shape inputs_shape{2, 1, 4, 4};
const std::vector<float> inputs_conv{
// batch 1
1.0f, -1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f, 1.0f,
// batch 2
-1.0f, -1.0f, -1.0f, -1.0f,
1.0f, 1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f, -1.0f};
const std::vector<float> inputs_bin_conv{
// batch 1
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
// batch 2
0.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f};
const Shape filters_shape{1, 1, 3, 3};
const std::vector<float> filters_conv{1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f};
const std::vector<uint8_t> filters_bin_conv{0xAA, 0x80}; // 10101010 10000000
const Shape outputs_shape{2, 1, 2, 2};
const std::vector<float> outputs{
// batch 1
1.0f, 1.0f,
3.0f, -1.0f,
// batch 2
-3.0f, 3.0f,
5.0f, -7.0f};
BinaryConvolutionTest(
inputs_bin_conv,
inputs_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
ConvolutionTest(
inputs_conv,
inputs_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
}
// clang-format on

View File

@ -0,0 +1,245 @@
//*****************************************************************************
// Copyright 2021 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//*****************************************************************************
#include "gtest/gtest.h"
#include "ngraph/ngraph.hpp"
#include "ngraph/runtime/tensor.hpp"
#include "runtime/backend.hpp"
#include "util/all_close.hpp"
#include "util/all_close_f.hpp"
#include "util/test_control.hpp"
using namespace std;
using namespace ngraph;
static string s_manifest = "${MANIFEST}";
template <typename T_IN, typename T_KERN>
static void BinaryConvolutionTest(const std::vector<T_IN>& inputs,
const Shape inputs_shape,
const std::vector<T_KERN>& filters,
const Shape filters_shape,
const std::vector<T_IN>& outputs,
const Shape outputs_shape,
const Strides& strides,
const CoordinateDiff& padding,
const Strides& dilations)
{
const CoordinateDiff pads_begin{padding};
const CoordinateDiff pads_end{padding};
const op::PadType auto_pad{op::PadType::EXPLICIT};
float pad_value = 0;
auto inputs_param = make_shared<op::Parameter>(element::from<T_IN>(), inputs_shape);
auto filters_const = make_shared<op::Constant>(element::u1, filters_shape, &filters[0]);
auto bin_conv = make_shared<op::v1::BinaryConvolution>(
inputs_param,
filters_const,
strides,
pads_begin,
pads_end,
dilations,
op::v1::BinaryConvolution::BinaryConvolutionMode::XNOR_POPCOUNT,
pad_value,
auto_pad);
auto f = make_shared<Function>(bin_conv, ParameterVector{inputs_param});
auto backend = runtime::Backend::create("INTERPRETER");
auto input_tensor = backend->create_tensor(element::from<T_IN>(), inputs_shape);
copy_data(input_tensor, inputs);
auto result = backend->create_tensor(element::from<T_IN>(), outputs_shape);
auto handle = backend->compile(f);
handle->call_with_validate({result}, {input_tensor});
EXPECT_TRUE(test::all_close_f((outputs), read_vector<T_IN>(result), MIN_FLOAT_TOLERANCE_BITS));
}
template <typename T_IN>
static void ConvolutionTest(const std::vector<T_IN>& inputs,
const Shape inputs_shape,
const std::vector<T_IN>& filters,
const Shape filters_shape,
const std::vector<T_IN>& outputs,
const Shape outputs_shape,
const Strides& strides,
const CoordinateDiff& padding,
const Strides& dilations)
{
const CoordinateDiff pads_begin{padding};
const CoordinateDiff pads_end{padding};
const op::PadType auto_pad{op::PadType::EXPLICIT};
auto inputs_param = make_shared<op::Parameter>(element::from<T_IN>(), inputs_shape);
auto filters_param = make_shared<op::Parameter>(element::from<T_IN>(), filters_shape);
auto conv = make_shared<op::v1::Convolution>(
inputs_param, filters_param, strides, pads_begin, pads_end, dilations, auto_pad);
auto f = make_shared<Function>(conv, ParameterVector{inputs_param, filters_param});
auto backend = runtime::Backend::create("INTERPRETER");
auto input_tensor = backend->create_tensor(element::from<T_IN>(), inputs_shape);
copy_data(input_tensor, inputs);
auto filters_tensor = backend->create_tensor(element::from<T_IN>(), filters_shape);
copy_data(filters_tensor, filters);
auto result = backend->create_tensor(element::from<T_IN>(), outputs_shape);
auto handle = backend->compile(f);
handle->call_with_validate({result}, {input_tensor, filters_tensor});
EXPECT_TRUE(test::all_close_f((outputs), read_vector<T_IN>(result), MIN_FLOAT_TOLERANCE_BITS));
}
// --------------------- 1D convolution ------------------------------------------
TEST(op_eval, bin_convolution_1D_1batch_1channel_no_padding)
{
const Strides strides{1};
const CoordinateDiff padding{0};
const Strides dilations{1};
const Shape inputs_shape{1, 1, 5};
const std::vector<float> inputs_conv{1.0f, -1.0f, -1.0f, 1.0f, -1.0f};
const std::vector<float> inputs_bin_conv{1.0f, 0.0f, 0.0f, 1.0f, 0.0f};
const Shape filters_shape{1, 1, 3};
const std::vector<float> filters_conv{1.0f, -1.0f, 1.0f};
const std::vector<uint8_t> filters_bin_conv{0xA0}; // 1010 0000
const Shape outputs_shape{1, 1, 3};
const std::vector<float> outputs{1.0f, 1.0f, -3.0f};
BinaryConvolutionTest(inputs_bin_conv,
inputs_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
ConvolutionTest(inputs_conv,
inputs_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
}
// --------------------- 3D convolution ------------------------------------------
// clang-format off
NGRAPH_TEST(op_eval, bin_convolution_3D_1batch_1channel_no_padding)
{
const Strides strides{1, 1, 1};
const CoordinateDiff padding{0, 0, 0};
const Strides dilations{1, 1, 1};
const Shape inputs_shape{1, 1, 4, 4, 4};
const std::vector<float> inputs_conv{
// depth: 1
1.0f, -1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f, -1.0f,
// depth: 2
-1.0f, 1.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f, -1.0f,
// depth: 3
1.0f, 1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f, -1.0f,
// depth: 4
1.0f, -1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f, 1.0f
};
const std::vector<float> inputs_bin_conv{
// depth: 1
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f,
// depth: 2
0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f,
// depth: 3
1.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f,
// depth: 4
1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f
};
const Shape filters_shape{1, 1, 3, 3, 3};
const std::vector<float> filters_conv{
// depth: 1
1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
// depth: 2
-1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
// depth: 3
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f};
const std::vector<uint8_t> filters_bin_conv{0xAA, 0xBB, 0xB2, 0xE0};
const Shape outputs_shape{1, 1, 2, 2, 2};
const std::vector<float> outputs{
// depth: 1
13.0f, 3.0f,
-3.0f, -3.0f,
// depth: 2
-3.0f, 5.0f,
11.0f, -3.0f};
BinaryConvolutionTest(
inputs_bin_conv,
inputs_shape,
filters_bin_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
ConvolutionTest(
inputs_conv,
inputs_shape,
filters_conv,
filters_shape,
outputs,
outputs_shape,
strides,
padding,
dilations);
}
// clang-format off

View File

@ -1592,3 +1592,14 @@ IE_CPU.onnx_mvn_v6
# The test randomly fails in CI (MSVC2019 Debug) # The test randomly fails in CI (MSVC2019 Debug)
onnx_model_experimental_detectron_generate_proposals_single_image onnx_model_experimental_detectron_generate_proposals_single_image
# Issue 49621: Incorrect blob sizes for node BinaryConvolution_X
bin_convolution_2D_1batch_1channel
bin_convolution_2D_1batch_1channel_padding_pad_val_0
bin_convolution_2D_1batch_1channel_padding_pad_val_1
bin_convolution_2D_1batch_1channel_stride
bin_convolution_2D_1batch_1channel_dilation
bin_convolution_2D_1batch_1channel_strides_dilation_padding_pad_val_0
bin_convolution_2D_1batch_1channel_strides_dilation_padding_pad_val_1
bin_convolution_2D_1batch_2channel
bin_convolution_2D_2batch_1channel

View File

@ -22,6 +22,7 @@
#include <ngraph/runtime/reference/abs.hpp> #include <ngraph/runtime/reference/abs.hpp>
#include <ngraph/runtime/reference/avg_pool.hpp> #include <ngraph/runtime/reference/avg_pool.hpp>
#include <ngraph/runtime/reference/batch_norm.hpp> #include <ngraph/runtime/reference/batch_norm.hpp>
#include <ngraph/runtime/reference/binary_convolution.hpp>
#include <ngraph/runtime/reference/bucketize.hpp> #include <ngraph/runtime/reference/bucketize.hpp>
#include <ngraph/runtime/reference/ceiling.hpp> #include <ngraph/runtime/reference/ceiling.hpp>
#include <ngraph/runtime/reference/convert.hpp> #include <ngraph/runtime/reference/convert.hpp>
@ -214,6 +215,55 @@ namespace
return true; return true;
} }
namespace bin_conv_v1
{
template <element::Type_t t_in, element::Type_t t_f>
inline void evaluate(const shared_ptr<op::v1::BinaryConvolution>& op,
const HostTensorVector& outputs,
const HostTensorVector& inputs)
{
using T_IN = typename element_type_traits<t_in>::value_type;
using T_F = typename element_type_traits<t_f>::value_type;
const auto in_data_ptr = inputs[0]->get_data_ptr<T_IN>();
const auto filter_data_ptr = inputs[1]->get_data_ptr<T_F>();
auto out_data_ptr = outputs[0]->get_data_ptr<T_IN>();
const auto in_shape = inputs[0]->get_shape();
const auto filter_shape = inputs[1]->get_shape();
const auto out_shape = outputs[0]->get_shape();
runtime::reference::binary_convolution<T_IN, T_F>(in_data_ptr,
filter_data_ptr,
out_data_ptr,
in_shape,
filter_shape,
out_shape,
op->get_strides(),
op->get_dilations(),
op->get_pads_begin(),
op->get_pads_end(),
op->get_pad_value());
}
} // bin_conv_v1
template <element::Type_t ET>
bool evaluate(const shared_ptr<op::v1::BinaryConvolution>& op,
const HostTensorVector& outputs,
const HostTensorVector& inputs)
{
switch (inputs[1]->get_element_type())
{
case element::Type_t::u1:
bin_conv_v1::evaluate<ET, element::Type_t::u8>(op, outputs, inputs);
break;
default:
throw std::runtime_error(
"BinaryConvolution supports only u1 element type for filters input");
break;
}
return true;
}
template <element::Type_t ET> template <element::Type_t ET>
bool evaluate(const shared_ptr<op::v1::ConvolutionBackpropData>& op, bool evaluate(const shared_ptr<op::v1::ConvolutionBackpropData>& op,
const HostTensorVector& outputs, const HostTensorVector& outputs,

View File

@ -49,6 +49,7 @@ NGRAPH_OP(TensorIterator, op::v0)
NGRAPH_OP(ROIPooling, op::v0) NGRAPH_OP(ROIPooling, op::v0)
NGRAPH_OP(AvgPool, op::v1) NGRAPH_OP(AvgPool, op::v1)
NGRAPH_OP(BinaryConvolution, ngraph::op::v1)
NGRAPH_OP(ConvertLike, op::v1) NGRAPH_OP(ConvertLike, op::v1)
NGRAPH_OP(Convolution, ngraph::op::v1) NGRAPH_OP(Convolution, ngraph::op::v1)
NGRAPH_OP(ConvolutionBackpropData, ngraph::op::v1) NGRAPH_OP(ConvolutionBackpropData, ngraph::op::v1)

View File

@ -21,7 +21,7 @@
using namespace std; using namespace std;
using namespace ngraph; using namespace ngraph;
TEST(type_prop, binary_conv_v1_partial_auto_padding_same) TEST(type_prop, bin_convolution_auto_padding_same)
{ {
const PartialShape data_batch_shape{1, 1, 5, 5}; const PartialShape data_batch_shape{1, 1, 5, 5};
const PartialShape filters_shape{1, 1, 3, 3}; const PartialShape filters_shape{1, 1, 3, 3};
@ -34,7 +34,7 @@ TEST(type_prop, binary_conv_v1_partial_auto_padding_same)
const auto auto_pad = op::PadType::SAME_LOWER; const auto auto_pad = op::PadType::SAME_LOWER;
auto data_batch = make_shared<op::Parameter>(element::f32, data_batch_shape); auto data_batch = make_shared<op::Parameter>(element::f32, data_batch_shape);
auto filters = make_shared<op::Parameter>(element::f32, filters_shape); auto filters = make_shared<op::Parameter>(element::u1, filters_shape);
auto conv = make_shared<op::v1::BinaryConvolution>( auto conv = make_shared<op::v1::BinaryConvolution>(
data_batch, filters, strides, pads_begin, pads_end, dilations, mode, pad_value, auto_pad); data_batch, filters, strides, pads_begin, pads_end, dilations, mode, pad_value, auto_pad);
@ -44,10 +44,10 @@ TEST(type_prop, binary_conv_v1_partial_auto_padding_same)
ASSERT_EQ(conv->get_pads_end(), (CoordinateDiff{1, 1})); ASSERT_EQ(conv->get_pads_end(), (CoordinateDiff{1, 1}));
} }
TEST(type_prop, binary_conv_v1_partial_auto_padding_same_nc_dims_dynamic_same_lower) TEST(type_prop, bin_convolution_auto_padding_same_lower_spatial_dims_static)
{ {
const PartialShape data_batch_shape{Dimension::dynamic(), Dimension::dynamic(), 5, 5}; const PartialShape data_batch_shape{Dimension::dynamic(), Dimension::dynamic(), 5, 5};
const PartialShape filters_shape{1, 1, 3, 3}; const PartialShape filters_shape{Dimension::dynamic(), Dimension::dynamic(), 3, 3};
Strides strides{1, 1}; Strides strides{1, 1};
CoordinateDiff pads_begin{0, 0}; CoordinateDiff pads_begin{0, 0};
CoordinateDiff pads_end{0, 0}; CoordinateDiff pads_end{0, 0};
@ -57,20 +57,21 @@ TEST(type_prop, binary_conv_v1_partial_auto_padding_same_nc_dims_dynamic_same_lo
const auto auto_pad = op::PadType::SAME_LOWER; const auto auto_pad = op::PadType::SAME_LOWER;
auto data_batch = make_shared<op::Parameter>(element::f32, data_batch_shape); auto data_batch = make_shared<op::Parameter>(element::f32, data_batch_shape);
auto filters = make_shared<op::Parameter>(element::f32, filters_shape); auto filters = make_shared<op::Parameter>(element::u1, filters_shape);
auto conv = make_shared<op::v1::BinaryConvolution>( auto conv = make_shared<op::v1::BinaryConvolution>(
data_batch, filters, strides, pads_begin, pads_end, dilations, mode, pad_value, auto_pad); data_batch, filters, strides, pads_begin, pads_end, dilations, mode, pad_value, auto_pad);
ASSERT_TRUE(conv->get_output_partial_shape(0).same_scheme({Dimension::dynamic(), 1, 5, 5})); ASSERT_TRUE(conv->get_output_partial_shape(0).same_scheme(
{Dimension::dynamic(), Dimension::dynamic(), 5, 5}));
ASSERT_EQ(conv->get_pads_begin(), (CoordinateDiff{1, 1})); ASSERT_EQ(conv->get_pads_begin(), (CoordinateDiff{1, 1}));
ASSERT_EQ(conv->get_pads_end(), (CoordinateDiff{1, 1})); ASSERT_EQ(conv->get_pads_end(), (CoordinateDiff{1, 1}));
} }
TEST(type_prop, binary_conv_v1_partial_auto_padding_same_nc_dims_dynamic_same_upper) TEST(type_prop, bin_convolution_auto_padding_same_upper_spatial_dims_static)
{ {
const PartialShape data_batch_shape{Dimension::dynamic(), Dimension::dynamic(), 5, 5}; const PartialShape data_batch_shape{Dimension::dynamic(), Dimension::dynamic(), 5, 5};
const PartialShape filters_shape{1, 1, 2, 2}; const PartialShape filters_shape{Dimension::dynamic(), Dimension::dynamic(), 2, 2};
Strides strides{1, 1}; Strides strides{1, 1};
CoordinateDiff pads_begin{0, 0}; CoordinateDiff pads_begin{0, 0};
CoordinateDiff pads_end{0, 0}; CoordinateDiff pads_end{0, 0};
@ -80,20 +81,21 @@ TEST(type_prop, binary_conv_v1_partial_auto_padding_same_nc_dims_dynamic_same_up
const auto auto_pad = op::PadType::SAME_UPPER; const auto auto_pad = op::PadType::SAME_UPPER;
auto data_batch = make_shared<op::Parameter>(element::f32, data_batch_shape); auto data_batch = make_shared<op::Parameter>(element::f32, data_batch_shape);
auto filters = make_shared<op::Parameter>(element::f32, filters_shape); auto filters = make_shared<op::Parameter>(element::u1, filters_shape);
auto conv = make_shared<op::v1::BinaryConvolution>( auto conv = make_shared<op::v1::BinaryConvolution>(
data_batch, filters, strides, pads_begin, pads_end, dilations, mode, pad_value, auto_pad); data_batch, filters, strides, pads_begin, pads_end, dilations, mode, pad_value, auto_pad);
ASSERT_TRUE(conv->get_output_partial_shape(0).same_scheme({Dimension::dynamic(), 1, 5, 5})); ASSERT_TRUE(conv->get_output_partial_shape(0).same_scheme(
{Dimension::dynamic(), Dimension::dynamic(), 5, 5}));
ASSERT_EQ(conv->get_pads_begin(), (CoordinateDiff{0, 0})); ASSERT_EQ(conv->get_pads_begin(), (CoordinateDiff{0, 0}));
ASSERT_EQ(conv->get_pads_end(), (CoordinateDiff{1, 1})); ASSERT_EQ(conv->get_pads_end(), (CoordinateDiff{1, 1}));
} }
TEST(type_prop, binary_conv_v1_partial_auto_padding_same_spatial_dims_dynamic) TEST(type_prop, bin_convolution_auto_padding_same_data_batch_spatial_dims_dynamic)
{ {
const PartialShape data_batch_shape{1, 1, Dimension::dynamic(), 5}; const PartialShape data_batch_shape{1, 1, Dimension::dynamic(), 5};
const PartialShape filters_shape{1, 1, 3, 3}; const PartialShape filters_shape{Dimension::dynamic(), 1, 3, 3};
Strides strides{1, 1}; Strides strides{1, 1};
CoordinateDiff pads_begin{0, 0}; CoordinateDiff pads_begin{0, 0};
CoordinateDiff pads_end{0, 0}; CoordinateDiff pads_end{0, 0};
@ -103,12 +105,305 @@ TEST(type_prop, binary_conv_v1_partial_auto_padding_same_spatial_dims_dynamic)
const auto auto_pad = op::PadType::SAME_LOWER; const auto auto_pad = op::PadType::SAME_LOWER;
auto data_batch = make_shared<op::Parameter>(element::f32, data_batch_shape); auto data_batch = make_shared<op::Parameter>(element::f32, data_batch_shape);
auto filters = make_shared<op::Parameter>(element::f32, filters_shape); auto filters = make_shared<op::Parameter>(element::u1, filters_shape);
auto conv = make_shared<op::v1::BinaryConvolution>( auto conv = make_shared<op::v1::BinaryConvolution>(
data_batch, filters, strides, pads_begin, pads_end, dilations, mode, pad_value, auto_pad); data_batch, filters, strides, pads_begin, pads_end, dilations, mode, pad_value, auto_pad);
ASSERT_TRUE(conv->get_output_partial_shape(0).same_scheme({1, 1, Dimension::dynamic(), 5})); ASSERT_TRUE(conv->get_output_partial_shape(0).same_scheme(
{1, Dimension::dynamic(), Dimension::dynamic(), 5}));
ASSERT_EQ(conv->get_pads_begin(), (CoordinateDiff{0, 1})); ASSERT_EQ(conv->get_pads_begin(), (CoordinateDiff{0, 1}));
ASSERT_EQ(conv->get_pads_end(), (CoordinateDiff{0, 1})); ASSERT_EQ(conv->get_pads_end(), (CoordinateDiff{0, 1}));
} }
TEST(type_prop, bin_convolution_dyn_data_batch)
{
const auto mode = op::v1::BinaryConvolution::BinaryConvolutionMode::XNOR_POPCOUNT;
const float pad_value = 1.0f;
const auto auto_pad = op::PadType::EXPLICIT;
const auto data_batch = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
const auto filters = make_shared<op::Parameter>(element::u1, PartialShape{1, 1, 3});
const auto bin_conv = make_shared<op::v1::BinaryConvolution>(data_batch,
filters,
Strides{},
CoordinateDiff{},
CoordinateDiff{},
Strides{},
mode,
pad_value,
auto_pad);
ASSERT_TRUE(bin_conv->get_output_partial_shape(0).rank().same_scheme(Rank{3}));
ASSERT_TRUE(bin_conv->get_output_partial_shape(0).same_scheme(
PartialShape{Dimension::dynamic(), 1, Dimension::dynamic()}));
}
TEST(type_prop, bin_convolution_dyn_filters)
{
const auto mode = op::v1::BinaryConvolution::BinaryConvolutionMode::XNOR_POPCOUNT;
const float pad_value = 1.0f;
const auto auto_pad = op::PadType::EXPLICIT;
const auto data_batch = make_shared<op::Parameter>(element::f32, PartialShape{1, 1, 5, 5});
const auto filters = make_shared<op::Parameter>(element::u1, PartialShape::dynamic());
const auto bin_conv = make_shared<op::v1::BinaryConvolution>(data_batch,
filters,
Strides{},
CoordinateDiff{},
CoordinateDiff{},
Strides{},
mode,
pad_value,
auto_pad);
ASSERT_TRUE(bin_conv->get_output_partial_shape(0).rank().same_scheme(Rank{4}));
ASSERT_TRUE(bin_conv->get_output_partial_shape(0).same_scheme(
PartialShape{1, Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()}));
}
TEST(type_prop, bin_convolution_dyn_data_batch_and_filters)
{
const auto mode = op::v1::BinaryConvolution::BinaryConvolutionMode::XNOR_POPCOUNT;
const float pad_value = 1.0f;
const auto auto_pad = op::PadType::EXPLICIT;
const auto data_batch = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
const auto filters = make_shared<op::Parameter>(element::u1, PartialShape::dynamic());
const auto bin_conv = make_shared<op::v1::BinaryConvolution>(data_batch,
filters,
Strides{},
CoordinateDiff{},
CoordinateDiff{},
Strides{},
mode,
pad_value,
auto_pad);
ASSERT_TRUE(bin_conv->get_output_partial_shape(0).rank().is_dynamic());
ASSERT_TRUE(bin_conv->get_output_partial_shape(0).same_scheme(PartialShape::dynamic()));
}
TEST(type_prop, bin_convolution_invalid_inputs_et)
{
const auto mode = op::v1::BinaryConvolution::BinaryConvolutionMode::XNOR_POPCOUNT;
const float pad_value = 1.0f;
const auto auto_pad = op::PadType::EXPLICIT;
try
{
const auto data_batch = make_shared<op::Parameter>(element::i32, PartialShape{1, 1, 5, 5});
const auto filters = make_shared<op::Parameter>(element::u1, PartialShape{1, 1, 3, 3});
const auto bin_conv = make_shared<op::v1::BinaryConvolution>(data_batch,
filters,
Strides{},
CoordinateDiff{},
CoordinateDiff{},
Strides{},
mode,
pad_value,
auto_pad);
// data batch element type must be float point
FAIL() << "Incompatible element type of data batch input not detected";
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(), "Data batch element type must be float point");
}
catch (...)
{
FAIL() << "Data batch element type validation check failed for unexpected reason";
}
// TODO: Add test with check filters element type once u1 is supported in nGraph Python API
// (#49517)
}
TEST(type_prop, bin_convolution_incompatible_input_channels)
{
const auto mode = op::v1::BinaryConvolution::BinaryConvolutionMode::XNOR_POPCOUNT;
const float pad_value = 1.0f;
const auto auto_pad = op::PadType::EXPLICIT;
auto data_batch = make_shared<op::Parameter>(element::f32, PartialShape{1, 1, 5, 5});
auto filters = make_shared<op::Parameter>(element::u1, PartialShape{1, 2, 3, 3});
try
{
auto conv = make_shared<op::v1::BinaryConvolution>(data_batch,
filters,
Strides{},
CoordinateDiff{},
CoordinateDiff{},
Strides{},
mode,
pad_value,
auto_pad);
FAIL() << "Incompatible input channel dimension in data batch and filters not detected";
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(), std::string("Data batch channel count"));
}
catch (...)
{
FAIL() << "Data batch and filters input channel count validation check failed for "
"unexpected reason";
}
}
TEST(type_prop, bin_convolution_invalid_input_ranks)
{
const auto mode = op::v1::BinaryConvolution::BinaryConvolutionMode::XNOR_POPCOUNT;
const float pad_value = 1.0f;
const auto auto_pad = op::PadType::EXPLICIT;
// data partial shape provided is rank 4 (Conv2D)
// filter partial shape provided is rank 5 (Conv3D)
try
{
const auto data_batch = make_shared<op::Parameter>(element::f32, PartialShape{1, 1, 5, 5});
const auto filters = make_shared<op::Parameter>(element::u1, PartialShape{1, 1, 3, 3, 3});
const auto bin_conv = make_shared<op::v1::BinaryConvolution>(data_batch,
filters,
Strides{},
CoordinateDiff{},
CoordinateDiff{},
Strides{},
mode,
pad_value,
auto_pad);
// data batch and filters have incompatible ranks
FAIL() << "Incompatible input ranks not detected";
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Shapes for data batch and filters must have same rank.");
}
catch (...)
{
FAIL() << "Rank validation check of inputs failed for unexpected reason";
}
// data partial shape provided is rank 5 (Conv3D)
// filter partial shape provided is rank 4 (Conv2D)
try
{
const auto data_batch =
make_shared<op::Parameter>(element::f32, PartialShape{1, 1, 5, 5, 5});
const auto filters = make_shared<op::Parameter>(element::u1, PartialShape{1, 1, 3, 3});
const auto bin_conv = make_shared<op::v1::BinaryConvolution>(data_batch,
filters,
Strides{},
CoordinateDiff{},
CoordinateDiff{},
Strides{},
mode,
pad_value,
auto_pad);
// data batch and filters have incompatible ranks
FAIL() << "Incompatible input ranks not detected";
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Shapes for data batch and filters must have same rank.");
}
catch (...)
{
FAIL() << "Rank validation check of inputs failed for unexpected reason";
}
}
TEST(type_prop, bin_convolution_invalid_spatial_dims_parameters)
{
Strides strides_1d{1};
Strides strides_3d{1, 1, 1};
Strides dilations_2d{1, 1};
Strides dilations_3d{1, 1, 1};
CoordinateDiff pads_end_2d{0, 0};
CoordinateDiff pads_begin_3d{0, 0, 0};
const auto mode = op::v1::BinaryConvolution::BinaryConvolutionMode::XNOR_POPCOUNT;
const float pad_value = 1.0f;
const auto auto_pad = op::PadType::EXPLICIT;
try
{
const auto data_batch = make_shared<op::Parameter>(element::f32, PartialShape{1, 1, 5, 5});
const auto filters = make_shared<op::Parameter>(element::u1, PartialShape{1, 1, 3, 3});
const auto bin_conv = make_shared<op::v1::BinaryConvolution>(data_batch,
filters,
strides_3d,
CoordinateDiff{},
CoordinateDiff{},
dilations_2d,
mode,
pad_value,
auto_pad);
// Strides have incompatible number of spatial dimensions
FAIL() << "Incompatible stride number of spatial dimensions not detected.";
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(
error.what(),
std::string("Strides should be defined for all and only spatial features."));
}
catch (...)
{
FAIL() << "Strides validation check failed for unexpected reason.";
}
try
{
const auto data_batch = make_shared<op::Parameter>(element::f32, PartialShape{1, 1, 5});
const auto filters = make_shared<op::Parameter>(element::u1, PartialShape{1, 1, 3});
const auto bin_conv = make_shared<op::v1::BinaryConvolution>(data_batch,
filters,
strides_1d,
CoordinateDiff{},
CoordinateDiff{},
dilations_2d,
mode,
pad_value,
auto_pad);
// Dilations have incompatible number of spatial dimensions
FAIL() << "Incompatible dilations number of spatial dimensions not detected.";
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(
error.what(),
std::string("Dilations should be defined for all and only spatial features."));
}
catch (...)
{
FAIL() << "Dilations validation check failed for unexpected reason.";
}
try
{
const auto data_batch =
make_shared<op::Parameter>(element::f32, PartialShape{1, 1, 5, 5, 5});
const auto filters = make_shared<op::Parameter>(element::u1, PartialShape{1, 1, 3, 3, 3});
const auto bin_conv = make_shared<op::v1::BinaryConvolution>(data_batch,
filters,
strides_3d,
pads_begin_3d,
pads_end_2d,
dilations_3d,
mode,
pad_value,
auto_pad);
// Pads have incompatible number of spatial dimensions
FAIL() << "Incompatible pads number of spatial dimensions not detected.";
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(
error.what(), std::string("Pads should be defined for all and only spatial features."));
}
catch (...)
{
FAIL() << "Pads validation check failed for unexpected reason.";
}
}

View File

@ -148,7 +148,7 @@ namespace
case element::Type_t::u16: return InferenceEngine::Precision::U16; break; case element::Type_t::u16: return InferenceEngine::Precision::U16; break;
case element::Type_t::u32: return InferenceEngine::Precision::U32; break; case element::Type_t::u32: return InferenceEngine::Precision::U32; break;
case element::Type_t::u64: return InferenceEngine::Precision::U64; break; case element::Type_t::u64: return InferenceEngine::Precision::U64; break;
case element::Type_t::u1: throw std::runtime_error("unsupported type"); case element::Type_t::u1: return InferenceEngine::Precision::BIN; break;
case element::Type_t::undefined: throw std::runtime_error("unsupported type"); case element::Type_t::undefined: throw std::runtime_error("unsupported type");
case element::Type_t::dynamic: throw std::runtime_error("unsupported type"); case element::Type_t::dynamic: throw std::runtime_error("unsupported type");
} }