blob: 30da04e7c1e85b623d57400f7941ba9b9aefda6e [file] [log] [blame]
//
// 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
{
using BindingPointInfo = std::pair<armnn::LayerBindingId, armnn::TensorInfo>;
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();
};
}