From 96209c73b071bb65d4919fb441076f977095a31b Mon Sep 17 00:00:00 2001 From: SiCong Li Date: Fri, 21 Aug 2020 12:28:30 +0100 Subject: COMPMID-3694 COMPMID-3695 COMPMID-3458: Softmax Axis * Properly support "axis" in CL and NEON (and GC) SoftmaxLayer and LogSoftmaxLayer in accord with mainstream frameworks. Axis now defines the dimension on which softmax is performed, and supports the range [-rank, rank) * Extend validation tests to include valid and invalid axes * Remove unnecessary LogSoftmaxLayer fixture, as it is only a specialisation of the SoftmaxLayer fixture * Change the validation fill value range from [-1000, 1000] to [-10, 10], as the former often results in sparse outputs with a single one and zeros elsewhere Change-Id: I8a0040453182b04ed88260de3ba434e98258d863 Signed-off-by: Manuel Bottini Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/3830 Tested-by: Arm Jenkins Comments-Addressed: Arm Jenkins Reviewed-by: Michele Di Giorgio Reviewed-by: Gian Marco Iodice --- tests/validation/reference/SoftmaxLayer.cpp | 89 ++++++++++++++++------------- 1 file changed, 48 insertions(+), 41 deletions(-) (limited to 'tests/validation/reference/SoftmaxLayer.cpp') diff --git a/tests/validation/reference/SoftmaxLayer.cpp b/tests/validation/reference/SoftmaxLayer.cpp index 00206766f8..3fbac32a9b 100644 --- a/tests/validation/reference/SoftmaxLayer.cpp +++ b/tests/validation/reference/SoftmaxLayer.cpp @@ -25,6 +25,7 @@ #include "arm_compute/core/Helpers.h" #include "arm_compute/core/Types.h" +#include "utils/TypePrinter.h" namespace arm_compute { @@ -35,39 +36,43 @@ namespace validation namespace reference { template ::value, int>::type> -SimpleTensor softmax_layer_generic(const SimpleTensor &src, float beta, int32_t reduce_end_axis, bool is_log) +SimpleTensor softmax_layer_generic(const SimpleTensor &src, float beta, int32_t axis, bool is_log) { // Create reference SimpleTensor dst{ src.shape(), src.data_type(), 1 }; - // Convert reduce-before axis (inclusive) to first n axes to reduce - const size_t first_n_reduce_axes = dim_index_2_num_dims(reduce_end_axis, static_cast(src.shape().num_dimensions())); + const int32_t n_dims = static_cast(src.shape().num_dimensions()); + ARM_COMPUTE_ERROR_ON(axis < -n_dims || axis >= n_dims); - // Compute reference. Lower dims are the collapsing of the first axis - // dimensions (i.e., the flattened dimension of each batch). The upper dims are - // instead the batches we want to normalize + const unsigned int actual_axis = static_cast(wrap_around(axis, n_dims)); + Window window; + window.use_tensor_dimensions(src.shape()); + const unsigned int axis_dimension = src.shape()[actual_axis]; + window.set(actual_axis, Window::Dimension(0, 1, 1)); - const int lower_dims = src.shape().total_size_lower(first_n_reduce_axes); - - const int upper_dims = src.shape().total_size_upper(first_n_reduce_axes); - -#if defined(_OPENMP) - #pragma omp parallel for -#endif /* _OPENMP */ - for(int r = 0; r < upper_dims; ++r) + execute_window_loop(window, [&](const Coordinates & id) { - const T *src_row_ptr = src.data() + r * lower_dims; - T *dst_row_ptr = dst.data() + r * lower_dims; - - // Find max - const T max = *std::max_element(src_row_ptr, src_row_ptr + lower_dims); + // Find max along axis + Coordinates offset(id); + offset.set(actual_axis, 0); + T max = *reinterpret_cast(src(offset)); + for(unsigned int axis_id = 1; axis_id < axis_dimension; ++axis_id) + { + offset.set(actual_axis, axis_id); + const T val = *reinterpret_cast(src(offset)); + if(val > max) + { + max = val; + } + } // Regularize T sum(0.f); - std::transform(src_row_ptr, src_row_ptr + lower_dims, dst_row_ptr, [&sum, max, beta, is_log](T val) + for(unsigned int axis_id = 0; axis_id < axis_dimension; ++axis_id) { - T res{ (val - max) *beta }; - + offset.set(actual_axis, axis_id); + const T val = *reinterpret_cast(src(offset)); + T res{ (val - max) *beta }; if(is_log) { sum += std::exp(res); @@ -77,50 +82,52 @@ SimpleTensor softmax_layer_generic(const SimpleTensor &src, float beta, in res = std::exp(res); sum += res; } - return res; - }); + *reinterpret_cast(dst(offset)) = res; + } // Normalize - std::transform(dst_row_ptr, dst_row_ptr + lower_dims, dst_row_ptr, [sum, is_log](T val) + for(unsigned int axis_id = 0; axis_id < axis_dimension; ++axis_id) { + offset.set(actual_axis, axis_id); + const T val = *reinterpret_cast(dst(offset)); if(is_log) { - return val - static_cast(std::log(sum)); + *reinterpret_cast(dst(offset)) = val - static_cast(std::log(sum)); } else { - return val / sum; + *reinterpret_cast(dst(offset)) = val / sum; } - }); - } - + } + }); return dst; } -template SimpleTensor softmax_layer_generic(const SimpleTensor &src, float beta, int32_t reduce_end_axis, bool is_log); -template SimpleTensor softmax_layer_generic(const SimpleTensor &src, float beta, int32_t reduce_end_axis, bool is_log); +template SimpleTensor softmax_layer_generic(const SimpleTensor &src, float beta, int32_t axis, bool is_log); +template SimpleTensor softmax_layer_generic(const SimpleTensor &src, float beta, int32_t axis, bool is_log); template ::value, int>::type> -SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t reduce_end_axis) +SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t axis, bool is_log) { - return softmax_layer_generic(src, beta, reduce_end_axis, false); + return softmax_layer_generic(src, beta, axis, is_log); } template < typename T, typename std::enable_if < std::is_same::value || std::is_same::value, int >::type > -SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t reduce_end_axis) +SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t axis, bool is_log) { - const QuantizationInfo output_quantization_info = arm_compute::get_softmax_output_quantization_info(src.data_type(), false); + const QuantizationInfo output_quantization_info = arm_compute::get_softmax_output_quantization_info(src.data_type(), is_log); SimpleTensor src_tmp = convert_from_asymmetric(src); - SimpleTensor dst_tmp = softmax_layer(src_tmp, beta, reduce_end_axis); + SimpleTensor dst_tmp = softmax_layer(src_tmp, beta, axis, is_log); SimpleTensor dst = convert_to_asymmetric(dst_tmp, output_quantization_info); return dst; } -template SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t reduce_end_axis); -template SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t reduce_end_axis); -template SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t reduce_end_axis); -template SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t reduce_end_axis); +template SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t axis, bool is_log); +template SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t axis, bool is_log); +template SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t axis, bool is_log); +template SimpleTensor softmax_layer(const SimpleTensor &src, float beta, int32_t axis, bool is_log); + } // namespace reference } // namespace validation } // namespace test -- cgit v1.2.1