blob: ce7ef37d139657b39df00256b57a8338f2fbfd66 [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
Teresa Charlinfbf0e5b2020-08-17 01:01:06 +01002// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5
Aron Virginas-Tar00d306e2019-08-28 18:08:46 +01006#include "NormalizationTestImpl.hpp"
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00007
David Beckac42efd2018-09-26 17:41:13 +01008#include <armnn/Exceptions.hpp>
9#include <armnn/LayerSupport.hpp>
telsoa014fcda012018-03-09 14:13:49 +000010
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000011#include <backendsCommon/CpuTensorHandle.hpp>
Aron Virginas-Tar00d306e2019-08-28 18:08:46 +010012
13#include <backendsCommon/test/TensorCopyUtils.hpp>
14#include <backendsCommon/test/WorkloadTestUtils.hpp>
15
16#include <test/TensorHelpers.hpp>
17
18namespace
19{
telsoa014fcda012018-03-09 14:13:49 +000020
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000021LayerTestResult<float,4> SimpleNormalizationTestImpl(
22 armnn::IWorkloadFactory& workloadFactory,
23 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
24 armnn::NormalizationAlgorithmChannel normChannel,
25 armnn::NormalizationAlgorithmMethod normMethod)
telsoa014fcda012018-03-09 14:13:49 +000026{
Jan Eilers8eb25602020-03-09 12:13:48 +000027 IgnoreUnused(memoryManager);
telsoa014fcda012018-03-09 14:13:49 +000028 const unsigned int inputHeight = 2;
29 const unsigned int inputWidth = 2;
30 const unsigned int inputChannels = 1;
31 const unsigned int inputNum = 2;
32
33 unsigned int outputHeight = inputHeight;
34 unsigned int outputWidth = inputWidth;
35 unsigned int outputChannels = inputChannels;
36 unsigned int outputNum = inputNum;
37
38 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
39 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
40
41 auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
42 auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
43
44 LayerTestResult<float,4> ret(outputTensorInfo);
45
46 auto input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>({
47 // Batch #0
48 1.0f, 2.0f,
49 3.0f, 4.0f,
50 // Batch #1
51 5.0f, 6.0f,
52 7.0f, 8.0f
53 }));
54
55 float alpha = 1.f;
56 float beta = 1.f;
57 float kappa = 1.f;
58 uint32_t normSize = 3;
59
Teresa Charlinfbf0e5b2020-08-17 01:01:06 +010060 ARMNN_NO_DEPRECATE_WARN_BEGIN
telsoa014fcda012018-03-09 14:13:49 +000061 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
62 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
Teresa Charlinfbf0e5b2020-08-17 01:01:06 +010063 ARMNN_NO_DEPRECATE_WARN_END
telsoa014fcda012018-03-09 14:13:49 +000064
65 armnn::NormalizationQueueDescriptor data;
66 armnn::WorkloadInfo info;
67 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
68 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
69 data.m_Parameters.m_NormChannelType = normChannel;
70 data.m_Parameters.m_NormMethodType = normMethod;
71 data.m_Parameters.m_NormSize = normSize;
72 data.m_Parameters.m_Alpha = alpha;
73 data.m_Parameters.m_Beta = beta;
74 data.m_Parameters.m_K = kappa;
narpra0155a97bc2018-10-02 14:35:53 +010075 data.m_Parameters.m_DataLayout = armnn::DataLayout::NCHW;
telsoa014fcda012018-03-09 14:13:49 +000076
77 armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
78 armnn::NormalizationQueueDescriptor refData = data;
79 armnn::WorkloadInfo refInfo = info;
80 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
81
82 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
83
84 inputHandle->Allocate();
85 outputHandle->Allocate();
86
87 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
88
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000089 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +000090
91 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
92
93 switch (normMethod)
94 {
95 case armnn::NormalizationAlgorithmMethod::LocalBrightness:
96 {
97 switch (normChannel)
98 {
99 case armnn::NormalizationAlgorithmChannel::Within:
100 {
101 // When normalising within channels, the 3x3 kernel covers the entire 2x2 input at every index.
102 // Therefore, all output values should equal the inputs, but divided by:
103 // pow((kappa + (accumulatedScale * alpha)), beta)
telsoa01c577f2c2018-08-31 09:22:23 +0100104 // ...where accumulatedScale is the sum of every element squared.
telsoa014fcda012018-03-09 14:13:49 +0000105 float divisor[inputNum];
106 for(int i = 0; i < boost::numeric_cast<int>(inputNum); i++)
107 {
108 float accumulatedScale = input[i][0][0][0]*input[i][0][0][0] +
109 input[i][0][0][1]*input[i][0][0][1] +
110 input[i][0][1][0]*input[i][0][1][0] +
111 input[i][0][1][1]*input[i][0][1][1];
112 divisor[i] = powf((kappa + accumulatedScale * alpha), beta);
113 }
114 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo,
115 std::vector<float>({input[0][0][0][0]/divisor[0],
116 input[0][0][0][1]/divisor[0],
117 input[0][0][1][0]/divisor[0],
118 input[0][0][1][1]/divisor[0],
119 input[1][0][0][0]/divisor[1],
120 input[1][0][0][1]/divisor[1],
121 input[1][0][1][0]/divisor[1],
122 input[1][0][1][1]/divisor[1]}));
123 break;
124 }
125 case armnn::NormalizationAlgorithmChannel::Across:
126 {
127 // When normalising across channels, all output values should equal the inputs, but multiplied by:
128 // pow((kappa + (accumulatedScale * alpha)), -beta)
129 // ...where accumulatedScale is the sum of the inputs for adjacent channels for this element squared
130 // ...where adjacent channels means within half the normSize for the channel
131 // The test data has only one channel, so this is simplified below.
132 std::vector<float> outputVector;
133 for (int n = 0; n < boost::numeric_cast<int>(inputNum); ++n)
134 {
135 for (int h = 0; h < boost::numeric_cast<int>(inputHeight); ++h)
136 {
137 for (int w = 0; w < boost::numeric_cast<int>(inputWidth); ++w)
138 {
139 float accumulatedScale = input[n][0][h][w]*input[n][0][h][w];
140 float scale = powf((kappa + accumulatedScale * alpha), -beta);
141 outputVector.push_back(input[n][0][h][w] * scale);
142 }
143 }
144 }
145 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, outputVector);
146 break;
147 }
148 default:
149 {
150 throw armnn::UnimplementedException("Unsupported normalisation channel type, "
151 "only Across and Within are supported");
152 }
153 }
154 break;
155 }
telsoa01c577f2c2018-08-31 09:22:23 +0100156 case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
telsoa014fcda012018-03-09 14:13:49 +0000157 default:
158 {
159 throw armnn::UnimplementedException("Unsupported normalisation method type, "
160 "only LocalBrightness is supported");
161 }
162 }
163
164 return ret;
165}
166
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000167LayerTestResult<float,4> SimpleNormalizationNhwcTestImpl(
168 armnn::IWorkloadFactory& workloadFactory,
169 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
170 armnn::NormalizationAlgorithmChannel normChannel,
171 armnn::NormalizationAlgorithmMethod normMethod)
narpra0155a97bc2018-10-02 14:35:53 +0100172{
173 const unsigned int inputHeight = 2;
174 const unsigned int inputWidth = 2;
175 const unsigned int inputChannels = 1;
176 const unsigned int inputNum = 2;
177
178 unsigned int outputHeight = inputHeight;
179 unsigned int outputWidth = inputWidth;
180 unsigned int outputChannels = inputChannels;
181 unsigned int outputNum = inputNum;
182
183 unsigned int inputShape[] = { inputNum, inputHeight, inputWidth, inputChannels };
184 unsigned int outputShape[] = { outputNum, outputHeight, outputWidth, outputChannels };
185
186 auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
187 auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
188
189 LayerTestResult<float,4> ret(outputTensorInfo);
190
191 auto input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>({
192 // Batch #0
193 1.0f, 2.0f,
194 3.0f, 4.0f,
195 // Batch #1
196 5.0f, 6.0f,
197 7.0f, 8.0f
198 }));
199
200 float alpha = 1.f;
201 float beta = 1.f;
202 float kappa = 1.f;
203 uint32_t normSize = 3;
204
Teresa Charlinfbf0e5b2020-08-17 01:01:06 +0100205 ARMNN_NO_DEPRECATE_WARN_BEGIN
narpra0155a97bc2018-10-02 14:35:53 +0100206 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
207 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
Teresa Charlinfbf0e5b2020-08-17 01:01:06 +0100208 ARMNN_NO_DEPRECATE_WARN_END
narpra0155a97bc2018-10-02 14:35:53 +0100209
210 armnn::NormalizationQueueDescriptor data;
211 armnn::WorkloadInfo info;
212 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
213 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
214 data.m_Parameters.m_NormChannelType = normChannel;
215 data.m_Parameters.m_NormMethodType = normMethod;
216 data.m_Parameters.m_NormSize = normSize;
217 data.m_Parameters.m_Alpha = alpha;
218 data.m_Parameters.m_Beta = beta;
219 data.m_Parameters.m_K = kappa;
220 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
221
222 armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
223 armnn::NormalizationQueueDescriptor refData = data;
224 armnn::WorkloadInfo refInfo = info;
225 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
226
227 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
228
229 inputHandle->Allocate();
230 outputHandle->Allocate();
231
232 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
233
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000234 ExecuteWorkload(*workload, memoryManager);
narpra0155a97bc2018-10-02 14:35:53 +0100235
236 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
237
238 switch (normMethod)
239 {
240 case armnn::NormalizationAlgorithmMethod::LocalBrightness:
241 {
242 switch (normChannel)
243 {
244 case armnn::NormalizationAlgorithmChannel::Across:
245 {
246 std::vector<float> expectedOutput{ 0.5f, 0.400000006f, 0.300000012f, 0.235294119f,
247 0.192307696f, 0.16216217f, 0.140000001f, 0.123076923f };
248 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, expectedOutput);
249 break;
250 }
251 default:
252 {
253 throw armnn::UnimplementedException("Unsupported normalisation channel type, "
254 "Only Cross-map is supported for NHWC layout");
255 }
256 }
257 break;
258 }
259 case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
260 default:
261 {
262 throw armnn::UnimplementedException("Unsupported normalisation method type, "
263 "only LocalBrightness is supported");
264 }
265 }
266
267 return ret;
268}
269
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000270LayerTestResult<float,4> CompareNormalizationTestImpl(
271 armnn::IWorkloadFactory& workloadFactory,
272 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
273 armnn::IWorkloadFactory& refWorkloadFactory,
274 armnn::NormalizationAlgorithmChannel normChannel,
275 armnn::NormalizationAlgorithmMethod normMethod)
telsoa014fcda012018-03-09 14:13:49 +0000276{
277 constexpr unsigned int inputNum = 5;
278 constexpr unsigned int inputChannels = 3;
279 constexpr unsigned int inputHeight = 32;
280 constexpr unsigned int inputWidth = 24;
281
282 constexpr unsigned int outputNum = inputNum;
283 constexpr unsigned int outputChannels = inputChannels;
284 constexpr unsigned int outputHeight = inputHeight;
285 constexpr unsigned int outputWidth = inputWidth;
286
287 armnn::TensorInfo inputTensorInfo;
288 armnn::TensorInfo outputTensorInfo;
289
290 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
291 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
292
293 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
294 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
295
296 LayerTestResult<float,4> ret(outputTensorInfo);
297
298 auto input = MakeRandomTensor<float, 4>(inputTensorInfo, 111234);
299
300 constexpr float alpha = 1.f;
301 constexpr float beta = 1.f;
302 constexpr float kappa = 1.f;
303 constexpr uint32_t normSize = 5;
304
Teresa Charlinfbf0e5b2020-08-17 01:01:06 +0100305 ARMNN_NO_DEPRECATE_WARN_BEGIN
telsoa014fcda012018-03-09 14:13:49 +0000306 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
307 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
Teresa Charlinfbf0e5b2020-08-17 01:01:06 +0100308 ARMNN_NO_DEPRECATE_WARN_END
telsoa014fcda012018-03-09 14:13:49 +0000309
310 armnn::NormalizationQueueDescriptor data;
311 armnn::WorkloadInfo info;
312 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
313 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
314 data.m_Parameters.m_NormChannelType = normChannel;
315 data.m_Parameters.m_NormMethodType = normMethod;
316 data.m_Parameters.m_NormSize = normSize;
317 data.m_Parameters.m_Alpha = alpha;
318 data.m_Parameters.m_Beta = beta;
319 data.m_Parameters.m_K = kappa;
320
Teresa Charlinfbf0e5b2020-08-17 01:01:06 +0100321 ARMNN_NO_DEPRECATE_WARN_BEGIN
telsoa014fcda012018-03-09 14:13:49 +0000322 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
323 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
Teresa Charlinfbf0e5b2020-08-17 01:01:06 +0100324 ARMNN_NO_DEPRECATE_WARN_END
telsoa014fcda012018-03-09 14:13:49 +0000325
326 armnn::NormalizationQueueDescriptor refData = data;
327 armnn::WorkloadInfo refInfo = info;
328 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
329 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
330
331 // Don't execute if Normalization is not supported for the method and channel types, as an exception will be raised.
David Beck79141b92018-10-23 16:09:36 +0100332 armnn::BackendId backend = workloadFactory.GetBackendId();
telsoa014fcda012018-03-09 14:13:49 +0000333 const size_t reasonIfUnsupportedMaxLen = 255;
334 char reasonIfUnsupported[reasonIfUnsupportedMaxLen+1];
David Beck79141b92018-10-23 16:09:36 +0100335 ret.supported = armnn::IsNormalizationSupported(backend, inputTensorInfo, outputTensorInfo, data.m_Parameters,
telsoa014fcda012018-03-09 14:13:49 +0000336 reasonIfUnsupported, reasonIfUnsupportedMaxLen);
337 if (!ret.supported)
338 {
339 return ret;
340 }
341
342 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
343 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateNormalization(refData, refInfo);
344
345 outputHandleRef->Allocate();
346 inputHandleRef->Allocate();
347
348 inputHandle->Allocate();
349 outputHandle->Allocate();
350
351 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
352 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
353
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000354 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar60578952018-10-31 11:04:01 +0000355
telsoa014fcda012018-03-09 14:13:49 +0000356 workloadRef->Execute();
357
358 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
359 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
360
361 return ret;
362}
363
Aron Virginas-Tar00d306e2019-08-28 18:08:46 +0100364} // anonymous namespace
365
366LayerTestResult<float,4> SimpleNormalizationAcrossTest(
367 armnn::IWorkloadFactory& workloadFactory,
368 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
369{
370 auto normMethod = armnn::NormalizationAlgorithmMethod::LocalBrightness;
371 auto normChannel = armnn::NormalizationAlgorithmChannel::Across;
372 return SimpleNormalizationTestImpl(workloadFactory, memoryManager, normChannel, normMethod);
373}
374
375LayerTestResult<float,4> SimpleNormalizationWithinTest(
376 armnn::IWorkloadFactory& workloadFactory,
377 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
378{
379 auto normMethod = armnn::NormalizationAlgorithmMethod::LocalBrightness;
380 auto normChannel = armnn::NormalizationAlgorithmChannel::Within;
381 return SimpleNormalizationTestImpl(workloadFactory, memoryManager, normChannel, normMethod);
382}
383
384LayerTestResult<float,4> SimpleNormalizationAcrossNhwcTest(
385 armnn::IWorkloadFactory& workloadFactory,
386 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
387{
388 auto normMethod = armnn::NormalizationAlgorithmMethod::LocalBrightness;
389 auto normChannel = armnn::NormalizationAlgorithmChannel::Across;
390 return SimpleNormalizationNhwcTestImpl(workloadFactory, memoryManager, normChannel, normMethod);
391}
392
393LayerTestResult<float,4> CompareNormalizationTest(
394 armnn::IWorkloadFactory& workloadFactory,
395 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
396 armnn::IWorkloadFactory& refWorkloadFactory,
397 armnn::NormalizationAlgorithmChannel normChannel,
398 armnn::NormalizationAlgorithmMethod normMethod)
399{
400 return CompareNormalizationTestImpl(workloadFactory, memoryManager, refWorkloadFactory, normChannel, normMethod);
401}