ArmNN
 21.02
PermuteAndBatchToSpaceAsDepthToSpaceTests.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2019 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "../TestUtils.hpp"
7 
8 #include <Network.hpp>
9 #include <Optimizer.hpp>
10 
11 #include <boost/test/unit_test.hpp>
12 
13 using namespace armnn;
14 
16 using namespace armnn::optimizations;
17 
18 namespace
19 {
20 
21 /// Shared function for the below tests, so that we test the same network in both cases.
22 std::unique_ptr<NetworkImpl> CreateTestNetworkImpl()
23 {
24  std::unique_ptr<NetworkImpl> network(new NetworkImpl());
25 
26  auto input = network->AddInputLayer(0, "input");
27  const TensorInfo inputInfo({ 1, 2, 3, 4 }, DataType::Float32);
28  input->GetOutputSlot(0).SetTensorInfo(inputInfo);
29 
30  // Insert Permute which swaps batches and channels dimensions
31  auto permute = network->AddPermuteLayer(PermuteDescriptor(PermutationVector{ 3, 1, 2, 0 }), "permute");
32  const TensorInfo permuteInfo({ 4, 2, 3, 1 }, DataType::Float32);
33  permute->GetOutputSlot(0).SetTensorInfo(permuteInfo);
34  input->GetOutputSlot(0).Connect(permute->GetInputSlot(0));
35 
36  // Insert BatchToSpace
37  BatchToSpaceNdDescriptor batchToSpaceDesc;
38  batchToSpaceDesc.m_BlockShape = { 2, 2 };
39  batchToSpaceDesc.m_DataLayout = DataLayout::NHWC;
40  auto batchToSpace = network->AddBatchToSpaceNdLayer(batchToSpaceDesc, "batchToSpace");
41  const TensorInfo batchToSpaceInfo({ 1, 4, 6, 1 }, DataType::Float32);
42  batchToSpace->GetOutputSlot(0).SetTensorInfo(batchToSpaceInfo);
43  permute->GetOutputSlot(0).Connect(batchToSpace->GetInputSlot(0));
44 
45  auto output = network->AddOutputLayer(0, "output");
46  batchToSpace->GetOutputSlot(0).Connect(output->GetInputSlot(0));
47 
48  return network;
49 }
50 
51 /// Shared function for the below tests, so that we test the same network in both cases.
52 std::unique_ptr<NetworkImpl> CreateTransposeTestNetworkImpl()
53 {
54  // Create a network
55  std::unique_ptr<NetworkImpl> network(new NetworkImpl());
56 
57  auto input = network->AddInputLayer(0, "input");
58  const TensorInfo inputInfo({ 1, 2, 3, 4 }, DataType::Float32);
59  input->GetOutputSlot(0).SetTensorInfo(inputInfo);
60 
61  // Insert Permute which swaps batches and channels dimensions
62  auto permute = network->AddTransposeLayer(TransposeDescriptor(PermutationVector{ 3, 1, 2, 0 }), "permute");
63  const TensorInfo permuteInfo({ 4, 2, 3, 1 }, DataType::Float32);
64  permute->GetOutputSlot(0).SetTensorInfo(permuteInfo);
65  input->GetOutputSlot(0).Connect(permute->GetInputSlot(0));
66 
67  // Insert BatchToSpace
68  BatchToSpaceNdDescriptor batchToSpaceDesc;
69  batchToSpaceDesc.m_BlockShape = { 2, 2 };
70  batchToSpaceDesc.m_DataLayout = DataLayout::NHWC;
71  auto batchToSpace = network->AddBatchToSpaceNdLayer(batchToSpaceDesc, "batchToSpace");
72  const TensorInfo batchToSpaceInfo({ 1, 4, 6, 1 }, DataType::Float32);
73  batchToSpace->GetOutputSlot(0).SetTensorInfo(batchToSpaceInfo);
74  permute->GetOutputSlot(0).Connect(batchToSpace->GetInputSlot(0));
75 
76  auto output = network->AddOutputLayer(0, "output");
77  batchToSpace->GetOutputSlot(0).Connect(output->GetInputSlot(0));
78 
79  return network;
80 }
81 
82 } // namespace
83 
84 /// Tests that the optimization performed by PermuteAndBatchToSpaceAsDepthToSpace is as expected.
85 /// Note this does not ensure the correctness of the optimization - that is done in the below test.
86 BOOST_AUTO_TEST_CASE(PermuteAndBatchToSpaceAsDepthToSpaceOptimizerTest)
87 {
88  std::unique_ptr<NetworkImpl> network = CreateTestNetworkImpl();
89  Graph graph = network.get()->GetGraph();
90 
91  // Confirm initial graph is as we expect
92  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(), &IsLayerOfType<InputLayer>, &IsLayerOfType<PermuteLayer>,
93  &IsLayerOfType<BatchToSpaceNdLayer>, &IsLayerOfType<OutputLayer>));
94 
95  // Perform the optimization which should merge the two layers into a DepthToSpace
97 
98  // Check that the replacement has been made as expected
99  auto checkDepthToSpace = [](const Layer* const layer) -> bool {
100  return IsLayerOfType<DepthToSpaceLayer>(layer) &&
101  static_cast<const DepthToSpaceLayer*>(layer)->GetParameters().m_BlockSize == 2 &&
102  static_cast<const DepthToSpaceLayer*>(layer)->GetParameters().m_DataLayout == DataLayout::NHWC &&
103  layer->GetOutputHandler().GetTensorInfo() == TensorInfo({ 1, 4, 6, 1 }, DataType::Float32);
104  };
105 
106  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(), &IsLayerOfType<InputLayer>, checkDepthToSpace,
107  &IsLayerOfType<OutputLayer>));
108 
109  // Check the new layer has the two merged layers listed as related layers
110  std::list<std::string> testRelatedLayers = { "batchToSpace", "permute" };
111  BOOST_TEST(CheckRelatedLayers<DepthToSpaceLayer>(graph, testRelatedLayers));
112 }
113 
114 /// Tests that the optimization performed by PermuteAndBatchToSpaceAsDepthToSpace is as expected.
115 /// Note this does not ensure the correctness of the optimization - that is done in the below test.
116 BOOST_AUTO_TEST_CASE(TransposeAndBatchToSpaceAsDepthToSpaceOptimizerTest)
117 {
118  std::unique_ptr<NetworkImpl> network = CreateTransposeTestNetworkImpl();
119  Graph graph = network.get()->GetGraph();
120 
121  // Confirm initial graph is as we expect
122  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(), &IsLayerOfType<InputLayer>, &IsLayerOfType<TransposeLayer>,
123  &IsLayerOfType<BatchToSpaceNdLayer>, &IsLayerOfType<OutputLayer>));
124 
125  // Perform the optimization which should merge the two layers into a DepthToSpace
127 
128  // Check that the replacement has been made as expected
129  auto checkDepthToSpace = [](const Layer* const layer) -> bool {
130  return IsLayerOfType<DepthToSpaceLayer>(layer) &&
131  static_cast<const DepthToSpaceLayer*>(layer)->GetParameters().m_BlockSize == 2 &&
132  static_cast<const DepthToSpaceLayer*>(layer)->GetParameters().m_DataLayout == DataLayout::NHWC &&
133  layer->GetOutputHandler().GetTensorInfo() == TensorInfo({ 1, 4, 6, 1 }, DataType::Float32);
134  };
135 
136  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(), &IsLayerOfType<InputLayer>, checkDepthToSpace,
137  &IsLayerOfType<OutputLayer>));
138 
139  // Check the new layer has the two merged layers listed as related layers
140  std::list<std::string> testRelatedLayers = { "batchToSpace", "permute" };
141  BOOST_TEST(CheckRelatedLayers<DepthToSpaceLayer>(graph, testRelatedLayers));
142 }
143 
144 // This unit test needs the reference backend, it's not available if the reference backend is not built
145 #if defined(ARMNNREF_ENABLED)
146 
147 /// Shared function for the below tests, so that we test the same network in both cases.
148 INetworkPtr CreateTestNetwork()
149 {
150  // Create a network
151  INetworkPtr network = INetwork::Create();
152 
153  auto input = network->AddInputLayer(0, "input");
154  const TensorInfo inputInfo({ 1, 2, 3, 4 }, DataType::Float32);
155  input->GetOutputSlot(0).SetTensorInfo(inputInfo);
156 
157  // Insert Permute which swaps batches and channels dimensions
158  auto permute = network->AddPermuteLayer(PermuteDescriptor(PermutationVector{ 3, 1, 2, 0 }), "permute");
159  const TensorInfo permuteInfo({ 4, 2, 3, 1 }, DataType::Float32);
160  permute->GetOutputSlot(0).SetTensorInfo(permuteInfo);
161  input->GetOutputSlot(0).Connect(permute->GetInputSlot(0));
162 
163  // Insert BatchToSpace
164  BatchToSpaceNdDescriptor batchToSpaceDesc;
165  batchToSpaceDesc.m_BlockShape = { 2, 2 };
166  batchToSpaceDesc.m_DataLayout = DataLayout::NHWC;
167  auto batchToSpace = network->AddBatchToSpaceNdLayer(batchToSpaceDesc, "batchToSpace");
168  const TensorInfo batchToSpaceInfo({ 1, 4, 6, 1 }, DataType::Float32);
169  batchToSpace->GetOutputSlot(0).SetTensorInfo(batchToSpaceInfo);
170  permute->GetOutputSlot(0).Connect(batchToSpace->GetInputSlot(0));
171 
172  auto output = network->AddOutputLayer(0, "output");
173  batchToSpace->GetOutputSlot(0).Connect(output->GetInputSlot(0));
174 
175  return network;
176 }
177 
178 /// Shared function for the below tests, so that we test the same network in both cases.
179 INetworkPtr CreateTransposeTestNetwork()
180 {
181  // Create a network
182  INetworkPtr network = INetwork::Create();
183 
184  auto input = network->AddInputLayer(0, "input");
185  const TensorInfo inputInfo({ 1, 2, 3, 4 }, DataType::Float32);
186  input->GetOutputSlot(0).SetTensorInfo(inputInfo);
187 
188  // Insert Permute which swaps batches and channels dimensions
189  auto permute = network->AddTransposeLayer(TransposeDescriptor(PermutationVector{ 3, 1, 2, 0 }), "permute");
190  const TensorInfo permuteInfo({ 4, 2, 3, 1 }, DataType::Float32);
191  permute->GetOutputSlot(0).SetTensorInfo(permuteInfo);
192  input->GetOutputSlot(0).Connect(permute->GetInputSlot(0));
193 
194  // Insert BatchToSpace
195  BatchToSpaceNdDescriptor batchToSpaceDesc;
196  batchToSpaceDesc.m_BlockShape = { 2, 2 };
197  batchToSpaceDesc.m_DataLayout = DataLayout::NHWC;
198  auto batchToSpace = network->AddBatchToSpaceNdLayer(batchToSpaceDesc, "batchToSpace");
199  const TensorInfo batchToSpaceInfo({ 1, 4, 6, 1 }, DataType::Float32);
200  batchToSpace->GetOutputSlot(0).SetTensorInfo(batchToSpaceInfo);
201  permute->GetOutputSlot(0).Connect(batchToSpace->GetInputSlot(0));
202 
203  auto output = network->AddOutputLayer(0, "output");
204  batchToSpace->GetOutputSlot(0).Connect(output->GetInputSlot(0));
205 
206  return network;
207 }
208 
209 /// Tests that a optimization performed by PermuteAndBatchToSpaceAsDepthToSpace does not change the behaviour
210 /// of the network (i.e. it still produces the correct output).
211 BOOST_AUTO_TEST_CASE(PermuteAndBatchToSpaceAsDepthToSpaceCorrectnessTest)
212 {
213  INetworkPtr network = CreateTestNetwork();
214 
216  IOptimizedNetworkPtr optimizedNetwork = Optimize(*network, { Compute::CpuRef }, runtime->GetDeviceSpec());
217 
218  // Confirm that the optimization has actually taken place
219  const Graph& optGraph = GetGraphForTesting(optimizedNetwork.get());
220  BOOST_TEST(CheckSequence(optGraph.cbegin(), optGraph.cend(), &IsLayerOfType<InputLayer>,
221  &IsLayerOfType<DepthToSpaceLayer>, &IsLayerOfType<OutputLayer>));
222 
223  // Load the graph into a runtime so we can check it produces the correct output
224  NetworkId netId;
225  runtime->LoadNetwork(netId, std::move(optimizedNetwork));
226 
227  std::vector<float> inputData{
228  // Each row here is a row of pixels where each pixel has 4 channels
229  // clang-format off
230  1.0f, 2.0f, 3.0f, 4.0f, 10.0f, 20.0f, 30.0f, 40.0f, 100.0f, 200.0f, 300.0f, 400.0f,
231  -1.0f, -2.0f, -3.0f, -4.0f, -10.0f, -20.0f, -30.0f, -40.0f, -100.0f, -200.0f, -300.0f, -400.0f,
232  // clang-format on
233  };
234  ConstTensor input(TensorInfo({ 1, 2, 3, 4 }, DataType::Float32), inputData);
235  InputTensors inputs = { { 0, input } };
236  std::vector<float> outputData(4 * 6);
237  Tensor output(TensorInfo({ 1, 4, 6, 1 }, DataType::Float32), outputData.data());
238  OutputTensors outputs = { { 0, output } };
239  runtime->EnqueueWorkload(netId, inputs, outputs);
240 
241  // Check the output is as expected.
242  // Note this output has been generated by running the network *without* the optimization.
243  std::vector<float> expectedOutput = {
244  // Rows and columns here match exactly with the tensor, as there is only 1 channel.
245  // clang-format off
246  1.0f, 2.0f, 10.0f, 20.0f, 100.0f, 200.0f,
247  3.0f, 4.0f, 30.0f, 40.0f, 300.0f, 400.0f,
248 
249  -1.0f, -2.0f, -10.0f, -20.0f, -100.0f, -200.0f,
250  -3.0f, -4.0f, -30.0f, -40.0f, -300.0f, -400.0f,
251  // clang-format on
252  };
253  BOOST_TEST(outputData == expectedOutput);
254 }
255 
256 /// Tests that a optimization performed by PermuteAndBatchToSpaceAsDepthToSpace does not change the behaviour
257 /// of the network (i.e. it still produces the correct output).
258 BOOST_AUTO_TEST_CASE(TransposeAndBatchToSpaceAsDepthToSpaceCorrectnessTest)
259 {
260  INetworkPtr network = CreateTransposeTestNetwork();
261 
263  IOptimizedNetworkPtr optimizedNetwork = Optimize(*network, { Compute::CpuRef }, runtime->GetDeviceSpec());
264 
265  // Confirm that the optimization has actually taken place
266  const Graph& optGraph = GetGraphForTesting(optimizedNetwork.get());
267  BOOST_TEST(CheckSequence(optGraph.cbegin(), optGraph.cend(), &IsLayerOfType<InputLayer>,
268  &IsLayerOfType<DepthToSpaceLayer>, &IsLayerOfType<OutputLayer>));
269 
270  // Load the graph into a runtime so we can check it produces the correct output
271  NetworkId netId;
272  runtime->LoadNetwork(netId, std::move(optimizedNetwork));
273 
274  std::vector<float> inputData{
275  // Each row here is a row of pixels where each pixel has 4 channels
276  // clang-format off
277  1.0f, 2.0f, 3.0f, 4.0f, 10.0f, 20.0f, 30.0f, 40.0f, 100.0f, 200.0f, 300.0f, 400.0f,
278  -1.0f, -2.0f, -3.0f, -4.0f, -10.0f, -20.0f, -30.0f, -40.0f, -100.0f, -200.0f, -300.0f, -400.0f,
279  // clang-format on
280  };
281  ConstTensor input(TensorInfo({ 1, 2, 3, 4 }, DataType::Float32), inputData);
282  InputTensors inputs = { { 0, input } };
283  std::vector<float> outputData(4 * 6);
284  Tensor output(TensorInfo({ 1, 4, 6, 1 }, DataType::Float32), outputData.data());
285  OutputTensors outputs = { { 0, output } };
286  runtime->EnqueueWorkload(netId, inputs, outputs);
287 
288  // Check the output is as expected.
289  // Note this output has been generated by running the network *without* the optimization.
290  std::vector<float> expectedOutput = {
291  // Rows and columns here match exactly with the tensor, as there is only 1 channel.
292  // clang-format off
293  1.0f, 2.0f, 10.0f, 20.0f, 100.0f, 200.0f,
294  3.0f, 4.0f, 30.0f, 40.0f, 300.0f, 400.0f,
295 
296  -1.0f, -2.0f, -10.0f, -20.0f, -100.0f, -200.0f,
297  -3.0f, -4.0f, -30.0f, -40.0f, -300.0f, -400.0f,
298  // clang-format on
299  };
300  BOOST_TEST(outputData == expectedOutput);
301 }
302 #endif
303 
BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:37
CPU Execution: Reference C++ kernels.
Optimizer::Optimizations MakeOptimizations(Args &&... args)
Definition: Optimizer.hpp:43
OptimizeForConnection< TransposeLayer, BatchToSpaceNdLayer, PermuteAndBatchToSpaceAsDepthToSpaceImpl< TransposeLayer > > TransposeAndBatchToSpaceAsDepthToSpace
ConstIterator cbegin() const
Returns const iterator pointing to the beginning of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:172
static void Pass(Graph &graph, const Optimizations &optimizations)
Definition: Optimizer.cpp:16
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:26
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::vector< std::pair< LayerBindingId, class ConstTensor > > InputTensors
Definition: Tensor.hpp:340
int NetworkId
Definition: IRuntime.hpp:20
Copyright (c) 2021 ARM Limited and Contributors.
A BatchToSpaceNdDescriptor for the BatchToSpaceNdLayer.
Private implementation of INetwork.
Definition: Network.hpp:31
A tensor defined by a TensorInfo (shape and data type) and a mutable backing store.
Definition: Tensor.hpp:306
OptimizeForConnection< PermuteLayer, BatchToSpaceNdLayer, PermuteAndBatchToSpaceAsDepthToSpaceImpl< PermuteLayer > > PermuteAndBatchToSpaceAsDepthToSpace
IOptimizedNetworkPtr Optimize(const INetwork &network, const std::vector< BackendId > &backendPreferences, const IDeviceSpec &deviceSpec, const OptimizerOptions &options=OptimizerOptions(), Optional< std::vector< std::string > &> messages=EmptyOptional())
Create an optimized version of the network.
Definition: Network.cpp:1502
std::vector< unsigned int > m_BlockShape
Block shape values.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:314
std::vector< std::pair< LayerBindingId, class Tensor > > OutputTensors
Definition: Tensor.hpp:341
std::unique_ptr< IOptimizedNetwork, void(*)(IOptimizedNetwork *network)> IOptimizedNetworkPtr
Definition: INetwork.hpp:174
BOOST_AUTO_TEST_CASE(CheckConvolution2dLayer)
Graph & GetGraphForTesting(IOptimizedNetwork *optNet)
Definition: TestUtils.cpp:25
BOOST_AUTO_TEST_SUITE_END()
bool CheckSequence(const armnn::Graph::ConstIterator first, const armnn::Graph::ConstIterator last)
Definition: TestUtils.hpp:21
A TransposeDescriptor for the TransposeLayer.
ConstIterator cend() const
Returns const iterator pointing to the end of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:174
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:173
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:510
This layer represents a DepthToSpace operation.
A PermuteDescriptor for the PermuteLayer.