aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralexander <alexander.efremov@arm.com>2021-10-18 19:17:24 +0100
committerJan Eilers <jan.eilers@arm.com>2021-10-20 13:05:51 +0000
commit73010788725f8f07efb6df20711ece712ee213ea (patch)
treec31261ee0d4bb69fdb76f4f5147b82a74763ff55
parente8d7ccb1a18b5c39ecf17fb063fcc50baacc74b3 (diff)
downloadarmnn-73010788725f8f07efb6df20711ece712ee213ea.tar.gz
MLECO-2488: added model optimization options and updated OptimizerOptions constructor.
Signed-off-by: alexander <alexander.efremov@arm.com> Change-Id: Ic2ad6a46c3830f2526ba8b20ca0db0780be4b9a2
-rw-r--r--python/pyarmnn/src/pyarmnn/__init__.py4
-rw-r--r--python/pyarmnn/src/pyarmnn/swig/armnn.i1
-rw-r--r--python/pyarmnn/src/pyarmnn/swig/modules/armnn_backend_opt.i103
-rw-r--r--python/pyarmnn/src/pyarmnn/swig/modules/armnn_network.i24
-rw-r--r--python/pyarmnn/src/pyarmnn/swig/typemaps/model_options.i55
-rw-r--r--python/pyarmnn/test/test_modeloption.py149
-rw-r--r--python/pyarmnn/test/test_network.py11
-rw-r--r--python/pyarmnn/test/test_onnx_parser.py1
-rw-r--r--python/pyarmnn/test/test_runtime.py6
-rw-r--r--python/pyarmnn/test/test_supported_backends.py2
-rw-r--r--python/pyarmnn/test/test_tflite_parser.py1
11 files changed, 350 insertions, 7 deletions
diff --git a/python/pyarmnn/src/pyarmnn/__init__.py b/python/pyarmnn/src/pyarmnn/__init__.py
index 13fdf95c6f..44992522c0 100644
--- a/python/pyarmnn/src/pyarmnn/__init__.py
+++ b/python/pyarmnn/src/pyarmnn/__init__.py
@@ -53,6 +53,7 @@ from ._generated.pyarmnn import Optimize, OptimizerOptions, IOptimizedNetwork, I
# Backend
from ._generated.pyarmnn import BackendId
from ._generated.pyarmnn import IDeviceSpec
+from ._generated.pyarmnn import BackendOptions, BackendOption
# Tensors
from ._generated.pyarmnn import TensorInfo, TensorShape
@@ -65,7 +66,8 @@ from ._generated.pyarmnn import IProfiler
# Types
from ._generated.pyarmnn import DataType_Float16, DataType_Float32, DataType_QAsymmU8, DataType_Signed32, \
- DataType_Boolean, DataType_QSymmS16, DataType_QSymmS8, DataType_QAsymmS8
+ DataType_Boolean, DataType_QSymmS16, DataType_QSymmS8, DataType_QAsymmS8, ShapeInferenceMethod_ValidateOnly, \
+ ShapeInferenceMethod_InferAndValidate
from ._generated.pyarmnn import DataLayout_NCHW, DataLayout_NHWC
from ._generated.pyarmnn import MemorySource_Malloc, MemorySource_Undefined, MemorySource_DmaBuf, \
MemorySource_DmaBufProtected
diff --git a/python/pyarmnn/src/pyarmnn/swig/armnn.i b/python/pyarmnn/src/pyarmnn/swig/armnn.i
index 172d2d80cf..c525e1767e 100644
--- a/python/pyarmnn/src/pyarmnn/swig/armnn.i
+++ b/python/pyarmnn/src/pyarmnn/swig/armnn.i
@@ -14,6 +14,7 @@
//armnn api submodules
%include "modules/armnn_backend.i"
+%include "modules/armnn_backend_opt.i"
%include "modules/armnn_types.i"
%include "modules/armnn_descriptors.i"
%include "modules/armnn_lstmparam.i"
diff --git a/python/pyarmnn/src/pyarmnn/swig/modules/armnn_backend_opt.i b/python/pyarmnn/src/pyarmnn/swig/modules/armnn_backend_opt.i
new file mode 100644
index 0000000000..2ff54aaaa1
--- /dev/null
+++ b/python/pyarmnn/src/pyarmnn/swig/modules/armnn_backend_opt.i
@@ -0,0 +1,103 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+%{
+#include "armnn/BackendId.hpp"
+#include "armnn/BackendOptions.hpp"
+%}
+
+#pragma SWIG nowarn=SWIGWARN_PARSE_NESTED_CLASS
+
+%{
+ typedef armnn::BackendOptions::BackendOption BackendOption;
+%}
+
+%feature("docstring",
+"
+Struct for the users to pass backend specific option.
+") BackendOption;
+%nodefaultctor BackendOption;
+struct BackendOption
+{
+ BackendOption(std::string name, bool value);
+ BackendOption(std::string name, int value);
+ BackendOption(std::string name, unsigned int value);
+ BackendOption(std::string name, float value);
+ BackendOption(std::string name, std::string value);
+
+ std::string GetName();
+};
+
+namespace armnn
+{
+%feature("docstring",
+"
+Struct for backend specific options, see `BackendOption`.
+Options are assigned to a specific backend by providing a backend id.
+
+") BackendOptions;
+%nodefaultctor BackendOptions;
+struct BackendOptions
+{
+ BackendOptions(BackendId backend);
+
+ BackendOptions(const BackendOptions& other);
+
+ %feature("docstring",
+ "
+ Add backend option.
+
+ Args:
+ option (`BackendOption`): backend option
+ ") AddOption;
+ void AddOption(const BackendOption& option);
+
+ %feature("docstring",
+ "
+ Get a backend id.
+
+ Returns:
+ BackendId: assigned backend id.
+ ") GetBackendId;
+ const BackendId& GetBackendId();
+
+ %feature("docstring",
+ "
+ Get backend options count.
+
+ Returns:
+ int: number of options for a backend.
+ ") GetOptionCount;
+ size_t GetOptionCount();
+
+ %feature("docstring",
+ "
+ Get backend option by index.
+
+ Args:
+ idx (int): backend option index
+
+ Returns:
+ BackendOption: backend option.
+ ") GetOption;
+ const BackendOption& GetOption(size_t idx);
+
+ %pythoncode %{
+ def __iter__(self):
+ for count in range(self.GetOptionCount()):
+ yield self[count]
+ %}
+};
+
+%extend BackendOptions {
+
+ const BackendOption& __getitem__(size_t i) const {
+ return $self->GetOption(i);
+ }
+
+ size_t __len__() const {
+ return $self->GetOptionCount();
+ }
+}
+}
diff --git a/python/pyarmnn/src/pyarmnn/swig/modules/armnn_network.i b/python/pyarmnn/src/pyarmnn/swig/modules/armnn_network.i
index f4581ca5ec..d50b841f4a 100644
--- a/python/pyarmnn/src/pyarmnn/swig/modules/armnn_network.i
+++ b/python/pyarmnn/src/pyarmnn/swig/modules/armnn_network.i
@@ -11,6 +11,11 @@
%}
%include <typemaps/network_optimize.i>
+%include <typemaps/model_options.i>
+
+namespace std {
+ %template() std::vector<armnn::BackendOptions>;
+}
namespace armnn
{
@@ -24,9 +29,19 @@ Contains:
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.
- m_ImportEnabled (bool): Enable memory import.
+ m_ImportEnabled (bool): Enable memory import.
+ m_shapeInferenceMethod: The ShapeInferenceMethod modifies how the output shapes are treated.
+ When ValidateOnly is selected, the output shapes are inferred from the input parameters
+ of the layer and any mismatch is reported.
+ When InferAndValidate is selected 2 actions are performed: (1)infer output shape from
+ inputs and (2)validate the shapes as in ValidateOnly. This option has been added to work
+ with tensors which rank or dimension sizes are not specified explicitly, however this
+ information can be calculated from the inputs.
+ m_ModelOptions: List of backends optimisation options.
") OptimizerOptions;
+
+%model_options_typemap;
struct OptimizerOptions
{
OptimizerOptions();
@@ -34,13 +49,18 @@ struct OptimizerOptions
OptimizerOptions(bool reduceFp32ToFp16,
bool debug,
bool reduceFp32ToBf16 = false,
- bool importEnabled = false);
+ ShapeInferenceMethod shapeInferenceMethod = armnn::ShapeInferenceMethod::ValidateOnly,
+ bool importEnabled = false,
+ std::vector<armnn::BackendOptions> modelOptions = {});
bool m_ReduceFp32ToBf16;
bool m_ReduceFp32ToFp16;
bool m_Debug;
+ ShapeInferenceMethod m_shapeInferenceMethod;
bool m_ImportEnabled;
+ std::vector<armnn::BackendOptions> m_ModelOptions;
};
+%model_options_clear;
%feature("docstring",
"
diff --git a/python/pyarmnn/src/pyarmnn/swig/typemaps/model_options.i b/python/pyarmnn/src/pyarmnn/swig/typemaps/model_options.i
new file mode 100644
index 0000000000..c4acaefb1b
--- /dev/null
+++ b/python/pyarmnn/src/pyarmnn/swig/typemaps/model_options.i
@@ -0,0 +1,55 @@
+//
+// Copyright © 2021 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+%inline %{
+
+ static PyObject* from_model_options_to_python(std::vector<armnn::BackendOptions>* input) {
+ Py_ssize_t size = input->size();
+ PyObject* localList = PyList_New(size);
+
+ if (!localList) {
+ Py_XDECREF(localList);
+ return PyErr_NoMemory();
+ }
+
+ for(Py_ssize_t i = 0; i < size; ++i) {
+
+ PyObject* obj = SWIG_NewPointerObj(SWIG_as_voidptr(&input->at(i)), SWIGTYPE_p_armnn__BackendOptions, 0 | 0 );
+
+ PyList_SET_ITEM(localList, i, obj);
+ }
+ return localList;
+ }
+%}
+
+%define %model_options_typemap
+
+// this typemap works for struct argument get
+
+ %typemap(out) std::vector<armnn::BackendOptions>* {
+ $result = from_model_options_to_python($1);
+ }
+
+// this typemap works for struct argument set
+ %typemap(in) std::vector<armnn::BackendOptions>* {
+ if (PySequence_Check($input)) {
+
+ int res = swig::asptr($input, &$1);
+ if (!SWIG_IsOK(res) || !$1) {
+ SWIG_exception_fail(SWIG_ArgError(($1 ? res : SWIG_TypeError)),
+ "in method '" "OptimizerOptions_m_ModelOptions_set" "', argument " "2"" of type '" "std::vector< armnn::BackendOptions,std::allocator< armnn::BackendOptions > > *""'");
+ }
+
+ } else {
+ PyErr_SetString(PyExc_TypeError, "Argument value object does not provide sequence protocol.");
+ SWIG_fail;
+ }
+ }
+
+%enddef
+
+%define %model_options_clear
+ %typemap(out) std::vector<armnn::BackendOptions>*;
+ %typemap(in) std::vector<armnn::BackendOptions>*;
+%enddef
diff --git a/python/pyarmnn/test/test_modeloption.py b/python/pyarmnn/test/test_modeloption.py
new file mode 100644
index 0000000000..c03d4a8cce
--- /dev/null
+++ b/python/pyarmnn/test/test_modeloption.py
@@ -0,0 +1,149 @@
+# Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+# SPDX-License-Identifier: MIT
+import pytest
+
+from pyarmnn import BackendOptions, BackendOption, BackendId, OptimizerOptions, ShapeInferenceMethod_InferAndValidate
+
+
+@pytest.mark.parametrize("data", (True, -100, 128, 0.12345, 'string'))
+def test_backend_option_ctor(data):
+ bo = BackendOption("name", data)
+ assert "name" == bo.GetName()
+
+
+def test_backend_options_ctor():
+ backend_id = BackendId('a')
+ bos = BackendOptions(backend_id)
+
+ assert 'a' == str(bos.GetBackendId())
+
+ another_bos = BackendOptions(bos)
+ assert 'a' == str(another_bos.GetBackendId())
+
+
+def test_backend_options_add():
+ backend_id = BackendId('a')
+ bos = BackendOptions(backend_id)
+ bo = BackendOption("name", 1)
+ bos.AddOption(bo)
+
+ assert 1 == bos.GetOptionCount()
+ assert 1 == len(bos)
+
+ assert 'name' == bos[0].GetName()
+ assert 'name' == bos.GetOption(0).GetName()
+ for option in bos:
+ assert 'name' == option.GetName()
+
+ bos.AddOption(BackendOption("name2", 2))
+
+ assert 2 == bos.GetOptionCount()
+ assert 2 == len(bos)
+
+
+def test_backend_option_ownership():
+ backend_id = BackendId('b')
+ bos = BackendOptions(backend_id)
+ bo = BackendOption('option', True)
+ bos.AddOption(bo)
+
+ assert bo.thisown
+
+ del bo
+
+ assert 1 == bos.GetOptionCount()
+ option = bos[0]
+ assert not option.thisown
+ assert 'option' == option.GetName()
+
+ del option
+
+ option_again = bos[0]
+ assert not option_again.thisown
+ assert 'option' == option_again.GetName()
+
+
+def test_optimizer_options_with_model_opt():
+ a = BackendOptions(BackendId('a'))
+
+ oo = OptimizerOptions(True,
+ False,
+ False,
+ ShapeInferenceMethod_InferAndValidate,
+ True,
+ [a])
+
+ mo = oo.m_ModelOptions
+
+ assert 1 == len(mo)
+ assert 'a' == str(mo[0].GetBackendId())
+
+ b = BackendOptions(BackendId('b'))
+
+ c = BackendOptions(BackendId('c'))
+
+ oo.m_ModelOptions = (a, b, c)
+
+ mo = oo.m_ModelOptions
+
+ assert 3 == len(oo.m_ModelOptions)
+
+ assert 'a' == str(mo[0].GetBackendId())
+ assert 'b' == str(mo[1].GetBackendId())
+ assert 'c' == str(mo[2].GetBackendId())
+
+
+def test_optimizer_option_default():
+ oo = OptimizerOptions(True,
+ False,
+ False,
+ ShapeInferenceMethod_InferAndValidate,
+ True)
+
+ assert 0 == len(oo.m_ModelOptions)
+
+
+def test_optimizer_options_fail():
+ a = BackendOptions(BackendId('a'))
+
+ with pytest.raises(TypeError) as err:
+ OptimizerOptions(True,
+ False,
+ False,
+ ShapeInferenceMethod_InferAndValidate,
+ True,
+ a)
+
+ assert "Wrong number or type of arguments" in str(err.value)
+
+ with pytest.raises(RuntimeError) as err:
+ OptimizerOptions(True,
+ False,
+ True,
+ ShapeInferenceMethod_InferAndValidate,
+ True,
+ [a])
+
+ assert "BFloat16 and Float16 optimization cannot be enabled at the same time" in str(err.value)
+
+ with pytest.raises(TypeError) as err:
+ oo = OptimizerOptions(True,
+ False,
+ False,
+ ShapeInferenceMethod_InferAndValidate,
+ True)
+
+ oo.m_ModelOptions = 'nonsense'
+
+ assert "in method 'OptimizerOptions_m_ModelOptions_set', argument 2" in str(err.value)
+
+ with pytest.raises(TypeError) as err:
+ oo = OptimizerOptions(True,
+ False,
+ False,
+ ShapeInferenceMethod_InferAndValidate,
+ True)
+
+ oo.m_ModelOptions = ['nonsense', a]
+
+ assert "in method 'OptimizerOptions_m_ModelOptions_set', argument 2" in str(err.value)
diff --git a/python/pyarmnn/test/test_network.py b/python/pyarmnn/test/test_network.py
index c24b113cdb..9b2dbf7f3a 100644
--- a/python/pyarmnn/test/test_network.py
+++ b/python/pyarmnn/test/test_network.py
@@ -6,12 +6,15 @@ 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
assert opt.m_ImportEnabled == False
+ assert opt.m_shapeInferenceMethod == ann.ShapeInferenceMethod_ValidateOnly
+
def test_optimizer_options_set_values1():
opt = ann.OptimizerOptions(True, True)
@@ -19,6 +22,8 @@ def test_optimizer_options_set_values1():
assert opt.m_Debug == True
assert opt.m_ReduceFp32ToBf16 == False
assert opt.m_ImportEnabled == False
+ assert opt.m_shapeInferenceMethod == ann.ShapeInferenceMethod_ValidateOnly
+
def test_optimizer_options_set_values2():
opt = ann.OptimizerOptions(False, False, True)
@@ -26,13 +31,17 @@ def test_optimizer_options_set_values2():
assert opt.m_Debug == False
assert opt.m_ReduceFp32ToBf16 == True
assert opt.m_ImportEnabled == False
+ assert opt.m_shapeInferenceMethod == ann.ShapeInferenceMethod_ValidateOnly
+
def test_optimizer_options_set_values3():
- opt = ann.OptimizerOptions(False, False, True, True)
+ opt = ann.OptimizerOptions(False, False, True, ann.ShapeInferenceMethod_InferAndValidate, True)
assert opt.m_ReduceFp32ToFp16 == False
assert opt.m_Debug == False
assert opt.m_ReduceFp32ToBf16 == True
assert opt.m_ImportEnabled == True
+ assert opt.m_shapeInferenceMethod == ann.ShapeInferenceMethod_InferAndValidate
+
@pytest.fixture(scope="function")
def get_runtime(shared_data_folder, network_file):
diff --git a/python/pyarmnn/test/test_onnx_parser.py b/python/pyarmnn/test/test_onnx_parser.py
index 99353a0959..1424c24cea 100644
--- a/python/pyarmnn/test/test_onnx_parser.py
+++ b/python/pyarmnn/test/test_onnx_parser.py
@@ -5,7 +5,6 @@ import os
import pytest
import pyarmnn as ann
import numpy as np
-from typing import List
@pytest.fixture()
diff --git a/python/pyarmnn/test/test_runtime.py b/python/pyarmnn/test/test_runtime.py
index 295c870370..fbdd8044ce 100644
--- a/python/pyarmnn/test/test_runtime.py
+++ b/python/pyarmnn/test/test_runtime.py
@@ -97,6 +97,7 @@ def test_python_disowns_network(random_runtime):
assert not opt_network.thisown
+
def test_load_network(random_runtime):
preferred_backends = random_runtime[0]
network = random_runtime[1]
@@ -109,6 +110,7 @@ def test_load_network(random_runtime):
assert "" == messages
assert net_id == 0
+
def test_create_runtime_with_external_profiling_enabled():
options = ann.CreationOptions()
@@ -125,6 +127,7 @@ def test_create_runtime_with_external_profiling_enabled():
assert runtime is not None
+
def test_create_runtime_with_external_profiling_enabled_invalid_options():
options = ann.CreationOptions()
@@ -157,6 +160,7 @@ def test_load_network_properties_provided(random_runtime):
assert "" == messages
assert net_id == 0
+
def test_network_properties_constructor(random_runtime):
preferred_backends = random_runtime[0]
network = random_runtime[1]
@@ -178,10 +182,12 @@ def test_network_properties_constructor(random_runtime):
assert "" == messages
assert net_id == 0
+
def test_network_properties_deprecated_constructor():
with pytest.warns(DeprecationWarning):
warnings.warn("Deprecated: Use constructor with MemorySource argument instead.", DeprecationWarning)
+
def test_unload_network_fails_for_invalid_net_id(random_runtime):
preferred_backends = random_runtime[0]
network = random_runtime[1]
diff --git a/python/pyarmnn/test/test_supported_backends.py b/python/pyarmnn/test/test_supported_backends.py
index e1ca5ee6df..4ff5eb07a5 100644
--- a/python/pyarmnn/test/test_supported_backends.py
+++ b/python/pyarmnn/test/test_supported_backends.py
@@ -1,7 +1,5 @@
# Copyright © 2020 Arm Ltd. All rights reserved.
# SPDX-License-Identifier: MIT
-import os
-import platform
import pytest
import pyarmnn as ann
diff --git a/python/pyarmnn/test/test_tflite_parser.py b/python/pyarmnn/test/test_tflite_parser.py
index 8735eef3fb..2764f3f30d 100644
--- a/python/pyarmnn/test/test_tflite_parser.py
+++ b/python/pyarmnn/test/test_tflite_parser.py
@@ -48,6 +48,7 @@ def create_with_opt() :
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"))