Add CNN1D limitation for stride > filter, add negative tests, simplify (#9506)

Pad filter when effective stride > filter
    Add tests with stride bigger than filter
    Add stride not aligned to 8 from review
This commit is contained in:
Krzysztof Bruniecki
2022-01-20 17:14:44 +01:00
committed by GitHub
parent ae648614d9
commit 68ef22a53d
2 changed files with 49 additions and 21 deletions

View File

@@ -283,6 +283,9 @@ void GNAGraphCompiler::ConvolutionPrimitive(InferenceEngine::CNNLayerPtr layer)
(convolution._stride_y * convolution._kernel_x);
convolution._kernel_x *= convolution._kernel_y;
convolution._kernel_y = 1;
// since _kernel_y = 1 && in_height = 1
// it will be finalized with finalizeConvolution1DPrimitive()
// unless some other exception thrown
}
if (in_batch != 1 || out_batch != 1) {
@@ -316,7 +319,6 @@ void GNAGraphCompiler::ConvolutionPrimitive(InferenceEngine::CNNLayerPtr layer)
// When layer layout is in NHWC it means that is was created by PassManager
return finalizeConvolution2DPrimitive(layer, in_batch, in_channels, in_height, in_width,
out_batch, out_channels, out_height, out_width);
THROW_GNA_LAYER_EXCEPTION(layer) << "Convolution 2D is not supported on GNA 1.0 library";
}
finalizeConvolution1DPrimitive(layer, in_batch, in_channels, in_width,
out_batch, out_channels, out_width, in_kernel_w, in_kernel_h, transpose_h_w);
@@ -346,15 +348,29 @@ void GNAGraphCompiler::finalizeConvolution1DPrimitive(InferenceEngine::CNNLayerP
IE_ASSERT(convolution._kernel_y == 1);
uint32_t total_conv_kernel_size = convolution._kernel_x * convolution._out_depth * in_channels;
uint32_t single_conv_kernel_size = convolution._kernel_x * in_channels;
const uint32_t single_conv_kernel_size = convolution._kernel_x * in_channels;
auto actual_kernel_size = details::product(convolution._weights->getTensorDesc().getDims());
if (total_conv_kernel_size != actual_kernel_size) {
THROW_GNA_LAYER_EXCEPTION(&convolution) << "Weights size does not equal kernel size "
<< actual_kernel_size << " vs " << total_conv_kernel_size;
}
// GNA HW Convolution 1D layer natively supports single channel input and filter
// to use it for multiple channels input and filter
// the convolution stride must be multiplied accordingly
auto effectiveStride = in_channels * convolution._stride_x;
if (convolution._stride_y == 1 && in_width == 1 && convolution._stride_x != 1) {
// TODO: investigate whether this condition is needed at all
// seams that if in_width == 1, then convolution._stride_x == 1 as well
THROW_GNA_LAYER_EXCEPTION(&convolution) << "Convolution 1D Layer has horizontal stride != 1 despite input width == 1\n";
}
// padding of convolution kernel to be multiply of 8
uint32_t num_conv_kernel_padding = ALIGN(single_conv_kernel_size, 8) - single_conv_kernel_size;
// additionally have to pad filter for stride > filter since
// GNA HW supports CNN1D with convolution stride not greater than filter length
const auto num_filter_coefficients = ALIGN(std::max(single_conv_kernel_size, effectiveStride), 8);
const auto num_conv_kernel_padding = num_filter_coefficients - single_conv_kernel_size;
if (num_conv_kernel_padding == 0) {
gnalog() << LAYER_NAME(&convolution) << "Kernel is aligned \n";
} else {
@@ -363,23 +379,13 @@ void GNAGraphCompiler::finalizeConvolution1DPrimitive(InferenceEngine::CNNLayerP
// have to pad input to let last kernel meets it's corresponding input
const auto num_inputs = in_width * in_channels;
uint32_t num_input_padding = ALIGN(num_inputs, 8) - num_inputs;
// convert to 2D and set GNA input feature map size
auto convStride = convolution._stride_x * convolution._stride_y;
if (convolution._stride_y != 1) {
convStride = convolution._stride_x;
} else if (in_width == 1 && convolution._stride_x != 1) {
convStride = convolution._stride_y;
}
const auto effectiveStride = in_channels * convStride;
uint32_t num_filters = convolution._out_depth;
uint32_t num_filter_coefficients = single_conv_kernel_size + num_conv_kernel_padding;
uint32_t num_columns_in = num_inputs + num_input_padding;
uint32_t num_columns_out = (((num_inputs - num_filter_coefficients) / effectiveStride) + 1) * convolution._out_depth;
uint32_t num_columns_out_unpadded = (((num_inputs - single_conv_kernel_size) / effectiveStride) + 1) * convolution._out_depth;
const uint32_t num_filters = convolution._out_depth;
uint32_t num_columns_out = (((num_inputs - num_filter_coefficients) / effectiveStride) + 1) * num_filters;
uint32_t num_columns_out_unpadded = (((num_inputs - single_conv_kernel_size) / effectiveStride) + 1) * num_filters;
uint32_t original_input_padding = num_input_padding;
uint32_t additional_padding = 0;
@@ -388,7 +394,7 @@ void GNAGraphCompiler::finalizeConvolution1DPrimitive(InferenceEngine::CNNLayerP
while (num_columns_out < out_batch * out_channels * out_width) {
num_input_padding = original_input_padding + additional_padding;
num_columns_in = num_inputs + num_input_padding;
num_columns_out = (((num_inputs + num_input_padding - num_filter_coefficients) / effectiveStride) + 1) * convolution._out_depth;
num_columns_out = (((num_inputs + num_input_padding - num_filter_coefficients) / effectiveStride) + 1) * num_filters;
dnn->new_num_conv_columns = num_columns_out;
additional_padding += 8;
}
@@ -476,7 +482,7 @@ void GNAGraphCompiler::finalizeConvolution1DPrimitive(InferenceEngine::CNNLayerP
auto B = transpose_h_w ? in_kernel_w : convolution._kernel[X_AXIS];
std::vector<uint8_t> transposedWeights;
for (uint32_t k = 0; k < convolution._out_depth; k++) {
for (uint32_t k = 0; k < num_filters; k++) {
uint8_t * ptr_filt_current
= convolution._weights->cbuffer().as<uint8_t*>() +
k * A * B * convolution.precision.size();
@@ -495,7 +501,7 @@ void GNAGraphCompiler::finalizeConvolution1DPrimitive(InferenceEngine::CNNLayerP
convolution._weights->byteSize(),
64);
} else {
auto paddedWeights = (single_conv_kernel_size + num_conv_kernel_padding) * convolution._out_depth;
auto paddedWeights = num_filter_coefficients * num_filters;
auto paddedWeightsSize = paddedWeights * convolution.precision.size();
auto initializer = [=](void* data, std::size_t size) {
if (paddedWeightsSize > size) {
@@ -504,7 +510,7 @@ void GNAGraphCompiler::finalizeConvolution1DPrimitive(InferenceEngine::CNNLayerP
std::size_t offset = 0;
std::vector<uint8_t> padding_zeros(num_conv_kernel_padding * convolution.precision.size(), 0);
uint8_t* dstPtr = reinterpret_cast<uint8_t*>(data);
for (int i = 0; i < convolution._out_depth; i++) {
for (int i = 0; i < num_filters; i++) {
ie_memcpy(dstPtr + offset,
size - offset,
transposedWeights.data() + single_conv_kernel_size * i * convolution.precision.size(),

View File

@@ -41,6 +41,8 @@ const std::vector<std::vector<size_t >> kernelsH1 = {{1, 3},
{1, 5}};
const std::vector<std::vector<size_t >> stridesH1 = {{1, 1},
{1, 3}};
const std::vector<std::vector<size_t>> stridesH1Big = {{1, 9},
{1, 16}};
const std::vector<std::vector<ptrdiff_t>> padBeginsH1 = {{1, 0},
{1, 3}};
const std::vector<std::vector<ptrdiff_t>> padEndsH1 = {{1, 0},
@@ -146,6 +148,14 @@ const auto conv2DParams_AutoPadValid_Height1 = ::testing::Combine(
::testing::ValuesIn(numOutCannels),
::testing::Values(ngraph::op::PadType::VALID)
);
const auto conv2DParams_AutoPadValid_Height1_BigStride = ::testing::Combine(
::testing::ValuesIn(kernelsH1),
::testing::ValuesIn(stridesH1Big),
::testing::Values(std::vector<ptrdiff_t>({0, 0})),
::testing::Values(std::vector<ptrdiff_t>({0, 0})),
::testing::ValuesIn(dilationsH1),
::testing::ValuesIn(numOutCannels),
::testing::Values(ngraph::op::PadType::VALID));
const auto conv2DParams_AutoPadValid_Width1 = ::testing::Combine(
::testing::ValuesIn(kernelsW1),
::testing::ValuesIn(stridesW1),
@@ -202,6 +212,18 @@ INSTANTIATE_TEST_SUITE_P(smoke_Convolution2D_AutoPadValid_Height1, ConvolutionLa
::testing::Values(CommonTestUtils::DEVICE_GNA)),
ConvolutionLayerTest::getTestCaseName);
INSTANTIATE_TEST_SUITE_P(smoke_Convolution2D_AutoPadValid_Height1_BigStride,
ConvolutionLayerTest,
::testing::Combine(conv2DParams_AutoPadValid_Height1_BigStride,
::testing::ValuesIn(netPrecisions),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::ValuesIn(inputShapesH1),
::testing::Values(CommonTestUtils::DEVICE_GNA)),
ConvolutionLayerTest::getTestCaseName);
INSTANTIATE_TEST_SUITE_P(smoke_Convolution2D_AutoPadValid_Width1, ConvolutionLayerTest,
::testing::Combine(
conv2DParams_AutoPadValid_Width1,