blob: e8df420f6717d1a10f4f5aee2182e74c0c1adafb [file] [log] [blame]
Michele Di Giorgio56dd7262017-07-27 09:53:49 +01001/*
Matthew Bentham314d3e22023-06-23 10:53:52 +00002 * Copyright (c) 2017-2021, 2023 Arm Limited.
Michele Di Giorgio56dd7262017-07-27 09:53:49 +01003 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
Georgios Pinitas7891a732021-08-20 21:39:25 +010024#include "src/gpu/cl/kernels/ClQuantizeKernel.h"
Michele Di Giorgio56dd7262017-07-27 09:53:49 +010025
Michele Di Giorgio56dd7262017-07-27 09:53:49 +010026#include "arm_compute/core/CL/CLHelpers.h"
27#include "arm_compute/core/CL/CLKernelLibrary.h"
28#include "arm_compute/core/CL/ICLTensor.h"
Manuel Bottini5a1bf622021-03-01 17:39:36 +000029#include "arm_compute/core/Error.h"
Michele Di Giorgio56dd7262017-07-27 09:53:49 +010030#include "arm_compute/core/TensorInfo.h"
31#include "arm_compute/core/Utils.h"
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010032#include "arm_compute/core/utils/quantization/AsymmHelpers.h"
Matthew Bentham314d3e22023-06-23 10:53:52 +000033#include "arm_compute/core/utils/StringUtils.h"
Michele Di Giorgio56dd7262017-07-27 09:53:49 +010034#include "arm_compute/core/Validate.h"
Georgios Pinitasef516e82021-04-30 14:46:05 +010035
Sang-Hoon Park68dd25f2020-10-19 16:00:11 +010036#include "src/core/CL/CLValidate.h"
37#include "src/core/helpers/WindowHelpers.h"
Manuel Bottini5a1bf622021-03-01 17:39:36 +000038#include "support/Cast.h"
Matthew Bentham758b5ba2020-03-05 23:37:48 +000039#include "support/StringSupport.h"
Michele Di Giorgio56dd7262017-07-27 09:53:49 +010040
Michele Di Giorgiod87a7b22019-09-10 10:42:27 +010041namespace arm_compute
42{
Manuel Bottini5a1bf622021-03-01 17:39:36 +000043namespace opencl
44{
45namespace kernels
46{
Alex Gilday60954c62018-03-05 16:22:48 +000047namespace
48{
Manuel Bottini5a1bf622021-03-01 17:39:36 +000049Status validate_arguments(const ITensorInfo *src, const ITensorInfo *dst)
Alex Gilday60954c62018-03-05 16:22:48 +000050{
Manuel Bottini5a1bf622021-03-01 17:39:36 +000051 ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(src, dst);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010052 ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(src, 1, DataType::QASYMM8, DataType::QASYMM8_SIGNED,
53 DataType::F32, DataType::F16);
Manuel Bottini5a1bf622021-03-01 17:39:36 +000054 ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(src);
Michele Di Giorgiod87a7b22019-09-10 10:42:27 +010055
56 // Output must always be initialized
Manuel Bottini5a1bf622021-03-01 17:39:36 +000057 ARM_COMPUTE_RETURN_ERROR_ON(dst->tensor_shape().total_size() == 0);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010058 ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(dst, 1, DataType::QASYMM8, DataType::QASYMM8_SIGNED,
59 DataType::QASYMM16);
Manuel Bottini5a1bf622021-03-01 17:39:36 +000060 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_SHAPES(src, dst);
Alex Gilday60954c62018-03-05 16:22:48 +000061
62 return Status{};
63}
Alex Gilday60954c62018-03-05 16:22:48 +000064} // namespace
65
Giorgio Arena4a95bba2021-06-28 11:00:27 +010066ClQuantizeKernel::ClQuantizeKernel()
67{
68 _type = CLKernelType::ELEMENTWISE;
69}
70
Georgios Pinitasef516e82021-04-30 14:46:05 +010071void ClQuantizeKernel::configure(const CLCompileContext &compile_context, const ITensorInfo *src, ITensorInfo *dst)
Michele Di Giorgio56dd7262017-07-27 09:53:49 +010072{
Manuel Bottini5a1bf622021-03-01 17:39:36 +000073 ARM_COMPUTE_ERROR_ON_NULLPTR(src, dst);
Manuel Bottini4c6bd512020-04-08 10:15:51 +010074
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010075 auto padding_info = get_padding_info({src, dst});
Sheri Zhang11d73272020-10-28 14:01:55 +000076
Manuel Bottini5a1bf622021-03-01 17:39:36 +000077 ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(src, dst));
Sheri Zhang11d73272020-10-28 14:01:55 +000078
Manuel Bottini5a1bf622021-03-01 17:39:36 +000079 const int vec_size_x = 16 / src->element_size();
80 const int input_width_x = src->tensor_shape().x();
Usama Arife03802e2019-03-11 12:20:20 +000081 const bool multi_access_x = (input_width_x / vec_size_x > 0);
82
Manuel Bottini5a1bf622021-03-01 17:39:36 +000083 const UniformQuantizationInfo qinfo = dst->quantization_info().uniform();
84 const DataType output_data_type = dst->data_type();
Georgios Pinitas4c5469b2019-05-21 13:32:43 +010085
Manuel Bottini2f602212020-01-30 17:30:32 +000086 float scale_to_apply = qinfo.scale;
87 int32_t offset_to_apply = qinfo.offset;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010088 if (is_data_type_quantized_asymmetric(src->data_type()))
Manuel Bottini2f602212020-01-30 17:30:32 +000089 {
90 /*
91 * In case of requantization of a quantized input tensor to an output tensor with another quantization
92 * instead of of apply dequantization and then a quantization functions, we just compute new scale and
93 * offset to apply.
94 *
95 * Assuming:
96 * - q_i as input quantized value
97 * - q_o as output quantized value
98 * - z_i as input quantization offset value
99 * - z_o as output quantization offset value
100 * - s_i as input quantization scale value
101 * - s_o as output quantization scale value
102 * - z_n as new quantization offset value
103 * - s_n as new quantization scale value
104 *
105 * q_o = ( q_i - z_i ) * s_i / s_o + z_o
106 *
107 * We can rewrite the formula as:
108 *
109 * q_o = ( q_i * s_i / s_o ) - z_i * s_i / s_o + z_o
110 *
111 * q_o = q_i / s_n + z_n
112 *
113 * Where:
114 *
115 * s_n = s_o / s_i
116 *
117 * z_n = - z_i * s_i / s_o + z_o
118 *
119 */
Manuel Bottini5a1bf622021-03-01 17:39:36 +0000120 const UniformQuantizationInfo qinfo_in = src->quantization_info().uniform();
Manuel Bottini2f602212020-01-30 17:30:32 +0000121 scale_to_apply /= qinfo_in.scale;
122 // In order to minimize flooring we convert the offset to a float,
123 // then compute the new offset in the float domain,
124 // finally we convert it back as int32_t
125 offset_to_apply -= static_cast<int32_t>(static_cast<float>(qinfo_in.offset) * qinfo_in.scale / qinfo.scale);
126 }
127
Michele Di Giorgio56dd7262017-07-27 09:53:49 +0100128 // Create kernel
Usama Arife03802e2019-03-11 12:20:20 +0000129 CLBuildOptions build_opts;
Manuel Bottini5a1bf622021-03-01 17:39:36 +0000130 build_opts.add_option_if(is_data_type_float(src->data_type()), "-DIS_FLOAT");
Manuel Bottini2f602212020-01-30 17:30:32 +0000131 build_opts.add_option("-DSCALE=" + float_to_string_with_full_precision(scale_to_apply));
132 build_opts.add_option("-DOFFSET=" + support::cpp11::to_string(offset_to_apply));
Usama Arife03802e2019-03-11 12:20:20 +0000133 build_opts.add_option("-DVEC_SIZE=" + support::cpp11::to_string(vec_size_x));
Manuel Bottini5a1bf622021-03-01 17:39:36 +0000134 build_opts.add_option("-DDATA_TYPE_IN=" + get_cl_type_from_data_type(src->data_type()));
Michele Di Giorgiod87a7b22019-09-10 10:42:27 +0100135 build_opts.add_option("-DDATA_TYPE_OUT=" + get_cl_type_from_data_type(output_data_type));
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100136 build_opts.add_option_if(
137 multi_access_x, "-DLAST_ACCESSED_X=" + support::cpp11::to_string(std::max<int>(input_width_x - vec_size_x, 0)));
138 std::pair<int, int> min_max_quant_values =
139 quantization::get_min_max_values_from_quantized_data_type(output_data_type);
Michele Di Giorgiod87a7b22019-09-10 10:42:27 +0100140 build_opts.add_option("-DMIN_QUANT_VAL=" + support::cpp11::to_string(min_max_quant_values.first));
141 build_opts.add_option("-DMAX_QUANT_VAL=" + support::cpp11::to_string(min_max_quant_values.second));
142
Manuel Bottini4c6bd512020-04-08 10:15:51 +0100143 _kernel = create_kernel(compile_context, "quantization_layer", build_opts.options());
Sheri Zhang11d73272020-10-28 14:01:55 +0000144
145 // Configure kernel window
Manuel Bottini5a1bf622021-03-01 17:39:36 +0000146 Window win = calculate_max_window(*src, Steps());
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100147 if (multi_access_x)
Sheri Zhang11d73272020-10-28 14:01:55 +0000148 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100149 win.set(Window::DimX,
150 Window::Dimension(win.x().start(), ceil_to_multiple(win.x().end(), vec_size_x), vec_size_x));
Sheri Zhang11d73272020-10-28 14:01:55 +0000151 }
152 ICLKernel::configure_internal(win);
153
Sheri Zhang11d73272020-10-28 14:01:55 +0000154 ARM_COMPUTE_ERROR_ON(has_padding_changed(padding_info));
Alex Gilday60954c62018-03-05 16:22:48 +0000155}
Michele Di Giorgio56dd7262017-07-27 09:53:49 +0100156
Georgios Pinitasef516e82021-04-30 14:46:05 +0100157Status ClQuantizeKernel::validate(const ITensorInfo *src, const ITensorInfo *dst)
Alex Gilday60954c62018-03-05 16:22:48 +0000158{
Manuel Bottini5a1bf622021-03-01 17:39:36 +0000159 ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(src, dst));
Alex Gilday60954c62018-03-05 16:22:48 +0000160 return Status{};
Michele Di Giorgio56dd7262017-07-27 09:53:49 +0100161}
162
Georgios Pinitasef516e82021-04-30 14:46:05 +0100163void ClQuantizeKernel::run_op(ITensorPack &tensors, const Window &window, cl::CommandQueue &queue)
Michele Di Giorgio56dd7262017-07-27 09:53:49 +0100164{
165 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
166 ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(ICLKernel::window(), window);
167
Manuel Bottini5a1bf622021-03-01 17:39:36 +0000168 auto src = utils::cast::polymorphic_downcast<const ICLTensor *>(tensors.get_const_tensor(TensorType::ACL_SRC));
169 auto dst = utils::cast::polymorphic_downcast<ICLTensor *>(tensors.get_tensor(TensorType::ACL_DST));
170
Michele Di Giorgio56dd7262017-07-27 09:53:49 +0100171 Window window_collapsed = window.collapse_if_possible(ICLKernel::window(), 3);
172 Window slice = window_collapsed.first_slice_window_3D();
173
Michele Di Giorgio56dd7262017-07-27 09:53:49 +0100174 do
175 {
176 unsigned int idx = 0;
Manuel Bottini5a1bf622021-03-01 17:39:36 +0000177 add_3D_tensor_argument(idx, src, slice);
178 add_3D_tensor_argument(idx, dst, slice);
Georgios Pinitas275f99c2019-08-23 12:44:11 +0100179 enqueue(queue, *this, slice, lws_hint());
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100180 } while (window_collapsed.slide_window_slice_3D(slice));
Michele Di Giorgio56dd7262017-07-27 09:53:49 +0100181}
Manuel Bottini5a1bf622021-03-01 17:39:36 +0000182} // namespace kernels
183} // namespace opencl
Michele Di Giorgiod87a7b22019-09-10 10:42:27 +0100184} // namespace arm_compute