| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- /*
- * Copyright (C) 2010-2020 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_softmax_q7.c
- * Description: Q7 softmax function
- *
- * $Date: February 27, 2020
- * $Revision: V.1.0.1
- *
- * Target Processor: Cortex-M cores
- *
- * -------------------------------------------------------------------- */
- #include "arm_math.h"
- #include "arm_nnfunctions.h"
- /**
- * @ingroup groupNN
- */
- /**
- * @addtogroup Softmax
- * @{
- */
- /**
- * @brief Q7 softmax function
- * @param[in] vec_in pointer to input vector
- * @param[in] dim_vec input vector dimention
- * @param[out] p_out pointer to output vector
- *
- * @details
- *
- * Here, instead of typical natural logarithm e based softmax, we use
- * 2-based softmax here, i.e.,:
- *
- * y_i = 2^(x_i) / sum(2^x_j)
- *
- * The relative output will be different here.
- * But mathematically, the gradient will be the same
- * with a log(2) scaling factor.
- *
- * If we compare the position of the max value in output of this
- * function with a reference float32 softmax (and thus using exp)
- * we see that the position of the max value is sometimes different.
- *
- * If we do statistics on lot of input vectors we can compute
- * an average error rate in percent. It is the percent of time
- * that the max will be at a position different from the one
- * computed with a reference float32 implementation.
- *
- * This average error rate is dependent on the vector size.
- * We have:
- *
- * Average error rate in percent = -0.555548 + 0.246918 dim_vec
- * Variance of the error rate = -0.0112281 + 0.0382476 dim_vec
- *
- *
- */
- #define Q7BITS 8
- #define LOG2Q7BITS 3
- void arm_softmax_q7(const q7_t * vec_in, const uint16_t dim_vec, q7_t * p_out )
- {
- #if defined (ARM_MATH_DSP)
- q31_t sum;
- int16_t i;
- uint8_t shift;
- q15_t base;
- uint16_t blkCnt;
- q31_t in,in1,in2;
- q31_t out1, out2;
- q31_t baseV;
- q31_t shiftV;
- const q31_t pad=0x0d0d0d0d;
- const q7_t *pIn=vec_in;
- base = -128;
- /* We first search for the maximum */
- for (i = 0; i < dim_vec; i++)
- {
- if (vec_in[i] > base)
- {
- base = vec_in[i];
- }
- }
- /*
- * So the base is set to max-8, meaning
- * that we ignore really small values.
- * anyway, they will be 0 after shrinking to q7_t.
- */
- base = base - Q7BITS;
- baseV = ((base & 0x0FF) << 24) | ((base & 0x0FF) << 16) | ((base & 0x0FF) << 8) | ((base & 0x0FF));
- sum = 0;
- blkCnt = dim_vec >> 2;
- while(blkCnt)
- {
- in=arm_nn_read_q7x4_ia(&pIn);
- in=__SSUB8(in,baseV);
- in1 = __SXTB16(__ROR(in, 8));
- /* extend remaining two q7_t values to q15_t values */
- in2 = __SXTB16(in);
- #ifndef ARM_MATH_BIG_ENDIAN
- out2 = __PKHTB(in1, in2, 16);
- out1 = __PKHBT(in2, in1, 16);
- #else
- out1 = __PKHTB(in1, in2, 16);
- out2 = __PKHBT(in2, in1, 16);
- #endif
- shiftV = __USAT16(out1,LOG2Q7BITS);
- sum += 0x1 << (shiftV & 0x0FF);
- sum += 0x1 << ((shiftV >> 16) & 0x0FF);
- shiftV = __USAT16(out2,LOG2Q7BITS);
- sum += 0x1 << (shiftV & 0x0FF);
- sum += 0x1 << ((shiftV >> 16) & 0x0FF);
- blkCnt--;
- }
- blkCnt = dim_vec & 3;
- while(blkCnt)
- {
- shift = (uint8_t)__USAT(*pIn++ - base, LOG2Q7BITS);
- sum += 0x1 << shift;
- blkCnt--;
- }
- /* This is effectively (0x1 << 20) / sum */
- int output_base = (1 << 20) / sum;
- pIn=vec_in;
- blkCnt = dim_vec >> 2;
- while(blkCnt)
- {
- /* Here minimum value of 13+base-vec_in[i] will be 5 */
- in=arm_nn_read_q7x4_ia(&pIn);
- in=__SSUB8(pad,in);
- in=__SADD8(in,baseV);
- in1 = __SXTB16(__ROR(in, 8));
- /* extend remaining two q7_t values to q15_t values */
- in2 = __SXTB16(in);
- #ifndef ARM_MATH_BIG_ENDIAN
- out2 = __PKHTB(in1, in2, 16);
- out1 = __PKHBT(in2, in1, 16);
- #else
- out1 = __PKHTB(in1, in2, 16);
- out2 = __PKHBT(in2, in1, 16);
- #endif
- shiftV = __USAT16(out1,5);
- *p_out++ = (q7_t) __SSAT((output_base >> (shiftV & 0x0FF)), 8);
- *p_out++ = (q7_t) __SSAT((output_base >> ((shiftV >> 16) & 0x0FF)), 8);
- shiftV = __USAT16(out2,5);
- *p_out++ = (q7_t) __SSAT((output_base >> (shiftV & 0x0FF)), 8);
- *p_out++ = (q7_t) __SSAT((output_base >> ((shiftV >> 16) & 0x0FF)), 8);
- blkCnt --;
- }
- blkCnt = dim_vec & 3;
- while(blkCnt)
- {
- /* Here minimum value of 13+base-vec_in[i] will be 5 */
- shift = (uint8_t)__USAT(13 + base - *pIn++, 5);
- *p_out++ = (q7_t) __SSAT((output_base >> shift), 8);
- blkCnt --;
- }
- #else
- q31_t sum;
- int16_t i;
- uint8_t shift;
- q15_t base;
- base = -128;
- /* We first search for the maximum */
- for (i = 0; i < dim_vec; i++)
- {
- if (vec_in[i] > base)
- {
- base = vec_in[i];
- }
- }
- /*
- * So the base is set to max-8, meaning
- * that we ignore really small values.
- * anyway, they will be 0 after shrinking to q7_t.
- */
- base = base - Q7BITS;
- sum = 0;
- for (i = 0; i < dim_vec; i++)
- {
- shift = (uint8_t)__USAT(vec_in[i] - base, LOG2Q7BITS);
- sum += 0x1 << shift;
- }
- /* This is effectively (0x1 << 20) / sum */
- int output_base = (1 << 20) / sum;
- for (i = 0; i < dim_vec; i++)
- {
- /* Here minimum value of 13+base-vec_in[i] will be 5 */
- shift = (uint8_t)__USAT(13 + base - vec_in[i], 5);
- p_out[i] = (q7_t) __SSAT((output_base >> shift), 8);
- }
- #endif
- }
- /**
- * @} end of Softmax group
- */
|