From 27c08abe6947b1ee5b266799f2bb2bf0a05d0def Mon Sep 17 00:00:00 2001 From: Alex Gilday Date: Thu, 22 Feb 2018 11:36:16 +0000 Subject: COMPMID-754: Add validation to LocallyConnected and NEDeconv layers Change-Id: Ifed8713f4d7f1315af684b30d11323db2b533f10 Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/121783 Reviewed-by: Anthony Barbier Tested-by: Jenkins Reviewed-by: Michalis Spyrou --- .../CLLocallyConnectedMatrixMultiplyKernel.h | 11 +- .../NELocallyConnectedMatrixMultiplyKernel.h | 9 ++ .../runtime/CL/functions/CLLocallyConnectedLayer.h | 16 ++- .../runtime/NEON/functions/NEDeconvolutionLayer.h | 14 +++ .../NEON/functions/NELocallyConnectedLayer.h | 16 ++- .../CLLocallyConnectedMatrixMultiplyKernel.cpp | 63 +++++++--- .../NELocallyConnectedMatrixMultiplyKernel.cpp | 57 ++++++--- .../CL/functions/CLLocallyConnectedLayer.cpp | 132 ++++++++++++++------- .../NEON/functions/NEDeconvolutionLayer.cpp | 69 +++++++++-- .../NEON/functions/NELocallyConnectedLayer.cpp | 132 ++++++++++++++------- tests/validation/CL/LocallyConnected.cpp | 61 ++++++++++ tests/validation/NEON/DeconvolutionLayer.cpp | 100 ++++++++++++++++ tests/validation/NEON/LocallyConnected.cpp | 61 ++++++++++ 13 files changed, 607 insertions(+), 134 deletions(-) diff --git a/arm_compute/core/CL/kernels/CLLocallyConnectedMatrixMultiplyKernel.h b/arm_compute/core/CL/kernels/CLLocallyConnectedMatrixMultiplyKernel.h index 948f47a901..fee505523d 100644 --- a/arm_compute/core/CL/kernels/CLLocallyConnectedMatrixMultiplyKernel.h +++ b/arm_compute/core/CL/kernels/CLLocallyConnectedMatrixMultiplyKernel.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 ARM Limited. + * Copyright (c) 2017-2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -55,6 +55,15 @@ public: * @param[out] output Output tensor to store the result. Data type supported: same as @p input0 */ void configure(const ICLTensor *input0, const ICLTensor *input1, ICLTensor *output); + /** Static function to check if given info will lead to a valid configuration of @ref CLLocallyConnectedMatrixMultiplyKernel + * + * @param[in] input0 First input tensor info. Data types supported: F32 + * @param[in] input1 Second input tensor info. Data type supported: same as @p input0 + * @param[in] output Output tensor info. Data type supported: same as @p input0 + * + * @return a status + */ + static Status validate(const ITensorInfo *input0, const ITensorInfo *input1, const ITensorInfo *output); // Inherited methods overridden: void run(const Window &window, cl::CommandQueue &queue) override; diff --git a/arm_compute/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.h b/arm_compute/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.h index 5e3410fe6f..19a92c0cdc 100644 --- a/arm_compute/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.h +++ b/arm_compute/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.h @@ -55,6 +55,15 @@ public: * @param[out] output Output tensor to store the result of matrix multiplication. Data type supported: same as @p input0 */ void configure(const ITensor *input0, const ITensor *input1, ITensor *output); + /** Static function to check if given info will lead to a valid configuration of @ref NELocallyConnectedMatrixMultiplyKernel + * + * @param[in] input0 First input tensor info. Data types supported: F16, F32 + * @param[in] input1 Second input tensor info. Data type supported: same as @p input0 + * @param[in] output Output tensor info. Data type supported: same as @p input0 + * + * @return a status + */ + static Status validate(const ITensorInfo *input0, const ITensorInfo *input1, const ITensorInfo *output); // Inherited methods overridden: void run(const Window &window, const ThreadInfo &info) override; diff --git a/arm_compute/runtime/CL/functions/CLLocallyConnectedLayer.h b/arm_compute/runtime/CL/functions/CLLocallyConnectedLayer.h index f56039f62a..e5aafd8547 100644 --- a/arm_compute/runtime/CL/functions/CLLocallyConnectedLayer.h +++ b/arm_compute/runtime/CL/functions/CLLocallyConnectedLayer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 ARM Limited. + * Copyright (c) 2017-2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -65,6 +65,20 @@ public: * @param[in] conv_info Contains padding and stride information described in @ref PadStrideInfo. */ void configure(const ICLTensor *input, const ICLTensor *weights, const ICLTensor *biases, ICLTensor *output, const PadStrideInfo &conv_info); + /** Static function to check if given info will lead to a valid configuration of @ref CLLocallyConnectedLayer + * + * @param[in] input Input tensor info. 3 lower dimensions represent a single input [width, height, IFM], + * while every optional dimension from 4 and above represent a batch of inputs. + * Data types supported: F32. + * @param[in] weights Weights tensor info. Weights are 5D tensor with dimensions [kernel_x, kernel_y, IFM, OFM, num_patches]. Data type supported:Same as @p input. + * @param[in] biases Biases tensor info. Shared biases supported. Biases are 2D tensor with dimensions [OFM, num_patches]. Data type supported:Same as @p input. + * @param[in] output Output tensor info. 3 lower dimensions represent a single output [width, height, OFM], while the rest represent batch of outputs. + * Data types supported: Same as @p input. + * @param[in] conv_info Contains padding and stride information described in @ref PadStrideInfo. + * + * @return a status + */ + static Status validate(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *biases, const ITensorInfo *output, const PadStrideInfo &conv_info); // Inherited methods overridden: void run() override; diff --git a/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h b/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h index c148c3ff69..1d5dd3fbb4 100644 --- a/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h +++ b/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h @@ -91,6 +91,20 @@ public: */ void configure(ITensor *input, const ITensor *weights, const ITensor *bias, ITensor *output, const PadStrideInfo &info, unsigned int inner_border_right, unsigned int inner_border_top); + /** Static function to check if given info will lead to a valid configuration of @ref NEDeconvolutionLayer + * + * @param[in] input Input tensor info. 3 lower dimensions represent a single input, and an optional 4th dimension for batch of inputs. Data types supported: F32. + * @param[in] weights The 4d weights info with dimensions [width, height, OFM, IFM]. Data type supported: Same as @p input. + * @param[in] bias (Optional) The biases have one dimension. Data type supported: Same as @p input. + * @param[in] output Output tensor info. The output has the same number of dimensions as the @p input. + * @param[in] info Contains padding and policies to be used in the deconvolution, this is decribed in @ref PadStrideInfo. + * @param[in] inner_border_right The number of zeros added to right edge of the input. + * @param[in] inner_border_top The number of zeros added to top edge of the input. + * + * @return a status + */ + static Status validate(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *bias, const ITensorInfo *output, const PadStrideInfo &info, + unsigned int inner_border_right, unsigned int inner_border_top); // Inherited methods overridden: void run() override; diff --git a/arm_compute/runtime/NEON/functions/NELocallyConnectedLayer.h b/arm_compute/runtime/NEON/functions/NELocallyConnectedLayer.h index 18d2a1dfb5..e07f09c588 100644 --- a/arm_compute/runtime/NEON/functions/NELocallyConnectedLayer.h +++ b/arm_compute/runtime/NEON/functions/NELocallyConnectedLayer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 ARM Limited. + * Copyright (c) 2017-2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -65,6 +65,20 @@ public: * @param[in] conv_info Contains padding and stride information described in @ref PadStrideInfo. */ void configure(const ITensor *input, const ITensor *weights, const ITensor *biases, ITensor *output, const PadStrideInfo &conv_info); + /** Static function to check if given info will lead to a valid configuration of @ref NELocallyConnectedLayer + * + * @param[in] input Input tensor info. 3 lower dimensions represent a single input [width, height, IFM], + * while every optional dimension from 4 and above represent a batch of inputs. + * Data types supported: F16, F32. + * @param[in] weights Weights tensor info. Weights are 5D tensor with dimensions [kernel_x, kernel_y, IFM, OFM, num_patches]. Data type supported:Same as @p input. + * @param[in] biases Biases tensor info. Shared biases supported. Biases are 2D tensor with dimensions [OFM, num_patches]. Data type supported:Same as @p input. + * @param[in] output Output tensor info. 3 lower dimensions represent a single output [width, height, OFM], while the rest represent batch of outputs. + * Data types supported: Same as @p input. + * @param[in] conv_info Contains padding and stride information described in @ref PadStrideInfo. + * + * @return a status + */ + static Status validate(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *biases, const ITensorInfo *output, const PadStrideInfo &conv_info); // Inherited methods overridden: void run() override; diff --git a/src/core/CL/kernels/CLLocallyConnectedMatrixMultiplyKernel.cpp b/src/core/CL/kernels/CLLocallyConnectedMatrixMultiplyKernel.cpp index a3af5b00ba..84f2e0cbac 100644 --- a/src/core/CL/kernels/CLLocallyConnectedMatrixMultiplyKernel.cpp +++ b/src/core/CL/kernels/CLLocallyConnectedMatrixMultiplyKernel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018 ARM Limited. + * Copyright (c) 2017-2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -46,13 +46,44 @@ CLLocallyConnectedMatrixMultiplyKernel::CLLocallyConnectedMatrixMultiplyKernel() { } +namespace +{ +Status validate_arguments(const ITensorInfo *input0, const ITensorInfo *input1, const ITensorInfo *output) +{ + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input0, input1, output); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input0, 1, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input1, 1, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input0, input1, output); + ARM_COMPUTE_RETURN_ERROR_ON(input0->dimension(0) != input1->dimension(1)); + + return Status{}; +} + +std::tuple validate_and_configure_window(ITensorInfo *input0, ITensorInfo *input1, ITensorInfo *output) +{ + const unsigned int num_elems_processed_per_iteration_x = max_cl_vector_width / data_size_from_type(input0->data_type()); + + Window win = calculate_max_window(*output, Steps(num_elems_processed_per_iteration_x)); + + AccessWindowHorizontal input0_access(input0, 0, num_elems_processed_per_iteration_x); + AccessWindowHorizontal input1_access(input1, 0, num_elems_processed_per_iteration_x); + AccessWindowHorizontal output_access(output, 0, num_elems_processed_per_iteration_x); + + bool window_changed = update_window_and_padding(win, input0_access, input1_access, output_access); + + output_access.set_valid_region(win, ValidRegion(Coordinates(), output->tensor_shape())); + + Status err = (window_changed) ? ARM_COMPUTE_CREATE_ERROR(ErrorCode::RUNTIME_ERROR, "Insufficient Padding!") : Status{}; + + return std::make_tuple(err, win); +} +} // namespace + void CLLocallyConnectedMatrixMultiplyKernel::configure(const ICLTensor *input0, const ICLTensor *input1, ICLTensor *output) { - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input0, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input1, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_MISMATCHING_DATA_TYPES(input0, input1, output); - ARM_COMPUTE_ERROR_ON(input0->info()->dimension(0) != input1->info()->dimension(1)); + ARM_COMPUTE_ERROR_ON_NULLPTR(input0, input1, output); + ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input0->info(), input1->info(), output->info())); _input0 = input0; _input1 = input1; @@ -77,20 +108,20 @@ void CLLocallyConnectedMatrixMultiplyKernel::configure(const ICLTensor *input0, std::string data_type_name = lower_string(string_from_data_type(input0->info()->data_type())); _kernel = static_cast(CLKernelLibrary::get().create_kernel(("gemm_lc_vm_" + data_type_name), build_opts)); - // Configure window kernel - const unsigned int num_elems_processed_per_iteration_x = max_cl_vector_width / data_size_from_type(input0->info()->data_type()); - - Window win = calculate_max_window(*output->info(), Steps(num_elems_processed_per_iteration_x)); + // Configure kernel window + auto win_config = validate_and_configure_window(input0->info(), input1->info(), output->info()); - AccessWindowRectangle input0_access(input0->info(), 0, 0, num_elems_processed_per_iteration_x, 1); - AccessWindowRectangle input1_access(input1->info(), 0, 0, num_elems_processed_per_iteration_x, 1); - AccessWindowRectangle output_access(output->info(), 0, 0, num_elems_processed_per_iteration_x, 1); + ARM_COMPUTE_ERROR_THROW_ON(std::get<0>(win_config)); - update_window_and_padding(win, input0_access, input1_access, output_access); + ICLKernel::configure(std::get<1>(win_config)); +} - output_access.set_valid_region(win, ValidRegion(Coordinates(), output->info()->tensor_shape())); +Status CLLocallyConnectedMatrixMultiplyKernel::validate(const ITensorInfo *input0, const ITensorInfo *input1, const ITensorInfo *output) +{ + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input0, input1, output)); + ARM_COMPUTE_RETURN_ON_ERROR(std::get<0>(validate_and_configure_window(input0->clone().get(), input1->clone().get(), output->clone().get()))); - ICLKernel::configure(win); + return Status{}; } void CLLocallyConnectedMatrixMultiplyKernel::run(const Window &window, cl::CommandQueue &queue) diff --git a/src/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.cpp b/src/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.cpp index 35beb82689..099626d259 100644 --- a/src/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.cpp +++ b/src/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.cpp @@ -302,6 +302,37 @@ void vector_matrix_multiply_f32(const ITensor *input0, const ITensor *input1, IT }, ina, out); } + +Status validate_arguments(const ITensorInfo *input0, const ITensorInfo *input1, const ITensorInfo *output) +{ + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input0, input1, output); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input0, 1, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input1, 1, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input0, input1, output); + ARM_COMPUTE_RETURN_ERROR_ON(input0->dimension(0) != input1->dimension(1)); + + return Status{}; +} + +std::tuple validate_and_configure_window(ITensorInfo *input0, ITensorInfo *input1, ITensorInfo *output) +{ + const unsigned int num_elems_processed_per_iteration_x = 16; + + Window win = calculate_max_window(*output, Steps(num_elems_processed_per_iteration_x)); + + AccessWindowHorizontal input0_access(input0, 0, num_elems_processed_per_iteration_x); + AccessWindowHorizontal input1_access(input1, 0, num_elems_processed_per_iteration_x); + AccessWindowHorizontal output_access(output, 0, num_elems_processed_per_iteration_x); + + bool window_changed = update_window_and_padding(win, input0_access, input1_access, output_access); + + output_access.set_valid_region(win, ValidRegion(Coordinates(), output->tensor_shape())); + + Status err = (window_changed) ? ARM_COMPUTE_CREATE_ERROR(ErrorCode::RUNTIME_ERROR, "Insufficient Padding!") : Status{}; + + return std::make_tuple(err, win); +} } // namespace NELocallyConnectedMatrixMultiplyKernel::NELocallyConnectedMatrixMultiplyKernel() @@ -311,31 +342,27 @@ NELocallyConnectedMatrixMultiplyKernel::NELocallyConnectedMatrixMultiplyKernel() void NELocallyConnectedMatrixMultiplyKernel::configure(const ITensor *input0, const ITensor *input1, ITensor *output) { - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input0, 1, DataType::F16, DataType::F32); - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input1, 1, DataType::F16, DataType::F32); - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::F16, DataType::F32); - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::F16, DataType::F32); - ARM_COMPUTE_ERROR_ON(input0->info()->dimension(0) != input1->info()->dimension(1)); + ARM_COMPUTE_ERROR_ON_NULLPTR(input0, input1, output); + ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input0->info(), input1->info(), output->info())); _input0 = input0; _input1 = input1; _output = output; - const unsigned int num_elems_processed_per_iteration_x = 16; - // Configure kernel window - Window win = calculate_max_window(*output->info(), Steps(num_elems_processed_per_iteration_x)); + auto win_config = validate_and_configure_window(input0->info(), input1->info(), output->info()); - AccessWindowHorizontal output_access(output->info(), 0, num_elems_processed_per_iteration_x); + ARM_COMPUTE_ERROR_THROW_ON(std::get<0>(win_config)); - update_window_and_padding(win, - AccessWindowHorizontal(input0->info(), 0, num_elems_processed_per_iteration_x), - AccessWindowHorizontal(input1->info(), 0, num_elems_processed_per_iteration_x), - output_access); + INEKernel::configure(std::get<1>(win_config)); +} - output_access.set_valid_region(win, ValidRegion(Coordinates(), output->info()->tensor_shape())); +Status NELocallyConnectedMatrixMultiplyKernel::validate(const ITensorInfo *input0, const ITensorInfo *input1, const ITensorInfo *output) +{ + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input0, input1, output)); + ARM_COMPUTE_RETURN_ON_ERROR(std::get<0>(validate_and_configure_window(input0->clone().get(), input1->clone().get(), output->clone().get()))); - INEKernel::configure(win); + return Status{}; } void NELocallyConnectedMatrixMultiplyKernel::run(const Window &window, const ThreadInfo &info) diff --git a/src/runtime/CL/functions/CLLocallyConnectedLayer.cpp b/src/runtime/CL/functions/CLLocallyConnectedLayer.cpp index d284949323..a3eb5010bd 100644 --- a/src/runtime/CL/functions/CLLocallyConnectedLayer.cpp +++ b/src/runtime/CL/functions/CLLocallyConnectedLayer.cpp @@ -33,40 +33,102 @@ using namespace arm_compute; +namespace +{ +void calculate_shapes(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *biases, const ITensorInfo *output, const PadStrideInfo &conv_info, + TensorShape &shape_wr, TensorShape &shape_im2col, TensorShape &shape_gemm) +{ + ARM_COMPUTE_UNUSED(output); + + const unsigned int kernel_width = weights->dimension(0); + const unsigned int kernel_height = weights->dimension(1); + + bool has_bias = (biases != nullptr); + + // Get convolved dimensions + unsigned int conv_w = 0; + unsigned int conv_h = 0; + std::tie(conv_w, conv_h) = scaled_dimensions(input->dimension(0), input->dimension(1), kernel_width, kernel_height, + conv_info); + + const size_t mat_weights_cols = weights->dimension(3); + const size_t mat_weights_rows = weights->dimension(0) * weights->dimension(1) * weights->dimension(2) + ((has_bias) ? 1 : 0); + const size_t mat_weights_num = weights->dimension(4); + + shape_wr = TensorShape(mat_weights_cols, mat_weights_rows, mat_weights_num); + + const size_t mat_input_cols = mat_weights_rows; + const size_t mat_input_rows = conv_w * conv_h; + + shape_im2col = input->tensor_shape(); + shape_im2col.set(0, mat_input_cols); + shape_im2col.set(1, mat_input_rows); + shape_im2col.set(2, 1); + + shape_gemm = shape_im2col; + shape_gemm.set(0, mat_weights_cols); + shape_gemm.set(1, mat_input_rows); +} +} // namespace + CLLocallyConnectedLayer::CLLocallyConnectedLayer(std::shared_ptr memory_manager) : _memory_group(std::move(memory_manager)), _input_im2col_kernel(), _weights_reshape_kernel(), _mm_kernel(), _output_col2im_kernel(), _input_im2col_reshaped(), _weights_reshaped(), _gemm_output(), _is_first_run(false) { } -void CLLocallyConnectedLayer::configure(const ICLTensor *input, const ICLTensor *weights, const ICLTensor *biases, ICLTensor *output, const PadStrideInfo &conv_info) +Status CLLocallyConnectedLayer::validate(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *biases, const ITensorInfo *output, const PadStrideInfo &conv_info) { - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(weights, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_MISMATCHING_DATA_TYPES(input, weights, output); - ARM_COMPUTE_ERROR_ON(weights->info()->dimension(2) != input->info()->dimension(2)); - ARM_COMPUTE_ERROR_ON(!conv_info.padding_is_symmetric()); - - if(biases != nullptr) + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, weights, output); + ARM_COMPUTE_RETURN_ERROR_ON(weights->dimension(2) != input->dimension(2)); + ARM_COMPUTE_RETURN_ERROR_ON(!conv_info.padding_is_symmetric()); + + bool has_bias = (biases != nullptr); + + if(has_bias) { - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(biases, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_MISMATCHING_DATA_TYPES(input, biases); - ARM_COMPUTE_ERROR_ON(biases->info()->dimension(0) != weights->info()->dimension(3)); - ARM_COMPUTE_ERROR_ON(biases->info()->num_dimensions() > 2); + ARM_COMPUTE_RETURN_ERROR_ON(biases->dimension(0) != weights->dimension(3)); + ARM_COMPUTE_RETURN_ERROR_ON(biases->num_dimensions() > 2); } + const unsigned int kernel_width = weights->dimension(0); + const unsigned int kernel_height = weights->dimension(1); + + // Get convolved dimensions + unsigned int conv_w = 0; + unsigned int conv_h = 0; + std::tie(conv_w, conv_h) = scaled_dimensions(input->dimension(0), input->dimension(1), kernel_width, kernel_height, + conv_info); + + ARM_COMPUTE_RETURN_ERROR_ON_MSG((output->dimension(0) != conv_w) || (output->dimension(1) != conv_h), "Output shape does not match the expected one"); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(weights->dimension(4) != (conv_w * conv_h), "Weights shape does not match the expected one"); + + // Calculate intermediate buffer shapes + TensorShape shape_wr; + TensorShape shape_im2col; + TensorShape shape_gemm; + calculate_shapes(input, weights, biases, output, conv_info, shape_wr, shape_im2col, shape_gemm); + + TensorInfo weights_reshaped_info(shape_wr, 1, weights->data_type()); + TensorInfo input_im2col_reshaped_info(shape_im2col, 1, input->data_type()); + TensorInfo gemm_output_info(shape_gemm, 1, input->data_type()); + + ARM_COMPUTE_RETURN_ON_ERROR(CLIm2ColKernel::validate(input, &input_im2col_reshaped_info, Size2D(kernel_width, kernel_height), conv_info, has_bias)); + ARM_COMPUTE_RETURN_ON_ERROR(CLWeightsReshapeKernel::validate(weights, biases, &weights_reshaped_info)); + ARM_COMPUTE_RETURN_ON_ERROR(CLLocallyConnectedMatrixMultiplyKernel::validate(&input_im2col_reshaped_info, &weights_reshaped_info, &gemm_output_info)); + ARM_COMPUTE_RETURN_ON_ERROR(CLCol2ImKernel::validate(&gemm_output_info, output, std::make_pair(conv_w, conv_h))); + + return Status{}; +} + +void CLLocallyConnectedLayer::configure(const ICLTensor *input, const ICLTensor *weights, const ICLTensor *biases, ICLTensor *output, const PadStrideInfo &conv_info) +{ + ARM_COMPUTE_ERROR_ON_NULLPTR(input, weights, output); + ARM_COMPUTE_ERROR_THROW_ON(CLLocallyConnectedLayer::validate(input->info(), weights->info(), biases == nullptr ? nullptr : biases->info(), output->info(), conv_info)); + bool _has_bias = (biases != nullptr); _is_first_run = true; - // Get parameters for conv_info - unsigned int stride_x = 0; - unsigned int stride_y = 0; - unsigned int pad_x = 0; - unsigned int pad_y = 0; - std::tie(stride_x, stride_y) = conv_info.stride(); - std::tie(pad_x, pad_y) = conv_info.pad(); - const unsigned int kernel_width = weights->info()->dimension(0); const unsigned int kernel_height = weights->info()->dimension(1); @@ -76,32 +138,14 @@ void CLLocallyConnectedLayer::configure(const ICLTensor *input, const ICLTensor std::tie(conv_w, conv_h) = scaled_dimensions(input->info()->dimension(0), input->info()->dimension(1), kernel_width, kernel_height, conv_info); - ARM_COMPUTE_ERROR_ON_MSG((output->info()->dimension(0) != conv_w) || (output->info()->dimension(1) != conv_h), "Output shape does not match the expected one"); - ARM_COMPUTE_ERROR_ON_MSG(weights->info()->dimension(4) != (conv_w * conv_h), "Weights shape does not match the expected one"); - - // Create tensor to store the reshaped weights - const size_t mat_weights_cols = weights->info()->dimension(3); - const size_t mat_weights_rows = weights->info()->dimension(0) * weights->info()->dimension(1) * weights->info()->dimension(2) + ((_has_bias) ? 1 : 0); - const size_t mat_weights_num = weights->info()->dimension(4); - - const TensorShape shape_wr(mat_weights_cols, mat_weights_rows, mat_weights_num); + // Calculate intermediate buffer shapes + TensorShape shape_wr; + TensorShape shape_im2col; + TensorShape shape_gemm; + calculate_shapes(input->info(), weights->info(), biases == nullptr ? nullptr : biases->info(), output->info(), conv_info, shape_wr, shape_im2col, shape_gemm); _weights_reshaped.allocator()->init(TensorInfo(shape_wr, 1, weights->info()->data_type())); - - // Create tensor to store im2col reshaped inputs - const size_t mat_input_cols = mat_weights_rows; - const size_t mat_input_rows = conv_w * conv_h; - TensorShape shape_im2col = input->info()->tensor_shape(); - shape_im2col.set(0, mat_input_cols); - shape_im2col.set(1, mat_input_rows); - shape_im2col.set(2, 1); - _input_im2col_reshaped.allocator()->init(TensorInfo(shape_im2col, 1, input->info()->data_type())); - - // Create locally connected layer output tensor - TensorShape shape_gemm = _input_im2col_reshaped.info()->tensor_shape(); - shape_gemm.set(0, mat_weights_cols); - shape_gemm.set(1, mat_input_rows); _gemm_output.allocator()->init(TensorInfo(shape_gemm, 1, input->info()->data_type())); // Manage intermediate buffers diff --git a/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp b/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp index 693d7a4f70..14d54d2f73 100644 --- a/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp +++ b/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp @@ -42,13 +42,64 @@ NEDeconvolutionLayer::NEDeconvolutionLayer(std::shared_ptr memor { } +Status NEDeconvolutionLayer::validate(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *bias, const ITensorInfo *output, const PadStrideInfo &info, + unsigned int inner_border_right, unsigned int inner_border_top) +{ + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, weights, output); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(weights, 1, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON(weights->dimension(0) != weights->dimension(1)); + ARM_COMPUTE_RETURN_ERROR_ON(weights->dimension(0) < 1); + ARM_COMPUTE_RETURN_ERROR_ON(!info.padding_is_symmetric()); + + const unsigned int stride_x = info.stride().first; + const unsigned int stride_y = info.stride().second; + + ARM_COMPUTE_RETURN_ERROR_ON_MSG(inner_border_right > stride_x - 1, "inner_border_right must be smaller than stride_x"); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(inner_border_top > stride_y - 1, "inner_border_top must be smaller than stride_y"); + + auto out_dims = deconvolution_output_dimensions(input->dimension(0), input->dimension(1), weights->dimension(0), weights->dimension(1), + info.pad().first, info.pad().second, inner_border_right, inner_border_top, stride_x, stride_y); + + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, weights, bias); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_FIXED_POINT(input, weights, bias); + + if(bias != nullptr) + { + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, bias); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_FIXED_POINT(input, bias); + } + + if(output->tensor_shape().total_size() > 0) + { + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, output); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_FIXED_POINT(input, output); + + const TensorShape output_shape = deconvolution_output_shape(out_dims, input->tensor_shape(), weights->tensor_shape()); + + ARM_COMPUTE_RETURN_ERROR_ON_MSG(output->dimension(Window::DimX) != output_shape.x(), "Output's width is invalid."); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(output->dimension(Window::DimY) != output_shape.y(), "Output's height is invalid."); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(output->dimension(Window::DimZ) != output_shape.z(), "Output's depth is invalid."); + } + + TensorInfo scale_out_info(input->clone()->set_is_resizable(true).reset_padding().set_tensor_shape(compute_deconvolution_shape(*input, stride_x, stride_y, inner_border_right, inner_border_top, + info))); + const PadStrideInfo conv_info(1, 1, 0, 0, 0, 0, DimensionRoundingType::CEIL); + + for(size_t i = 2; i < Coordinates::num_max_dimensions; ++i) + { + ARM_COMPUTE_RETURN_ERROR_ON(input->dimension(i) != scale_out_info.dimension(i)); + } + + ARM_COMPUTE_RETURN_ON_ERROR(NEConvolutionLayer::validate(&scale_out_info, weights, bias, output, info, WeightsInfo())); + + return Status{}; +} + void NEDeconvolutionLayer::configure(ITensor *input, const ITensor *weights, const ITensor *bias, ITensor *output, const PadStrideInfo &info, unsigned int inner_border_right, unsigned int inner_border_top) { - ARM_COMPUTE_ERROR_ON_NULLPTR(output); - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON(weights->info()->dimension(0) != weights->info()->dimension(1)); - ARM_COMPUTE_ERROR_ON(!info.padding_is_symmetric()); + ARM_COMPUTE_ERROR_ON_NULLPTR(input, weights, output); _input = input; _info = info; @@ -56,15 +107,9 @@ void NEDeconvolutionLayer::configure(ITensor *input, const ITensor *weights, con const unsigned int stride_x = info.stride().first; const unsigned int stride_y = info.stride().second; - auto out_dims = deconvolution_output_dimensions(input->info()->dimension(0), input->info()->dimension(1), weights->info()->dimension(0), weights->info()->dimension(1), - info.pad().first, info.pad().second, inner_border_right, inner_border_top, stride_x, stride_y); - - const TensorShape output_shape = deconvolution_output_shape(out_dims, input->info()->tensor_shape(), weights->info()->tensor_shape()); - ARM_COMPUTE_UNUSED(output_shape); - ARM_COMPUTE_ERROR_ON_MSG(output->info()->dimension(Window::DimX) != output_shape.x(), "Output's width is invalid."); - ARM_COMPUTE_ERROR_ON_MSG(output->info()->dimension(Window::DimY) != output_shape.y(), "Output's height is invalid."); - ARM_COMPUTE_ERROR_ON_MSG(output->info()->dimension(Window::DimZ) != output_shape.z(), "Output's depth is invalid."); + // Perform validation step + ARM_COMPUTE_ERROR_THROW_ON(NEDeconvolutionLayer::validate(input->info(), weights->info(), bias == nullptr ? nullptr : bias->info(), output->info(), info, inner_border_right, inner_border_top)); _memory_group.manage(&_scaled_output); diff --git a/src/runtime/NEON/functions/NELocallyConnectedLayer.cpp b/src/runtime/NEON/functions/NELocallyConnectedLayer.cpp index 45ddb70ff5..973559441f 100644 --- a/src/runtime/NEON/functions/NELocallyConnectedLayer.cpp +++ b/src/runtime/NEON/functions/NELocallyConnectedLayer.cpp @@ -33,40 +33,102 @@ using namespace arm_compute; +namespace +{ +void calculate_shapes(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *biases, const ITensorInfo *output, const PadStrideInfo &conv_info, + TensorShape &shape_wr, TensorShape &shape_im2col, TensorShape &shape_gemm) +{ + ARM_COMPUTE_UNUSED(output); + + const unsigned int kernel_width = weights->dimension(0); + const unsigned int kernel_height = weights->dimension(1); + + bool has_bias = (biases != nullptr); + + // Get convolved dimensions + unsigned int conv_w = 0; + unsigned int conv_h = 0; + std::tie(conv_w, conv_h) = scaled_dimensions(input->dimension(0), input->dimension(1), kernel_width, kernel_height, + conv_info); + + const size_t mat_weights_cols = weights->dimension(3); + const size_t mat_weights_rows = weights->dimension(0) * weights->dimension(1) * weights->dimension(2) + ((has_bias) ? 1 : 0); + const size_t mat_weights_num = weights->dimension(4); + + shape_wr = TensorShape(mat_weights_cols, mat_weights_rows, mat_weights_num); + + const size_t mat_input_cols = mat_weights_rows; + const size_t mat_input_rows = conv_w * conv_h; + + shape_im2col = input->tensor_shape(); + shape_im2col.set(0, mat_input_cols); + shape_im2col.set(1, mat_input_rows); + shape_im2col.set(2, 1); + + shape_gemm = shape_im2col; + shape_gemm.set(0, mat_weights_cols); + shape_gemm.set(1, mat_input_rows); +} +} // namespace + NELocallyConnectedLayer::NELocallyConnectedLayer(std::shared_ptr memory_manager) : _memory_group(std::move(memory_manager)), _input_im2col_kernel(), _weights_reshape_kernel(), _mm_kernel(), _output_col2im_kernel(), _input_im2col_reshaped(), _weights_reshaped(), _gemm_output(), _is_first_run(false) { } -void NELocallyConnectedLayer::configure(const ITensor *input, const ITensor *weights, const ITensor *biases, ITensor *output, const PadStrideInfo &conv_info) +Status NELocallyConnectedLayer::validate(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *biases, const ITensorInfo *output, const PadStrideInfo &conv_info) { - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(weights, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_MISMATCHING_DATA_TYPES(input, weights, output); - ARM_COMPUTE_ERROR_ON(weights->info()->dimension(2) != input->info()->dimension(2)); - ARM_COMPUTE_ERROR_ON(!conv_info.padding_is_symmetric()); - - if(biases != nullptr) + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, weights, output); + ARM_COMPUTE_RETURN_ERROR_ON(weights->dimension(2) != input->dimension(2)); + ARM_COMPUTE_RETURN_ERROR_ON(!conv_info.padding_is_symmetric()); + + bool has_bias = (biases != nullptr); + + if(has_bias) { - ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(biases, 1, DataType::F32); - ARM_COMPUTE_ERROR_ON_MISMATCHING_DATA_TYPES(input, biases); - ARM_COMPUTE_ERROR_ON(biases->info()->dimension(0) != weights->info()->dimension(3)); - ARM_COMPUTE_ERROR_ON(biases->info()->num_dimensions() > 2); + ARM_COMPUTE_RETURN_ERROR_ON(biases->dimension(0) != weights->dimension(3)); + ARM_COMPUTE_RETURN_ERROR_ON(biases->num_dimensions() > 2); } + const unsigned int kernel_width = weights->dimension(0); + const unsigned int kernel_height = weights->dimension(1); + + // Get convolved dimensions + unsigned int conv_w = 0; + unsigned int conv_h = 0; + std::tie(conv_w, conv_h) = scaled_dimensions(input->dimension(0), input->dimension(1), kernel_width, kernel_height, + conv_info); + + ARM_COMPUTE_RETURN_ERROR_ON_MSG((output->dimension(0) != conv_w) || (output->dimension(1) != conv_h), "Output shape does not match the expected one"); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(weights->dimension(4) != (conv_w * conv_h), "Weights shape does not match the expected one"); + + // Calculate intermediate buffer shapes + TensorShape shape_wr; + TensorShape shape_im2col; + TensorShape shape_gemm; + calculate_shapes(input, weights, biases, output, conv_info, shape_wr, shape_im2col, shape_gemm); + + TensorInfo weights_reshaped_info(shape_wr, 1, weights->data_type()); + TensorInfo input_im2col_reshaped_info(shape_im2col, 1, input->data_type()); + TensorInfo gemm_output_info(shape_gemm, 1, input->data_type()); + + ARM_COMPUTE_RETURN_ON_ERROR(NEIm2ColKernel::validate(input, &input_im2col_reshaped_info, Size2D(kernel_width, kernel_height), conv_info, has_bias, false)); + ARM_COMPUTE_RETURN_ON_ERROR(NEWeightsReshapeKernel::validate(weights, biases, &weights_reshaped_info)); + ARM_COMPUTE_RETURN_ON_ERROR(NELocallyConnectedMatrixMultiplyKernel::validate(&input_im2col_reshaped_info, &weights_reshaped_info, &gemm_output_info)); + ARM_COMPUTE_RETURN_ON_ERROR(NECol2ImKernel::validate(&gemm_output_info, output, Size2D(conv_w, conv_h))); + + return Status{}; +} + +void NELocallyConnectedLayer::configure(const ITensor *input, const ITensor *weights, const ITensor *biases, ITensor *output, const PadStrideInfo &conv_info) +{ + ARM_COMPUTE_ERROR_ON_NULLPTR(input, weights, output); + ARM_COMPUTE_ERROR_THROW_ON(NELocallyConnectedLayer::validate(input->info(), weights->info(), biases == nullptr ? nullptr : biases->info(), output->info(), conv_info)); + bool _has_bias = (biases != nullptr); _is_first_run = true; - // Get parameters for conv_info - unsigned int stride_x = 0; - unsigned int stride_y = 0; - unsigned int pad_x = 0; - unsigned int pad_y = 0; - std::tie(stride_x, stride_y) = conv_info.stride(); - std::tie(pad_x, pad_y) = conv_info.pad(); - const unsigned int kernel_width = weights->info()->dimension(0); const unsigned int kernel_height = weights->info()->dimension(1); @@ -76,32 +138,14 @@ void NELocallyConnectedLayer::configure(const ITensor *input, const ITensor *wei std::tie(conv_w, conv_h) = scaled_dimensions(input->info()->dimension(0), input->info()->dimension(1), kernel_width, kernel_height, conv_info); - ARM_COMPUTE_ERROR_ON_MSG((output->info()->dimension(0) != conv_w) || (output->info()->dimension(1) != conv_h), "Output shape does not match the expected one"); - ARM_COMPUTE_ERROR_ON_MSG(weights->info()->dimension(4) != (conv_w * conv_h), "Weights shape does not match the expected one"); - - // Create tensor to store the reshaped weights - const size_t mat_weights_cols = weights->info()->dimension(3); - const size_t mat_weights_rows = weights->info()->dimension(0) * weights->info()->dimension(1) * weights->info()->dimension(2) + ((_has_bias) ? 1 : 0); - const size_t mat_weights_num = weights->info()->dimension(4); - - const TensorShape shape_wr(mat_weights_cols, mat_weights_rows, mat_weights_num); + // Calculate intermediate buffer shapes + TensorShape shape_wr; + TensorShape shape_im2col; + TensorShape shape_gemm; + calculate_shapes(input->info(), weights->info(), biases == nullptr ? nullptr : biases->info(), output->info(), conv_info, shape_wr, shape_im2col, shape_gemm); _weights_reshaped.allocator()->init(TensorInfo(shape_wr, 1, weights->info()->data_type())); - - // Create tensor to store im2col reshaped inputs - const size_t mat_input_cols = mat_weights_rows; - const size_t mat_input_rows = conv_w * conv_h; - TensorShape shape_im2col = input->info()->tensor_shape(); - shape_im2col.set(0, mat_input_cols); - shape_im2col.set(1, mat_input_rows); - shape_im2col.set(2, 1); - _input_im2col_reshaped.allocator()->init(TensorInfo(shape_im2col, 1, input->info()->data_type())); - - // Create locally connected layer output tensor - TensorShape shape_gemm = _input_im2col_reshaped.info()->tensor_shape(); - shape_gemm.set(0, mat_weights_cols); - shape_gemm.set(1, mat_input_rows); _gemm_output.allocator()->init(TensorInfo(shape_gemm, 1, input->info()->data_type())); // Manage intermediate buffers diff --git a/tests/validation/CL/LocallyConnected.cpp b/tests/validation/CL/LocallyConnected.cpp index 05cab29226..de79d60e7f 100644 --- a/tests/validation/CL/LocallyConnected.cpp +++ b/tests/validation/CL/LocallyConnected.cpp @@ -47,6 +47,67 @@ namespace TEST_SUITE(CL) TEST_SUITE(LocallyConnected) +// *INDENT-OFF* +// clang-format off +DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip(zip(zip( + framework::dataset::make("InputInfo", { TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching data type input/weights + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching data type input/bias + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching data type input/output + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching shape input/weights + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching shape input/bias + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching shape input/output + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Asymmetric padding + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0) + }), + framework::dataset::make("WeightsInfo",{ TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F16, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 274U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0) + })), + framework::dataset::make("BiasInfo", { TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F16, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 274U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0) + })), + framework::dataset::make("OutputInfo", { TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F16, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 22U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0) + })), + framework::dataset::make("PadStride", { PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 1, 0, 0, 0, DimensionRoundingType::FLOOR), + PadStrideInfo(2, 1, 0, 0) + })), + framework::dataset::make("Expected", { false, false, false, false, false, false, false, true })), + input_info, weights_info, bias_info, output_info, conv_info, expected) +{ + bool is_valid = bool(CLLocallyConnectedLayer::validate(&input_info.clone()->set_is_resizable(false), + &weights_info.clone()->set_is_resizable(false), + &bias_info.clone()->set_is_resizable(false), + &output_info.clone()->set_is_resizable(false), + conv_info)); + ARM_COMPUTE_EXPECT(is_valid == expected, framework::LogLevel::ERRORS); +} +// clang-format on +// *INDENT-ON* + DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(framework::dataset::concat(datasets::SmallLocallyConnectedDataset(), datasets::LargeLocallyConnectedDataset()), framework::dataset::make("DataType", DataType::F32)), src_shape, weights_shape, bias_shape, dst_shape, info, data_type) diff --git a/tests/validation/NEON/DeconvolutionLayer.cpp b/tests/validation/NEON/DeconvolutionLayer.cpp index 566b75a827..3bb6d6f8fc 100644 --- a/tests/validation/NEON/DeconvolutionLayer.cpp +++ b/tests/validation/NEON/DeconvolutionLayer.cpp @@ -58,6 +58,106 @@ const auto data1x1 = datasets::SmallDeconvolutionShapes() * framework::dataset:: TEST_SUITE(NEON) TEST_SUITE(DeconvolutionLayer) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, (combine(datasets::SmallDeconvolutionShapes(), framework::dataset::make("DataType", DataType::F32))), + input_shape, data_type) +{ + // Create shapes + const unsigned int kernel_size_x = 3; + const unsigned int kernel_size_y = 3; + const unsigned int num_kernels = 1; + const TensorShape weights_shape(kernel_size_x, kernel_size_y, input_shape.z(), num_kernels); + const TensorShape bias_shape(num_kernels); + auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, 1, 1, 0, 0, 1, 1); + TensorShape output_shape = deconvolution_output_shape(out_dim, input_shape, weights_shape); + + // Create tensors + Tensor src = create_tensor(input_shape, data_type, 1); + Tensor weights = create_tensor(weights_shape, data_type, 1); + Tensor bias = create_tensor(bias_shape, data_type, 1); + Tensor dst = create_tensor(output_shape, data_type, 1); + + ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(weights.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(bias.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Create and configure function + NEDeconvolutionLayer deconv; + deconv.configure(&src, &weights, &bias, &dst, PadStrideInfo(1, 1, 1, 1, DimensionRoundingType::CEIL), 0, 0); + + // Validate valid region + const ValidRegion src_valid_region = shape_to_valid_region(input_shape); + const ValidRegion weights_valid_region = shape_to_valid_region(weights_shape); + const ValidRegion bias_valid_region = shape_to_valid_region(bias_shape); + const ValidRegion dst_valid_region = shape_to_valid_region(output_shape); + + validate(src.info()->valid_region(), src_valid_region); + validate(weights.info()->valid_region(), weights_valid_region); + validate(bias.info()->valid_region(), bias_valid_region); + validate(dst.info()->valid_region(), dst_valid_region); +} + +// *INDENT-OFF* +// clang-format off +DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip(zip(zip(zip(zip( + framework::dataset::make("InputInfo", { TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::F32, 0), // Mismatching data type + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::F32, 0), // Invalid weights shape + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::QS8, 4), // Non supported data type + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::F32, 11), // Invalid bias shape + TensorInfo(TensorShape(13U, 11U, 4U, 3U), 1, DataType::F32, 0), // Window shrink + TensorInfo(TensorShape(32U, 16U, 2U), 1, DataType::F32, 0), + }), + framework::dataset::make("WeightsInfo", { TensorInfo(TensorShape(3U, 3U, 2U, 2U), 1, DataType::F16, 0), + TensorInfo(TensorShape(3U, 3U, 2U, 4U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 2U, 2U), 1, DataType::QS8, 5), + TensorInfo(TensorShape(3U, 2U, 2U, 2U), 1, DataType::F32, 11), + TensorInfo(TensorShape(3U, 3U, 4U), 1, DataType::F32, 0), + TensorInfo(TensorShape(1U, 1U, 2U, 4U), 1, DataType::F32, 0), + })), + framework::dataset::make("BiasInfo", { TensorInfo(TensorShape(1U), 1, DataType::F16, 0), + TensorInfo(TensorShape(1U), 1, DataType::F32, 0), + TensorInfo(TensorShape(1U), 1, DataType::F32, 5), + TensorInfo(TensorShape(25U, 11U), 1, DataType::F32, 11), + TensorInfo(TensorShape(1U), 1, DataType::F32, 0), + TensorInfo(TensorShape(4U), 1, DataType::F32, 0), + })), + framework::dataset::make("OutputInfo",{ TensorInfo(TensorShape(25U, 11U, 2U), 1, DataType::F16, 0), + TensorInfo(TensorShape(25U, 10U, 2U), 1, DataType::F32, 0), + TensorInfo(TensorShape(25U, 11U, 2U), 1, DataType::F32, 5), + TensorInfo(TensorShape(13U, 13U, 2U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 9U, 1U, 3U), 1, DataType::F32, 0), + TensorInfo(TensorShape(32U, 16U, 4U), 1, DataType::F32, 0), + })), + framework::dataset::make("PadStrideInfo", { PadStrideInfo(1, 1, 0, 0), + PadStrideInfo(1, 1, 0, 0), + PadStrideInfo(1, 1, 0, 0), + PadStrideInfo(1, 1, 0, 0), + PadStrideInfo(1, 1, 1, 1), + PadStrideInfo(1, 1, 0, 0), + })), + framework::dataset::make("ax", { 1U, + 1U, + 1U, + 1U, + 0U, + 0U, + })), + framework::dataset::make("ay", { 1U, + 1U, + 1U, + 1U, + 0U, + 0U, + })), + framework::dataset::make("Expected", { false, false, false, false, false, true })), + input_info, weights_info, bias_info, output_info, pad_info, ax, ay, expected) +{ + bool is_valid = bool(NEDeconvolutionLayer::validate(&input_info.clone()->set_is_resizable(false), &weights_info.clone()->set_is_resizable(false), &bias_info.clone()->set_is_resizable(false), &output_info.clone()->set_is_resizable(false), pad_info, ax, ay)); + ARM_COMPUTE_EXPECT(is_valid == expected, framework::LogLevel::ERRORS); +} +// clang-format on +// *INDENT-ON* + template using NEDeconvolutionLayerFixture4x4 = DeconvolutionValidationFixture; diff --git a/tests/validation/NEON/LocallyConnected.cpp b/tests/validation/NEON/LocallyConnected.cpp index 56430d9650..b00f27445c 100644 --- a/tests/validation/NEON/LocallyConnected.cpp +++ b/tests/validation/NEON/LocallyConnected.cpp @@ -48,6 +48,67 @@ constexpr RelativeTolerance tolerance_f32(0.0001f); /**< Tolerance value TEST_SUITE(NEON) TEST_SUITE(LocallyConnected) +// *INDENT-OFF* +// clang-format off +DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip(zip(zip( + framework::dataset::make("InputInfo", { TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching data type input/weights + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching data type input/bias + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching data type input/output + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching shape input/weights + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching shape input/bias + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Mismatching shape input/output + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0), // Asymmetric padding + TensorInfo(TensorShape(23U, 27U, 5U), 1, DataType::F32, 0) + }), + framework::dataset::make("WeightsInfo",{ TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F16, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 274U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(3U, 3U, 5U, 21U, 275U), 1, DataType::F32, 0) + })), + framework::dataset::make("BiasInfo", { TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F16, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 274U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0), + TensorInfo(TensorShape(21U, 275U), 1, DataType::F32, 0) + })), + framework::dataset::make("OutputInfo", { TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F16, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 22U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0), + TensorInfo(TensorShape(11U, 25U, 21U), 1, DataType::F32, 0) + })), + framework::dataset::make("PadStride", { PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 0, 0), + PadStrideInfo(2, 1, 1, 0, 0, 0, DimensionRoundingType::FLOOR), + PadStrideInfo(2, 1, 0, 0) + })), + framework::dataset::make("Expected", { false, false, false, false, false, false, false, true })), + input_info, weights_info, bias_info, output_info, conv_info, expected) +{ + bool is_valid = bool(NELocallyConnectedLayer::validate(&input_info.clone()->set_is_resizable(false), + &weights_info.clone()->set_is_resizable(false), + &bias_info.clone()->set_is_resizable(false), + &output_info.clone()->set_is_resizable(false), + conv_info)); + ARM_COMPUTE_EXPECT(is_valid == expected, framework::LogLevel::ERRORS); +} +// clang-format on +// *INDENT-ON* + DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(framework::dataset::concat(datasets::SmallLocallyConnectedDataset(), datasets::LargeLocallyConnectedDataset()), framework::dataset::make("DataType", DataType::F32)), src_shape, weights_shape, bias_shape, dst_shape, info, data_type) -- cgit v1.2.1