aboutsummaryrefslogtreecommitdiff
path: root/src/armnnCaffeParser
diff options
context:
space:
mode:
Diffstat (limited to 'src/armnnCaffeParser')
-rw-r--r--src/armnnCaffeParser/CaffeParser.cpp1871
-rw-r--r--src/armnnCaffeParser/CaffeParser.hpp173
-rw-r--r--src/armnnCaffeParser/CaffeSupport.md37
-rw-r--r--src/armnnCaffeParser/README.md7
-rw-r--r--src/armnnCaffeParser/RecordByRecordCaffeParser.cpp733
-rw-r--r--src/armnnCaffeParser/RecordByRecordCaffeParser.hpp53
-rw-r--r--src/armnnCaffeParser/test/TestAdd.cpp70
-rw-r--r--src/armnnCaffeParser/test/TestConcat.cpp73
-rw-r--r--src/armnnCaffeParser/test/TestConvolution.cpp133
-rw-r--r--src/armnnCaffeParser/test/TestDropout.cpp53
-rw-r--r--src/armnnCaffeParser/test/TestInPlace.cpp98
-rw-r--r--src/armnnCaffeParser/test/TestInputs.cpp122
-rw-r--r--src/armnnCaffeParser/test/TestMul.cpp73
-rw-r--r--src/armnnCaffeParser/test/TestMultiInputsOutputs.cpp54
-rw-r--r--src/armnnCaffeParser/test/TestPooling2d.cpp54
-rw-r--r--src/armnnCaffeParser/test/TestSplit.cpp47
16 files changed, 0 insertions, 3651 deletions
diff --git a/src/armnnCaffeParser/CaffeParser.cpp b/src/armnnCaffeParser/CaffeParser.cpp
deleted file mode 100644
index ce5c5bd4f5..0000000000
--- a/src/armnnCaffeParser/CaffeParser.cpp
+++ /dev/null
@@ -1,1871 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#include "CaffeParser.hpp"
-#include "RecordByRecordCaffeParser.hpp"
-
-#include "armnn/Descriptors.hpp"
-#include "armnn/INetwork.hpp"
-#include "armnn/Utils.hpp"
-#include "armnn/Exceptions.hpp"
-
-#include "GraphTopologicalSort.hpp"
-#include "VerificationHelpers.hpp"
-
-#include <boost/numeric/conversion/cast.hpp>
-#include <boost/assert.hpp>
-#include <boost/format.hpp>
-
-// Caffe
-#include "caffe/proto/caffe.pb.h"
-
-// ProtoBuf
-#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/io/zero_copy_stream.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/text_format.h>
-#include <google/protobuf/stubs/common.h>
-#include <google/protobuf/stubs/once.h>
-#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/descriptor.h>
-#include <google/protobuf/generated_message_reflection.h>
-#include <google/protobuf/reflection_ops.h>
-#include <google/protobuf/wire_format.h>
-
-#include <cmath>
-#include <sstream>
-#include <queue>
-#include <fcntl.h>
-
-/// Caffe networks are loaded from protobuf files (binary or text) using the protobuf library and the generated
-/// code from caffe.pb.h. This gives us a caffe::NetParameter which is an in-memory version of the file.
-/// This contains a flat list of Caffe 'layers' (e.g. convolution, pooling etc.).
-/// Each layer has inputs (called "bottoms") and outputs (called "tops"). Data flows from bottom to top.
-/// The bottoms of a layer refer to the tops of other layers, not their names.
-/// The names of layers seem to be arbitrary (you could rename a layer and the network wouldn't
-/// need any other changes).
-///
-/// Some layers (e.g. Relu) can be configured so that their top and bottom are both the same. This is called an
-/// "in-place" layer and is a Caffe runtime feature used to reduce memory usage by modifying tensors in-place.
-/// This isn't relevant to the parser and so we preprocess these layers to convert them to regular layers, to result
-/// in a consistent graph structure.
-
-namespace armnnCaffeParser
-{
-
-using namespace armnn;
-using namespace caffe;
-using namespace std;
-using namespace google::protobuf::io;
-
-namespace
-{
-
-const float* GetArrayPtrFromBlob(const LayerParameter& layerParam, unsigned int blobIndex)
-{
- auto nBlobs = layerParam.blobs_size();
- if (blobIndex >= boost::numeric_cast<unsigned int>(nBlobs))
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Expected data blob at index %1% in layer %2% not found. nBlobs=%2%. %4%") %
- blobIndex %
- layerParam.name() %
- nBlobs %
- CHECK_LOCATION().AsString()));
- }
-
- const BlobProto& blob = layerParam.blobs(boost::numeric_cast<int>(blobIndex));
-
- const float* arrayPtr = blob.data().data();
- return arrayPtr;
-}
-
-void GetDataFromBlob(const LayerParameter& layerParam, vector<float>& outData, unsigned int blobIndex)
-{
- auto nBlobs = layerParam.blobs_size();
- if (blobIndex >= boost::numeric_cast<unsigned int>(nBlobs))
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Expected data blob at index %1% in layer %2% not found. %3%") %
- blobIndex %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
-
- const BlobProto& blob = layerParam.blobs(boost::numeric_cast<int>(blobIndex));
-
- size_t blobSize = boost::numeric_cast<size_t>(blob.data_size());
- if (blobSize != outData.size())
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Data blob at index %1% in layer %2% has an unexpected size. "
- "Expected %3% elements but got %4% elements. %5%") %
- blobIndex %
- layerParam.name() %
- outData.size() %
- blobSize %
- CHECK_LOCATION().AsString()));
- }
-
- int outSizeInt = boost::numeric_cast<int>(outData.size());
- for (int i = 0; i < outSizeInt; ++i)
- {
- outData[static_cast<size_t>(i)] = blob.data(i);
- }
-}
-
-template <typename T>
-size_t SizeOfVectorData(const vector<T>& vec)
-{
- return vec.size() * sizeof(T);
-}
-
-void ValidateNumInputsOutputs(const caffe::LayerParameter& layerParameter,
- unsigned int numInputs,
- unsigned int numOutputs)
-{
- int numInputsActual = layerParameter.bottom_size();
- if (numInputs != boost::numeric_cast<unsigned int>(numInputsActual))
- {
- throw ParseException(
- boost::str(
- boost::format("Invalid number of inputs requested %1% for layer %2% "
- "while only %3% present. %4%") %
- numInputs %
- layerParameter.name() %
- numInputsActual %
- CHECK_LOCATION().AsString()));
- }
-
- int numOutputsActual = layerParameter.top_size();
- if (numOutputs != boost::numeric_cast<unsigned int>(numOutputsActual))
- {
- throw ParseException(
- boost::str(
- boost::format("Invalid number of outputs requested %1% for layer %2% "
- "while only %3% present. %4%") %
- numOutputs %
- layerParameter.name() %
- numOutputsActual %
- CHECK_LOCATION().AsString()));
- }
-}
-
-template <typename ParamType, typename ExtractOptional, typename ExtractFallback, typename ValueType>
-ValueType GetOptionalWithFallback(const ParamType& param,
- ExtractOptional extractOptional,
- ExtractFallback extractFallback,
- ValueType defaultValue)
-{
- auto optValue = extractOptional(param, defaultValue);
- if (optValue.first)
- {
- return optValue.second;
- }
- auto fallbackValue = extractFallback(param, defaultValue);
- return fallbackValue.second;
-}
-
-#define GET_OPTIONAL_WITH_VECTOR_FALLBACK(PARAM, \
- PARAM_TYPE, \
- OPTIONAL_VALUE, \
- FALLBACK_VECTOR, \
- VALUE_TYPE, \
- DEFAULT_VALUE) \
- GetOptionalWithFallback( \
- PARAM, \
- [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
- { \
- if (param.has_##OPTIONAL_VALUE ()) \
- { \
- return std::make_pair(true, param.OPTIONAL_VALUE ()); \
- } \
- else \
- { \
- return std::make_pair(false, defaultValue); \
- } \
- }, \
- [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
- { \
- if (param.FALLBACK_VECTOR##_size() > 0) \
- { \
- return std::make_pair(true, (param.FALLBACK_VECTOR ()).Get(0)); \
- } \
- else \
- { \
- return std::make_pair(false, defaultValue); \
- } \
- }, \
- DEFAULT_VALUE)
-
-#define GET_OPTIONAL_WITH_FALLBACK(PARAM, \
- PARAM_TYPE, \
- OPTIONAL_VALUE, \
- FALLBACK_VALUE, \
- VALUE_TYPE, \
- DEFAULT_VALUE) \
- GetOptionalWithFallback( \
- PARAM, \
- [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
- { \
- if (param.has_##OPTIONAL_VALUE ()) \
- { \
- return std::make_pair(true, param.OPTIONAL_VALUE ()); \
- } \
- else \
- { \
- return std::make_pair(false, defaultValue); \
- } \
- }, \
- [](const PARAM_TYPE & param, VALUE_TYPE defaultValue) \
- { \
- if (param.has_##FALLBACK_VALUE ()) \
- { \
- return std::make_pair(true, param.FALLBACK_VALUE ()); \
- } \
- else \
- { \
- return std::make_pair(false, defaultValue); \
- } \
- }, \
- DEFAULT_VALUE)
-
-} // namespace <anonymous>
-
-const std::map<std::string, CaffeParserBase::OperationParsingFunction>
- CaffeParserBase::ms_CaffeLayerNameToParsingFunctions = {
- { "Input", &CaffeParserBase::ParseInputLayer },
- { "Convolution", &CaffeParserBase::ParseConvLayer },
- { "Pooling", &CaffeParserBase::ParsePoolingLayer },
- { "ReLU", &CaffeParserBase::ParseReluLayer },
- { "LRN", &CaffeParserBase::ParseLRNLayer },
- { "InnerProduct", &CaffeParserBase::ParseInnerProductLayer },
- { "Softmax", &CaffeParserBase::ParseSoftmaxLayer },
- { "Eltwise", &CaffeParserBase::ParseEltwiseLayer },
- { "Concat", &CaffeParserBase::ParseConcatLayer },
- { "BatchNorm", &CaffeParserBase::ParseBatchNormLayer },
- { "Scale", &CaffeParserBase::ParseScaleLayer },
- { "Split", &CaffeParserBase::ParseSplitLayer },
- { "Dropout", &CaffeParserBase::ParseDropoutLayer},
-};
-
-ICaffeParser* ICaffeParser::CreateRaw()
-{
- return new RecordByRecordCaffeParser();
-}
-
-ICaffeParserPtr ICaffeParser::Create()
-{
- return ICaffeParserPtr(CreateRaw(), &ICaffeParser::Destroy);
-}
-
-void ICaffeParser::Destroy(ICaffeParser* parser)
-{
- delete parser;
-}
-
-CaffeParserBase::CaffeParserBase()
- : m_Network(nullptr, nullptr)
-{
-
-}
-
-CaffeParser::CaffeParser()
-: CaffeParserBase()
-{
-
-}
-
-BindingPointInfo CaffeParserBase::GetNetworkInputBindingInfo(const std::string& name) const
-{
- return GetBindingInfo(name, "input", m_NetworkInputsBindingInfo);
-}
-
-BindingPointInfo CaffeParserBase::GetNetworkOutputBindingInfo(const std::string& name) const
-{
- return GetBindingInfo(name, "output", m_NetworkOutputsBindingInfo);
-}
-
-std::pair<armnn::LayerBindingId, armnn::TensorInfo> CaffeParserBase::GetBindingInfo(const std::string& layerName,
- const char* bindingPointDesc,
- const std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
-{
- auto it = nameToBindingInfo.find(layerName);
- if (it == nameToBindingInfo.end())
- {
- throw InvalidArgumentException(
- boost::str(
- boost::format(
- "Unknown binding %1% for layer '%2%'. %3%") %
- bindingPointDesc %
- layerName %
- CHECK_LOCATION().AsString()));
- }
- return it->second;
-}
-
-TensorInfo CaffeParserBase::BlobShapeToTensorInfo(const caffe::BlobShape& blobShape) const
-{
- std::vector<unsigned int> shape;
- for (int j = 0; j < blobShape.dim_size(); ++j)
- {
- shape.push_back(static_cast<unsigned int>(blobShape.dim(j)));
- }
-
- return TensorInfo(boost::numeric_cast<unsigned int>(shape.size()), shape.data(), DataType::Float32);
-}
-
-BlobShape TensorDescToBlobShape(const TensorInfo& desc)
-{
- BlobShape ret;
- for (unsigned int i = 0; i < desc.GetNumDimensions(); ++i)
- {
- ret.add_dim(i);
- ret.set_dim(boost::numeric_cast<int>(i), desc.GetShape()[i]);
- }
-
- return ret;
-}
-
-// Note: can move to CaffeParser when/if we optimise the text/string format
-// to load on a layer by layer basis
-vector<const LayerParameter*> CaffeParserBase::GetInputs(const LayerParameter& layerParam)
-{
- std::vector<const caffe::LayerParameter*> ret;
- ret.reserve(boost::numeric_cast<size_t>(layerParam.bottom_size()));
- for (int j = 0; j < layerParam.bottom_size(); ++j)
- {
- std::string inputName = layerParam.bottom(j);
- auto inputIt = m_CaffeLayersByTopName.find(inputName);
- if (inputIt == m_CaffeLayersByTopName.end())
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Can't find Caffe layer with top called '%1%', "
- "which is listed as an input of '%2%'. %3%") %
- inputName %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
- ret.push_back(inputIt->second);
- }
-
- return ret;
-}
-
-void CaffeParserBase::ParseInputLayer(const LayerParameter& layerParam)
-{
- BOOST_ASSERT(layerParam.type() == "Input");
- ValidateNumInputsOutputs(layerParam, 0, 1);
-
- const InputParameter& param = layerParam.input_param();
-
- const armnn::LayerBindingId inputId = boost::numeric_cast<armnn::LayerBindingId>(
- m_NetworkInputsBindingInfo.size());
- armnn::IConnectableLayer* const inputLayer = m_Network->AddInputLayer(inputId, layerParam.name().c_str());
-
- // Decides the tensor info for this input. This can be specified in the Caffe network but can also
- // be overriden by user input (m_inputShapes).
- armnn::TensorInfo inputTensorInfo;
-
- const BlobShape* originalShape = param.shape_size() > 0 && param.shape(0).dim_size() > 0 ?
- &param.shape(0) : nullptr;
- if (originalShape)
- {
- inputTensorInfo = BlobShapeToTensorInfo(*originalShape);
- }
-
- auto overrideIt = m_InputShapes.find(layerParam.name());
- if (overrideIt != m_InputShapes.end())
- {
- const TensorShape& overrideShape = overrideIt->second;
- if (originalShape &&
- ( originalShape->dim(1) != overrideShape[1]
- || originalShape->dim(2) != overrideShape[2]
- || originalShape->dim(3) != overrideShape[3]))
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Parsed input shape for '%1%' is incompatible with the override provided. %2%") %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
- inputTensorInfo.SetShape(overrideShape);
- }
- else if (!originalShape)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "No input descriptor given for '%1%' and no input shape found in caffe model. %2%") %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
-
- TrackInputBinding(inputLayer, inputId, inputTensorInfo);
- inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), inputLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::AddConvLayerWithSplits(const caffe::LayerParameter& layerParam,
- const armnn::Convolution2dDescriptor& desc,
- unsigned int kernelW,
- unsigned int kernelH)
-{
- BOOST_ASSERT(layerParam.type() == "Convolution");
- ValidateNumInputsOutputs(layerParam, 1, 1);
-
- ConvolutionParameter convParam = layerParam.convolution_param();
- BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
- const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
-
- // asusme these were already verified by the caller ParseConvLayer() function
- BOOST_ASSERT(numGroups < inputShape.dim(1));
- BOOST_ASSERT(numGroups > 1);
-
- // Handle grouping
- armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
-
- vector<string> convLayerNames(numGroups);
- vector<armnn::IConnectableLayer*> convLayers(numGroups);
- convLayerNames[0] = layerParam.name();
-
- // This convolution is to be applied to chunks of the input data so add a splitter layer
-
- // Redirect the convolution input to the splitter
- unsigned int splitterDimSizes[4] = {static_cast<unsigned int>(inputShape.dim(0)),
- static_cast<unsigned int>(inputShape.dim(1)),
- static_cast<unsigned int>(inputShape.dim(2)),
- static_cast<unsigned int>(inputShape.dim(3))};
-
- // Split dimension 1 of the splitter output shape and conv input shapes
- // according to the number of groups
-
- splitterDimSizes[1] /= numGroups;
- inputShape.set_dim(1, splitterDimSizes[1]);
-
- // This is used to describe how the input is to be split
- ViewsDescriptor splitterDesc(numGroups);
-
- // Create an output node for each group, giving each a unique name
- for (unsigned int g = 0; g < numGroups; ++g)
- {
- // Work out the names of the splitter layers child convolutions
- stringstream ss;
- ss << layerParam.name() << "_" << g;
- convLayerNames[g] = ss.str();
-
- splitterDesc.SetViewOriginCoord(g, 1, splitterDimSizes[1] * g);
-
- // Set the size of the views.
- for (unsigned int dimIdx=0; dimIdx < 4; dimIdx++)
- {
- splitterDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
- }
- }
-
- const std::string splitterLayerName = std::string("splitter_") + layerParam.bottom(0);
- armnn::IConnectableLayer* splitterLayer = m_Network->AddSplitterLayer(splitterDesc, splitterLayerName.c_str());
-
- inputConnection.Connect(splitterLayer->GetInputSlot(0));
- for (unsigned int i = 0; i < splitterLayer->GetNumOutputSlots(); i++)
- {
- splitterLayer->GetOutputSlot(i).SetTensorInfo(BlobShapeToTensorInfo(inputShape));
- }
-
- unsigned int numFilters = convParam.num_output();
-
- // Populates convolution output tensor descriptor dimensions.
- BlobShape outputShape;
- outputShape.add_dim(0);
- outputShape.set_dim(0, inputShape.dim(0));
- outputShape.add_dim(1);
- // Ensures that dimension 1 of the convolution output is split according to the number of groups.
- outputShape.set_dim(1, numFilters / numGroups);
- outputShape.add_dim(2);
- outputShape.set_dim(
- 2, (static_cast<int>(
- static_cast<float>(inputShape.dim(2) + 2 * desc.m_PadBottom - kernelH) /
- static_cast<float>(desc.m_StrideY)) + 1));
- outputShape.add_dim(3);
- outputShape.set_dim(
- 3, (static_cast<int>(
- static_cast<float>(inputShape.dim(3) + 2 * desc.m_PadRight - kernelW) /
- static_cast<float>(desc.m_StrideX)) + 1));
-
- // Load the weight data for ALL groups
- vector<float> weightData(boost::numeric_cast<size_t>(numGroups *
- inputShape.dim(1) * // number of input channels
- outputShape.dim(1) * // number of output channels
- kernelH *
- kernelW));
- GetDataFromBlob(layerParam, weightData, 0);
-
- const unsigned int weightDimSizes[4] = {
- static_cast<unsigned int>(outputShape.dim(1)),
- static_cast<unsigned int>(inputShape.dim(1)),
- kernelH,
- kernelW};
-
- TensorInfo biasInfo;
- vector<float> biasData;
-
- if (desc.m_BiasEnabled)
- {
- biasData.resize(boost::numeric_cast<size_t>(numGroups * outputShape.dim(1)), 1.f);
- GetDataFromBlob(layerParam, biasData, 1);
-
- const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
- biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
- }
-
- const unsigned int numWeightsPerGroup = boost::numeric_cast<unsigned int>(weightData.size()) / numGroups;
- const unsigned int numBiasesPerGroup = boost::numeric_cast<unsigned int>(biasData.size()) / numGroups;
-
- for (unsigned int g = 0; g < numGroups; ++g)
- {
- // Sets the slot index, group 0 should be connected to the 0th output of the splitter
- // group 1 should be connected to the 1st output of the splitter.
-
- // Pulls out the weights for this group from that loaded from the model file earlier.
- ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32),
- weightData.data() + numWeightsPerGroup * g);
-
- IConnectableLayer* convLayer = nullptr;
- Optional<ConstTensor> optionalBiases;
- if (desc.m_BiasEnabled)
- {
- // Pulls out the biases for this group from that loaded from the model file earlier.
- ConstTensor biases(biasInfo, biasData.data() + numBiasesPerGroup * g);
- optionalBiases = Optional<ConstTensor>(biases);
- }
- convLayer = m_Network->AddConvolution2dLayer(desc,
- weights,
- optionalBiases,
- convLayerNames[g].c_str());
- convLayers[g] = convLayer;
-
- // If we have more than one group then the input to the nth convolution the splitter layer's nth output,
- // otherwise it's the regular input to this layer.
- armnn::IOutputSlot& splitterInputConnection =
- splitterLayer ? splitterLayer->GetOutputSlot(g) : inputConnection;
- splitterInputConnection.Connect(convLayer->GetInputSlot(0));
- convLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
- }
-
- // If the convolution was performed in chunks, add a layer to concatenate the results
-
- // The merge input shape matches that of the convolution output
- unsigned int concatDimSizes[4] = {static_cast<unsigned int>(outputShape.dim(0)),
- static_cast<unsigned int>(outputShape.dim(1)),
- static_cast<unsigned int>(outputShape.dim(2)),
- static_cast<unsigned int>(outputShape.dim(3))};
-
- // This is used to describe how the input is to be concatenated
- OriginsDescriptor concatDesc(numGroups);
-
- // Now create an input node for each group, using the name from
- // the output of the corresponding convolution
- for (unsigned int g = 0; g < numGroups; ++g)
- {
- concatDesc.SetViewOriginCoord(g, 1, concatDimSizes[1] * g);
- }
-
- // Make sure the output from the concat is the correct size to hold the data for all groups
- concatDimSizes[1] *= numGroups;
- outputShape.set_dim(1, concatDimSizes[1]);
-
- // Finally add the concat layer
- IConnectableLayer* concatLayer = m_Network->AddConcatLayer(concatDesc, layerParam.name().c_str());
-
- if (!concatLayer)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Failed to create final concat layer for Split+Convolution+Concat. "
- "Layer=%1% #groups=%2% #filters=%3% %4%") %
- layerParam.name() %
- numGroups %
- numFilters %
- CHECK_LOCATION().AsString()));
- }
-
- for (unsigned int g = 0; g < numGroups; ++g)
- {
- convLayers[g]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(g));
- }
- concatLayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(4, concatDimSizes, DataType::Float32));
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::AddConvLayerWithDepthwiseConv(const caffe::LayerParameter& layerParam,
- const armnn::Convolution2dDescriptor& convDesc,
- unsigned int kernelW,
- unsigned int kernelH)
-{
- BOOST_ASSERT(layerParam.type() == "Convolution");
- ValidateNumInputsOutputs(layerParam, 1, 1);
-
- ConvolutionParameter convParam = layerParam.convolution_param();
- BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
-
- DepthwiseConvolution2dDescriptor desc;
- desc.m_PadLeft = convDesc.m_PadLeft;
- desc.m_PadRight = convDesc.m_PadRight;
- desc.m_PadTop = convDesc.m_PadTop;
- desc.m_PadBottom = convDesc.m_PadBottom;
- desc.m_StrideX = convDesc.m_StrideX;
- desc.m_StrideY = convDesc.m_StrideY;
- desc.m_BiasEnabled = convDesc.m_BiasEnabled;
-
- unsigned int numFilters = convParam.num_output();
-
- BlobShape outputShape;
- outputShape.add_dim(0);
- outputShape.set_dim(0, inputShape.dim(0));
- outputShape.add_dim(1);
- outputShape.set_dim(1, numFilters);
- outputShape.add_dim(2);
- outputShape.set_dim(
- 2, (static_cast<int>(
- static_cast<float>(inputShape.dim(2) + 2 * desc.m_PadBottom - kernelH) /
- static_cast<float>(desc.m_StrideY)) + 1));
- outputShape.add_dim(3);
- outputShape.set_dim(
- 3, (static_cast<int>(
- static_cast<float>(inputShape.dim(3) + 2 * desc.m_PadRight - kernelW) /
- static_cast<float>(desc.m_StrideX)) + 1));
-
- // Load the weight data
- size_t allWeightsSize = boost::numeric_cast<size_t>(inputShape.dim(1) * kernelH * kernelW);
- vector<float> weightData(allWeightsSize);
-
- GetDataFromBlob(layerParam, weightData, 0);
-
- // depth multiplier will be 1 for the depthwise convolution
- const unsigned int weightDimSizes[4] = {
- static_cast<unsigned int>(1), // depth multiplier
- static_cast<unsigned int>(inputShape.dim(1)), // #channels
- kernelH,
- kernelW};
-
- armnn::IConnectableLayer* returnLayer = nullptr;
- ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
- Optional<ConstTensor> optionalBiases;
- vector<float> biasData;
- if (desc.m_BiasEnabled)
- {
- TensorInfo biasInfo;
-
- biasData.resize(boost::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
- GetDataFromBlob(layerParam, biasData, 1);
-
- const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
- biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
-
- ConstTensor biases(biasInfo, biasData.data());
- optionalBiases = Optional<ConstTensor>(biases);
- }
- returnLayer = m_Network->AddDepthwiseConvolution2dLayer(desc,
- weights,
- optionalBiases,
- layerParam.name().c_str());
-
- if (!returnLayer)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Failed to create depthwise convolution layer. "
- "Layer=%1% #filters=%2% %3%") %
- layerParam.name() %
- numFilters %
- CHECK_LOCATION().AsString()));
- }
- armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
- inputConnection.Connect(returnLayer->GetInputSlot(0));
- returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParseConvLayer(const LayerParameter& layerParam)
-{
- // Ignored Caffe Parameters
- // * Dilation Size
- // * Weight Filler
- // * Bias Filler
- // * Engine
- // * Force nd_im2col
- // * Axis
-
- // Not Available ArmNN Interface Parameters
- // * Rounding policy;
-
- BOOST_ASSERT(layerParam.type() == "Convolution");
- ValidateNumInputsOutputs(layerParam, 1, 1);
-
- ConvolutionParameter convParam = layerParam.convolution_param();
- BlobShape inputShape = TensorDescToBlobShape(GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo());
- const unsigned int numGroups = convParam.has_group() ? convParam.group() : 1;
- unsigned int numFilters = convParam.num_output();
-
- const auto notFound = std::numeric_limits<unsigned int>::max();
-
- unsigned int kernelH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
- kernel_h, kernel_size, unsigned int, notFound);
- unsigned int kernelW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
- kernel_w, kernel_size, unsigned int, notFound);
-
- unsigned int strideH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
- stride_h, stride, unsigned int, 1u);
- unsigned int strideW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
- stride_w, stride, unsigned int, 1u);
-
- unsigned int padH = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
- pad_h, pad, unsigned int, 0u);
- unsigned int padW = GET_OPTIONAL_WITH_VECTOR_FALLBACK(convParam, ConvolutionParameter,
- pad_w, pad, unsigned int, 0u);
-
- Convolution2dDescriptor convolution2dDescriptor;
- convolution2dDescriptor.m_PadLeft = padW;
- convolution2dDescriptor.m_PadRight = padW;
- convolution2dDescriptor.m_PadTop = padH;
- convolution2dDescriptor.m_PadBottom = padH;
- convolution2dDescriptor.m_StrideX = strideW;
- convolution2dDescriptor.m_StrideY = strideH;
- convolution2dDescriptor.m_BiasEnabled = convParam.has_bias_term() ? convParam.bias_term() : true;
-
- if (numGroups > numFilters)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Error parsing Convolution: %1%. "
- "The 'group'=%2% parameter cannot be larger than the "
- "number of filters supplied ='%3%'. %4%") %
- layerParam.name() %
- numGroups %
- numFilters %
- CHECK_LOCATION().AsString()));
- }
-
- if (inputShape.dim_size() != 4)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Convolution input shape is expected to have 4 dimensions. "
- "%1%'s input has only %2%. %3%") %
- layerParam.name() %
- inputShape.dim_size() %
- CHECK_LOCATION().AsString()));
- }
-
- if (numGroups > 1)
- {
- if (numGroups > inputShape.dim(1))
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Error parsing Convolution: %1%. "
- "The 'group'=%2% parameter cannot be larger than the "
- "channel of the input shape=%3% (in NCHW format). %4%") %
- layerParam.name() %
- numGroups %
- inputShape.dim(1) %
- CHECK_LOCATION().AsString()));
- }
- else if (numGroups == inputShape.dim(1))
- {
- // we use a depthwise convolution here, because the number of groups equals to the
- // input channels
- AddConvLayerWithDepthwiseConv(layerParam, convolution2dDescriptor, kernelW, kernelH);
- return;
- }
- else
- {
- // we split the input by channels into channels/groups separate convolutions
- // and concatenate the results afterwards
- AddConvLayerWithSplits(layerParam, convolution2dDescriptor, kernelW, kernelH);
- return;
- }
- }
-
- // NOTE: at this point we only need to handle #group=1 case, all other cases should be
- // handled by the AddConvLayer* helpers
-
- // Populate convolution output tensor descriptor dimensions
- BlobShape outputShape;
- outputShape.add_dim(0);
- outputShape.set_dim(0, inputShape.dim(0));
- outputShape.add_dim(1);
- outputShape.set_dim(1, numFilters);
- outputShape.add_dim(2);
- outputShape.set_dim(
- 2, (static_cast<int>(
- static_cast<float>(inputShape.dim(2) + 2 * padH - kernelH) /
- static_cast<float>(strideH)) + 1));
- outputShape.add_dim(3);
- outputShape.set_dim(
- 3, (static_cast<int>(
- static_cast<float>(inputShape.dim(3) + 2 * padW - kernelW) /
- static_cast<float>(strideW)) + 1));
-
- // Load the weight data for ALL groups
- vector<float> weightData(boost::numeric_cast<size_t>(inputShape.dim(1) *
- outputShape.dim(1) *
- kernelH *
- kernelW));
- GetDataFromBlob(layerParam, weightData, 0);
-
- const unsigned int weightDimSizes[4] = {
- static_cast<unsigned int>(outputShape.dim(1)), // output channels
- static_cast<unsigned int>(inputShape.dim(1)), // input channels
- kernelH,
- kernelW};
-
- armnn::IConnectableLayer* returnLayer = nullptr;
-
- // Pull out the weights for this group from that loaded from the model file earlier
- ConstTensor weights(TensorInfo(4, weightDimSizes, DataType::Float32), weightData.data());
- Optional<ConstTensor> optionalBiases;
- vector<float> biasData;
- if (convolution2dDescriptor.m_BiasEnabled)
- {
- TensorInfo biasInfo;
-
- biasData.resize(boost::numeric_cast<size_t>(outputShape.dim(1)), 1.f);
- GetDataFromBlob(layerParam, biasData, 1);
-
- const unsigned int biasDimSizes[1] = {static_cast<unsigned int>(outputShape.dim(1))};
- biasInfo = TensorInfo(1, biasDimSizes, DataType::Float32);
-
- // Pull out the biases for this group from that loaded from the model file earlier
- ConstTensor biases(biasInfo, biasData.data());
- optionalBiases = Optional<ConstTensor>(biases);
- }
- returnLayer = m_Network->AddConvolution2dLayer(convolution2dDescriptor,
- weights,
- optionalBiases,
- layerParam.name().c_str());
-
- armnn::IOutputSlot& inputConnection = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
- inputConnection.Connect(returnLayer->GetInputSlot(0));
- returnLayer->GetOutputSlot(0).SetTensorInfo(BlobShapeToTensorInfo(outputShape));
-
- if (!returnLayer)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Failed to create Convolution layer. "
- "Layer=%1% #groups=%2% #filters=%3% %4%") %
- layerParam.name() %
- numGroups %
- numFilters %
- CHECK_LOCATION().AsString()));
- }
-
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), returnLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParsePoolingLayer(const LayerParameter& layerParam)
-{
- // Ignored Caffe Parameters
- // Stochastic Pooling
- // Engine
-
- ValidateNumInputsOutputs(layerParam, 1, 1);
- PoolingParameter param = layerParam.pooling_param();
- const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
-
- const auto notFound = std::numeric_limits<unsigned int>::max();
-
- unsigned int kernel_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
- kernel_h, kernel_size, unsigned int, notFound);
- unsigned int kernel_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
- kernel_w, kernel_size, unsigned int, notFound);
-
- if ((kernel_h == notFound || kernel_w == notFound) && param.has_global_pooling())
- {
- kernel_h = inputInfo.GetShape()[2];
- kernel_w = inputInfo.GetShape()[3];
- }
-
- unsigned int stride_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
- stride_h, stride, unsigned int, notFound);
- unsigned int stride_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
- stride_h, stride, unsigned int, notFound);
-
- if ((stride_h == notFound || stride_w == notFound) && param.has_global_pooling())
- {
- stride_h = 1;
- stride_w = 1;
- }
-
- unsigned int pad_h = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
- pad_h, pad, unsigned int, 0u);
- unsigned int pad_w = GET_OPTIONAL_WITH_FALLBACK(param, PoolingParameter,
- pad_w, pad, unsigned int, 0u);
-
- // Populate Weight and Bias Filter Descriptor
- Pooling2dDescriptor pooling2dDescriptor;
- if (param.has_pool())
- {
- PoolingParameter_PoolMethod p = param.pool();
- switch (p)
- {
- case PoolingParameter_PoolMethod_MAX:
- {
- pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Max;
- break;
- }
- case PoolingParameter_PoolMethod_AVE:
- {
- pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Average;
- break;
- }
- case PoolingParameter_PoolMethod_STOCHASTIC:
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Pooling Layer: Stochastic Pooling Not Supported. Layer=%1% %2%") %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
- default:
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Pooling Layer: unknown pooling method: %1% for layer: %2% %3%") %
- p %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
- }
- }
- else
- {
- throw ParseException(
- boost::str(
- boost::format(
- "No Pooling Method Defined for %1% %2%") %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
-
- pooling2dDescriptor.m_PadLeft = pad_w;
- pooling2dDescriptor.m_PadRight = pad_w;
- pooling2dDescriptor.m_PadTop = pad_h;
- pooling2dDescriptor.m_PadBottom = pad_h;
- pooling2dDescriptor.m_StrideX = stride_w;
- pooling2dDescriptor.m_StrideY = stride_h;
- pooling2dDescriptor.m_PoolWidth = kernel_w;
- pooling2dDescriptor.m_PoolHeight = kernel_h;
-
- pooling2dDescriptor.m_OutputShapeRounding = OutputShapeRounding::Ceiling;
- pooling2dDescriptor.m_PaddingMethod = PaddingMethod::IgnoreValue;
-
- armnn::IConnectableLayer* poolingLayer = m_Network->AddPooling2dLayer(pooling2dDescriptor,
- layerParam.name().c_str());
-
- TensorInfo outputInfo(
- { inputInfo.GetShape()[0],
- inputInfo.GetShape()[1],
- static_cast<unsigned int>(ceil(
- static_cast<float>(inputInfo.GetShape()[2] + 2 * pad_h - kernel_h) /
- boost::numeric_cast<float>(stride_h))) + 1,
- static_cast<unsigned int>(ceil(
- static_cast<float>(inputInfo.GetShape()[3] + 2 * pad_w - kernel_w) /
- boost::numeric_cast<float>(stride_w))) + 1 },
- DataType::Float32);
-
- GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(poolingLayer->GetInputSlot(0));
- poolingLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), poolingLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParseReluLayer(const LayerParameter& layerParam)
-{
- ValidateNumInputsOutputs(layerParam, 1, 1);
-
- const string& name = layerParam.name();
- const ReLUParameter& param = layerParam.relu_param();
-
- ActivationDescriptor activationDescriptor;
- const float negativeSlope = param.negative_slope();
- if (negativeSlope == 0.0f)
- {
- activationDescriptor.m_Function = ActivationFunction::ReLu;
- }
- else
- {
- activationDescriptor.m_Function = ActivationFunction::LeakyReLu;
- activationDescriptor.m_A = negativeSlope;
- }
-
- const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
- IConnectableLayer* const activationLayer = m_Network->AddActivationLayer(activationDescriptor, name.c_str());
- GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(activationLayer->GetInputSlot(0));
- activationLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), activationLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParseLRNLayer(const LayerParameter& layerParam)
-{
- ValidateNumInputsOutputs(layerParam, 1, 1);
-
- LRNParameter param = layerParam.lrn_param();
-
- const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
-
- // Ignored BATCH NORMALIZATION Caffe Parameters.
- // Ignored MVN Caffe Parameters.
- // Ignored LRN Caffe Parameters.
- // Engine
-
- NormalizationDescriptor normalizationDescriptor;
- if (param.has_norm_region())
- {
- LRNParameter_NormRegion n = param.norm_region();
- switch (n)
- {
- case LRNParameter_NormRegion_ACROSS_CHANNELS:
- {
- normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Across;
- break;
- }
- case LRNParameter_NormRegion_WITHIN_CHANNEL:
- {
- normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Within;
- break;
- }
- default:
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Unknown region %1% for LRN layer %2% %3%") %
- n %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
- }
- }
- else
- {
- // Caffe defaults to normalization across channels.
- normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Across;
- }
-
- normalizationDescriptor.m_NormMethodType = NormalizationAlgorithmMethod::LocalBrightness;
- if (param.has_local_size())
- {
- normalizationDescriptor.m_NormSize = param.local_size();
- }
- else
- {
- throw ParseException(
- boost::str(
- boost::format(
- "local_size not defined for LRN layer %1% %2%") %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
-
- if (param.has_alpha())
- {
- normalizationDescriptor.m_Alpha = param.alpha();
- normalizationDescriptor.m_Alpha /= boost::numeric_cast<float>(param.local_size());
- }
- else
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Alpha not defined for LRN layer %1% %2%") %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
- if (param.has_beta())
- {
- normalizationDescriptor.m_Beta = param.beta();
- }
- else
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Beta not defined for LRN layer %1% %2%") %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
-
- if (param.has_k())
- {
- normalizationDescriptor.m_K = param.k();
- }
- else
- {
- normalizationDescriptor.m_K = 1;
- }
-
- IConnectableLayer* const normLayer = m_Network->AddNormalizationLayer(normalizationDescriptor,
- layerParam.name().c_str());
- GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(normLayer->GetInputSlot(0));
- normLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
-
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), normLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParseInnerProductLayer(const LayerParameter& layerParam)
-{
- InnerProductParameter param = layerParam.inner_product_param();
-
- ValidateNumInputsOutputs(layerParam, 1, 1);
-
- unsigned int outputSize = param.num_output();
-
- // Ignored Caffe Parameters:
- // Weight Filler
- // Bias Filler
- // Engine
- // Axis
-
- FullyConnectedDescriptor tensorFullyConnectedDescriptor;
-
- if (param.has_transpose())
- {
- // If true, assumes transposed weights.
- tensorFullyConnectedDescriptor.m_TransposeWeightMatrix = param.transpose();
- }
- else
- {
- // Caffe defaults to transposed.
- tensorFullyConnectedDescriptor.m_TransposeWeightMatrix = true;
- }
-
- const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
-
- TensorInfo weightInfo;
- TensorInfo biasInfo;
-
- // Allows implicit flattening of extra dimensions.
- unsigned int inputSize = inputInfo.GetShape()[1];
- for (unsigned int i = 2; i < inputInfo.GetNumDimensions(); ++i)
- {
- inputSize *= inputInfo.GetShape()[i];
- }
-
- const float* weightDataPtr = GetArrayPtrFromBlob(layerParam, 0);
- const unsigned int swTD[2] = { outputSize, inputSize };
- ConstTensor weights(TensorInfo(2, swTD, DataType::Float32), weightDataPtr);
-
- tensorFullyConnectedDescriptor.m_BiasEnabled = true;
- // Todo: check whether bias enabled.
- armnn::IConnectableLayer* fullyConnectedLayer = nullptr;
- if (tensorFullyConnectedDescriptor.m_BiasEnabled)
- {
- // BIAS VALUE
- const float* biasDataPtr = GetArrayPtrFromBlob(layerParam, 1);
-
- const unsigned int sbTD[1] = { outputSize };
-
- ConstTensor biases(TensorInfo(1, sbTD, DataType::Float32), biasDataPtr);
-
- fullyConnectedLayer = m_Network->AddFullyConnectedLayer(tensorFullyConnectedDescriptor,
- weights,
- Optional<ConstTensor>(biases),
- layerParam.name().c_str());
- }
- else
- {
- fullyConnectedLayer = m_Network->AddFullyConnectedLayer(tensorFullyConnectedDescriptor,
- weights,
- EmptyOptional(),
- layerParam.name().c_str());
- }
-
- TensorInfo outputInfo({ inputInfo.GetShape()[0], outputSize }, DataType::Float32);
- GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(fullyConnectedLayer->GetInputSlot(0));
- fullyConnectedLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), fullyConnectedLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParseSoftmaxLayer(const LayerParameter& layerParam)
-{
- ValidateNumInputsOutputs(layerParam, 1, 1);
-
- SoftmaxParameter param = layerParam.softmax_param();
-
- const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
-
- // Ignored Caffe Parameters:
- // axis
- // Engine
-
- armnn::SoftmaxDescriptor softmaxDescriptor;
- softmaxDescriptor.m_Axis = 1;
- armnn::IConnectableLayer* const softmaxLayer = m_Network->AddSoftmaxLayer(
- softmaxDescriptor,
- layerParam.name().c_str());
- GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(softmaxLayer->GetInputSlot(0));
- softmaxLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), softmaxLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParseEltwiseLayer(const LayerParameter& layerParam)
-{
- ValidateNumInputsOutputs(layerParam, 2, 1);
-
- const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
-
- // Ignored Caffe Parameters:
- // coeff
-
- EltwiseParameter_EltwiseOp operation = EltwiseParameter_EltwiseOp_SUM; // Defaults to sum as per caffe.
-
- if (layerParam.has_eltwise_param() && layerParam.eltwise_param().has_operation())
- {
- operation = layerParam.eltwise_param().operation();
- }
-
- armnn::IConnectableLayer* newLayer = nullptr;
- switch (operation)
- {
- case EltwiseParameter_EltwiseOp_SUM:
- {
- newLayer = m_Network->AddAdditionLayer(layerParam.name().c_str());
- break;
- }
- case EltwiseParameter_EltwiseOp_PROD:
- {
- newLayer = m_Network->AddMultiplicationLayer(layerParam.name().c_str());
- break;
- }
- default:
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Unsupported operation %1% in Eltwise layer %2% %3%") %
- operation %
- layerParam.name() %
- CHECK_LOCATION().AsString()));
- }
- }
-
- GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(newLayer->GetInputSlot(0));
- GetArmnnOutputSlotForCaffeTop(layerParam.bottom(1)).Connect(newLayer->GetInputSlot(1));
- newLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), newLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParseConcatLayer(const LayerParameter& layerParam)
-{
- unsigned int numInputs = static_cast<unsigned int>(layerParam.bottom_size());
- // We assume concat happens along the channel dimension, which is 1 in (0, 1, 2, 3).
- unsigned int concatDim = 1;
- unsigned int numOfDims = 4;
-
- // we only consider 4-D tensor here
- OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numInputs), numOfDims);
- std::vector<unsigned int>mergeDimSizes(numOfDims, 0u);
-
- unsigned int mergeDim = 0;
- for (unsigned int viewIndex = 0; viewIndex < numInputs; ++viewIndex)
- {
- const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(
- layerParam.bottom(boost::numeric_cast<int>(viewIndex))).GetTensorInfo();
- // Checks whether the dimensions of the input tensors are actually 4.
- if (inputInfo.GetNumDimensions()!=4)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "The number of dimensions for input tensors of "
- "the concatenation op should be 4. Inputs of %1% has "
- "%2% dimensions. %3%") %
- layerParam.name() %
- inputInfo.GetNumDimensions() %
- CHECK_LOCATION().AsString()));
- }
-
- mergeDimSizes[0] = inputInfo.GetShape()[0];
- mergeDimSizes[1] = inputInfo.GetShape()[1];
- mergeDimSizes[2] = inputInfo.GetShape()[2];
- mergeDimSizes[3] = inputInfo.GetShape()[3];
-
- for (unsigned int j = 0; j < concatDim; ++j)
- {
- concatDescriptor.SetViewOriginCoord(viewIndex, j, 0);
- }
-
- concatDescriptor.SetViewOriginCoord(viewIndex, concatDim, mergeDim);
- mergeDim += mergeDimSizes[concatDim];
-
- for (unsigned int j = concatDim+1; j < numOfDims; ++j)
- {
- concatDescriptor.SetViewOriginCoord(viewIndex, j, 0);
- }
- }
- mergeDimSizes[concatDim] = mergeDim;
-
- armnn::IConnectableLayer* concatlayer = m_Network->AddConcatLayer(concatDescriptor, layerParam.name().c_str());
- for (unsigned int i = 0; i < numInputs; ++i)
- {
- armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(boost::numeric_cast<int>(i)));
- outputSlot.Connect(concatlayer->GetInputSlot(i));
- }
-
- concatlayer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(numOfDims, mergeDimSizes.data(), DataType::Float32));
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), concatlayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParseBatchNormLayer(const LayerParameter& layerParam)
-{
- ValidateNumInputsOutputs(layerParam, 1, 1);
-
- const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
-
- string name = layerParam.name();
-
- BatchNormParameter param = layerParam.batch_norm_param();
- // If use_global_stats is not explicitly set in the model, assume it to be true (its default value
- // when the network is in the testing phase).
- if (param.has_use_global_stats())
- {
- if (!param.use_global_stats())
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Error parsing Batch Norm layer '%1%': "
- "Parameter 'use_global_stats' is set to false, which is "
- "unsupported (value used for training). %2%") %
- name %
- CHECK_LOCATION().AsString()));
- }
- }
-
- BatchNormalizationDescriptor desc;
- desc.m_Eps = param.eps();
-
- unsigned int channels = inputInfo.GetShape()[1];
- unsigned int shape[] = {channels};
-
- vector<float> meanData(channels);
- GetDataFromBlob(layerParam, meanData, 0);
-
- vector<float> varianceData(channels);
- GetDataFromBlob(layerParam, varianceData, 1);
-
- // Reads moving average factor and applies scaling (if required).
- const BlobProto& blob = layerParam.blobs(boost::numeric_cast<int>(2));
- const float movingAverageFactor = blob.data(boost::numeric_cast<int>(0));
- if(movingAverageFactor != 0.0f)
- {
- const float scaleFactor = 1.0f / movingAverageFactor;
- auto scaleFunction = [scaleFactor](float f) -> float { return f * scaleFactor; };
-
- std::transform(varianceData.begin(), varianceData.end(), varianceData.begin(), scaleFunction);
- std::transform(meanData.begin(), meanData.end(), meanData.begin(), scaleFunction);
- }
-
- // Identifies scale operation.
- vector<float> betaData(channels, 0.0f);
- vector<float> gammaData(channels, 1.0f);
-
- ConstTensor mean(TensorInfo(1, shape, armnn::DataType::Float32), meanData);
- ConstTensor variance(TensorInfo(1, shape, armnn::DataType::Float32), varianceData);
- ConstTensor beta(TensorInfo(1, shape, armnn::DataType::Float32), betaData);
- ConstTensor gamma(TensorInfo(1, shape, armnn::DataType::Float32), gammaData);
-
- armnn::IConnectableLayer* const batchNormLayer = m_Network->AddBatchNormalizationLayer(desc,
- mean, variance, beta, gamma, name.c_str());
- GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(batchNormLayer->GetInputSlot(0));
- batchNormLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), batchNormLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParseScaleLayer(const LayerParameter& layerParam)
-{
- // Current unoptimal solution: add a batchnormalization layer with 0 mean and 1 variance.
- ValidateNumInputsOutputs(layerParam, 1, 1);
-
- const TensorInfo& inputInfo = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).GetTensorInfo();
-
- string name = layerParam.name();
-
- ScaleParameter param = layerParam.scale_param();
- if (param.axis() != 1)
- {
- // Would have to use something other than BatchNormalizationLayer in this case
- throw ParseException(
- boost::str(
- boost::format(
- "Loading Scale Layer: Only axis 1 is supported currently. "
- "Layer=%1% Axis=%2% %3%") %
- layerParam.name() %
- param.axis() %
- CHECK_LOCATION().AsString()));
- }
-
- unsigned int channels = inputInfo.GetShape()[1];
- unsigned int shape[] = {channels};
-
- BatchNormalizationDescriptor desc;
- desc.m_Eps = 0.0f; // Don't need epsilon if variance is 1.
- vector<float> meanData(channels, 0.0f);
- vector<float> varianceData(channels, 1.0f);
- vector<float> betaData(channels, 0.0f);
- vector<float> gammaData(channels);
-
- GetDataFromBlob(layerParam, gammaData, 0);
-
- if(param.has_bias_term())
- {
- GetDataFromBlob(layerParam, betaData, 1);
- }
-
- ConstTensor mean(TensorInfo(1, shape, armnn::DataType::Float32), meanData);
- ConstTensor variance(TensorInfo(1, shape, armnn::DataType::Float32), varianceData);
- ConstTensor beta(TensorInfo(1, shape, armnn::DataType::Float32), betaData);
- ConstTensor gamma(TensorInfo(1, shape, armnn::DataType::Float32), gammaData);
-
- armnn::IConnectableLayer* const batchNormLayer = m_Network->AddBatchNormalizationLayer(desc,
- mean, variance, beta, gamma, name.c_str());
- GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)).Connect(batchNormLayer->GetInputSlot(0));
- batchNormLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), batchNormLayer->GetOutputSlot(0));
-}
-
-void CaffeParserBase::ParseSplitLayer(const caffe::LayerParameter& layerParam)
-{
- // Used in caffe to duplicate memory - not necessary in armnn.
- if (layerParam.bottom_size() != 1)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Split layer '%1%' should have exactly 1 bottom. "
- "#bottoms=%2% %3%") %
- layerParam.name() %
- layerParam.bottom_size() %
- CHECK_LOCATION().AsString()));
- }
- armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0));
- for (int i = 0; i < layerParam.top_size(); i++)
- {
- SetArmnnOutputSlotForCaffeTop(layerParam.top(i), outputSlot);
- }
-}
-
-void CaffeParserBase::ParseDropoutLayer(const caffe::LayerParameter& layerParam)
-{
- // Ignored for inference, so patch the single input to its single output.
- if (layerParam.bottom_size() != 1 || layerParam.top_size() != 1)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Dropout layer '%1%' should have exactly 1 bottom and 1 top. "
- "#bottoms=%2% #tops=%3% %4%") %
- layerParam.name() %
- layerParam.bottom_size() %
- layerParam.top_size() %
- CHECK_LOCATION().AsString()));
- }
- SetArmnnOutputSlotForCaffeTop(layerParam.top(0), GetArmnnOutputSlotForCaffeTop(layerParam.bottom(0)));
-}
-
-void CaffeParserBase::TrackInputBinding(armnn::IConnectableLayer* layer,
- armnn::LayerBindingId id,
- const armnn::TensorInfo& tensorInfo)
-{
- return TrackBindingPoint(layer, id, tensorInfo, layer->GetName(), m_NetworkInputsBindingInfo);
-}
-
-void CaffeParserBase::TrackOutputBinding(armnn::IConnectableLayer* layer,
- armnn::LayerBindingId id,
- const armnn::TensorInfo& tensorInfo)
-{
- return TrackBindingPoint(layer, id, tensorInfo, layer->GetName(), m_NetworkOutputsBindingInfo);
-}
-
-void CaffeParserBase::TrackBindingPoint(armnn::IConnectableLayer* layer,
- armnn::LayerBindingId id,
- const armnn::TensorInfo& tensorInfo,
- const char* bindingPointDesc,
- std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
-{
- const std::string layerName = layer->GetName();
- auto it = nameToBindingInfo.find(layerName);
- if (it == nameToBindingInfo.end())
- {
- nameToBindingInfo[layerName] = std::make_pair(id, tensorInfo);
- }
- else
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Id %1% used by more than one %2% layer %3%") %
- id %
- bindingPointDesc %
- CHECK_LOCATION().AsString()));
- }
-}
-
-armnn::IOutputSlot& CaffeParserBase::GetArmnnOutputSlotForCaffeTop(const std::string& caffeTopName) const
-{
- auto it = m_ArmnnOutputSlotForCaffeTop.find(caffeTopName);
- if (it != m_ArmnnOutputSlotForCaffeTop.end())
- {
- return *it->second;
- }
- else
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Could not find armnn output slot for Caffe top '%1%' %2%") %
- caffeTopName %
- CHECK_LOCATION().AsString()));
- }
-}
-
-void CaffeParserBase::SetArmnnOutputSlotForCaffeTop(
- const std::string& caffeTopName, armnn::IOutputSlot& armnnOutputSlot)
-{
- auto it = m_ArmnnOutputSlotForCaffeTop.find(caffeTopName);
- if (it == m_ArmnnOutputSlotForCaffeTop.end())
- {
- m_ArmnnOutputSlotForCaffeTop[caffeTopName] = &armnnOutputSlot;
- }
- else
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Attempting to add duplicate entry for Caffe top '%1%' %2%") %
- caffeTopName %
- CHECK_LOCATION().AsString()));
- }
-}
-
-// Note: can move to CaffeParser when/if we optimise the text/string format
-// to load on a layer by layer basis
-void CaffeParserBase::ResolveInPlaceLayers(caffe::NetParameter& netParameter)
-{
- // Finds layers with the same top.
- std::map<std::string, std::vector<caffe::LayerParameter*>> layersByTop;
- for (int layerIdx = 0; layerIdx < netParameter.layer_size(); ++layerIdx)
- {
- caffe::LayerParameter& layer = *netParameter.mutable_layer(layerIdx);
- std::string name = layer.name();
- for (int i = 0; i < layer.top_size(); ++i)
- {
- layersByTop[layer.top(i)].push_back(&layer);
- }
- }
-
- // For each set of layers with the same top, resolves them to a linear chain rather than in-place layers.
- // Note that for 'regular' layers, there will be a single layer in each group and so this will be a no-op.
- for (auto layersWithSameTopIt : layersByTop)
- {
- const std::string& top = layersWithSameTopIt.first;
- const std::vector<caffe::LayerParameter*>& layersWithSameTop = layersWithSameTopIt.second;
-
- // Chains the layers together in the order that they are listed in the prototxt (hopefully this is correct).
- // Note that the last layer will not have its top modified so that other layers will continue to reference it.
- for (unsigned int layerIdx = 0; layerIdx < layersWithSameTop.size() - 1; ++layerIdx)
- {
- caffe::LayerParameter& layer1 = *layersWithSameTop[layerIdx];
- caffe::LayerParameter& layer2 = *layersWithSameTop[layerIdx+1];
- if (layer1.top_size() != 1)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Node '%1%' is an in-place layer but doesn't have exactly one "
- "top. It has %2% instead. %3%") %
- layer1.name() %
- layer1.top_size() %
- CHECK_LOCATION().AsString()));
- }
- std::string newTop = layer1.name() + "_top";
- layer1.set_top(0, newTop);
- if (layer2.bottom_size() != 1 || layer2.bottom(0) != top)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Node '%1%' is an in-place layer but "
- "doesn't have exactly one bottom, or it doesn't match its top. "
- "#bottoms=%2%, first bottom is %3%, top is %4% %5%") %
- layer2.name() %
- layer2.bottom(0) %
- top %
- CHECK_LOCATION().AsString()));
- }
- layer2.set_bottom(0, newTop);
- }
- }
-}
-
-// Note: can move to CaffeParser when/if we optimise the text/string format
-// to load on a layer by layer basis
-void CaffeParserBase::LoadNetParam(NetParameter& netParameter)
-{
- // Caffe models sometimes have an implicit input layer.
- // In that case, add an explicit one.
- if (netParameter.input_size() > 0)
- {
- LayerParameter* newLayer = netParameter.add_layer();
-
- newLayer->set_type("Input");
- newLayer->set_name(netParameter.input(0));
- newLayer->add_top(netParameter.input(0));
-
- InputParameter* inputParam = newLayer->mutable_input_param();
- BlobShape* shape = inputParam->add_shape();
-
- int dim_size = netParameter.input_dim_size();
- for (int i = 0; i < dim_size; ++i)
- {
- shape->add_dim(netParameter.input_dim(i));
- }
- }
-
- // Replaces in-place layers with regular ones to make the rest of the parsing easier.
- ResolveInPlaceLayers(netParameter);
-
- // Creates a lookup of Caffe layers by name.
- for (int i = 0; i < netParameter.layer_size(); ++i)
- {
- const caffe::LayerParameter& layer = netParameter.layer(i);
- for (int i = 0; i < layer.top_size(); ++i)
- {
- m_CaffeLayersByTopName[layer.top(i)] = &layer;
- }
- }
-
- // Finds the output layers the user requested.
- std::vector<const caffe::LayerParameter*> targetLayers;
- for (const std::string& requestedOutputName : m_RequestedOutputs)
- {
- auto nodeIt = m_CaffeLayersByTopName.find(requestedOutputName);
- if (nodeIt == m_CaffeLayersByTopName.end())
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Couldn't find requested output layer '%1%' in graph %2%") %
- requestedOutputName %
- CHECK_LOCATION().AsString()));
- }
- targetLayers.push_back(nodeIt->second);
- }
-
- // Sorts them into a linear ordering such that all inputs of a node are before the node itself.
- std::vector<const caffe::LayerParameter*> sortedNodes;
- if (!armnnUtils::GraphTopologicalSort<const caffe::LayerParameter*>(
- targetLayers,
- [this](const caffe::LayerParameter* node)
- {
- return GetInputs(*node);
- },
- sortedNodes))
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Cycle detected in graph. #nodes: %1% %2%") %
- sortedNodes.size() %
- CHECK_LOCATION().AsString()));
- }
-
- // Parses each node in order, knowing that all inputs of a node will be processed before the node itself.
- for (const caffe::LayerParameter* current : sortedNodes)
- {
- auto it = ms_CaffeLayerNameToParsingFunctions.find(current->type());
- if (it == ms_CaffeLayerNameToParsingFunctions.end())
- {
- throw ParseException(
- boost::str(
- boost::format("Unsupported layer type: '%1%' for layer %2% %3%") %
- current->type() %
- current->name() %
- CHECK_LOCATION().AsString()));
- }
- auto func = it->second;
- (this->*func)(*current);
- }
-
- // Adds ArmNN output layers connected to each requested output.
- for (const std::string& requestedOutput : m_RequestedOutputs)
- {
- armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(requestedOutput);
-
- const armnn::LayerBindingId outputId = boost::numeric_cast<armnn::LayerBindingId>(
- m_NetworkOutputsBindingInfo.size());
- armnn::IConnectableLayer* const outputLayer = m_Network->AddOutputLayer(outputId, requestedOutput.c_str());
- outputSlot.Connect(outputLayer->GetInputSlot(0));
-
- TrackOutputBinding(outputLayer, outputId, outputLayer->GetInputSlot(0).GetConnection()->GetTensorInfo());
- }
-}
-
-INetworkPtr CaffeParserBase::CreateNetworkFromTextFile(const char* graphFile,
- const std::map<std::string, armnn::TensorShape>& inputShapes,
- const std::vector<std::string>& requestedOutputs)
-{
- FILE* fd = fopen(graphFile, "r");
-
- if (fd == nullptr)
- {
- throw FileNotFoundException(
- boost::str(
- boost::format(
- "Failed to open graph file: %1% %2%") %
- graphFile %
- CHECK_LOCATION().AsString()));
- }
-
- // Parses the file into a message.
- NetParameter netParam;
- auto input = new google::protobuf::io::FileInputStream(fileno(fd));
- bool success = google::protobuf::TextFormat::Parse(input, &netParam);
- delete input;
- fclose(fd);
-
- if (!success)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Failed to parse graph file: %1% %2%") %
- graphFile %
- CHECK_LOCATION().AsString()));
- }
-
- return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
-}
-
-INetworkPtr CaffeParserBase::CreateNetworkFromString(const char* protoText,
- const std::map<std::string, armnn::TensorShape>& inputShapes,
- const std::vector<std::string>& requestedOutputs)
-{
- // Parses the string into a message.
- NetParameter netParam;
- bool success = google::protobuf::TextFormat::ParseFromString(protoText, &netParam);
-
- if (!success)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Failed to parse graph string %1%") %
- CHECK_LOCATION().AsString()));
- }
-
- return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
-}
-
-INetworkPtr CaffeParser::CreateNetworkFromBinaryFile(const char* graphFile,
- const std::map<std::string, armnn::TensorShape>& inputShapes,
- const std::vector<std::string>& requestedOutputs)
-{
- FILE* fd = fopen(graphFile, "rb");
-
- if (fd == nullptr)
- {
- throw FileNotFoundException(
- boost::str(
- boost::format(
- "Failed to open graph file at: %1% %2%") %
- graphFile %
- CHECK_LOCATION().AsString()));
- }
-
- // Parses the file into a message.
- NetParameter netParam;
-
- FileInputStream inStream(fileno(fd));
- CodedInputStream codedStream(&inStream);
- codedStream.SetTotalBytesLimit(INT_MAX, INT_MAX);
- bool success = netParam.ParseFromCodedStream(&codedStream);
- fclose(fd);
-
- if (!success)
- {
- throw ParseException(
- boost::str(
- boost::format(
- "Failed to parse protobuf file: %1% %2%") %
- graphFile %
- CHECK_LOCATION().AsString()));
- }
-
- return CreateNetworkFromNetParameter(netParam, inputShapes, requestedOutputs);
-}
-
-// Note: can move to CaffeParser when/if we optimise the text/string format
-// to load on a layer by layer basis
-INetworkPtr CaffeParserBase::CreateNetworkFromNetParameter(NetParameter& netParam,
- const std::map<std::string, armnn::TensorShape>& inputShapes,
- const std::vector<std::string>& requestedOutputs)
-{
- m_NetworkInputsBindingInfo.clear();
- m_NetworkOutputsBindingInfo.clear();
-
- m_Network = INetwork::Create();
-
- m_InputShapes = inputShapes;
- if (requestedOutputs.size() == 0)
- {
- throw ParseException("requestedOutputs must have at least one entry");
- }
- m_RequestedOutputs = requestedOutputs;
-
- try
- {
- LoadNetParam(netParam);
- }
- catch (const ParseException& e)
- {
- Cleanup();
- throw e;
- }
-
- Cleanup();
-
- return move(m_Network);
-}
-
-void CaffeParserBase::Cleanup() {
- // cleanup, in case we reuse this parser
- m_InputShapes.clear();
- m_RequestedOutputs.clear();
- m_ArmnnOutputSlotForCaffeTop.clear();
- // NOTE: when we get the text/string format
- // optimised for memory then this data structure can
- // also move to the CaffeParser class
- m_CaffeLayersByTopName.clear();
-}
-
-}
diff --git a/src/armnnCaffeParser/CaffeParser.hpp b/src/armnnCaffeParser/CaffeParser.hpp
deleted file mode 100644
index f28b3a4889..0000000000
--- a/src/armnnCaffeParser/CaffeParser.hpp
+++ /dev/null
@@ -1,173 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#pragma once
-#include "armnnCaffeParser/ICaffeParser.hpp"
-
-#include "armnn/Types.hpp"
-#include "armnn/NetworkFwd.hpp"
-#include "armnn/Tensor.hpp"
-
-#include <memory>
-#include <vector>
-#include <unordered_map>
-
-namespace caffe
-{
-class BlobShape;
-class LayerParameter;
-class NetParameter;
-}
-
-namespace armnnCaffeParser
-{
-
-class CaffeParserBase: public ICaffeParser
-{
-public:
-
- // Because we haven't looked at reducing the memory usage when loading from Text/String
- // have to retain these functions here for the moment.
- /// Create the network from a protobuf text file on disk
- virtual armnn::INetworkPtr CreateNetworkFromTextFile(
- const char* graphFile,
- const std::map<std::string, armnn::TensorShape>& inputShapes,
- const std::vector<std::string>& requestedOutputs) override;
-
-
- /// Creates the network directly from protobuf text in a string. Useful for debugging/testing.
- virtual armnn::INetworkPtr CreateNetworkFromString(
- const char* protoText,
- const std::map<std::string, armnn::TensorShape>& inputShapes,
- const std::vector<std::string>& requestedOutputs) override;
-
- /// Retrieves binding info (layer id and tensor info) for the network input identified by the given layer name.
- virtual BindingPointInfo GetNetworkInputBindingInfo(const std::string& name) const override;
-
- /// Retrieves binding info (layer id and tensor info) for the network output identified by the given layer name.
- virtual BindingPointInfo GetNetworkOutputBindingInfo(const std::string& name) const override;
-
- CaffeParserBase();
-
-protected:
- /// Adds an armnn layer to m_Network given a Caffe LayerParameter of the correct type
- /// and is responsible for recording any newly created IOutputSlots using SetArmnnOutputSlotForCaffeTop().
- /// @{
- void ParseInputLayer(const caffe::LayerParameter& layerParam);
- void ParseConvLayer(const caffe::LayerParameter& layerParam);
- void ParsePoolingLayer(const caffe::LayerParameter& layerParam);
- void ParseReluLayer(const caffe::LayerParameter& layerParam);
- void ParseLRNLayer(const caffe::LayerParameter& layerParam);
- void ParseInnerProductLayer(const caffe::LayerParameter& layerParam);
- void ParseSoftmaxLayer(const caffe::LayerParameter& layerParam);
- void ParseEltwiseLayer(const caffe::LayerParameter& layerParam);
- void ParseConcatLayer(const caffe::LayerParameter& layerParam);
- void ParseBatchNormLayer(const caffe::LayerParameter& layerParam);
- void ParseScaleLayer(const caffe::LayerParameter& layerParam);
- void ParseSplitLayer(const caffe::LayerParameter& layerParam);
- void ParseDropoutLayer(const caffe::LayerParameter& layerParam);
- /// @}
-
- /// ParseConv may use these helpers depending on the group parameter
- /// @{
- void AddConvLayerWithSplits(const caffe::LayerParameter& layerParam,
- const armnn::Convolution2dDescriptor & desc,
- unsigned int kernelW,
- unsigned int kernelH);
- void AddConvLayerWithDepthwiseConv(const caffe::LayerParameter& layerParam,
- const armnn::Convolution2dDescriptor & desc,
- unsigned int kernelW,
- unsigned int kernelH);
- /// @}
-
- /// Converts Caffe's protobuf tensor shape format to ArmNN's
- armnn::TensorInfo BlobShapeToTensorInfo(const caffe::BlobShape& blobShape) const;
-
- void TrackInputBinding(armnn::IConnectableLayer* layer,
- armnn::LayerBindingId id,
- const armnn::TensorInfo& tensorInfo);
-
- static void TrackBindingPoint(armnn::IConnectableLayer* layer, armnn::LayerBindingId id,
- const armnn::TensorInfo& tensorInfo,
- const char* bindingPointDesc,
- std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo);
-
- void TrackOutputBinding(armnn::IConnectableLayer* layer,
- armnn::LayerBindingId id,
- const armnn::TensorInfo& tensorInfo);
-
-
- void SetArmnnOutputSlotForCaffeTop(const std::string& caffeTopName, armnn::IOutputSlot& armnnOutputSlot);
-
- /// Retrieves the Armnn IOutputSlot representing the given Caffe top.
- /// Throws if it cannot be found (e.g. not parsed yet).
- armnn::IOutputSlot& GetArmnnOutputSlotForCaffeTop(const std::string& caffeTopName) const;
-
- static std::pair<armnn::LayerBindingId, armnn::TensorInfo> GetBindingInfo(
- const std::string& layerName,
- const char* bindingPointDesc,
- const std::unordered_map<std::string, BindingPointInfo>& bindingInfos);
-
-
- void Cleanup();
-
- using OperationParsingFunction = void(CaffeParserBase::*)(const caffe::LayerParameter& layerParam);
-
- /// Maps Caffe layer names to parsing member functions.
- static const std::map<std::string, OperationParsingFunction> ms_CaffeLayerNameToParsingFunctions;
-
- /// maps input layer names to their corresponding ids and tensor infos
- std::unordered_map<std::string, BindingPointInfo> m_NetworkInputsBindingInfo;
-
- /// maps output layer names to their corresponding ids and tensor infos
- std::unordered_map<std::string, BindingPointInfo> m_NetworkOutputsBindingInfo;
-
- armnn::INetworkPtr m_Network;
-
- std::map<std::string, armnn::TensorShape> m_InputShapes;
-
- /// As we add armnn layers we store the armnn IOutputSlot which corresponds to the Caffe tops.
- std::unordered_map<std::string, armnn::IOutputSlot*> m_ArmnnOutputSlotForCaffeTop;
-
- std::vector<std::string> m_RequestedOutputs;
-
-
- // Stuff which has gone to base class simply because we haven't done any
- // memory optimisation on the text/string format. If we move this to a layer
- // by layer parse as well these can move to the CaffeParser class.
- std::map<std::string, const caffe::LayerParameter*> m_CaffeLayersByTopName;
-
- /// Parses a NetParameter loaded into memory from one of the other CreateNetwork*
- armnn::INetworkPtr CreateNetworkFromNetParameter(
- caffe::NetParameter& netParam,
- const std::map<std::string, armnn::TensorShape>& inputShapes,
- const std::vector<std::string>& requestedOutputs);
-
- /// does the actual conversion from caffe::NetParameter to armnn::INetwork
- void LoadNetParam(caffe::NetParameter& netParameter);
-
- /// Find the Caffe layers listed as inputs (bottoms) for a given layer.
- std::vector<const caffe::LayerParameter*> GetInputs(const caffe::LayerParameter& layerParam);
-
- /// Modifies the Caffe network to replace "in-place" layers (whose top() and bottom() are both the same)
- /// with regular layers. This simplifies further parsing.
- void ResolveInPlaceLayers(caffe::NetParameter& netParameter);
-
-};
-
-class CaffeParser : public CaffeParserBase
-{
-public:
-
- /// Create the network from a protobuf binary file on disk
- virtual armnn::INetworkPtr CreateNetworkFromBinaryFile(
- const char* graphFile,
- const std::map<std::string, armnn::TensorShape>& inputShapes,
- const std::vector<std::string>& requestedOutputs) override;
-
-public:
- CaffeParser();
-
-};
-} \ No newline at end of file
diff --git a/src/armnnCaffeParser/CaffeSupport.md b/src/armnnCaffeParser/CaffeSupport.md
deleted file mode 100644
index 9e4f1fa993..0000000000
--- a/src/armnnCaffeParser/CaffeSupport.md
+++ /dev/null
@@ -1,37 +0,0 @@
-#Caffe layers supported by the Arm NN SDK
-This reference guide provides a list of Caffe layers the Arm NN SDK currently supports.
-
-Although some other neural networks might work, Arm tests the Arm NN SDK with Caffe implementations of the following neural networks:
-
-- AlexNet.
-- Cifar10.
-- Inception-BN.
-- Resnet_50, Resnet_101 and Resnet_152.
-- VGG_CNN_S, VGG_16 and VGG_19.
-- Yolov1_tiny.
-- Lenet.
-- MobileNetv1.
-
-The Arm NN SDK supports the following machine learning layers for Caffe networks:
-
-
-- BatchNorm, in inference mode.
-- Convolution, excluding the Dilation Size, Weight Filler, Bias Filler, Engine, Force nd_im2col, and Axis parameters.
-
- Caffe doesn't support depthwise convolution, the equivalent layer is implemented through the notion of groups. ArmNN supports groups this way:
- - when group=1, it is a normal conv2d
- - when group=#input_channels, we can replace it by a depthwise convolution
- - when group>1 && group<#input_channels, we need to split the input into the given number of groups, apply a separate convolution and then merge the results
-- Concat, along the channel dimension only.
-- Dropout, in inference mode.
-- Eltwise, excluding the coeff parameter.
-- Inner Product, excluding the Weight Filler, Bias Filler, Engine, and Axis parameters.
-- Input.
-- LRN, excluding the Engine parameter.
-- Pooling, excluding the Stochastic Pooling and Engine parameters.
-- ReLU.
-- Scale.
-- Softmax, excluding the Axis and Engine parameters.
-- Split.
-
-More machine learning layers will be supported in future releases. \ No newline at end of file
diff --git a/src/armnnCaffeParser/README.md b/src/armnnCaffeParser/README.md
deleted file mode 100644
index 89f0a3f586..0000000000
--- a/src/armnnCaffeParser/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-#Arm NN Caffe parser
-
-`armnnCaffeParser` is a library for loading neural networks defined in Caffe protobuf files into the Arm NN runtime.
-
-For more information about the Caffe layers that are supported, and the networks that have been tested, see [CaffeSupport.md](./CaffeSupport.md).
-
-Please note that certain deprecated Caffe features are not supported by the armnnCaffeParser. If you think that Arm NN should be able to load your model according to the list of supported layers in [CaffeSupport.md](./CaffeSupport.md), but you are getting strange error messages, then try upgrading your model to the latest format using Caffe, either by saving it to a new file or using the upgrade utilities in `caffe/tools`.
diff --git a/src/armnnCaffeParser/RecordByRecordCaffeParser.cpp b/src/armnnCaffeParser/RecordByRecordCaffeParser.cpp
deleted file mode 100644
index cb7943655d..0000000000
--- a/src/armnnCaffeParser/RecordByRecordCaffeParser.cpp
+++ /dev/null
@@ -1,733 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-
-#include "RecordByRecordCaffeParser.hpp"
-
-#include "armnn/Exceptions.hpp"
-#include "armnn/Utils.hpp"
-
-
-#include "GraphTopologicalSort.hpp"
-
-#include <boost/numeric/conversion/cast.hpp>
-
-// Caffe
-#include <google/protobuf/wire_format.h>
-
-
-//#include <stdio.h>
-#include <limits.h>
-#include <sstream>
-//#include <iostream>
-#include <fstream>
-
-namespace armnnCaffeParser
-{
-// class which holds information on the absolute position in the stream
-// of the data and the length of the data record.
-class VarLenDataInfo
-{
-public:
- VarLenDataInfo(std::streamoff positionOfData, size_t sizeOfData) :
- m_PositionOfData(positionOfData), m_SizeOfData(sizeOfData) {}
-
- VarLenDataInfo(const VarLenDataInfo& x) :
- m_PositionOfData(x.PositionOfData()), m_SizeOfData (x.SizeOfData()) {}
-
- VarLenDataInfo& operator=(const VarLenDataInfo& x)
- {
- // handle self assignment
- if (this == &x) {
- return *this;
- }
- m_PositionOfData = x.PositionOfData(); m_SizeOfData = x.SizeOfData(); return *this;
- }
-
- std::streamoff PositionOfData() const {return m_PositionOfData;}
- size_t SizeOfData() const {return m_SizeOfData;}
-
-private:
- std::streamoff m_PositionOfData;
- size_t m_SizeOfData;
-
-};
-
-// class which holds enough information on a LayerParameter in the Caffe protobuf
-// format to allow it to be resolved for in place layering and sorted topologically
-// prior to the entire record being parsed into memory.
-//
-// NOTE: function naming follows that of the protobuf classes these proxies are standing in for
-class LayerParameterInfo : public VarLenDataInfo
-{
-public:
- static const std::string INPUT;
- LayerParameterInfo(const VarLenDataInfo& varLenDataInfo) :
- VarLenDataInfo(varLenDataInfo.PositionOfData(), varLenDataInfo.SizeOfData()),
- m_newTops(false), m_newBottoms(false) {}
-
- LayerParameterInfo(std::streamoff positionOfData, size_t sizeOfData) :
- VarLenDataInfo(positionOfData, sizeOfData), m_newTops(false), m_newBottoms(false) {}
-
- LayerParameterInfo(const LayerParameterInfo& x) :
- VarLenDataInfo(x.PositionOfData(), x.SizeOfData()),
- m_name(x.m_name),
- m_type(x.m_type),
- m_tops(x.m_tops),
- m_bottoms(x.m_bottoms),
- m_newTops(x.m_newTops),
- m_newBottoms(x.m_newBottoms) {}
-
- LayerParameterInfo& operator=(const LayerParameterInfo& x)
- {
- if (this == &x) {
- return *this;
- }
- VarLenDataInfo::operator=(x);
- m_name = x.m_name;
- m_type = x.m_type;
- m_tops = x.m_tops;
- m_bottoms = x.m_bottoms;
- m_newTops = x.m_newTops;
- m_newBottoms = x.m_newBottoms;
- return *this;
- }
-
- const std::string name() const {return m_name;}
- void set_name(const std::unique_ptr<char[]>& theName, size_t length)
- {
- m_name = std::string(theName.get(), length);
- }
- void set_name(const std::string& theName) {m_name = theName;}
-
- const std::string type() const {return m_type;}
- void set_type(const std::unique_ptr<char[]>& theType, size_t length)
- {
- m_type = std::string(theType.get(), length);
- }
- void set_type(const std::string& theType) {m_type = theType;}
-
- void add_top(const std::unique_ptr<char[]>& top, size_t length)
- {
- std::string topName(top.get(), length);
- m_tops.push_back(topName);
- }
- void add_top(const std::string& topName)
- {
- m_tops.push_back(topName);
- }
- const std::string top(unsigned long i) const {return m_tops[i];}
- unsigned long top_size() const {return m_tops.size();}
- void set_top(unsigned long i, const std::string& newName) {m_tops[i] = newName; m_newTops = true;}
- bool new_tops() const {return m_newTops;}
-
- void add_bottom(const std::unique_ptr<char[]>& bottom, size_t length)
- {
- std::string bottomName(bottom.get(), length);
- m_bottoms.push_back(bottomName);
- }
- unsigned long bottom_size() const {return m_bottoms.size();}
- const std::string bottom(unsigned long i) const {return m_bottoms[i];}
- void set_bottom(unsigned long i, const std::string& newName) {m_bottoms[i] = newName; m_newBottoms = true;}
- bool new_bottoms() const {return m_newBottoms;}
-
- // if the position and size of the data is zero and the type is "Input" then this is an 'Implicit Input Layer'
- // and needs to be handled differently from ordinary layers.
- bool isImplicitInputLayer() const
- {
- if ((PositionOfData() == 0) && (SizeOfData() == 0) && INPUT.compare(type()) == 0)
- {return true;} else {return false;}
- }
-
-private:
- std::string m_name;
- std::string m_type;
- std::vector<std::string> m_tops;
- std::vector<std::string> m_bottoms;
- // mark the layers whose topology was changed
- // by the ResolveInPlaceLayers method.
- bool m_newTops;
- bool m_newBottoms;
-};
-
-// class which holds the field type (wire type) and field id (id from the .proto schema)
-// read from the protobuf messages as per the binary encoding described in
-// https://developers.google.com/protocol-buffers/docs/encoding
-//
-// NOTE: function naming follows that of the protobuf classes these proxies are standing in for
-class ProtobufFieldInfo
-{
-public:
- ProtobufFieldInfo(int field_type, int field_id) :
- m_eof(false), m_field_type(field_type), m_field_id(field_id) {}
- ProtobufFieldInfo() : m_eof(true), m_field_type(0), m_field_id(0) {}
-
- bool eof() {return m_eof;}
- int field_type() {return m_field_type;}
- int field_id() {return m_field_id;}
-
-private:
- bool m_eof;
- int m_field_type;
- int m_field_id;
-};
-
-
-// There are some NetParameter level data which are required
-// to correctly processes some Caffe models. Specifically those which
-// have 'implicit' input layers. Also it is nice to have the name of the model.
-//
-// NOTE: function naming follows that of the protobuf classes these proxies are standing in for
-class NetParameterInfo
-{
-public:
- const std::string name() const {return m_name;}
- void set_name(const std::unique_ptr<char[]>& theName, size_t length)
- {
- m_name = std::string(theName.get(), length);
- }
-
- void add_input(const std::unique_ptr<char[]>& input, size_t length)
- {
- std::string inputName(input.get(), length);
- m_inputs.push_back(inputName);
- }
- const std::string input(unsigned long i) const {return m_inputs[i];}
- unsigned long input_size() const {return m_inputs.size();}
-
- void add_input_dimension(int input_dimension) {
- m_input_dimensions.push_back(input_dimension);
- }
- int input_dimension(unsigned long i) const {return m_input_dimensions[i];}
- unsigned long input_dimensions_size() const {return m_input_dimensions.size();}
-
- void add_blob_shape(caffe::BlobShape shape) {
- m_blob_shapes.push_back(shape);
- }
- const caffe::BlobShape blob_shape(unsigned long i) const {return m_blob_shapes[i];}
- unsigned long blob_shapes_size() const {return m_blob_shapes.size();}
-
-private:
- std::string m_name;
- std::vector<std::string> m_inputs;
- std::vector<int> m_input_dimensions;
- std::vector<caffe::BlobShape> m_blob_shapes;
-
-};
-
-}; // namespace armnnCaffeParser
-
-using namespace armnnCaffeParser;
-
-// Initialise the class const
-const std::string LayerParameterInfo::INPUT = "Input";
-
-namespace
-{
-
-ProtobufFieldInfo readFieldInfo(std::ifstream& ifs)
-{
- unsigned char first_byte = static_cast<unsigned char>(ifs.get());
- if (!ifs.good())
- {
- ProtobufFieldInfo eof;
- return eof;
- }
- int field_type = first_byte&7;
- int field_id = first_byte>>3;
- if ((field_id & 16) == 16)
- {
- unsigned char second_byte = static_cast<unsigned char>(ifs.get());
- if (!ifs.good())
- {
- ProtobufFieldInfo eof;
- return eof;
- }
- field_id = (field_id-16) + ((second_byte&127)<<4);
- }
- ProtobufFieldInfo fieldInfo(field_type, field_id);
- return fieldInfo;
-}
-
-const static int MAX_NUM_BYTES = 5;
-
-int ReadBase128(std::ifstream& ifs)
-{
- int result = 0;
- unsigned int shift_by = 0;
- int bytesRead = 0;
- while (true)
- {
- unsigned char a_byte = static_cast<unsigned char>(ifs.get());
- ++bytesRead;
- if (bytesRead > MAX_NUM_BYTES)
- {
- throw armnn::ParseException(
- "ReadBase128 exceeded the maximum number of bytes expected for an integer representation");
- }
- result += (a_byte & 127) << shift_by;
- shift_by += 7;
- if ((a_byte & 128) != 128)
- {
- break;
- }
- }
- return result;
-}
-
-
-std::unique_ptr<char[]> AllocateBuffer(std::ifstream& ifs, VarLenDataInfo& dataInfo)
-{
- std::unique_ptr<char[]> ptr(new char[dataInfo.SizeOfData()]);
- ifs.clear();
- ifs.seekg(dataInfo.PositionOfData(), std::ios_base::beg);
- ifs.read(ptr.get(), boost::numeric_cast<std::streamsize>(dataInfo.SizeOfData()));
- return ptr;
-}
-
-VarLenDataInfo CreateVarLenDataInfo(std::streamoff bufferStart, std::streamoff endOfLayer) {
- std::streamoff sizeOfLayer = endOfLayer - bufferStart;
- if (sizeOfLayer < 0)
- {
- std::stringstream ss;
- ss << "error when determining buffer size, negative value [" << sizeOfLayer << "]";
- throw armnn::ParseException(ss.str());
- }
- // NOTE: as some of the data being read in will be translated into strings (names of layers etc)
- // the maximum size we can deal with is the upper size limit of a string i.e. size_t
- // on the platform in which I am currently compiling std::streamoff is signed long int and
- // size_t is unsigned long int so there is no way this error condition can fire but this stuff
- // is supposed to be portable so the check remains in place
- if (boost::numeric_cast<size_t>(sizeOfLayer) > SIZE_MAX) {
- std::stringstream ss;
- ss << "layer is greater than " << SIZE_MAX << " in size cannot process. layer size = [" << sizeOfLayer << "]";
- throw armnn::ParseException(ss.str());
- }
- LayerParameterInfo info(bufferStart, boost::numeric_cast<size_t>(sizeOfLayer));
- return info;
-}
-
-void ReadTopologicalInfoForLayerParameter(LayerParameterInfo& layerInfo, std::ifstream& ifs)
-{
- // position the file pointer to the start of the layer data
- ifs.clear();
- ifs.seekg(layerInfo.PositionOfData(), std::ios_base::beg);
- std::streamoff endOfLayer = layerInfo.PositionOfData() +
- boost::numeric_cast<std::streamoff>(layerInfo.SizeOfData());
- while(true)
- {
- // check to see if we have reached the end of the record
- std::streamoff currentPosition = ifs.tellg();
- if (currentPosition >= endOfLayer) {
- return;
- }
- // read the information for the next field.
- ProtobufFieldInfo fieldInfo = readFieldInfo(ifs);
- if (fieldInfo.eof())
- {
- return;
- // TODO: figure out whether this is an error condition or not...
- //throw armnn::ParseException("failed to read field from LayerParameter data");
- }
- // process the field
- switch (fieldInfo.field_type())
- {
- case 0:
- {
- ReadBase128(ifs);
- break;
- }
- case 2:
- {
- int size = ReadBase128(ifs);
- std::streamoff posStartOfData = ifs.tellg();
- VarLenDataInfo dataInfo(posStartOfData, boost::numeric_cast<size_t>(size));
- //optional string name = 1; // the layer name
- //optional string type = 2; // the layer type
- //repeated string bottom = 3; // the name of each bottom blob
- //repeated string top = 4; // the name of each top blob
- if (fieldInfo.field_id() == 1)
- {
- // read and set the name of the layer
- auto layerName = AllocateBuffer(ifs, dataInfo);
- layerInfo.set_name(layerName, dataInfo.SizeOfData());
- }
- else if (fieldInfo.field_id() == 2)
- {
- // read and set the type of the layer
- auto layerType = AllocateBuffer(ifs, dataInfo);
- layerInfo.set_type(layerType, dataInfo.SizeOfData());
- }
- else if (fieldInfo.field_id() == 3)
- {
- // read and add a bottom to the layer
- auto bottom = AllocateBuffer(ifs, dataInfo);
- layerInfo.add_bottom(bottom, dataInfo.SizeOfData());
- }
- else if (fieldInfo.field_id() == 4)
- {
- // read and add a top to the layer
- auto top = AllocateBuffer(ifs, dataInfo);
- layerInfo.add_top(top, dataInfo.SizeOfData());
- }
- else
- {
- ifs.seekg(size, std::ios_base::cur);
- if (!ifs.good())
- {
- // TODO: error out?
- return;
- }
- }
- break;
- }
- case 1:
- {
- // 64 bit
- // advance by eight bytes
- ifs.seekg(8, std::ios_base::cur);
- if (!ifs.good())
- {
- // TODO: error out?
- return;
- }
- break;
- }
- case 5:
- {
- // 32 bit
- // advance by four bytes
- ifs.seekg(4, std::ios_base::cur);
- if (!ifs.good())
- {
- // TODO: error out?
- return;
- }
- break;
- }
- default:
- {
- throw armnn::ParseException("Encounted an unknown field type");
- break;
- }
- }
- }
-}
-
-void ResolveInPlaceLayers(std::vector<LayerParameterInfo>& layerInfo)
-{
- std::map<std::string, std::vector<LayerParameterInfo*>> layersByTop;
- for (auto& info : layerInfo)
- {
- for (unsigned long i = 0; i < info.top_size(); ++i)
- {
- layersByTop[info.top(i)].push_back(&info);
- }
- }
- // For each set of layers with the same top, resolve them to a linear chain rather than in-place layers.
- // Note that for 'regular' layers, there will be a single layer in each group and so this will be a no-op.
- for (auto& layersWithSameTopIterator : layersByTop)
- {
- const std::string& top = layersWithSameTopIterator.first;
- const std::vector<LayerParameterInfo*> layersWithSameTop = layersWithSameTopIterator.second;
-
- // Chain the layers together in the order that they are listed in the prototxt (hopefully this is correct).
- // Note that the last layer will not have its top modified so that other layers will continue to reference it.
- for (unsigned int layerIdx = 0; layerIdx < layersWithSameTop.size() - 1; ++layerIdx)
- {
- LayerParameterInfo* layer1 = layersWithSameTop[layerIdx];
- LayerParameterInfo* layer2 = layersWithSameTop[layerIdx + 1];
- if (layer1->top_size() != 1)
- {
- throw armnn::ParseException("Node '" + layer1->name() + "' is an in-place layer but "
- "doesn't have exactly one top.");
- }
- std::string newTop = layer1->name() + "_top";
- layer1->set_top(0, newTop);
- if (layer2->bottom_size() != 1 || layer2->bottom(0) != top)
- {
- throw armnn::ParseException("Node '" + layer2->name() + "' is an in-place layer but "
- " doesn't have exactly one bottom, or it doesn't match its top.");
- }
- layer2->set_bottom(0, newTop);
-
- }
- }
-}
-
-} // anonymous namespace, can't be seen outside this source file
-
-RecordByRecordCaffeParser::RecordByRecordCaffeParser() : CaffeParserBase()
-{}
-
-armnn::INetworkPtr RecordByRecordCaffeParser::CreateNetworkFromBinaryFile(
- const char* graphFile,
- const std::map<std::string, armnn::TensorShape>& inputShapes,
- const std::vector<std::string>& requestedOutputs)
-{
-
- m_InputShapes = inputShapes;
- if (requestedOutputs.size() == 0)
- {
- throw armnn::ParseException("requestedOutputs must have at least one entry");
- }
- m_RequestedOutputs = requestedOutputs;
-
- std::ifstream ifs(graphFile, std::ifstream::in|std::ifstream::binary);
- if (ifs.fail())
- {
- throw armnn::FileNotFoundException("Failed to open graph file '" + std::string(graphFile) + "'");
- }
-
- std::vector<LayerParameterInfo> layerInfo;
- NetParameterInfo netParameterInfo;
- while(true)
- {
- ProtobufFieldInfo fieldInfo = readFieldInfo(ifs);
- if (fieldInfo.eof())
- {
- break;
- }
- switch(fieldInfo.field_type())
- {
- case 0:
- {
- ReadBase128(ifs);
- break;
- }
- case 2:
- {
- // The values of interest from the caffe.proto schema are:
- // optional string name = 1; // consider giving the network a name
- // DEPRECATED. See InputParameter. The input blobs to the network.
- // repeated string input = 3;
- // DEPRECATED. See InputParameter. The shape of the input blobs.
- // repeated BlobShape input_shape = 8;
-
- // 4D input dimensions -- deprecated. Use "input_shape" instead.
- // If specified, for each input blob there should be four
- // values specifying the num, channels, height and width of the input blob.
- // Thus, there should be a total of (4 * #input) numbers.
- // repeated int32 input_dim = 4;
-
- // The layers that make up the net. Each of their configurations, including
- // connectivity and behavior, is specified as a LayerParameter.
- // repeated LayerParameter layer = 100; // ID 100 so layers are printed last.
-
- // The first four will (if present) be read into the NetParameterInfo
- // the LayerParameters will be read into the LayerParameterInfo vector.
-
- int size = ReadBase128(ifs);
- std::streamoff posStartOfData = ifs.tellg();
- ifs.seekg(size, std::ios_base::cur);
- if(!ifs.good())
- {
- throw armnn::ParseException("failed to seek ahead in binary caffe file");
- }
- std::streamoff endOfLayer = ifs.tellg();
- if (fieldInfo.field_id() == 1)
- {
- VarLenDataInfo dataInfo = CreateVarLenDataInfo(posStartOfData, endOfLayer);
- auto graphName = AllocateBuffer(ifs, dataInfo);
- netParameterInfo.set_name(graphName, dataInfo.SizeOfData());
- }
- if (fieldInfo.field_id() == 3)
- {
- VarLenDataInfo dataInfo = CreateVarLenDataInfo(posStartOfData, endOfLayer);
- auto inputName = AllocateBuffer(ifs, dataInfo);
- netParameterInfo.add_input(inputName, dataInfo.SizeOfData());
- }
- if (fieldInfo.field_id() == 8)
- {
- VarLenDataInfo dataInfo = CreateVarLenDataInfo(posStartOfData, endOfLayer);
- auto inputShape = AllocateBuffer(ifs, dataInfo);
- caffe::BlobShape blobShape;
- bool bRet = blobShape.ParseFromArray(inputShape.get(), static_cast<int>(dataInfo.SizeOfData()));
- if (!bRet)
- {
- throw armnn::ParseException("Failed to parse input shape");
- }
- netParameterInfo.add_blob_shape(blobShape);
- }
- if (fieldInfo.field_id() == 4)
- {
- int input_dim = ReadBase128(ifs);
- netParameterInfo.add_input_dimension(input_dim);
- }
- if (fieldInfo.field_id() == 100)
- {
- LayerParameterInfo info(CreateVarLenDataInfo(posStartOfData, endOfLayer));
- ReadTopologicalInfoForLayerParameter(info, ifs);
- layerInfo.push_back(info);
- }
- break;
- }
- default:
- {
- break;
- }
- }
- }
- std::vector<const LayerParameterInfo*> sortedNodes;
- ProcessLayers(netParameterInfo, layerInfo, m_RequestedOutputs, sortedNodes);
- armnn::INetworkPtr networkPtr = LoadLayers(ifs, sortedNodes, netParameterInfo);
- return networkPtr;
-
-}
-
-void RecordByRecordCaffeParser::ProcessLayers(
- const NetParameterInfo& netParameterInfo,
- std::vector<LayerParameterInfo>& layerInfo,
- const std::vector<std::string>& m_RequestedOutputs,
- std::vector<const LayerParameterInfo*>& sortedNodes)
-{
- // if there is an implicit input layer add it to the layerInfo list
- if (netParameterInfo.input_size() > 0)
- {
- LayerParameterInfo implicitInputLayer(0, 0);
- implicitInputLayer.set_type(LayerParameterInfo::INPUT);
- implicitInputLayer.set_name(netParameterInfo.input(0));
- implicitInputLayer.add_top(netParameterInfo.input(0));
- layerInfo.push_back(implicitInputLayer);
- }
- ::ResolveInPlaceLayers(layerInfo);
-
- for (LayerParameterInfo& info : layerInfo)
- {
- for (unsigned long i = 0; i < info.top_size(); ++i)
- {
- m_CaffeLayersByTopName[info.top(i)] = &info;
- }
- }
-
- // Find the output layers the user requested
- std::vector<const LayerParameterInfo*> targetLayers;
- for (const std::string& requestedOutputName : m_RequestedOutputs)
- {
- auto nodeIt = m_CaffeLayersByTopName.find(requestedOutputName);
- if (nodeIt == m_CaffeLayersByTopName.end())
- {
- throw armnn::ParseException(
- "Couldn't find requested output layer '" + requestedOutputName + "' in graph");
- }
- targetLayers.push_back(nodeIt->second);
- }
-
- // Sort them into a linear ordering such that all inputs of a node are before the node itself
- if (!armnnUtils::GraphTopologicalSort<const LayerParameterInfo*>(
- targetLayers,
- [this](const LayerParameterInfo* node)
- {
- return GetInputs(*node);
- },
- sortedNodes))
- {
- throw armnn::ParseException("Cycle detected in graph");
- }
-}
-
-
-std::vector<const LayerParameterInfo*> RecordByRecordCaffeParser::GetInputs(
- const LayerParameterInfo& layerParam)
-{
- std::vector<const LayerParameterInfo*> ret;
- ret.reserve(layerParam.bottom_size());
- for (unsigned long j = 0; j < layerParam.bottom_size(); ++j)
- {
- std::string inputName = layerParam.bottom(j);
- auto inputIt = m_CaffeLayersByTopName.find(inputName);
- if (inputIt == m_CaffeLayersByTopName.end())
- {
- throw armnn::ParseException(
- "Can't find Caffe layer with top called '" + inputName + "', which is listed as an input of '" +
- layerParam.name() + "'");
- }
- ret.push_back(inputIt->second);
- }
-
- return ret;
-}
-
-armnn::INetworkPtr RecordByRecordCaffeParser::LoadLayers(std::ifstream& ifs,
- std::vector<const LayerParameterInfo *>& sortedNodes,
- const NetParameterInfo& netParameterInfo)
-{
-
- m_NetworkInputsBindingInfo.clear();
- m_NetworkOutputsBindingInfo.clear();
-
- m_Network = armnn::INetwork::Create();
-
- for (auto info : sortedNodes)
- {
- caffe::LayerParameter layer;
- if (info->isImplicitInputLayer())
- {
- // create the matching Layer Parameter programatically from the data in the
- // net parameter info which has been passed in...
- layer.set_type(LayerParameterInfo::INPUT);
- layer.set_name(netParameterInfo.input(0));
- layer.add_top(netParameterInfo.input(0));
-
- caffe::InputParameter* inputParam = layer.mutable_input_param();
- caffe::BlobShape* shape = inputParam->add_shape();
-
- long unsigned int dim_size = netParameterInfo.input_dimensions_size();
- for (long unsigned int i = 0; i < dim_size; ++i)
- {
- shape->add_dim(netParameterInfo.input_dimension(i));
- }
- }
- else
- {
- char *buffer = new char[info->SizeOfData()];
- ifs.clear();
- ifs.seekg(info->PositionOfData(), std::ios_base::beg);
- ifs.read(buffer, boost::numeric_cast<std::streamsize>(info->SizeOfData()));
- bool bRet = layer.ParseFromArray(buffer, static_cast<int>(info->SizeOfData()));
- delete[] buffer;
- if (!bRet)
- {
- throw armnn::ParseException("Failed to parse layer [" + info->name() + "]");
- }
- }
-
- if (info->new_tops())
- {
- //update the tops
- layer.set_top(0, info->top(0));
- }
- if (info->new_bottoms())
- {
- //update the bottoms
- layer.set_bottom(0, info->bottom(0));
- }
-
- auto it = ms_CaffeLayerNameToParsingFunctions.find(layer.type());
- if (it == ms_CaffeLayerNameToParsingFunctions.end())
- {
- throw armnn::ParseException("Unsupported layer type '" + layer.type() + "'");
- }
- auto func = it->second;
- (this->*func)(layer);
- }
- ifs.close();
-
- // Add ArmNN output layers connected to each requested output
- for (const std::string& requestedOutput : m_RequestedOutputs)
- {
- armnn::IOutputSlot& outputSlot = GetArmnnOutputSlotForCaffeTop(requestedOutput);
-
- const armnn::LayerBindingId outputId = boost::numeric_cast<armnn::LayerBindingId>(
- m_NetworkOutputsBindingInfo.size());
- armnn::IConnectableLayer* const outputLayer = m_Network->AddOutputLayer(outputId, requestedOutput.c_str());
- outputSlot.Connect(outputLayer->GetInputSlot(0));
-
- TrackOutputBinding(outputLayer, outputId, outputLayer->GetInputSlot(0).GetConnection()->GetTensorInfo());
- }
-
- Cleanup();
-
- return move(m_Network);
-}
diff --git a/src/armnnCaffeParser/RecordByRecordCaffeParser.hpp b/src/armnnCaffeParser/RecordByRecordCaffeParser.hpp
deleted file mode 100644
index 361d6f428d..0000000000
--- a/src/armnnCaffeParser/RecordByRecordCaffeParser.hpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-
-#pragma once
-
-#include <string>
-#include <vector>
-#include <iostream>
-
-#include "caffe/proto/caffe.pb.h"
-
-#include "CaffeParser.hpp"
-
-
-
-namespace armnnCaffeParser
-{
-
-class NetParameterInfo;
-class LayerParameterInfo;
-
-
-class RecordByRecordCaffeParser : public CaffeParserBase
-{
-public:
-
- /// Create the network from a protobuf binary file on disk
- virtual armnn::INetworkPtr CreateNetworkFromBinaryFile(
- const char* graphFile,
- const std::map<std::string, armnn::TensorShape>& inputShapes,
- const std::vector<std::string>& requestedOutputs) override;
-
- RecordByRecordCaffeParser();
-
-private:
- void ProcessLayers(const NetParameterInfo& netParameterInfo,
- std::vector<LayerParameterInfo>& layerInfo,
- const std::vector<std::string>& m_RequestedOutputs,
- std::vector<const LayerParameterInfo*>& sortedNodes);
- armnn::INetworkPtr LoadLayers(std::ifstream& ifs,
- std::vector<const LayerParameterInfo *>& sortedNodes,
- const NetParameterInfo& netParameterInfo);
- std::vector<const LayerParameterInfo*> GetInputs(
- const LayerParameterInfo& layerParam);
-
- std::map<std::string, const LayerParameterInfo*> m_CaffeLayersByTopName;
- std::vector<std::string> m_RequestedOutputs;
-};
-
-} // namespace armnnCaffeParser
-
diff --git a/src/armnnCaffeParser/test/TestAdd.cpp b/src/armnnCaffeParser/test/TestAdd.cpp
deleted file mode 100644
index 01fb9c4ddc..0000000000
--- a/src/armnnCaffeParser/test/TestAdd.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#include <boost/test/unit_test.hpp>
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#include "ParserPrototxtFixture.hpp"
-
-BOOST_AUTO_TEST_SUITE(CaffeParser)
-
-struct AddFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
-{
- AddFixture()
- {
- m_Prototext = "name: \"MinimalAdd\"\n"
- "layer {\n"
- " name: \"data\"\n"
- " type: \"Input\"\n"
- " top: \"data\"\n"
- " input_param { shape: { dim: 1 dim: 1 dim: 4 dim: 4 } }\n"
- "}\n"
- "layer {\n"
- " bottom: \"data\"\n"
- " top: \"pool1\"\n"
- " name: \"pool1\"\n"
- " type: \"Pooling\"\n"
- " pooling_param {\n"
- " kernel_size: 2\n"
- " stride: 2\n"
- " pool: MAX\n"
- " }\n"
- "}\n"
- "layer {\n"
- " bottom: \"data\"\n"
- " top: \"pool2\"\n"
- " name: \"pool2\"\n"
- " type: \"Pooling\"\n"
- " pooling_param {\n"
- " kernel_size: 2\n"
- " stride: 2\n"
- " pool: MAX\n"
- " }\n"
- "}\n"
- "layer {\n"
- " bottom: \"pool1\"\n"
- " bottom: \"pool2\"\n"
- " top: \"add\"\n"
- " name: \"add\"\n"
- " type: \"Eltwise\"\n"
- "}\n";
- SetupSingleInputSingleOutput("data", "add");
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(ParseAdd, AddFixture)
-{
- RunTest<4>(
- {
- 0, 1, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 1, 0,
- 1, 0, 1, 1
- },
- {
- 2, 0,
- 2, 2
- });
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/armnnCaffeParser/test/TestConcat.cpp b/src/armnnCaffeParser/test/TestConcat.cpp
deleted file mode 100644
index 5843cc06a5..0000000000
--- a/src/armnnCaffeParser/test/TestConcat.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#include <boost/test/unit_test.hpp>
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#include "ParserPrototxtFixture.hpp"
-
-BOOST_AUTO_TEST_SUITE(CaffeParser)
-
-struct ConcatFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
-{
- ConcatFixture()
- {
- m_Prototext = "name: \"Concat\"\n"
- "layer {\n"
- " name: \"data\"\n"
- " type: \"Input\"\n"
- " top: \"data\"\n"
- " input_param { shape: { dim: 1 dim: 1 dim: 4 dim: 4 } }\n"
- "}\n"
- "layer {\n"
- " bottom: \"data\"\n"
- " top: \"pool1\"\n"
- " name: \"pool1\"\n"
- " type: \"Pooling\"\n"
- " pooling_param {\n"
- " kernel_size: 2\n"
- " stride: 2\n"
- " pool: MAX\n"
- " }\n"
- "}\n"
- "layer {\n"
- " bottom: \"data\"\n"
- " top: \"pool2\"\n"
- " name: \"pool2\"\n"
- " type: \"Pooling\"\n"
- " pooling_param {\n"
- " kernel_size: 2\n"
- " stride: 2\n"
- " pool: MAX\n"
- " }\n"
- "}\n"
- "layer {\n"
- " bottom: \"pool1\"\n"
- " bottom: \"pool2\"\n"
- " top: \"concat\"\n"
- " name: \"concat\"\n"
- " type: \"Concat\"\n"
- "}\n";
- SetupSingleInputSingleOutput("data", "concat");
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(ParseConcat, ConcatFixture)
-{
- RunTest<4>(
- {
- 0, 1, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 1, 0,
- 1, 0, 1, 1
- },
- {
- 1, 0,
- 1, 1,
-
- 1, 0,
- 1, 1
- });
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/armnnCaffeParser/test/TestConvolution.cpp b/src/armnnCaffeParser/test/TestConvolution.cpp
deleted file mode 100644
index b881f1f58f..0000000000
--- a/src/armnnCaffeParser/test/TestConvolution.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#include <boost/test/unit_test.hpp>
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#include "ParserPrototxtFixture.hpp"
-#include <sstream>
-#include <initializer_list>
-
-namespace
-{
-
-template <typename T>
-std::string TaggedSequence(const std::string & tag, const std::initializer_list<T> & data)
-{
- bool first = true;
- std::stringstream ss;
- for (auto && d : data)
- {
- if (!first)
- {
- ss << " , ";
- }
- else
- {
- first = false;
- }
- ss << " " << tag << " : " << d << " ";
- }
- return ss.str();
-}
-
-template <typename T>
-std::string TaggedSequence(const std::string & tag, T data, unsigned int n)
-{
- std::stringstream ss;
- for (unsigned int i=0; i<n; ++i)
- {
- if (i>0)
- {
- ss << " , ";
- }
- ss << " " << tag << " : " << data << " ";
- }
- return ss.str();
-}
-
-} // namespace <anonymous>
-
-BOOST_AUTO_TEST_SUITE(CaffeParser)
-
-struct ConvolutionFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
-{
- ConvolutionFixture(const std::initializer_list<unsigned int> & inputDims,
- const std::initializer_list<float> & filterData,
- unsigned int kernelSize,
- unsigned int numOutput=1,
- unsigned int group=1)
- {
- m_Prototext = R"(
- name: "ConvolutionTest"
- layer {
- name: "input1"
- type: "Input"
- top: "input1"
- input_param { shape: { )" + TaggedSequence("dim", inputDims) + R"( } }
- }
- layer {
- name: "conv1"
- type: "Convolution"
- bottom: "input1"
- top: "conv1"
- blobs: { )" + TaggedSequence("data", filterData) + R"( }
- blobs: { )" + TaggedSequence("data", 0, numOutput) + R"( }
- convolution_param {
- num_output: )" + std::to_string(numOutput) + R"(
- pad: 0
- kernel_size: )" + std::to_string(kernelSize) + R"(
- stride: 1
- group: )" + std::to_string(group) + R"(
- }
- }
- )";
- SetupSingleInputSingleOutput("input1", "conv1");
- }
-};
-
-struct SimpleConvolutionFixture : public ConvolutionFixture
-{
- SimpleConvolutionFixture()
- : ConvolutionFixture( {1, 1, 2, 2}, {1.0f, 1.0f, 1.0f, 1.0f}, 2)
- {
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(SimpleConvolution, SimpleConvolutionFixture)
-{
- RunTest<4>({ 1, 3, 5, 7 }, { 16 });
-}
-
-struct GroupConvolutionFixture : public ConvolutionFixture
-{
- GroupConvolutionFixture()
- : ConvolutionFixture(
- {1, 2, 2, 2},
- {
- 1.0f, 1.0f, 1.0f, 1.0f, // filter for channel #0
- 2.0f, 2.0f, 2.0f, 2.0f // filter for channel #1
- },
- 2, // kernel size is 2x2
- 2, // number of output channels is 2
- 2) // number of groups (separate filters)
- {
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(GroupConvolution, GroupConvolutionFixture)
-{
- RunTest<4>(
- {
- 1, 2, 3, 4, // input channel #0
- 5, 6, 7, 8, // input channel #1
- },
- {
- 10, // convolution result for channel #0 applying filter #0
- 52 // same for channel #1 and filter #1
- }
- );
-}
-
-
-BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file
diff --git a/src/armnnCaffeParser/test/TestDropout.cpp b/src/armnnCaffeParser/test/TestDropout.cpp
deleted file mode 100644
index 503766248b..0000000000
--- a/src/armnnCaffeParser/test/TestDropout.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-
-#include <boost/test/unit_test.hpp>
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#include "ParserPrototxtFixture.hpp"
-
-BOOST_AUTO_TEST_SUITE(CaffeParser)
-
-struct DropoutFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
-{
- DropoutFixture()
- {
- m_Prototext = "name: \"DropoutFixture\"\n"
- "layer {\n"
- " name: \"data\"\n"
- " type: \"Input\"\n"
- " top: \"data\"\n"
- " input_param { shape: { dim: 1 dim: 1 dim: 2 dim: 2 } }\n"
- "}\n"
- "layer {\n"
- " bottom: \"data\"\n"
- " top: \"drop1\"\n"
- " name: \"drop1\"\n"
- " type: \"Dropout\"\n"
- "}\n"
- "layer {\n"
- " bottom: \"drop1\"\n"
- " bottom: \"drop1\"\n"
- " top: \"add\"\n"
- " name: \"add\"\n"
- " type: \"Eltwise\"\n"
- "}\n";
- SetupSingleInputSingleOutput("data", "add");
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(ParseDropout, DropoutFixture)
-{
- RunTest<4>(
- {
- 1, 2,
- 3, 4,
- },
- {
- 2, 4,
- 6, 8
- });
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/armnnCaffeParser/test/TestInPlace.cpp b/src/armnnCaffeParser/test/TestInPlace.cpp
deleted file mode 100644
index 5a8dd787b6..0000000000
--- a/src/armnnCaffeParser/test/TestInPlace.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#include <boost/test/unit_test.hpp>
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#include "ParserPrototxtFixture.hpp"
-
-BOOST_AUTO_TEST_SUITE(CaffeParser)
-
-// The pooling layer should take its input from the relu, not the add directly.
-struct InPlaceFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
-{
- InPlaceFixture()
- {
- m_Prototext = R"(
-name: "InPlace"
-layer {
- name: "data"
- type: "Input"
- top: "data"
- input_param { shape: { dim: 1 dim: 1 dim: 1 dim: 1 } }
-}
-layer {
- bottom: "data"
- bottom: "data"
- top: "add"
- name: "add"
- type: "Eltwise"
-}
-layer {
- name: "relu"
- type: "ReLU"
- bottom: "add"
- top: "relu"
- phase: TEST
-}
-layer {
- name: "pool"
- type: "Pooling"
- bottom: "relu"
- top: "pool"
- phase: TEST
- pooling_param {
- pool: MAX
- kernel_size: 1
- stride: 1
- }
-}
- )";
- SetupSingleInputSingleOutput("data", "pool");
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(ParseInPlace, InPlaceFixture)
-{
- RunTest<1>({ -1.0f }, { 0.0f });
-}
-
-// The requested output of the network is a layer which has an activation attached.
-// The output of the network should therefore actually be the activation layer.
-struct InPlaceOutputFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
-{
- InPlaceOutputFixture()
- {
- m_Prototext = R"(
-name: "InPlace"
-layer {
- name: "data"
- type: "Input"
- top: "data"
- input_param { shape: { dim: 1 dim: 1 dim: 1 dim: 1 } }
-}
-layer {
- bottom: "data"
- bottom: "data"
- top: "add"
- name: "add"
- type: "Eltwise"
-}
-layer {
- name: "relu"
- type: "ReLU"
- bottom: "add"
- top: "add"
- phase: TEST
-}
- )";
- SetupSingleInputSingleOutput("data", "add");
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(InPlaceOutput, InPlaceOutputFixture)
-{
- RunTest<1>({ -1.0f }, { 0.0f });
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/armnnCaffeParser/test/TestInputs.cpp b/src/armnnCaffeParser/test/TestInputs.cpp
deleted file mode 100644
index bd13a0984d..0000000000
--- a/src/armnnCaffeParser/test/TestInputs.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#include <boost/test/unit_test.hpp>
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#include "armnn/IRuntime.hpp"
-#include "armnn/INetwork.hpp"
-#include "armnn/Exceptions.hpp"
-
-#include "test/TensorHelpers.hpp"
-
-#include <string>
-
-#include "ParserPrototxtFixture.hpp"
-
-BOOST_AUTO_TEST_SUITE(CaffeParser)
-
-
-BOOST_AUTO_TEST_CASE(InputShapes)
-{
- std::string explicitInput = "name: \"Minimal\"\n"
- "layer {\n"
- " name: \"data\"\n"
- " type: \"Input\"\n"
- " top: \"data\"\n"
- " input_param { shape: { dim: 1 dim: 2 dim: 3 dim: 4 } }\n"
- "}";
- std::string implicitInput = "name: \"Minimal\"\n"
- "input: \"data\" \n"
- "input_dim: 1 \n"
- "input_dim: 2 \n"
- "input_dim: 3 \n"
- "input_dim: 4 \n";
- std::string implicitInputNoShape = "name: \"Minimal\"\n"
- "input: \"data\" \n";
-
- armnn::IRuntime::CreationOptions options;
- armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
- armnnCaffeParser::ICaffeParserPtr parser(armnnCaffeParser::ICaffeParser::Create());
- armnn::INetworkPtr network(nullptr, nullptr);
- armnn::NetworkId netId;
-
- // Check everything works normally
- std::vector<armnn::BackendId> backends = {armnn::Compute::CpuRef};
- {
- network = parser->CreateNetworkFromString(explicitInput.c_str(), {}, { "data" });
- BOOST_TEST(network.get());
- runtime->LoadNetwork(netId, Optimize(*network, backends, runtime->GetDeviceSpec()));
-
- armnnCaffeParser::BindingPointInfo inputBindingInfo = parser->GetNetworkInputBindingInfo("data");
- armnn::TensorInfo inputTensorInfo = inputBindingInfo.second;
- BOOST_TEST((inputTensorInfo == runtime->GetInputTensorInfo(netId, inputBindingInfo.first)));
-
- BOOST_TEST(inputTensorInfo.GetShape()[0] == 1);
- BOOST_TEST(inputTensorInfo.GetShape()[1] == 2);
- BOOST_TEST(inputTensorInfo.GetShape()[2] == 3);
- BOOST_TEST(inputTensorInfo.GetShape()[3] == 4);
- }
-
- // Checks everything works with implicit input.
- {
- network = parser->CreateNetworkFromString(implicitInput.c_str(), {}, { "data" });
- BOOST_TEST(network.get());
- runtime->LoadNetwork(netId, Optimize(*network, backends, runtime->GetDeviceSpec()));
-
- armnnCaffeParser::BindingPointInfo inputBindingInfo = parser->GetNetworkInputBindingInfo("data");
- armnn::TensorInfo inputTensorInfo = inputBindingInfo.second;
- BOOST_TEST((inputTensorInfo == runtime->GetInputTensorInfo(netId, inputBindingInfo.first)));
-
- BOOST_TEST(inputTensorInfo.GetShape()[0] == 1);
- BOOST_TEST(inputTensorInfo.GetShape()[1] == 2);
- BOOST_TEST(inputTensorInfo.GetShape()[2] == 3);
- BOOST_TEST(inputTensorInfo.GetShape()[3] == 4);
- }
-
- // Checks everything works with implicit and passing shape.
- {
- network = parser->CreateNetworkFromString(implicitInput.c_str(), { {"data", { 2, 2, 3, 4 } } }, { "data" });
- BOOST_TEST(network.get());
- runtime->LoadNetwork(netId, Optimize(*network, backends, runtime->GetDeviceSpec()));
-
- armnnCaffeParser::BindingPointInfo inputBindingInfo = parser->GetNetworkInputBindingInfo("data");
- armnn::TensorInfo inputTensorInfo = inputBindingInfo.second;
- BOOST_TEST((inputTensorInfo == runtime->GetInputTensorInfo(netId, inputBindingInfo.first)));
-
- BOOST_TEST(inputTensorInfo.GetShape()[0] == 2);
- BOOST_TEST(inputTensorInfo.GetShape()[1] == 2);
- BOOST_TEST(inputTensorInfo.GetShape()[2] == 3);
- BOOST_TEST(inputTensorInfo.GetShape()[3] == 4);
- }
-
- // Checks everything works with implicit (no shape) and passing shape.
- {
- network = parser->CreateNetworkFromString(implicitInputNoShape.c_str(), {{"data", {2, 2, 3, 4} }}, { "data" });
- BOOST_TEST(network.get());
- runtime->LoadNetwork(netId, Optimize(*network, backends, runtime->GetDeviceSpec()));
-
- armnnCaffeParser::BindingPointInfo inputBindingInfo = parser->GetNetworkInputBindingInfo("data");
- armnn::TensorInfo inputTensorInfo = inputBindingInfo.second;
- BOOST_TEST((inputTensorInfo == runtime->GetInputTensorInfo(netId, inputBindingInfo.first)));
-
- BOOST_TEST(inputTensorInfo.GetShape()[0] == 2);
- BOOST_TEST(inputTensorInfo.GetShape()[1] == 2);
- BOOST_TEST(inputTensorInfo.GetShape()[2] == 3);
- BOOST_TEST(inputTensorInfo.GetShape()[3] == 4);
- }
-
- // Checks exception on incompatible shapes.
- {
- BOOST_CHECK_THROW(parser->CreateNetworkFromString(implicitInput.c_str(), {{"data",{ 2, 2, 3, 2 }}}, {"data"}),
- armnn::ParseException);
- }
-
- // Checks exception when no shape available.
- {
- BOOST_CHECK_THROW(parser->CreateNetworkFromString(implicitInputNoShape.c_str(), {}, { "data" }),
- armnn::ParseException);
- }
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/armnnCaffeParser/test/TestMul.cpp b/src/armnnCaffeParser/test/TestMul.cpp
deleted file mode 100644
index eeb937e6ea..0000000000
--- a/src/armnnCaffeParser/test/TestMul.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#include <boost/test/unit_test.hpp>
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#include "ParserPrototxtFixture.hpp"
-
-BOOST_AUTO_TEST_SUITE(CaffeParser)
-
-struct MulFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
-{
- MulFixture()
- {
- m_Prototext = "name: \"MinimalMul\"\n"
- "layer {\n"
- " name: \"data\"\n"
- " type: \"Input\"\n"
- " top: \"data\"\n"
- " input_param { shape: { dim: 1 dim: 1 dim: 4 dim: 4 } }\n"
- "}\n"
- "layer {\n"
- " bottom: \"data\"\n"
- " top: \"pool1\"\n"
- " name: \"pool1\"\n"
- " type: \"Pooling\"\n"
- " pooling_param {\n"
- " kernel_size: 2\n"
- " stride: 2\n"
- " pool: MAX\n"
- " }\n"
- "}\n"
- "layer {\n"
- " bottom: \"data\"\n"
- " top: \"pool2\"\n"
- " name: \"pool2\"\n"
- " type: \"Pooling\"\n"
- " pooling_param {\n"
- " kernel_size: 2\n"
- " stride: 2\n"
- " pool: MAX\n"
- " }\n"
- "}\n"
- "layer {\n"
- " bottom: \"pool1\"\n"
- " bottom: \"pool2\"\n"
- " top: \"mul\"\n"
- " name: \"mul\"\n"
- " type: \"Eltwise\"\n"
- " eltwise_param {\n"
- " operation: 0\n"
- " }\n"
- "}\n";
- SetupSingleInputSingleOutput("data", "mul");
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(ParseMul, MulFixture)
-{
- RunTest<4>(
- {
- 0, 1, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 1, 0,
- 1, 0, 1, 1
- },
- {
- 1, 0,
- 1, 1
- });
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/armnnCaffeParser/test/TestMultiInputsOutputs.cpp b/src/armnnCaffeParser/test/TestMultiInputsOutputs.cpp
deleted file mode 100644
index 23dcc78941..0000000000
--- a/src/armnnCaffeParser/test/TestMultiInputsOutputs.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#include <boost/test/unit_test.hpp>
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#include "ParserPrototxtFixture.hpp"
-
-BOOST_AUTO_TEST_SUITE(CaffeParser)
-
-struct MultiInputsOutputsFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
-{
- MultiInputsOutputsFixture()
- {
- m_Prototext = R"(
-name: "MultiInputsOutputs"
-layer {
- name: "input1"
- type: "Input"
- top: "input1"
- input_param { shape: { dim: 1 } }
-}
-layer {
- name: "input2"
- type: "Input"
- top: "input2"
- input_param { shape: { dim: 1 } }
-}
-layer {
- bottom: "input1"
- bottom: "input2"
- top: "add1"
- name: "add1"
- type: "Eltwise"
-}
-layer {
- bottom: "input2"
- bottom: "input1"
- top: "add2"
- name: "add2"
- type: "Eltwise"
-}
- )";
- Setup({ }, { "add1", "add2" });
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(MultiInputsOutputs, MultiInputsOutputsFixture)
-{
- RunTest<1>({ { "input1",{ 12.0f } },{ "input2",{ 13.0f } } },
- { { "add1",{ 25.0f } },{ "add2",{ 25.0f } } });
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/armnnCaffeParser/test/TestPooling2d.cpp b/src/armnnCaffeParser/test/TestPooling2d.cpp
deleted file mode 100644
index 47cd313f4b..0000000000
--- a/src/armnnCaffeParser/test/TestPooling2d.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#include <boost/test/unit_test.hpp>
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#include "ParserPrototxtFixture.hpp"
-
-BOOST_AUTO_TEST_SUITE(CaffeParser)
-
-struct GlobalPoolingFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
-{
- GlobalPoolingFixture()
- {
- m_Prototext = "name: \"GlobalPooling\"\n"
- "layer {\n"
- " name: \"data\"\n"
- " type: \"Input\"\n"
- " top: \"data\"\n"
- " input_param { shape: { dim: 1 dim: 3 dim: 2 dim: 2 } }\n"
- "}\n"
- "layer {\n"
- " bottom: \"data\"\n"
- " top: \"pool1\"\n"
- " name: \"pool1\"\n"
- " type: \"Pooling\"\n"
- " pooling_param {\n"
- " pool: AVE\n"
- " global_pooling: true\n"
- " }\n"
- "}\n";
- SetupSingleInputSingleOutput("data", "pool1");
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(GlobalPooling, GlobalPoolingFixture)
-{
- RunTest<4>(
- {
- 1,3,
- 5,7,
-
- 2,2,
- 2,2,
-
- 4,4,
- 6,6
- },
- {
- 4, 2, 5
- });
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/armnnCaffeParser/test/TestSplit.cpp b/src/armnnCaffeParser/test/TestSplit.cpp
deleted file mode 100644
index 249de5a676..0000000000
--- a/src/armnnCaffeParser/test/TestSplit.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-// Copyright © 2017 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#include <boost/test/unit_test.hpp>
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#include "ParserPrototxtFixture.hpp"
-
-BOOST_AUTO_TEST_SUITE(CaffeParser)
-
-struct SplitFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
-{
- SplitFixture()
- {
- m_Prototext = R"(
-name: "Split"
-layer {
- name: "data"
- type: "Input"
- top: "data"
- input_param { shape: { dim: 1 dim: 1 dim: 1 dim: 1 } }
-}
-layer {
- name: "split"
- type: "Split"
- bottom: "data"
- top: "split_top0"
- top: "split_top1"
-}
-layer {
- bottom: "split_top0"
- bottom: "split_top1"
- top: "add"
- name: "add"
- type: "Eltwise"
-}
- )";
- SetupSingleInputSingleOutput("data", "add");
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(Split, SplitFixture)
-{
- RunTest<1>({ 1.0f }, { 2.0f });
-}
-
-BOOST_AUTO_TEST_SUITE_END()