From 3a7d3a70d99cbe22f5e4711d5dbbea2a245da7ed Mon Sep 17 00:00:00 2001 From: Rob Hughes Date: Tue, 24 Sep 2019 16:59:56 +0100 Subject: NNXSW-1826 Add an optimization step which combines Permute and BatchToSpace into DepthToSpace This is only possible in some limited cases, but removes an extra layer from the graph and so should improve performance in all cases. Change-Id: I7b3e6ba5dacb4fdb816ad270edaecda1436ab4cf Signed-off-by: Rob Hughes --- .../PermuteAndBatchToSpaceAsDepthToSpaceTests.cpp | 132 +++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/armnn/test/optimizations/PermuteAndBatchToSpaceAsDepthToSpaceTests.cpp (limited to 'src/armnn/test/optimizations/PermuteAndBatchToSpaceAsDepthToSpaceTests.cpp') diff --git a/src/armnn/test/optimizations/PermuteAndBatchToSpaceAsDepthToSpaceTests.cpp b/src/armnn/test/optimizations/PermuteAndBatchToSpaceAsDepthToSpaceTests.cpp new file mode 100644 index 0000000000..ec1dd511c9 --- /dev/null +++ b/src/armnn/test/optimizations/PermuteAndBatchToSpaceAsDepthToSpaceTests.cpp @@ -0,0 +1,132 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "../TestUtils.hpp" + +#include +#include + +#include + +using namespace armnn; + +BOOST_AUTO_TEST_SUITE(Optimizer) +using namespace armnn::optimizations; + +namespace +{ + +/// Shared function for the below tests, so that we test the same network in both cases. +INetworkPtr CreateTestNetwork() +{ + // Create a network + INetworkPtr network = INetwork::Create(); + + auto input = network->AddInputLayer(0, "input"); + const TensorInfo inputInfo({ 1, 2, 3, 4 }, DataType::Float32); + input->GetOutputSlot(0).SetTensorInfo(inputInfo); + + // Insert Permute which swaps batches and channels dimensions + auto permute = network->AddPermuteLayer(PermuteDescriptor(PermutationVector{ 3, 1, 2, 0 }), "permute"); + const TensorInfo permuteInfo({ 4, 2, 3, 1 }, DataType::Float32); + permute->GetOutputSlot(0).SetTensorInfo(permuteInfo); + input->GetOutputSlot(0).Connect(permute->GetInputSlot(0)); + + // Insert BatchToSpace + BatchToSpaceNdDescriptor batchToSpaceDesc; + batchToSpaceDesc.m_BlockShape = { 2, 2 }; + batchToSpaceDesc.m_DataLayout = DataLayout::NHWC; + auto batchToSpace = network->AddBatchToSpaceNdLayer(batchToSpaceDesc, "batchToSpace"); + const TensorInfo batchToSpaceInfo({ 1, 4, 6, 1 }, DataType::Float32); + batchToSpace->GetOutputSlot(0).SetTensorInfo(batchToSpaceInfo); + permute->GetOutputSlot(0).Connect(batchToSpace->GetInputSlot(0)); + + auto output = network->AddOutputLayer(0, "output"); + batchToSpace->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + return network; +} + +} // namespace + +/// Tests that the optimization performed by PermuteAndBatchToSpaceAsDepthToSpace is as expected. +/// Note this does not ensure the correctness of the optimization - that is done in the below test. +BOOST_AUTO_TEST_CASE(PermuteAndBatchToSpaceAsDepthToSpaceOptimizerTest) +{ + INetworkPtr network = CreateTestNetwork(); + Graph graph = static_cast(network.get())->GetGraph(); + + // Confirm initial graph is as we expect + BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(), &IsLayerOfType, &IsLayerOfType, + &IsLayerOfType, &IsLayerOfType)); + + // Perform the optimization which should merge the two layers into a DepthToSpace + armnn::Optimizer::Pass(graph, MakeOptimizations(PermuteAndBatchToSpaceAsDepthToSpace())); + + // Check that the replacement has been made as expected + auto checkDepthToSpace = [](const Layer* const layer) -> bool { + return IsLayerOfType(layer) && + static_cast(layer)->GetParameters().m_BlockSize == 2 && + static_cast(layer)->GetParameters().m_DataLayout == DataLayout::NHWC && + layer->GetOutputHandler().GetTensorInfo() == TensorInfo({ 1, 4, 6, 1 }, DataType::Float32); + }; + + BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(), &IsLayerOfType, checkDepthToSpace, + &IsLayerOfType)); + + // Check the new layer has the two merged layers listed as related layers + std::list testRelatedLayers = { "batchToSpace", "permute" }; + BOOST_TEST(CheckRelatedLayers(graph, testRelatedLayers)); +} + +/// Tests that a optimization performed by PermuteAndBatchToSpaceAsDepthToSpace does not change the behaviour +/// of the network (i.e. it still produces the correct output). +BOOST_AUTO_TEST_CASE(PermuteAndBatchToSpaceAsDepthToSpaceCorrectnessTest) +{ + INetworkPtr network = CreateTestNetwork(); + + IRuntimePtr runtime = IRuntime::Create(IRuntime::CreationOptions()); + + IOptimizedNetworkPtr optimizedNetwork = Optimize(*network, { Compute::CpuRef }, runtime->GetDeviceSpec()); + + // Confirm that the optimization has actually taken place + const Graph& optGraph = static_cast(optimizedNetwork.get())->GetGraph(); + BOOST_TEST(CheckSequence(optGraph.cbegin(), optGraph.cend(), &IsLayerOfType, + &IsLayerOfType, &IsLayerOfType)); + + // Load the graph into a runtime so we can check it produces the correct output + NetworkId netId; + runtime->LoadNetwork(netId, std::move(optimizedNetwork)); + + std::vector inputData{ + // Each row here is a row of pixels where each pixel has 4 channels + // clang-format off + 1.0f, 2.0f, 3.0f, 4.0f, 10.0f, 20.0f, 30.0f, 40.0f, 100.0f, 200.0f, 300.0f, 400.0f, + -1.0f, -2.0f, -3.0f, -4.0f, -10.0f, -20.0f, -30.0f, -40.0f, -100.0f, -200.0f, -300.0f, -400.0f, + // clang-format on + }; + ConstTensor input(TensorInfo({ 1, 2, 3, 4 }, DataType::Float32), inputData); + InputTensors inputs = { { 0, input } }; + std::vector outputData(4 * 6); + Tensor output(TensorInfo({ 1, 4, 6, 1 }, DataType::Float32), outputData.data()); + OutputTensors outputs = { { 0, output } }; + runtime->EnqueueWorkload(netId, inputs, outputs); + + // Check the output is as expected. + // Note this output has been generated by running the network *without* the optimization. + std::vector expectedOutput = { + // Rows and columns here match exactly with the tensor, as there is only 1 channel. + // clang-format off + 1.0f, 2.0f, 10.0f, 20.0f, 100.0f, 200.0f, + 3.0f, 4.0f, 30.0f, 40.0f, 300.0f, 400.0f, + + -1.0f, -2.0f, -10.0f, -20.0f, -100.0f, -200.0f, + -3.0f, -4.0f, -30.0f, -40.0f, -300.0f, -400.0f, + // clang-format on + }; + BOOST_TEST(outputData == expectedOutput); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file -- cgit v1.2.1