blob: ef3a45b391934b5df5619c3e0e10edc42b3e1e17 [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. 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
60 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
61 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
62
63 armnn::NormalizationQueueDescriptor data;
64 armnn::WorkloadInfo info;
65 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
66 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
67 data.m_Parameters.m_NormChannelType = normChannel;
68 data.m_Parameters.m_NormMethodType = normMethod;
69 data.m_Parameters.m_NormSize = normSize;
70 data.m_Parameters.m_Alpha = alpha;
71 data.m_Parameters.m_Beta = beta;
72 data.m_Parameters.m_K = kappa;
narpra0155a97bc2018-10-02 14:35:53 +010073 data.m_Parameters.m_DataLayout = armnn::DataLayout::NCHW;
telsoa014fcda012018-03-09 14:13:49 +000074
75 armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
76 armnn::NormalizationQueueDescriptor refData = data;
77 armnn::WorkloadInfo refInfo = info;
78 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
79
80 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
81
82 inputHandle->Allocate();
83 outputHandle->Allocate();
84
85 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
86
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000087 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +000088
89 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
90
91 switch (normMethod)
92 {
93 case armnn::NormalizationAlgorithmMethod::LocalBrightness:
94 {
95 switch (normChannel)
96 {
97 case armnn::NormalizationAlgorithmChannel::Within:
98 {
99 // When normalising within channels, the 3x3 kernel covers the entire 2x2 input at every index.
100 // Therefore, all output values should equal the inputs, but divided by:
101 // pow((kappa + (accumulatedScale * alpha)), beta)
telsoa01c577f2c2018-08-31 09:22:23 +0100102 // ...where accumulatedScale is the sum of every element squared.
telsoa014fcda012018-03-09 14:13:49 +0000103 float divisor[inputNum];
104 for(int i = 0; i < boost::numeric_cast<int>(inputNum); i++)
105 {
106 float accumulatedScale = input[i][0][0][0]*input[i][0][0][0] +
107 input[i][0][0][1]*input[i][0][0][1] +
108 input[i][0][1][0]*input[i][0][1][0] +
109 input[i][0][1][1]*input[i][0][1][1];
110 divisor[i] = powf((kappa + accumulatedScale * alpha), beta);
111 }
112 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo,
113 std::vector<float>({input[0][0][0][0]/divisor[0],
114 input[0][0][0][1]/divisor[0],
115 input[0][0][1][0]/divisor[0],
116 input[0][0][1][1]/divisor[0],
117 input[1][0][0][0]/divisor[1],
118 input[1][0][0][1]/divisor[1],
119 input[1][0][1][0]/divisor[1],
120 input[1][0][1][1]/divisor[1]}));
121 break;
122 }
123 case armnn::NormalizationAlgorithmChannel::Across:
124 {
125 // When normalising across channels, all output values should equal the inputs, but multiplied by:
126 // pow((kappa + (accumulatedScale * alpha)), -beta)
127 // ...where accumulatedScale is the sum of the inputs for adjacent channels for this element squared
128 // ...where adjacent channels means within half the normSize for the channel
129 // The test data has only one channel, so this is simplified below.
130 std::vector<float> outputVector;
131 for (int n = 0; n < boost::numeric_cast<int>(inputNum); ++n)
132 {
133 for (int h = 0; h < boost::numeric_cast<int>(inputHeight); ++h)
134 {
135 for (int w = 0; w < boost::numeric_cast<int>(inputWidth); ++w)
136 {
137 float accumulatedScale = input[n][0][h][w]*input[n][0][h][w];
138 float scale = powf((kappa + accumulatedScale * alpha), -beta);
139 outputVector.push_back(input[n][0][h][w] * scale);
140 }
141 }
142 }
143 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, outputVector);
144 break;
145 }
146 default:
147 {
148 throw armnn::UnimplementedException("Unsupported normalisation channel type, "
149 "only Across and Within are supported");
150 }
151 }
152 break;
153 }
telsoa01c577f2c2018-08-31 09:22:23 +0100154 case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
telsoa014fcda012018-03-09 14:13:49 +0000155 default:
156 {
157 throw armnn::UnimplementedException("Unsupported normalisation method type, "
158 "only LocalBrightness is supported");
159 }
160 }
161
162 return ret;
163}
164
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000165LayerTestResult<float,4> SimpleNormalizationNhwcTestImpl(
166 armnn::IWorkloadFactory& workloadFactory,
167 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
168 armnn::NormalizationAlgorithmChannel normChannel,
169 armnn::NormalizationAlgorithmMethod normMethod)
narpra0155a97bc2018-10-02 14:35:53 +0100170{
171 const unsigned int inputHeight = 2;
172 const unsigned int inputWidth = 2;
173 const unsigned int inputChannels = 1;
174 const unsigned int inputNum = 2;
175
176 unsigned int outputHeight = inputHeight;
177 unsigned int outputWidth = inputWidth;
178 unsigned int outputChannels = inputChannels;
179 unsigned int outputNum = inputNum;
180
181 unsigned int inputShape[] = { inputNum, inputHeight, inputWidth, inputChannels };
182 unsigned int outputShape[] = { outputNum, outputHeight, outputWidth, outputChannels };
183
184 auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
185 auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
186
187 LayerTestResult<float,4> ret(outputTensorInfo);
188
189 auto input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>({
190 // Batch #0
191 1.0f, 2.0f,
192 3.0f, 4.0f,
193 // Batch #1
194 5.0f, 6.0f,
195 7.0f, 8.0f
196 }));
197
198 float alpha = 1.f;
199 float beta = 1.f;
200 float kappa = 1.f;
201 uint32_t normSize = 3;
202
203 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
204 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
205
206 armnn::NormalizationQueueDescriptor data;
207 armnn::WorkloadInfo info;
208 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
209 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
210 data.m_Parameters.m_NormChannelType = normChannel;
211 data.m_Parameters.m_NormMethodType = normMethod;
212 data.m_Parameters.m_NormSize = normSize;
213 data.m_Parameters.m_Alpha = alpha;
214 data.m_Parameters.m_Beta = beta;
215 data.m_Parameters.m_K = kappa;
216 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
217
218 armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
219 armnn::NormalizationQueueDescriptor refData = data;
220 armnn::WorkloadInfo refInfo = info;
221 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
222
223 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
224
225 inputHandle->Allocate();
226 outputHandle->Allocate();
227
228 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
229
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000230 ExecuteWorkload(*workload, memoryManager);
narpra0155a97bc2018-10-02 14:35:53 +0100231
232 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
233
234 switch (normMethod)
235 {
236 case armnn::NormalizationAlgorithmMethod::LocalBrightness:
237 {
238 switch (normChannel)
239 {
240 case armnn::NormalizationAlgorithmChannel::Across:
241 {
242 std::vector<float> expectedOutput{ 0.5f, 0.400000006f, 0.300000012f, 0.235294119f,
243 0.192307696f, 0.16216217f, 0.140000001f, 0.123076923f };
244 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, expectedOutput);
245 break;
246 }
247 default:
248 {
249 throw armnn::UnimplementedException("Unsupported normalisation channel type, "
250 "Only Cross-map is supported for NHWC layout");
251 }
252 }
253 break;
254 }
255 case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
256 default:
257 {
258 throw armnn::UnimplementedException("Unsupported normalisation method type, "
259 "only LocalBrightness is supported");
260 }
261 }
262
263 return ret;
264}
265
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000266LayerTestResult<float,4> CompareNormalizationTestImpl(
267 armnn::IWorkloadFactory& workloadFactory,
268 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
269 armnn::IWorkloadFactory& refWorkloadFactory,
270 armnn::NormalizationAlgorithmChannel normChannel,
271 armnn::NormalizationAlgorithmMethod normMethod)
telsoa014fcda012018-03-09 14:13:49 +0000272{
273 constexpr unsigned int inputNum = 5;
274 constexpr unsigned int inputChannels = 3;
275 constexpr unsigned int inputHeight = 32;
276 constexpr unsigned int inputWidth = 24;
277
278 constexpr unsigned int outputNum = inputNum;
279 constexpr unsigned int outputChannels = inputChannels;
280 constexpr unsigned int outputHeight = inputHeight;
281 constexpr unsigned int outputWidth = inputWidth;
282
283 armnn::TensorInfo inputTensorInfo;
284 armnn::TensorInfo outputTensorInfo;
285
286 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
287 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
288
289 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
290 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
291
292 LayerTestResult<float,4> ret(outputTensorInfo);
293
294 auto input = MakeRandomTensor<float, 4>(inputTensorInfo, 111234);
295
296 constexpr float alpha = 1.f;
297 constexpr float beta = 1.f;
298 constexpr float kappa = 1.f;
299 constexpr uint32_t normSize = 5;
300
301 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
302 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
303
304 armnn::NormalizationQueueDescriptor data;
305 armnn::WorkloadInfo info;
306 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
307 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
308 data.m_Parameters.m_NormChannelType = normChannel;
309 data.m_Parameters.m_NormMethodType = normMethod;
310 data.m_Parameters.m_NormSize = normSize;
311 data.m_Parameters.m_Alpha = alpha;
312 data.m_Parameters.m_Beta = beta;
313 data.m_Parameters.m_K = kappa;
314
315 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
316 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
317
318 armnn::NormalizationQueueDescriptor refData = data;
319 armnn::WorkloadInfo refInfo = info;
320 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
321 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
322
323 // 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 +0100324 armnn::BackendId backend = workloadFactory.GetBackendId();
telsoa014fcda012018-03-09 14:13:49 +0000325 const size_t reasonIfUnsupportedMaxLen = 255;
326 char reasonIfUnsupported[reasonIfUnsupportedMaxLen+1];
David Beck79141b92018-10-23 16:09:36 +0100327 ret.supported = armnn::IsNormalizationSupported(backend, inputTensorInfo, outputTensorInfo, data.m_Parameters,
telsoa014fcda012018-03-09 14:13:49 +0000328 reasonIfUnsupported, reasonIfUnsupportedMaxLen);
329 if (!ret.supported)
330 {
331 return ret;
332 }
333
334 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
335 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateNormalization(refData, refInfo);
336
337 outputHandleRef->Allocate();
338 inputHandleRef->Allocate();
339
340 inputHandle->Allocate();
341 outputHandle->Allocate();
342
343 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
344 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
345
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000346 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar60578952018-10-31 11:04:01 +0000347
telsoa014fcda012018-03-09 14:13:49 +0000348 workloadRef->Execute();
349
350 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
351 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
352
353 return ret;
354}
355
Aron Virginas-Tar00d306e2019-08-28 18:08:46 +0100356} // anonymous namespace
357
358LayerTestResult<float,4> SimpleNormalizationAcrossTest(
359 armnn::IWorkloadFactory& workloadFactory,
360 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
361{
362 auto normMethod = armnn::NormalizationAlgorithmMethod::LocalBrightness;
363 auto normChannel = armnn::NormalizationAlgorithmChannel::Across;
364 return SimpleNormalizationTestImpl(workloadFactory, memoryManager, normChannel, normMethod);
365}
366
367LayerTestResult<float,4> SimpleNormalizationWithinTest(
368 armnn::IWorkloadFactory& workloadFactory,
369 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
370{
371 auto normMethod = armnn::NormalizationAlgorithmMethod::LocalBrightness;
372 auto normChannel = armnn::NormalizationAlgorithmChannel::Within;
373 return SimpleNormalizationTestImpl(workloadFactory, memoryManager, normChannel, normMethod);
374}
375
376LayerTestResult<float,4> SimpleNormalizationAcrossNhwcTest(
377 armnn::IWorkloadFactory& workloadFactory,
378 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
379{
380 auto normMethod = armnn::NormalizationAlgorithmMethod::LocalBrightness;
381 auto normChannel = armnn::NormalizationAlgorithmChannel::Across;
382 return SimpleNormalizationNhwcTestImpl(workloadFactory, memoryManager, normChannel, normMethod);
383}
384
385LayerTestResult<float,4> CompareNormalizationTest(
386 armnn::IWorkloadFactory& workloadFactory,
387 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
388 armnn::IWorkloadFactory& refWorkloadFactory,
389 armnn::NormalizationAlgorithmChannel normChannel,
390 armnn::NormalizationAlgorithmMethod normMethod)
391{
392 return CompareNormalizationTestImpl(workloadFactory, memoryManager, refWorkloadFactory, normChannel, normMethod);
393}