From 81beae3a870004795275e9266bc43d845b9f78db Mon Sep 17 00:00:00 2001 From: Matthew Sloyan Date: Tue, 13 Jul 2021 19:46:11 +0100 Subject: IVGCVSW-6119 ConstTensorsAsInput: FullyConnected * Constant weights and biases are now stored as Constant layers. * Updated Serializer, Deserializer and unit tests to reflect this. * Updated TfLiteDelegate, TfLiteParser and OnnxParser. * Updated Schema with IsConstant and ConstantTensorsAsInputs. * Updated Ref backend to handle constant weights and bias as inputs rather than reading from member variables. * Added dynamic or constant input EndToEnd tests. !android-nn-driver:5959 Signed-off-by: Matthew Sloyan Change-Id: Ibf3cf437df1100e4b322b0d303c575c6339f9696 --- src/armnn/BackendHelper.cpp | 47 ++++-- src/armnn/Descriptors.cpp | 16 +- src/armnn/Network.cpp | 128 +++++++------- src/armnn/Network.hpp | 16 +- src/armnn/layers/FullyConnectedLayer.cpp | 79 ++------- src/armnn/layers/FullyConnectedLayer.hpp | 2 + src/armnn/test/ConstTensorLayerVisitor.cpp | 52 ++++-- src/armnn/test/ConstTensorLayerVisitor.hpp | 10 -- src/armnn/test/CreateWorkload.hpp | 85 ++++++++-- src/armnn/test/GraphTests.cpp | 8 +- src/armnn/test/NetworkTests.cpp | 36 +++- src/armnn/test/ShapeInferenceTests.cpp | 18 +- .../test/optimizations/FuseActivationTests.cpp | 185 ++++++++++++++++++--- 13 files changed, 441 insertions(+), 241 deletions(-) (limited to 'src/armnn') diff --git a/src/armnn/BackendHelper.cpp b/src/armnn/BackendHelper.cpp index 13bde0aafa..9ab30f8fb2 100644 --- a/src/armnn/BackendHelper.cpp +++ b/src/armnn/BackendHelper.cpp @@ -5,6 +5,7 @@ #include #include +#include #include @@ -399,22 +400,48 @@ bool LayerSupportHandle::IsFullyConnectedSupported(const TensorInfo& input, const FullyConnectedDescriptor& descriptor, Optional reasonIfUnsupported) { - if(!descriptor.m_ConstantWeights && !m_BackendId.IsUndefined()) + if(!m_BackendId.IsUndefined()) { - auto capability = GetCapability("NonConstWeights", m_BackendId); - if (capability.has_value() && capability.value().GetValue().AsBool() == true) + auto capability = GetCapability("ConstantTensorsAsInputs", m_BackendId); + if(!capability.has_value() || capability.value().GetValue().AsBool() == false) { - return true; + if(!weights.IsConstant()) + { + return false; + } + if(descriptor.m_BiasEnabled) + { + if(!biases.IsConstant()) + { + return false; + } + } + + // At the first stage we will only print a warning. this is to give + // backend developers a chance to adopt and read weights from input slots. + ARMNN_LOG(warning) << "The backend makes use of a deprecated interface to read constant tensors. " + "If you are a backend developer please find more information in our " + "doxygen documentation on github https://github.com/ARM-software/armnn " + "under the keyword 'ConstTensorsAsInputs'."; + } + + if(!descriptor.m_ConstantWeights) + { + auto capability = GetCapability("NonConstWeights", m_BackendId); + if (capability.has_value() && capability.value().GetValue().AsBool() == true) + { + return true; + } + return false; } - return false; } return m_LayerSupport->IsFullyConnectedSupported(input, - output, - weights, - biases, - descriptor, - reasonIfUnsupported.value()); + output, + weights, + biases, + descriptor, + reasonIfUnsupported.value()); } bool LayerSupportHandle::IsGatherSupported(const TensorInfo& input0, diff --git a/src/armnn/Descriptors.cpp b/src/armnn/Descriptors.cpp index 706992ccb0..4521894c28 100644 --- a/src/armnn/Descriptors.cpp +++ b/src/armnn/Descriptors.cpp @@ -425,19 +425,13 @@ int StridedSliceDescriptor::GetStopForAxis(const TensorShape& inputShape, } -uint32_t FullyConnectedDescriptor::GetNumViews() const +uint32_t FullyConnectedDescriptor::GetNumInputs() const { - // Return 1 with constant weights, otherwise check if bias is enabled - uint32_t numInputs = 1; - if (!m_ConstantWeights) + // Return 2 otherwise check if bias is enabled + unsigned int numInputs = 2; + if (m_BiasEnabled) { - // non-const weights - numInputs = 2; - if (m_BiasEnabled) - { - // non-const bias - numInputs = 3; - } + numInputs = 3; } return numInputs; } diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp index 83eafe7993..a29ce83c5a 100644 --- a/src/armnn/Network.cpp +++ b/src/armnn/Network.cpp @@ -30,6 +30,8 @@ #include +#include + #include #include #include @@ -178,38 +180,22 @@ IConnectableLayer* INetwork::AddFillLayer(const FillDescriptor& fillDescriptor, } IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const ConstTensor& weights, - const Optional& biases, const char* name) { - return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, - armnn::Optional(weights), - biases, - name); + return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, name); } IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, const ConstTensor& weights, + const Optional& biases, const char* name) { - armnn::Optional biases; return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, armnn::Optional(weights), biases, name); } -IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const ConstTensor& weights, - const ConstTensor& biases, - const char* name) -{ - return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, - armnn::Optional(weights), - armnn::Optional(biases), - name); -} - IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, const Optional& weights, const Optional& biases, @@ -1799,69 +1785,87 @@ IConnectableLayer* NetworkImpl::AddFillLayer(const FillDescriptor& fillDescripto return m_Graph->AddLayer(fillDescriptor, name); } -IConnectableLayer* NetworkImpl::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const Optional& weights, - const Optional& biases, - const char* name) +IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, + const char* name) +{ + return m_Graph->AddLayer(fullyConnectedDescriptor, name); +} + +IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, + const Optional& weights, + const Optional& biases, + const char* name) { - if (fullyConnectedDescriptor.m_ConstantWeights && !weights.has_value()) + ConstantLayer* weightsLayer = nullptr; + ConstantLayer* biasLayer = nullptr; + unsigned int numInputs = fullyConnectedDescriptor.GetNumInputs(); + + // Add a constant layer for weights + if (weights.has_value()) { - throw InvalidArgumentException("AddFullyConnectedLayer: weights cannot be empty"); + weightsLayer = m_Graph->AddLayer("Weights"); + weightsLayer->m_LayerOutput = std::make_shared(weights.value()); + weightsLayer->GetOutputSlot(0).SetTensorInfo(weightsLayer->m_LayerOutput->GetTensorInfo()); + } + else if (fullyConnectedDescriptor.m_ConstantWeights) + { + throw InvalidArgumentException("AddFullyConnectedLayer: Constant weights tensor is empty."); + } - if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value()) - { - throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty"); - } + // Add a constant layer for biases + if (biases.has_value() && fullyConnectedDescriptor.m_BiasEnabled) + { + biasLayer = m_Graph->AddLayer("Biases"); + biasLayer->m_LayerOutput = std::make_shared(biases.value()); + biasLayer->GetOutputSlot(0).SetTensorInfo(biasLayer->m_LayerOutput->GetTensorInfo()); } - const auto layer = m_Graph->AddLayer(fullyConnectedDescriptor, name); + if (numInputs < 2) + { + throw InvalidArgumentException("AddFullyConnectedLayer: Requires at least 2 input tensors: Input, Weights"); + } + + auto layer = m_Graph->AddLayer(fullyConnectedDescriptor, name); + + if (weightsLayer) + { + // Connect weights layer + weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1)); + } - if (fullyConnectedDescriptor.m_ConstantWeights) + if ( fullyConnectedDescriptor.m_BiasEnabled && numInputs == 3 ) { - layer->m_Weight = std::make_shared(weights.value()); - if (fullyConnectedDescriptor.m_BiasEnabled) + if (biasLayer) { - layer->m_Bias = std::make_shared(biases.value()); + // Connect bias layer + biasLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2)); } } + else if ( !fullyConnectedDescriptor.m_BiasEnabled && numInputs == 2 ) + { + // Bias is disabled + layer->m_Bias = nullptr; + } + else + { + throw InvalidArgumentException(fmt::format( + "AddFullyConnectedLayer: Value mismatch. When bias is enabled in the " + "descriptor the number of inputs is expected to be 3 otherwise 2. " + "BiasEnabled={}, numInputs={}", + fullyConnectedDescriptor.m_BiasEnabled, + numInputs)); + } return layer; } IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const Optional& weights, + const ConstTensor& weights, const Optional& biases, const char* name) -{ - return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name); -} - -IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const ConstTensor& weights, - const Optional& biases, - const char* name) { Optional optionalWeights(weights); - return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, biases, name); -} - -IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const ConstTensor& weights, - const char* name) -{ - Optional optionalWeights(weights); - Optional biases; - return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, biases, name); -} - -IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const ConstTensor& weights, - const ConstTensor& biases, - const char* name) -{ - Optional optionalWeights(weights); - Optional optionalBiases(biases); - return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, optionalBiases, name); + return AddFullyConnectedLayer(fullyConnectedDescriptor, optionalWeights, biases, name); } IConnectableLayer* NetworkImpl::AddConcatLayer(const ConcatDescriptor& concatDescriptor, diff --git a/src/armnn/Network.hpp b/src/armnn/Network.hpp index 54c3497c90..c22c865e3b 100644 --- a/src/armnn/Network.hpp +++ b/src/armnn/Network.hpp @@ -133,24 +133,17 @@ public: IConnectableLayer* AddFloorLayer(const char* name = nullptr); IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const Optional& weights, - const Optional& biases, const char* name = nullptr); IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const ConstTensor& weights, + const Optional& weights, const Optional& biases, const char* name = nullptr); ARMNN_DEPRECATED_MSG("This AddFullyConnectedLayer overload is deprecated") IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, const ConstTensor& weights, - const char* name = nullptr); - - ARMNN_DEPRECATED_MSG("This AddFullyConnectedLayer overload is deprecated") - IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const ConstTensor& weights, - const ConstTensor& biases, + const Optional& biases, const char* name = nullptr); ARMNN_DEPRECATED_MSG("This AddGatherLayer overload is deprecated") @@ -288,11 +281,6 @@ private: const Optional& biases, const char* name); - IConnectableLayer* AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor, - const Optional& weights, - const Optional& biases, - const char* name); - bool GetShapeInferenceMethod(); NetworkOptions m_NetworkOptions; diff --git a/src/armnn/layers/FullyConnectedLayer.cpp b/src/armnn/layers/FullyConnectedLayer.cpp index 9d4f57d260..8dfb011730 100644 --- a/src/armnn/layers/FullyConnectedLayer.cpp +++ b/src/armnn/layers/FullyConnectedLayer.cpp @@ -15,24 +15,20 @@ namespace armnn { FullyConnectedLayer::FullyConnectedLayer(const FullyConnectedDescriptor& param, const char* name) - : LayerWithParameters(param.GetNumViews(), 1, LayerType::FullyConnected, param, name) + : LayerWithParameters(param.GetNumInputs(), 1, LayerType::FullyConnected, param, name) { } std::unique_ptr FullyConnectedLayer::CreateWorkload(const IWorkloadFactory& factory) const { - // on this level constant data should not be released.. FullyConnectedQueueDescriptor descriptor; - if (m_Param.m_ConstantWeights) + if (m_Weight) { - ARMNN_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null."); descriptor.m_Weight = m_Weight.get(); - - if (m_Param.m_BiasEnabled) - { - ARMNN_ASSERT_MSG(m_Bias != nullptr, "FullyConnectedLayer: Bias data should not be null."); - descriptor.m_Bias = m_Bias.get(); - } + } + if (m_Param.m_BiasEnabled && m_Bias) + { + descriptor.m_Bias = m_Bias.get(); } SetAdditionalInfo(descriptor); @@ -42,15 +38,6 @@ std::unique_ptr FullyConnectedLayer::CreateWorkload(const IWorkloadFa FullyConnectedLayer* FullyConnectedLayer::Clone(Graph& graph) const { auto layer = CloneBase(graph, m_Param, GetName()); - if (m_Param.m_ConstantWeights) - { - layer->m_Weight = m_Weight ? m_Weight : nullptr; - - if (layer->m_Param.m_BiasEnabled) - { - layer->m_Bias = m_Bias ? m_Bias : nullptr; - } - } return std::move(layer); } @@ -73,20 +60,9 @@ void FullyConnectedLayer::ValidateTensorShapesFromInputs() VerifyShapeInferenceType(outputShape, m_ShapeInferenceMethod); - std::vector inferredShapes; - if (m_Param.m_ConstantWeights) - { - // check if m_Weight data is not nullptr - ARMNN_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null."); - - inferredShapes = InferOutputShapes({GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(), - m_Weight->GetTensorInfo().GetShape()}); - } - else - { - inferredShapes = InferOutputShapes({GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(), - GetInputSlot(1).GetConnection()->GetTensorInfo().GetShape()}); - } + std::vector inferredShapes = InferOutputShapes( + {GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(), + GetInputSlot(1).GetConnection()->GetTensorInfo().GetShape()}); ARMNN_ASSERT(inferredShapes.size() == 1); ARMNN_ASSERT(inferredShapes[0].GetDimensionality() == Dimensionality::Specified); @@ -101,45 +77,12 @@ Layer::ConstantTensors FullyConnectedLayer::GetConstantTensorsByRef() void FullyConnectedLayer::Accept(ILayerVisitor& visitor) const { - Optional optionalWeightsTensor = EmptyOptional(); - Optional optionalBiasTensor = EmptyOptional(); - - ManagedConstTensorHandle managedWeight(m_Weight); - ManagedConstTensorHandle managedBias(m_Bias); - if (GetParameters().m_ConstantWeights) - { - ConstTensor weightsTensor(managedWeight.GetTensorInfo(), managedWeight.Map()); - optionalWeightsTensor = Optional(weightsTensor); - - if (GetParameters().m_BiasEnabled) - { - ConstTensor biasTensor(managedBias.GetTensorInfo(), managedBias.Map()); - optionalBiasTensor = Optional(biasTensor); - } - } - - visitor.VisitFullyConnectedLayer(this, - GetParameters(), - optionalWeightsTensor.value(), - optionalBiasTensor, - GetName()); + visitor.VisitFullyConnectedLayer(this, GetParameters(), GetName()); } void FullyConnectedLayer::ExecuteStrategy(IStrategy& strategy) const { - std::vector constTensors; - ManagedConstTensorHandle managedWeight(m_Weight); - ManagedConstTensorHandle managedBias(m_Bias); - - if(GetParameters().m_ConstantWeights) - { - constTensors.emplace_back(ConstTensor(managedWeight.GetTensorInfo(), managedWeight.Map())); - if (GetParameters().m_BiasEnabled) - { - constTensors.emplace_back(ConstTensor(managedBias.GetTensorInfo(), managedBias.Map())); - } - } - strategy.ExecuteStrategy(this, GetParameters(), constTensors, GetName()); + strategy.ExecuteStrategy(this, GetParameters(), {}, GetName()); } } // namespace armnn diff --git a/src/armnn/layers/FullyConnectedLayer.hpp b/src/armnn/layers/FullyConnectedLayer.hpp index 7fc7b0d596..5639bf27b4 100644 --- a/src/armnn/layers/FullyConnectedLayer.hpp +++ b/src/armnn/layers/FullyConnectedLayer.hpp @@ -16,8 +16,10 @@ class FullyConnectedLayer : public LayerWithParameters { public: /// A unique pointer to store Weight values. + /// @Note: Deprecated. Weights are stored in ConstantLayers now. std::shared_ptr m_Weight; /// A unique pointer to store Bias values. + /// @Note: Deprecated. Bias are stored in ConstantLayers now. std::shared_ptr m_Bias; /// Makes a workload for the FullyConnected type. diff --git a/src/armnn/test/ConstTensorLayerVisitor.cpp b/src/armnn/test/ConstTensorLayerVisitor.cpp index baafcf41ef..d3d8698972 100644 --- a/src/armnn/test/ConstTensorLayerVisitor.cpp +++ b/src/armnn/test/ConstTensorLayerVisitor.cpp @@ -484,16 +484,23 @@ TEST_CASE("CheckFullyConnectedLayer") { FullyConnectedDescriptor descriptor; descriptor.m_TransposeWeightMatrix = true; + descriptor.m_ConstantWeights = true; + descriptor.m_BiasEnabled = false; std::vector data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; std::vector dimensions = {1, 1, 3, 3}; ConstTensor weights(TensorInfo(4, dimensions.data(), DataType::Float32), data); - TestFullyConnectedLayerVistor visitor(descriptor, weights, EmptyOptional()); + TestConstantLayerVisitor weightsVisitor(weights); + TestFullyConnectedLayerVistor visitor(descriptor); NetworkImpl net; - IConnectableLayer* const layer = net.AddFullyConnectedLayer(descriptor, weights, EmptyOptional()); + IConnectableLayer* const weightsLayer = net.AddConstantLayer(weights); + IConnectableLayer* const layer = net.AddFullyConnectedLayer(descriptor); + weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1)); + + weightsLayer->Accept(weightsVisitor); layer->Accept(visitor); } @@ -502,16 +509,23 @@ TEST_CASE("CheckNamedFullyConnectedLayer") const char* layerName = "FullyConnectedLayer"; FullyConnectedDescriptor descriptor; descriptor.m_TransposeWeightMatrix = true; + descriptor.m_ConstantWeights = true; + descriptor.m_BiasEnabled = false; std::vector data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; std::vector dimensions = {1, 1, 3, 3}; ConstTensor weights(TensorInfo(4, dimensions.data(), DataType::Float32), data); - TestFullyConnectedLayerVistor visitor(descriptor, weights, EmptyOptional(), layerName); + TestConstantLayerVisitor weightsVisitor(weights); + TestFullyConnectedLayerVistor visitor(descriptor, layerName); NetworkImpl net; - IConnectableLayer* const layer = net.AddFullyConnectedLayer(descriptor, weights, EmptyOptional(), layerName); + IConnectableLayer* const weightsLayer = net.AddConstantLayer(weights); + IConnectableLayer* const layer = net.AddFullyConnectedLayer(descriptor, layerName); + weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1)); + + weightsLayer->Accept(weightsVisitor); layer->Accept(visitor); } @@ -519,6 +533,7 @@ TEST_CASE("CheckFullyConnectedLayerWithBiases") { FullyConnectedDescriptor descriptor; descriptor.m_TransposeWeightMatrix = true; + descriptor.m_ConstantWeights = true; descriptor.m_BiasEnabled = true; std::vector data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; @@ -528,13 +543,21 @@ TEST_CASE("CheckFullyConnectedLayerWithBiases") std::vector biasData = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; std::vector biasDimensions = {1, 1, 3, 3}; ConstTensor biases(TensorInfo(4, biasDimensions.data(), DataType::Float32), biasData); - Optional optionalBiases(biases); - TestFullyConnectedLayerVistor visitor(descriptor, weights, optionalBiases); + TestConstantLayerVisitor weightsVisitor(weights); + TestConstantLayerVisitor biasesVisitor(biases); + TestFullyConnectedLayerVistor visitor(descriptor); NetworkImpl net; - IConnectableLayer* const layer = net.AddFullyConnectedLayer(descriptor, weights, optionalBiases); + IConnectableLayer* const weightsLayer = net.AddConstantLayer(weights); + IConnectableLayer* const biasesLayer = net.AddConstantLayer(biases); + IConnectableLayer* const layer = net.AddFullyConnectedLayer(descriptor); + weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1)); + biasesLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2)); + + weightsLayer->Accept(weightsVisitor); + biasesLayer->Accept(biasesVisitor); layer->Accept(visitor); } @@ -543,6 +566,7 @@ TEST_CASE("CheckNamedFullyConnectedLayerWithBiases") const char* layerName = "FullyConnectedLayer"; FullyConnectedDescriptor descriptor; descriptor.m_TransposeWeightMatrix = true; + descriptor.m_ConstantWeights = true; descriptor.m_BiasEnabled = true; std::vector data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; @@ -552,13 +576,21 @@ TEST_CASE("CheckNamedFullyConnectedLayerWithBiases") std::vector biasData = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; std::vector biasDimensions = {1, 1, 3, 3}; ConstTensor biases(TensorInfo(4, biasDimensions.data(), DataType::Float32), biasData); - Optional optionalBiases(biases); - TestFullyConnectedLayerVistor visitor(descriptor, weights, optionalBiases, layerName); + TestConstantLayerVisitor weightsVisitor(weights); + TestConstantLayerVisitor biasesVisitor(biases); + TestFullyConnectedLayerVistor visitor(descriptor, layerName); NetworkImpl net; - IConnectableLayer* const layer = net.AddFullyConnectedLayer(descriptor, weights, optionalBiases, layerName); + IConnectableLayer* const weightsLayer = net.AddConstantLayer(weights); + IConnectableLayer* const biasesLayer = net.AddConstantLayer(biases); + IConnectableLayer* const layer = net.AddFullyConnectedLayer(descriptor, layerName); + weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1)); + biasesLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2)); + + weightsLayer->Accept(weightsVisitor); + biasesLayer->Accept(biasesVisitor); layer->Accept(visitor); } diff --git a/src/armnn/test/ConstTensorLayerVisitor.hpp b/src/armnn/test/ConstTensorLayerVisitor.hpp index e423e0f6e3..35e2e872f7 100644 --- a/src/armnn/test/ConstTensorLayerVisitor.hpp +++ b/src/armnn/test/ConstTensorLayerVisitor.hpp @@ -90,36 +90,26 @@ class TestFullyConnectedLayerVistor : public TestLayerVisitor { public: explicit TestFullyConnectedLayerVistor(const FullyConnectedDescriptor& descriptor, - const ConstTensor& weights, - const Optional biases, const char* name = nullptr) : TestLayerVisitor(name) , m_Descriptor(descriptor) - , m_Weights(weights) - , m_Biases(biases) {} virtual ~TestFullyConnectedLayerVistor() {} void VisitFullyConnectedLayer(const IConnectableLayer* layer, const FullyConnectedDescriptor& fullyConnectedDescriptor, - const ConstTensor& weights, - const Optional& biases, const char* name = nullptr) override { CheckLayerPointer(layer); CheckLayerName(name); CheckDescriptor(fullyConnectedDescriptor); - CheckConstTensors(m_Weights, weights); - CheckOptionalConstTensors(m_Biases, biases); } protected: void CheckDescriptor(const FullyConnectedDescriptor& descriptor); private: FullyConnectedDescriptor m_Descriptor; - ConstTensor m_Weights; - Optional m_Biases; }; class TestBatchNormalizationLayerVisitor : public TestLayerVisitor diff --git a/src/armnn/test/CreateWorkload.hpp b/src/armnn/test/CreateWorkload.hpp index b07e3b80a5..759ada97cd 100644 --- a/src/armnn/test/CreateWorkload.hpp +++ b/src/armnn/test/CreateWorkload.hpp @@ -1193,7 +1193,7 @@ std::unique_ptr CreateFullyConnectedWorkloadTest(armnn:: { // Creates the layer we're testing. FullyConnectedDescriptor layerDesc; - layerDesc.m_BiasEnabled = true; + layerDesc.m_BiasEnabled = false; layerDesc.m_TransposeWeightMatrix = true; FullyConnectedLayer* const layer = graph.AddLayer(layerDesc, "layer"); @@ -1201,17 +1201,24 @@ std::unique_ptr CreateFullyConnectedWorkloadTest(armnn:: float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0; float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0; + // As optimization isn't run member variables need to be updated. layer->m_Weight = std::make_unique(TensorInfo({7, 20}, DataType, inputsQScale, 0)); - layer->m_Bias = std::make_unique(TensorInfo({7}, GetBiasDataType(DataType), inputsQScale)); layer->m_Weight->Allocate(); - layer->m_Bias->Allocate(); + + armnn::TensorInfo weightsTensorInfo({7, 20}, DataType, inputsQScale); + weightsTensorInfo.SetConstant(); // Creates extra layers. Layer* const input = graph.AddLayer(0, "input"); + auto const weights = graph.AddLayer("weights"); Layer* const output = graph.AddLayer(0, "output"); + weights->m_LayerOutput = std::make_unique(weightsTensorInfo); + weights->m_LayerOutput->Allocate(); + // Connects up. - Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale)); + Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale), 0, 0); + Connect(weights, layer, weightsTensorInfo, 0, 1); Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale)); CreateTensorHandles(graph, factory); @@ -1219,13 +1226,10 @@ std::unique_ptr CreateFullyConnectedWorkloadTest(armnn:: auto workload = MakeAndCheckWorkload(*layer, factory); FullyConnectedQueueDescriptor queueDescriptor = workload->GetData(); - CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == true); CHECK(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true); - CHECK(queueDescriptor.m_Inputs.size() == 1); + CHECK(queueDescriptor.m_Inputs.size() == 2); CHECK(queueDescriptor.m_Outputs.size() == 1); - CHECK((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({7, 20}, DataType, inputsQScale))); - CHECK((queueDescriptor.m_Bias->GetTensorInfo() == TensorInfo({7}, GetBiasDataType(DataType), inputsQScale))); // Returns so we can do extra, backend-specific tests. return workload; @@ -1246,11 +1250,17 @@ std::unique_ptr CreateFullyConnectedWithBlobWorkloadTest float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0; float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0; + // As optimization isn't run member variables need to be updated. layer->m_Weight = std::make_unique(TensorInfo({7, 20}, DataType, inputsQScale, 0)); layer->m_Bias = std::make_unique(TensorInfo({7}, GetBiasDataType(DataType), inputsQScale)); layer->m_Weight->Allocate(); layer->m_Bias->Allocate(); + armnn::TensorInfo weightsTensorInfo({7, 20}, DataType, inputsQScale); + armnn::TensorInfo biasesTensorInfo({7}, GetBiasDataType(DataType), inputsQScale); + weightsTensorInfo.SetConstant(); + biasesTensorInfo.SetConstant(); + auto activationDesc = std::make_shared(); activationDesc->m_A = 10.0f; activationDesc->m_B = 5.0f; @@ -1267,10 +1277,19 @@ std::unique_ptr CreateFullyConnectedWithBlobWorkloadTest // Creates extra layers. Layer* const input = graph.AddLayer(0, "input"); + auto const weights = graph.AddLayer("weights"); + auto const biases = graph.AddLayer("biases"); Layer* const output = graph.AddLayer(0, "output"); + weights->m_LayerOutput = std::make_unique(weightsTensorInfo); + weights->m_LayerOutput->Allocate(); + biases->m_LayerOutput = std::make_unique(biasesTensorInfo); + biases->m_LayerOutput->Allocate(); + // Connects up. - Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale)); + Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale), 0, 0); + Connect(weights, layer, weightsTensorInfo, 0, 1); + Connect(biases, layer, biasesTensorInfo, 0, 2); Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale)); CreateTensorHandles(graph, factory); @@ -1290,10 +1309,52 @@ std::unique_ptr CreateFullyConnectedWithBlobWorkloadTest CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == true); CHECK(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true); - CHECK(queueDescriptor.m_Inputs.size() == 1); + CHECK(queueDescriptor.m_Inputs.size() == 3); + CHECK(queueDescriptor.m_Outputs.size() == 1); + + // Returns so we can do extra, backend-specific tests. + return workload; +} + +template +std::unique_ptr CreateFullyConnectedWorkloadWeightsBiasesAsInputsTest + (armnn::IWorkloadFactory& factory, + armnn::Graph& graph) +{ + // Creates the layer we're testing. + FullyConnectedDescriptor layerDesc; + layerDesc.m_BiasEnabled = true; + layerDesc.m_TransposeWeightMatrix = true; + layerDesc.m_ConstantWeights = false; + + FullyConnectedLayer* const layer = graph.AddLayer(layerDesc, "layer"); + + float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0; + float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0; + + // Creates extra layers with weights and biases as input layers. + Layer* const input = graph.AddLayer(1, "input"); + Layer* const weights = graph.AddLayer(2, "weights"); + Layer* const biases = graph.AddLayer(3, "biases"); + Layer* const output = graph.AddLayer(0, "output"); + + // Connects up. + Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale), 0, 0); + Connect(weights, layer, TensorInfo({7, 20}, DataType, inputsQScale), 0, 1); + Connect(biases, layer, TensorInfo({7}, GetBiasDataType(DataType), inputsQScale), 0, 2); + Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale)); + CreateTensorHandles(graph, factory); + + // Makes the workload and checks it. + auto workload = MakeAndCheckWorkload(*layer, factory); + + FullyConnectedQueueDescriptor queueDescriptor = workload->GetData(); + + CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == true); + CHECK(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true); + CHECK(queueDescriptor.m_Parameters.m_ConstantWeights == false); + CHECK(queueDescriptor.m_Inputs.size() == 3); CHECK(queueDescriptor.m_Outputs.size() == 1); - CHECK((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({7, 20}, DataType, inputsQScale))); - CHECK((queueDescriptor.m_Bias->GetTensorInfo() == TensorInfo({7}, GetBiasDataType(DataType), inputsQScale))); // Returns so we can do extra, backend-specific tests. return workload; diff --git a/src/armnn/test/GraphTests.cpp b/src/armnn/test/GraphTests.cpp index 0dc2619e51..b697f6dbe6 100644 --- a/src/armnn/test/GraphTests.cpp +++ b/src/armnn/test/GraphTests.cpp @@ -598,14 +598,14 @@ TEST_CASE("CheckGraphConstTensorSharing") { armnn::Graph graph1; - armnn::FullyConnectedLayer* const fcLayer = - graph1.AddLayer(armnn::FullyConnectedDescriptor(), "fc"); + armnn::ConstantLayer* const constantLayer = graph1.AddLayer("ConstantLayer"); float weight = 1.0f; armnn::ConstTensor constTensor({{ 1, 1 }, armnn::DataType::Float32}, &weight); - fcLayer->m_Weight = std::make_shared(constTensor);; + constantLayer->m_LayerOutput = std::make_shared(constTensor);; + // point sharedWeightPtr to graph1's const tensor - sharedWeightPtr = fcLayer->m_Weight->GetConstTensor(); + sharedWeightPtr = constantLayer->m_LayerOutput->GetConstTensor(); graph0 = armnn::Graph(graph1); // graph1 goes out of scope diff --git a/src/armnn/test/NetworkTests.cpp b/src/armnn/test/NetworkTests.cpp index d763a85100..9acb60df4a 100644 --- a/src/armnn/test/NetworkTests.cpp +++ b/src/armnn/test/NetworkTests.cpp @@ -86,12 +86,15 @@ TEST_CASE("NetworkModification") inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0)); armnn::FullyConnectedDescriptor fullyConnectedDesc; + + // Constant layer that now holds weights data for FullyConnected + armnn::IConnectableLayer* const constantWeightsLayer = net.AddConstantLayer(weights, "const weights"); armnn::IConnectableLayer* const fullyConnectedLayer = net.AddFullyConnectedLayer(fullyConnectedDesc, - weights, - armnn::EmptyOptional(), "fully connected"); + CHECK(constantWeightsLayer); CHECK(fullyConnectedLayer); + constantWeightsLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(1)); convLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(0)); armnn::Pooling2dDescriptor pooling2dDesc; @@ -152,11 +155,12 @@ TEST_CASE("NetworkModification") multiplicationLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); //Tests that all layers are present in the graph. - CHECK(net.GetGraph().GetNumLayers() == 11); + CHECK(net.GetGraph().GetNumLayers() == 12); //Tests that the vertices exist and have correct names. CHECK(GraphHasNamedLayer(net.GetGraph(), "input layer")); CHECK(GraphHasNamedLayer(net.GetGraph(), "conv layer")); + CHECK(GraphHasNamedLayer(net.GetGraph(), "const weights")); CHECK(GraphHasNamedLayer(net.GetGraph(), "fully connected")); CHECK(GraphHasNamedLayer(net.GetGraph(), "pooling2d")); CHECK(GraphHasNamedLayer(net.GetGraph(), "activation")); @@ -200,6 +204,28 @@ TEST_CASE("NetworkModification") CHECK(&srcLayer->GetOutputSlot(0) == tgtLayer->GetInputSlot(i).GetConnection()); } }; + auto checkOneOutputToTwoInputConnectionForTwoDifferentLayers = [] + (const armnn::IConnectableLayer* const srcLayer1, + const armnn::IConnectableLayer* const srcLayer2, + const armnn::IConnectableLayer* const tgtLayer, + int expectedSrcNumInputs1 = 1, + int expectedSrcNumInputs2 = 1, + int expectedDstNumOutputs = 1) + { + CHECK(srcLayer1->GetNumInputSlots() == expectedSrcNumInputs1); + CHECK(srcLayer1->GetNumOutputSlots() == 1); + CHECK(srcLayer2->GetNumInputSlots() == expectedSrcNumInputs2); + CHECK(srcLayer2->GetNumOutputSlots() == 1); + CHECK(tgtLayer->GetNumInputSlots() == 2); + CHECK(tgtLayer->GetNumOutputSlots() == expectedDstNumOutputs); + + CHECK(srcLayer1->GetOutputSlot(0).GetNumConnections() == 1); + CHECK(srcLayer2->GetOutputSlot(0).GetNumConnections() == 1); + CHECK(srcLayer1->GetOutputSlot(0).GetConnection(0) == &tgtLayer->GetInputSlot(0)); + CHECK(srcLayer2->GetOutputSlot(0).GetConnection(0) == &tgtLayer->GetInputSlot(1)); + CHECK(&srcLayer1->GetOutputSlot(0) == tgtLayer->GetInputSlot(0).GetConnection()); + CHECK(&srcLayer2->GetOutputSlot(0) == tgtLayer->GetInputSlot(1).GetConnection()); + }; CHECK(AreAllLayerInputSlotsConnected(*convLayer)); CHECK(AreAllLayerInputSlotsConnected(*fullyConnectedLayer)); @@ -214,8 +240,8 @@ TEST_CASE("NetworkModification") // Checks connectivity. checkOneOutputToOneInputConnection(inputLayer, convLayer, 0); - checkOneOutputToOneInputConnection(convLayer, fullyConnectedLayer); - checkOneOutputToOneInputConnection(fullyConnectedLayer, poolingLayer); + checkOneOutputToTwoInputConnectionForTwoDifferentLayers(convLayer, constantWeightsLayer, fullyConnectedLayer, 1, 0); + checkOneOutputToOneInputConnection(fullyConnectedLayer, poolingLayer, 2, 1); checkOneOutputToOneInputConnection(poolingLayer, activationLayer); checkOneOutputToOneInputConnection(activationLayer, normalizationLayer); checkOneOutputToOneInputConnection(normalizationLayer, softmaxLayer); diff --git a/src/armnn/test/ShapeInferenceTests.cpp b/src/armnn/test/ShapeInferenceTests.cpp index 8abcfd7595..d3c928fec1 100644 --- a/src/armnn/test/ShapeInferenceTests.cpp +++ b/src/armnn/test/ShapeInferenceTests.cpp @@ -401,24 +401,16 @@ TEST_CASE("FloorTest") TEST_CASE("FullyConnectedTest") { - Graph graph; - const unsigned int inputWidth = 3u; const unsigned int inputHeight = 2u; const unsigned int inputChannels = 1u; const unsigned int outputChannels = 2u; - auto layer = BuildGraph(&graph, - {{1, inputChannels, inputHeight, inputWidth}}, - FullyConnectedDescriptor(), - "fc"); - - - const float Datum = 0.0f; - ConstTensor weights({{inputChannels, outputChannels}, DataType::Float32}, &Datum); - layer->m_Weight = std::make_unique(weights); - - RunShapeInferenceTest(layer, {{ 1, outputChannels }}); + CreateGraphAndRunTest({{ 1, inputChannels, inputHeight, inputWidth }, // input + { inputChannels, outputChannels }}, // weights + {{ 1, outputChannels }}, // output + FullyConnectedDescriptor(), + "fc"); } TEST_CASE("GatherTest") diff --git a/src/armnn/test/optimizations/FuseActivationTests.cpp b/src/armnn/test/optimizations/FuseActivationTests.cpp index 24ea8f6680..2352a3c498 100644 --- a/src/armnn/test/optimizations/FuseActivationTests.cpp +++ b/src/armnn/test/optimizations/FuseActivationTests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "test/GraphUtils.hpp" #include #include @@ -41,6 +42,7 @@ struct Convolution2dTest { using LayerType = Convolution2dLayer; static const bool isElementWise = false; + static const bool isConstTensorAsInputSupported = false; static TensorShape GetInputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCin static TensorShape GetOutputShape() { return TensorShape( {1, 3, 3, 4}); } // NHWCout @@ -70,6 +72,16 @@ struct Convolution2dTest return network->AddConvolution2dLayer(descriptor, weights, optionalBias, name); } + + static std::vector AddConstantLayers(INetwork* network, + float scale = 1.f, + int32_t offset = 0) + { + IgnoreUnused(network); + IgnoreUnused(scale); + IgnoreUnused(offset); + return {}; + } }; template> @@ -78,6 +90,7 @@ struct DWConvolution2dTest public: using LayerType = DepthwiseConvolution2dLayer; static const bool isElementWise = false; + static const bool isConstTensorAsInputSupported = false; static TensorShape GetInputShape() { return TensorShape( {1, 4, 4, 3}); } // [N,H,W,Cin] static TensorShape GetOutputShape() { return TensorShape( {1, 3, 3, 12}); } // [N,H,W,Cout] @@ -108,6 +121,16 @@ public: return network->AddDepthwiseConvolution2dLayer(descriptor, weights, optionalBias, name); } + + static std::vector AddConstantLayers(INetwork* network, + float scale = 1.f, + int32_t offset = 0) + { + IgnoreUnused(network); + IgnoreUnused(scale); + IgnoreUnused(offset); + return {}; + } }; template> @@ -116,6 +139,7 @@ struct FullyConnectedTest public: using LayerType = FullyConnectedLayer; static const bool isElementWise = false; + static const bool isConstTensorAsInputSupported = true; static TensorShape GetInputShape() { return TensorShape( {2, 5, 1, 1}); } // NCinHW static TensorShape GetOutputShape() { return TensorShape( {2, 3}); } // NCout @@ -129,18 +153,31 @@ public: float scale = 1.f, int32_t offset = 0) { + IgnoreUnused(scale); + IgnoreUnused(offset); + FullyConnectedDescriptor descriptor; descriptor.m_BiasEnabled = false; + return network->AddFullyConnectedLayer(descriptor, name); + } + + static std::vector AddConstantLayers(INetwork* network, + float scale = 1.f, + int32_t offset = 0) + { std::vector weightsData = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15}; + 11, 12, 13, 14, 15}; std::vector weightsVector = armnnUtils::QuantizedVector(weightsData, scale, offset); - TensorInfo weightsInfo(GetWeightsShape(), ArmnnType, scale, offset); + TensorInfo weightsInfo(GetWeightsShape(), ArmnnType, scale, offset, true); ConstTensor weights(weightsInfo, weightsVector); - Optional optionalBias; - return network->AddFullyConnectedLayer(descriptor, weights, optionalBias, name); + IConnectableLayer* weightsLayer = network->AddConstantLayer(weights, "Weights"); + weightsLayer->GetOutputSlot(0).SetTensorInfo(weightsInfo); + + std::vector layers = { weightsLayer }; + return layers; } }; @@ -150,6 +187,7 @@ struct BatchNormTest public: using LayerType = BatchNormalizationLayer; static const bool isElementWise = false; + static const bool isConstTensorAsInputSupported = false; static TensorShape GetInputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCin static TensorShape GetOutputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCout @@ -181,6 +219,16 @@ public: return network->AddBatchNormalizationLayer(descriptor, mean, variance, beta, gamma, name); } + + static std::vector AddConstantLayers(INetwork* network, + float scale = 1.f, + int32_t offset = 0) + { + IgnoreUnused(network); + IgnoreUnused(scale); + IgnoreUnused(offset); + return {}; + } }; template> @@ -188,6 +236,7 @@ struct MultiplicationTest { using LayerType = MultiplicationLayer; static const bool isElementWise = true; + static const bool isConstTensorAsInputSupported = false; static TensorShape GetInputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCin static TensorShape GetOutputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCout @@ -205,6 +254,16 @@ struct MultiplicationTest return network->AddMultiplicationLayer(name); } + + static std::vector AddConstantLayers(INetwork* network, + float scale = 1.f, + int32_t offset = 0) + { + IgnoreUnused(network); + IgnoreUnused(scale); + IgnoreUnused(offset); + return {}; + } }; template> @@ -212,6 +271,7 @@ struct AdditionTest { using LayerType = AdditionLayer; static const bool isElementWise = true; + static const bool isConstTensorAsInputSupported = false; static TensorShape GetInputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCin static TensorShape GetOutputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCout @@ -229,6 +289,16 @@ struct AdditionTest return network->AddAdditionLayer(name); } + + static std::vector AddConstantLayers(INetwork* network, + float scale = 1.f, + int32_t offset = 0) + { + IgnoreUnused(network); + IgnoreUnused(scale); + IgnoreUnused(offset); + return {}; + } }; template> @@ -236,6 +306,7 @@ struct SubtractionTest { using LayerType = SubtractionLayer; static const bool isElementWise = true; + static const bool isConstTensorAsInputSupported = false; static TensorShape GetInputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCin static TensorShape GetOutputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCout @@ -253,6 +324,16 @@ struct SubtractionTest return network->AddSubtractionLayer(name); } + + static std::vector AddConstantLayers(INetwork* network, + float scale = 1.f, + int32_t offset = 0) + { + IgnoreUnused(network); + IgnoreUnused(scale); + IgnoreUnused(offset); + return {}; + } }; template> @@ -260,6 +341,7 @@ struct DivisionTest { using LayerType = DivisionLayer; static const bool isElementWise = true; + static const bool isConstTensorAsInputSupported = false; static TensorShape GetInputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCin static TensorShape GetOutputShape() { return TensorShape( {1, 4, 4, 3}); } // NHWCout @@ -277,11 +359,21 @@ struct DivisionTest return network->AddDivisionLayer(name); } + + static std::vector AddConstantLayers(INetwork* network, + float scale = 1.f, + int32_t offset = 0) + { + IgnoreUnused(network); + IgnoreUnused(scale); + IgnoreUnused(offset); + return {}; + } }; template -INetworkPtr CreatNetwork(ActivationDescriptor activationDescriptor, bool preventFusing, +INetworkPtr CreateNetwork(ActivationDescriptor activationDescriptor, bool preventFusing, float scale, int32_t offset) { // Create a network @@ -300,6 +392,20 @@ INetworkPtr CreatNetwork(ActivationDescriptor activationDescriptor, bool prevent IConnectableLayer* outputLayer = network->AddOutputLayer(0); IConnectableLayer* output2Layer = preventFusing?network->AddOutputLayer(1):nullptr; + // If ConstTensorAsInputs is supported weights and bias are stored as constant layers. + if(LayerTest::isConstTensorAsInputSupported) + { + std::vector constantLayers = LayerTest::AddConstantLayers(network.get(), + scale, + offset); + + // Connect constant layers to receiverLayer. + for (unsigned int i = 0; i < constantLayers.size(); ++i) + { + constantLayers[i]->GetOutputSlot(0).Connect(receiverLayer->GetInputSlot(i + 1)); + } + } + // Define layers information TensorInfo inputInfo(LayerTest::GetInputShape(), ArmnnType, scale, offset); TensorInfo outputInfo(LayerTest::GetOutputShape(), ArmnnType, scale, offset); @@ -335,7 +441,7 @@ void FuseActivationIntoPreviousLayerTest(ActivationDescriptor activationDescript { // FIRST NETWORK: Fused // Construct ArmNN network - INetworkPtr networkFused = CreatNetwork(activationDescriptor, false, scale, offset); + INetworkPtr networkFused = CreateNetwork(activationDescriptor, false, scale, offset); // Create ArmNN runtime IRuntimePtr run = IRuntime::Create(IRuntime::CreationOptions()); // default options @@ -350,12 +456,31 @@ void FuseActivationIntoPreviousLayerTest(ActivationDescriptor activationDescript (layer->GetNameStr() == "fused-activation-into-receiverLayer"); }; - CHECK(3 == graphFused.GetNumLayers()); - CHECK(CheckSequence(graphFused.cbegin(), - graphFused.cend(), - &IsLayerOfType, - checkFusedConv2d, - &IsLayerOfType)); + // If ConstTensorAsInputs is supported, weights and bias are stored as constant layers. + if(LayerTest::isConstTensorAsInputSupported) + { + CHECK(4 == graphFused.GetNumLayers()); + CHECK(CheckSequence(graphFused.cbegin(), + graphFused.cend(), + &IsLayerOfType, + &IsLayerOfType, + checkFusedConv2d, + &IsLayerOfType)); + + // Check if new constant layer is connected to fused receiver layer. + Layer* fusedReceiverLayer = GetFirstLayerWithName(graphFused, "fused-activation-into-receiverLayer"); + CHECK(fusedReceiverLayer); + CHECK(fusedReceiverLayer->GetInputSlot(1).GetConnection() != nullptr); + } + else + { + CHECK(3 == graphFused.GetNumLayers()); + CHECK(CheckSequence(graphFused.cbegin(), + graphFused.cend(), + &IsLayerOfType, + checkFusedConv2d, + &IsLayerOfType)); + } // Load network into runtime NetworkId networkIdentifier; @@ -376,7 +501,7 @@ void FuseActivationIntoPreviousLayerTest(ActivationDescriptor activationDescript // SECOND NETWORK: NotFused // Construct ArmNN network - INetworkPtr networkNotFused = CreatNetwork(activationDescriptor, true, scale, offset); + INetworkPtr networkNotFused = CreateNetwork(activationDescriptor, true, scale, offset); // Create ArmNN runtime IRuntimePtr runNotFused = IRuntime::Create(IRuntime::CreationOptions()); // default options @@ -386,14 +511,30 @@ void FuseActivationIntoPreviousLayerTest(ActivationDescriptor activationDescript Graph& graphNotFused = GetGraphForTesting(optNetNotFused.get()); - CHECK(5 == graphNotFused.GetNumLayers()); - CHECK(CheckSequence(graphNotFused.cbegin(), - graphNotFused.cend(), - &IsLayerOfType, - &IsLayerOfType, - &IsLayerOfType, - &IsLayerOfType, - &IsLayerOfType)); + // If ConstTensorAsInputs is supported, weights and bias are stored as constant layers. + if(LayerTest::isConstTensorAsInputSupported) + { + CHECK(6 == graphNotFused.GetNumLayers()); + CHECK(CheckSequence(graphNotFused.cbegin(), + graphNotFused.cend(), + &IsLayerOfType, + &IsLayerOfType, + &IsLayerOfType, + &IsLayerOfType, + &IsLayerOfType, + &IsLayerOfType)); + } + else + { + CHECK(5 == graphNotFused.GetNumLayers()); + CHECK(CheckSequence(graphNotFused.cbegin(), + graphNotFused.cend(), + &IsLayerOfType, + &IsLayerOfType, + &IsLayerOfType, + &IsLayerOfType, + &IsLayerOfType)); + } // Load network into runtime NetworkId networkIdentifierNotFused; @@ -433,7 +574,7 @@ bool FuseActivationSimpleTest(ActivationDescriptor activationDescriptor, Compute try { // Construct ArmNN network - INetworkPtr networkFused = CreatNetwork(activationDescriptor, false, scale, offset); + INetworkPtr networkFused = CreateNetwork(activationDescriptor, false, scale, offset); // Create ArmNN runtime IRuntimePtr run = IRuntime::Create(IRuntime::CreationOptions()); // default options -- cgit v1.2.1