15 #include <fmt/format.h>
17 #include <google/protobuf/text_format.h>
18 #include <google/protobuf/io/zero_copy_stream_impl.h>
24 using namespace armnn;
29 IOnnxParser::IOnnxParser() : pOnnxParserImpl(new OnnxParserImpl()) {}
31 IOnnxParser::~IOnnxParser() =
default;
55 armnn::INetworkPtr IOnnxParser::CreateNetworkFromBinary(
const std::vector<uint8_t>& binaryContent)
57 return pOnnxParserImpl->CreateNetworkFromBinary(binaryContent);
60 armnn::INetworkPtr IOnnxParser::CreateNetworkFromBinary(
const std::vector<uint8_t>& 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);
95 BindingPointInfo IOnnxParser::GetNetworkInputBindingInfo(
const std::string& name)
const
97 return pOnnxParserImpl->GetNetworkInputBindingInfo(name);
102 return pOnnxParserImpl->GetNetworkOutputBindingInfo(name);
107 void CheckValidDataType(std::initializer_list<onnx::TensorProto::DataType> validInputTypes,
109 const char* validExpr,
110 std::string nodeName,
111 std::string tensorName,
114 bool isValid = std::any_of(validInputTypes.begin(),
115 validInputTypes.end(),
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, ...) \
130 CheckValidDataType({__VA_ARGS__}, ACTUAL, #__VA_ARGS__, NODE, TENSOR, CHECK_LOCATION())
132 using StrTypeListPair = std::pair<const char*, std::initializer_list<onnx::TensorProto::DataType>>;
133 #define STR_LIST(...) StrTypeListPair(#__VA_ARGS__, {__VA_ARGS__})
135 template <
typename Callable>
136 void 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 {} {}",
172 template <
typename Callable>
173 void 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()),
202 int ReadMandatoryNodeIntAttribute(
const onnx::NodeProto& node,
203 const std::string& name)
206 ReadMandatoryNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
207 [&attribValue](
const onnx::AttributeProto& attrValue)
214 int64_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();
227 std::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)
242 uint32_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)
255 std::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)
271 float 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();
284 std::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:
302 type = DataType::Float32;
305 case onnx::TensorProto::INT32:
306 case onnx::TensorProto::INT64:
308 type = DataType::Signed32;
314 fmt::format(
"'{}' is not a currently supported datatype for tensor {}."
315 " Supported dataTypes are FLOAT, INT32 and INT64. {}",
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());
361 std::string TensorInfoAsString(
const TensorInfo& info,
362 const std::string& name,
366 std::stringstream ss;
367 ss <<
"tensor '" << name <<
"' contains "
368 << onnx::TensorProto::DataType_Name(type)
369 <<
" and has shape [";
373 ss << shape[i] <<
", ";
379 void 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,
408 DataType dataType = DataType::Float32)
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 {} {}",
446 auto targetNumElements = armnn::numeric_cast<unsigned int>(
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;
473 const 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 }
495 template<
typename TypePair,
typename Location>
496 void 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, \
516 std::vector<TensorInfo> OnnxParserImpl::ComputeOutputInfo(std::vector<std::string> outNames,
518 std::vector<TensorShape> inputShapes,
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;
538 DataType armnnType = DataType::Float32;
541 if (inferredShapes.size() != outNames.size())
547 case onnx::TensorProto::FLOAT: {
548 armnnType = DataType::Float32;
551 case onnx::TensorProto::INT32:
552 case onnx::TensorProto::INT64: {
553 armnnType = DataType::Signed32;
558 fmt::format(
"'{}' is not a currently supported datatype for {}."
559 " Supported dataTypes are FLOAT, INT32 and INT64. {}",
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);
580 OnnxParserImpl::OnnxParserImpl()
581 : m_Network(nullptr, nullptr)
585 void OnnxParserImpl::ResetParser()
589 m_InputInfos.clear();
590 m_OutputInfos.clear();
593 void OnnxParserImpl::Cleanup()
595 m_TensorConnections.clear();
596 m_TensorsInfo.clear();
597 m_OutputsMap.clear();
598 m_OutputsFusedAndUsed.clear();
599 m_InputShapes.clear();
603 std::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));
629 std::pair<ConstTensor, std::unique_ptr<float[]>>
630 OnnxParserImpl::CreateConstTensor(
const std::string name,
633 TensorInfo tensorInfo = *m_TensorsInfo[name].m_info;
634 onnx::TensorProto onnxTensor = *m_TensorsInfo[name].m_tensor;
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()))
658 fmt::format(
"The number of data provided ({}) does not match the tensor '{}' number of "
660 onnxTensor.float_data_size(),
665 return CreateConstTensorImpl<float>(srcData, tensorInfo, permutationVector);
669 return CreateConstTensorImpl<float>(
reinterpret_cast<const float*
>(onnxTensor.raw_data().c_str()),
675 std::pair<ConstTensor, std::unique_ptr<int32_t[]>>
676 OnnxParserImpl::CreateInt64ConstTensor(
const std::string name,
679 TensorInfo tensorInfo = *m_TensorsInfo[name].m_info;
680 onnx::TensorProto onnxTensor = *m_TensorsInfo[name].m_tensor;
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()))
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);
719 return CreateConstTensorImpl<int32_t>(int32Data.data(), tensorInfo, permutationVector);
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);
730 return CreateConstTensorImpl<int32_t>(int32Data.data(), tensorInfo, permutationVector);
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);
892 INetworkPtr OnnxParserImpl::CreateNetworkFromModel(onnx::ModelProto& model)
894 m_Network = INetwork::Create();
897 m_Graph = std::make_unique<onnx::GraphProto>(*model.mutable_graph());
906 return std::move(m_Network);
909 void OnnxParserImpl::LoadGraph()
911 if (m_Graph.get() ==
nullptr)
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 =
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;
993 void 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 =
1004 void 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;
1061 template<
typename Location>
1062 void 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);
1094 template<
typename Location>
1095 void OnnxParserImpl::To1DTensor(
const std::string& name,
const Location& location)
1097 TensorShape shape = m_TensorsInfo[name].m_info->GetShape();
1098 std::vector<uint32_t> newShape;
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()));
1114 void OnnxParserImpl::AddConvLayerWithDepthwiseConv(
const onnx::NodeProto& node,
const Convolution2dDescriptor& convDesc)
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);
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));
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)});
1185 void 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;
1202 std::vector<std::string> inputNames;
1215 GetInputAndParam(*addNode,
nullptr, &biasName,
CHECK_LOCATION());
1219 biasInfo = *m_TensorsInfo[biasName].m_info;
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());
1258 std::vector<unsigned int> reshapedDimensions(2);
1259 reshapedDimensions[1] = weightInfo.
GetShape()[0];
1260 reshapedDimensions[0] = inputInfo.
GetNumElements() / reshapedDimensions[1];
1265 fmt::format(
"Failed to deduce input tensor shape from filter size {} {}",
1266 reshapedDimensions[1],
1272 inputInfo = reshapedTensorInfo;
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)
1324 fmt::format(
"Failed to deduce output tensor shape from filter size {} {}",
1325 reshapedDimensions[1],
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 });
1349 void 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());
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)});
1445 std::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;
1468 void 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});
1476 void 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});
1484 void 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;
1492 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1503 RegisterInputSlots(layer, {inputName});
1506 RegisterOutputSlots(layer, {outputName});
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());
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)});
1551 void OnnxParserImpl::ParseClip(
const onnx::NodeProto& node)
1553 ParseActivation(node, ActivationFunction::BoundedReLu);
1556 void OnnxParserImpl::ParseSigmoid(
const onnx::NodeProto& node)
1558 ParseActivation(node, ActivationFunction::Sigmoid);
1561 void OnnxParserImpl::ParseTanh(
const onnx::NodeProto& node)
1563 ParseActivation(node, ActivationFunction::TanH);
1566 void OnnxParserImpl::ParseRelu(
const onnx::NodeProto& node)
1568 ParseActivation(node, ActivationFunction::ReLu);
1571 void OnnxParserImpl::ParseLeakyRelu(
const onnx::NodeProto& node)
1573 ParseActivation(node, ActivationFunction::LeakyReLu);
1576 void 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())
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)
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());
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)});
1642 void 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);
1655 void 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())
1669 fmt::format(
"Input tensor '{}' should be constant in BatchNormalization node '{}' {}",
1676 float epsilon = ReadOptionalNodeFloatAttribute(node,
"epsilon", 1e-5f);
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));
1690 node.name().c_str());
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)});
1706 void 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));
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());
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) });
1757 void 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));
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 '{}' {}",
1789 void OnnxParserImpl::ParseConv(
const onnx::NodeProto& node)
1796 if(m_TensorsInfo[node.input(0)].m_info->GetNumDimensions() != 4)
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())
1809 fmt::format(
"Weights '{}' should be constant in Conv layer '{}' {}",
1815 auto inputInfo = *m_TensorsInfo[node.input(0)].m_info;
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")
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])
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). {}",
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));
1965 tensorIndexes.emplace_back(node.input(2));
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)});
1986 void 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];
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());
2033 void OnnxParserImpl::ParseGather(
const onnx::NodeProto& node)
2039 gatherDescriptor.
m_Axis =
static_cast<int>(ReadOptionalNodeInt64Attribute(node,
"axis", 0));
2041 IConnectableLayer* layer = m_Network->AddGatherLayer(gatherDescriptor, node.name().c_str());
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) });
2061 void 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();
2083 layer = m_Network->AddFullyConnectedLayer(fullyConnectedDescriptor, node.name().c_str());
2093 std::string transAName =
"transpose_" + node.input(0);
2096 IConnectableLayer* transALayer = m_Network->AddTransposeLayer(transposeADescriptor, transAName.c_str());
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);
2128 activationDescriptor.
m_A = alpha;
2129 activationDescriptor.
m_Function = ActivationFunction::Linear;
2130 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
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);
2156 activationDescriptor.
m_A = alpha;
2157 activationDescriptor.
m_Function = ActivationFunction::Linear;
2158 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
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);
2190 activationDescriptor.
m_A = beta;
2191 activationDescriptor.
m_Function = ActivationFunction::Linear;
2192 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
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);
2225 activationDescriptor.
m_A = beta;
2226 activationDescriptor.
m_Function = ActivationFunction::Linear;
2227 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
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)});
2255 void 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());
2272 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {inputShape});
2277 RegisterInputSlots(layer, {node.input(0)});
2280 RegisterOutputSlots(layer, {node.output(0)});
2283 void OnnxParserImpl::ParseMaxPool(
const onnx::NodeProto& node)
2288 AddPoolingLayer(node, desc);
2291 void OnnxParserImpl::ParseShape(
const onnx::NodeProto& node)
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)});
2314 void 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());
2388 void 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();
2406 uint numDim = armnn::numeric_cast<uint>(m_TensorsInfo[node.input(1)].m_tensor->int64_data_size());
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++)
2430 targetShape.insert(targetShape.begin() + armnn::numeric_cast<int>(dims[i]), 1);
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());
2441 void OnnxParserImpl::PrependForBroadcast(
const std::string& outputName,
2442 const std::string& input0,
2443 const std::string& input1)
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);
2480 void 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())
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() });
2516 void OnnxParserImpl::SetupOutputLayers()
2518 if(m_Graph->output_size() == 0)
2523 for(
int outputIndex = 0; outputIndex < m_Graph->output_size(); ++outputIndex)
2527 m_Graph->output(outputIndex).name().c_str());
2529 RegisterInputSlots(layer, { m_Graph->output(outputIndex).name() });
2534 const std::string& tensorId,
2535 unsigned int 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);
2549 void OnnxParserImpl::RegisterInputSlots(
IConnectableLayer* layer,
const std::vector<std::string>& tensorIds)
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];
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);
2581 void OnnxParserImpl::RegisterOutputSlots(
IConnectableLayer* layer,
const std::vector<std::string>& tensorIds)
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];
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 unsigned int GetNumInputSlots() const =0
Returns the number of connectable input slots.
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 GetNumOutputSlots() const =0
Returns the number of connectable output slots.
virtual const char * GetName() const =0
Returns the name of the layer.
An output connection slot for a layer.
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
virtual int Connect(IInputSlot &destination)=0
bool has_value() const noexcept
unsigned int GetNumDimensions() const
unsigned int GetNumElements() const
const TensorShape & GetShape() 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.
armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile)
Create the network from a protobuf binary file on disk.
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.
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::unique_ptr< IOnnxParser, void(*)(IOnnxParser *parser)> IOnnxParserPtr
std::pair< armnn::ConstTensor, std::unique_ptr< T[]> > CreateConstTensorImpl(const T *bufferPtr, armnn::TensorInfo &tensorInfo, const armnn::Optional< armnn::PermutationVector & > permutationVector)
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)
void Permute(const armnn::TensorShape &dstShape, const armnn::PermutationVector &mappings, const void *src, void *dst, size_t dataTypeSize)
An ActivationDescriptor for the ActivationLayer.
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,...
A BatchNormalizationDescriptor for the BatchNormalizationLayer.
float m_Eps
Value to add to the variance. Used to avoid dividing by zero.
std::string AsString() const
A Convolution2dDescriptor for the Convolution2dLayer.
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.
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
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.
A FullyConnectedDescriptor for the FullyConnectedLayer.
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
bool m_BiasEnabled
Enable/disable bias.
A GatherDescriptor for the GatherLayer.
int32_t m_Axis
The axis in params to gather indices from.
An OriginsDescriptor for the ConcatLayer.
A PermuteDescriptor for the PermuteLayer.
A Pooling2dDescriptor for the Pooling2dLayer.
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).
A ReshapeDescriptor for the ReshapeLayer.
TensorShape m_TargetShape
Target shape value.
A TransposeDescriptor for the TransposeLayer.
PermutationVector m_DimMappings
Indicates how to translate tensor elements from a given source into the target destination,...