ArmNN
 22.05
FuseBatchNormTests.cpp File Reference
#include "LayersFwd.hpp"
#include <Network.hpp>
#include <ResolveType.hpp>
#include <armnn/INetwork.hpp>
#include <TestUtils.hpp>
#include <doctest/doctest.h>

Go to the source code of this file.

Functions

 TEST_SUITE ("Optimizer")
 

Function Documentation

◆ TEST_SUITE()

TEST_SUITE ( "Optimizer"  )

Definition at line 17 of file FuseBatchNormTests.cpp.

References INetwork::AddConstantLayer(), INetwork::AddConvolution2dLayer(), INetwork::AddDepthwiseConvolution2dLayer(), ARMNN_NO_DEPRECATE_WARN_BEGIN, ARMNN_NO_DEPRECATE_WARN_END, IOutputSlot::Connect(), INetwork::Create(), BaseTensor< MemoryType >::GetInfo(), IConnectableLayer::GetInputSlot(), IConnectableLayer::GetOutputSlot(), armnn::IgnoreUnused(), DepthwiseConvolution2dDescriptor::m_BiasEnabled, BatchNormalizationDescriptor::m_DataLayout, armnn::NHWC, IOutputSlot::SetTensorInfo(), and OptionalReferenceSwitch< std::is_reference< T >::value, T >::value().

