Compute Library
 20.02.1
FullyConnectedLayer.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-2020 ARM Limited.
3  *
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  */
24 #include "FullyConnectedLayer.h"
25 
26 #include "arm_compute/core/Types.h"
28 
30 
31 #include <numeric>
32 
33 namespace arm_compute
34 {
35 namespace test
36 {
37 namespace validation
38 {
39 namespace reference
40 {
41 namespace
42 {
43 // Vector matrix multiply for floating point
44 template < typename T, typename TB, typename std::enable_if < is_floating_point<T>::value &&is_floating_point<TB>::value, int >::type = 0 >
45 void vector_matrix_multiply(const SimpleTensor<T> &src, const SimpleTensor<T> &weights, const SimpleTensor<TB> &bias, SimpleTensor<T> &dst, int offset_src, int offset_dst, int cols_weights,
46  int rows_weights)
47 {
48  const T *src_ptr = src.data() + offset_src;
49  const T *weights_ptr = weights.data();
50  const TB *bias_ptr = bias.data();
51  T *dst_ptr = dst.data() + offset_dst;
52 
53  for(int y = 0; y < rows_weights; ++y)
54  {
55  dst_ptr[y] = std::inner_product(src_ptr, src_ptr + cols_weights, weights_ptr, static_cast<T>(0)) + bias_ptr[y];
56  weights_ptr += cols_weights;
57  }
58 }
59 
60 // Vector matrix multiply for quantized type
61 template < typename T, typename TB, typename std::enable_if < (std::is_same<T, uint8_t>::value || std::is_same<T, int8_t>::value) &&std::is_same<TB, int32_t>::value, int >::type = 0 >
62 void vector_matrix_multiply(const SimpleTensor<T> &src, const SimpleTensor<T> &weights, const SimpleTensor<TB> &bias, SimpleTensor<T> &dst, int offset_src, int offset_dst,
63  int cols_weights, int rows_weights)
64 {
65  const T *src_ptr = src.data() + offset_src;
66  const T *weights_ptr = weights.data();
67  const TB *bias_ptr = bias.data();
68  T *dst_ptr = dst.data() + offset_dst;
69 
70  const UniformQuantizationInfo iq_info = src.quantization_info().uniform();
71  const UniformQuantizationInfo wq_info = weights.quantization_info().uniform();
72  const UniformQuantizationInfo oq_info = dst.quantization_info().uniform();
73 
74  const int input_offset = -iq_info.offset;
75  const float input_scale = iq_info.scale;
76  const int weights_offset = -wq_info.offset;
77  const float weights_scale = wq_info.scale;
78  const int output_offset = oq_info.offset;
79  const float output_scale = oq_info.scale;
80 
81  int output_multiplier = 0;
82  int output_shift = 0;
83  const float multiplier = input_scale * weights_scale / output_scale;
84  arm_compute::quantization::calculate_quantized_multiplier(multiplier, &output_multiplier, &output_shift);
85 
86  const int min = std::numeric_limits<T>::lowest();
87  const int max = std::numeric_limits<T>::max();
88 
89  for(int y = 0; y < rows_weights; ++y)
90  {
91  // Reset accumulator
92  int32_t acc = 0;
93 
94  for(int x = 0; x < cols_weights; ++x)
95  {
96  acc += (src_ptr[x] + input_offset) * (weights_ptr[x] + weights_offset);
97  }
98 
99  // Accumulate the bias
100  acc += bias_ptr[y];
101 
102  // Quantize down
103  acc = quantize_down_scale_by_fixedpoint(acc, output_multiplier, output_shift, output_offset, min, max);
104 
105  // Store the result
106  dst_ptr[y] = static_cast<T>(acc);
107 
108  weights_ptr += cols_weights;
109  }
110 }
111 } // namespace
112 
113 template <typename T, typename TB>
115 {
116  // if no explicit quantization has been set you the same as src
117  if(out_quant_info == QuantizationInfo())
118  {
119  out_quant_info = src.quantization_info();
120  }
121 
122  // Create reference
123  SimpleTensor<T> dst{ TensorShape{ dst_shape }, src.data_type(), 1, out_quant_info };
124 
125  // Sanity checks
126  const int num_batch_dimensions = std::max(0, static_cast<int>(dst_shape.num_dimensions()) - 1);
127  const int num_input_dimensions = src.shape().num_dimensions() - num_batch_dimensions;
128  const unsigned int linear_input_size = src.shape().total_size_lower(num_input_dimensions);
129 
130  ARM_COMPUTE_UNUSED(num_batch_dimensions);
131  ARM_COMPUTE_UNUSED(num_input_dimensions);
132  ARM_COMPUTE_UNUSED(linear_input_size);
133  ARM_COMPUTE_ERROR_ON(weights.shape().x() != linear_input_size);
134  ARM_COMPUTE_ERROR_ON(weights.shape().y() != bias.shape().x());
135  ARM_COMPUTE_ERROR_ON(weights.shape().y() != dst.shape().x());
136 
137  // Compute reference
138  const int cols_weights = weights.shape().x();
139  const int rows_weights = weights.shape().y();
140  const int num_batches = dst_shape.total_size_upper(1);
141 
142  for(int k = 0; k < num_batches; ++k)
143  {
144  const int offset_in = k * cols_weights;
145  const int offset_out = k * rows_weights;
146 
147  vector_matrix_multiply<T>(src,
148  weights,
149  bias,
150  dst,
151  offset_in,
152  offset_out,
153  cols_weights,
154  rows_weights);
155  }
156 
157  return dst;
158 }
159 
161  QuantizationInfo out_quant_info);
163  QuantizationInfo out_quant_info);
165  QuantizationInfo out_quant_info);
167  QuantizationInfo out_quant_info);
168 } // namespace reference
169 } // namespace validation
170 } // namespace test
171 } // namespace arm_compute
Shape of a tensor.
Definition: TensorShape.h:39
SimpleTensor< T > fully_connected_layer(const SimpleTensor< T > &src, const SimpleTensor< T > &weights, const SimpleTensor< TB > &bias, const TensorShape &dst_shape, QuantizationInfo out_quant_info)
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
size_t total_size_upper(size_t dimension) const
Collapses given dimension and above.
Definition: TensorShape.h:181
Copyright (c) 2017-2020 ARM Limited.
Quantization information.
#define ARM_COMPUTE_UNUSED(...)
To avoid unused variables warnings.
Definition: Error.h:152
int32_t quantize_down_scale_by_fixedpoint(int32_t val, int32_t result_mult_int, int32_t result_shift, int32_t result_offset_after_shift, int32_t min, int32_t max)
Quantize down the input value in range [min, max].
Simple tensor object that stores elements in a consecutive chunk of memory.
Definition: SimpleTensor.h:59
unsigned int num_dimensions() const
Returns the effective dimensionality of the tensor.
Definition: Dimensions.h:122
Status calculate_quantized_multiplier(float multiplier, int32_t *quant_multiplier, int32_t *shift)
Calculate quantized representation of multiplier.
cast configure & src
Definition: Cast.cpp:169