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;
53 armnn::INetworkPtr IOnnxParser::CreateNetworkFromBinary(
const std::vector<uint8_t>& binaryContent)
55 return pOnnxParserImpl->CreateNetworkFromBinary(binaryContent);
58 armnn::INetworkPtr IOnnxParser::CreateNetworkFromBinary(
const std::vector<uint8_t>& binaryContent,
59 const std::map<std::string, armnn::TensorShape>& inputShapes)
61 return pOnnxParserImpl->CreateNetworkFromBinary(binaryContent, inputShapes);
66 return pOnnxParserImpl->CreateNetworkFromTextFile(graphFile);
71 return pOnnxParserImpl->CreateNetworkFromString(protoText);
75 const char* graphFile,
76 const std::map<std::string, armnn::TensorShape>& inputShapes)
78 return pOnnxParserImpl->CreateNetworkFromBinaryFile(graphFile, inputShapes);
82 const std::map<std::string, armnn::TensorShape>& inputShapes)
84 return pOnnxParserImpl->CreateNetworkFromTextFile(graphFile, inputShapes);
88 const std::map<std::string, armnn::TensorShape>& inputShapes)
90 return pOnnxParserImpl->CreateNetworkFromString(protoText, inputShapes);
93 BindingPointInfo IOnnxParser::GetNetworkInputBindingInfo(
const std::string& name)
const
95 return pOnnxParserImpl->GetNetworkInputBindingInfo(name);
98 BindingPointInfo IOnnxParser::GetNetworkOutputBindingInfo(
const std::string& name)
const
100 return pOnnxParserImpl->GetNetworkOutputBindingInfo(name);
105 void CheckValidDataType(std::initializer_list<onnx::TensorProto::DataType> validInputTypes,
107 const char* validExpr,
108 std::string nodeName,
109 std::string tensorName,
112 bool isValid = std::any_of(validInputTypes.begin(),
113 validInputTypes.end(),
118 fmt::format(
"Datatype {} is not valid for tensor '{}' of node '{}', not in {{{}}}. {}",
119 onnx::TensorProto::DataType_Name(actualValue),
127 #define CHECK_VALID_DATATYPE(NODE, TENSOR, ACTUAL, ...) \
128 CheckValidDataType({__VA_ARGS__}, ACTUAL, #__VA_ARGS__, NODE, TENSOR, CHECK_LOCATION())
130 using StrTypeListPair = std::pair<const char*, std::initializer_list<onnx::TensorProto::DataType>>;
131 #define STR_LIST(...) StrTypeListPair(#__VA_ARGS__, {__VA_ARGS__})
133 template <
typename Callable>
134 void ReadMandatoryNodeAttributeImpl(
const onnx::NodeProto& node,
135 const std::string& attribName,
136 onnx::AttributeProto::AttributeType expectedType,
139 auto attribs = node.attribute();
141 while (attriNum < node.attribute_size())
143 if (attribs.Get(attriNum).name() == attribName)
145 if (attribs.Get(attriNum).type() == expectedType)
147 callable(attribs.Get(attriNum));
151 throw ParseException(fmt::format(
"Attribute {} of node {} expected to have {} as "
152 "onnx::AttributeProto::AttributeType, but found {} instead {}",
155 onnx::AttributeProto::AttributeType_Name(expectedType),
156 onnx::AttributeProto::AttributeType_Name(attribs.Get(attriNum).type()),
163 if (attriNum == node.attribute_size())
165 throw ParseException(fmt::format(
"Could not find required attribute {} in node {} {}",
170 template <
typename Callable>
171 void ReadOptionalNodeAttributeImpl(
const onnx::NodeProto& node,
172 const std::string& attribName,
173 onnx::AttributeProto::AttributeType expectedType,
176 auto attribs = node.attribute();
177 for (
int attriNum = 0; attriNum < node.attribute_size(); ++attriNum)
179 if (attribs.Get(attriNum).name() == attribName)
181 if (attribs.Get(attriNum).type() == expectedType)
183 callable(attribs.Get(attriNum));
188 fmt::format(
"Attribute {} of node {} expected to have {} as onnx::AttributeProto::AttributeType, "
189 "but found {} instead {}",
192 onnx::AttributeProto::AttributeType_Name(expectedType),
193 onnx::AttributeProto::AttributeType_Name(attribs.Get(attriNum).type()),
200 int ReadMandatoryNodeIntAttribute(
const onnx::NodeProto& node,
201 const std::string& name)
204 ReadMandatoryNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
205 [&attribValue](
const onnx::AttributeProto& attrValue)
212 int64_t ReadOptionalNodeInt64Attribute(
const onnx::NodeProto& node,
213 const std::string& name,
214 const int64_t defaultValue = 0)
216 int64_t attribValue = defaultValue;
217 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
218 [&attribValue](
const onnx::AttributeProto& attrValue)
220 attribValue = attrValue.i();
225 std::vector<uint32_t> ReadMandatoryNodeUint32ListAttribute(
const onnx::NodeProto& node,
226 const std::string& name)
228 std::vector<uint32_t> attriList;
229 ReadMandatoryNodeAttributeImpl(node, name, onnx::AttributeProto::INTS,
230 [&attriList](
const onnx::AttributeProto& attrValue)
232 for (
int attriNum = 0; attriNum < attrValue.ints_size(); ++attriNum)
240 uint32_t ReadOptionalNodeUint32Attribute(
const onnx::NodeProto& node,
241 const std::string& name,
242 const uint32_t defaultVal = 0u)
244 uint32_t attribValue = defaultVal;
245 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
246 [&attribValue](
const onnx::AttributeProto& attrValue)
253 std::vector<uint32_t> ReadOptionalNodeUint32ListAttribute(
const onnx::NodeProto& node,
254 const std::string& name)
256 std::vector<uint32_t> attriList;
257 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INTS,
258 [&attriList](
const onnx::AttributeProto& attrValue)
260 for (
int attriNum = 0; attriNum < attrValue.ints_size(); ++attriNum)
269 float ReadOptionalNodeFloatAttribute(
const onnx::NodeProto& node,
270 const std::string& name,
271 const float defaultValue = 0.0f)
273 float attribValue = defaultValue;
274 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::FLOAT,
275 [&attribValue](
const onnx::AttributeProto& attrValue)
277 attribValue = attrValue.f();
282 std::string ReadOptionalNodeStringAttribute(
const onnx::NodeProto& node,
const std::string& name)
284 std::string attribValue =
"";
285 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::STRING,
286 [&attribValue](
const onnx::AttributeProto& attrValue)
288 attribValue = attrValue.s();
298 case onnx::TensorProto::FLOAT:
300 type = DataType::Float32;
303 case onnx::TensorProto::INT32:
304 case onnx::TensorProto::INT64:
306 type = DataType::Signed32;
312 fmt::format(
"'{}' is not a currently supported datatype for tensor {}."
313 " Supported dataTypes are FLOAT, INT32 and INT64. {}",
327 if(std::find(shape.begin(), shape.end(), 0) != shape.end())
337 const onnx::TensorShapeProto onnxShape =
info.type().tensor_type().shape();
338 std::vector<unsigned int> shapeDims;
339 for (
int i = 0; i < onnxShape.dim_size(); ++i)
349 std::vector<unsigned int> shapeDims;
351 for (
auto dim: tensor.dims())
356 return ToTensorInfo(tensor.name(), shapeDims, tensor.data_type());
359 std::string TensorInfoAsString(
const TensorInfo& info,
360 const std::string& name,
364 std::stringstream ss;
365 ss <<
"tensor '" << name <<
"' contains "
366 << onnx::TensorProto::DataType_Name(type)
367 <<
" and has shape [";
371 ss << shape[i] <<
", ";
377 void CalcPadding(uint32_t inputSize,
381 uint32_t* paddingFront,
382 uint32_t* paddingBack,
385 uint32_t outputSize = (inputSize + stride - 1) / stride;
386 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
387 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
388 *paddingFront = (temp - inputSize) / 2;
389 *paddingBack = *paddingFront;
390 if((temp - inputSize) % 2 == 1)
405 const std::string& outName,
406 DataType dataType = DataType::Float32)
408 std::vector<int> targetDims;
414 targetDims.push_back(
static_cast<int>(inShape[
static_cast<uint
>(i)]));
418 targetDims.push_back(val);
422 std::vector<unsigned int> outDims(targetDims.begin(), targetDims.end());
423 const auto stretchDim = std::find(targetDims.begin(), targetDims.end(), -1);
424 if (stretchDim != targetDims.end())
426 if (std::find(std::next(stretchDim), targetDims.end(), -1) != targetDims.end())
428 std::stringstream ss;
430 for(uint i = 0; i < targetDims.size() - 1; ++i)
432 ss << targetDims[i] <<
", ";
434 ss << targetDims[targetDims.size() - 1] <<
" ]";
437 fmt::format(
"Error during creation of reshaped tensor '{}'. At most one component of shape can be "
438 " -1 and here, shape is {} {}",
444 auto targetNumElements = armnn::numeric_cast<unsigned int>(
445 std::accumulate(targetDims.begin(), targetDims.end(), -1, std::multiplies<int32_t>()));
446 auto stretchIndex =
static_cast<size_t>(std::distance(targetDims.begin(), stretchDim));
447 if (targetNumElements == 0)
451 outDims[stretchIndex] = 0;
456 fmt::format(
"Input to reshape is a tensor with elements, but the requested shape has 0. {}",
462 outDims[stretchIndex] = inShape.
GetNumElements() / targetNumElements;
471 const std::map<std::string, OnnxParserImpl::OperationParsingFunction> OnnxParserImpl::m_ParserFunctions = {
472 {
"BatchNormalization", &OnnxParserImpl::ParseBatchNormalization},
473 {
"GlobalAveragePool", &OnnxParserImpl::ParseGlobalAveragePool},
474 {
"AveragePool", &OnnxParserImpl::ParseAveragePool },
475 {
"Clip", &OnnxParserImpl::ParseClip },
476 {
"Constant", &OnnxParserImpl::ParseConstant },
477 {
"MaxPool", &OnnxParserImpl::ParseMaxPool },
478 {
"Reshape", &OnnxParserImpl::ParseReshape },
479 {
"Sigmoid", &OnnxParserImpl::ParseSigmoid },
480 {
"Tanh", &OnnxParserImpl::ParseTanh },
481 {
"Relu", &OnnxParserImpl::ParseRelu },
482 {
"LeakyRelu", &OnnxParserImpl::ParseLeakyRelu },
483 {
"Conv", &OnnxParserImpl::ParseConv },
484 {
"Add", &OnnxParserImpl::ParseAdd },
485 {
"Flatten", &OnnxParserImpl::ParseFlatten },
486 {
"Shape", &OnnxParserImpl::ParseShape },
487 {
"Gather", &OnnxParserImpl::ParseGather },
488 {
"Unsqueeze", &OnnxParserImpl::ParseUnsqueeze },
489 {
"Concat", &OnnxParserImpl::ParseConcat },
490 {
"Gemm", &OnnxParserImpl::ParseGemm }
493 template<
typename TypePair,
typename Location>
494 void OnnxParserImpl::ValidateInputs(
const onnx::NodeProto& node,
495 TypePair validInputs,
496 const Location& location)
498 for(
auto input : node.input())
500 CheckValidDataType(validInputs.second,
501 m_TensorsInfo[input].m_dtype,
509 #define VALID_INPUTS(NODE, VALID_INPUTS) \
510 OnnxParserImpl::ValidateInputs(NODE, \
514 std::vector<TensorInfo> OnnxParserImpl::ComputeOutputInfo(std::vector<std::string> outNames,
516 std::vector<TensorShape> inputShapes,
519 if (outNames.empty())
524 bool needCompute = std::any_of(outNames.begin(),
526 [
this](std::string name)
528 return (m_TensorsInfo.count(name) == 0 ||
529 m_TensorsInfo[name].m_info == nullptr ||
530 m_TensorsInfo[name].m_info->GetShape().GetDimensionality() ==
531 Dimensionality::NotSpecified);
533 std::vector<TensorInfo> outInfo;
535 std::vector<TensorShape> inferredShapes;
536 DataType armnnType = DataType::Float32;
539 if (inferredShapes.size() != outNames.size())
545 case onnx::TensorProto::FLOAT: {
546 armnnType = DataType::Float32;
549 case onnx::TensorProto::INT32:
550 case onnx::TensorProto::INT64: {
551 armnnType = DataType::Signed32;
556 fmt::format(
"'{}' is not a currently supported datatype for {}."
557 " Supported dataTypes are FLOAT, INT32 and INT64. {}",
564 for (uint i = 0; i < outNames.size(); ++i)
568 m_TensorsInfo[outNames[i]] = OnnxTensor();
569 m_TensorsInfo[outNames[i]].m_info = std::make_unique<TensorInfo>(
571 m_TensorsInfo[outNames[i]].m_dtype = dataType;
573 outInfo.push_back(*m_TensorsInfo[outNames[i]].m_info);
578 OnnxParserImpl::OnnxParserImpl()
579 : m_Network(nullptr, nullptr)
583 void OnnxParserImpl::ResetParser()
587 m_InputInfos.clear();
588 m_OutputInfos.clear();
591 void OnnxParserImpl::Cleanup()
593 m_TensorConnections.clear();
594 m_TensorsInfo.clear();
595 m_OutputsMap.clear();
596 m_OutputsFusedAndUsed.clear();
597 m_InputShapes.clear();
601 std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
606 if (bufferPtr ==
nullptr)
617 reinterpret_cast<const T*
>(bufferPtr), data.get(),
sizeof(T));
621 ::memcpy(data.get(), bufferPtr, tensorInfo.
GetNumBytes());
624 return std::make_pair(
ConstTensor(tensorInfo, data.get()), std::move(data));
627 std::pair<ConstTensor, std::unique_ptr<float[]>>
628 OnnxParserImpl::CreateConstTensor(
const std::string name,
631 TensorInfo tensorInfo = *m_TensorsInfo[name].m_info;
632 onnx::TensorProto onnxTensor = *m_TensorsInfo[name].m_tensor;
644 throw ParseException(fmt::format(
"No tensor data found for Const tensor '{}' {}",
649 auto srcData = onnxTensor.float_data().data();
651 if (!onnxTensor.has_raw_data())
653 if(tensorInfo.
GetNumElements() !=
static_cast<uint
>(onnxTensor.float_data_size()))
656 fmt::format(
"The number of data provided ({}) does not match the tensor '{}' number of "
658 onnxTensor.float_data_size(),
663 return CreateConstTensorImpl<float>(srcData, tensorInfo, permutationVector);
667 return CreateConstTensorImpl<float>(
reinterpret_cast<const float*
>(onnxTensor.raw_data().c_str()),
673 std::pair<ConstTensor, std::unique_ptr<int32_t[]>>
674 OnnxParserImpl::CreateInt64ConstTensor(
const std::string name,
677 TensorInfo tensorInfo = *m_TensorsInfo[name].m_info;
678 onnx::TensorProto onnxTensor = *m_TensorsInfo[name].m_tensor;
688 if (numElements == 0)
690 throw ParseException(fmt::format(
"No tensor data found for Const tensor '{}' {}",
696 if (!onnxTensor.has_raw_data())
698 auto srcData = onnxTensor.int64_data().data();
699 if(numElements !=
static_cast<uint
>(onnxTensor.int64_data_size()))
702 fmt::format(
"The number of data provided ({}) does not match the tensor '{}' number of "
704 onnxTensor.int64_data_size(),
710 std::vector<int32_t> int32Data;
711 for(uint i = 0; i < numElements; i++)
714 int32Data.push_back(int32Value);
717 return CreateConstTensorImpl<int32_t>(int32Data.data(), tensorInfo, permutationVector);
721 auto srcData =
reinterpret_cast<const int64_t*
>(onnxTensor.raw_data().c_str());
722 std::vector<int32_t> int32Data;
723 for(uint i = 0; i < numElements; i++)
726 int32Data.push_back(int32Value);
728 return CreateConstTensorImpl<int32_t>(int32Data.data(), tensorInfo, permutationVector);
734 FILE* fd = fopen(graphFile,
"r");
742 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
743 using google::protobuf::io::FileInputStream;
744 std::unique_ptr<FileInputStream> input = std::make_unique<FileInputStream>(fileno(fd));
745 bool success = google::protobuf::TextFormat::Parse(input.get(), modelProto.get());
750 std::stringstream
error;
751 error <<
"Failed to parse graph file";
761 return CreateNetworkFromModel(*modelProto);
765 const std::map<std::string, armnn::TensorShape>& inputShapes)
768 m_InputShapes = inputShapes;
770 return CreateNetworkFromModel(*modelProto);
777 return CreateNetworkFromModel(*modelProto);
781 const std::map<std::string, armnn::TensorShape>& inputShapes)
784 m_InputShapes = inputShapes;
786 return CreateNetworkFromModel(*modelProto);
791 if (binaryContent.size() == 0)
796 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
798 google::protobuf::io::CodedInputStream codedStream(binaryContent.data(),
static_cast<int>(binaryContent.size()));
799 codedStream.SetTotalBytesLimit(INT_MAX);
800 bool success = modelProto.get()->ParseFromCodedStream(&codedStream);
804 std::stringstream
error;
805 error <<
"Failed to parse graph";
813 FILE* fd = fopen(graphFile,
"rb");
821 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
823 google::protobuf::io::FileInputStream inStream(fileno(fd));
824 google::protobuf::io::CodedInputStream codedStream(&inStream);
825 codedStream.SetTotalBytesLimit(INT_MAX);
826 bool success = modelProto.get()->ParseFromCodedStream(&codedStream);
831 std::stringstream
error;
832 error <<
"Failed to parse graph file";
843 return CreateNetworkFromModel(*modelProto);
847 const std::map<std::string, armnn::TensorShape>& inputShapes)
850 m_InputShapes = inputShapes;
852 return CreateNetworkFromModel(*modelProto);
863 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
864 bool success = google::protobuf::TextFormat::ParseFromString(protoText, modelProto.get());
867 std::stringstream
error;
868 error <<
"Failed to parse graph file";
878 return CreateNetworkFromModel(*modelProto);
882 const std::map<std::string, armnn::TensorShape>& inputShapes)
885 m_InputShapes = inputShapes;
887 return CreateNetworkFromModel(*modelProto);
890 INetworkPtr OnnxParserImpl::CreateNetworkFromModel(onnx::ModelProto& model)
892 m_Network = INetwork::Create();
895 m_Graph = std::make_unique<onnx::GraphProto>(*model.mutable_graph());
904 return std::move(m_Network);
907 void OnnxParserImpl::LoadGraph()
909 if (m_Graph.get() ==
nullptr)
915 SetupInfo(m_Graph->mutable_output());
916 SetupInfo(m_Graph->mutable_input());
917 SetupInfo(m_Graph->mutable_value_info());
919 for (
auto tensor : m_Graph->initializer())
921 m_TensorsInfo[tensor.name()].m_tensor = std::make_unique<const onnx::TensorProto>(tensor);
922 m_TensorsInfo[tensor.name()].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(tensor));
923 m_TensorsInfo[tensor.name()].m_dtype =
931 DetectFullyConnected();
934 for(
size_t nodeIndex = 0; nodeIndex < static_cast<size_t>(m_Graph->node_size()); nodeIndex++)
936 auto node = m_Graph->node(
static_cast<int>(nodeIndex));
937 const std::string& operation = node.op_type();
940 if (operation ==
"MatMul" )
942 if(m_OutputsFusedAndUsed[nodeIndex].inputForNodes != m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.size())
945 AddFullyConnected(node);
948 else if (!(m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.empty()) && operation ==
"Add")
950 int matmulIndex =
static_cast<int> (m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes[0]);
951 AddFullyConnected(m_Graph->node(matmulIndex), &node);
953 else if (m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.empty())
955 auto it = m_ParserFunctions.find(operation);
956 if (it != m_ParserFunctions.end())
958 auto func = it->second;
963 throw ParseException(fmt::format(
"Unsupported operation {} for node '{}' {}",
972 for (
const auto& tensorCon : m_TensorConnections)
974 if (tensorCon.second.outputSlot !=
nullptr)
976 for (
size_t inputSlotIdx = 0; inputSlotIdx < tensorCon.second.inputSlots.size(); ++inputSlotIdx)
978 tensorCon.second.outputSlot->Connect(*(tensorCon.second.inputSlots[inputSlotIdx]));
984 for(
int outputIndex = 0; outputIndex < m_Graph->output_size(); ++outputIndex)
986 auto output = m_Graph->output(outputIndex);
987 m_OutputInfos[output.name()] = *m_TensorsInfo[output.name()].m_info;
991 void OnnxParserImpl::SetupInfo(
const google::protobuf::RepeatedPtrField<onnx::ValueInfoProto >* list)
993 for (
auto tensor : *list)
995 m_TensorsInfo[tensor.name()] = OnnxTensor();
996 m_TensorsInfo[tensor.name()].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(tensor));
997 m_TensorsInfo[tensor.name()].m_dtype =
1002 void OnnxParserImpl::DetectFullyConnected()
1004 m_OutputsFusedAndUsed = std::vector<UsageSummary> (
static_cast<size_t>(m_Graph->node_size()), UsageSummary());
1005 auto matmulAndConstant = [&](
const std::string& constInput,
1006 const std::string& matmulInput,
1009 auto matmulIt = m_OutputsMap.find(matmulInput);
1010 if(matmulIt != m_OutputsMap.end() && matmulIt->second.first->op_type() ==
"MatMul"
1011 && m_TensorsInfo[constInput].isConstant())
1013 nodeIndex = matmulIt->second.second;
1019 for(
int nodeIndex = 0; nodeIndex < m_Graph->node_size(); nodeIndex++)
1021 const onnx::NodeProto* node = &m_Graph->node(nodeIndex);
1022 for (
const std::string& output : node->output())
1024 m_OutputsMap[output] = std::make_pair(node, nodeIndex);
1027 for (
const std::string& input : node->input())
1029 auto matmulIt = m_OutputsMap.find(input);
1030 if(matmulIt != m_OutputsMap.end()){
1031 ++m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIt->second.second)].inputForNodes;
1035 if (node->op_type() ==
"Add")
1037 int matmulIndex = 0;
1038 if (matmulAndConstant(node->input(0), node->input(1), matmulIndex) ||
1039 matmulAndConstant(node->input(1), node->input(0), matmulIndex))
1042 m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIndex)].fusedWithNodes
1043 .push_back(
static_cast<size_t>(nodeIndex));
1045 m_OutputsFusedAndUsed[
static_cast<size_t>(nodeIndex)].fusedWithNodes
1046 .push_back(
static_cast<size_t>(matmulIndex));
1051 for (
auto output: m_Graph->output()) {
1052 auto matmulIt = m_OutputsMap.find(output.name());
1053 if(matmulIt != m_OutputsMap.end()){
1054 ++m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIt->second.second)].inputForNodes;
1059 template<
typename Location>
1060 void OnnxParserImpl::GetInputAndParam(
const onnx::NodeProto& node,
1061 std::string* inputName,
1062 std::string* constName,
1063 const Location& location)
1066 if (m_TensorsInfo[node.input(0)].isConstant())
1070 else if (m_TensorsInfo[node.input(1)].isConstant())
1076 throw ParseException(fmt::format(
"One of the input tensors ('{}' or '{}') should be constant in node '{}' {}",
1080 location.AsString()));
1084 *constName = node.input(cstIndex);
1088 *inputName = node.input(!cstIndex);
1092 template<
typename Location>
1093 void OnnxParserImpl::To1DTensor(
const std::string& name,
const Location& location)
1095 TensorShape shape = m_TensorsInfo[name].m_info->GetShape();
1096 std::vector<uint32_t> newShape;
1102 fmt::format(
"Only tensors with shape [1, ..., 1, X] can be converted to 1D and {} {}",
1103 TensorInfoAsString(*m_TensorsInfo[name].m_info, name, m_TensorsInfo[name].m_dtype),
1104 location.AsString()));
1109 m_TensorsInfo[name].m_info->SetShape(
TensorShape(
static_cast<unsigned int>(newShape.size()), newShape.data()));
1112 void OnnxParserImpl::AddConvLayerWithDepthwiseConv(
const onnx::NodeProto& node,
const Convolution2dDescriptor& convDesc)
1126 std::string permuteStr =
"permute_" + node.input(1);
1127 std::vector<std::string> tensorIndexes= {node.input(0), permuteStr};
1129 auto weightTensor = CreateConstTensor(node.input(1));
1130 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(weightTensor.first);
1145 if (node.input_size() == 3)
1147 if(!m_TensorsInfo[node.input(2)].isConstant())
1149 throw ParseException(fmt::format(
"Bias '{}' should be constant in Conv layer '{}' {}",
1156 auto biasTensor = CreateConstTensor(node.input(2));
1157 tensorIndexes.emplace_back(node.input(2));
1169 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1170 { m_TensorsInfo[node.input(0)].m_info->GetShape(),
1177 RegisterInputSlots(layer, tensorIndexes);
1180 RegisterOutputSlots(layer, {node.output(0)});
1183 void OnnxParserImpl::AddFullyConnected(
const onnx::NodeProto& matmulNode,
const onnx::NodeProto* addNode)
1186 std::string inputName;
1187 std::string weightName;
1188 std::string biasName;
1189 std::string outputName;
1194 GetInputAndParam(matmulNode, &inputName, &weightName,
CHECK_LOCATION());
1196 TensorInfo inputInfo = *m_TensorsInfo[inputName].m_info;
1197 TensorInfo weightInfo = *m_TensorsInfo[weightName].m_info;
1200 std::vector<std::string> inputNames;
1213 GetInputAndParam(*addNode,
nullptr, &biasName,
CHECK_LOCATION());
1217 biasInfo = *m_TensorsInfo[biasName].m_info;
1222 fmt::format(
"Shape of weights '{}' and bias of following Add node '{}' do not match : {}"
1223 " and {} ( /!\\ bias should be a 1D tensor) {}",
1226 TensorInfoAsString(*m_TensorsInfo[weightName].m_info, weightName,
1227 m_TensorsInfo[weightName].m_dtype),
1228 TensorInfoAsString(*m_TensorsInfo[biasName].m_info, biasName,
1229 m_TensorsInfo[biasName].m_dtype ),
1233 inputNames = { inputName, weightName, biasName };
1234 outputName = addNode->output(0);
1238 inputNames = { inputName, weightName };
1239 outputName = matmulNode.output(0);
1243 layer = m_Network->AddFullyConnectedLayer(desc, matmulNode.name().c_str());
1256 std::vector<unsigned int> reshapedDimensions(2);
1257 reshapedDimensions[1] = weightInfo.
GetShape()[0];
1258 reshapedDimensions[0] = inputInfo.
GetNumElements() / reshapedDimensions[1];
1263 fmt::format(
"Failed to deduce input tensor shape from filter size {} {}",
1264 reshapedDimensions[1],
1270 inputInfo = reshapedTensorInfo;
1275 std::string reshapeLayerName = fmt::format(
"Reshape_for:{}", layer->
GetName());
1276 IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(reshapeDescriptor, reshapeLayerName.c_str());
1281 RegisterInputSlots(reshapeLayer, {inputName});
1282 inputNames[0] = reshapeLayerName;
1285 auto outputInfo = ComputeOutputInfo({ outputName },
1291 RegisterInputSlots(layer, inputNames);
1294 if(m_TensorsInfo[weightName].isConstant())
1296 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(CreateConstTensor(weightName).first);
1303 if(desc.
m_BiasEnabled && m_TensorsInfo[biasName].isConstant())
1305 IConnectableLayer* biasLayer = m_Network->AddConstantLayer(CreateConstTensor(biasName).first);
1312 if (outputInfo[0].GetNumDimensions() > 2)
1315 std::vector<unsigned int> reshapedDimensions(2);
1316 reshapedDimensions[1] = weightInfo.
GetShape()[1];
1317 reshapedDimensions[0] = outputInfo[0].
GetNumElements() / reshapedDimensions[1];
1319 if (outputInfo[0].GetNumElements() % reshapedDimensions[1] != 0)
1322 fmt::format(
"Failed to deduce output tensor shape from filter size {} {}",
1323 reshapedDimensions[1],
1334 std::string reshapeLayerName = fmt::format(
"ExpandDims_for:{}", layer->
GetName());
1335 IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, reshapeLayerName.c_str());
1340 RegisterInputSlots(reshapeLayer, {layer->
GetName()});
1341 layer = reshapeLayer;
1344 RegisterOutputSlots(layer, { outputName });
1347 void OnnxParserImpl::AddPoolingLayer(
const onnx::NodeProto& node,
Pooling2dDescriptor& desc)
1355 std::vector<uint32_t> kernel_shape = ReadMandatoryNodeUint32ListAttribute(node,
"kernel_shape");
1356 std::vector<uint32_t> strides = ReadOptionalNodeUint32ListAttribute(node,
"strides");
1357 std::vector<uint32_t> pads = ReadOptionalNodeUint32ListAttribute(node,
"pads");
1378 std::string paddingString = ReadOptionalNodeStringAttribute(node,
"auto_pad");
1379 if(paddingString !=
"VALID" && paddingString !=
"" && paddingString !=
"NOTSET")
1382 if( paddingString ==
"SAME_LOWER")
1386 else if (paddingString ==
"SAME_UPPER")
1392 throw ParseException(fmt::format(
"Invalid auto_pad attribute for node {}. "
1393 "Only SAME_UPPER, SAME_LOWER or VALID supported and found {} {}",
1398 auto inputInfo = *m_TensorsInfo[node.input(0)].m_info;
1399 uint32_t inputHeight = inputInfo.
GetShape()[2];
1400 uint32_t inputWidth = inputInfo.
GetShape()[3];
1401 CalcPadding(inputHeight,
1408 CalcPadding(inputWidth,
1425 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, node.name().c_str());
1432 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1437 RegisterInputSlots(layer, {node.input(0)});
1440 RegisterOutputSlots(layer, {node.output(0)});
1443 std::pair<std::string, std::string> OnnxParserImpl::AddPrepareBroadcast(
const std::string& input0,
1444 const std::string& input1)
1446 std::pair<std::string, std::string> inputs = std::make_pair(input0, input1);
1448 TensorShape input0Shape = m_TensorsInfo[input0].m_info->GetShape();
1449 TensorShape input1Shape = m_TensorsInfo[input1].m_info->GetShape();
1453 auto outputName = fmt::format(
"reshape_output_{}", input1);
1454 PrependForBroadcast(outputName, input1, input0);
1455 inputs.second = outputName;
1459 auto outputName = fmt::format(
"reshape_output_{}", input0);
1460 PrependForBroadcast(outputName, input0, input1);
1461 inputs.first = outputName;
1466 void OnnxParserImpl::CreateConstantLayer(
const std::string& tensorName,
const std::string& layerName)
1468 auto armnnTensor = CreateConstTensor(tensorName);
1469 IConnectableLayer* layer = m_Network->AddConstantLayer(armnnTensor.first, layerName.c_str());
1471 RegisterOutputSlots(layer, {tensorName});
1474 void OnnxParserImpl::CreateInt64ConstantLayer(
const std::string& tensorName,
const std::string& layerName)
1476 auto armnnTensor = CreateInt64ConstTensor(tensorName);
1477 IConnectableLayer* layer = m_Network->AddConstantLayer(armnnTensor.first, layerName.c_str());
1479 RegisterOutputSlots(layer, {tensorName});
1482 void OnnxParserImpl::CreateReshapeLayer(
const std::string& inputName,
1483 const std::string& outputName,
1484 const std::string& layerName)
1486 const TensorInfo outputTensorInfo = *m_TensorsInfo[outputName].m_info;
1490 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1501 RegisterInputSlots(layer, {inputName});
1504 RegisterOutputSlots(layer, {outputName});
1517 if (func == ActivationFunction::BoundedReLu)
1519 if (node.input_size() == 1 && node.attribute_size() > 0)
1521 desc.
m_A = ReadOptionalNodeFloatAttribute(node,
"max", std::numeric_limits<float>::max());
1522 desc.
m_B = ReadOptionalNodeFloatAttribute(node,
"min", std::numeric_limits<float>::lowest());
1526 desc.
m_A = node.input(2).empty() ? std::numeric_limits<float>::max() :
std::stof(node.input(2));
1527 desc.
m_B = node.input(1).empty() ? std::numeric_limits<float>::lowest() :
std::stof(node.input(1));
1531 IConnectableLayer*
const layer = m_Network->AddActivationLayer(desc, node.name().c_str());
1538 auto outputInfo = ComputeOutputInfo({ node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1543 RegisterInputSlots(layer, {node.input(0)});
1546 RegisterOutputSlots(layer, {node.output(0)});
1549 void OnnxParserImpl::ParseClip(
const onnx::NodeProto& node)
1551 ParseActivation(node, ActivationFunction::BoundedReLu);
1554 void OnnxParserImpl::ParseSigmoid(
const onnx::NodeProto& node)
1556 ParseActivation(node, ActivationFunction::Sigmoid);
1559 void OnnxParserImpl::ParseTanh(
const onnx::NodeProto& node)
1561 ParseActivation(node, ActivationFunction::TanH);
1564 void OnnxParserImpl::ParseRelu(
const onnx::NodeProto& node)
1566 ParseActivation(node, ActivationFunction::ReLu);
1569 void OnnxParserImpl::ParseLeakyRelu(
const onnx::NodeProto& node)
1571 ParseActivation(node, ActivationFunction::LeakyReLu);
1574 void OnnxParserImpl::ParseAdd(
const onnx::NodeProto& node)
1584 auto inputs = AddPrepareBroadcast(node.input(0), node.input(1));
1585 auto input0 = *m_TensorsInfo[inputs.first].m_info;
1586 auto input1 = *m_TensorsInfo[inputs.second].m_info;
1587 if (input0.GetNumDimensions() != input1.GetNumDimensions())
1594 unsigned int numDims = input0.GetNumDimensions();
1595 for (
unsigned int i = 0; i < numDims; i++)
1597 unsigned int dim0 = input0.GetShape()[i];
1598 unsigned int dim1 = input1.GetShape()[i];
1599 if (dim0 != dim1 && dim0 != 1 && dim1 != 1)
1602 fmt::format(
"Broadcast is only supported for scalar or 1D tensors in Add node '{}'. "
1603 "Input dimensions should either match or one should be of size 1 and here, "
1606 TensorInfoAsString(*m_TensorsInfo[inputs.first].m_info, inputs.first,
1607 m_TensorsInfo[inputs.first].m_dtype),
1608 TensorInfoAsString(*m_TensorsInfo[inputs.second].m_info, inputs.second,
1609 m_TensorsInfo[inputs.second].m_dtype),
1615 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::Add, node.name().c_str());
1622 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1623 { m_TensorsInfo[inputs.first].m_info->GetShape(),
1624 m_TensorsInfo[inputs.second].m_info->GetShape() });
1628 if(m_TensorsInfo[inputs.first].isConstant()) {
1629 CreateConstantLayer(inputs.first, fmt::format(
"Add:constant_of_{}", node.input(0)));
1631 if(m_TensorsInfo[inputs.second].isConstant()) {
1632 CreateConstantLayer(inputs.second, fmt::format(
"Add:constant_of_{}", node.input(1)));
1634 RegisterInputSlots(layer, {inputs.first, inputs.second});
1637 RegisterOutputSlots(layer, {node.output(0)});
1640 void OnnxParserImpl::ParseAveragePool(
const onnx::NodeProto& node)
1645 uint32_t count_include_pad = 0;
1646 count_include_pad = ReadOptionalNodeUint32Attribute(node,
"count_include_pad");
1647 if(count_include_pad) {
1650 AddPoolingLayer(node, desc);
1653 void OnnxParserImpl::ParseBatchNormalization(
const onnx::NodeProto& node)
1661 for(
int ind = 1; ind < node.input_size(); ++ind)
1663 auto tensor = node.input(ind);
1664 if(! m_TensorsInfo[tensor].isConstant())
1667 fmt::format(
"Input tensor '{}' should be constant in BatchNormalization node '{}' {}",
1674 float epsilon = ReadOptionalNodeFloatAttribute(node,
"epsilon", 1e-5f);
1676 desc.
m_Eps = epsilon;
1678 auto scaleTensor = CreateConstTensor(node.input(1));
1679 auto biasTensor = CreateConstTensor(node.input(2));
1680 auto meanTensor = CreateConstTensor(node.input(3));
1681 auto varTensor = CreateConstTensor(node.input(4));
1688 node.name().c_str());
1695 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1698 RegisterInputSlots(layer, {node.input(0)});
1701 RegisterOutputSlots(layer, {node.output(0)});
1704 void OnnxParserImpl::ParseConcat(
const onnx::NodeProto& node)
1708 uint32_t numConcatView =
static_cast<uint32_t
>(node.input_size());
1709 uint32_t inputRank = m_TensorsInfo[node.input(0)].m_info->GetNumDimensions();
1711 int axisInt = ReadMandatoryNodeIntAttribute(node,
"axis");
1713 unsigned int concatDimInput =
static_cast<unsigned int>(
1714 (
static_cast<int>(inputRank) + axisInt) %
static_cast<int>(inputRank));
1717 concatDescriptor.SetConcatAxis(concatDimInput);
1719 unsigned int mergeDimOrigin = 0;
1721 std::vector<TensorShape> inputShapes;
1722 std::vector<std::string> tensorIds;
1724 for (
unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1726 std::string nodeName = node.input(
static_cast<int>(viewIndex));
1727 auto inputTensorInfo = *m_TensorsInfo[nodeName].m_info;
1728 inputShapes.push_back(inputTensorInfo.GetShape());
1729 tensorIds.push_back(nodeName);
1733 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
1736 IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, node.name().c_str());
1743 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, inputShapes,
1744 m_TensorsInfo[node.input(0)].m_dtype);
1749 RegisterInputSlots(layer, tensorIds);
1752 RegisterOutputSlots(layer, { node.output(0) });
1755 void OnnxParserImpl::ParseConstant(
const onnx::NodeProto& node)
1758 if (!node.attribute(0).has_t())
1760 throw ParseException(fmt::format(
"Value not found for Constant node '{}' {}",
1764 const onnx::TensorProto& onnxTensor = node.attribute(0).t();
1767 m_TensorsInfo[node.output(0)].m_tensor = std::make_unique<const onnx::TensorProto>(onnxTensor);
1768 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(onnxTensor));
1771 if (m_TensorsInfo[node.output(0)].m_dtype == onnx::TensorProto_DataType_FLOAT)
1773 CreateConstantLayer(node.output(0), node.name());
1775 else if (m_TensorsInfo[node.output(0)].m_dtype == onnx::TensorProto_DataType_INT64)
1777 CreateInt64ConstantLayer(node.output(0), node.name());
1781 throw ParseException(fmt::format(
"Data type not support for Constant node '{}' {}",
1787 void OnnxParserImpl::ParseConv(
const onnx::NodeProto& node)
1794 if(m_TensorsInfo[node.input(0)].m_info->GetNumDimensions() != 4)
1797 fmt::format(
"ArmNN only supports 2D convolution and Conv layer '{}' input {} {}",
1799 TensorInfoAsString(*m_TensorsInfo[node.input(0)].m_info, node.input(0),
1800 m_TensorsInfo[node.input(0)].m_dtype),
1804 if(!m_TensorsInfo[node.input(1)].isConstant())
1807 fmt::format(
"Weights '{}' should be constant in Conv layer '{}' {}",
1813 auto inputInfo = *m_TensorsInfo[node.input(0)].m_info;
1818 std::vector<uint32_t> strides = ReadOptionalNodeUint32ListAttribute(node,
"strides");
1830 std::vector<uint32_t> dilations = ReadOptionalNodeUint32ListAttribute(node,
"dilations");
1831 if(!dilations.empty())
1837 std::vector<uint32_t> pads = ReadOptionalNodeUint32ListAttribute(node,
"pads");
1842 std::string paddingString = ReadOptionalNodeStringAttribute(node,
"auto_pad");
1843 if(paddingString !=
"VALID" && paddingString !=
"" && paddingString !=
"NOTSET")
1846 if( paddingString ==
"SAME_LOWER")
1850 else if (paddingString ==
"SAME_UPPER")
1857 fmt::format(
"Invalid auto_pad attribute for node {}. Only SAME_UPPER, SAME_LOWER or VALID "
1858 "supported and found {} {}",
1863 uint32_t inputHeight = inputInfo.
GetShape()[2];
1864 uint32_t inputWidth = inputInfo.
GetShape()[3];
1866 uint32_t weightHeight;
1867 uint32_t weightWidth;
1868 std::vector<uint32_t> kernel_shape = ReadOptionalNodeUint32ListAttribute(node,
"kernel_shape");
1869 if (kernel_shape.empty())
1871 const TensorInfo weightTensorInfo = *m_TensorsInfo[node.input(1)].m_info;
1872 weightHeight = weightTensorInfo.
GetShape()[2];
1873 weightWidth = weightTensorInfo.
GetShape()[3];
1877 weightHeight = kernel_shape[0];
1878 weightWidth = kernel_shape[1];
1880 CalcPadding(inputHeight,
1887 CalcPadding(inputWidth,
1904 uint32_t group = ReadOptionalNodeUint32Attribute(node,
"group", 1);
1907 if (group > inputInfo.
GetShape()[1])
1910 fmt::format(
"Error parsing Convolution node: {}. "
1911 "The 'group'={} parameter cannot be larger than the "
1912 "channel of the input shape={} (in NCHW format). {}",
1918 else if (group == inputInfo.
GetShape()[1])
1922 AddConvLayerWithDepthwiseConv(node, desc);
1927 throw ParseException(fmt::format(
"Error parsing Convolution node: {}. "
1928 "The 'group'={} parameter should be 1 or be equal to the "
1929 "channel of the input shape={} (in NCHW format). {}",
1939 std::vector<std::string> tensorIndexes= {node.input(0), node.input(1)};
1941 auto weightTensor = CreateConstTensor(node.input(1));
1943 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(weightTensor.first);
1947 if (node.input_size() == 3)
1949 if(!m_TensorsInfo[node.input(2)].isConstant())
1951 throw ParseException(fmt::format(
"Bias '{}' should be constant in Conv layer '{}' {}",
1957 auto biasTensor = CreateConstTensor(node.input(2));
1963 tensorIndexes.emplace_back(node.input(2));
1971 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1972 { m_TensorsInfo[node.input(0)].m_info->GetShape(),
1973 m_TensorsInfo[node.input(1)].m_info->GetShape() });
1978 RegisterInputSlots(layer, tensorIndexes);
1981 RegisterOutputSlots(layer, {node.output(0)});
1984 void OnnxParserImpl::ParseFlatten(
const onnx::NodeProto& node)
1990 m_TensorsInfo[node.input(0)].m_dtype,
1991 onnx::TensorProto::FLOAT);
1993 int64_t axis = ReadOptionalNodeInt64Attribute(node,
"axis", 1);
1994 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2005 throw ParseException(fmt::format(
"Axis '{}' invalid. Tensor has '{}' dimensions in FlattenLayer '{}'",
2015 for (i = 0; i < axis; i++){
2016 dimension1 *= inputShape[i];
2021 dimension2 *= inputShape[i];
2026 auto outInfo = ComputeReshapeInfo(outputShape, inputShape, node.output(0));
2027 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
2028 CreateReshapeLayer(node.input(0), node.output(0), node.name());
2031 void OnnxParserImpl::ParseGather(
const onnx::NodeProto& node)
2037 gatherDescriptor.
m_Axis =
static_cast<int>(ReadOptionalNodeInt64Attribute(node,
"axis", 0));
2039 IConnectableLayer* layer = m_Network->AddGatherLayer(gatherDescriptor, node.name().c_str());
2046 const TensorShape& inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2047 const TensorShape& indicesShape = m_TensorsInfo[node.input(1)].m_info->GetShape();
2048 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, { inputShape, indicesShape },
2049 m_TensorsInfo[node.input(0)].m_dtype);
2053 RegisterInputSlots(layer, { node.input(0), node.input(1) });
2056 RegisterOutputSlots(layer, { node.output(0) });
2059 void OnnxParserImpl::ParseGemm(
const onnx::NodeProto& node)
2064 int transA =
static_cast<int>(ReadOptionalNodeUint32Attribute(node,
"transA", 0));
2065 int transB =
static_cast<int>(ReadOptionalNodeUint32Attribute(node,
"transB", 0));
2066 float alpha = ReadOptionalNodeFloatAttribute(node,
"alpha", 1.0);
2067 float beta = ReadOptionalNodeFloatAttribute(node,
"beta", 1.0);
2068 bool biasEnabled = node.input_size() == 3;
2070 TensorShape input0Shape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2071 TensorShape input1Shape = m_TensorsInfo[node.input(1)].m_info->GetShape();
2081 layer = m_Network->AddFullyConnectedLayer(fullyConnectedDescriptor, node.name().c_str());
2091 std::string transAName =
"transpose_" + node.input(0);
2094 IConnectableLayer* transALayer = m_Network->AddTransposeLayer(transposeADescriptor, transAName.c_str());
2101 auto transAInfo = ComputeOutputInfo({ transAName }, transALayer, { input0Shape });
2105 RegisterInputSlot(transALayer, node.input(0), 0);
2106 input0Shape = transAInfo[0].GetShape();
2110 RegisterInputSlot(layer, node.input(0), 0);
2114 if(m_TensorsInfo[node.input(1)].isConstant())
2116 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(CreateConstTensor(node.input(1)).first);
2117 TensorInfo weightInfo = *m_TensorsInfo[node.input(1)].m_info;
2124 std::string activationName =
"activation_" + node.input(1);
2126 activationDescriptor.
m_A = alpha;
2127 activationDescriptor.
m_Function = ActivationFunction::Linear;
2128 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
2135 auto actInfo = ComputeOutputInfo({ activationName }, actLayer, { weightInfo.
GetShape() });
2139 input1Shape = actInfo[0].GetShape();
2144 input1Shape = weightInfo.
GetShape();
2152 std::string activationName =
"activation_" + node.input(1);
2154 activationDescriptor.
m_A = alpha;
2155 activationDescriptor.
m_Function = ActivationFunction::Linear;
2156 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
2163 auto actInfo = ComputeOutputInfo({ activationName }, actLayer, { input1Shape });
2166 RegisterInputSlot(actLayer, node.input(1), 0);
2167 input1Shape = actInfo[0].GetShape();
2171 RegisterInputSlot(layer, node.input(1), 1);
2175 if(biasEnabled && m_TensorsInfo[node.input(2)].isConstant())
2178 IConnectableLayer* biasLayer = m_Network->AddConstantLayer(CreateConstTensor(node.input(2)).first);
2179 TensorInfo biasInfo = *m_TensorsInfo[node.input(2)].m_info;
2186 std::string activationName =
"activation_" + node.input(2);
2188 activationDescriptor.
m_A = beta;
2189 activationDescriptor.
m_Function = ActivationFunction::Linear;
2190 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
2197 auto actInfo = ComputeOutputInfo({ activationName }, actLayer, { biasInfo.
GetShape() });
2207 else if (biasEnabled)
2210 if (m_TensorsInfo[node.input(2)].m_info->GetNumDimensions() != 1)
2212 throw ParseException(fmt::format(
"The parser supports constant or non-constant with 1 dimension for "
2213 "Input C of Gemm. Input '{}' in '{}' is not supported '{}'",
2221 std::string activationName =
"activation_" + node.input(2);
2223 activationDescriptor.
m_A = beta;
2224 activationDescriptor.
m_Function = ActivationFunction::Linear;
2225 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
2232 auto actInfo = ComputeOutputInfo({ activationName },
2234 { m_TensorsInfo[node.input(2)].m_info->GetShape() });
2237 RegisterInputSlot(actLayer, node.input(2), 0);
2241 RegisterInputSlot(layer, node.input(2), 2);
2246 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
2247 { input0Shape, input1Shape });
2250 RegisterOutputSlots(layer, {node.output(0)});
2253 void OnnxParserImpl::ParseGlobalAveragePool(
const onnx::NodeProto& node)
2259 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2263 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, node.name().c_str());
2270 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {inputShape});
2275 RegisterInputSlots(layer, {node.input(0)});
2278 RegisterOutputSlots(layer, {node.output(0)});
2281 void OnnxParserImpl::ParseMaxPool(
const onnx::NodeProto& node)
2286 AddPoolingLayer(node, desc);
2289 void OnnxParserImpl::ParseShape(
const onnx::NodeProto& node)
2301 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2302 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {inputShape}, onnx::TensorProto::INT64);
2306 RegisterInputSlots(layer, {node.input(0)});
2309 RegisterOutputSlots(layer, {node.output(0)});
2312 void OnnxParserImpl::ParseReshape(
const onnx::NodeProto& node)
2318 m_TensorsInfo[node.input(0)].m_dtype,
2319 onnx::TensorProto::FLOAT);
2321 m_TensorsInfo[node.input(1)].m_dtype,
2322 onnx::TensorProto::INT64);
2324 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2326 std::vector<unsigned int> targetShape;
2327 if(m_TensorsInfo[node.input(1)].isConstant())
2329 unsigned int dims =
static_cast<unsigned int>(m_TensorsInfo[node.input(1)].m_tensor->int64_data_size());
2330 targetShape.reserve(dims);
2332 for(uint i = 0; i < dims; i++)
2334 int val =
CHECKED_INT32(m_TensorsInfo[node.input(1)].m_tensor->int64_data(
static_cast<int>(i)));
2335 targetShape[i]=
static_cast<unsigned int>(val);
2341 unsigned int dims = m_TensorsInfo[node.input(1)].m_info->GetNumDimensions();
2342 TensorShape shapes = m_TensorsInfo[node.input(1)].m_info->GetShape();
2343 if (dims != 1 || shapes[0] > 2)
2345 throw ParseException(fmt::format(
"Invalid input shape '{}' in Reshape layer '{}' {}",
2351 unsigned int numInputElements = m_TensorsInfo[node.input(0)].m_info->GetNumElements();
2354 targetShape = { numInputElements };
2356 else if (shapes[0] == 2)
2358 targetShape = { inputShape[0] , numInputElements / inputShape[0] };
2362 if(m_TensorsInfo[node.input(0)].isConstant())
2365 if(m_TensorsInfo.count(node.output(0)) == 0)
2367 m_TensorsInfo[node.output(0)] = OnnxTensor();
2369 m_TensorsInfo[node.output(0)].m_tensor =
2370 std::make_unique<onnx::TensorProto>(*m_TensorsInfo[node.input(0)].m_tensor);
2374 if(m_TensorsInfo.count(node.output(0)) == 0 || m_TensorsInfo[node.output(0)].m_info ==
nullptr)
2376 auto outInfo = ComputeReshapeInfo(
2377 TensorShape(
static_cast<unsigned int>(targetShape.size()), targetShape.data()),
2378 inputShape, node.output(0));
2379 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
2382 CreateReshapeLayer(node.input(0), node.output(0), node.name());
2386 void OnnxParserImpl::ParseUnsqueeze(
const onnx::NodeProto& node)
2391 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2392 std::vector<uint32_t> dims;
2393 if (node.input_size() == 1 && node.attribute_size() > 0)
2395 dims = ReadMandatoryNodeUint32ListAttribute(node,
"axes");
2400 m_TensorsInfo[node.input(1)].m_dtype,
2401 onnx::TensorProto::INT64);
2403 auto int64Axes = m_TensorsInfo[node.input(1)].m_tensor->int64_data().data();
2404 uint numDim = armnn::numeric_cast<uint>(m_TensorsInfo[node.input(1)].m_tensor->int64_data_size());
2406 for(uint i = 0; i < numDim; i++)
2409 dims.push_back(uint32Value);
2414 std::sort(dims.begin(), dims.end());
2416 std::vector<unsigned int> targetShape;
2422 targetShape.push_back(inputShape[i]);
2426 for(uint i = 0; i < dims.size(); i++)
2428 targetShape.insert(targetShape.begin() + armnn::numeric_cast<int>(dims[i]), 1);
2431 auto outInfo = ComputeReshapeInfo(
TensorShape(
static_cast<unsigned int>(targetShape.size()), targetShape.data()),
2432 inputShape, node.output(0), m_TensorsInfo[node.input(0)].m_info->GetDataType());
2433 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
2434 m_TensorsInfo[node.output(0)].m_dtype = m_TensorsInfo[node.input(0)].m_dtype;
2436 CreateReshapeLayer(node.input(0), node.output(0), node.name());
2439 void OnnxParserImpl::PrependForBroadcast(
const std::string& outputName,
2440 const std::string& input0,
2441 const std::string& input1)
2446 TensorShape input0Shape = m_TensorsInfo[input0].m_info->GetShape();
2447 TensorShape input1Shape = m_TensorsInfo[input1].m_info->GetShape();
2450 std::vector<uint32_t> newShape;
2453 newShape.push_back(1);
2458 newShape.push_back(input0Shape[dim]);
2460 outputTensorInfo.
SetShape(
TensorShape(
static_cast<unsigned int>(newShape.size()), newShape.data()));
2463 m_TensorsInfo[outputName] = OnnxTensor();
2464 m_TensorsInfo[outputName].m_info = std::make_unique<TensorInfo>(outputTensorInfo);
2467 if( ! m_TensorsInfo[input0].isConstant())
2469 CreateReshapeLayer(input0, outputName, fmt::format(
"Add:reshapeOf{}", input0));
2473 m_TensorsInfo[outputName].m_tensor = std::make_unique<onnx::TensorProto>(*m_TensorsInfo[input0].m_tensor);
2478 void OnnxParserImpl::SetupInputLayers()
2481 for(
int inputIndex = 0; inputIndex < m_Graph->input_size(); ++inputIndex)
2483 auto input = m_Graph->input(inputIndex);
2484 if (!m_TensorsInfo[input.name()].isConstant())
2488 TensorInfo tensorInfo = *m_TensorsInfo[input.name()].m_info;
2491 if (m_InputShapes.find(input.name()) == m_InputShapes.end())
2493 throw ParseException(fmt::format(
"The parser does not support dynamic tensor, "
2494 "please specify input shape for {}. {}",
2500 tensorInfo.
SetShape(m_InputShapes[input.name()]);
2501 m_TensorsInfo[input.name()].m_info = std::make_unique<TensorInfo>(tensorInfo);
2507 m_InputInfos[input.name()] = tensorInfo;
2509 RegisterOutputSlots(layer,{ input.name() });
2514 void OnnxParserImpl::SetupOutputLayers()
2516 if(m_Graph->output_size() == 0)
2521 for(
int outputIndex = 0; outputIndex < m_Graph->output_size(); ++outputIndex)
2525 m_Graph->output(outputIndex).name().c_str());
2527 RegisterInputSlots(layer, { m_Graph->output(outputIndex).name() });
2532 const std::string& tensorId,
2533 unsigned int slotIndex)
2537 auto it = m_TensorConnections.find(tensorId);
2539 if (it == m_TensorConnections.end())
2542 m_TensorConnections[tensorId] = TensorSlots();
2544 m_TensorConnections[tensorId].inputSlots.push_back(slot);
2547 void OnnxParserImpl::RegisterInputSlots(
IConnectableLayer* layer,
const std::vector<std::string>& tensorIds)
2557 fmt::format(
"The number of tensor inputs ({}) does not match the number expected ({}) {}",
2563 for (
unsigned int slotIndex = 0; slotIndex < layer->
GetNumInputSlots(); ++slotIndex)
2565 std::string tensorId = tensorIds[slotIndex];
2568 auto it = m_TensorConnections.find(tensorId);
2570 if (it == m_TensorConnections.end())
2573 m_TensorConnections[tensorId] = TensorSlots();
2575 m_TensorConnections[tensorId].inputSlots.push_back(slot);
2579 void OnnxParserImpl::RegisterOutputSlots(
IConnectableLayer* layer,
const std::vector<std::string>& tensorIds)
2589 fmt::format(
"The number of tensor outputs ({}) does not match the number expected ({}) {} ",
2595 for (
unsigned int slotIndex = 0; slotIndex < layer->
GetNumOutputSlots(); ++slotIndex)
2597 std::string tensorId = tensorIds[slotIndex];
2600 auto it = m_TensorConnections.find(tensorId);
2602 if (it == m_TensorConnections.end())
2605 m_TensorConnections[tensorId] = TensorSlots();
2608 TensorSlots& tensorSlots = m_TensorConnections[tensorId];
2611 if (tensorSlots.outputSlot !=
nullptr)
2613 throw ParseException(fmt::format(
"Another layer has already registered itself as the producer of "
2618 tensorSlots.outputSlot = slot;
2625 for(
int i = 0; i < m_Graph->input_size(); ++i)
2627 auto input = m_Graph->input(i);
2628 if(input.name() == name)
2630 auto it = m_InputInfos.find(name);
2632 if (it != m_InputInfos.end())
2644 for(
int i = 0; i < m_Graph->output_size(); ++i)
2646 auto output = m_Graph->output(i);
2647 if(output.name() == name)
2649 auto it = m_OutputInfos.find(name);
2651 if (it != m_OutputInfos.end())
2663 if(model ==
nullptr) {
2668 std::vector<std::string> inputNames;
2669 std::map<std::string, bool> isConstant;
2670 for(
auto tensor : model->graph().initializer())
2672 isConstant[tensor.name()] =
true;
2674 for(
auto input : model->graph().input())
2676 auto it = isConstant.find(input.name());
2677 if(it == isConstant.end())
2679 inputNames.push_back(input.name());
2687 if(model ==
nullptr) {
2692 std::vector<std::string> outputNames;
2693 for(
auto output : model->graph().output())
2695 outputNames.push_back(output.name());