From 841aca155b35cc17ea9527599d2c364695e28166 Mon Sep 17 00:00:00 2001 From: Jan Eilers Date: Wed, 12 Aug 2020 14:59:06 +0100 Subject: IVGCVSW-5200 Update pyarmnn * Add HalfPixelCenters to Resize * Update pyarmnn version to semantic versioning * Add fill operator * Add Bf16 optimization * Add Gather operator * Update TransposeConvolution2d descriptor * Add Rank operator * Add load dynamic tensor support of TfLiteParser Signed-off-by: Jan Eilers Change-Id: I7e76ed286ab87bd97a65ff62868ba7db7967376f --- python/pyarmnn/examples/example_utils.py | 4 +- python/pyarmnn/src/pyarmnn/__init__.py | 6 +- .../pyarmnn/src/pyarmnn/swig/armnn_tfliteparser.i | 30 +++++++++- python/pyarmnn/src/pyarmnn/swig/armnn_version.i | 4 +- .../src/pyarmnn/swig/modules/armnn_descriptors.i | 66 +++++++++++++++++++--- .../src/pyarmnn/swig/modules/armnn_network.i | 58 +++++++++++++++---- python/pyarmnn/test/test_descriptors.py | 21 ++++++- python/pyarmnn/test/test_network.py | 19 +++++++ python/pyarmnn/test/test_tflite_parser.py | 42 ++++++++++++++ 9 files changed, 217 insertions(+), 33 deletions(-) diff --git a/python/pyarmnn/examples/example_utils.py b/python/pyarmnn/examples/example_utils.py index f4d1e4eb1f..5ef30f2331 100644 --- a/python/pyarmnn/examples/example_utils.py +++ b/python/pyarmnn/examples/example_utils.py @@ -73,7 +73,7 @@ def __create_network(model_file: str, backends: list, parser=None): def create_tflite_network(model_file: str, backends: list = ['CpuAcc', 'CpuRef']): - """Creates a network from an onnx model file. + """Creates a network from a tflite model file. Args: model_file (str): Path of the model file. @@ -92,7 +92,7 @@ def create_tflite_network(model_file: str, backends: list = ['CpuAcc', 'CpuRef'] def create_onnx_network(model_file: str, backends: list = ['CpuAcc', 'CpuRef']): - """Creates a network from a tflite model file. + """Creates a network from an onnx model file. Args: model_file (str): Path of the model file. diff --git a/python/pyarmnn/src/pyarmnn/__init__.py b/python/pyarmnn/src/pyarmnn/__init__.py index 265880f2bc..b1aa81f507 100644 --- a/python/pyarmnn/src/pyarmnn/__init__.py +++ b/python/pyarmnn/src/pyarmnn/__init__.py @@ -47,7 +47,7 @@ except ImportError as err: raise RuntimeError(message) try: - from ._generated.pyarmnn_tfliteparser import ITfLiteParser + from ._generated.pyarmnn_tfliteparser import ITfLiteParser, TfLiteParserOptions except ImportError as err: logger = logging.getLogger(__name__) message = "Your ArmNN library instance does not support TF lite models parser functionality. " @@ -92,8 +92,8 @@ from ._generated.pyarmnn import ComparisonDescriptor, ComparisonOperation_Equal, from ._generated.pyarmnn import UnaryOperation_Abs, UnaryOperation_Exp, UnaryOperation_Sqrt, UnaryOperation_Rsqrt, \ UnaryOperation_Neg, ElementwiseUnaryDescriptor from ._generated.pyarmnn import Convolution2dDescriptor, DepthToSpaceDescriptor, DepthwiseConvolution2dDescriptor, \ - DetectionPostProcessDescriptor, FakeQuantizationDescriptor, FullyConnectedDescriptor, \ - InstanceNormalizationDescriptor, LstmDescriptor, L2NormalizationDescriptor, MeanDescriptor + DetectionPostProcessDescriptor, FakeQuantizationDescriptor, FillDescriptor, FullyConnectedDescriptor, \ + GatherDescriptor, InstanceNormalizationDescriptor, LstmDescriptor, L2NormalizationDescriptor, MeanDescriptor from ._generated.pyarmnn import NormalizationAlgorithmChannel_Across, NormalizationAlgorithmChannel_Within, \ NormalizationAlgorithmMethod_LocalBrightness, NormalizationAlgorithmMethod_LocalContrast, NormalizationDescriptor from ._generated.pyarmnn import PadDescriptor diff --git a/python/pyarmnn/src/pyarmnn/swig/armnn_tfliteparser.i b/python/pyarmnn/src/pyarmnn/swig/armnn_tfliteparser.i index 825b104a27..3ed5d6b8fd 100644 --- a/python/pyarmnn/src/pyarmnn/swig/armnn_tfliteparser.i +++ b/python/pyarmnn/src/pyarmnn/swig/armnn_tfliteparser.i @@ -87,14 +87,37 @@ public: list: A list of the output tensor names for the given model. ") GetSubgraphOutputTensorNames; std::vector GetSubgraphOutputTensorNames(size_t subgraphId); + + %feature("flatnested"); + %feature("docstring", + " + Options for TfLiteParser. + + Contains: + m_StandInLayerForUnsupported (bool): Add StandInLayers as placeholders for unsupported operators. + Default: False + m_InferAndValidate (bool): Infer output shape of operations based on their input shape. Default: False + ")TfLiteParserOptions; + struct TfLiteParserOptions + { + TfLiteParserOptions(); + + bool m_StandInLayerForUnsupported; + bool m_InferAndValidate; + }; }; %extend ITfLiteParser { // This is not a substitution of the default constructor of the Armnn class. It tells swig to create custom __init__ // method for ITfLiteParser python object that will use static factory method to do the job. - ITfLiteParser() { - return armnnTfLiteParser::ITfLiteParser::CreateRaw(); + ITfLiteParser(const armnnTfLiteParser::ITfLiteParser::TfLiteParserOptions* options = nullptr) { + if (options) { + return armnnTfLiteParser::ITfLiteParser::CreateRaw( + armnn::Optional(*options)); + } else { + return armnnTfLiteParser::ITfLiteParser::CreateRaw(); + } } // The following does not replace a real destructor of the Armnn class. @@ -127,6 +150,7 @@ public: } -} +} // end of namespace armnnTfLiteParser + // Clear exception typemap. %exception; diff --git a/python/pyarmnn/src/pyarmnn/swig/armnn_version.i b/python/pyarmnn/src/pyarmnn/swig/armnn_version.i index b21fbb1393..039b16605d 100644 --- a/python/pyarmnn/src/pyarmnn/swig/armnn_version.i +++ b/python/pyarmnn/src/pyarmnn/swig/armnn_version.i @@ -39,7 +39,7 @@ std::string GetVersion(); %feature("docstring", " - Returns Arm NN library major version. The year of the release. + Returns Arm NN library major version. Returns: str: Major version of Arm NN installed. @@ -49,7 +49,7 @@ std::string GetMajorVersion(); %feature("docstring", " - Returns Arm NN library minor version. Month of the year of the release. + Returns Arm NN library minor version. Returns: str: Minor version of Arm NN installed. diff --git a/python/pyarmnn/src/pyarmnn/swig/modules/armnn_descriptors.i b/python/pyarmnn/src/pyarmnn/swig/modules/armnn_descriptors.i index 9a01a52e13..a050722bb9 100644 --- a/python/pyarmnn/src/pyarmnn/swig/modules/armnn_descriptors.i +++ b/python/pyarmnn/src/pyarmnn/swig/modules/armnn_descriptors.i @@ -15,6 +15,7 @@ namespace std { } %include "typemaps/vectors.i" +%include "stdint.i" %typemap(out) const uint32_t* %{ @@ -386,6 +387,46 @@ struct FakeQuantizationDescriptor bool operator ==(const FakeQuantizationDescriptor& rhs) const; }; +%feature("docstring", + " + A descriptor for the Fill layer. Creates a tensor filled with a scalar value. + + Contains: + m_Value (float): Value the tensor will be filled with. + + ") FillDescriptor; +struct FillDescriptor +{ + FillDescriptor(); + FillDescriptor(const float& value); + + bool operator ==(const FillDescriptor& rhs) const; + + float m_Value; +}; + +%feature("docstring", + " + A descriptor for the Gather layer. + + Contains: + m_Axis (int32_t): The axis from where to gather values from. + + ") GatherDescriptor; +struct GatherDescriptor +{ + GatherDescriptor(); + + GatherDescriptor(int32_t axis); + + bool operator ==(const GatherDescriptor& rhs) const + { + return m_Axis == rhs.m_Axis; + } + + int32_t m_Axis; +}; + %feature("docstring", " A descriptor for the FullyConnected layer. See `INetwork.AddFullyConnectedLayer()`. @@ -661,7 +702,9 @@ struct ReshapeDescriptor Default: `ResizeMethod_NearestNeighbor`. m_DataLayout (int): The data layout to be used (`DataLayout_NCHW`, `DataLayout_NHWC`). Default: `DataLayout_NCHW`. m_AlignCorners (bool): Align corners or not when resizing. If True, corner pixel values are preserved after resizing. - Default: False + Default: False. + m_HalfPixelCenters (bool): If true, calculates the pixels from the center instead of from the edge. + Default: False. ") ResizeDescriptor; struct ResizeDescriptor @@ -673,6 +716,7 @@ struct ResizeDescriptor ResizeMethod m_Method; DataLayout m_DataLayout; bool m_AlignCorners; + bool m_HalfPixelCenters; bool operator ==(const ResizeDescriptor& rhs) const; }; @@ -970,20 +1014,24 @@ struct SoftmaxDescriptor m_StrideY (int): Underlying C++ data type is uint32_t. Stride value when proceeding through input for the height dimension. Default: 0. m_BiasEnabled (bool): Enable/disable bias. Default: false. m_DataLayout (int): The data layout to be used (`DataLayout_NCHW`, `DataLayout_NHWC`). Default: `DataLayout_NCHW`. + m_OutputShapeEnabled (bool): Set to true if output shape is specified. Will prevent output shape inference. + m_OutputShape (list of int): Output shape if it has been specified. ") TransposeConvolution2dDescriptor; struct TransposeConvolution2dDescriptor { TransposeConvolution2dDescriptor(); - uint32_t m_PadLeft; - uint32_t m_PadRight; - uint32_t m_PadTop; - uint32_t m_PadBottom; - uint32_t m_StrideX; - uint32_t m_StrideY; - bool m_BiasEnabled; - DataLayout m_DataLayout; + uint32_t m_PadLeft; + uint32_t m_PadRight; + uint32_t m_PadTop; + uint32_t m_PadBottom; + uint32_t m_StrideX; + uint32_t m_StrideY; + bool m_BiasEnabled; + DataLayout m_DataLayout; + bool m_OutputShapeEnabled; + std::vector m_OutputShape; bool operator ==(const TransposeConvolution2dDescriptor& rhs) const; }; diff --git a/python/pyarmnn/src/pyarmnn/swig/modules/armnn_network.i b/python/pyarmnn/src/pyarmnn/swig/modules/armnn_network.i index b065331992..4665e6087e 100644 --- a/python/pyarmnn/src/pyarmnn/swig/modules/armnn_network.i +++ b/python/pyarmnn/src/pyarmnn/swig/modules/armnn_network.i @@ -20,15 +20,21 @@ Struct for holding options relating to the Arm NN optimizer. See `Optimize`. Contains: m_debug (bool): Add debug data for easier troubleshooting. - m_ReduceFp32ToFp16 (bool): Reduce Fp32 data to Fp16 for faster processing. + m_ReduceFp32ToBf16 (bool): Reduces Fp32 network to BFloat16 (Bf16) for faster processing. Layers + that can not be reduced will be left in Fp32. + m_ReduceFp32ToFp16 (bool): Reduces Fp32 network to Fp16 for faster processing. Layers + that can not be reduced will be left in Fp32. ") OptimizerOptions; struct OptimizerOptions { OptimizerOptions(); - OptimizerOptions(bool reduceFp32ToFp16, bool debug); + OptimizerOptions(bool reduceFp32ToFp16, + bool debug, + bool reduceFp32ToBf16 = false); + bool m_ReduceFp32ToBf16; bool m_ReduceFp32ToFp16; bool m_Debug; }; @@ -501,19 +507,33 @@ public: armnn::IConnectableLayer* AddDivisionLayer(const char* name = nullptr); %feature("docstring", - " - Adds an Elementwise Unary layer to the network. Type of unary operation to use is decided by elementwiseUnaryDescriptor. Unary operations supported are (Abs, Exp, Neg, Rsqrt, Sqrt) + " + Adds an Elementwise Unary layer to the network. Type of unary operation to use is decided by elementwiseUnaryDescriptor. Unary operations supported are (Abs, Exp, Neg, Rsqrt, Sqrt) - Args: - elementwiseUnaryDescriptor (ElementwiseUnaryDescriptor): ElementwiseUnaryDescriptor to configure the choice of unary operation added to the network. - name (str): Optional name for the layer. + Args: + elementwiseUnaryDescriptor (ElementwiseUnaryDescriptor): ElementwiseUnaryDescriptor to configure the choice of unary operation added to the network. + name (str): Optional name for the layer. - Returns: - IConnectableLayer: Interface for configuring the layer. - ") AddElementwiseUnaryLayer; + Returns: + IConnectableLayer: Interface for configuring the layer. + ") AddElementwiseUnaryLayer; armnn::IConnectableLayer* AddElementwiseUnaryLayer(const ElementwiseUnaryDescriptor& elementwiseUnaryDescriptor, const char* name = nullptr); + %feature("docstring", + " + Add a Fill layer to the network. + + Args: + FillDescriptor (FillDescriptor): Descriptor for the fill operation. + name (str): Optional name for the layer. + + Returns: + IConnectableLayer: Interface for configuring the layer. + ") AddFillLayer; + armnn::IConnectableLayer* AddFillLayer(const FillDescriptor& fillDescriptor, + const char* name = nullptr); + %feature("docstring", " Adds a Floor layer to the network. @@ -531,12 +551,14 @@ public: Add Gather layer to the network. Args: + descriptor (GatherDescriptor): Descriptor for the gather operation. name (str): Optional name for the layer. Returns: IConnectableLayer: Interface for configuring the layer. ") AddGatherLayer; - armnn::IConnectableLayer* AddGatherLayer(const char* name = nullptr); + armnn::IConnectableLayer* AddGatherLayer(const GatherDescriptor& descriptor, + const char* name = nullptr); %feature("docstring", " @@ -752,6 +774,20 @@ public: armnn::IConnectableLayer* AddQuantizedLstmLayer(const armnn::QuantizedLstmInputParams& params, const char* name = nullptr); + + %feature("docstring", + " + Adds a Rank layer to the network. + + Args: + name (str): Optional name for the layer. + + Returns: + IConnectableLayer: Interface for configuring the layer. + ") AddRankLayer; + armnn::IConnectableLayer* AddRankLayer(const char* name = nullptr); + + %feature("docstring", " Adds a Reshape layer to the network. diff --git a/python/pyarmnn/test/test_descriptors.py b/python/pyarmnn/test/test_descriptors.py index 6d49747d5a..b0574a14ba 100644 --- a/python/pyarmnn/test/test_descriptors.py +++ b/python/pyarmnn/test/test_descriptors.py @@ -143,6 +143,16 @@ def test_fakequantization_descriptor_default_values(): np.allclose(-6, desc.m_Min) +def test_fill_descriptor_default_values(): + desc = ann.FillDescriptor() + np.allclose(0, desc.m_Value) + + +def test_gather_descriptor_default_values(): + desc = ann.GatherDescriptor() + assert desc.m_Axis == 0 + + def test_fully_connected_descriptor_default_values(): desc = ann.FullyConnectedDescriptor() assert desc.m_BiasEnabled == False @@ -370,7 +380,7 @@ def test_space_to_batch_nd_descriptor_ctor(): def test_transpose_convolution2d_descriptor_default_values(): - desc = ann.DepthwiseConvolution2dDescriptor() + desc = ann.TransposeConvolution2dDescriptor() assert desc.m_PadLeft == 0 assert desc.m_PadTop == 0 assert desc.m_PadRight == 0 @@ -379,6 +389,7 @@ def test_transpose_convolution2d_descriptor_default_values(): assert desc.m_StrideY == 0 assert desc.m_BiasEnabled == False assert desc.m_DataLayout == ann.DataLayout_NCHW + assert desc.m_OutputShapeEnabled == False def test_view_descriptor_default_values(): @@ -480,7 +491,9 @@ generated_classes_names = list(map(lambda x: x[0], generated_classes)) 'StackDescriptor', 'StridedSliceDescriptor', 'TransposeConvolution2dDescriptor', - 'ElementwiseUnaryDescriptor']) + 'ElementwiseUnaryDescriptor', + 'FillDescriptor', + 'GatherDescriptor']) class TestDescriptorMassChecks: def test_desc_implemented(self, desc_name): @@ -522,7 +535,9 @@ generated_classes_names = list(map(lambda x: x[0], generated_classes)) 'StackDescriptor', 'StridedSliceDescriptor', 'TransposeConvolution2dDescriptor', - 'ElementwiseUnaryDescriptor']) + 'ElementwiseUnaryDescriptor', + 'FillDescriptor', + 'GatherDescriptor']) class TestDescriptorMassChecks: def test_desc_implemented(self, desc_name): diff --git a/python/pyarmnn/test/test_network.py b/python/pyarmnn/test/test_network.py index fc2591c1d5..679e640374 100644 --- a/python/pyarmnn/test/test_network.py +++ b/python/pyarmnn/test/test_network.py @@ -6,6 +6,23 @@ import stat import pytest import pyarmnn as ann +def test_optimizer_options_default_values(): + opt = ann.OptimizerOptions() + assert opt.m_ReduceFp32ToFp16 == False + assert opt.m_Debug == False + assert opt.m_ReduceFp32ToBf16 == False + +def test_optimizer_options_set_values1(): + opt = ann.OptimizerOptions(True, True) + assert opt.m_ReduceFp32ToFp16 == True + assert opt.m_Debug == True + assert opt.m_ReduceFp32ToBf16 == False + +def test_optimizer_options_set_values2(): + opt = ann.OptimizerOptions(False, False, True) + assert opt.m_ReduceFp32ToFp16 == False + assert opt.m_Debug == False + assert opt.m_ReduceFp32ToBf16 == True @pytest.fixture(scope="function") def get_runtime(shared_data_folder, network_file): @@ -166,6 +183,7 @@ def test_serialize_to_dot_mode_readonly(network_file, get_runtime, tmpdir): 'AddDivisionLayer', 'AddElementwiseUnaryLayer', 'AddFloorLayer', + 'AddFillLayer', 'AddFullyConnectedLayer', 'AddGatherLayer', 'AddInputLayer', @@ -186,6 +204,7 @@ def test_serialize_to_dot_mode_readonly(network_file, get_runtime, tmpdir): 'AddPreluLayer', 'AddQuantizeLayer', 'AddQuantizedLstmLayer', + 'AddRankLayer', 'AddReshapeLayer', 'AddResizeLayer', 'AddSliceLayer', diff --git a/python/pyarmnn/test/test_tflite_parser.py b/python/pyarmnn/test/test_tflite_parser.py index 344ec7ca13..8735eef3fb 100644 --- a/python/pyarmnn/test/test_tflite_parser.py +++ b/python/pyarmnn/test/test_tflite_parser.py @@ -7,6 +7,12 @@ import pyarmnn as ann import numpy as np +def test_TfLiteParserOptions_default_values(): + parserOptions = ann.TfLiteParserOptions() + assert parserOptions.m_InferAndValidate == False + assert parserOptions.m_StandInLayerForUnsupported == False + + @pytest.fixture() def parser(shared_data_folder): """ @@ -30,6 +36,42 @@ def test_check_tflite_parser_swig_ownership(parser): assert parser.thisown +def test_tflite_parser_with_optional_options(): + parserOptions = ann.TfLiteParserOptions() + parserOptions.m_InferAndValidate = True + parser = ann.ITfLiteParser(parserOptions) + assert parser.thisown + + +def create_with_opt() : + parserOptions = ann.TfLiteParserOptions() + parserOptions.m_InferAndValidate = True + return ann.ITfLiteParser(parserOptions) + +def test_tflite_parser_with_optional_options_out_of_scope(shared_data_folder): + parser = create_with_opt() + network = parser.CreateNetworkFromBinaryFile(os.path.join(shared_data_folder, "mock_model.tflite")) + + graphs_count = parser.GetSubgraphCount() + graph_id = graphs_count - 1 + + input_names = parser.GetSubgraphInputTensorNames(graph_id) + input_binding_info = parser.GetNetworkInputBindingInfo(graph_id, input_names[0]) + + output_names = parser.GetSubgraphOutputTensorNames(graph_id) + + preferred_backends = [ann.BackendId('CpuAcc'), ann.BackendId('CpuRef')] + + options = ann.CreationOptions() + runtime = ann.IRuntime(options) + + opt_network, messages = ann.Optimize(network, preferred_backends, runtime.GetDeviceSpec(), ann.OptimizerOptions()) + assert 0 == len(messages) + + net_id, messages = runtime.LoadNetwork(opt_network) + assert "" == messages + + def test_tflite_get_sub_graph_count(parser): graphs_count = parser.GetSubgraphCount() assert graphs_count == 1 -- cgit v1.2.1