From aefa80732d967b97a451fc52d3a7cba4071a80da Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 1 Apr 2019 00:20:05 +0200 Subject: [PATCH] Use nonsquare NN functions. --- src/cmsis/include/arm_nnfunctions.h | 81 ++++++-- .../arm_pool_q7_HWC_nonsquare.c | 180 ++++++++++++++++++ src/omv/nn/nn.c | 117 +++++++++--- src/omv/nn/nn.h | 10 + 4 files changed, 353 insertions(+), 35 deletions(-) create mode 100644 src/cmsis/src/nn/PoolingFunctions/arm_pool_q7_HWC_nonsquare.c diff --git a/src/cmsis/include/arm_nnfunctions.h b/src/cmsis/include/arm_nnfunctions.h index 366ece529..ee16b58e9 100644 --- a/src/cmsis/include/arm_nnfunctions.h +++ b/src/cmsis/include/arm_nnfunctions.h @@ -180,7 +180,7 @@ extern "C" * @param[in] dim_im_out_y output tensor dimension y * @param[in,out] bufferA pointer to buffer space for input * @param[in,out] bufferB pointer to buffer space for output - * @return The function returns ARM_MATH_SUCCESS + * @return The function returns ARM_MATH_SUCCESS */ arm_status arm_convolve_HWC_q7_basic_nonsquare(const q7_t * Im_in, @@ -219,9 +219,9 @@ extern "C" * @param[in] out_shift amount of right-shift for output * @param[in,out] Im_out pointer to output tensor * @param[in] dim_im_out output tensor dimension - * @param[in,out] bufferA pointer to buffer space for input + * @param[in,out] bufferA pointer to buffer space for input * @param[in,out] bufferB pointer to buffer space for output - * @return The function returns ARM_MATH_SUCCESS + * @return The function returns ARM_MATH_SUCCESS * */ @@ -236,9 +236,9 @@ extern "C" const q15_t * bias, const uint16_t bias_shift, const uint16_t out_shift, - q15_t * Im_out, - const uint16_t dim_im_out, - q15_t * bufferA, + q15_t * Im_out, + const uint16_t dim_im_out, + q15_t * bufferA, q7_t * bufferB); /** @@ -256,7 +256,7 @@ extern "C" * @param[in] out_shift amount of right-shift for output * @param[in,out] Im_out pointer to output tensor * @param[in] dim_im_out output tensor dimension - * @param[in,out] bufferA pointer to buffer space for input + * @param[in,out] bufferA pointer to buffer space for input * @param[in,out] bufferB pointer to buffer space for output * @return The function returns either * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. @@ -278,9 +278,9 @@ extern "C" const q7_t * bias, const uint16_t bias_shift, const uint16_t out_shift, - q7_t * Im_out, - const uint16_t dim_im_out, - q15_t * bufferA, + q7_t * Im_out, + const uint16_t dim_im_out, + q15_t * bufferA, q7_t * bufferB); /** @@ -303,7 +303,7 @@ extern "C" * @param[in,out] Im_out pointer to output tensor * @param[in] dim_im_out_x output tensor dimension x * @param[in] dim_im_out_y output tensor dimension y - * @param[in,out] bufferA pointer to buffer space for input + * @param[in,out] bufferA pointer to buffer space for input * @param[in,out] bufferB pointer to buffer space for output * @return The function returns either * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. @@ -355,7 +355,7 @@ extern "C" * @param[in,out] Im_out pointer to output tensor * @param[in] dim_im_out_x output tensor dimension x * @param[in] dim_im_out_y output tensor dimension y - * @param[in,out] bufferA pointer to buffer space for input + * @param[in,out] bufferA pointer to buffer space for input * @param[in,out] bufferB pointer to buffer space for output * @return The function returns either * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. @@ -949,6 +949,35 @@ extern "C" q7_t * bufferA, q7_t * Im_out); + /** + * @brief Q7 max pooling nonsquare function + * @param[in] Im_in pointer to input tensor + * @param[in] dim_im_in_x input tensor dimention x + * @param[in] dim_im_in_x input tensor dimention y + * @param[in] ch_im_in number of input tensor channels + * @param[in] dim_kernel filter kernel size + * @param[in] padding padding sizes + * @param[in] stride convolution stride + * @param[in] dim_im_out_x output tensor dimension x + * @param[in] dim_im_out_y output tensor dimension y + * @param[in,out] bufferA pointer to buffer space for input + * @param[in,out] Im_out pointer to output tensor + * @return none. + * + */ + + void arm_maxpool_q7_HWC_nonsquare(q7_t * Im_in, + const uint16_t dim_im_in_x, + const uint16_t dim_im_in_y, + const uint16_t ch_im_in, + const uint16_t dim_kernel, + const uint16_t padding, + const uint16_t stride, + const uint16_t dim_im_out_x, + const uint16_t dim_im_out_y, + q7_t * bufferA, + q7_t * Im_out); + /** * @brief Q7 average pooling function * @param[in] Im_in pointer to input tensor @@ -974,6 +1003,34 @@ extern "C" q7_t * bufferA, q7_t * Im_out); + /** + * @brief Q7 average pooling nonsquare function + * @param[in] Im_in pointer to input tensor + * @param[in] dim_im_in input tensor dimention + * @param[in] ch_im_in number of input tensor channels + * @param[in] dim_kernel filter kernel size + * @param[in] padding padding sizes + * @param[in] stride convolution stride + * @param[in] dim_im_out output tensor dimension + * @param[in,out] bufferA pointer to buffer space for input + * @param[in,out] Im_out pointer to output tensor + * @return none. + * + */ + + void arm_avepool_q7_HWC_nonsquare(q7_t * Im_in, + const uint16_t dim_im_in_x, + const uint16_t dim_im_in_y, + const uint16_t ch_im_in, + const uint16_t dim_kernel, + const uint16_t padding, + const uint16_t stride, + const uint16_t dim_im_out_x, + const uint16_t dim_im_out_y, + q7_t * bufferA, + q7_t * Im_out); + + /** * @defgroup Softmax Softmax Functions * diff --git a/src/cmsis/src/nn/PoolingFunctions/arm_pool_q7_HWC_nonsquare.c b/src/cmsis/src/nn/PoolingFunctions/arm_pool_q7_HWC_nonsquare.c new file mode 100644 index 000000000..1f9dcf7ba --- /dev/null +++ b/src/cmsis/src/nn/PoolingFunctions/arm_pool_q7_HWC_nonsquare.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2010-2018 Arm Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 + * + * 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. + */ + +/* ---------------------------------------------------------------------- + * Project: CMSIS NN Library + * Title: arm_pool_q7_HWC.c + * Description: Pooling function implementations + * + * $Date: 17. January 2018 + * $Revision: V.1.0.0 + * + * Target Processor: Cortex-M cores + * + * -------------------------------------------------------------------- */ + +#include "arm_math.h" +#include "arm_nnfunctions.h" + +/** + * @ingroup groupNN + */ + +/** + * @addtogroup Pooling + * @{ + */ + + /** + * @brief Q7 max pooling function + * @param[in, out] Im_in pointer to input tensor + * @param[in] dim_im_in input tensor dimention + * @param[in] ch_im_in number of input tensor channels + * @param[in] dim_kernel filter kernel size + * @param[in] padding padding sizes + * @param[in] stride convolution stride + * @param[in] dim_im_out output tensor dimension + * @param[in,out] bufferA pointer to buffer space for input + * @param[in,out] Im_out pointer to output tensor + * @return none. + * + * @details + * + * Buffer size: + * + * bufferA size: 0 + * + * The pooling function is implemented as split x-pooling then + * y-pooling. + * + * This pooling function is input-destructive. Input data is undefined + * after calling this function. + * + */ + +void +arm_maxpool_q7_HWC_nonsquare(q7_t * Im_in, + const uint16_t dim_im_in_x, + const uint16_t dim_im_in_y, + const uint16_t ch_im_in, + const uint16_t dim_kernel, + const uint16_t padding, + const uint16_t stride, + const uint16_t dim_im_out_x, + const uint16_t dim_im_out_y, + q7_t * bufferA, q7_t * Im_out) +{ + /* Run the following code as reference implementation for Cortex-M0 and Cortex-M3 */ + + int16_t i_ch_in, i_x, i_y; + int16_t k_x, k_y; + + for (i_ch_in = 0; i_ch_in < ch_im_in; i_ch_in++) + { + for (i_y = 0; i_y < dim_im_out_y; i_y++) + { + for (i_x = 0; i_x < dim_im_out_x; i_x++) + { + int max = -129; + for (k_y = i_y * stride - padding; k_y < i_y * stride - padding + dim_kernel; k_y++) + { + for (k_x = i_x * stride - padding; k_x < i_x * stride - padding + dim_kernel; k_x++) + { + if (k_y >= 0 && k_x >= 0 && k_y < dim_im_in_y && k_x < dim_im_in_x) + { + if (Im_in[i_ch_in + ch_im_in * (k_x + k_y * dim_im_in_x)] > max) + { + max = Im_in[i_ch_in + ch_im_in * (k_x + k_y * dim_im_in_x)]; + } + } + } + } + Im_out[i_ch_in + ch_im_in * (i_x + i_y * dim_im_out_x)] = max; + } + } + } +} + + /** + * @brief Q7 average pooling function + * @param[in,out] Im_in pointer to input tensor + * @param[in] dim_im_in input tensor dimention + * @param[in] ch_im_in number of input tensor channels + * @param[in] dim_kernel filter kernel size + * @param[in] padding padding sizes + * @param[in] stride convolution stride + * @param[in] dim_im_out output tensor dimension + * @param[in,out] bufferA pointer to buffer space for input + * @param[in,out] Im_out pointer to output tensor + * @return none. + * + * @details + * + * Buffer size: + * + * bufferA size: 2*dim_im_out*ch_im_in + * + * The pooling function is implemented as split x-pooling then + * y-pooling. + * + * This pooling function is input-destructive. Input data is undefined + * after calling this function. + * + */ + +void +arm_avepool_q7_HWC_nonsquare(q7_t * Im_in, + const uint16_t dim_im_in_x, + const uint16_t dim_im_in_y, + const uint16_t ch_im_in, + const uint16_t dim_kernel, + const uint16_t padding, + const uint16_t stride, + const uint16_t dim_im_out_x, + const uint16_t dim_im_out_y, + q7_t * bufferA, q7_t * Im_out) +{ + /* Run the following code as reference implementation for Cortex-M0 and Cortex-M3 */ + + int16_t i_ch_in, i_x, i_y; + int16_t k_x, k_y; + + for (i_ch_in = 0; i_ch_in < ch_im_in; i_ch_in++) + { + for (i_y = 0; i_y < dim_im_out_y; i_y++) + { + for (i_x = 0; i_x < dim_im_out_x; i_x++) + { + int sum = 0; + int count = 0; + for (k_y = i_y * stride - padding; k_y < i_y * stride - padding + dim_kernel; k_y++) + { + for (k_x = i_x * stride - padding; k_x < i_x * stride - padding + dim_kernel; k_x++) + { + if (k_y >= 0 && k_x >= 0 && k_y < dim_im_in_y && k_x < dim_im_in_x) + { + sum += Im_in[i_ch_in + ch_im_in * (k_x + k_y * dim_im_in_x)]; + count++; + } + } + } + Im_out[i_ch_in + ch_im_in * (i_x + i_y * dim_im_out_x)] = sum / count; + } + } + } +} diff --git a/src/omv/nn/nn.c b/src/omv/nn/nn.c index 7077bd98c..634413300 100644 --- a/src/omv/nn/nn.c +++ b/src/omv/nn/nn.c @@ -381,19 +381,34 @@ int nn_run_network(nn_t *net, image_t *img, rectangle_t *roi, bool softmax) case LAYER_TYPE_CONV: { conv_func_t conv_func = NULL; + conv_func_nonsquare_t conv_func_nonsquare = NULL; conv_layer_t *conv_layer = (conv_layer_t *) layer; if (prev_layer->c % 4 != 0 || conv_layer->n % 2 != 0 || prev_layer->h % 2 != 0) { - conv_func = arm_convolve_HWC_q7_basic; if (prev_layer->c == 3) { conv_func = arm_convolve_HWC_q7_RGB; + } else if (prev_layer->w == prev_layer->h) { + conv_func = arm_convolve_HWC_q7_basic; + } else { + conv_func_nonsquare = arm_convolve_HWC_q7_basic_nonsquare; } } else { - conv_func = arm_convolve_HWC_q7_fast; + if (prev_layer->w == prev_layer->h) { + conv_func = arm_convolve_HWC_q7_fast; + } else { + conv_func_nonsquare = arm_convolve_HWC_q7_fast_nonsquare; + } + } + if (conv_func) { + conv_func(input_buffer, prev_layer->h, prev_layer->c, conv_layer->wt, conv_layer->c, + conv_layer->krn_dim, conv_layer->krn_pad, conv_layer->krn_str, conv_layer->bias, + conv_layer->l_shift, conv_layer->r_shift, output_buffer, conv_layer->h, (q15_t*)col_buffer, NULL); + } else { + conv_func_nonsquare(input_buffer, prev_layer->w, prev_layer->h, prev_layer->c, conv_layer->wt, conv_layer->c, + conv_layer->krn_dim, conv_layer->krn_dim, conv_layer->krn_pad, conv_layer->krn_pad, conv_layer->krn_str, + conv_layer->krn_str, conv_layer->bias, conv_layer->l_shift, conv_layer->r_shift, output_buffer, + conv_layer->w, conv_layer->h, (q15_t*)col_buffer, NULL); } - conv_func(input_buffer, prev_layer->h, prev_layer->c, conv_layer->wt, conv_layer->c, - conv_layer->krn_dim, conv_layer->krn_pad, conv_layer->krn_str, conv_layer->bias, - conv_layer->l_shift, conv_layer->r_shift, output_buffer, conv_layer->h, (q15_t*)col_buffer, NULL); break; } @@ -405,14 +420,28 @@ int nn_run_network(nn_t *net, image_t *img, rectangle_t *roi, bool softmax) case LAYER_TYPE_POOL: { pool_func_t pool_func = NULL; + pool_func_nonsquare_t pool_func_nonsquare = NULL; pool_layer_t *pool_layer = (pool_layer_t *) layer; if (pool_layer->ptype == POOL_TYPE_MAX) { - pool_func = arm_maxpool_q7_HWC; + if (prev_layer->w == prev_layer->h) { + pool_func = arm_maxpool_q7_HWC; + } else { + pool_func_nonsquare = arm_maxpool_q7_HWC_nonsquare; + } } else { - pool_func = arm_avepool_q7_HWC; + if (prev_layer->w == prev_layer->h) { + pool_func = arm_avepool_q7_HWC; + } else { + pool_func_nonsquare = arm_avepool_q7_HWC_nonsquare; + } + } + if (pool_func) { + pool_func(input_buffer, prev_layer->h, prev_layer->c, pool_layer->krn_dim, + pool_layer->krn_pad, pool_layer->krn_str, layer->w, col_buffer, output_buffer); + } else { + pool_func_nonsquare(input_buffer, prev_layer->w, prev_layer->h, prev_layer->c, pool_layer->krn_dim, + pool_layer->krn_pad, pool_layer->krn_str, layer->w, layer->h, col_buffer, output_buffer); } - pool_func(input_buffer, prev_layer->h, prev_layer->c, pool_layer->krn_dim, - pool_layer->krn_pad, pool_layer->krn_str, layer->w, col_buffer, output_buffer); break; } @@ -468,6 +497,13 @@ int nn_run_network(nn_t *net, image_t *img, rectangle_t *roi, bool softmax) #define POOL_FUNC_2STR(pool_func)\ (pool_func == arm_maxpool_q7_HWC) ? "arm_maxpool_q7_HWC" : "arm_avepool_q7_HWC" +#define CONV_FUNC_NONSQ_2STR(conv_func)\ + (conv_func == arm_convolve_HWC_q7_basic_nonsquare) ? "arm_convolve_HWC_q7_basic_nonsquare":\ + "arm_convolve_HWC_q7_fast_nonsquare" + +#define POOL_FUNC_NONSQ_2STR(pool_func)\ + (pool_func == arm_maxpool_q7_HWC_nonsquare) ? "arm_maxpool_q7_HWC_nonsquare" : "arm_avepool_q7_HWC_nonsquare" + int nn_dry_run_network(nn_t *net, image_t *img, bool softmax) { uint32_t layer_idx = 0; @@ -505,22 +541,41 @@ int nn_dry_run_network(nn_t *net, image_t *img, bool softmax) case LAYER_TYPE_CONV: { conv_func_t conv_func = NULL; + conv_func_nonsquare_t conv_func_nonsquare = NULL; conv_layer_t *conv_layer = (conv_layer_t *) layer; if (prev_layer->c % 4 != 0 || conv_layer->n % 2 != 0 || prev_layer->h % 2 != 0) { - conv_func = arm_convolve_HWC_q7_basic; if (prev_layer->c == 3) { conv_func = arm_convolve_HWC_q7_RGB; + } else if (prev_layer->w == prev_layer->h) { + conv_func = arm_convolve_HWC_q7_basic; + } else { + conv_func_nonsquare = arm_convolve_HWC_q7_basic_nonsquare; } } else { - conv_func = arm_convolve_HWC_q7_fast; + if (prev_layer->w == prev_layer->h) { + conv_func = arm_convolve_HWC_q7_fast; + } else { + conv_func_nonsquare = arm_convolve_HWC_q7_fast_nonsquare; + } + } + + if (conv_func) { + printf("forward: %s(%s, %lu, %lu, %s, %lu, %lu, %lu, %lu, %s, %lu, %lu, %s, %lu, %s, %p);\n", + CONV_FUNC_2STR(conv_func), BUFFER_2STR(input_buffer), + prev_layer->h, prev_layer->c, "conv_wt", conv_layer->c, + conv_layer->krn_dim, conv_layer->krn_pad, conv_layer->krn_str, + "conv_bias", conv_layer->l_shift, conv_layer->r_shift, + BUFFER_2STR(output_buffer), conv_layer->h, "col_buffer", NULL); + } else { + printf("forward: %s(%s, %lu, %lu, %lu, %s, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %s, %lu, %lu, \ + %s, %lu, %lu, %s, %p);\n", + CONV_FUNC_NONSQ_2STR(conv_func_nonsquare), BUFFER_2STR(input_buffer), + prev_layer->w, prev_layer->h, prev_layer->c, "conv_wt", conv_layer->c, + conv_layer->krn_dim, conv_layer->krn_dim, conv_layer->krn_pad, conv_layer->krn_pad, + conv_layer->krn_str, conv_layer->krn_str, "conv_bias", conv_layer->l_shift, conv_layer->r_shift, + BUFFER_2STR(output_buffer), conv_layer->w, conv_layer->h, "col_buffer", NULL); } - printf("forward: %s(%s, %lu, %lu, %s, %lu, %lu, %lu, %lu, %s, %lu, %lu, %s, %lu, %s, %p);\n", - CONV_FUNC_2STR(conv_func), BUFFER_2STR(input_buffer), - prev_layer->h, prev_layer->c, "conv_wt", conv_layer->c, - conv_layer->krn_dim, conv_layer->krn_pad, conv_layer->krn_str, - "conv_bias", conv_layer->l_shift, conv_layer->r_shift, - BUFFER_2STR(output_buffer), conv_layer->h, "col_buffer", NULL); break; } @@ -533,16 +588,32 @@ int nn_dry_run_network(nn_t *net, image_t *img, bool softmax) case LAYER_TYPE_POOL: { pool_func_t pool_func = NULL; + pool_func_nonsquare_t pool_func_nonsquare = NULL; pool_layer_t *pool_layer = (pool_layer_t *) layer; if (pool_layer->ptype == POOL_TYPE_MAX) { - pool_func = arm_maxpool_q7_HWC; + if (prev_layer->w == prev_layer->h) { + pool_func = arm_maxpool_q7_HWC; + } else { + pool_func_nonsquare = arm_maxpool_q7_HWC_nonsquare; + } } else { - pool_func = arm_avepool_q7_HWC; + if (prev_layer->w == prev_layer->h) { + pool_func = arm_avepool_q7_HWC; + } else { + pool_func_nonsquare = arm_avepool_q7_HWC_nonsquare; + } + } + if (pool_func) { + printf("forward: %s(%s, %lu, %lu, %lu, %lu, %lu, %lu, %s, %s);\n", + POOL_FUNC_2STR(pool_func), BUFFER_2STR(input_buffer), + prev_layer->h, prev_layer->c, pool_layer->krn_dim, + pool_layer->krn_pad, pool_layer->krn_str, layer->w, "col_buffer", BUFFER_2STR(output_buffer)); + } else { + printf("forward: %s(%s, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %s, %s);\n", + POOL_FUNC_NONSQ_2STR(pool_func_nonsquare), BUFFER_2STR(input_buffer), + prev_layer->w, prev_layer->h, prev_layer->c, pool_layer->krn_dim, + pool_layer->krn_pad, pool_layer->krn_str, layer->w, layer->h, "col_buffer", BUFFER_2STR(output_buffer)); } - printf("forward: %s(%s, %lu, %lu, %lu, %lu, %lu, %lu, %s, %s);\n", - POOL_FUNC_2STR(pool_func), BUFFER_2STR(input_buffer), - prev_layer->h, prev_layer->c, pool_layer->krn_dim, - pool_layer->krn_pad, pool_layer->krn_str, layer->w, "col_buffer", BUFFER_2STR(output_buffer)); break; } diff --git a/src/omv/nn/nn.h b/src/omv/nn/nn.h index bfc852ac0..fb1becb7c 100644 --- a/src/omv/nn/nn.h +++ b/src/omv/nn/nn.h @@ -93,10 +93,20 @@ typedef arm_status (*conv_func_t) (const q7_t * Im_in, const uint16_t dim_im_in, const uint16_t stride, const q7_t * bias, const uint16_t bias_shift, const uint16_t out_shift, q7_t * Im_out, const uint16_t dim_im_out, q15_t * bufferA, q7_t * bufferB); +typedef arm_status (*conv_func_nonsquare_t) (const q7_t * Im_in, const uint16_t dim_im_in_x, const uint16_t dim_im_in_y, + const uint16_t ch_im_in, const q7_t * wt, const uint16_t ch_im_out, const uint16_t dim_kernel_x, const uint16_t dim_kernel_y, + const uint16_t padding_x, const uint16_t padding_y, const uint16_t stride_x, const uint16_t stride_y, const q7_t * bias, + const uint16_t bias_shift, const uint16_t out_shift, q7_t * Im_out, const uint16_t dim_im_out_x, const uint16_t dim_im_out_y, + q15_t * bufferA, q7_t * bufferB); + typedef void (*pool_func_t)(q7_t * Im_in, const uint16_t dim_im_in, const uint16_t ch_im_in, const uint16_t dim_kernel, const uint16_t padding, const uint16_t stride, const uint16_t dim_im_out, q7_t * bufferA, q7_t * Im_out); +typedef void (*pool_func_nonsquare_t)(q7_t * Im_in, const uint16_t dim_im_in_x, const uint16_t dim_im_in_y, const uint16_t ch_im_in, + const uint16_t dim_kernel, const uint16_t padding, const uint16_t stride, + const uint16_t dim_im_out_x, const uint16_t dim_im_out_y, q7_t * bufferA, q7_t * Im_out); + int nn_dump_network(nn_t *net); int nn_load_network(nn_t *net, const char *path);