15#include <fmt/format.h>
17#include <google/protobuf/text_format.h>
18#include <google/protobuf/io/zero_copy_stream_impl.h>
29IOnnxParser::IOnnxParser() : pOnnxParserImpl(new
OnnxParserImpl()) {}
31IOnnxParser::~IOnnxParser() =
default;
35 return new IOnnxParser();
52 return pOnnxParserImpl->CreateNetworkFromBinaryFile(graphFile);
57 return pOnnxParserImpl->CreateNetworkFromBinary(binaryContent);
61 const std::map<std::string, armnn::TensorShape>& inputShapes)
63 return pOnnxParserImpl->CreateNetworkFromBinary(binaryContent, inputShapes);
68 return pOnnxParserImpl->CreateNetworkFromTextFile(graphFile);
73 return pOnnxParserImpl->CreateNetworkFromString(protoText);
77 const char* graphFile,
78 const std::map<std::string, armnn::TensorShape>& inputShapes)
80 return pOnnxParserImpl->CreateNetworkFromBinaryFile(graphFile, inputShapes);
84 const std::map<std::string, armnn::TensorShape>& inputShapes)
86 return pOnnxParserImpl->CreateNetworkFromTextFile(graphFile, inputShapes);
90 const std::map<std::string, armnn::TensorShape>& inputShapes)
92 return pOnnxParserImpl->CreateNetworkFromString(protoText, inputShapes);
97 return pOnnxParserImpl->GetNetworkInputBindingInfo(name);
102 return pOnnxParserImpl->GetNetworkOutputBindingInfo(name);
107void CheckValidDataType(std::initializer_list<onnx::TensorProto::DataType> validInputTypes,
108 const onnx::TensorProto::DataType actualValue,
109 const char* validExpr,
110 std::string nodeName,
111 std::string tensorName,
114 bool isValid = std::any_of(validInputTypes.begin(),
115 validInputTypes.end(),
116 [&actualValue](onnx::TensorProto::DataType x) { return x == actualValue; } );
120 fmt::format(
"Datatype {} is not valid for tensor '{}' of node '{}', not in {{{}}}. {}",
121 onnx::TensorProto::DataType_Name(actualValue),
129#define CHECK_VALID_DATATYPE(NODE, TENSOR, ACTUAL, ...) \
130CheckValidDataType({__VA_ARGS__}, ACTUAL, #__VA_ARGS__, NODE, TENSOR, CHECK_LOCATION())
132using StrTypeListPair = std::pair<const char*, std::initializer_list<onnx::TensorProto::DataType>>;
133#define STR_LIST(...) StrTypeListPair(#__VA_ARGS__, {__VA_ARGS__})
135template <
typename Callable>
136void ReadMandatoryNodeAttributeImpl(
const onnx::NodeProto& node,
137 const std::string& attribName,
138 onnx::AttributeProto::AttributeType expectedType,
141 auto attribs = node.attribute();
143 while (attriNum < node.attribute_size())
145 if (attribs.Get(attriNum).name() == attribName)
147 if (attribs.Get(attriNum).type() == expectedType)
149 callable(attribs.Get(attriNum));
153 throw ParseException(fmt::format(
"Attribute {} of node {} expected to have {} as "
154 "onnx::AttributeProto::AttributeType, but found {} instead {}",
157 onnx::AttributeProto::AttributeType_Name(expectedType),
158 onnx::AttributeProto::AttributeType_Name(attribs.Get(attriNum).type()),
165 if (attriNum == node.attribute_size())
167 throw ParseException(fmt::format(
"Could not find required attribute {} in node {} {}",
172template <
typename Callable>
173void ReadOptionalNodeAttributeImpl(
const onnx::NodeProto& node,
174 const std::string& attribName,
175 onnx::AttributeProto::AttributeType expectedType,
178 auto attribs = node.attribute();
179 for (
int attriNum = 0; attriNum < node.attribute_size(); ++attriNum)
181 if (attribs.Get(attriNum).name() == attribName)
183 if (attribs.Get(attriNum).type() == expectedType)
185 callable(attribs.Get(attriNum));
190 fmt::format(
"Attribute {} of node {} expected to have {} as onnx::AttributeProto::AttributeType, "
191 "but found {} instead {}",
194 onnx::AttributeProto::AttributeType_Name(expectedType),
195 onnx::AttributeProto::AttributeType_Name(attribs.Get(attriNum).type()),
202int ReadMandatoryNodeIntAttribute(
const onnx::NodeProto& node,
203 const std::string& name)
206 ReadMandatoryNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
207 [&attribValue](
const onnx::AttributeProto& attrValue)
214int64_t ReadOptionalNodeInt64Attribute(
const onnx::NodeProto& node,
215 const std::string& name,
216 const int64_t defaultValue = 0)
218 int64_t attribValue = defaultValue;
219 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
220 [&attribValue](
const onnx::AttributeProto& attrValue)
222 attribValue = attrValue.i();
227std::vector<uint32_t> ReadMandatoryNodeUint32ListAttribute(
const onnx::NodeProto& node,
228 const std::string& name)
230 std::vector<uint32_t> attriList;
231 ReadMandatoryNodeAttributeImpl(node, name, onnx::AttributeProto::INTS,
232 [&attriList](
const onnx::AttributeProto& attrValue)
234 for (
int attriNum = 0; attriNum < attrValue.ints_size(); ++attriNum)
242uint32_t ReadOptionalNodeUint32Attribute(
const onnx::NodeProto& node,
243 const std::string& name,
244 const uint32_t defaultVal = 0u)
246 uint32_t attribValue = defaultVal;
247 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
248 [&attribValue](
const onnx::AttributeProto& attrValue)
255std::vector<uint32_t> ReadOptionalNodeUint32ListAttribute(
const onnx::NodeProto& node,
256 const std::string& name)
258 std::vector<uint32_t> attriList;
259 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INTS,
260 [&attriList](
const onnx::AttributeProto& attrValue)
262 for (
int attriNum = 0; attriNum < attrValue.ints_size(); ++attriNum)
271float ReadOptionalNodeFloatAttribute(
const onnx::NodeProto& node,
272 const std::string& name,
273 const float defaultValue = 0.0f)
275 float attribValue = defaultValue;
276 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::FLOAT,
277 [&attribValue](
const onnx::AttributeProto& attrValue)
279 attribValue = attrValue.f();
284std::string ReadOptionalNodeStringAttribute(
const onnx::NodeProto& node,
const std::string& name)
286 std::string attribValue =
"";
287 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::STRING,
288 [&attribValue](
const onnx::AttributeProto& attrValue)
290 attribValue = attrValue.s();
300 case onnx::TensorProto::FLOAT:
305 case onnx::TensorProto::INT32:
306 case onnx::TensorProto::INT64:
314 fmt::format(
"'{}' is not a currently supported datatype for tensor {}."
315 " Supported dataTypes are FLOAT, INT32 and INT64. {}",
316 onnx::TensorProto::DataType_Name(
static_cast<onnx::TensorProto::DataType
>(data_type)),
329 if(std::find(shape.begin(), shape.end(), 0) != shape.end())
339 const onnx::TensorShapeProto onnxShape =
info.type().tensor_type().shape();
340 std::vector<unsigned int> shapeDims;
341 for (
int i = 0; i < onnxShape.dim_size(); ++i)
351 std::vector<unsigned int> shapeDims;
353 for (
auto dim: tensor.dims())
358 return ToTensorInfo(tensor.name(), shapeDims, tensor.data_type());
362 const std::string& name,
363 const onnx::TensorProto::DataType& type)
366 std::stringstream ss;
367 ss <<
"tensor '" << name <<
"' contains "
368 << onnx::TensorProto::DataType_Name(type)
369 <<
" and has shape [";
373 ss << shape[i] <<
", ";
379void CalcPadding(uint32_t inputSize,
383 uint32_t* paddingFront,
384 uint32_t* paddingBack,
387 uint32_t outputSize = (inputSize + stride - 1) / stride;
388 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
389 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
390 *paddingFront = (temp - inputSize) / 2;
391 *paddingBack = *paddingFront;
392 if((temp - inputSize) % 2 == 1)
407 const std::string& outName,
410 std::vector<int> targetDims;
416 targetDims.push_back(
static_cast<int>(inShape[
static_cast<uint
>(i)]));
420 targetDims.push_back(val);
424 std::vector<unsigned int> outDims(targetDims.begin(), targetDims.end());
425 const auto stretchDim = std::find(targetDims.begin(), targetDims.end(), -1);
426 if (stretchDim != targetDims.end())
428 if (std::find(std::next(stretchDim), targetDims.end(), -1) != targetDims.end())
430 std::stringstream ss;
432 for(uint i = 0; i < targetDims.size() - 1; ++i)
434 ss << targetDims[i] <<
", ";
436 ss << targetDims[targetDims.size() - 1] <<
" ]";
439 fmt::format(
"Error during creation of reshaped tensor '{}'. At most one component of shape can be "
440 " -1 and here, shape is {} {}",
447 std::accumulate(targetDims.begin(), targetDims.end(), -1, std::multiplies<int32_t>()));
448 auto stretchIndex =
static_cast<size_t>(std::distance(targetDims.begin(), stretchDim));
449 if (targetNumElements == 0)
453 outDims[stretchIndex] = 0;
458 fmt::format(
"Input to reshape is a tensor with elements, but the requested shape has 0. {}",
464 outDims[stretchIndex] = inShape.
GetNumElements() / targetNumElements;
473const std::map<std::string, OnnxParserImpl::OperationParsingFunction> OnnxParserImpl::m_ParserFunctions = {
474 {
"BatchNormalization", &OnnxParserImpl::ParseBatchNormalization},
475 {
"GlobalAveragePool", &OnnxParserImpl::ParseGlobalAveragePool},
476 {
"AveragePool", &OnnxParserImpl::ParseAveragePool },
477 {
"Clip", &OnnxParserImpl::ParseClip },
478 {
"Constant", &OnnxParserImpl::ParseConstant },
479 {
"MaxPool", &OnnxParserImpl::ParseMaxPool },
480 {
"Reshape", &OnnxParserImpl::ParseReshape },
481 {
"Sigmoid", &OnnxParserImpl::ParseSigmoid },
482 {
"Tanh", &OnnxParserImpl::ParseTanh },
483 {
"Relu", &OnnxParserImpl::ParseRelu },
484 {
"LeakyRelu", &OnnxParserImpl::ParseLeakyRelu },
485 {
"Conv", &OnnxParserImpl::ParseConv },
486 {
"Add", &OnnxParserImpl::ParseAdd },
487 {
"Flatten", &OnnxParserImpl::ParseFlatten },
488 {
"Shape", &OnnxParserImpl::ParseShape },
489 {
"Gather", &OnnxParserImpl::ParseGather },
490 {
"Unsqueeze", &OnnxParserImpl::ParseUnsqueeze },
491 {
"Concat", &OnnxParserImpl::ParseConcat },
492 {
"Gemm", &OnnxParserImpl::ParseGemm }
495template<
typename TypePair,
typename Location>
496void OnnxParserImpl::ValidateInputs(
const onnx::NodeProto& node,
497 TypePair validInputs,
498 const Location& location)
500 for(
auto input : node.input())
502 CheckValidDataType(validInputs.second,
503 m_TensorsInfo[input].m_dtype,
511#define VALID_INPUTS(NODE, VALID_INPUTS) \
512 OnnxParserImpl::ValidateInputs(NODE, \
516std::vector<TensorInfo> OnnxParserImpl::ComputeOutputInfo(std::vector<std::string> outNames,
518 std::vector<TensorShape> inputShapes,
519 const onnx::TensorProto::DataType& dataType)
521 if (outNames.empty())
526 bool needCompute = std::any_of(outNames.begin(),
528 [
this](std::string name)
530 return (m_TensorsInfo.count(name) == 0 ||
531 m_TensorsInfo[name].m_info == nullptr ||
532 m_TensorsInfo[name].m_info->GetShape().GetDimensionality() ==
533 Dimensionality::NotSpecified);
535 std::vector<TensorInfo> outInfo;
537 std::vector<TensorShape> inferredShapes;
541 if (inferredShapes.size() != outNames.size())
547 case onnx::TensorProto::FLOAT: {
551 case onnx::TensorProto::INT32:
552 case onnx::TensorProto::INT64: {
558 fmt::format(
"'{}' is not a currently supported datatype for {}."
559 " Supported dataTypes are FLOAT, INT32 and INT64. {}",
560 onnx::TensorProto::DataType_Name(
static_cast<onnx::TensorProto::DataType
>(dataType)),
566 for (uint i = 0; i < outNames.size(); ++i)
570 m_TensorsInfo[outNames[i]] = OnnxTensor();
571 m_TensorsInfo[outNames[i]].m_info = std::make_unique<TensorInfo>(
573 m_TensorsInfo[outNames[i]].m_dtype = dataType;
575 outInfo.push_back(*m_TensorsInfo[outNames[i]].m_info);
581 : m_Network(nullptr, nullptr)
585void OnnxParserImpl::ResetParser()
589 m_InputInfos.clear();
590 m_OutputInfos.clear();
593void OnnxParserImpl::Cleanup()
595 m_TensorConnections.clear();
596 m_TensorsInfo.clear();
597 m_OutputsMap.clear();
598 m_OutputsFusedAndUsed.clear();
599 m_InputShapes.clear();
603std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
608 if (bufferPtr ==
nullptr)
615 if (permutationVector.
has_value() && permutationVector.
value().GetSize() > 0)
619 reinterpret_cast<const T*
>(bufferPtr), data.get(),
sizeof(T));
623 ::memcpy(data.get(), bufferPtr, tensorInfo.
GetNumBytes());
626 return std::make_pair(
ConstTensor(tensorInfo, data.get()), std::move(data));
629std::pair<ConstTensor, std::unique_ptr<float[]>>
630OnnxParserImpl::CreateConstTensor(
const std::string name,
633 TensorInfo tensorInfo = *m_TensorsInfo[name].m_info;
634 onnx::TensorProto onnxTensor = *m_TensorsInfo[name].m_tensor;
638 static_cast<onnx::TensorProto::DataType
>(onnxTensor.data_type()), onnx::TensorProto::FLOAT);
646 throw ParseException(fmt::format(
"No tensor data found for Const tensor '{}' {}",
651 auto srcData = onnxTensor.float_data().data();
653 if (!onnxTensor.has_raw_data())
655 if(tensorInfo.
GetNumElements() !=
static_cast<uint
>(onnxTensor.float_data_size()))
657 throw ParseException(
658 fmt::format(
"The number of data provided ({}) does not match the tensor '{}' number of "
660 onnxTensor.float_data_size(),
675std::pair<ConstTensor, std::unique_ptr<int32_t[]>>
676OnnxParserImpl::CreateInt64ConstTensor(
const std::string name,
677 armnn::Optional<armnn::PermutationVector&> permutationVector)
679 TensorInfo tensorInfo = *m_TensorsInfo[name].m_info;
680 onnx::TensorProto onnxTensor = *m_TensorsInfo[name].m_tensor;
683 static_cast<onnx::TensorProto::DataType
>(onnxTensor.data_type()), onnx::TensorProto::INT64);
690 if (numElements == 0)
692 throw ParseException(fmt::format(
"No tensor data found for Const tensor '{}' {}",
698 if (!onnxTensor.has_raw_data())
700 auto srcData = onnxTensor.int64_data().data();
701 if(numElements !=
static_cast<uint
>(onnxTensor.int64_data_size()))
703 throw ParseException(
704 fmt::format(
"The number of data provided ({}) does not match the tensor '{}' number of "
706 onnxTensor.int64_data_size(),
712 std::vector<int32_t> int32Data;
713 for(uint i = 0; i < numElements; i++)
716 int32Data.push_back(int32Value);
723 auto srcData =
reinterpret_cast<const int64_t*
>(onnxTensor.raw_data().c_str());
724 std::vector<int32_t> int32Data;
725 for(uint i = 0; i < numElements; i++)
728 int32Data.push_back(int32Value);
736 FILE* fd = fopen(graphFile,
"r");
744 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
745 using google::protobuf::io::FileInputStream;
746 std::unique_ptr<FileInputStream> input = std::make_unique<FileInputStream>(fileno(fd));
747 bool success = google::protobuf::TextFormat::Parse(input.get(), modelProto.get());
752 std::stringstream
error;
753 error <<
"Failed to parse graph file";
763 return CreateNetworkFromModel(*modelProto);
767 const std::map<std::string, armnn::TensorShape>& inputShapes)
770 m_InputShapes = inputShapes;
772 return CreateNetworkFromModel(*modelProto);
779 return CreateNetworkFromModel(*modelProto);
783 const std::map<std::string, armnn::TensorShape>& inputShapes)
786 m_InputShapes = inputShapes;
788 return CreateNetworkFromModel(*modelProto);
793 if (binaryContent.size() == 0)
798 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
800 google::protobuf::io::CodedInputStream codedStream(binaryContent.data(),
static_cast<int>(binaryContent.size()));
801 codedStream.SetTotalBytesLimit(INT_MAX);
802 bool success = modelProto.get()->ParseFromCodedStream(&codedStream);
806 std::stringstream
error;
807 error <<
"Failed to parse graph";
815 FILE* fd = fopen(graphFile,
"rb");
823 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
825 google::protobuf::io::FileInputStream inStream(fileno(fd));
826 google::protobuf::io::CodedInputStream codedStream(&inStream);
827 codedStream.SetTotalBytesLimit(INT_MAX);
828 bool success = modelProto.get()->ParseFromCodedStream(&codedStream);
833 std::stringstream
error;
834 error <<
"Failed to parse graph file";
845 return CreateNetworkFromModel(*modelProto);
849 const std::map<std::string, armnn::TensorShape>& inputShapes)
852 m_InputShapes = inputShapes;
854 return CreateNetworkFromModel(*modelProto);
865 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
866 bool success = google::protobuf::TextFormat::ParseFromString(protoText, modelProto.get());
869 std::stringstream
error;
870 error <<
"Failed to parse graph file";
880 return CreateNetworkFromModel(*modelProto);
884 const std::map<std::string, armnn::TensorShape>& inputShapes)
887 m_InputShapes = inputShapes;
889 return CreateNetworkFromModel(*modelProto);
892INetworkPtr OnnxParserImpl::CreateNetworkFromModel(onnx::ModelProto& model)
897 m_Graph = std::make_unique<onnx::GraphProto>(*model.mutable_graph());
906 return std::move(m_Network);
909void OnnxParserImpl::LoadGraph()
911 if (m_Graph.get() ==
nullptr)
913 throw armnn::ParseException(fmt::format(
"Graph pointer is null {}",
CHECK_LOCATION().AsString()));
917 SetupInfo(m_Graph->mutable_output());
918 SetupInfo(m_Graph->mutable_input());
919 SetupInfo(m_Graph->mutable_value_info());
921 for (
auto tensor : m_Graph->initializer())
923 m_TensorsInfo[tensor.name()].m_tensor = std::make_unique<const onnx::TensorProto>(tensor);
924 m_TensorsInfo[tensor.name()].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(tensor));
925 m_TensorsInfo[tensor.name()].m_dtype =
926 static_cast<onnx::TensorProto::DataType
>(tensor.data_type());
933 DetectFullyConnected();
936 for(
size_t nodeIndex = 0; nodeIndex < static_cast<size_t>(m_Graph->node_size()); nodeIndex++)
938 auto node = m_Graph->node(
static_cast<int>(nodeIndex));
939 const std::string& operation = node.op_type();
942 if (operation ==
"MatMul" )
944 if(m_OutputsFusedAndUsed[nodeIndex].inputForNodes != m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.size())
947 AddFullyConnected(node);
950 else if (!(m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.empty()) && operation ==
"Add")
952 int matmulIndex =
static_cast<int> (m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes[0]);
953 AddFullyConnected(m_Graph->node(matmulIndex), &node);
955 else if (m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.empty())
957 auto it = m_ParserFunctions.find(operation);
958 if (it != m_ParserFunctions.end())
960 auto func = it->second;
965 throw ParseException(fmt::format(
"Unsupported operation {} for node '{}' {}",
974 for (
const auto& tensorCon : m_TensorConnections)
976 if (tensorCon.second.outputSlot !=
nullptr)
978 for (
size_t inputSlotIdx = 0; inputSlotIdx < tensorCon.second.inputSlots.size(); ++inputSlotIdx)
980 tensorCon.second.outputSlot->Connect(*(tensorCon.second.inputSlots[inputSlotIdx]));
986 for(
int outputIndex = 0; outputIndex < m_Graph->output_size(); ++outputIndex)
988 auto output = m_Graph->output(outputIndex);
989 m_OutputInfos[output.name()] = *m_TensorsInfo[output.name()].m_info;
993void OnnxParserImpl::SetupInfo(
const google::protobuf::RepeatedPtrField<onnx::ValueInfoProto >* list)
995 for (
auto tensor : *list)
997 m_TensorsInfo[tensor.name()] = OnnxTensor();
998 m_TensorsInfo[tensor.name()].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(tensor));
999 m_TensorsInfo[tensor.name()].m_dtype =
1000 static_cast<onnx::TensorProto::DataType
>(tensor.type().tensor_type().elem_type());
1004void OnnxParserImpl::DetectFullyConnected()
1006 m_OutputsFusedAndUsed = std::vector<UsageSummary> (
static_cast<size_t>(m_Graph->node_size()), UsageSummary());
1007 auto matmulAndConstant = [&](
const std::string& constInput,
1008 const std::string& matmulInput,
1011 auto matmulIt = m_OutputsMap.find(matmulInput);
1012 if(matmulIt != m_OutputsMap.end() && matmulIt->second.first->op_type() ==
"MatMul"
1013 && m_TensorsInfo[constInput].isConstant())
1015 nodeIndex = matmulIt->second.second;
1021 for(
int nodeIndex = 0; nodeIndex < m_Graph->node_size(); nodeIndex++)
1023 const onnx::NodeProto* node = &m_Graph->node(nodeIndex);
1024 for (
const std::string& output : node->output())
1026 m_OutputsMap[output] = std::make_pair(node, nodeIndex);
1029 for (
const std::string& input : node->input())
1031 auto matmulIt = m_OutputsMap.find(input);
1032 if(matmulIt != m_OutputsMap.end()){
1033 ++m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIt->second.second)].inputForNodes;
1037 if (node->op_type() ==
"Add")
1039 int matmulIndex = 0;
1040 if (matmulAndConstant(node->input(0), node->input(1), matmulIndex) ||
1041 matmulAndConstant(node->input(1), node->input(0), matmulIndex))
1044 m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIndex)].fusedWithNodes
1045 .push_back(
static_cast<size_t>(nodeIndex));
1047 m_OutputsFusedAndUsed[
static_cast<size_t>(nodeIndex)].fusedWithNodes
1048 .push_back(
static_cast<size_t>(matmulIndex));
1053 for (
auto output: m_Graph->output()) {
1054 auto matmulIt = m_OutputsMap.find(output.name());
1055 if(matmulIt != m_OutputsMap.end()){
1056 ++m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIt->second.second)].inputForNodes;
1061template<
typename Location>
1062void OnnxParserImpl::GetInputAndParam(
const onnx::NodeProto& node,
1063 std::string* inputName,
1064 std::string* constName,
1065 const Location& location)
1068 if (m_TensorsInfo[node.input(0)].isConstant())
1072 else if (m_TensorsInfo[node.input(1)].isConstant())
1078 throw ParseException(fmt::format(
"One of the input tensors ('{}' or '{}') should be constant in node '{}' {}",
1082 location.AsString()));
1086 *constName = node.input(cstIndex);
1090 *inputName = node.input(!cstIndex);
1094template<
typename Location>
1095void OnnxParserImpl::To1DTensor(
const std::string& name,
const Location& location)
1097 TensorShape shape = m_TensorsInfo[name].m_info->GetShape();
1098 std::vector<uint32_t> newShape;
1103 throw ParseException(
1104 fmt::format(
"Only tensors with shape [1, ..., 1, X] can be converted to 1D and {} {}",
1105 TensorInfoAsString(*m_TensorsInfo[name].m_info, name, m_TensorsInfo[name].m_dtype),
1106 location.AsString()));
1111 m_TensorsInfo[name].m_info->SetShape(TensorShape(
static_cast<unsigned int>(newShape.size()), newShape.data()));
1114void OnnxParserImpl::AddConvLayerWithDepthwiseConv(
const onnx::NodeProto& node,
const Convolution2dDescriptor& convDesc)
1118 DepthwiseConvolution2dDescriptor desc;
1127 armnn::IConnectableLayer* layer = m_Network->AddDepthwiseConvolution2dLayer(desc, node.name().c_str());
1128 std::string permuteStr =
"permute_" + node.input(1);
1129 std::vector<std::string> tensorIndexes= {node.input(0), permuteStr};
1131 auto weightTensor = CreateConstTensor(node.input(1));
1132 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(weightTensor.first);
1135 armnn::PermutationVector perVec {3, 0, 1, 2};
1139 IConnectableLayer* permuteLayer = m_Network->AddPermuteLayer(PermuteDescriptor(perVec),
1147 if (node.input_size() == 3)
1149 if(!m_TensorsInfo[node.input(2)].isConstant())
1151 throw ParseException(fmt::format(
"Bias '{}' should be constant in Conv layer '{}' {}",
1158 auto biasTensor = CreateConstTensor(node.input(2));
1159 tensorIndexes.emplace_back(node.input(2));
1161 IConnectableLayer* biasLayer = m_Network->AddConstantLayer(biasTensor.first);
1168 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
1171 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1172 { m_TensorsInfo[node.input(0)].m_info->GetShape(),
1179 RegisterInputSlots(layer, tensorIndexes);
1182 RegisterOutputSlots(layer, {node.output(0)});
1185void OnnxParserImpl::AddFullyConnected(
const onnx::NodeProto& matmulNode,
const onnx::NodeProto* addNode)
1188 std::string inputName;
1189 std::string weightName;
1190 std::string biasName;
1191 std::string outputName;
1196 GetInputAndParam(matmulNode, &inputName, &weightName,
CHECK_LOCATION());
1198 TensorInfo inputInfo = *m_TensorsInfo[inputName].m_info;
1199 TensorInfo weightInfo = *m_TensorsInfo[weightName].m_info;
1200 TensorInfo biasInfo;
1202 std::vector<std::string> inputNames;
1204 FullyConnectedDescriptor desc;
1207 IConnectableLayer* layer =
nullptr;
1215 GetInputAndParam(*addNode,
nullptr, &biasName,
CHECK_LOCATION());
1219 biasInfo = *m_TensorsInfo[biasName].m_info;
1223 throw ParseException(
1224 fmt::format(
"Shape of weights '{}' and bias of following Add node '{}' do not match : {}"
1225 " and {} ( /!\\ bias should be a 1D tensor) {}",
1228 TensorInfoAsString(*m_TensorsInfo[weightName].m_info, weightName,
1229 m_TensorsInfo[weightName].m_dtype),
1230 TensorInfoAsString(*m_TensorsInfo[biasName].m_info, biasName,
1231 m_TensorsInfo[biasName].m_dtype ),
1235 inputNames = { inputName, weightName, biasName };
1236 outputName = addNode->output(0);
1240 inputNames = { inputName, weightName };
1241 outputName = matmulNode.output(0);
1245 layer = m_Network->AddFullyConnectedLayer(desc, matmulNode.name().c_str());
1249 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
1258 std::vector<unsigned int> reshapedDimensions(2);
1259 reshapedDimensions[1] = weightInfo.
GetShape()[0];
1260 reshapedDimensions[0] = inputInfo.
GetNumElements() / reshapedDimensions[1];
1264 throw ParseException(
1265 fmt::format(
"Failed to deduce input tensor shape from filter size {} {}",
1266 reshapedDimensions[1],
1270 TensorInfo reshapedTensorInfo = inputInfo;
1271 reshapedTensorInfo.
SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
1272 inputInfo = reshapedTensorInfo;
1274 ReshapeDescriptor reshapeDescriptor;
1277 std::string reshapeLayerName = fmt::format(
"Reshape_for:{}", layer->
GetName());
1278 IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(reshapeDescriptor, reshapeLayerName.c_str());
1283 RegisterInputSlots(reshapeLayer, {inputName});
1284 inputNames[0] = reshapeLayerName;
1287 auto outputInfo = ComputeOutputInfo({ outputName },
1293 RegisterInputSlots(layer, inputNames);
1296 if(m_TensorsInfo[weightName].isConstant())
1298 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(CreateConstTensor(weightName).first);
1305 if(desc.
m_BiasEnabled && m_TensorsInfo[biasName].isConstant())
1307 IConnectableLayer* biasLayer = m_Network->AddConstantLayer(CreateConstTensor(biasName).first);
1314 if (outputInfo[0].GetNumDimensions() > 2)
1317 std::vector<unsigned int> reshapedDimensions(2);
1318 reshapedDimensions[1] = weightInfo.
GetShape()[1];
1319 reshapedDimensions[0] = outputInfo[0].
GetNumElements() / reshapedDimensions[1];
1321 if (outputInfo[0].GetNumElements() % reshapedDimensions[1] != 0)
1323 throw ParseException(
1324 fmt::format(
"Failed to deduce output tensor shape from filter size {} {}",
1325 reshapedDimensions[1],
1329 armnn::TensorInfo reshapedOutputTensorInfo = outputInfo[0];
1330 reshapedOutputTensorInfo.
SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
1333 ReshapeDescriptor desc;
1336 std::string reshapeLayerName = fmt::format(
"ExpandDims_for:{}", layer->
GetName());
1337 IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, reshapeLayerName.c_str());
1342 RegisterInputSlots(reshapeLayer, {layer->
GetName()});
1343 layer = reshapeLayer;
1346 RegisterOutputSlots(layer, { outputName });
1349void OnnxParserImpl::AddPoolingLayer(
const onnx::NodeProto& node, Pooling2dDescriptor& desc)
1357 std::vector<uint32_t> kernel_shape = ReadMandatoryNodeUint32ListAttribute(node,
"kernel_shape");
1358 std::vector<uint32_t> strides = ReadOptionalNodeUint32ListAttribute(node,
"strides");
1359 std::vector<uint32_t> pads = ReadOptionalNodeUint32ListAttribute(node,
"pads");
1380 std::string paddingString = ReadOptionalNodeStringAttribute(node,
"auto_pad");
1381 if(paddingString !=
"VALID" && paddingString !=
"" && paddingString !=
"NOTSET")
1384 if( paddingString ==
"SAME_LOWER")
1388 else if (paddingString ==
"SAME_UPPER")
1394 throw ParseException(fmt::format(
"Invalid auto_pad attribute for node {}. "
1395 "Only SAME_UPPER, SAME_LOWER or VALID supported and found {} {}",
1400 auto inputInfo = *m_TensorsInfo[node.input(0)].m_info;
1401 uint32_t inputHeight = inputInfo.
GetShape()[2];
1402 uint32_t inputWidth = inputInfo.
GetShape()[3];
1403 CalcPadding(inputHeight,
1410 CalcPadding(inputWidth,
1427 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, node.name().c_str());
1431 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
1434 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1439 RegisterInputSlots(layer, {node.input(0)});
1442 RegisterOutputSlots(layer, {node.output(0)});
1445std::pair<std::string, std::string> OnnxParserImpl::AddPrepareBroadcast(
const std::string& input0,
1446 const std::string& input1)
1448 std::pair<std::string, std::string> inputs = std::make_pair(input0, input1);
1450 TensorShape input0Shape = m_TensorsInfo[input0].m_info->GetShape();
1451 TensorShape input1Shape = m_TensorsInfo[input1].m_info->GetShape();
1455 auto outputName = fmt::format(
"reshape_output_{}", input1);
1456 PrependForBroadcast(outputName, input1, input0);
1457 inputs.second = outputName;
1461 auto outputName = fmt::format(
"reshape_output_{}", input0);
1462 PrependForBroadcast(outputName, input0, input1);
1463 inputs.first = outputName;
1468void OnnxParserImpl::CreateConstantLayer(
const std::string& tensorName,
const std::string& layerName)
1470 auto armnnTensor = CreateConstTensor(tensorName);
1471 IConnectableLayer* layer = m_Network->AddConstantLayer(armnnTensor.first, layerName.c_str());
1473 RegisterOutputSlots(layer, {tensorName});
1476void OnnxParserImpl::CreateInt64ConstantLayer(
const std::string& tensorName,
const std::string& layerName)
1478 auto armnnTensor = CreateInt64ConstTensor(tensorName);
1479 IConnectableLayer* layer = m_Network->AddConstantLayer(armnnTensor.first, layerName.c_str());
1481 RegisterOutputSlots(layer, {tensorName});
1484void OnnxParserImpl::CreateReshapeLayer(
const std::string& inputName,
1485 const std::string& outputName,
1486 const std::string& layerName)
1488 const TensorInfo outputTensorInfo = *m_TensorsInfo[outputName].m_info;
1489 ReshapeDescriptor reshapeDesc;
1492 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1496 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
1503 RegisterInputSlots(layer, {inputName});
1506 RegisterOutputSlots(layer, {outputName});
1516 ActivationDescriptor desc;
1519 if (func == ActivationFunction::BoundedReLu)
1521 if (node.input_size() == 1 && node.attribute_size() > 0)
1523 desc.
m_A = ReadOptionalNodeFloatAttribute(node,
"max", std::numeric_limits<float>::max());
1524 desc.
m_B = ReadOptionalNodeFloatAttribute(node,
"min", std::numeric_limits<float>::lowest());
1528 desc.
m_A = node.input(2).empty() ? std::numeric_limits<float>::max() : std::stof(node.input(2));
1529 desc.
m_B = node.input(1).empty() ? std::numeric_limits<float>::lowest() : std::stof(node.input(1));
1533 IConnectableLayer*
const layer = m_Network->AddActivationLayer(desc, node.name().c_str());
1537 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
1540 auto outputInfo = ComputeOutputInfo({ node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1545 RegisterInputSlots(layer, {node.input(0)});
1548 RegisterOutputSlots(layer, {node.output(0)});
1551void OnnxParserImpl::ParseClip(
const onnx::NodeProto& node)
1553 ParseActivation(node, ActivationFunction::BoundedReLu);
1556void OnnxParserImpl::ParseSigmoid(
const onnx::NodeProto& node)
1558 ParseActivation(node, ActivationFunction::Sigmoid);
1561void OnnxParserImpl::ParseTanh(
const onnx::NodeProto& node)
1563 ParseActivation(node, ActivationFunction::TanH);
1566void OnnxParserImpl::ParseRelu(
const onnx::NodeProto& node)
1568 ParseActivation(node, ActivationFunction::ReLu);
1571void OnnxParserImpl::ParseLeakyRelu(
const onnx::NodeProto& node)
1573 ParseActivation(node, ActivationFunction::LeakyReLu);
1576void OnnxParserImpl::ParseAdd(
const onnx::NodeProto& node)
1586 auto inputs = AddPrepareBroadcast(node.input(0), node.input(1));
1587 auto input0 = *m_TensorsInfo[inputs.first].m_info;
1588 auto input1 = *m_TensorsInfo[inputs.second].m_info;
1589 if (input0.GetNumDimensions() != input1.GetNumDimensions())
1591 throw armnn::ParseException(fmt::format(
"Dimension mismatch in node {} {}",
1596 unsigned int numDims = input0.GetNumDimensions();
1597 for (
unsigned int i = 0; i < numDims; i++)
1599 unsigned int dim0 = input0.GetShape()[i];
1600 unsigned int dim1 = input1.GetShape()[i];
1601 if (dim0 != dim1 && dim0 != 1 && dim1 != 1)
1603 throw ParseException(
1604 fmt::format(
"Broadcast is only supported for scalar or 1D tensors in Add node '{}'. "
1605 "Input dimensions should either match or one should be of size 1 and here, "
1608 TensorInfoAsString(*m_TensorsInfo[inputs.first].m_info, inputs.first,
1609 m_TensorsInfo[inputs.first].m_dtype),
1610 TensorInfoAsString(*m_TensorsInfo[inputs.second].m_info, inputs.second,
1611 m_TensorsInfo[inputs.second].m_dtype),
1617 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::Add, node.name().c_str());
1621 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
1624 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1625 { m_TensorsInfo[inputs.first].m_info->GetShape(),
1626 m_TensorsInfo[inputs.second].m_info->GetShape() });
1630 if(m_TensorsInfo[inputs.first].isConstant()) {
1631 CreateConstantLayer(inputs.first, fmt::format(
"Add:constant_of_{}", node.input(0)));
1633 if(m_TensorsInfo[inputs.second].isConstant()) {
1634 CreateConstantLayer(inputs.second, fmt::format(
"Add:constant_of_{}", node.input(1)));
1636 RegisterInputSlots(layer, {inputs.first, inputs.second});
1639 RegisterOutputSlots(layer, {node.output(0)});
1642void OnnxParserImpl::ParseAveragePool(
const onnx::NodeProto& node)
1647 uint32_t count_include_pad = 0;
1648 count_include_pad = ReadOptionalNodeUint32Attribute(node,
"count_include_pad");
1649 if(count_include_pad) {
1652 AddPoolingLayer(node, desc);
1655void OnnxParserImpl::ParseBatchNormalization(
const onnx::NodeProto& node)
1663 for(
int ind = 1; ind < node.input_size(); ++ind)
1665 auto tensor = node.input(ind);
1666 if(! m_TensorsInfo[tensor].isConstant())
1668 throw ParseException(
1669 fmt::format(
"Input tensor '{}' should be constant in BatchNormalization node '{}' {}",
1676 float epsilon = ReadOptionalNodeFloatAttribute(node,
"epsilon", 1e-5f);
1677 BatchNormalizationDescriptor desc;
1678 desc.
m_Eps = epsilon;
1680 auto scaleTensor = CreateConstTensor(node.input(1));
1681 auto biasTensor = CreateConstTensor(node.input(2));
1682 auto meanTensor = CreateConstTensor(node.input(3));
1683 auto varTensor = CreateConstTensor(node.input(4));
1685 IConnectableLayer* layer = m_Network->AddBatchNormalizationLayer(desc,
1690 node.name().c_str());
1694 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
1697 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1700 RegisterInputSlots(layer, {node.input(0)});
1703 RegisterOutputSlots(layer, {node.output(0)});
1706void OnnxParserImpl::ParseConcat(
const onnx::NodeProto& node)
1710 uint32_t numConcatView =
static_cast<uint32_t
>(node.input_size());
1711 uint32_t inputRank = m_TensorsInfo[node.input(0)].m_info->GetNumDimensions();
1713 int axisInt = ReadMandatoryNodeIntAttribute(node,
"axis");
1715 unsigned int concatDimInput =
static_cast<unsigned int>(
1716 (
static_cast<int>(inputRank) + axisInt) %
static_cast<int>(inputRank));
1718 OriginsDescriptor concatDescriptor(numConcatView, inputRank);
1719 concatDescriptor.SetConcatAxis(concatDimInput);
1721 unsigned int mergeDimOrigin = 0;
1723 std::vector<TensorShape> inputShapes;
1724 std::vector<std::string> tensorIds;
1726 for (
unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1728 std::string nodeName = node.input(
static_cast<int>(viewIndex));
1729 auto inputTensorInfo = *m_TensorsInfo[nodeName].m_info;
1730 inputShapes.push_back(inputTensorInfo.GetShape());
1731 tensorIds.push_back(nodeName);
1735 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
1738 IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, node.name().c_str());
1742 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
1745 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, inputShapes,
1746 m_TensorsInfo[node.input(0)].m_dtype);
1751 RegisterInputSlots(layer, tensorIds);
1754 RegisterOutputSlots(layer, { node.output(0) });
1757void OnnxParserImpl::ParseConstant(
const onnx::NodeProto& node)
1760 if (!node.attribute(0).has_t())
1762 throw ParseException(fmt::format(
"Value not found for Constant node '{}' {}",
1766 const onnx::TensorProto& onnxTensor = node.attribute(0).t();
1769 m_TensorsInfo[node.output(0)].m_tensor = std::make_unique<const onnx::TensorProto>(onnxTensor);
1770 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(onnxTensor));
1771 m_TensorsInfo[node.output(0)].m_dtype =
static_cast<onnx::TensorProto::DataType
>(onnxTensor.data_type());
1773 if (m_TensorsInfo[node.output(0)].m_dtype == onnx::TensorProto_DataType_FLOAT)
1775 CreateConstantLayer(node.output(0), node.name());
1777 else if (m_TensorsInfo[node.output(0)].m_dtype == onnx::TensorProto_DataType_INT64)
1779 CreateInt64ConstantLayer(node.output(0), node.name());
1783 throw ParseException(fmt::format(
"Data type not support for Constant node '{}' {}",
1789void OnnxParserImpl::ParseConv(
const onnx::NodeProto& node)
1796 if(m_TensorsInfo[node.input(0)].m_info->GetNumDimensions() != 4)
1798 throw ParseException(
1799 fmt::format(
"ArmNN only supports 2D convolution and Conv layer '{}' input {} {}",
1801 TensorInfoAsString(*m_TensorsInfo[node.input(0)].m_info, node.input(0),
1802 m_TensorsInfo[node.input(0)].m_dtype),
1806 if(!m_TensorsInfo[node.input(1)].isConstant())
1808 throw ParseException(
1809 fmt::format(
"Weights '{}' should be constant in Conv layer '{}' {}",
1815 auto inputInfo = *m_TensorsInfo[node.input(0)].m_info;
1817 Convolution2dDescriptor desc;
1820 std::vector<uint32_t> strides = ReadOptionalNodeUint32ListAttribute(node,
"strides");
1832 std::vector<uint32_t> dilations = ReadOptionalNodeUint32ListAttribute(node,
"dilations");
1833 if(!dilations.empty())
1839 std::vector<uint32_t> pads = ReadOptionalNodeUint32ListAttribute(node,
"pads");
1844 std::string paddingString = ReadOptionalNodeStringAttribute(node,
"auto_pad");
1845 if(paddingString !=
"VALID" && paddingString !=
"" && paddingString !=
"NOTSET")
1848 if( paddingString ==
"SAME_LOWER")
1852 else if (paddingString ==
"SAME_UPPER")
1858 throw ParseException(
1859 fmt::format(
"Invalid auto_pad attribute for node {}. Only SAME_UPPER, SAME_LOWER or VALID "
1860 "supported and found {} {}",
1865 uint32_t inputHeight = inputInfo.
GetShape()[2];
1866 uint32_t inputWidth = inputInfo.
GetShape()[3];
1868 uint32_t weightHeight;
1869 uint32_t weightWidth;
1870 std::vector<uint32_t> kernel_shape = ReadOptionalNodeUint32ListAttribute(node,
"kernel_shape");
1871 if (kernel_shape.empty())
1873 const TensorInfo weightTensorInfo = *m_TensorsInfo[node.input(1)].m_info;
1874 weightHeight = weightTensorInfo.
GetShape()[2];
1875 weightWidth = weightTensorInfo.
GetShape()[3];
1879 weightHeight = kernel_shape[0];
1880 weightWidth = kernel_shape[1];
1882 CalcPadding(inputHeight,
1889 CalcPadding(inputWidth,
1906 uint32_t group = ReadOptionalNodeUint32Attribute(node,
"group", 1);
1909 if (group > inputInfo.
GetShape()[1])
1911 throw ParseException(
1912 fmt::format(
"Error parsing Convolution node: {}. "
1913 "The 'group'={} parameter cannot be larger than the "
1914 "channel of the input shape={} (in NCHW format). {}",
1920 else if (group == inputInfo.
GetShape()[1])
1924 AddConvLayerWithDepthwiseConv(node, desc);
1929 throw ParseException(fmt::format(
"Error parsing Convolution node: {}. "
1930 "The 'group'={} parameter should be 1 or be equal to the "
1931 "channel of the input shape={} (in NCHW format). {}",
1940 armnn::IConnectableLayer* layer = m_Network->AddConvolution2dLayer(desc, node.name().c_str());
1941 std::vector<std::string> tensorIndexes= {node.input(0), node.input(1)};
1943 auto weightTensor = CreateConstTensor(node.input(1));
1945 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(weightTensor.first);
1949 if (node.input_size() == 3)
1951 if(!m_TensorsInfo[node.input(2)].isConstant())
1953 throw ParseException(fmt::format(
"Bias '{}' should be constant in Conv layer '{}' {}",
1959 auto biasTensor = CreateConstTensor(node.input(2));
1961 IConnectableLayer* biasLayer = m_Network->AddConstantLayer(biasTensor.first);
1965 tensorIndexes.emplace_back(node.input(2));
1970 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
1973 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1974 { m_TensorsInfo[node.input(0)].m_info->GetShape(),
1975 m_TensorsInfo[node.input(1)].m_info->GetShape() });
1980 RegisterInputSlots(layer, tensorIndexes);
1983 RegisterOutputSlots(layer, {node.output(0)});
1986void OnnxParserImpl::ParseFlatten(
const onnx::NodeProto& node)
1992 m_TensorsInfo[node.input(0)].m_dtype,
1993 onnx::TensorProto::FLOAT);
1995 int64_t axis = ReadOptionalNodeInt64Attribute(node,
"axis", 1);
1996 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2007 throw ParseException(fmt::format(
"Axis '{}' invalid. Tensor has '{}' dimensions in FlattenLayer '{}'",
2017 for (i = 0; i < axis; i++){
2018 dimension1 *= inputShape[i];
2023 dimension2 *= inputShape[i];
2026 TensorShape outputShape{dimension1, dimension2};
2028 auto outInfo = ComputeReshapeInfo(outputShape, inputShape, node.output(0));
2029 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
2030 CreateReshapeLayer(node.input(0), node.output(0), node.name());
2033void OnnxParserImpl::ParseGather(
const onnx::NodeProto& node)
2038 armnn::GatherDescriptor gatherDescriptor;
2039 gatherDescriptor.
m_Axis =
static_cast<int>(ReadOptionalNodeInt64Attribute(node,
"axis", 0));
2041 IConnectableLayer* layer = m_Network->AddGatherLayer(gatherDescriptor, node.name().c_str());
2045 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2048 const TensorShape& inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2049 const TensorShape& indicesShape = m_TensorsInfo[node.input(1)].m_info->GetShape();
2050 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, { inputShape, indicesShape },
2051 m_TensorsInfo[node.input(0)].m_dtype);
2055 RegisterInputSlots(layer, { node.input(0), node.input(1) });
2058 RegisterOutputSlots(layer, { node.output(0) });
2061void OnnxParserImpl::ParseGemm(
const onnx::NodeProto& node)
2066 int transA =
static_cast<int>(ReadOptionalNodeUint32Attribute(node,
"transA", 0));
2067 int transB =
static_cast<int>(ReadOptionalNodeUint32Attribute(node,
"transB", 0));
2068 float alpha = ReadOptionalNodeFloatAttribute(node,
"alpha", 1.0);
2069 float beta = ReadOptionalNodeFloatAttribute(node,
"beta", 1.0);
2070 bool biasEnabled = node.input_size() == 3;
2072 TensorShape input0Shape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2073 TensorShape input1Shape = m_TensorsInfo[node.input(1)].m_info->GetShape();
2076 armnn::FullyConnectedDescriptor fullyConnectedDescriptor;
2080 IConnectableLayer* layer =
nullptr;
2083 layer = m_Network->AddFullyConnectedLayer(fullyConnectedDescriptor, node.name().c_str());
2087 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2093 std::string transAName =
"transpose_" + node.input(0);
2094 armnn::TransposeDescriptor transposeADescriptor;
2096 IConnectableLayer* transALayer = m_Network->AddTransposeLayer(transposeADescriptor, transAName.c_str());
2100 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2103 auto transAInfo = ComputeOutputInfo({ transAName }, transALayer, { input0Shape });
2107 RegisterInputSlot(transALayer, node.input(0), 0);
2108 input0Shape = transAInfo[0].GetShape();
2112 RegisterInputSlot(layer, node.input(0), 0);
2116 if(m_TensorsInfo[node.input(1)].isConstant())
2118 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(CreateConstTensor(node.input(1)).first);
2119 TensorInfo weightInfo = *m_TensorsInfo[node.input(1)].m_info;
2126 std::string activationName =
"activation_" + node.input(1);
2127 armnn::ActivationDescriptor activationDescriptor;
2128 activationDescriptor.
m_A = alpha;
2129 activationDescriptor.
m_Function = ActivationFunction::Linear;
2130 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
2134 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2137 auto actInfo = ComputeOutputInfo({ activationName }, actLayer, { weightInfo.
GetShape() });
2141 input1Shape = actInfo[0].GetShape();
2146 input1Shape = weightInfo.
GetShape();
2154 std::string activationName =
"activation_" + node.input(1);
2155 armnn::ActivationDescriptor activationDescriptor;
2156 activationDescriptor.
m_A = alpha;
2157 activationDescriptor.
m_Function = ActivationFunction::Linear;
2158 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
2162 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2165 auto actInfo = ComputeOutputInfo({ activationName }, actLayer, { input1Shape });
2168 RegisterInputSlot(actLayer, node.input(1), 0);
2169 input1Shape = actInfo[0].GetShape();
2173 RegisterInputSlot(layer, node.input(1), 1);
2177 if(biasEnabled && m_TensorsInfo[node.input(2)].isConstant())
2180 IConnectableLayer* biasLayer = m_Network->AddConstantLayer(CreateConstTensor(node.input(2)).first);
2181 TensorInfo biasInfo = *m_TensorsInfo[node.input(2)].m_info;
2188 std::string activationName =
"activation_" + node.input(2);
2189 armnn::ActivationDescriptor activationDescriptor;
2190 activationDescriptor.
m_A = beta;
2191 activationDescriptor.
m_Function = ActivationFunction::Linear;
2192 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
2196 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2199 auto actInfo = ComputeOutputInfo({ activationName }, actLayer, { biasInfo.
GetShape() });
2209 else if (biasEnabled)
2212 if (m_TensorsInfo[node.input(2)].m_info->GetNumDimensions() != 1)
2214 throw ParseException(fmt::format(
"The parser supports constant or non-constant with 1 dimension for "
2215 "Input C of Gemm. Input '{}' in '{}' is not supported '{}'",
2223 std::string activationName =
"activation_" + node.input(2);
2224 armnn::ActivationDescriptor activationDescriptor;
2225 activationDescriptor.
m_A = beta;
2226 activationDescriptor.
m_Function = ActivationFunction::Linear;
2227 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
2231 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2234 auto actInfo = ComputeOutputInfo({ activationName },
2236 { m_TensorsInfo[node.input(2)].m_info->GetShape() });
2239 RegisterInputSlot(actLayer, node.input(2), 0);
2243 RegisterInputSlot(layer, node.input(2), 2);
2248 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
2249 { input0Shape, input1Shape });
2252 RegisterOutputSlots(layer, {node.output(0)});
2255void OnnxParserImpl::ParseGlobalAveragePool(
const onnx::NodeProto& node)
2261 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2265 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, node.name().c_str());
2269 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2272 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {inputShape});
2277 RegisterInputSlots(layer, {node.input(0)});
2280 RegisterOutputSlots(layer, {node.output(0)});
2283void OnnxParserImpl::ParseMaxPool(
const onnx::NodeProto& node)
2288 AddPoolingLayer(node, desc);
2291void OnnxParserImpl::ParseShape(
const onnx::NodeProto& node)
2296 IConnectableLayer* layer = m_Network->AddShapeLayer(node.name().c_str());
2300 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2303 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2304 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {inputShape}, onnx::TensorProto::INT64);
2308 RegisterInputSlots(layer, {node.input(0)});
2311 RegisterOutputSlots(layer, {node.output(0)});
2314void OnnxParserImpl::ParseReshape(
const onnx::NodeProto& node)
2320 m_TensorsInfo[node.input(0)].m_dtype,
2321 onnx::TensorProto::FLOAT);
2323 m_TensorsInfo[node.input(1)].m_dtype,
2324 onnx::TensorProto::INT64);
2326 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2328 std::vector<unsigned int> targetShape;
2329 if(m_TensorsInfo[node.input(1)].isConstant())
2331 unsigned int dims =
static_cast<unsigned int>(m_TensorsInfo[node.input(1)].m_tensor->int64_data_size());
2332 targetShape.reserve(dims);
2334 for(uint i = 0; i < dims; i++)
2336 int val =
CHECKED_INT32(m_TensorsInfo[node.input(1)].m_tensor->int64_data(
static_cast<int>(i)));
2337 targetShape[i]=
static_cast<unsigned int>(val);
2343 unsigned int dims = m_TensorsInfo[node.input(1)].m_info->GetNumDimensions();
2344 TensorShape shapes = m_TensorsInfo[node.input(1)].m_info->GetShape();
2345 if (dims != 1 || shapes[0] > 2)
2347 throw ParseException(fmt::format(
"Invalid input shape '{}' in Reshape layer '{}' {}",
2353 unsigned int numInputElements = m_TensorsInfo[node.input(0)].m_info->GetNumElements();
2356 targetShape = { numInputElements };
2358 else if (shapes[0] == 2)
2360 targetShape = { inputShape[0] , numInputElements / inputShape[0] };
2364 if(m_TensorsInfo[node.input(0)].isConstant())
2367 if(m_TensorsInfo.count(node.output(0)) == 0)
2369 m_TensorsInfo[node.output(0)] = OnnxTensor();
2371 m_TensorsInfo[node.output(0)].m_tensor =
2372 std::make_unique<onnx::TensorProto>(*m_TensorsInfo[node.input(0)].m_tensor);
2376 if(m_TensorsInfo.count(node.output(0)) == 0 || m_TensorsInfo[node.output(0)].m_info ==
nullptr)
2378 auto outInfo = ComputeReshapeInfo(
2379 TensorShape(
static_cast<unsigned int>(targetShape.size()), targetShape.data()),
2380 inputShape, node.output(0));
2381 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
2384 CreateReshapeLayer(node.input(0), node.output(0), node.name());
2388void OnnxParserImpl::ParseUnsqueeze(
const onnx::NodeProto& node)
2393 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2394 std::vector<uint32_t> dims;
2395 if (node.input_size() == 1 && node.attribute_size() > 0)
2397 dims = ReadMandatoryNodeUint32ListAttribute(node,
"axes");
2402 m_TensorsInfo[node.input(1)].m_dtype,
2403 onnx::TensorProto::INT64);
2405 auto int64Axes = m_TensorsInfo[node.input(1)].m_tensor->int64_data().data();
2408 for(uint i = 0; i < numDim; i++)
2411 dims.push_back(uint32Value);
2416 std::sort(dims.begin(), dims.end());
2418 std::vector<unsigned int> targetShape;
2424 targetShape.push_back(inputShape[i]);
2428 for(uint i = 0; i < dims.size(); i++)
2433 auto outInfo = ComputeReshapeInfo(TensorShape(
static_cast<unsigned int>(targetShape.size()), targetShape.data()),
2434 inputShape, node.output(0), m_TensorsInfo[node.input(0)].m_info->GetDataType());
2435 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
2436 m_TensorsInfo[node.output(0)].m_dtype = m_TensorsInfo[node.input(0)].m_dtype;
2438 CreateReshapeLayer(node.input(0), node.output(0), node.name());
2441void OnnxParserImpl::PrependForBroadcast(
const std::string& outputName,
2442 const std::string& input0,
2443 const std::string& input1)
2446 TensorInfo outputTensorInfo = TensorInfo(*m_TensorsInfo[input0].m_info);
2448 TensorShape input0Shape = m_TensorsInfo[input0].m_info->GetShape();
2449 TensorShape input1Shape = m_TensorsInfo[input1].m_info->GetShape();
2452 std::vector<uint32_t> newShape;
2455 newShape.push_back(1);
2460 newShape.push_back(input0Shape[dim]);
2462 outputTensorInfo.
SetShape(TensorShape(
static_cast<unsigned int>(newShape.size()), newShape.data()));
2465 m_TensorsInfo[outputName] = OnnxTensor();
2466 m_TensorsInfo[outputName].m_info = std::make_unique<TensorInfo>(outputTensorInfo);
2469 if( ! m_TensorsInfo[input0].isConstant())
2471 CreateReshapeLayer(input0, outputName, fmt::format(
"Add:reshapeOf{}", input0));
2475 m_TensorsInfo[outputName].m_tensor = std::make_unique<onnx::TensorProto>(*m_TensorsInfo[input0].m_tensor);
2480void OnnxParserImpl::SetupInputLayers()
2483 for(
int inputIndex = 0; inputIndex < m_Graph->input_size(); ++inputIndex)
2485 auto input = m_Graph->input(inputIndex);
2486 if (!m_TensorsInfo[input.name()].isConstant())
2488 IConnectableLayer* layer =
2490 TensorInfo tensorInfo = *m_TensorsInfo[input.name()].m_info;
2493 if (m_InputShapes.find(input.name()) == m_InputShapes.end())
2495 throw ParseException(fmt::format(
"The parser does not support dynamic tensor, "
2496 "please specify input shape for {}. {}",
2502 tensorInfo.
SetShape(m_InputShapes[input.name()]);
2503 m_TensorsInfo[input.name()].m_info = std::make_unique<TensorInfo>(tensorInfo);
2509 m_InputInfos[input.name()] = tensorInfo;
2511 RegisterOutputSlots(layer,{ input.name() });
2516void OnnxParserImpl::SetupOutputLayers()
2518 if(m_Graph->output_size() == 0)
2520 throw ParseException(fmt::format(
"The given model does not have any outputs {}",
CHECK_LOCATION().AsString()));
2523 for(
int outputIndex = 0; outputIndex < m_Graph->output_size(); ++outputIndex)
2525 IConnectableLayer* layer =
2527 m_Graph->output(outputIndex).name().c_str());
2529 RegisterInputSlots(layer, { m_Graph->output(outputIndex).name() });
2533void OnnxParserImpl::RegisterInputSlot(IConnectableLayer* layer,
2534 const std::string& tensorId,
2535 unsigned int slotIndex)
2537 armnn::IInputSlot* slot = &(layer->
GetInputSlot(slotIndex));
2539 auto it = m_TensorConnections.find(tensorId);
2541 if (it == m_TensorConnections.end())
2544 m_TensorConnections[tensorId] = TensorSlots();
2546 m_TensorConnections[tensorId].inputSlots.push_back(slot);
2549void OnnxParserImpl::RegisterInputSlots(IConnectableLayer* layer,
const std::vector<std::string>& tensorIds)
2553 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2558 throw ParseException(
2559 fmt::format(
"The number of tensor inputs ({}) does not match the number expected ({}) {}",
2565 for (
unsigned int slotIndex = 0; slotIndex < layer->
GetNumInputSlots(); ++slotIndex)
2567 std::string tensorId = tensorIds[slotIndex];
2568 armnn::IInputSlot* slot = &(layer->
GetInputSlot(slotIndex));
2570 auto it = m_TensorConnections.find(tensorId);
2572 if (it == m_TensorConnections.end())
2575 m_TensorConnections[tensorId] = TensorSlots();
2577 m_TensorConnections[tensorId].inputSlots.push_back(slot);
2581void OnnxParserImpl::RegisterOutputSlots(IConnectableLayer* layer,
const std::vector<std::string>& tensorIds)
2585 throw armnn::NullPointerException(fmt::format(
"Layer pointer is null {}",
CHECK_LOCATION().AsString()));
2590 throw ParseException(
2591 fmt::format(
"The number of tensor outputs ({}) does not match the number expected ({}) {} ",
2597 for (
unsigned int slotIndex = 0; slotIndex < layer->
GetNumOutputSlots(); ++slotIndex)
2599 std::string tensorId = tensorIds[slotIndex];
2600 armnn::IOutputSlot* slot = &(layer->
GetOutputSlot(slotIndex));
2602 auto it = m_TensorConnections.find(tensorId);
2604 if (it == m_TensorConnections.end())
2607 m_TensorConnections[tensorId] = TensorSlots();
2610 TensorSlots& tensorSlots = m_TensorConnections[tensorId];
2613 if (tensorSlots.outputSlot !=
nullptr)
2615 throw ParseException(fmt::format(
"Another layer has already registered itself as the producer of "
2620 tensorSlots.outputSlot = slot;
2627 for(
int i = 0; i < m_Graph->input_size(); ++i)
2629 auto input = m_Graph->input(i);
2630 if(input.name() == name)
2632 auto it = m_InputInfos.find(name);
2634 if (it != m_InputInfos.end())
2646 for(
int i = 0; i < m_Graph->output_size(); ++i)
2648 auto output = m_Graph->output(i);
2649 if(output.name() == name)
2651 auto it = m_OutputInfos.find(name);
2653 if (it != m_OutputInfos.end())
2665 if(model ==
nullptr) {
2670 std::vector<std::string> inputNames;
2671 std::map<std::string, bool> isConstant;
2672 for(
auto tensor : model->graph().initializer())
2674 isConstant[tensor.name()] =
true;
2676 for(
auto input : model->graph().input())
2678 auto it = isConstant.find(input.name());
2679 if(it == isConstant.end())
2681 inputNames.push_back(input.name());
2689 if(model ==
nullptr) {
2694 std::vector<std::string> outputNames;
2695 for(
auto output : model->graph().output())
2697 outputNames.push_back(output.name());
#define ARMNN_ASSERT(COND)
#define ARMNN_NO_DEPRECATE_WARN_BEGIN
#define ARMNN_NO_DEPRECATE_WARN_END
#define VALID_INPUTS(NODE, VALID_INPUTS)
#define CHECK_VALID_DATATYPE(NODE, TENSOR, ACTUAL,...)
#define CHECK_VALID_SIZE(ACTUAL,...)
#define CHECKED_INT32(VALUE)
#define CHECKED_NON_NEGATIVE(VALUE)
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
virtual std::vector< TensorShape > InferOutputShapes(const std::vector< TensorShape > &inputShapes) const =0
Infer the shape of the output(s) based on the provided input shape(s)
virtual unsigned int GetNumInputSlots() const =0
Returns the number of connectable input slots.
virtual unsigned int GetNumOutputSlots() const =0
Returns the number of connectable output slots.
virtual const char * GetName() const =0
Returns the name of the layer.
static INetworkPtr Create(const NetworkOptions &networkOptions={})
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
virtual int Connect(IInputSlot &destination)=0
bool has_value() const noexcept
const TensorShape & GetShape() const
unsigned int GetNumDimensions() const
unsigned int GetNumElements() const
void SetConstant(const bool IsConstant=true)
Marks the data corresponding to this tensor info as constant.
unsigned int GetNumBytes() const
void SetShape(const TensorShape &newShape)
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Dimensionality GetDimensionality() const
Function that returns the tensor type.
unsigned int GetNumElements() const
Function that calculates the tensor elements by multiplying all dimension size which are Specified.
static IOnnxParser * CreateRaw()
BindingPointInfo GetNetworkOutputBindingInfo(const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network output identified by the given layer...
armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile)
Create the network from a protobuf binary file on disk.
static void Destroy(IOnnxParser *parser)
armnn::INetworkPtr CreateNetworkFromString(const std::string &protoText)
Create the network directly from protobuf text in a string. Useful for debugging/testing.
BindingPointInfo GetNetworkInputBindingInfo(const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network input identified by the given layer ...
armnn::INetworkPtr CreateNetworkFromBinary(const std::vector< uint8_t > &binaryContent)
Create the network from a protobuf binary vector.
armnn::INetworkPtr CreateNetworkFromTextFile(const char *graphFile)
Create the network from a protobuf text file on disk.
static IOnnxParserPtr Create()
static ModelPtr LoadModelFromString(const std::string &inputString)
armnn::INetworkPtr CreateNetworkFromString(const std::string &protoText)
Create the network directly from protobuf text in a string. Useful for debugging/testing.
armnn::INetworkPtr CreateNetworkFromBinary(const std::vector< uint8_t > &binaryContent)
Create the network from a protobuf binary.
BindingPointInfo GetNetworkOutputBindingInfo(const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network output identified by the given layer...
static std::vector< std::string > GetInputs(ModelPtr &model)
Retrieve inputs names.
BindingPointInfo GetNetworkInputBindingInfo(const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network input identified by the given layer ...
static ModelPtr LoadModelFromBinary(const std::vector< uint8_t > &binaryContent)
static ModelPtr LoadModelFromTextFile(const char *fileName)
static const std::string GetVersion()
Retrieve version in X.Y.Z form.
armnn::INetworkPtr CreateNetworkFromTextFile(const char *graphFile)
Create the network from a protobuf text file on disk.
static ModelPtr LoadModelFromBinaryFile(const char *fileName)
static std::vector< std::string > GetOutputs(ModelPtr &model)
Retrieve outputs names.
armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile)
Create the network from a protobuf binary file on disk.
#define ONNX_PARSER_VERSION
ONNX_PARSER_VERSION: "X.Y.Z" where: X = Major version number Y = Minor version number Z = Patch versi...
const armnnSerializer::Pooling2dDescriptor * Pooling2dDescriptor
armnn::TensorInfo ToTensorInfo(TensorRawPtr tensorPtr)
Copyright (c) 2021 ARM Limited and Contributors.
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
std::unique_ptr< onnx::ModelProto > ModelPtr
armnn::BindingPointInfo BindingPointInfo
std::pair< armnn::ConstTensor, std::unique_ptr< T[]> > CreateConstTensorImpl(const T *bufferPtr, armnn::TensorInfo &tensorInfo, const armnn::Optional< armnn::PermutationVector & > permutationVector)
std::unique_ptr< IOnnxParser, void(*)(IOnnxParser *parser)> IOnnxParserPtr
void ProcessConcatInputTensorInfo(armnn::TensorInfo &inputTensorInfo, armnn::OriginsDescriptor &concatDescriptor, const unsigned int &concatAxis, unsigned int inputIndex, unsigned int &mergeDimOrigin)
armnn::TensorShape Permuted(const armnn::TensorShape &srcShape, const armnn::PermutationVector &mappings)
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH,...
float m_B
Beta lower bound value used by the activation functions. (BoundedReLu, Linear, TanH).
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu,...
float m_Eps
Value to add to the variance. Used to avoid dividing by zero.
std::string AsString() const
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_DilationY
Dilation along y axis.
uint32_t m_PadTop
Padding top value in the height dimension.
uint32_t m_DilationX
Dilation along x axis.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
bool m_BiasEnabled
Enable/disable bias.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_PadTop
Padding top value in the height dimension.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
bool m_BiasEnabled
Enable/disable bias.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
bool m_BiasEnabled
Enable/disable bias.
int32_t m_Axis
The axis in params to gather indices from.
uint32_t m_PadRight
Padding right value in the width dimension.
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadTop
Padding top value in the height dimension.
uint32_t m_PoolWidth
Pooling width value.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
OutputShapeRounding m_OutputShapeRounding
The rounding method for the output shape. (Floor, Ceiling).
TensorShape m_TargetShape
Target shape value.
PermutationVector m_DimMappings
Indicates how to translate tensor elements from a given source into the target destination,...