18 {
19 namespace
20 {
21 
22 class Conv2dTest
23 {
24 public:
25  using ConvDescriptorType = armnn::Convolution2dDescriptor;
26  using ConvLayerType = armnn::Convolution2dLayer;
27 
28  static IConnectableLayer *AddConvolution(INetwork *network,
29  const Convolution2dDescriptor &descriptor,
30  const ConstTensor &weights,
31  const Optional<ConstTensor> &biases,
32  const char *name)
33  {
35  return network->AddConvolution2dLayer(descriptor, weights, biases, name);
37  }
38 
39  static std::vector<IConnectableLayer*> AddConstantLayers(INetwork *network,
40  const Convolution2dDescriptor &descriptor,
41  const ConstTensor &weights,
42  const Optional<ConstTensor> &biases)
43  {
44  IgnoreUnused(network);
45  IgnoreUnused(descriptor);
46  IgnoreUnused(weights);
47  IgnoreUnused(biases);
48 
49  return {};
50  }
51 };
52 
53 class DepthwiseConv2dTest
54 {
55 public:
56  using ConvDescriptorType = armnn::DepthwiseConvolution2dDescriptor;
57  using ConvLayerType = armnn::DepthwiseConvolution2dLayer;
58 
59  static IConnectableLayer* AddConvolution(INetwork* network,
60  const DepthwiseConvolution2dDescriptor& descriptor,
61  const ConstTensor& weights,
62  const Optional<ConstTensor>& biases,
63  const char* name)
64  {
65  IgnoreUnused(weights);
66  IgnoreUnused(biases);
67 
68  return network->AddDepthwiseConvolution2dLayer(descriptor, name);
69  }
70 
71  static std::vector<IConnectableLayer*> AddConstantLayers(INetwork *network,
72  const DepthwiseConvolution2dDescriptor &descriptor,
73  const ConstTensor &weights,
74  const Optional<ConstTensor> &biases)
75  {
76  auto weightsLayer = network->AddConstantLayer(weights, "Weights");
77  weightsLayer->GetOutputSlot(0).SetTensorInfo(weights.GetInfo());
78  std::vector<IConnectableLayer*> layers = {weightsLayer};
79 
80  if (descriptor.m_BiasEnabled)
81  {
82  auto biasLayer = network->AddConstantLayer(biases.value(), "Bias");
83  biasLayer->GetOutputSlot(0).SetTensorInfo(biases.value().GetInfo());
84  layers.emplace_back(biasLayer);
85  }
86 
87  return layers;
88  }
89 };
90 
91 template<typename T>
92 std::vector<T> GetVector(unsigned int size, float initial, float increment)
93 {
94  std::vector<float> typeVector(size, initial);
95  std::vector<T> vector(size);
96 
97  if (size > 1)
98  {
99  for (unsigned int i = 0; i < size; ++i)
100  {
101  vector[i] = T(initial + (increment * static_cast<float>(i)));
102  }
103  }
104  return vector;
105 }
106 
107 } // namespace
108 
109 template <typename Conv2dTest,
110  armnn::DataType ArmnnType,
111  typename ConvDescriptorType = typename Conv2dTest::ConvDescriptorType,
112  typename T = armnn::ResolveType<ArmnnType>>
113 INetworkPtr CreateNetwork(bool depthwise, bool preventFusing)
114 {
115  // Define layers information
116  ConvDescriptorType convolution2dDescriptor;
117  convolution2dDescriptor.m_BiasEnabled = false;
118  convolution2dDescriptor.m_DataLayout = DataLayout::NHWC;
119  convolution2dDescriptor.m_StrideX = 1;
120  convolution2dDescriptor.m_StrideY = 1;
121  BatchNormalizationDescriptor batchNormDescriptor;
122  batchNormDescriptor.m_DataLayout = DataLayout::NHWC;
123 
124  const unsigned int inputDimensionSizes[] = {1, 4, 4, 3}; // NHWCin
125  unsigned int weightsDimensionSizes[] = {4, 2, 2, 3}; // CoutHWCin
126  unsigned int outputDimensionSizes[] = {1, 3, 3, 4}; // NHWCout
127 
128  if (depthwise)
129  {
130  // [1, H, W, Cout]
131  weightsDimensionSizes[0] = 1;
132  weightsDimensionSizes[1] = 2;
133  weightsDimensionSizes[2] = 2;
134  weightsDimensionSizes[3] = 12;
135  outputDimensionSizes[3] = weightsDimensionSizes[3];
136  }
137  const unsigned int outputChannelSize[] = {outputDimensionSizes[3]}; // Cout
138 
139  TensorInfo inputInfo(4, inputDimensionSizes, ArmnnType);
140  TensorInfo outputInfo(4, outputDimensionSizes, ArmnnType);
141 
142  std::vector<int> weightsIntVector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
143  11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
144  21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
145  31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42};
146  std::vector<T> weightsVector(begin(weightsIntVector), end(weightsIntVector));
147  TensorInfo weightsInfo(4, weightsDimensionSizes, ArmnnType, 0.0f, 0, true);
148  ConstTensor weights(weightsInfo, weightsVector);
149 
150  std::vector<T> betaVector = GetVector<T>(outputDimensionSizes[3], 0.0f, 0.2f);
151  std::vector<T> gammaVector = GetVector<T>(outputDimensionSizes[3], 0.5f, 0.1f);
152  std::vector<T> meanVector = GetVector<T>(outputDimensionSizes[3], 0.1f, 0.1f);
153  std::vector<T> varianceVector = GetVector<T>(outputDimensionSizes[3], 1.0f, 0.1f);
154 
155  ConstTensor beta (TensorInfo(1, outputChannelSize, ArmnnType, 0.0f, 0, true), betaVector);
156  ConstTensor gamma (TensorInfo(1, outputChannelSize, ArmnnType, 0.0f, 0, true), gammaVector);
157  ConstTensor mean (TensorInfo(1, outputChannelSize, ArmnnType, 0.0f, 0, true), meanVector);
158  ConstTensor variance(TensorInfo(1, outputChannelSize, ArmnnType, 0.0f, 0, true), varianceVector);
159 
160  // Create a network
161  INetworkPtr network = INetwork::Create();
162 
163  IConnectableLayer* inputLayer = network->AddInputLayer(0);
164 
165  IConnectableLayer* convLayer = Conv2dTest::AddConvolution(network.get(),
166  convolution2dDescriptor,
167  weights,
169  "convolution");
170 
171  IConnectableLayer* batchNormLayer = network->AddBatchNormalizationLayer(batchNormDescriptor,
172  mean,
173  variance,
174  beta,
175  gamma,
176  "batchNorm");
177 
178  IConnectableLayer* outputLayer = network->AddOutputLayer(0);
179  IConnectableLayer* output2Layer = nullptr;
180 
181  if (preventFusing)
182  {
183  output2Layer = network->AddOutputLayer(1);
184  }
185 
186  std::vector<IConnectableLayer*> constantLayers = Conv2dTest::AddConstantLayers(network.get(),
187  convolution2dDescriptor,
188  weights,
190 
191  // Connect constant layers to receiverLayer.
192  for (unsigned int i = 0; i < constantLayers.size(); ++i)
193  {
194  constantLayers[i]->GetOutputSlot(0).Connect(convLayer->GetInputSlot(i + 1));
195  }
196 
197  // Set layer information
198  inputLayer ->GetOutputSlot(0).SetTensorInfo(inputInfo);
199  convLayer ->GetOutputSlot(0).SetTensorInfo(outputInfo);
200  batchNormLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
201 
202  // Connect layers
203  inputLayer ->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
204  convLayer ->GetOutputSlot(0).Connect(batchNormLayer->GetInputSlot(0));
205  batchNormLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
206 
207  if (preventFusing)
208  {
209  convLayer ->GetOutputSlot(0).Connect(output2Layer->GetInputSlot(0));
210  }
211 
212  return network;
213 }
214 
215 template <typename Conv2dTest,
216  armnn::DataType ArmnnType,
217  typename ConvDescriptorType = typename Conv2dTest::ConvDescriptorType,
218  typename ConvLayerType = typename Conv2dTest::ConvLayerType,
219  typename T = armnn::ResolveType<ArmnnType>>
220 void FuseBatchNormIntoConvTest(bool depthwise, float tolerance, armnn::Compute backendId)
221 {
222  // FIRST NETWORK: Fused
223  // Construct ArmNN network
224  INetworkPtr networkFused = CreateNetwork<Conv2dTest, ArmnnType>(depthwise, false);
225 
226  // Create ArmNN runtime
227  IRuntimePtr run = IRuntime::Create(IRuntime::CreationOptions()); // default options
228 
229  // Optimise ArmNN network
230  IOptimizedNetworkPtr optNetFused = Optimize(*networkFused, {backendId}, run->GetDeviceSpec());
231 
232  Graph& graphFused = GetGraphForTesting(optNetFused.get());
233 
234  auto checkFusedConv2d = [ ](const armnn::Layer* const layer) -> bool
235  {
236  return IsLayerOfType<ConvLayerType>(layer) &&
237  (layer->GetNameStr() == "fused-batchNorm-into-convolution");
238  };
239 
240  CHECK(5 == graphFused.GetNumLayers());
241  CHECK(CheckSequence(graphFused.cbegin(),
242  graphFused.cend(),
243  &IsLayerOfType<InputLayer>,
244  &IsLayerOfType<ConstantLayer>,
245  &IsLayerOfType<ConstantLayer>,
246  checkFusedConv2d,
247  &IsLayerOfType<OutputLayer>));
248 
249  // Load network into runtime
250  NetworkId networkIdentifier;
251  CHECK(run->LoadNetwork(networkIdentifier, std::move(optNetFused)) == Status::Success);
252 
253  //Creates structures for inputs and outputs.
254  std::vector<T> inputDataFused = GetVector<T>(48, 1.0f, 0.1f);
255 
256  std::vector<T> outputDataFused(36);
257 
258  if (depthwise)
259  {
260  outputDataFused.resize(108);
261  }
262 
263  TensorInfo inputTensorInfo = run->GetInputTensorInfo(networkIdentifier, 0);
264  inputTensorInfo.SetConstant(true);
265  InputTensors inputTensorsFused {
266  {0, ConstTensor(inputTensorInfo, inputDataFused.data())}};
267  OutputTensors outputTensorsFused{
268  {0, Tensor(run->GetOutputTensorInfo(networkIdentifier, 0), outputDataFused.data())}};
269 
270  // Execute network
271  run->EnqueueWorkload(networkIdentifier, inputTensorsFused, outputTensorsFused);
272 
273  // SECOND NETWORK: NotFused
274  // Construct ArmNN network
275  INetworkPtr networkNotFused = CreateNetwork<Conv2dTest, ArmnnType>(depthwise, true);
276 
277  // Create ArmNN runtime
278  IRuntimePtr runNotFused = IRuntime::Create(IRuntime::CreationOptions()); // default options
279 
280  // Optimise ArmNN network
281  IOptimizedNetworkPtr optNetNotFused = Optimize(*networkNotFused, { backendId }, runNotFused->GetDeviceSpec());
282 
283  Graph& graphNotFused = GetGraphForTesting(optNetNotFused.get());
284 
285  CHECK(6 == graphNotFused.GetNumLayers());
286  CHECK(CheckSequence(graphNotFused.cbegin(),
287  graphNotFused.cend(),
288  &IsLayerOfType<armnn::InputLayer>,
289  &IsLayerOfType<armnn::ConstantLayer>,
290  &IsLayerOfType<ConvLayerType>,
291  &IsLayerOfType<armnn::BatchNormalizationLayer>,
292  &IsLayerOfType<armnn::OutputLayer>,
293  &IsLayerOfType<armnn::OutputLayer>));
294 
295  // Load network into runtime
296  NetworkId networkIdentifierNotFused;
297  CHECK(runNotFused->LoadNetwork(networkIdentifierNotFused, std::move(optNetNotFused)) == Status::Success);
298 
299  //Creates structures for inputs and outputs.
300  std::vector<T> inputDataNotFused = GetVector<T>(48, 1.0f, 0.1f);
301 
302  std::vector<T> outputDataNotFused(36);
303  std::vector<T> outputData2NotFused(36);
304 
305  if (depthwise)
306  {
307  outputDataNotFused.resize(108);
308  outputData2NotFused.resize(108);
309  }
310 
311  TensorInfo inputTensorInfo2 = runNotFused->GetInputTensorInfo(networkIdentifierNotFused, 0);
312  inputTensorInfo2.SetConstant(true);
313  InputTensors inputTensorsNotFused{
314  { 0, ConstTensor(inputTensorInfo2, inputDataNotFused.data()) } };
315  OutputTensors outputTensorsNotFused{
316  { 0, Tensor(runNotFused->GetOutputTensorInfo(networkIdentifierNotFused, 0), outputDataNotFused.data()) },
317  { 1, Tensor(runNotFused->GetOutputTensorInfo(networkIdentifierNotFused, 1), outputData2NotFused.data()) } };
318 
319  // Execute network
320  runNotFused->EnqueueWorkload(networkIdentifierNotFused, inputTensorsNotFused, outputTensorsNotFused);
321 
322  // Check the output of the fused-convolution matches with the output of the batchNormm in the "NotFused" network
323  auto epsilon = T(tolerance);
324  for (unsigned int n = 0; n < outputDataFused.size(); ++n)
325  {
326  CHECK_EQ(outputDataFused[n], doctest::Approx(outputDataNotFused[n]).epsilon(epsilon));
327  }
328 }
329 
330 // This unit test needs the reference backend, it's not available if the reference backend is not built
331 #if defined(ARMNNREF_ENABLED)
332 TEST_CASE("FuseBatchNormIntoConv2DFloat32Test")
333 {
334  FuseBatchNormIntoConvTest<Conv2dTest, DataType::Float32>(false, 0.0001f, armnn::Compute::CpuRef);
335 }
336 
337 TEST_CASE("FuseBatchNormIntoConv2DFloat16Test")
338 {
339  FuseBatchNormIntoConvTest<Conv2dTest, DataType::Float16>(false, 0.1f, armnn::Compute::CpuRef);
340 }
341 
342 TEST_CASE("FuseBatchNormIntoDepthwiseConv2DFloat32Test")
343 {
344  FuseBatchNormIntoConvTest<DepthwiseConv2dTest, DataType::Float32>(true, 0.0001f,armnn::Compute::CpuRef);
345 }
346 
347 TEST_CASE("FuseBatchNormIntoDepthwiseConv2DFloat16Test")
348 {
349  FuseBatchNormIntoConvTest<DepthwiseConv2dTest, DataType::Float16>(true, 0.2f,armnn::Compute::CpuRef);
350 }
351 #endif
352 
353 }
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:66
bool m_BiasEnabled
Enable/disable bias.
CPU Execution: Reference C++ kernels.
bool CheckSequence(const armnn::Graph::ConstIterator first, const armnn::Graph::ConstIterator last)
Definition: TestUtils.hpp:21
IConnectableLayer * AddConstantLayer(const ConstTensor &input, const char *name=nullptr)
Adds a layer with no inputs and a single output, which always corresponds to the passed in constant t...
Definition: Network.cpp:292
#define ARMNN_NO_DEPRECATE_WARN_BEGIN
Definition: Deprecated.hpp:33
IConnectableLayer * AddDepthwiseConvolution2dLayer(const DepthwiseConvolution2dDescriptor &convolution2dDescriptor, const char *name=nullptr)
Adds a 2D depthwise convolution layer to the network.
Definition: Network.cpp:118
This layer represents a depthwise convolution 2d operation.
A Convolution2dDescriptor for the Convolution2dLayer.
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:33
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
typename ResolveTypeImpl< DT >::Type ResolveType
Definition: ResolveType.hpp:79
Main network class which provides the interface for building up a neural network. ...
Definition: INetwork.hpp:249
std::vector< std::pair< LayerBindingId, class ConstTensor > > InputTensors
Definition: Tensor.hpp:392
void IgnoreUnused(Ts &&...)
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
A tensor defined by a TensorInfo (shape and data type) and a mutable backing store.
Definition: Tensor.hpp:319
Compute
The Compute enum is now deprecated and it is now being replaced by BackendId.
Definition: BackendId.hpp:21
DataType
Definition: Types.hpp:48
#define ARMNN_NO_DEPRECATE_WARN_END
Definition: Deprecated.hpp:34
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:1847
int NetworkId
Definition: IRuntime.hpp:27
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:327
std::vector< std::pair< LayerBindingId, class Tensor > > OutputTensors
Definition: Tensor.hpp:393
std::unique_ptr< IOptimizedNetwork, void(*)(IOptimizedNetwork *network)> IOptimizedNetworkPtr
Definition: INetwork.hpp:242
const TensorInfo & GetInfo() const
Definition: Tensor.hpp:295
Graph & GetGraphForTesting(IOptimizedNetwork *optNet)
Definition: TestUtils.cpp:49
IConnectableLayer * AddConvolution2dLayer(const Convolution2dDescriptor &convolution2dDescriptor, const char *name=nullptr)
Adds a 2D convolution layer to the network.
Definition: Network.cpp:85
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
void SetConstant(const bool IsConstant=true)
Marks the data corresponding to this tensor info as constant.
Definition: Tensor.cpp:514
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
This layer represents a convolution 2d operation.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:241
virtual int Connect(IInputSlot &destination)=0
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
A BatchNormalizationDescriptor for the BatchNormalizationLayer.