ArmNN
 20.02
Pooling2d.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "Pooling2d.hpp"
7 
8 #include <armnn/Exceptions.hpp>
9 #include <armnn/Types.hpp>
10 
12 
13 #include <boost/numeric/conversion/cast.hpp>
14 
15 #include <limits>
16 #include <algorithm>
17 #include <functional>
18 
19 namespace
20 {
22 
23  float DefaultInitializer(PoolingAlgorithm algorithm)
24  {
25  switch (algorithm)
26  {
27  case PoolingAlgorithm::Max:
28  {
29  return std::numeric_limits<float>::lowest();
30  }
31  case PoolingAlgorithm::Average:
32  case PoolingAlgorithm::L2:
33  {
34  return 0.0f;
35  }
36  default:
37  {
38  throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
39  }
40  }
41  }
42 
43  using Accumulator = std::function<void(float & accu, float value)>;
44 
45  Accumulator GetAccumulator(PoolingAlgorithm algorithm)
46  {
47  switch (algorithm)
48  {
49  case PoolingAlgorithm::Max:
50  {
51  return [](float & accu, float value) {
52  if (value > accu) {
53  accu = value;
54  }
55  };
56  }
57 
58  case PoolingAlgorithm::Average:
59  {
60  return [](float & accu, float value) {
61  accu += value;
62  };
63  }
64 
65  case PoolingAlgorithm::L2:
66  {
67  return [](float & accu, float value) {
68  accu += (value*value);
69  };
70  }
71 
72  default:
73  {
74  throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
75  }
76  }
77  }
78 
79  using Executor = std::function<void(float & accumulated, float kernelSize)>;
80 
81  Executor GetExecutor(PoolingAlgorithm algorithm)
82  {
83  switch (algorithm)
84  {
85  case PoolingAlgorithm::Max:
86  {
87  return [](float & /*accumulated*/, float /*kernelSize*/) {};
88  }
89 
90  case PoolingAlgorithm::Average:
91  {
92  return [](float & accumulated, float kernelSize) {
93  accumulated /= kernelSize;
94  };
95  }
96 
97  case PoolingAlgorithm::L2:
98  {
99  return [](float & accumulated, float kernelSize) {
100  accumulated = sqrtf(accumulated / kernelSize);
101  };
102  }
103 
104  default:
105  {
106  throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
107  }
108  }
109  }
110 
111  bool OnPaddingOnly(int start, int end, int maxRange)
112  {
113  if (end <= 0 || start > maxRange)
114  {
115  return true;
116  }
117  else
118  {
119  return false;
120  }
121  }
122 
123 
124  bool ClampRange(int & start, int & end, int maxRange)
125  {
126  if (start < 0 || end > maxRange)
127  {
128  start = std::min(std::max(start, 0), maxRange);
129  end = std::min(std::max(end, 0), maxRange);
130  return true;
131  }
132  else
133  {
134  return false;
135  }
136  }
137 }
138 
139 using namespace armnnUtils;
140 
141 namespace armnn
142 {
143 void Pooling2d(Decoder<float>& rInputDecoder,
144  Encoder<float>& rOutputEncoder,
145  const TensorInfo& inputInfo,
146  const TensorInfo& outputInfo,
147  const Pooling2dDescriptor& params)
148 {
149  const DataLayoutIndexed dataLayout(params.m_DataLayout);
150  auto channelsIndex = dataLayout.GetChannelsIndex();
151  auto heightIndex = dataLayout.GetHeightIndex();
152  auto widthIndex = dataLayout.GetWidthIndex();
153 
154  const int batchSize = boost::numeric_cast<int>(outputInfo.GetShape()[0]);
155  const int channels = boost::numeric_cast<int>(outputInfo.GetShape()[channelsIndex]);
156  const int heightOutput = boost::numeric_cast<int>(outputInfo.GetShape()[heightIndex]);
157  const int widthOutput = boost::numeric_cast<int>(outputInfo.GetShape()[widthIndex]);
158  const int heightInput = boost::numeric_cast<int>(inputInfo.GetShape()[heightIndex]);
159  const int widthInput = boost::numeric_cast<int>(inputInfo.GetShape()[widthIndex]);
160  const int padLeft = boost::numeric_cast<int>(params.m_PadLeft);
161  const int padRight = boost::numeric_cast<int>(params.m_PadRight);
162  const int padTop = boost::numeric_cast<int>(params.m_PadTop);
163  const int padBottom = boost::numeric_cast<int>(params.m_PadBottom);
164  const int strideX = boost::numeric_cast<int>(params.m_StrideX);
165  const int strideY = boost::numeric_cast<int>(params.m_StrideY);
166  const int poolHeight = boost::numeric_cast<int>(params.m_PoolHeight);
167  const int poolWidth = boost::numeric_cast<int>(params.m_PoolWidth);
168 
169  float defaultInitializer = DefaultInitializer(params.m_PoolType);
170 
171  Accumulator accumulate = GetAccumulator(params.m_PoolType);
172  Executor execute = GetExecutor(params.m_PoolType);
173 
174  TensorShape outputShape = outputInfo.GetShape();
175  TensorShape inputShape = inputInfo.GetShape();
176 
177  // Check supported padding methods outside the loop to simplify
178  // the inner loop.
179  if (params.m_PaddingMethod != PaddingMethod::Exclude &&
180  params.m_PaddingMethod != PaddingMethod::IgnoreValue)
181  {
182  throw armnn::InvalidArgumentException("Unsupported padding type");
183  }
184 
185  for (int n = 0; n < batchSize; n++)
186  {
187  for (int c = 0; c < channels; c++)
188  {
189  for (int yOutput = 0; yOutput < heightOutput; yOutput++)
190  {
191  // Calculate values independent of the x axis
192  int hstart = (yOutput * strideY) - padTop;
193  int hend = hstart + poolHeight;
194  // Clamp the pooling region inside the valid input area (which includes the padding).
195  // This is necessary because the final pooling in a row may overlap beyond the padding.
196  hend = std::min(hend, heightInput + padBottom);
197 
198  int height = hend - hstart;
199  bool hclamped = ClampRange(hstart, hend, heightInput);
200 
201  for (int xOutput = 0; xOutput < widthOutput; xOutput++)
202  {
203  int wstart = (xOutput * strideX) - padLeft;
204  int wend = wstart + poolWidth;
205 
206  // Clamp the pooling region inside the valid input area (which includes the padding).
207  // This is necessary because the final pooling in a row may overlap beyond the padding.
208  wend = std::min(wend, widthInput + padRight);
209 
210  float result = defaultInitializer;
211  float poolAreaSize = boost::numeric_cast<float>(height * (wend - wstart));
212 
213  // Special case: when the pooling kernel is over a padding region and the padding
214  // size is larger or equal to the kernel and the kernel only covers
215  // padding and no real values, then we initialize the result as zero
216  // by convention. This is because we need to choose a value here and
217  // all values we have are padding, which we ignore.
218  if (OnPaddingOnly(hstart, hend, heightInput) ||
219  OnPaddingOnly(wstart, wend, widthInput))
220  {
221  result = 0.0f;
222 
223  unsigned int outputIndex = dataLayout.GetIndex(outputShape,
224  boost::numeric_cast<unsigned int>(n),
225  boost::numeric_cast<unsigned int>(c),
226  boost::numeric_cast<unsigned int>(yOutput),
227  boost::numeric_cast<unsigned int>(xOutput));
228  rOutputEncoder[outputIndex];
229  rOutputEncoder.Set(result);
230  continue;
231  }
232 
233  bool clamped = hclamped |= ClampRange(wstart, wend, widthInput);
234 
235  if (clamped && params.m_PaddingMethod == PaddingMethod::Exclude)
236  {
237  // When we exclude the padding, it means we calculate with a smaller
238  // kernel size, so I changed the divisor here.
239  poolAreaSize = boost::numeric_cast<float>((hend - hstart) * (wend - wstart));
240  }
241 
242  for (auto yInput = hstart; yInput < hend; yInput++)
243  {
244  for (auto xInput = wstart; xInput < wend; xInput++)
245  {
246  unsigned int inputIndex = dataLayout.GetIndex(inputShape,
247  boost::numeric_cast<unsigned int>(n),
248  boost::numeric_cast<unsigned int>(c),
249  boost::numeric_cast<unsigned int>(yInput),
250  boost::numeric_cast<unsigned int>(xInput));
251 
252  rInputDecoder[inputIndex];
253  float inval = rInputDecoder.Get();
254 
255  accumulate(result, inval);
256  }
257  }
258 
259  execute(result, poolAreaSize);
260 
261  unsigned int outputIndex = dataLayout.GetIndex(outputShape,
262  boost::numeric_cast<unsigned int>(n),
263  boost::numeric_cast<unsigned int>(c),
264  boost::numeric_cast<unsigned int>(yOutput),
265  boost::numeric_cast<unsigned int>(xOutput));
266 
267  rOutputEncoder[outputIndex];
268  rOutputEncoder.Set(result);
269  }
270  }
271  }
272  }
273 }
274 
275 } //namespace armnn
uint32_t m_PadBottom
Padding bottom value in the height dimension.
unsigned int GetWidthIndex() const
const TensorShape & GetShape() const
Definition: Tensor.hpp:88
uint32_t m_PadLeft
Padding left value in the width dimension.
uint32_t m_PoolWidth
Pooling width value.
virtual void Set(IType right)=0
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
uint32_t m_PadTop
Padding top value in the height dimension.
Copyright (c) 2020 ARM Limited.
PoolingAlgorithm
Definition: Types.hpp:96
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
unsigned int GetHeightIndex() const
virtual IType Get() const =0
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadRight
Padding right value in the width dimension.
Provides access to the appropriate indexes for Channels, Height and Width based on DataLayout...
unsigned int GetIndex(const armnn::TensorShape &shape, unsigned int batchIndex, unsigned int channelIndex, unsigned int heightIndex, unsigned int widthIndex) const
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:33
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
A Pooling2dDescriptor for the Pooling2dLayer.
void Pooling2d(Decoder< float > &rInputDecoder, Encoder< float > &rOutputEncoder, const TensorInfo &inputInfo, const TensorInfo &outputInfo, const Pooling2dDescriptor &params)
Computes the Pooling2d operation.
Definition: Pooling2d.cpp:143
unsigned int GetChannelsIndex() const
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.