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 <matthew.sloyan@arm.com>
Change-Id: Ibf3cf437df1100e4b322b0d303c575c6339f9696
diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp
index 83eafe7..a29ce83 100644
--- a/src/armnn/Network.cpp
+++ b/src/armnn/Network.cpp
@@ -30,6 +30,8 @@
#include <common/include/ProfilingGuid.hpp>
+#include <fmt/format.h>
+
#include <fcntl.h>
#include <algorithm>
#include <fstream>
@@ -178,6 +180,12 @@
}
IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
+ const char* name)
+{
+ return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor, name);
+}
+
+IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
const ConstTensor& weights,
const Optional<ConstTensor>& biases,
const char* name)
@@ -189,28 +197,6 @@
}
IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
- const ConstTensor& weights,
- const char* name)
-{
- armnn::Optional<ConstTensor> biases;
- return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor,
- armnn::Optional<ConstTensor>(weights),
- biases,
- name);
-}
-
-IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
- const ConstTensor& weights,
- const ConstTensor& biases,
- const char* name)
-{
- return pNetworkImpl->AddFullyConnectedLayer(fullyConnectedDescriptor,
- armnn::Optional<ConstTensor>(weights),
- armnn::Optional<ConstTensor>(biases),
- name);
-}
-
-IConnectableLayer* INetwork::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
const Optional<ConstTensor>& weights,
const Optional<ConstTensor>& biases,
const char* name)
@@ -1799,33 +1785,10 @@
return m_Graph->AddLayer<FillLayer>(fillDescriptor, name);
}
-IConnectableLayer* NetworkImpl::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
- const Optional<ConstTensor>& weights,
- const Optional<ConstTensor>& biases,
- const char* name)
+IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
+ const char* name)
{
- if (fullyConnectedDescriptor.m_ConstantWeights && !weights.has_value())
- {
- throw InvalidArgumentException("AddFullyConnectedLayer: weights cannot be empty");
-
- if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value())
- {
- throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty");
- }
- }
-
- const auto layer = m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
-
- if (fullyConnectedDescriptor.m_ConstantWeights)
- {
- layer->m_Weight = std::make_shared<ScopedTensorHandle>(weights.value());
- if (fullyConnectedDescriptor.m_BiasEnabled)
- {
- layer->m_Bias = std::make_shared<ScopedTensorHandle>(biases.value());
- }
- }
-
- return layer;
+ return m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
}
IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
@@ -1833,35 +1796,76 @@
const Optional<ConstTensor>& biases,
const char* name)
{
- return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
+ ConstantLayer* weightsLayer = nullptr;
+ ConstantLayer* biasLayer = nullptr;
+ unsigned int numInputs = fullyConnectedDescriptor.GetNumInputs();
+
+ // Add a constant layer for weights
+ if (weights.has_value())
+ {
+ weightsLayer = m_Graph->AddLayer<ConstantLayer>("Weights");
+ weightsLayer->m_LayerOutput = std::make_shared<ScopedTensorHandle>(weights.value());
+ weightsLayer->GetOutputSlot(0).SetTensorInfo(weightsLayer->m_LayerOutput->GetTensorInfo());
+ }
+ else if (fullyConnectedDescriptor.m_ConstantWeights)
+ {
+ throw InvalidArgumentException("AddFullyConnectedLayer: Constant weights tensor is empty.");
+ }
+
+ // Add a constant layer for biases
+ if (biases.has_value() && fullyConnectedDescriptor.m_BiasEnabled)
+ {
+ biasLayer = m_Graph->AddLayer<ConstantLayer>("Biases");
+ biasLayer->m_LayerOutput = std::make_shared<ScopedTensorHandle>(biases.value());
+ biasLayer->GetOutputSlot(0).SetTensorInfo(biasLayer->m_LayerOutput->GetTensorInfo());
+ }
+
+ if (numInputs < 2)
+ {
+ throw InvalidArgumentException("AddFullyConnectedLayer: Requires at least 2 input tensors: Input, Weights");
+ }
+
+ auto layer = m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
+
+ if (weightsLayer)
+ {
+ // Connect weights layer
+ weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
+ }
+
+ if ( fullyConnectedDescriptor.m_BiasEnabled && numInputs == 3 )
+ {
+ if (biasLayer)
+ {
+ // 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 ConstTensor& weights,
- const Optional<ConstTensor>& biases,
- const char* name)
+ const ConstTensor& weights,
+ const Optional<ConstTensor>& biases,
+ const char* name)
{
Optional<ConstTensor> optionalWeights(weights);
- return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, biases, name);
-}
-
-IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
- const ConstTensor& weights,
- const char* name)
-{
- Optional<ConstTensor> optionalWeights(weights);
- Optional<ConstTensor> biases;
- return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, biases, name);
-}
-
-IConnectableLayer* NetworkImpl::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
- const ConstTensor& weights,
- const ConstTensor& biases,
- const char* name)
-{
- Optional<ConstTensor> optionalWeights(weights);
- Optional<ConstTensor> optionalBiases(biases);
- return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, optionalWeights, optionalBiases, name);
+ return AddFullyConnectedLayer(fullyConnectedDescriptor, optionalWeights, biases, name);
}
IConnectableLayer* NetworkImpl::AddConcatLayer(const ConcatDescriptor& concatDescriptor,