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)
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());