From c4270cf958e85e0c41590030e1f9e228493a5ba0 Mon Sep 17 00:00:00 2001 From: SiCongLi Date: Wed, 22 Dec 2021 11:22:40 +0000 Subject: Add tests for FP Cpu Pooling where pool region is completely outside the input * Add floating point validation tests for this configuration * Fix reference implementation to return -inf for this configuration * Prohibit this config in Cl, as well as non-float cases in Cpu * Direct this config to non-asm path Resolves COMPMID-4998 Change-Id: If88025c51b14ea337aea2441c548f858e95e5819 Signed-off-by: SiCongLi Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/6857 Reviewed-by: Gunes Bayir Tested-by: Arm Jenkins Comments-Addressed: Arm Jenkins --- arm_compute/core/Utils.h | 7 ++++++ arm_compute/runtime/CL/functions/CLPoolingLayer.h | 3 +++ .../runtime/NEON/functions/NEPoolingLayer.h | 2 ++ src/core/Utils.cpp | 12 ++++++++++ src/cpu/kernels/CpuPool2dKernel.cpp | 4 ++++ .../internal/CpuPool2dAssemblyWrapperKernel.cpp | 5 +--- src/gpu/cl/kernels/ClPool2dKernel.cpp | 3 +++ tests/validation/NEON/PoolingLayer.cpp | 28 ++++++++++++++++++++++ tests/validation/Validation.h | 5 ++-- tests/validation/reference/PoolingLayer.cpp | 4 ++-- 10 files changed, 64 insertions(+), 9 deletions(-) diff --git a/arm_compute/core/Utils.h b/arm_compute/core/Utils.h index 0ad80bc998..88cb295c44 100644 --- a/arm_compute/core/Utils.h +++ b/arm_compute/core/Utils.h @@ -886,6 +886,13 @@ const std::string &string_from_norm_type(NormType type); * @return The string describing the pooling type. */ const std::string &string_from_pooling_type(PoolingType type); +/** Check if the pool region is entirely outside the input tensor + * + * @param[in] info @ref PoolingLayerInfo to be checked. + * + * @return True if the pool region is entirely outside the input tensor, False otherwise. + */ +bool is_pool_region_entirely_outside_input(const PoolingLayerInfo &info); /** Translates a given GEMMLowp output stage to a string. * * @param[in] output_stage @ref GEMMLowpOutputStageInfo to be translated to string. diff --git a/arm_compute/runtime/CL/functions/CLPoolingLayer.h b/arm_compute/runtime/CL/functions/CLPoolingLayer.h index 1975e15470..2163c16801 100644 --- a/arm_compute/runtime/CL/functions/CLPoolingLayer.h +++ b/arm_compute/runtime/CL/functions/CLPoolingLayer.h @@ -66,6 +66,9 @@ public: * |F16 |F16 | * |F32 |F32 | * + * @note Source tensor is padded with -inf for MAX pooling and 0 otherwise + * Cases where pooling region is completely outside input tensor are not supported + * * @param[in,out] input Source tensor. (Written to only when padding != 0) Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32. * @param[out] output Destination tensor. Data types supported: Same as @p input. * @param[in] pool_info Contains pooling operation information described in @ref PoolingLayerInfo. diff --git a/arm_compute/runtime/NEON/functions/NEPoolingLayer.h b/arm_compute/runtime/NEON/functions/NEPoolingLayer.h index 9398e1fce9..9147ad9687 100644 --- a/arm_compute/runtime/NEON/functions/NEPoolingLayer.h +++ b/arm_compute/runtime/NEON/functions/NEPoolingLayer.h @@ -71,6 +71,8 @@ public: * |F32 |F32 | * * @note F16 is supported for pool sizes 2 and 3 only + * @note Source tensor is padded with -inf for MAX pooling and 0 otherwise + * Cases where pooling region is completely outside input tensor are only supported for floating point data type * * @param[in, out] input Source tensor. (Written to only when padding != 0) Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32. * @param[out] output Destination tensor. Data types supported: Same as @p input. diff --git a/src/core/Utils.cpp b/src/core/Utils.cpp index 930e4c7975..ef16cb5e75 100644 --- a/src/core/Utils.cpp +++ b/src/core/Utils.cpp @@ -232,6 +232,18 @@ const std::string &string_from_pooling_type(PoolingType type) return pool_type_map[type]; } +bool is_pool_region_entirely_outside_input(const PoolingLayerInfo &info) +{ + if(info.is_global_pooling || info.exclude_padding || info.pool_size.x() == 0 || info.pool_size.y() == 0) + { + return false; + } + const auto ps = info.pad_stride_info; + const auto pool_le_padding_x = info.pool_size.x() <= std::max({ ps.pad_left(), ps.pad_right() }); + const auto pool_le_padding_y = info.pool_size.y() <= std::max({ ps.pad_top(), ps.pad_bottom() }); + return pool_le_padding_x || pool_le_padding_y; +} + const std::string &string_from_gemmlowp_output_stage(GEMMLowpOutputStageType output_stage) { static std::map output_stage_map = diff --git a/src/cpu/kernels/CpuPool2dKernel.cpp b/src/cpu/kernels/CpuPool2dKernel.cpp index 00b1ac76f5..f61cd0835d 100644 --- a/src/cpu/kernels/CpuPool2dKernel.cpp +++ b/src/cpu/kernels/CpuPool2dKernel.cpp @@ -199,6 +199,10 @@ Status validate_arguments(const ITensorInfo *src, const ITensorInfo *dst, const const int idx_width = get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH); const int idx_height = get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT); + ARM_COMPUTE_RETURN_ERROR_ON_MSG((!is_data_type_float(src->data_type())) + && (is_pool_region_entirely_outside_input(pool_info)), + "Pooling region that is entirely outside input tensor is unsupported for non-float types"); + std::tie(output_width, output_height) = scaled_dimensions_signed(src->tensor_shape()[idx_width], src->tensor_shape()[idx_height], pool_size.x(), pool_size.y(), pool_info.pad_stride_info); ARM_COMPUTE_RETURN_ERROR_ON_MSG((output_width < 1 || output_height < 1), "Calculated output dimension size is invalid"); diff --git a/src/cpu/kernels/internal/CpuPool2dAssemblyWrapperKernel.cpp b/src/cpu/kernels/internal/CpuPool2dAssemblyWrapperKernel.cpp index 8610bc7f43..a8c6a5e611 100644 --- a/src/cpu/kernels/internal/CpuPool2dAssemblyWrapperKernel.cpp +++ b/src/cpu/kernels/internal/CpuPool2dAssemblyWrapperKernel.cpp @@ -104,10 +104,7 @@ Status CpuPool2dAssemblyWrapperKernel::validate(const ITensorInfo *src, const IT ARM_COMPUTE_RETURN_ERROR_ON_MSG((info.pool_type != PoolingType::AVG) && (info.pool_type != PoolingType::MAX), "Only AVG and MAX pooling are supported by assembly kernels"); - const auto ps = info.pad_stride_info; - const auto max_padding = std::max({ ps.pad_left(), ps.pad_right(), ps.pad_top(), ps.pad_bottom() }); - const auto min_pool_sz = std::min(info.pool_size.x(), info.pool_size.y()); - ARM_COMPUTE_RETURN_ERROR_ON_MSG(max_padding > min_pool_sz, "Convolution padding greater than pool size is unsupported by assembly kernels"); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(is_pool_region_entirely_outside_input(info), "Pooling region that is entirely outside input tensor is unsupported by assembly kernels"); if(dst->total_size() > 0) { diff --git a/src/gpu/cl/kernels/ClPool2dKernel.cpp b/src/gpu/cl/kernels/ClPool2dKernel.cpp index 5e53799f30..2c98c5940f 100644 --- a/src/gpu/cl/kernels/ClPool2dKernel.cpp +++ b/src/gpu/cl/kernels/ClPool2dKernel.cpp @@ -57,6 +57,9 @@ Status validate_arguments(const ITensorInfo *src, const ITensorInfo *dst, const unsigned int pool_size_y = is_global_pooling ? src->dimension(idx_height) : pool_info.pool_size.height; int output_width = 0; int output_height = 0; + + ARM_COMPUTE_RETURN_ERROR_ON_MSG(is_pool_region_entirely_outside_input(pool_info), "Pooling region that is entirely outside input tensor is unsupported"); + std::tie(output_width, output_height) = scaled_dimensions_signed(src->tensor_shape()[idx_width], src->tensor_shape()[idx_height], pool_size_x, pool_size_y, pool_info.pad_stride_info); ARM_COMPUTE_RETURN_ERROR_ON_MSG((output_width < 1 || output_height < 1), "Calculated output dimension size is invalid"); diff --git a/tests/validation/NEON/PoolingLayer.cpp b/tests/validation/NEON/PoolingLayer.cpp index 77a501582c..457610f2bd 100644 --- a/tests/validation/NEON/PoolingLayer.cpp +++ b/tests/validation/NEON/PoolingLayer.cpp @@ -81,6 +81,14 @@ const auto qasymm8_signed_out_qinfo_dataset = framework::dataset::make("OutputQu QuantizationInfo(.1f, -5), // Multiplier <= 1 QuantizationInfo(2.f, -3) // Multiplier > 1 }); + +// Cases where pooling region is completely outside the input tensor (excluding global pooling) +const auto pool_outside_input_dataset = zip(zip(zip(zip( + framework::dataset::make("Shape", { TensorShape{ 2U, 2U, 1U }, TensorShape{ 2U, 2U, 4U }, TensorShape{ 3U, 5U, 2U }, TensorShape{ 10U, 20U, 3U } }), + framework::dataset::make("PoolingType", { PoolingType::MAX, PoolingType::AVG, PoolingType::L2, PoolingType::MAX })), + framework::dataset::make("PoolingSize", { Size2D{ 2, 2 }, Size2D{ 3, 3 }, Size2D{ 2, 2 }, Size2D{ 3, 6 } })), + framework::dataset::make("PadStride", { PadStrideInfo{ 1, 1, 2, 2 }, PadStrideInfo{ 1, 1, 4, 4 }, PadStrideInfo{ 1, 1, 3, 3 }, PadStrideInfo{ 1, 1, 2, 5 } })), + framework::dataset::make("ExcludePadding", { false, false, false, false })); } // namespace TEST_SUITE(NEON) @@ -186,6 +194,16 @@ FIXTURE_DATA_TEST_CASE(RunLarge, NEPoolingLayerFixture, framework::Datase // Validate output validate(Accessor(_target), _reference, tolerance_f32); } +TEST_SUITE(CornerCases) +FIXTURE_DATA_TEST_CASE(PoolRegionCompletelyOutsideInput, NEPoolingLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(pool_outside_input_dataset, + framework::dataset::make("DataType", + DataType::F32)), + pool_data_layout_dataset)) +{ + // Validate output + validate(Accessor(_target), _reference, tolerance_f32); +} +TEST_SUITE_END() // CornerCases TEST_SUITE_END() // FP32 #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC @@ -213,6 +231,16 @@ FIXTURE_DATA_TEST_CASE(RunLarge, NEPoolingLayerFixture, framework::Dataset // Validate output validate(Accessor(_target), _reference, tolerance_f16); } +TEST_SUITE(CornerCases) +FIXTURE_DATA_TEST_CASE(PoolRegionCompletelyOutsideInput, NEPoolingLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(pool_outside_input_dataset, + framework::dataset::make("DataType", + DataType::F16)), + pool_data_layout_dataset)) +{ + // Validate output + validate(Accessor(_target), _reference, tolerance_f16); +} +TEST_SUITE_END() // CornerCases TEST_SUITE_END() // FP16 #endif /* __ARM_FEATURE_FP16_VECTOR_ARITHMETIC */ TEST_SUITE_END() // Float diff --git a/tests/validation/Validation.h b/tests/validation/Validation.h index 4f3f92da24..7bad1a2286 100644 --- a/tests/validation/Validation.h +++ b/tests/validation/Validation.h @@ -49,10 +49,9 @@ namespace { // Compare if 2 values are both infinities and if they are "equal" (has the same sign) template -bool are_equal_infs(T val0, T val1) +inline bool are_equal_infs(T val0, T val1) { - const auto same_sign = std::signbit(val0) == std::signbit(val1); - return (!support::cpp11::isfinite(val0)) && (!support::cpp11::isfinite(val1)) && same_sign; + return (!support::cpp11::isfinite(val0)) && (!support::cpp11::isfinite(val1)) && (std::signbit(val0) == std::signbit(val1)); } } // namespace diff --git a/tests/validation/reference/PoolingLayer.cpp b/tests/validation/reference/PoolingLayer.cpp index 5f4edfe49c..9e671e3173 100644 --- a/tests/validation/reference/PoolingLayer.cpp +++ b/tests/validation/reference/PoolingLayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Arm Limited. + * Copyright (c) 2017-2021 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -88,7 +88,7 @@ SimpleTensor pooling_layer_internal(const SimpleTensor &src, const Pooling int hend = std::min(hstart + pool_size_y, h_src); wstart = std::max(wstart, 0); hstart = std::max(hstart, 0); - auto max_val = std::numeric_limits::lowest(); + auto max_val = -std::numeric_limits::infinity(); int max_index{ 0 }; for(int y = hstart; y < hend; ++y) { -- cgit v1.2.1