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 return pOnnxParserImpl->CreateNetworkFromTextFile(graphFile);
60 return pOnnxParserImpl->CreateNetworkFromString(protoText);
64 const char* graphFile,
65 const std::map<std::string, armnn::TensorShape>& inputShapes)
67 return pOnnxParserImpl->CreateNetworkFromBinaryFile(graphFile, inputShapes);
71 const std::map<std::string, armnn::TensorShape>& inputShapes)
73 return pOnnxParserImpl->CreateNetworkFromTextFile(graphFile, inputShapes);
77 const std::map<std::string, armnn::TensorShape>& inputShapes)
79 return pOnnxParserImpl->CreateNetworkFromString(protoText, inputShapes);
82 BindingPointInfo IOnnxParser::GetNetworkInputBindingInfo(
const std::string& name)
const 84 return pOnnxParserImpl->GetNetworkInputBindingInfo(name);
87 BindingPointInfo IOnnxParser::GetNetworkOutputBindingInfo(
const std::string& name)
const 89 return pOnnxParserImpl->GetNetworkOutputBindingInfo(name);
94 void CheckValidDataType(std::initializer_list<onnx::TensorProto::DataType> validInputTypes,
96 const char* validExpr,
98 std::string tensorName,
101 bool isValid = std::any_of(validInputTypes.begin(),
102 validInputTypes.end(),
107 fmt::format(
"Datatype {} is not valid for tensor '{}' of node '{}', not in {{{}}}. {}",
108 onnx::TensorProto::DataType_Name(actualValue),
116 #define CHECK_VALID_DATATYPE(NODE, TENSOR, ACTUAL, ...) \ 117 CheckValidDataType({__VA_ARGS__}, ACTUAL, #__VA_ARGS__, NODE, TENSOR, CHECK_LOCATION()) 119 using StrTypeListPair = std::pair<const char*, std::initializer_list<onnx::TensorProto::DataType>>;
120 #define STR_LIST(...) StrTypeListPair(#__VA_ARGS__, {__VA_ARGS__}) 122 template <
typename Callable>
123 void ReadMandatoryNodeAttributeImpl(
const onnx::NodeProto& node,
124 const std::string& attribName,
125 onnx::AttributeProto::AttributeType expectedType,
128 auto attribs = node.attribute();
130 while (attriNum < node.attribute_size())
132 if (attribs.Get(attriNum).name() == attribName)
134 if (attribs.Get(attriNum).type() == expectedType)
136 callable(attribs.Get(attriNum));
140 throw ParseException(fmt::format(
"Attribute {} of node {} expected to have {} as " 141 "onnx::AttributeProto::AttributeType, but found {} instead {}",
144 onnx::AttributeProto::AttributeType_Name(expectedType),
145 onnx::AttributeProto::AttributeType_Name(attribs.Get(attriNum).type()),
152 if (attriNum == node.attribute_size())
154 throw ParseException(fmt::format(
"Could not find required attribute {} in node {} {}",
159 template <
typename Callable>
160 void ReadOptionalNodeAttributeImpl(
const onnx::NodeProto& node,
161 const std::string& attribName,
162 onnx::AttributeProto::AttributeType expectedType,
165 auto attribs = node.attribute();
166 for (
int attriNum = 0; attriNum < node.attribute_size(); ++attriNum)
168 if (attribs.Get(attriNum).name() == attribName)
170 if (attribs.Get(attriNum).type() == expectedType)
172 callable(attribs.Get(attriNum));
177 fmt::format(
"Attribute {} of node {} expected to have {} as onnx::AttributeProto::AttributeType, " 178 "but found {} instead {}",
181 onnx::AttributeProto::AttributeType_Name(expectedType),
182 onnx::AttributeProto::AttributeType_Name(attribs.Get(attriNum).type()),
189 int ReadMandatoryNodeIntAttribute(
const onnx::NodeProto& node,
190 const std::string& name)
193 ReadMandatoryNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
194 [&attribValue](
const onnx::AttributeProto& attrValue)
201 int64_t ReadOptionalNodeInt64Attribute(
const onnx::NodeProto& node,
202 const std::string& name,
203 const int64_t defaultValue = 0)
205 int64_t attribValue = defaultValue;
206 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
207 [&attribValue](
const onnx::AttributeProto& attrValue)
209 attribValue = attrValue.i();
214 std::vector<uint32_t> ReadMandatoryNodeUint32ListAttribute(
const onnx::NodeProto& node,
215 const std::string& name)
217 std::vector<uint32_t> attriList;
218 ReadMandatoryNodeAttributeImpl(node, name, onnx::AttributeProto::INTS,
219 [&attriList](
const onnx::AttributeProto& attrValue)
221 for (
int attriNum = 0; attriNum < attrValue.ints_size(); ++attriNum)
229 uint32_t ReadOptionalNodeUint32Attribute(
const onnx::NodeProto& node,
230 const std::string& name,
231 const uint32_t defaultVal = 0u)
233 uint32_t attribValue = defaultVal;
234 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
235 [&attribValue](
const onnx::AttributeProto& attrValue)
242 std::vector<uint32_t> ReadOptionalNodeUint32ListAttribute(
const onnx::NodeProto& node,
243 const std::string& name)
245 std::vector<uint32_t> attriList;
246 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INTS,
247 [&attriList](
const onnx::AttributeProto& attrValue)
249 for (
int attriNum = 0; attriNum < attrValue.ints_size(); ++attriNum)
258 float ReadOptionalNodeFloatAttribute(
const onnx::NodeProto& node,
259 const std::string& name,
260 const float defaultValue = 0.0f)
262 float attribValue = defaultValue;
263 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::FLOAT,
264 [&attribValue](
const onnx::AttributeProto& attrValue)
266 attribValue = attrValue.f();
271 std::string ReadOptionalNodeStringAttribute(
const onnx::NodeProto& node,
const std::string& name)
273 std::string attribValue =
"";
274 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::STRING,
275 [&attribValue](
const onnx::AttributeProto& attrValue)
277 attribValue = attrValue.s();
287 case onnx::TensorProto::FLOAT:
289 type = DataType::Float32;
292 case onnx::TensorProto::INT32:
293 case onnx::TensorProto::INT64:
295 type = DataType::Signed32;
301 fmt::format(
"'{}' is not a currently supported datatype for tensor {}." 302 " Supported dataTypes are FLOAT, INT32 and INT64. {}",
303 onnx::TensorProto::DataType_Name(static_cast<onnx::TensorProto::DataType>(data_type)),
316 if(std::find(shape.begin(), shape.end(), 0) != shape.end())
326 const onnx::TensorShapeProto onnxShape = info.type().tensor_type().shape();
327 std::vector<unsigned int> shapeDims;
328 for (
int i = 0; i < onnxShape.dim_size(); ++i)
333 return ToTensorInfo(info.name(), shapeDims, info.type().tensor_type().elem_type());
338 std::vector<unsigned int> shapeDims;
340 for (
auto dim: tensor.dims())
345 return ToTensorInfo(tensor.name(), shapeDims, tensor.data_type());
348 std::string TensorInfoAsString(
const TensorInfo& info,
349 const std::string& name,
353 std::stringstream ss;
354 ss <<
"tensor '" << name <<
"' contains " 355 << onnx::TensorProto::DataType_Name(type)
356 <<
" and has shape [";
360 ss << shape[i] <<
", ";
366 void CalcPadding(uint32_t inputSize,
370 uint32_t* paddingFront,
371 uint32_t* paddingBack,
374 uint32_t outputSize = (inputSize + stride - 1) / stride;
375 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
376 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
377 *paddingFront = (temp - inputSize) / 2;
378 *paddingBack = *paddingFront;
379 if((temp - inputSize) % 2 == 1)
394 const std::string& outName,
395 DataType dataType = DataType::Float32)
397 std::vector<int> targetDims;
403 targetDims.push_back(static_cast<int>(inShape[static_cast<uint>(i)]));
407 targetDims.push_back(val);
411 std::vector<unsigned int> outDims(targetDims.begin(), targetDims.end());
412 const auto stretchDim = std::find(targetDims.begin(), targetDims.end(), -1);
413 if (stretchDim != targetDims.end())
415 if (std::find(std::next(stretchDim), targetDims.end(), -1) != targetDims.end())
417 std::stringstream ss;
419 for(uint i = 0; i < targetDims.size() - 1; ++i)
421 ss << targetDims[i] <<
", ";
423 ss << targetDims[targetDims.size() - 1] <<
" ]";
426 fmt::format(
"Error during creation of reshaped tensor '{}'. At most one component of shape can be " 427 " -1 and here, shape is {} {}",
433 auto targetNumElements =
armnn::numeric_cast<
unsigned int>(std::accumulate(targetDims.begin(), targetDims.end(),
434 -1, std::multiplies<int32_t>()));
435 auto stretchIndex =
static_cast<size_t>(std::distance(targetDims.begin(), stretchDim));
436 outDims[stretchIndex] = inShape.
GetNumElements() / targetNumElements;
444 const std::map<std::string, OnnxParserImpl::OperationParsingFunction> OnnxParserImpl::m_ParserFunctions = {
445 {
"BatchNormalization", &OnnxParserImpl::ParseBatchNormalization},
446 {
"GlobalAveragePool", &OnnxParserImpl::ParseGlobalAveragePool},
447 {
"AveragePool", &OnnxParserImpl::ParseAveragePool },
448 {
"Clip", &OnnxParserImpl::ParseClip },
449 {
"Constant", &OnnxParserImpl::ParseConstant },
450 {
"MaxPool", &OnnxParserImpl::ParseMaxPool },
451 {
"Reshape", &OnnxParserImpl::ParseReshape },
452 {
"Sigmoid", &OnnxParserImpl::ParseSigmoid },
453 {
"Tanh", &OnnxParserImpl::ParseTanh },
454 {
"Relu", &OnnxParserImpl::ParseRelu },
455 {
"LeakyRelu", &OnnxParserImpl::ParseLeakyRelu },
456 {
"Conv", &OnnxParserImpl::ParseConv },
457 {
"Add", &OnnxParserImpl::ParseAdd },
458 {
"Flatten", &OnnxParserImpl::ParseFlatten },
459 {
"Shape", &OnnxParserImpl::ParseShape },
460 {
"Gather", &OnnxParserImpl::ParseGather },
461 {
"Unsqueeze", &OnnxParserImpl::ParseUnsqueeze },
462 {
"Concat", &OnnxParserImpl::ParseConcat },
463 {
"Gemm", &OnnxParserImpl::ParseGemm }
466 template<
typename TypePair,
typename Location>
467 void OnnxParserImpl::ValidateInputs(
const onnx::NodeProto& node,
468 TypePair validInputs,
469 const Location& location)
471 for(
auto input : node.input())
473 CheckValidDataType(validInputs.second,
474 m_TensorsInfo[input].m_dtype,
482 #define VALID_INPUTS(NODE, VALID_INPUTS) \ 483 OnnxParserImpl::ValidateInputs(NODE, \ 487 std::vector<TensorInfo> OnnxParserImpl::ComputeOutputInfo(std::vector<std::string> outNames,
489 std::vector<TensorShape> inputShapes,
493 bool needCompute = std::any_of(outNames.begin(),
495 [
this](std::string name)
497 return (m_TensorsInfo.count(name) == 0 || m_TensorsInfo[name].m_info ==
nullptr 498 || m_TensorsInfo[name].m_info->GetShape().GetDimensionality() ==
499 Dimensionality::NotSpecified);
501 std::vector<TensorInfo> outInfo;
503 std::vector<TensorShape> inferredShapes;
504 DataType armnnType = DataType::Float32;
509 case onnx::TensorProto::FLOAT: {
510 armnnType = DataType::Float32;
513 case onnx::TensorProto::INT32:
514 case onnx::TensorProto::INT64: {
515 armnnType = DataType::Signed32;
520 fmt::format(
"'{}' is not a currently supported datatype for {}." 521 " Supported dataTypes are FLOAT, INT32 and INT64. {}",
522 onnx::TensorProto::DataType_Name(static_cast<onnx::TensorProto::DataType>(dataType)),
528 for (uint i = 0; i < outNames.size(); ++i)
532 m_TensorsInfo[outNames[i]] = OnnxTensor();
533 m_TensorsInfo[outNames[i]].m_info = std::make_unique<TensorInfo>(
535 m_TensorsInfo[outNames[i]].m_dtype = dataType;
537 outInfo.push_back(*m_TensorsInfo[outNames[i]].m_info);
542 OnnxParserImpl::OnnxParserImpl()
543 : m_Network(nullptr, nullptr)
547 void OnnxParserImpl::ResetParser()
551 m_InputInfos.clear();
552 m_OutputInfos.clear();
555 void OnnxParserImpl::Cleanup()
557 m_TensorConnections.clear();
558 m_TensorsInfo.clear();
559 m_OutputsMap.clear();
560 m_OutputsFusedAndUsed.clear();
561 m_InputShapes.clear();
565 std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
570 ARMNN_ASSERT_MSG(bufferPtr !=
nullptr, fmt::format(
"Buffer for permutation is null").c_str());
578 reinterpret_cast<const T*
>(bufferPtr), data.get(),
sizeof(T));
582 ::memcpy(data.get(), bufferPtr, tensorInfo.
GetNumBytes());
585 return std::make_pair(
ConstTensor(tensorInfo, data.get()), std::move(data));
588 std::pair<ConstTensor, std::unique_ptr<float[]>>
589 OnnxParserImpl::CreateConstTensor(
const std::string name,
592 TensorInfo tensorInfo = *m_TensorsInfo[name].m_info;
593 onnx::TensorProto onnxTensor = *m_TensorsInfo[name].m_tensor;
605 throw ParseException(fmt::format(
"No tensor data found for Const tensor '{}' {}",
610 auto srcData = onnxTensor.float_data().data();
612 if (!onnxTensor.has_raw_data())
614 if(tensorInfo.
GetNumElements() !=
static_cast<uint
>(onnxTensor.float_data_size()))
617 fmt::format(
"The number of data provided ({}) does not match the tensor '{}' number of " 619 onnxTensor.float_data_size(),
624 return CreateConstTensorImpl<float>(srcData, tensorInfo, permutationVector);
628 return CreateConstTensorImpl<float>(
reinterpret_cast<const float*
>(onnxTensor.raw_data().c_str()),
634 std::pair<ConstTensor, std::unique_ptr<int32_t[]>>
635 OnnxParserImpl::CreateInt64ConstTensor(
const std::string name,
638 TensorInfo tensorInfo = *m_TensorsInfo[name].m_info;
639 onnx::TensorProto onnxTensor = *m_TensorsInfo[name].m_tensor;
649 if (numElements == 0)
651 throw ParseException(fmt::format(
"No tensor data found for Const tensor '{}' {}",
657 if (!onnxTensor.has_raw_data())
659 auto srcData = onnxTensor.int64_data().data();
660 if(numElements != static_cast<uint>(onnxTensor.int64_data_size()))
663 fmt::format(
"The number of data provided ({}) does not match the tensor '{}' number of " 665 onnxTensor.int64_data_size(),
671 std::vector<int32_t> int32Data;
672 for(uint i = 0; i < numElements; i++)
675 int32Data.push_back(int32Value);
678 return CreateConstTensorImpl<int32_t>(int32Data.data(), tensorInfo, permutationVector);
682 auto srcData =
reinterpret_cast<const int64_t*
>(onnxTensor.raw_data().c_str());
683 std::vector<int32_t> int32Data;
684 for(uint i = 0; i < numElements; i++)
687 int32Data.push_back(int32Value);
689 return CreateConstTensorImpl<int32_t>(int32Data.data(), tensorInfo, permutationVector);
695 FILE* fd = fopen(graphFile,
"r");
703 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
704 using google::protobuf::io::FileInputStream;
705 std::unique_ptr<FileInputStream> input = std::make_unique<FileInputStream>(fileno(fd));
706 bool success = google::protobuf::TextFormat::Parse(input.get(), modelProto.get());
711 std::stringstream
error;
712 error <<
"Failed to parse graph file";
722 return CreateNetworkFromModel(*modelProto);
726 const std::map<std::string, armnn::TensorShape>& inputShapes)
729 m_InputShapes = inputShapes;
731 return CreateNetworkFromModel(*modelProto);
736 FILE* fd = fopen(graphFile,
"rb");
744 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
746 google::protobuf::io::FileInputStream inStream(fileno(fd));
747 google::protobuf::io::CodedInputStream codedStream(&inStream);
748 codedStream.SetTotalBytesLimit(INT_MAX);
749 bool success = modelProto.get()->ParseFromCodedStream(&codedStream);
754 std::stringstream
error;
755 error <<
"Failed to parse graph file";
766 return CreateNetworkFromModel(*modelProto);
770 const std::map<std::string, armnn::TensorShape>& inputShapes)
773 m_InputShapes = inputShapes;
775 return CreateNetworkFromModel(*modelProto);
786 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
787 bool success = google::protobuf::TextFormat::ParseFromString(protoText, modelProto.get());
790 std::stringstream
error;
791 error <<
"Failed to parse graph file";
801 return CreateNetworkFromModel(*modelProto);
805 const std::map<std::string, armnn::TensorShape>& inputShapes)
808 m_InputShapes = inputShapes;
810 return CreateNetworkFromModel(*modelProto);
813 INetworkPtr OnnxParserImpl::CreateNetworkFromModel(onnx::ModelProto& model)
815 m_Network = INetwork::Create();
818 m_Graph = std::make_unique<onnx::GraphProto>(*model.mutable_graph());
827 return std::move(m_Network);
830 void OnnxParserImpl::LoadGraph()
835 SetupInfo(m_Graph->mutable_output());
836 SetupInfo(m_Graph->mutable_input());
837 SetupInfo(m_Graph->mutable_value_info());
839 for (
auto tensor : m_Graph->initializer())
841 m_TensorsInfo[tensor.name()].m_tensor = std::make_unique<const onnx::TensorProto>(tensor);
842 m_TensorsInfo[tensor.name()].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(tensor));
843 m_TensorsInfo[tensor.name()].m_dtype =
851 DetectFullyConnected();
854 for(
size_t nodeIndex = 0; nodeIndex < static_cast<size_t>(m_Graph->node_size()); nodeIndex++)
856 auto node = m_Graph->node(static_cast<int>(nodeIndex));
857 const std::string& operation = node.op_type();
860 if (operation ==
"MatMul" )
862 if(m_OutputsFusedAndUsed[nodeIndex].inputForNodes != m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.size())
865 AddFullyConnected(node);
868 else if (!(m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.empty()) && operation ==
"Add")
870 int matmulIndex =
static_cast<int> (m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes[0]);
871 AddFullyConnected(m_Graph->node(matmulIndex), &node);
873 else if (m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.empty())
875 auto it = m_ParserFunctions.find(operation);
876 if (it != m_ParserFunctions.end())
878 auto func = it->second;
883 throw ParseException(fmt::format(
"Unsupported operation {} for node '{}' {}",
892 for (
const auto& tensorCon : m_TensorConnections)
894 if (tensorCon.second.outputSlot !=
nullptr)
896 for (
size_t inputSlotIdx = 0; inputSlotIdx < tensorCon.second.inputSlots.size(); ++inputSlotIdx)
898 tensorCon.second.outputSlot->Connect(*(tensorCon.second.inputSlots[inputSlotIdx]));
904 for(
int outputIndex = 0; outputIndex < m_Graph->output_size(); ++outputIndex)
906 auto output = m_Graph->output(outputIndex);
907 m_OutputInfos[output.name()] = *m_TensorsInfo[output.name()].m_info;
911 void OnnxParserImpl::SetupInfo(
const google::protobuf::RepeatedPtrField<onnx::ValueInfoProto >* list)
913 for (
auto tensor : *list)
915 m_TensorsInfo[tensor.name()] = OnnxTensor();
916 m_TensorsInfo[tensor.name()].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(tensor));
917 m_TensorsInfo[tensor.name()].m_dtype =
922 void OnnxParserImpl::DetectFullyConnected()
924 m_OutputsFusedAndUsed = std::vector<UsageSummary> (
static_cast<size_t>(m_Graph->node_size()), UsageSummary());
925 auto matmulAndConstant = [&](
const std::string& constInput,
926 const std::string& matmulInput,
929 auto matmulIt = m_OutputsMap.find(matmulInput);
930 if(matmulIt != m_OutputsMap.end() && matmulIt->second.first->op_type() ==
"MatMul" 931 && m_TensorsInfo[constInput].isConstant())
933 nodeIndex = matmulIt->second.second;
939 for(
int nodeIndex = 0; nodeIndex < m_Graph->node_size(); nodeIndex++)
941 const onnx::NodeProto* node = &m_Graph->node(nodeIndex);
942 for (
const std::string& output : node->output())
944 m_OutputsMap[output] = std::make_pair(node, nodeIndex);
947 for (
const std::string& input : node->input())
949 auto matmulIt = m_OutputsMap.find(input);
950 if(matmulIt != m_OutputsMap.end()){
951 ++m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIt->second.second)].inputForNodes;
955 if (node->op_type() ==
"Add")
958 if (matmulAndConstant(node->input(0), node->input(1), matmulIndex) ||
959 matmulAndConstant(node->input(1), node->input(0), matmulIndex))
962 m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIndex)].fusedWithNodes
963 .push_back(static_cast<size_t>(nodeIndex));
965 m_OutputsFusedAndUsed[
static_cast<size_t>(nodeIndex)].fusedWithNodes
966 .push_back(static_cast<size_t>(matmulIndex));
971 for (
auto output: m_Graph->output()) {
972 auto matmulIt = m_OutputsMap.find(output.name());
973 if(matmulIt != m_OutputsMap.end()){
974 ++m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIt->second.second)].inputForNodes;
979 template<
typename Location>
980 void OnnxParserImpl::GetInputAndParam(
const onnx::NodeProto& node,
981 std::string* inputName,
982 std::string* constName,
983 const Location& location)
986 if (m_TensorsInfo[node.input(0)].isConstant())
990 else if (m_TensorsInfo[node.input(1)].isConstant())
996 throw ParseException(fmt::format(
"One of the input tensors ('{}' or '{}') should be constant in node '{}' {}",
1000 location.AsString()));
1004 *constName = node.input(cstIndex);
1008 *inputName = node.input(!cstIndex);
1012 template<
typename Location>
1013 void OnnxParserImpl::To1DTensor(
const std::string& name,
const Location& location)
1015 TensorShape shape = m_TensorsInfo[name].m_info->GetShape();
1016 std::vector<uint32_t> newShape;
1022 fmt::format(
"Only tensors with shape [1, ..., 1, X] can be converted to 1D and {} {}",
1023 TensorInfoAsString(*m_TensorsInfo[name].m_info, name, m_TensorsInfo[name].m_dtype),
1024 location.AsString()));
1029 m_TensorsInfo[name].m_info->SetShape(
TensorShape(static_cast<unsigned int>(newShape.size()), newShape.data()));
1032 void OnnxParserImpl::AddConvLayerWithDepthwiseConv(
const onnx::NodeProto& node,
const Convolution2dDescriptor& convDesc)
1049 auto weightTensor = CreateConstTensor(node.input(1), perVec);
1051 if (node.input_size() == 3)
1053 if(!m_TensorsInfo[node.input(2)].isConstant())
1055 throw ParseException(fmt::format(
"Bias '{}' should be constant in Conv layer '{}' {}",
1060 desc.m_BiasEnabled =
true;
1061 auto biasTensor = CreateConstTensor(node.input(2));
1062 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
1065 node.name().c_str());
1069 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
1072 node.name().c_str());
1076 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1077 { m_TensorsInfo[node.input(0)].m_info->GetShape(),
1078 weightTensor.first.GetInfo().GetShape() });
1084 RegisterInputSlots(layer, {node.input(0)});
1087 RegisterOutputSlots(layer, {node.output(0)});
1090 void OnnxParserImpl::AddFullyConnected(
const onnx::NodeProto& matmulNode,
const onnx::NodeProto* addNode)
1094 std::string weightName;
1095 std::string inputName;
1100 GetInputAndParam(matmulNode, &inputName, &weightName,
CHECK_LOCATION());
1109 std::string biasName;
1114 GetInputAndParam(*addNode,
nullptr, &biasName,
CHECK_LOCATION());
1118 TensorInfo weightInfo = *m_TensorsInfo[weightName].m_info;
1119 TensorInfo biasInfo = *m_TensorsInfo[biasName].m_info;
1124 fmt::format(
"Shape of weights '{}' and bias of following Add node '{}' do not match : {}" 1125 " and {} ( /!\\ bias should be a 1D tensor) {}",
1128 TensorInfoAsString(*m_TensorsInfo[weightName].m_info, weightName,
1129 m_TensorsInfo[weightName].m_dtype),
1130 TensorInfoAsString(*m_TensorsInfo[biasName].m_info, biasName,
1131 m_TensorsInfo[biasName].m_dtype ),
1136 layer = m_Network->AddFullyConnectedLayer(desc, matmulNode.name().c_str());
1139 auto outputInfo = ComputeOutputInfo({addNode->output(0)}, layer,
1140 {m_TensorsInfo[inputName].m_info->GetShape(),
1141 m_TensorsInfo[weightName].m_info->GetShape()});
1145 if(m_TensorsInfo[weightName].isConstant())
1147 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(CreateConstTensor(weightName).first);
1154 if(m_TensorsInfo[biasName].isConstant())
1156 IConnectableLayer* biasLayer = m_Network->AddConstantLayer(CreateConstTensor(biasName).first);
1163 RegisterInputSlots(layer, {inputName, weightName, biasName});
1164 RegisterOutputSlots(layer, {addNode->output(0)});
1168 layer = m_Network->AddFullyConnectedLayer(desc, matmulNode.name().c_str());
1171 auto outputInfo = ComputeOutputInfo({matmulNode.output(0)}, layer,
1172 {m_TensorsInfo[inputName].m_info->GetShape(),
1173 m_TensorsInfo[weightName].m_info->GetShape()});
1177 if(m_TensorsInfo[weightName].isConstant())
1179 TensorInfo weightInfo = *m_TensorsInfo[weightName].m_info;
1180 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(CreateConstTensor(weightName).first);
1187 RegisterInputSlots(layer, {inputName, weightName});
1188 RegisterOutputSlots(layer, {matmulNode.output(0)});
1192 void OnnxParserImpl::AddPoolingLayer(
const onnx::NodeProto& node,
Pooling2dDescriptor& desc)
1200 std::vector<uint32_t> kernel_shape = ReadMandatoryNodeUint32ListAttribute(node,
"kernel_shape");
1201 std::vector<uint32_t> strides = ReadOptionalNodeUint32ListAttribute(node,
"strides");
1202 std::vector<uint32_t> pads = ReadOptionalNodeUint32ListAttribute(node,
"pads");
1223 std::string paddingString = ReadOptionalNodeStringAttribute(node,
"auto_pad");
1224 if(paddingString !=
"VALID" && paddingString !=
"" && paddingString !=
"NOTSET")
1227 if( paddingString ==
"SAME_LOWER")
1231 else if (paddingString ==
"SAME_UPPER")
1237 throw ParseException(fmt::format(
"Invalid auto_pad attribute for node {}. " 1238 "Only SAME_UPPER, SAME_LOWER or VALID supported and found {} {}",
1243 auto inputInfo = *m_TensorsInfo[node.input(0)].m_info;
1244 uint32_t inputHeight = inputInfo.GetShape()[2];
1245 uint32_t inputWidth = inputInfo.GetShape()[3];
1246 CalcPadding(inputHeight,
1253 CalcPadding(inputWidth,
1270 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, node.name().c_str());
1273 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1278 RegisterInputSlots(layer, {node.input(0)});
1281 RegisterOutputSlots(layer, {node.output(0)});
1284 std::pair<std::string, std::string> OnnxParserImpl::AddPrepareBroadcast(
const std::string& input0,
1285 const std::string& input1)
1287 std::pair<std::string, std::string> inputs = std::make_pair(input0, input1);
1289 TensorShape input0Shape = m_TensorsInfo[input0].m_info->GetShape();
1290 TensorShape input1Shape = m_TensorsInfo[input1].m_info->GetShape();
1294 auto outputName = fmt::format(
"reshape_output_{}", input1);
1295 PrependForBroadcast(outputName, input1, input0);
1296 inputs.second = outputName;
1300 auto outputName = fmt::format(
"reshape_output_{}", input0);
1301 PrependForBroadcast(outputName, input0, input1);
1302 inputs.first = outputName;
1307 void OnnxParserImpl::CreateConstantLayer(
const std::string& tensorName,
const std::string& layerName)
1309 auto armnnTensor = CreateConstTensor(tensorName);
1310 IConnectableLayer* layer = m_Network->AddConstantLayer(armnnTensor.first, layerName.c_str());
1312 RegisterOutputSlots(layer, {tensorName});
1315 void OnnxParserImpl::CreateInt64ConstantLayer(
const std::string& tensorName,
const std::string& layerName)
1317 auto armnnTensor = CreateInt64ConstTensor(tensorName);
1318 IConnectableLayer* layer = m_Network->AddConstantLayer(armnnTensor.first, layerName.c_str());
1320 RegisterOutputSlots(layer, {tensorName});
1323 void OnnxParserImpl::CreateReshapeLayer(
const std::string& inputName,
1324 const std::string& outputName,
1325 const std::string& layerName)
1327 const TensorInfo outputTensorInfo = *m_TensorsInfo[outputName].m_info;
1331 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1337 RegisterInputSlots(layer, {inputName});
1340 RegisterOutputSlots(layer, {outputName});
1353 if (func == ActivationFunction::BoundedReLu)
1355 if (node.input_size() == 1 && node.attribute_size() > 0)
1357 desc.
m_A = ReadOptionalNodeFloatAttribute(node,
"max", std::numeric_limits<float>::max());
1358 desc.
m_B = ReadOptionalNodeFloatAttribute(node,
"min", std::numeric_limits<float>::lowest());
1362 desc.
m_A = node.input(2).empty() ? std::numeric_limits<float>::max() : std::stof(node.input(2));
1363 desc.
m_B = node.input(1).empty() ? std::numeric_limits<float>::lowest() : std::stof(node.input(1));
1367 IConnectableLayer*
const layer = m_Network->AddActivationLayer(desc, node.name().c_str());
1370 auto outputInfo = ComputeOutputInfo({ node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1371 layer->GetOutputSlot(0).SetTensorInfo(outputInfo[0]);
1375 RegisterInputSlots(layer, {node.input(0)});
1378 RegisterOutputSlots(layer, {node.output(0)});
1381 void OnnxParserImpl::ParseClip(
const onnx::NodeProto& node)
1383 ParseActivation(node, ActivationFunction::BoundedReLu);
1386 void OnnxParserImpl::ParseSigmoid(
const onnx::NodeProto& node)
1388 ParseActivation(node, ActivationFunction::Sigmoid);
1391 void OnnxParserImpl::ParseTanh(
const onnx::NodeProto& node)
1393 ParseActivation(node, ActivationFunction::TanH);
1396 void OnnxParserImpl::ParseRelu(
const onnx::NodeProto& node)
1398 ParseActivation(node, ActivationFunction::ReLu);
1401 void OnnxParserImpl::ParseLeakyRelu(
const onnx::NodeProto& node)
1403 ParseActivation(node, ActivationFunction::LeakyReLu);
1406 void OnnxParserImpl::ParseAdd(
const onnx::NodeProto& node)
1417 auto inputs = AddPrepareBroadcast(node.input(0), node.input(1));
1418 auto input0 = *m_TensorsInfo[inputs.first].m_info;
1419 auto input1 = *m_TensorsInfo[inputs.second].m_info;
1420 ARMNN_ASSERT(input0.GetNumDimensions() == input1.GetNumDimensions());
1422 unsigned int numDims = input0.GetNumDimensions();
1423 for (
unsigned int i = 0; i < numDims; i++)
1425 unsigned int dim0 = input0.GetShape()[i];
1426 unsigned int dim1 = input1.GetShape()[i];
1427 if (dim0 != dim1 && dim0 != 1 && dim1 != 1)
1430 fmt::format(
"Broadcast is only supported for scalar or 1D tensors in Add node '{}'. " 1431 "Input dimensions should either match or one should be of size 1 and here, " 1434 TensorInfoAsString(*m_TensorsInfo[inputs.first].m_info, inputs.first,
1435 m_TensorsInfo[inputs.first].m_dtype),
1436 TensorInfoAsString(*m_TensorsInfo[inputs.second].m_info, inputs.second,
1437 m_TensorsInfo[inputs.second].m_dtype),
1446 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1447 { m_TensorsInfo[inputs.first].m_info->GetShape(),
1448 m_TensorsInfo[inputs.second].m_info->GetShape() });
1452 if(m_TensorsInfo[inputs.first].isConstant()) {
1453 CreateConstantLayer(inputs.first, fmt::format(
"Add:constant_of_{}", node.input(0)));
1455 if(m_TensorsInfo[inputs.second].isConstant()) {
1456 CreateConstantLayer(inputs.second, fmt::format(
"Add:constant_of_{}", node.input(1)));
1458 RegisterInputSlots(layer, {inputs.first, inputs.second});
1461 RegisterOutputSlots(layer, {node.output(0)});
1464 void OnnxParserImpl::ParseAveragePool(
const onnx::NodeProto& node)
1469 uint32_t count_include_pad = 0;
1470 count_include_pad = ReadOptionalNodeUint32Attribute(node,
"count_include_pad");
1471 if(count_include_pad) {
1474 AddPoolingLayer(node, desc);
1477 void OnnxParserImpl::ParseBatchNormalization(
const onnx::NodeProto& node)
1485 for(
int ind = 1; ind < node.input_size(); ++ind)
1487 auto tensor = node.input(ind);
1488 if(! m_TensorsInfo[tensor].isConstant())
1491 fmt::format(
"Input tensor '{}' should be constant in BatchNormalization node '{}' {}",
1498 float epsilon = ReadOptionalNodeFloatAttribute(node,
"epsilon", 1e-5f);
1500 desc.
m_Eps = epsilon;
1502 auto scaleTensor = CreateConstTensor(node.input(1));
1503 auto biasTensor = CreateConstTensor(node.input(2));
1504 auto meanTensor = CreateConstTensor(node.input(3));
1505 auto varTensor = CreateConstTensor(node.input(4));
1512 node.name().c_str());
1515 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1516 layer->GetOutputSlot(0).SetTensorInfo(outputInfo[0]);
1518 RegisterInputSlots(layer, {node.input(0)});
1521 RegisterOutputSlots(layer, {node.output(0)});
1524 void OnnxParserImpl::ParseConcat(
const onnx::NodeProto& node)
1528 uint32_t numConcatView =
static_cast<uint32_t
>(node.input_size());
1529 uint32_t inputRank = m_TensorsInfo[node.input(0)].m_info->GetNumDimensions();
1531 int axisInt = ReadMandatoryNodeIntAttribute(node,
"axis");
1533 unsigned int concatDimInput =
static_cast<unsigned int>(
1534 (
static_cast<int>(inputRank) + axisInt) %
static_cast<int>(inputRank));
1539 unsigned int mergeDimOrigin = 0;
1541 std::vector<TensorShape> inputShapes;
1542 std::vector<std::string> tensorIds;
1544 for (
unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1546 std::string nodeName = node.input(static_cast<int>(viewIndex));
1547 auto inputTensorInfo = *m_TensorsInfo[nodeName].m_info;
1548 inputShapes.push_back(inputTensorInfo.GetShape());
1549 tensorIds.push_back(nodeName);
1553 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
1556 IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, node.name().c_str());
1559 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, inputShapes,
1560 m_TensorsInfo[node.input(0)].m_dtype);
1565 RegisterInputSlots(layer, tensorIds);
1568 RegisterOutputSlots(layer, { node.output(0) });
1571 void OnnxParserImpl::ParseConstant(
const onnx::NodeProto& node)
1574 if (!node.attribute(0).has_t())
1576 throw ParseException(fmt::format(
"Value not found for Constant node '{}' {}",
1580 const onnx::TensorProto& onnxTensor = node.attribute(0).t();
1583 m_TensorsInfo[node.output(0)].m_tensor = std::make_unique<const onnx::TensorProto>(onnxTensor);
1584 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(onnxTensor));
1587 if (m_TensorsInfo[node.output(0)].m_dtype == onnx::TensorProto_DataType_FLOAT)
1589 CreateConstantLayer(node.output(0), node.name());
1591 else if (m_TensorsInfo[node.output(0)].m_dtype == onnx::TensorProto_DataType_INT64)
1593 CreateInt64ConstantLayer(node.output(0), node.name());
1597 throw ParseException(fmt::format(
"Data type not support for Constant node '{}' {}",
1603 void OnnxParserImpl::ParseConv(
const onnx::NodeProto& node)
1610 if(m_TensorsInfo[node.input(0)].m_info->GetNumDimensions() != 4)
1613 fmt::format(
"ArmNN only supports 2D convolution and Conv layer '{}' input {} {}",
1615 TensorInfoAsString(*m_TensorsInfo[node.input(0)].m_info, node.input(0),
1616 m_TensorsInfo[node.input(0)].m_dtype),
1620 if(!m_TensorsInfo[node.input(1)].isConstant())
1623 fmt::format(
"Weights '{}' should be constant in Conv layer '{}' {}",
1629 auto inputInfo = *m_TensorsInfo[node.input(0)].m_info;
1634 std::vector<uint32_t> strides = ReadOptionalNodeUint32ListAttribute(node,
"strides");
1646 std::vector<uint32_t> dilations = ReadOptionalNodeUint32ListAttribute(node,
"dilations");
1647 if(!dilations.empty())
1653 std::vector<uint32_t> pads = ReadOptionalNodeUint32ListAttribute(node,
"pads");
1658 std::string paddingString = ReadOptionalNodeStringAttribute(node,
"auto_pad");
1659 if(paddingString !=
"VALID" && paddingString !=
"" && paddingString !=
"NOTSET")
1662 if( paddingString ==
"SAME_LOWER")
1666 else if (paddingString ==
"SAME_UPPER")
1673 fmt::format(
"Invalid auto_pad attribute for node {}. Only SAME_UPPER, SAME_LOWER or VALID " 1674 "supported and found {} {}",
1679 uint32_t inputHeight = inputInfo.GetShape()[2];
1680 uint32_t inputWidth = inputInfo.GetShape()[3];
1682 uint32_t weightHeight;
1683 uint32_t weightWidth;
1684 std::vector<uint32_t> kernel_shape = ReadOptionalNodeUint32ListAttribute(node,
"kernel_shape");
1685 if (kernel_shape.empty())
1687 const TensorInfo weightTensorInfo = *m_TensorsInfo[node.input(1)].m_info;
1688 weightHeight = weightTensorInfo.
GetShape()[2];
1689 weightWidth = weightTensorInfo.
GetShape()[3];
1693 weightHeight = kernel_shape[0];
1694 weightWidth = kernel_shape[1];
1696 CalcPadding(inputHeight,
1703 CalcPadding(inputWidth,
1720 uint32_t group = ReadOptionalNodeUint32Attribute(node,
"group", 1);
1723 if (group > inputInfo.GetShape()[1])
1726 fmt::format(
"Error parsing Convolution node: {}. " 1727 "The 'group'={} parameter cannot be larger than the " 1728 "channel of the input shape={} (in NCHW format). {}",
1731 inputInfo.GetShape()[1],
1734 else if (group == inputInfo.GetShape()[1])
1738 AddConvLayerWithDepthwiseConv(node, desc);
1745 throw ParseException(fmt::format(
"Error parsing Convolution node: {}. " 1746 "The 'group'={} parameter should be 1 or be equal to the " 1747 "channel of the input shape={} (in NCHW format). {}",
1750 inputInfo.GetShape()[1],
1756 auto weightTensor = CreateConstTensor(node.input(1));
1758 if (node.input_size() == 3)
1760 if(!m_TensorsInfo[node.input(2)].isConstant())
1762 throw ParseException(fmt::format(
"Bias '{}' should be constant in Conv layer '{}' {}",
1768 auto biasTensor = CreateConstTensor(node.input(2));
1769 layer = m_Network->AddConvolution2dLayer(desc,
1772 node.name().c_str());
1776 layer = m_Network->AddConvolution2dLayer(desc,
1779 node.name().c_str());
1783 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1784 { m_TensorsInfo[node.input(0)].m_info->GetShape(),
1785 m_TensorsInfo[node.input(1)].m_info->GetShape() });
1790 RegisterInputSlots(layer, {node.input(0)});
1793 RegisterOutputSlots(layer, {node.output(0)});
1796 void OnnxParserImpl::ParseFlatten(
const onnx::NodeProto& node)
1802 m_TensorsInfo[node.input(0)].m_dtype,
1803 onnx::TensorProto::FLOAT);
1805 int64_t axis = ReadOptionalNodeInt64Attribute(node,
"axis", 1);
1806 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
1817 throw ParseException(fmt::format(
"Axis '{}' invalid. Tensor has '{}' dimensions in FlattenLayer '{}'",
1827 for (i = 0; i < axis; i++){
1828 dimension1 *= inputShape[i];
1833 dimension2 *= inputShape[i];
1838 auto outInfo = ComputeReshapeInfo(outputShape, inputShape, node.output(0));
1839 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
1840 CreateReshapeLayer(node.input(0), node.output(0), node.name());
1843 void OnnxParserImpl::ParseGather(
const onnx::NodeProto& node)
1849 gatherDescriptor.
m_Axis =
static_cast<int>(ReadOptionalNodeInt64Attribute(node,
"axis", 0));
1851 IConnectableLayer* layer = m_Network->AddGatherLayer(gatherDescriptor, node.name().c_str());
1854 const TensorShape& inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
1855 const TensorShape& indicesShape = m_TensorsInfo[node.input(1)].m_info->GetShape();
1856 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, { inputShape, indicesShape },
1857 m_TensorsInfo[node.input(0)].m_dtype);
1861 RegisterInputSlots(layer, { node.input(0), node.input(1) });
1864 RegisterOutputSlots(layer, { node.output(0) });
1867 void OnnxParserImpl::ParseGemm(
const onnx::NodeProto& node)
1872 int transA =
static_cast<int>(ReadOptionalNodeUint32Attribute(node,
"transA", 0));
1873 int transB =
static_cast<int>(ReadOptionalNodeUint32Attribute(node,
"transB", 0));
1874 float alpha = ReadOptionalNodeFloatAttribute(node,
"alpha", 1.0);
1875 float beta = ReadOptionalNodeFloatAttribute(node,
"beta", 1.0);
1876 bool biasEnabled = node.input_size() == 3;
1878 TensorShape input0Shape = m_TensorsInfo[node.input(0)].m_info->GetShape();
1879 TensorShape input1Shape = m_TensorsInfo[node.input(1)].m_info->GetShape();
1889 layer = m_Network->AddFullyConnectedLayer(fullyConnectedDescriptor, node.name().c_str());
1895 std::string transAName =
"transpose_" + node.input(0);
1898 IConnectableLayer* transALayer = m_Network->AddTransposeLayer(transposeADescriptor, transAName.c_str());
1900 auto transAInfo = ComputeOutputInfo({ transAName }, transALayer, { input0Shape });
1901 transALayer->GetOutputSlot(0).SetTensorInfo(transAInfo[0]);
1902 transALayer->GetOutputSlot(0).Connect(layer->
GetInputSlot(0u));
1904 RegisterInputSlot(transALayer, node.input(0), 0);
1905 input0Shape = transAInfo[0].GetShape();
1909 RegisterInputSlot(layer, node.input(0), 0);
1913 if(m_TensorsInfo[node.input(1)].isConstant())
1915 IConnectableLayer* weightsLayer = m_Network->AddConstantLayer(CreateConstTensor(node.input(1)).first);
1916 TensorInfo weightInfo = *m_TensorsInfo[node.input(1)].m_info;
1923 std::string activationName =
"activation_" + node.input(1);
1925 activationDescriptor.
m_A = alpha;
1926 activationDescriptor.
m_Function = ActivationFunction::Linear;
1927 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
1930 auto actInfo = ComputeOutputInfo({ activationName }, actLayer, { weightInfo.
GetShape() });
1931 actLayer->GetOutputSlot(0).SetTensorInfo(actInfo[0]);
1932 actLayer->GetOutputSlot(0).Connect(layer->
GetInputSlot(1u));
1934 input1Shape = actInfo[0].GetShape();
1939 input1Shape = weightInfo.
GetShape();
1947 std::string activationName =
"activation_" + node.input(1);
1949 activationDescriptor.
m_A = alpha;
1950 activationDescriptor.
m_Function = ActivationFunction::Linear;
1951 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
1954 auto actInfo = ComputeOutputInfo({ activationName }, actLayer, { input1Shape });
1955 actLayer->GetOutputSlot(0).SetTensorInfo(actInfo[0]);
1956 actLayer->GetOutputSlot(0).Connect(layer->
GetInputSlot(1u));
1957 RegisterInputSlot(actLayer, node.input(1), 0);
1958 input1Shape = actInfo[0].GetShape();
1962 RegisterInputSlot(layer, node.input(1), 1);
1966 if(biasEnabled && m_TensorsInfo[node.input(2)].isConstant())
1969 IConnectableLayer* biasLayer = m_Network->AddConstantLayer(CreateConstTensor(node.input(2)).first);
1970 TensorInfo biasInfo = *m_TensorsInfo[node.input(2)].m_info;
1972 biasLayer->GetOutputSlot(0).SetTensorInfo(biasInfo);
1977 std::string activationName =
"activation_" + node.input(2);
1979 activationDescriptor.
m_A = beta;
1980 activationDescriptor.
m_Function = ActivationFunction::Linear;
1981 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
1984 auto actInfo = ComputeOutputInfo({ activationName }, actLayer, { biasInfo.
GetShape() });
1985 actLayer->GetOutputSlot(0).SetTensorInfo(actInfo[0]);
1986 actLayer->GetOutputSlot(0).Connect(layer->
GetInputSlot(2u));
1987 biasLayer->GetOutputSlot(0).Connect(actLayer->GetInputSlot(0u));
1991 biasLayer->GetOutputSlot(0).Connect(layer->
GetInputSlot(2u));
1994 else if (biasEnabled)
1997 if (m_TensorsInfo[node.input(2)].m_info->GetNumDimensions() != 1)
1999 throw ParseException(fmt::format(
"The parser supports constant or non-constant with 1 dimension for " 2000 "Input C of Gemm. Input '{}' in '{}' is not supported '{}'",
2008 std::string activationName =
"activation_" + node.input(2);
2010 activationDescriptor.
m_A = beta;
2011 activationDescriptor.
m_Function = ActivationFunction::Linear;
2012 IConnectableLayer* actLayer = m_Network->AddActivationLayer(activationDescriptor, activationName.c_str());
2015 auto actInfo = ComputeOutputInfo({ activationName },
2017 { m_TensorsInfo[node.input(2)].m_info->GetShape() });
2018 actLayer->GetOutputSlot(0).SetTensorInfo(actInfo[0]);
2019 actLayer->GetOutputSlot(0).Connect(layer->
GetInputSlot(2u));
2020 RegisterInputSlot(actLayer, node.input(2), 0);
2024 RegisterInputSlot(layer, node.input(2), 2);
2029 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
2030 { input0Shape, input1Shape });
2033 RegisterOutputSlots(layer, {node.output(0)});
2036 void OnnxParserImpl::ParseGlobalAveragePool(
const onnx::NodeProto& node)
2042 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2046 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, node.name().c_str());
2049 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {inputShape});
2054 RegisterInputSlots(layer, {node.input(0)});
2057 RegisterOutputSlots(layer, {node.output(0)});
2060 void OnnxParserImpl::ParseMaxPool(
const onnx::NodeProto& node)
2065 AddPoolingLayer(node, desc);
2068 void OnnxParserImpl::ParseShape(
const onnx::NodeProto& node)
2076 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2077 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {inputShape}, onnx::TensorProto::INT64);
2081 RegisterInputSlots(layer, {node.input(0)});
2084 RegisterOutputSlots(layer, {node.output(0)});
2087 void OnnxParserImpl::ParseReshape(
const onnx::NodeProto& node)
2093 m_TensorsInfo[node.input(0)].m_dtype,
2094 onnx::TensorProto::FLOAT);
2096 m_TensorsInfo[node.input(1)].m_dtype,
2097 onnx::TensorProto::INT64);
2099 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2101 std::vector<unsigned int> targetShape;
2102 if(m_TensorsInfo[node.input(1)].isConstant())
2104 unsigned int dims =
static_cast<unsigned int>(m_TensorsInfo[node.input(1)].m_tensor->int64_data_size());
2105 targetShape.reserve(dims);
2107 for(uint i = 0; i < dims; i++)
2109 int val =
CHECKED_INT32(m_TensorsInfo[node.input(1)].m_tensor->int64_data(static_cast<int>(i)));
2110 targetShape[i]=
static_cast<unsigned int>(val);
2116 unsigned int dims = m_TensorsInfo[node.input(1)].m_info->GetNumDimensions();
2117 TensorShape shapes = m_TensorsInfo[node.input(1)].m_info->GetShape();
2118 if (dims != 1 || shapes[0] > 2)
2120 throw ParseException(fmt::format(
"Invalid input shape '{}' in Reshape layer '{}' {}",
2126 unsigned int numInputElements = m_TensorsInfo[node.input(0)].m_info->GetNumElements();
2129 targetShape = { numInputElements };
2131 else if (shapes[0] == 2)
2133 targetShape = { inputShape[0] , numInputElements / inputShape[0] };
2137 if(m_TensorsInfo[node.input(0)].isConstant())
2140 if(m_TensorsInfo.count(node.output(0)) == 0)
2142 m_TensorsInfo[node.output(0)] = OnnxTensor();
2144 m_TensorsInfo[node.output(0)].m_tensor =
2145 std::make_unique<onnx::TensorProto>(*m_TensorsInfo[node.input(0)].m_tensor);
2149 if(m_TensorsInfo.count(node.output(0)) == 0 || m_TensorsInfo[node.output(0)].m_info ==
nullptr)
2151 auto outInfo = ComputeReshapeInfo(
2152 TensorShape(static_cast<unsigned int>(targetShape.size()), targetShape.data()),
2153 inputShape, node.output(0));
2154 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
2157 CreateReshapeLayer(node.input(0), node.output(0), node.name());
2161 void OnnxParserImpl::ParseUnsqueeze(
const onnx::NodeProto& node)
2166 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
2167 std::vector<uint32_t> dims;
2168 if (node.input_size() == 1 && node.attribute_size() > 0)
2170 dims = ReadMandatoryNodeUint32ListAttribute(node,
"axes");
2175 m_TensorsInfo[node.input(1)].m_dtype,
2176 onnx::TensorProto::INT64);
2178 auto int64Axes = m_TensorsInfo[node.input(1)].m_tensor->int64_data().data();
2179 uint numDim =
armnn::numeric_cast<uint>(m_TensorsInfo[node.input(1)].m_tensor->int64_data_size());
2181 for(uint i = 0; i < numDim; i++)
2184 dims.push_back(uint32Value);
2189 std::sort(dims.begin(), dims.end());
2191 std::vector<unsigned int> targetShape;
2197 targetShape.push_back(inputShape[i]);
2201 for(uint i = 0; i < dims.size(); i++)
2206 auto outInfo = ComputeReshapeInfo(
TensorShape(static_cast<unsigned int>(targetShape.size()), targetShape.data()),
2207 inputShape, node.output(0), m_TensorsInfo[node.input(0)].m_info->GetDataType());
2208 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
2209 m_TensorsInfo[node.output(0)].m_dtype = m_TensorsInfo[node.input(0)].m_dtype;
2211 CreateReshapeLayer(node.input(0), node.output(0), node.name());
2214 void OnnxParserImpl::PrependForBroadcast(
const std::string& outputName,
2215 const std::string& input0,
2216 const std::string& input1)
2221 TensorShape input0Shape = m_TensorsInfo[input0].m_info->GetShape();
2222 TensorShape input1Shape = m_TensorsInfo[input1].m_info->GetShape();
2225 std::vector<uint32_t> newShape;
2228 newShape.push_back(1);
2233 newShape.push_back(input0Shape[dim]);
2235 outputTensorInfo.
SetShape(
TensorShape(static_cast<unsigned int>(newShape.size()), newShape.data()));
2238 m_TensorsInfo[outputName] = OnnxTensor();
2239 m_TensorsInfo[outputName].m_info = std::make_unique<TensorInfo>(outputTensorInfo);
2242 if( ! m_TensorsInfo[input0].isConstant())
2244 CreateReshapeLayer(input0, outputName, fmt::format(
"Add:reshapeOf{}", input0));
2248 m_TensorsInfo[outputName].m_tensor = std::make_unique<onnx::TensorProto>(*m_TensorsInfo[input0].m_tensor);
2253 void OnnxParserImpl::SetupInputLayers()
2256 for(
int inputIndex = 0; inputIndex < m_Graph->input_size(); ++inputIndex)
2258 auto input = m_Graph->input(inputIndex);
2259 if (!m_TensorsInfo[input.name()].isConstant())
2262 m_Network->AddInputLayer(static_cast<armnn::LayerBindingId>(inputIndex), input.name().c_str());
2263 TensorInfo tensorInfo = *m_TensorsInfo[input.name()].m_info;
2266 if (m_InputShapes.find(input.name()) == m_InputShapes.end())
2268 throw ParseException(fmt::format(
"The parser does not support dynamic tensor, " 2269 "please specify input shape for {}. {}",
2275 tensorInfo.
SetShape(m_InputShapes[input.name()]);
2276 m_TensorsInfo[input.name()].m_info = std::make_unique<TensorInfo>(tensorInfo);
2282 m_InputInfos[input.name()] = tensorInfo;
2284 RegisterOutputSlots(layer,{ input.name() });
2289 void OnnxParserImpl::SetupOutputLayers()
2291 if(m_Graph->output_size() == 0)
2296 for(
int outputIndex = 0; outputIndex < m_Graph->output_size(); ++outputIndex)
2299 m_Network->AddOutputLayer(static_cast<armnn::LayerBindingId>(outputIndex),
2300 m_Graph->output(outputIndex).name().c_str());
2302 RegisterInputSlots(layer, { m_Graph->output(outputIndex).name() });
2307 const std::string& tensorId,
2308 unsigned int slotIndex)
2312 auto it = m_TensorConnections.find(tensorId);
2314 if (it == m_TensorConnections.end())
2317 m_TensorConnections[tensorId] = TensorSlots();
2319 m_TensorConnections[tensorId].inputSlots.push_back(slot);
2322 void OnnxParserImpl::RegisterInputSlots(
IConnectableLayer* layer,
const std::vector<std::string>& tensorIds)
2328 fmt::format(
"The number of tensor inputs ({}) does not match the number expected ({}) {}",
2334 for (
unsigned int slotIndex = 0; slotIndex < layer->
GetNumInputSlots(); ++slotIndex)
2336 std::string tensorId = tensorIds[slotIndex];
2339 auto it = m_TensorConnections.find(tensorId);
2341 if (it == m_TensorConnections.end())
2344 m_TensorConnections[tensorId] = TensorSlots();
2346 m_TensorConnections[tensorId].inputSlots.push_back(slot);
2350 void OnnxParserImpl::RegisterOutputSlots(
IConnectableLayer* layer,
const std::vector<std::string>& tensorIds)
2356 fmt::format(
"The number of tensor outputs ({}) does not match the number expected ({}) {} ",
2362 for (
unsigned int slotIndex = 0; slotIndex < layer->
GetNumOutputSlots(); ++slotIndex)
2364 std::string tensorId = tensorIds[slotIndex];
2367 auto it = m_TensorConnections.find(tensorId);
2369 if (it == m_TensorConnections.end())
2372 m_TensorConnections[tensorId] = TensorSlots();
2375 TensorSlots& tensorSlots = m_TensorConnections[tensorId];
2378 if (tensorSlots.outputSlot !=
nullptr)
2380 throw ParseException(fmt::format(
"Another layer has already registered itself as the producer of " 2385 tensorSlots.outputSlot = slot;
2392 for(
int i = 0; i < m_Graph->input_size(); ++i)
2394 auto input = m_Graph->input(i);
2395 if(input.name() == name)
2397 auto it = m_InputInfos.find(name);
2399 if (it != m_InputInfos.end())
2401 return std::make_pair(static_cast<armnn::LayerBindingId>(i), it->second);
2411 for(
int i = 0; i < m_Graph->output_size(); ++i)
2413 auto output = m_Graph->output(i);
2414 if(output.name() == name)
2416 auto it = m_OutputInfos.find(name);
2418 if (it != m_OutputInfos.end())
2420 return std::make_pair(static_cast<armnn::LayerBindingId>(i), it->second);
2430 if(model ==
nullptr) {
2435 std::vector<std::string> inputNames;
2436 std::map<std::string, bool> isConstant;
2437 for(
auto tensor : model->graph().initializer())
2439 isConstant[tensor.name()] =
true;
2441 for(
auto input : model->graph().input())
2443 auto it = isConstant.find(input.name());
2444 if(it == isConstant.end())
2446 inputNames.push_back(input.name());
2454 if(model ==
nullptr) {
2459 std::vector<std::string> outputNames;
2460 for(
auto output : model->graph().output())
2462 outputNames.push_back(output.name());
unsigned int GetNumElements() const
Function that calculates the tensor elements by multiplying all dimension size which are Specified...
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
virtual unsigned int GetNumOutputSlots() const =0
Returns the number of connectable output slots.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
virtual unsigned int GetNumInputSlots() const =0
Returns the number of connectable input slots.
const TensorShape & GetShape() const
uint32_t m_PadLeft
Padding left value in the width dimension.
std::string AsString() const
A ReshapeDescriptor for the ReshapeLayer.
BindingPointInfo GetNetworkInputBindingInfo(const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network input identified by the given layer ...
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
Dimensionality GetDimensionality() const
Function that returns the tensor type.
uint32_t m_PoolWidth
Pooling width value.
A Convolution2dDescriptor for the Convolution2dLayer.
uint32_t m_PadLeft
Padding left value in the width dimension.
unsigned int GetNumBytes() const
float m_Eps
Value to add to the variance. Used to avoid dividing by zero.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
BindingPointInfo GetNetworkOutputBindingInfo(const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network output identified by the given layer...
uint32_t m_PadTop
Padding top value in the height dimension.
void ProcessConcatInputTensorInfo(armnn::TensorInfo &inputTensorInfo, armnn::OriginsDescriptor &concatDescriptor, const unsigned int &concatAxis, unsigned int inputIndex, unsigned int &mergeDimOrigin)
uint32_t m_PadRight
Padding right value in the width dimension.
#define VALID_INPUTS(NODE, VALID_INPUTS)
Copyright (c) 2021 ARM Limited and Contributors.
uint32_t m_DilationY
Dilation along y axis.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile)
Create the network from a protobuf binary file on disk.
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
static const std::string GetVersion()
Retrieve version in X.Y.Z form.
static std::vector< std::string > GetInputs(ModelPtr &model)
Retrieve inputs names.
void SetShape(const TensorShape &newShape)
TensorShape m_TargetShape
Target shape value.
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadTop
Padding top value in the height dimension.
void Permute(const armnn::TensorShape &dstShape, const armnn::PermutationVector &mappings, const void *src, void *dst, size_t dataTypeSize)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
std::unique_ptr< onnx::ModelProto > ModelPtr
armnn::INetworkPtr CreateNetworkFromTextFile(const char *graphFile)
Create the network from a protobuf text file on disk.
static std::vector< std::string > GetOutputs(ModelPtr &model)
Retrieve outputs names.
uint32_t m_PadRight
Padding right value in the width dimension.
#define ARMNN_ASSERT_MSG(COND, MSG)
An output connection slot for a layer.
An OriginsDescriptor for the ConcatLayer.
armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile)
Create the network from a protobuf binary file on disk.
bool has_value() const noexcept
A FullyConnectedDescriptor for the FullyConnectedLayer.
bool m_BiasEnabled
Enable/disable bias.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
A GatherDescriptor for the GatherLayer.
#define CHECK_VALID_SIZE(ACTUAL,...)
#define CHECKED_NON_NEGATIVE(VALUE)
static ModelPtr LoadModelFromString(const std::string &inputString)
#define ARMNN_ASSERT(COND)
An ActivationDescriptor for the ActivationLayer.
static ModelPtr LoadModelFromTextFile(const char *fileName)
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH, Elu).
uint32_t m_DilationX
Dilation along x axis.
std::pair< armnn::ConstTensor, std::unique_ptr< T[]> > CreateConstTensorImpl(const T *bufferPtr, armnn::TensorInfo &tensorInfo, const armnn::Optional< armnn::PermutationVector &> permutationVector)
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
int32_t m_Axis
The axis in params to gather indices from.
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
OutputShapeRounding m_OutputShapeRounding
The rounding method for the output shape. (Floor, Ceiling).
void SetConcatAxis(unsigned int concatAxis)
Set the concatenation axis value.
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
void SetConstant(const bool IsConstant=true)
Marks the data corresponding to this tensor info as constant.
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
armnn::TensorInfo ToTensorInfo(TensorRawPtr tensorPtr)
A TransposeDescriptor for the TransposeLayer.
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
virtual const char * GetName() const =0
Returns the name of the layer.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
virtual int Connect(IInputSlot &destination)=0
#define CHECK_VALID_DATATYPE(NODE, TENSOR, ACTUAL,...)
virtual std::vector< TensorShape > InferOutputShapes(const std::vector< TensorShape > &inputShapes) const =0
Infer the shape of the output(s) based on the provided input shape(s)
armnn::BindingPointInfo BindingPointInfo
armnn::INetworkPtr CreateNetworkFromString(const std::string &protoText)
Create the network directly from protobuf text in a string. Useful for debugging/testing.
#define CHECKED_INT32(VALUE)
A Pooling2dDescriptor for the Pooling2dLayer.
#define ONNX_PARSER_VERSION
ONNX_PARSER_VERSION: "X.Y.Z" where: X = Major version number Y = Minor version number Z = Patch versi...
std::unique_ptr< IOnnxParser, void(*)(IOnnxParser *parser)> IOnnxParserPtr
static ModelPtr LoadModelFromBinaryFile(const char *fileName)
float m_B
Beta lower bound value used by the activation functions. (BoundedReLu, Linear, TanH).
armnn::TensorShape Permuted(const armnn::TensorShape &srcShape, const armnn::PermutationVector &mappings)
PermutationVector m_DimMappings
Indicates how to translate tensor elements from a given source into the target destination, when source and target potentially have different memory layouts e.g.
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu, LeakyReLu, Abs, Sqrt, Square, Elu).
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
A BatchNormalizationDescriptor for the BatchNormalizationLayer.
uint32_t m_PadLeft
Padding left value in the width dimension.
unsigned int GetNumElements() const