14 #include <fmt/format.h> 16 #include <google/protobuf/text_format.h> 17 #include <google/protobuf/io/zero_copy_stream_impl.h> 22 using namespace armnn;
27 IOnnxParser::IOnnxParser() : pOnnxParserImpl(new OnnxParserImpl()) {}
29 IOnnxParser::~IOnnxParser() =
default;
53 return pOnnxParserImpl->CreateNetworkFromTextFile(graphFile);
58 return pOnnxParserImpl->CreateNetworkFromString(protoText);
61 BindingPointInfo IOnnxParser::GetNetworkInputBindingInfo(
const std::string& name)
const 63 return pOnnxParserImpl->GetNetworkInputBindingInfo(name);
66 BindingPointInfo IOnnxParser::GetNetworkOutputBindingInfo(
const std::string& name)
const 68 return pOnnxParserImpl->GetNetworkOutputBindingInfo(name);
73 void CheckValidDataType(std::initializer_list<onnx::TensorProto::DataType> validInputTypes,
75 const char* validExpr,
77 std::string tensorName,
80 bool isValid = std::any_of(validInputTypes.begin(),
81 validInputTypes.end(),
86 fmt::format(
"Datatype {} is not valid for tensor '{}' of node '{}', not in {{{}}}. {}",
87 onnx::TensorProto::DataType_Name(actualValue),
95 #define CHECK_VALID_DATATYPE(NODE, TENSOR, ACTUAL, ...) \ 96 CheckValidDataType({__VA_ARGS__}, ACTUAL, #__VA_ARGS__, NODE, TENSOR, CHECK_LOCATION()) 98 using StrTypeListPair = std::pair<const char*, std::initializer_list<onnx::TensorProto::DataType>>;
99 #define STR_LIST(...) StrTypeListPair(#__VA_ARGS__, {__VA_ARGS__}) 101 template <
typename Callable>
102 void ReadMandatoryNodeAttributeImpl(
const onnx::NodeProto& node,
103 const std::string& attribName,
104 onnx::AttributeProto::AttributeType expectedType,
107 auto attribs = node.attribute();
109 while (attriNum < node.attribute_size())
111 if (attribs.Get(attriNum).name() == attribName)
113 if (attribs.Get(attriNum).type() == expectedType)
115 callable(attribs.Get(attriNum));
119 throw ParseException(fmt::format(
"Attribute {} of node {} expected to have {} as " 120 "onnx::AttributeProto::AttributeType, but found {} instead {}",
123 onnx::AttributeProto::AttributeType_Name(expectedType),
124 onnx::AttributeProto::AttributeType_Name(attribs.Get(attriNum).type()),
131 if (attriNum == node.attribute_size())
133 throw ParseException(fmt::format(
"Could not find required attribute {} in node {} {}",
138 template <
typename Callable>
139 void ReadOptionalNodeAttributeImpl(
const onnx::NodeProto& node,
140 const std::string& attribName,
141 onnx::AttributeProto::AttributeType expectedType,
144 auto attribs = node.attribute();
145 for (
int attriNum = 0; attriNum < node.attribute_size(); ++attriNum)
147 if (attribs.Get(attriNum).name() == attribName)
149 if (attribs.Get(attriNum).type() == expectedType)
151 callable(attribs.Get(attriNum));
156 fmt::format(
"Attribute {} of node {} expected to have {} as onnx::AttributeProto::AttributeType, " 157 "but found {} instead {}",
160 onnx::AttributeProto::AttributeType_Name(expectedType),
161 onnx::AttributeProto::AttributeType_Name(attribs.Get(attriNum).type()),
168 int64_t ReadOptionalNodeInt64Attribute(
const onnx::NodeProto& node,
169 const std::string& name,
170 const int64_t defaultValue = 0)
172 int64_t attribValue = defaultValue;
173 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
174 [&attribValue](
const onnx::AttributeProto& attrValue)
176 attribValue = attrValue.i();
181 std::vector<uint32_t> ReadMandatoryNodeUint32ListAttribute(
const onnx::NodeProto& node,
182 const std::string& name)
184 std::vector<uint32_t> attriList;
185 ReadMandatoryNodeAttributeImpl(node, name, onnx::AttributeProto::INTS,
186 [&attriList](
const onnx::AttributeProto& attrValue)
188 for (
int attriNum = 0; attriNum < attrValue.ints_size(); ++attriNum)
196 uint32_t ReadOptionalNodeUint32Attribute(
const onnx::NodeProto& node,
197 const std::string& name,
198 const uint32_t defaultVal = 0u)
200 uint32_t attribValue = defaultVal;
201 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INT,
202 [&attribValue](
const onnx::AttributeProto& attrValue)
209 std::vector<uint32_t> ReadOptionalNodeUint32ListAttribute(
const onnx::NodeProto& node,
210 const std::string& name)
212 std::vector<uint32_t> attriList;
213 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::INTS,
214 [&attriList](
const onnx::AttributeProto& attrValue)
216 for (
int attriNum = 0; attriNum < attrValue.ints_size(); ++attriNum)
225 float ReadOptionalNodeFloatAttribute(
const onnx::NodeProto& node,
226 const std::string& name,
227 const float defaultValue = 0.0f)
229 float attribValue = defaultValue;
230 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::FLOAT,
231 [&attribValue](
const onnx::AttributeProto& attrValue)
233 attribValue = attrValue.f();
238 std::string ReadOptionalNodeStringAttribute(
const onnx::NodeProto& node,
const std::string& name)
240 std::string attribValue =
"";
241 ReadOptionalNodeAttributeImpl(node, name, onnx::AttributeProto::STRING,
242 [&attribValue](
const onnx::AttributeProto& attrValue)
244 attribValue = attrValue.s();
254 case onnx::TensorProto::FLOAT:
256 type = DataType::Float32;
259 case onnx::TensorProto::INT32:
260 case onnx::TensorProto::INT64:
262 type = DataType::Signed32;
268 fmt::format(
"'{}' is not a currently supported datatype for tensor {}." 269 " Supported dataTypes are FLOAT, INT32 and INT64. {}",
270 onnx::TensorProto::DataType_Name(static_cast<onnx::TensorProto::DataType>(data_type)),
287 const onnx::TensorShapeProto onnxShape = info.type().tensor_type().shape();
288 std::vector<unsigned int> shapeDims;
289 for (
int i = 0; i < onnxShape.dim_size(); ++i)
294 if (shapeDims.empty())
296 shapeDims.push_back(1);
299 return ToTensorInfo(info.name(), shapeDims, info.type().tensor_type().elem_type());
304 std::vector<unsigned int> shapeDims;
306 for (
auto dim: tensor.dims())
311 if (shapeDims.empty())
313 shapeDims.push_back(1);
316 return ToTensorInfo(tensor.name(), shapeDims, tensor.data_type());
319 std::string TensorInfoAsString(
const TensorInfo& info,
320 const std::string& name,
324 std::stringstream ss;
325 ss <<
"tensor '" << name <<
"' contains " 326 << onnx::TensorProto::DataType_Name(type)
327 <<
" and has shape [";
331 ss << shape[i] <<
", ";
337 void CalcPadding(uint32_t inputSize,
341 uint32_t* paddingFront,
342 uint32_t* paddingBack,
345 uint32_t outputSize = (inputSize + stride - 1) / stride;
346 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
347 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
348 *paddingFront = (temp - inputSize) / 2;
349 *paddingBack = *paddingFront;
350 if((temp - inputSize) % 2 == 1)
365 const std::string& outName)
367 std::vector<int> targetDims;
373 targetDims.push_back(static_cast<int>(inShape[static_cast<uint>(i)]));
377 targetDims.push_back(val);
381 std::vector<unsigned int> outDims(targetDims.begin(), targetDims.end());
382 const auto stretchDim = std::find(targetDims.begin(), targetDims.end(), -1);
383 if (stretchDim != targetDims.end())
385 if (std::find(std::next(stretchDim), targetDims.end(), -1) != targetDims.end())
387 std::stringstream ss;
389 for(uint i = 0; i < targetDims.size() - 1; ++i)
391 ss << targetDims[i] <<
", ";
393 ss << targetDims[targetDims.size() - 1] <<
" ]";
396 fmt::format(
"Error during creation of reshaped tensor '{}'. At most one component of shape can be " 397 " -1 and here, shape is {} {}",
403 auto targetNumElements =
armnn::numeric_cast<
unsigned int>(std::accumulate(targetDims.begin(), targetDims.end(),
404 -1, std::multiplies<int32_t>()));
405 auto stretchIndex =
static_cast<size_t>(std::distance(targetDims.begin(), stretchDim));
406 outDims[stretchIndex] = inShape.
GetNumElements() / targetNumElements;
409 return TensorInfo(outShape, DataType::Float32);
414 const std::map<std::string, OnnxParserImpl::OperationParsingFunction> OnnxParserImpl::m_ParserFunctions = {
415 {
"BatchNormalization", &OnnxParserImpl::ParseBatchNormalization},
416 {
"GlobalAveragePool", &OnnxParserImpl::ParseGlobalAveragePool},
417 {
"AveragePool", &OnnxParserImpl::ParseAveragePool },
418 {
"Clip", &OnnxParserImpl::ParseClip },
419 {
"Constant", &OnnxParserImpl::ParseConstant },
420 {
"MaxPool", &OnnxParserImpl::ParseMaxPool },
421 {
"Reshape", &OnnxParserImpl::ParseReshape },
422 {
"Sigmoid", &OnnxParserImpl::ParseSigmoid },
423 {
"Tanh", &OnnxParserImpl::ParseTanh },
424 {
"Relu", &OnnxParserImpl::ParseRelu },
425 {
"LeakyRelu", &OnnxParserImpl::ParseLeakyRelu },
426 {
"Conv", &OnnxParserImpl::ParseConv },
427 {
"Add", &OnnxParserImpl::ParseAdd },
428 {
"Flatten", &OnnxParserImpl::ParseFlatten},
431 template<
typename TypePair,
typename Location>
432 void OnnxParserImpl::ValidateInputs(
const onnx::NodeProto& node,
433 TypePair validInputs,
434 const Location& location)
436 for(
auto input : node.input())
438 CheckValidDataType(validInputs.second,
439 m_TensorsInfo[input].m_dtype,
447 #define VALID_INPUTS(NODE, VALID_INPUTS) \ 448 OnnxParserImpl::ValidateInputs(NODE, \ 452 std::vector<TensorInfo> OnnxParserImpl::ComputeOutputInfo(std::vector<std::string> outNames,
454 std::vector<TensorShape> inputShapes)
457 bool needCompute = std::any_of(outNames.begin(),
459 [
this](std::string name)
461 return (m_TensorsInfo.count(name) == 0 || m_TensorsInfo[name].m_info ==
nullptr);
463 std::vector<TensorInfo> outInfo;
465 std::vector<TensorShape> inferredShapes;
471 for (uint i = 0; i < outNames.size(); ++i)
475 m_TensorsInfo[outNames[i]] = OnnxTensor();
476 m_TensorsInfo[outNames[i]].m_info = std::make_unique<TensorInfo>(
477 TensorInfo(inferredShapes[i], DataType::Float32));
479 outInfo.push_back(*m_TensorsInfo[outNames[i]].m_info);
484 OnnxParserImpl::OnnxParserImpl()
485 : m_Network(nullptr, nullptr)
489 void OnnxParserImpl::ResetParser()
495 void OnnxParserImpl::Cleanup()
497 m_TensorConnections.clear();
498 m_TensorsInfo.clear();
499 m_OutputsMap.clear();
500 m_OutputsFusedAndUsed.clear();
503 std::pair<ConstTensor, std::unique_ptr<float[]>> OnnxParserImpl::CreateConstTensor(
const std::string name)
505 const TensorInfo tensorInfo = *m_TensorsInfo[name].m_info;
506 onnx::TensorProto onnxTensor = *m_TensorsInfo[name].m_tensor;
508 auto srcData = onnxTensor.float_data().data();
509 std::unique_ptr<float[]> tensorData(
new float[tensorInfo.
GetNumElements()]);
510 const size_t tensorSizeInBytes = tensorInfo.
GetNumBytes();
512 if (!onnxTensor.has_raw_data())
514 if(tensorInfo.
GetNumElements() !=
static_cast<uint
>(onnxTensor.float_data_size()))
517 fmt::format(
"The number of data provided ({}) does not match the tensor '{}' number of " 519 onnxTensor.float_data_size(),
524 ::memcpy(tensorData.get(), srcData, tensorSizeInBytes);
528 ::memcpy(tensorData.get(), onnxTensor.raw_data().c_str(), tensorSizeInBytes);
534 throw ParseException(fmt::format(
"No tensor data found for Const tensor '{}' {}",
538 return std::make_pair(
ConstTensor(tensorInfo, tensorData.get()), std::move(tensorData));
543 FILE* fd = fopen(graphFile,
"r");
551 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
552 using google::protobuf::io::FileInputStream;
553 std::unique_ptr<FileInputStream> input = std::make_unique<FileInputStream>(fileno(fd));
554 bool success = google::protobuf::TextFormat::Parse(input.get(), modelProto.get());
559 std::stringstream
error;
560 error <<
"Failed to parse graph file";
570 return CreateNetworkFromModel(*modelProto);
576 FILE* fd = fopen(graphFile,
"rb");
584 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
586 google::protobuf::io::FileInputStream inStream(fileno(fd));
587 google::protobuf::io::CodedInputStream codedStream(&inStream);
588 codedStream.SetTotalBytesLimit(INT_MAX);
589 bool success = modelProto.get()->ParseFromCodedStream(&codedStream);
594 std::stringstream
error;
595 error <<
"Failed to parse graph file";
606 return CreateNetworkFromModel(*modelProto);
617 ModelPtr modelProto = std::make_unique<onnx::ModelProto>();
618 bool success = google::protobuf::TextFormat::ParseFromString(protoText, modelProto.get());
621 std::stringstream
error;
622 error <<
"Failed to parse graph file";
632 return CreateNetworkFromModel(*modelProto);
635 INetworkPtr OnnxParserImpl::CreateNetworkFromModel(onnx::ModelProto& model)
637 m_Network = INetwork::Create();
640 m_Graph = std::make_unique<onnx::GraphProto>(*model.mutable_graph());
649 return std::move(m_Network);
652 void OnnxParserImpl::LoadGraph()
657 SetupInfo(m_Graph->mutable_output());
658 SetupInfo(m_Graph->mutable_input());
659 SetupInfo(m_Graph->mutable_value_info());
661 for (
auto tensor : m_Graph->initializer())
663 m_TensorsInfo[tensor.name()].m_tensor = std::make_unique<const onnx::TensorProto>(tensor);
664 m_TensorsInfo[tensor.name()].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(tensor));
665 m_TensorsInfo[tensor.name()].m_dtype =
673 DetectFullyConnected();
676 for(
size_t nodeIndex = 0; nodeIndex < static_cast<size_t>(m_Graph->node_size()); nodeIndex++)
678 auto node = m_Graph->node(static_cast<int>(nodeIndex));
679 const std::string& operation = node.op_type();
682 if (operation ==
"MatMul" )
684 if(m_OutputsFusedAndUsed[nodeIndex].inputForNodes != m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.size())
687 AddFullyConnected(node);
690 else if (!(m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.empty()) && operation ==
"Add")
692 int matmulIndex =
static_cast<int> (m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes[0]);
693 AddFullyConnected(m_Graph->node(matmulIndex), &node);
695 else if (m_OutputsFusedAndUsed[nodeIndex].fusedWithNodes.empty())
697 auto it = m_ParserFunctions.find(operation);
698 if (it != m_ParserFunctions.end())
700 auto func = it->second;
705 throw ParseException(fmt::format(
"Unsupported operation {} for node '{}' {}",
714 for (
const auto& tensorCon : m_TensorConnections)
716 if (tensorCon.second.outputSlot !=
nullptr)
718 for (
size_t inputSlotIdx = 0; inputSlotIdx < tensorCon.second.inputSlots.size(); ++inputSlotIdx)
720 tensorCon.second.outputSlot->Connect(*(tensorCon.second.inputSlots[inputSlotIdx]));
726 void OnnxParserImpl::SetupInfo(
const google::protobuf::RepeatedPtrField<onnx::ValueInfoProto >* list)
728 for (
auto tensor : *list)
730 m_TensorsInfo[tensor.name()] = OnnxTensor();
731 m_TensorsInfo[tensor.name()].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(tensor));
732 m_TensorsInfo[tensor.name()].m_dtype =
737 void OnnxParserImpl::DetectFullyConnected()
739 m_OutputsFusedAndUsed = std::vector<UsageSummary> (
static_cast<size_t>(m_Graph->node_size()), UsageSummary());
740 auto matmulAndConstant = [&](
const std::string& constInput,
741 const std::string& matmulInput,
744 auto matmulIt = m_OutputsMap.find(matmulInput);
745 if(matmulIt != m_OutputsMap.end() && matmulIt->second.first->op_type() ==
"MatMul" 746 && m_TensorsInfo[constInput].isConstant())
748 nodeIndex = matmulIt->second.second;
754 for(
int nodeIndex = 0; nodeIndex < m_Graph->node_size(); nodeIndex++)
756 const onnx::NodeProto* node = &m_Graph->node(nodeIndex);
757 for (
const std::string& output : node->output())
759 m_OutputsMap[output] = std::make_pair(node, nodeIndex);
762 for (
const std::string& input : node->input())
764 auto matmulIt = m_OutputsMap.find(input);
765 if(matmulIt != m_OutputsMap.end()){
766 ++m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIt->second.second)].inputForNodes;
770 if (node->op_type() ==
"Add")
773 if (matmulAndConstant(node->input(0), node->input(1), matmulIndex) ||
774 matmulAndConstant(node->input(1), node->input(0), matmulIndex))
777 m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIndex)].fusedWithNodes
778 .push_back(static_cast<size_t>(nodeIndex));
780 m_OutputsFusedAndUsed[
static_cast<size_t>(nodeIndex)].fusedWithNodes
781 .push_back(static_cast<size_t>(matmulIndex));
786 for (
auto output: m_Graph->output()) {
787 auto matmulIt = m_OutputsMap.find(output.name());
788 if(matmulIt != m_OutputsMap.end()){
789 ++m_OutputsFusedAndUsed[
static_cast<size_t>(matmulIt->second.second)].inputForNodes;
794 template<
typename Location>
795 void OnnxParserImpl::GetInputAndParam(
const onnx::NodeProto& node,
796 std::string* inputName,
797 std::string* constName,
798 const Location& location)
801 if (m_TensorsInfo[node.input(0)].isConstant())
805 else if (m_TensorsInfo[node.input(1)].isConstant())
811 throw ParseException(fmt::format(
"One of the input tensors ('{}' or '{}') should be constant in node '{}' {}",
815 location.AsString()));
819 *constName = node.input(cstIndex);
823 *inputName = node.input(!cstIndex);
827 template<
typename Location>
828 void OnnxParserImpl::To1DTensor(
const std::string& name,
const Location& location)
830 TensorShape shape = m_TensorsInfo[name].m_info->GetShape();
831 std::vector<uint32_t> newShape;
837 fmt::format(
"Only tensors with shape [1, ..., 1, X] can be converted to 1D and {} {}",
838 TensorInfoAsString(*m_TensorsInfo[name].m_info, name, m_TensorsInfo[name].m_dtype),
839 location.AsString()));
844 m_TensorsInfo[name].m_info->SetShape(
TensorShape(static_cast<unsigned int>(newShape.size()), newShape.data()));
847 void OnnxParserImpl::AddConvLayerWithDepthwiseConv(
const onnx::NodeProto& node,
const Convolution2dDescriptor& convDesc)
861 auto weightTensor = CreateConstTensor(node.input(1));
862 TensorShape& weightShape = weightTensor.first.GetShape();
863 weightShape[1] = weightShape[0];
865 m_TensorsInfo[node.input(1)].m_info->SetShape(weightShape);
867 if (node.input_size() == 3)
869 if(!m_TensorsInfo[node.input(2)].isConstant())
871 throw ParseException(fmt::format(
"Bias '{}' should be constant in Conv layer '{}' {}",
876 desc.m_BiasEnabled =
true;
877 auto biasTensor = CreateConstTensor(node.input(2));
878 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
881 node.name().c_str());
885 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
888 node.name().c_str());
892 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
893 { m_TensorsInfo[node.input(0)].m_info->GetShape(),
894 m_TensorsInfo[node.input(1)].m_info->GetShape() });
900 RegisterInputSlots(layer, {node.input(0)});
903 RegisterOutputSlots(layer, {node.output(0)});
906 void OnnxParserImpl::AddFullyConnected(
const onnx::NodeProto& matmulNode,
const onnx::NodeProto* addNode)
910 std::string weightName;
911 std::string inputName;
916 GetInputAndParam(matmulNode, &inputName, &weightName,
CHECK_LOCATION());
925 std::string biasName;
934 TensorInfo weightInfo = *m_TensorsInfo[weightName].m_info;
935 TensorInfo biasInfo = *m_TensorsInfo[biasName].m_info;
940 fmt::format(
"Shape of weights '{}' and bias of following Add node '{}' do not match : {}" 941 " and {} ( /!\\ bias should be a 1D tensor) {}",
944 TensorInfoAsString(*m_TensorsInfo[weightName].m_info, weightName,
945 m_TensorsInfo[weightName].m_dtype),
946 TensorInfoAsString(*m_TensorsInfo[biasName].m_info, biasName,
947 m_TensorsInfo[biasName].m_dtype ),
950 layer = m_Network->AddFullyConnectedLayer(desc,
951 CreateConstTensor(weightName).first,
953 matmulNode.name().c_str());
956 auto outputInfo = ComputeOutputInfo({addNode->output(0)}, layer,
957 {m_TensorsInfo[inputName].m_info->GetShape(),
958 m_TensorsInfo[weightName].m_info->GetShape()});
962 RegisterInputSlots(layer, {inputName});
963 RegisterOutputSlots(layer, {addNode->output(0)});
967 layer = m_Network->AddFullyConnectedLayer(desc,
968 CreateConstTensor(weightName).first,
970 matmulNode.name().c_str());
973 auto outputInfo = ComputeOutputInfo({matmulNode.output(0)}, layer,
974 {m_TensorsInfo[inputName].m_info->GetShape(),
975 m_TensorsInfo[weightName].m_info->GetShape()});
978 RegisterInputSlots(layer, {inputName});
979 RegisterOutputSlots(layer, {matmulNode.output(0)});
983 void OnnxParserImpl::AddPoolingLayer(
const onnx::NodeProto& node,
Pooling2dDescriptor& desc)
991 std::vector<uint32_t> kernel_shape = ReadMandatoryNodeUint32ListAttribute(node,
"kernel_shape");
992 std::vector<uint32_t> strides = ReadOptionalNodeUint32ListAttribute(node,
"strides");
993 std::vector<uint32_t> pads = ReadOptionalNodeUint32ListAttribute(node,
"pads");
1014 std::string paddingString = ReadOptionalNodeStringAttribute(node,
"auto_pad");
1015 if(paddingString !=
"VALID" && paddingString !=
"" && paddingString !=
"NOTSET")
1018 if( paddingString ==
"SAME_LOWER")
1022 else if (paddingString ==
"SAME_UPPER")
1028 throw ParseException(fmt::format(
"Invalid auto_pad attribute for node {}. " 1029 "Only SAME_UPPER, SAME_LOWER or VALID supported and found {} {}",
1034 auto inputInfo = *m_TensorsInfo[node.input(0)].m_info;
1035 uint32_t inputHeight = inputInfo.GetShape()[2];
1036 uint32_t inputWidth = inputInfo.GetShape()[3];
1037 CalcPadding(inputHeight,
1044 CalcPadding(inputWidth,
1061 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, node.name().c_str());
1064 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1069 RegisterInputSlots(layer, {node.input(0)});
1072 RegisterOutputSlots(layer, {node.output(0)});
1075 std::pair<std::string, std::string> OnnxParserImpl::AddPrepareBroadcast(
const std::string& input0,
1076 const std::string& input1)
1078 std::pair<std::string, std::string> inputs = std::make_pair(input0, input1);
1080 TensorShape input0Shape = m_TensorsInfo[input0].m_info->GetShape();
1081 TensorShape input1Shape = m_TensorsInfo[input1].m_info->GetShape();
1085 auto outputName = fmt::format(
"reshape_output_{}", input1);
1086 PrependForBroadcast(outputName, input1, input0);
1087 inputs.second = outputName;
1091 auto outputName = fmt::format(
"reshape_output_{}", input0);
1092 PrependForBroadcast(outputName, input0, input1);
1093 inputs.first = outputName;
1098 void OnnxParserImpl::CreateConstantLayer(
const std::string& tensorName,
const std::string& layerName)
1100 auto armnnTensor = CreateConstTensor(tensorName);
1102 IConnectableLayer* layer = m_Network->AddConstantLayer(armnnTensor.first, layerName.c_str());
1104 RegisterOutputSlots(layer, {tensorName});
1107 void OnnxParserImpl::CreateReshapeLayer(
const std::string& inputName,
1108 const std::string& outputName,
1109 const std::string& layerName)
1111 const TensorInfo outputTensorInfo = *m_TensorsInfo[outputName].m_info;
1115 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1121 RegisterInputSlots(layer, {inputName});
1124 RegisterOutputSlots(layer, {outputName});
1137 if (func == ActivationFunction::BoundedReLu)
1139 desc.
m_A = node.input(2).empty() ? std::numeric_limits<float>::max() : std::stof(node.input(2));
1140 desc.
m_B = node.input(1).empty() ? std::numeric_limits<float>::lowest() : std::stof(node.input(1));
1143 IConnectableLayer*
const layer = m_Network->AddActivationLayer(desc, node.name().c_str());
1146 auto outputInfo = ComputeOutputInfo({ node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1147 layer->GetOutputSlot(0).SetTensorInfo(outputInfo[0]);
1151 RegisterInputSlots(layer, {node.input(0)});
1154 RegisterOutputSlots(layer, {node.output(0)});
1157 void OnnxParserImpl::ParseClip(
const onnx::NodeProto& node)
1159 ParseActivation(node, ActivationFunction::BoundedReLu);
1162 void OnnxParserImpl::ParseSigmoid(
const onnx::NodeProto& node)
1164 ParseActivation(node, ActivationFunction::Sigmoid);
1167 void OnnxParserImpl::ParseTanh(
const onnx::NodeProto& node)
1169 ParseActivation(node, ActivationFunction::TanH);
1172 void OnnxParserImpl::ParseRelu(
const onnx::NodeProto& node)
1174 ParseActivation(node, ActivationFunction::ReLu);
1177 void OnnxParserImpl::ParseLeakyRelu(
const onnx::NodeProto& node)
1179 ParseActivation(node, ActivationFunction::LeakyReLu);
1182 void OnnxParserImpl::ParseAdd(
const onnx::NodeProto& node)
1193 auto inputs = AddPrepareBroadcast(node.input(0), node.input(1));
1194 auto input0 = *m_TensorsInfo[inputs.first].m_info;
1195 auto input1 = *m_TensorsInfo[inputs.second].m_info;
1196 ARMNN_ASSERT(input0.GetNumDimensions() == input1.GetNumDimensions());
1198 unsigned int numDims = input0.GetNumDimensions();
1199 for (
unsigned int i = 0; i < numDims; i++)
1201 unsigned int dim0 = input0.GetShape()[i];
1202 unsigned int dim1 = input1.GetShape()[i];
1203 if (dim0 != dim1 && dim0 != 1 && dim1 != 1)
1206 fmt::format(
"Broadcast is only supported for scalar or 1D tensors in Add node '{}'. " 1207 "Input dimensions should either match or one should be of size 1 and here, " 1210 TensorInfoAsString(*m_TensorsInfo[inputs.first].m_info, inputs.first,
1211 m_TensorsInfo[inputs.first].m_dtype),
1212 TensorInfoAsString(*m_TensorsInfo[inputs.second].m_info, inputs.second,
1213 m_TensorsInfo[inputs.second].m_dtype),
1222 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1223 { m_TensorsInfo[inputs.first].m_info->GetShape(),
1224 m_TensorsInfo[inputs.second].m_info->GetShape() });
1228 if(m_TensorsInfo[inputs.first].isConstant()) {
1229 CreateConstantLayer(inputs.first, fmt::format(
"Add:constant_of_{}", node.input(0)));
1231 if(m_TensorsInfo[inputs.second].isConstant()) {
1232 CreateConstantLayer(inputs.second, fmt::format(
"Add:constant_of_{}", node.input(1)));
1234 RegisterInputSlots(layer, {inputs.first, inputs.second});
1237 RegisterOutputSlots(layer, {node.output(0)});
1240 void OnnxParserImpl::ParseAveragePool(
const onnx::NodeProto& node)
1245 uint32_t count_include_pad = 0;
1246 count_include_pad = ReadOptionalNodeUint32Attribute(node,
"count_include_pad");
1247 if(count_include_pad) {
1250 AddPoolingLayer(node, desc);
1253 void OnnxParserImpl::ParseBatchNormalization(
const onnx::NodeProto& node)
1261 for(
int ind = 1; ind < node.input_size(); ++ind)
1263 auto tensor = node.input(ind);
1264 if(! m_TensorsInfo[tensor].isConstant())
1267 fmt::format(
"Input tensor '{}' should be constant in BatchNormalization node '{}' {}",
1274 float epsilon = ReadOptionalNodeFloatAttribute(node,
"epsilon", 1e-5f);
1276 desc.
m_Eps = epsilon;
1278 auto scaleTensor = CreateConstTensor(node.input(1));
1279 auto biasTensor = CreateConstTensor(node.input(2));
1280 auto meanTensor = CreateConstTensor(node.input(3));
1281 auto varTensor = CreateConstTensor(node.input(4));
1288 node.name().c_str());
1291 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {m_TensorsInfo[node.input(0)].m_info->GetShape()});
1292 layer->GetOutputSlot(0).SetTensorInfo(outputInfo[0]);
1294 RegisterInputSlots(layer, {node.input(0)});
1297 RegisterOutputSlots(layer, {node.output(0)});
1300 void OnnxParserImpl::ParseConstant(
const onnx::NodeProto& node)
1303 if (!node.attribute(0).has_t())
1305 throw ParseException(fmt::format(
"Value not found for Constant node '{}' {}",
1309 const onnx::TensorProto& onnxTensor = node.attribute(0).t();
1316 m_TensorsInfo[node.output(0)].m_tensor = std::make_unique<const onnx::TensorProto>(onnxTensor);
1317 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(
ToTensorInfo(onnxTensor));
1320 CreateConstantLayer(node.output(0), node.name());
1323 void OnnxParserImpl::ParseConv(
const onnx::NodeProto& node)
1330 if(m_TensorsInfo[node.input(0)].m_info->GetNumDimensions() != 4)
1333 fmt::format(
"ArmNN only supports 2D convolution and Conv layer '{}' input {} {}",
1335 TensorInfoAsString(*m_TensorsInfo[node.input(0)].m_info, node.input(0),
1336 m_TensorsInfo[node.input(0)].m_dtype),
1340 if(!m_TensorsInfo[node.input(1)].isConstant())
1343 fmt::format(
"Weights '{}' should be constant in Conv layer '{}' {}",
1349 auto inputInfo = *m_TensorsInfo[node.input(0)].m_info;
1354 std::vector<uint32_t> strides = ReadOptionalNodeUint32ListAttribute(node,
"strides");
1366 std::vector<uint32_t> dilations = ReadOptionalNodeUint32ListAttribute(node,
"dilations");
1367 if(!dilations.empty())
1373 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")
1393 fmt::format(
"Invalid auto_pad attribute for node {}. Only SAME_UPPER, SAME_LOWER or VALID " 1394 "supported and found {} {}",
1399 uint32_t inputHeight = inputInfo.GetShape()[2];
1400 uint32_t inputWidth = inputInfo.GetShape()[3];
1402 uint32_t weightHeight;
1403 uint32_t weightWidth;
1404 std::vector<uint32_t> kernel_shape = ReadOptionalNodeUint32ListAttribute(node,
"kernel_shape");
1405 if (kernel_shape.empty())
1407 const TensorInfo weightTensorInfo = *m_TensorsInfo[node.input(1)].m_info;
1408 weightHeight = weightTensorInfo.
GetShape()[2];
1409 weightWidth = weightTensorInfo.
GetShape()[3];
1413 weightHeight = kernel_shape[0];
1414 weightWidth = kernel_shape[1];
1416 CalcPadding(inputHeight,
1423 CalcPadding(inputWidth,
1440 uint32_t group = ReadOptionalNodeUint32Attribute(node,
"group", 1);
1443 if (group > inputInfo.GetShape()[1])
1446 fmt::format(
"Error parsing Convolution node: {}. " 1447 "The 'group'={} parameter cannot be larger than the " 1448 "channel of the input shape={} (in NCHW format). {}",
1451 inputInfo.GetShape()[1],
1454 else if (group == inputInfo.GetShape()[1])
1458 AddConvLayerWithDepthwiseConv(node, desc);
1465 throw ParseException(fmt::format(
"Error parsing Convolution node: {}. " 1466 "The 'group'={} parameter should be 1 or be equal to the " 1467 "channel of the input shape={} (in NCHW format). {}",
1470 inputInfo.GetShape()[1],
1476 auto weightTensor = CreateConstTensor(node.input(1));
1478 if (node.input_size() == 3)
1480 if(!m_TensorsInfo[node.input(2)].isConstant())
1482 throw ParseException(fmt::format(
"Bias '{}' should be constant in Conv layer '{}' {}",
1488 auto biasTensor = CreateConstTensor(node.input(2));
1489 layer = m_Network->AddConvolution2dLayer(desc,
1492 node.name().c_str());
1496 layer = m_Network->AddConvolution2dLayer(desc,
1499 node.name().c_str());
1503 auto outputInfo = ComputeOutputInfo({ node.output(0) }, layer,
1504 { m_TensorsInfo[node.input(0)].m_info->GetShape(),
1505 m_TensorsInfo[node.input(1)].m_info->GetShape() });
1510 RegisterInputSlots(layer, {node.input(0)});
1513 RegisterOutputSlots(layer, {node.output(0)});
1516 void OnnxParserImpl::ParseFlatten(
const onnx::NodeProto& node)
1522 m_TensorsInfo[node.input(0)].m_dtype,
1523 onnx::TensorProto::FLOAT);
1525 int64_t axis = ReadOptionalNodeInt64Attribute(node,
"axis", 1);
1526 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
1537 throw ParseException(fmt::format(
"Axis '{}' invalid. Tensor has '{}' dimensions in FlattenLayer '{}'",
1547 for (i = 0; i < axis; i++){
1548 dimension1 *= inputShape[i];
1553 dimension2 *= inputShape[i];
1558 auto outInfo = ComputeReshapeInfo(outputShape, inputShape, node.output(0));
1559 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
1560 CreateReshapeLayer(node.input(0), node.output(0), node.name());
1563 void OnnxParserImpl::ParseGlobalAveragePool(
const onnx::NodeProto& node)
1569 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
1573 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, node.name().c_str());
1576 auto outputInfo = ComputeOutputInfo({node.output(0)}, layer, {inputShape});
1581 RegisterInputSlots(layer, {node.input(0)});
1584 RegisterOutputSlots(layer, {node.output(0)});
1587 void OnnxParserImpl::ParseMaxPool(
const onnx::NodeProto& node)
1592 AddPoolingLayer(node, desc);
1595 void OnnxParserImpl::ParseReshape(
const onnx::NodeProto& node)
1601 m_TensorsInfo[node.input(0)].m_dtype,
1602 onnx::TensorProto::FLOAT);
1604 m_TensorsInfo[node.input(1)].m_dtype,
1605 onnx::TensorProto::INT64);
1607 if(!m_TensorsInfo[node.input(1)].isConstant())
1609 throw ParseException(fmt::format(
"Shape '{}' should be constant in Reshape layer '{}' {}",
1615 if(m_TensorsInfo[node.input(0)].isConstant())
1618 if(m_TensorsInfo.count(node.output(0)) == 0)
1620 m_TensorsInfo[node.output(0)] = OnnxTensor();
1622 m_TensorsInfo[node.output(0)].m_tensor =
1623 std::make_unique<onnx::TensorProto>(*m_TensorsInfo[node.input(0)].m_tensor);
1627 TensorShape inputShape = m_TensorsInfo[node.input(0)].m_info->GetShape();
1629 if(m_TensorsInfo.count(node.output(0)) == 0 || m_TensorsInfo[node.output(0)].m_info ==
nullptr)
1631 uint64_t dims =
static_cast<uint64_t
>(m_TensorsInfo[node.input(1)].m_tensor->int64_data_size());
1632 TensorShape targetShape{
static_cast<unsigned int>(dims), 1};
1634 for(uint i = 0; i < dims; i++)
1636 int val =
CHECKED_INT32(m_TensorsInfo[node.input(1)].m_tensor->int64_data(static_cast<int>(i)));
1637 targetShape[i]=
static_cast<unsigned int>(val);
1640 auto outInfo = ComputeReshapeInfo(targetShape, inputShape, node.output(0));
1641 m_TensorsInfo[node.output(0)].m_info = std::make_unique<TensorInfo>(outInfo);
1644 CreateReshapeLayer(node.input(0), node.output(0), node.name());
1648 void OnnxParserImpl::PrependForBroadcast(
const std::string& outputName,
1649 const std::string& input0,
1650 const std::string& input1)
1655 TensorShape input0Shape = m_TensorsInfo[input0].m_info->GetShape();
1656 TensorShape input1Shape = m_TensorsInfo[input1].m_info->GetShape();
1659 std::vector<uint32_t> newShape;
1662 newShape.push_back(1);
1667 newShape.push_back(input0Shape[dim]);
1669 outputTensorInfo.
SetShape(
TensorShape(static_cast<unsigned int>(newShape.size()), newShape.data()));
1672 m_TensorsInfo[outputName] = OnnxTensor();
1673 m_TensorsInfo[outputName].m_info = std::make_unique<TensorInfo>(outputTensorInfo);
1676 if( ! m_TensorsInfo[input0].isConstant())
1678 CreateReshapeLayer(input0, outputName, fmt::format(
"Add:reshapeOf{}", input0));
1682 m_TensorsInfo[outputName].m_tensor = std::make_unique<onnx::TensorProto>(*m_TensorsInfo[input0].m_tensor);
1687 void OnnxParserImpl::SetupInputLayers()
1690 for(
int inputIndex = 0; inputIndex < m_Graph->input_size(); ++inputIndex)
1692 auto input = m_Graph->input(inputIndex);
1693 if (! m_TensorsInfo[input.name()].isConstant())
1696 m_Network->AddInputLayer(static_cast<armnn::LayerBindingId>(inputIndex), input.name().c_str());
1700 RegisterOutputSlots(layer,{ input.name() });
1705 void OnnxParserImpl::SetupOutputLayers()
1707 if(m_Graph->output_size() == 0)
1712 for(
int outputIndex = 0; outputIndex < m_Graph->output_size(); ++outputIndex)
1715 m_Network->AddOutputLayer(static_cast<armnn::LayerBindingId>(outputIndex),
1716 m_Graph->output(outputIndex).name().c_str());
1718 RegisterInputSlots(layer, { m_Graph->output(outputIndex).name() });
1722 void OnnxParserImpl::RegisterInputSlots(
IConnectableLayer* layer,
const std::vector<std::string>& tensorIds)
1728 fmt::format(
"The number of tensor inputs ({}) does not match the number expected ({}) {}",
1733 for (
unsigned int slotIndex = 0; slotIndex < layer->
GetNumInputSlots(); ++slotIndex)
1735 std::string tensorId = tensorIds[slotIndex];
1738 auto it = m_TensorConnections.find(tensorId);
1740 if (it == m_TensorConnections.end())
1743 m_TensorConnections[tensorId] = TensorSlots();
1745 m_TensorConnections[tensorId].inputSlots.push_back(slot);
1749 void OnnxParserImpl::RegisterOutputSlots(
IConnectableLayer* layer,
const std::vector<std::string>& tensorIds)
1755 fmt::format(
"The number of tensor outputs ({}) does not match the number expected ({}) {} ",
1761 for (
unsigned int slotIndex = 0; slotIndex < layer->
GetNumOutputSlots(); ++slotIndex)
1763 std::string tensorId = tensorIds[slotIndex];
1766 auto it = m_TensorConnections.find(tensorId);
1768 if (it == m_TensorConnections.end())
1771 m_TensorConnections[tensorId] = TensorSlots();
1774 TensorSlots& tensorSlots = m_TensorConnections[tensorId];
1777 if (tensorSlots.outputSlot !=
nullptr)
1779 throw ParseException(fmt::format(
"Another layer has already registered itself as the producer of " 1784 tensorSlots.outputSlot = slot;
1790 for(
int i = 0; i < m_Graph->input_size(); ++i)
1792 auto input = m_Graph->input(i);
1793 if(input.name() == name)
1795 return std::make_pair(static_cast<armnn::LayerBindingId>(i),
ToTensorInfo(input));
1804 for(
int i = 0; i < m_Graph->output_size(); ++i)
1806 auto output = m_Graph->output(i);
1807 if(output.name() == name)
1809 return std::make_pair(static_cast<armnn::LayerBindingId>(i),
ToTensorInfo(output));
1818 if(model ==
nullptr) {
1823 std::vector<std::string> inputNames;
1824 std::map<std::string, bool> isConstant;
1825 for(
auto tensor : model->graph().initializer())
1827 isConstant[tensor.name()] =
true;
1829 for(
auto input : model->graph().input())
1831 auto it = isConstant.find(input.name());
1832 if(it == isConstant.end())
1834 inputNames.push_back(input.name());
1842 if(model ==
nullptr) {
1847 std::vector<std::string> outputNames;
1848 for(
auto output : model->graph().output())
1850 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 ...
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.
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.
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.
An output connection slot for a layer.
armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile)
Create the network from a protobuf binary file on disk.
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.
#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.
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
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).
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
armnn::TensorInfo ToTensorInfo(TensorRawPtr tensorPtr)
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
#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).
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